Node-postgres: Can no longer pass an array of data to use inside `column IN ($1)` parameterized query

Created on 21 May 2018  路  8Comments  路  Source: brianc/node-postgres

I apologize if there was an update that caused this and I missed it, but suddenly (using node-postgres version 6.1.5) the following query doesn't work when it used to pretty recently:

import { Pool } from 'pg'
const pool = new Pool(config)
const { rows } = await pool.query('SELECT * FROM table WHERE id IN ($1)', [[1,2,3]]

I get the following error, which I haven't gotten in the past:

error: invalid input syntax for integer: "{"1","2","3"}"

I also tried the following which isn't working either:

const { rows } = await pool.query('SELECT * FROM table WHERE id IN ($1)', [[1,2,3].join(',')]
error: invalid input syntax for integer: "1,2,3"

How do I run a parameterized query using the IN keyword and provide a list of data as we used to be able to before? Is there documentation on this breaking change somewhere I missed?

Most helpful comment

Oh, use ANY as @sehrope mentions above. So instead of using col IN (val1, val2, ...) use col = ANY($1) with an array of values as your parameter for $1.

const arrayOfStrings = ['val1', 'val2', 'val3']
const { rows } = await pool.query('SELECT * FROM table WHERE my_string_column = ANY($1)', [arrayOfStrings])

All 8 comments

AFAIK there isn't any breaking change is as this is how both the server and node-postgres client have always been:

=> SELECT 1 WHERE 1 IN ('{1,2,3}'::int[]);
ERROR:  42883: operator does not exist: integer = integer[]
LINE 1: SELECT 1 WHERE 1 IN ('{1,2,3}'::int[]);
                         ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
LOCATION:  op_error, parse_oper.c:722

To do this type of thing replace the value IN (...) with value = ANY($1):

=> SELECT 1
    WHERE 1 = ANY('{1,2,3}'::int[]);
 ?column? 
----------
        1

Then the $1 can be an array. The IN (...) approach only works for a inline list in the SQL itself and requires dynamically composing (and escaping!) the values so they're part of the SQL text. The = ANY($1) approach is always better as it can be parameterized.

I鈥檓 pretty sure this has never worked, like sehrope said.

Does the library offer a way to safely quote values so I can inject them directly into the query?

@retorquere you should use parameterized queries wherever you can, but I believe for strings simply replacing all single ticks with multiple ticks should safely prevent injection:

SELECT * FROM table WHERE my_string_column = '${str_value.replace("'", "''")}'

I'd very much prefer parametrized queries, but according to what's above, there's no parametrized way to do IN ($1), right? So string injection is my only option.

Oh, use ANY as @sehrope mentions above. So instead of using col IN (val1, val2, ...) use col = ANY($1) with an array of values as your parameter for $1.

const arrayOfStrings = ['val1', 'val2', 'val3']
const { rows } = await pool.query('SELECT * FROM table WHERE my_string_column = ANY($1)', [arrayOfStrings])

Great! I thought that was the JSON syntax, sorry.

This doesn't work in redshift.

Oh, use ANY as @sehrope mentions above. So instead of using col IN (val1, val2, ...) use col = ANY($1) with an array of values as your parameter for $1.

const arrayOfStrings = ['val1', 'val2', 'val3']
const { rows } = await pool.query('SELECT * FROM table WHERE my_string_column = ANY($1)', [arrayOfStrings])
Was this page helpful?
0 / 5 - 0 ratings

Related issues

gajus picture gajus  路  4Comments

v1co1n picture v1co1n  路  4Comments

dindurthy picture dindurthy  路  4Comments

dipakdas99 picture dipakdas99  路  3Comments

AhmedBHameed picture AhmedBHameed  路  3Comments