0

I am trying to dynamically change the column queried in a WHERE statement.

My code looks like this

const pg = require('pg');
const pool = new pg.Pool();
await pool.connect();

let selector = {
  key: 'foo',
  value: 'bar',
};

const result = await pool.query(`SELECT * FROM foobar WHERE ${selector.key}=$1::text`, [selector.value]);

There is only a few key values I switch between. While the key is pre-defined and not from user input I would prefer not to use a template string.

Is there a way to parameterize the column name?

I have tried a few ways.

  1. Just using a default parameter
await pool.query('SELECT * FROM foobar WHERE $1=$2::text', [selector.key, selector.value]);

This doesn't work as it parameterizes the key as a string so is just doing a string comparison.

  1. Casting as oid (this was mentioned somewhere but I can't remember where)
await pool.query('SELECT * FROM foobar WHERE $1::oid=$2::text', [selector.key, selector.value]);

This throws an error as pg doesn't have a way to cast text to oid.

3 Answers 3

1
+50

Plpgsql could be a possible solution. Consider the following function:

CREATE OR REPLACE FUNCTION run_prepared_on_foobar(q text) RETURNS foobar AS $$
DECLARE
    retval foobar;
BEGIN
    EXECUTE q INTO retval;
    RETURN retval;
END $$ LANGUAGE plpgsql;

Once this is in place, you can then call it by passing any prepared statement:

SELECT * FROM run_prepared_on_foobar('SELECT * FROM foobar WHERE '||$1::text||'='||$2::int)

This is extremely poor in performance since the planner will not be able to peek inside what is going on in the function. All estimates will be random.

Shenanigans of plpgsql aside, a better approach would be to have extra code to switch over different queries depending on external conditions.

Sign up to request clarification or add additional context in comments.

1 Comment

The question was for query parameters and not executing a parameterized function.
1

You cannot use postgres parameters as identifiers (=column name). (1, via 2)

What's wrong with your current code's template string?

2 Comments

That's unfortunate. It introduces the possibility of SQL injection if a user is able to manipulate the key.
Didn't you say the key is pre-defined and not from user input?
1

As previous answers state: column names cannot be used in parameterized queries, they need to be specified in the SQL string. To escape any potential injections I used:


import { Client, Pool } from 'pg';

const { escapeIdentifier } = new Client();
const pool = new Pool({ /*config here*/ });

const columnName = 'id';
const userId = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

const result = pool.query(
  `SELECT * FROM users WHERE ${escapeIdentifier(columnName)} = $1`,
  [userId]
);

Source: https://node-postgres.com/apis/utilities#pgescapeidentifier

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.