porsager / postgres

Postgres.js - The Fastest full featured PostgreSQL client for Node.js, Deno, Bun and CloudFlare
The Unlicense
7.42k stars 271 forks source link

WHERE IN throws `TypeError: str.replace is not a function` #701

Open Nepherpitou opened 1 year ago

Nepherpitou commented 1 year ago

Queries with WHERE IN ${sql()} fails with error starting from version 3.4.0

await sql`
  select
    *
  from users
  where age in ${ sql([68, 75, 23]) }
`

Error:

TypeError: str.replace is not a function
    at escape (...\node_modules\postgres\cjs\src\types.js:217:20)
    at ...\node_modules\postgres\cjs\src\types.js:213:22
    at Array.map (<anonymous>)
    at escapeIdentifiers (...\node_modules\postgres\cjs\src\types.js:213:13)
    at Object.select [as fn] (...\node_modules\postgres\cjs\src\types.js:139:12)
    at Builder.build (...\node_modules\postgres\cjs\src\types.js:71:17)
    at stringifyValue (...\node_modules\postgres\cjs\src\types.js:109:38)
    at stringify (...\node_modules\postgres\cjs\src\types.js:100:16)
    at build (...\node_modules\postgres\cjs\src\connection.js:223:20)
    at Object.execute (...\node_modules\postgres\cjs\src\connection.js:167:7)
    at go (...\node_modules\postgres\cjs\src\index.js:341:14)
    at Query.handler (...\node_modules\postgres\cjs\src\index.js:330:14)
    at Query.handle (...\node_modules\postgres\cjs\src\query.js:140:65)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at new Query (...\node_modules\postgres\cjs\src\query.js:35:9)
    at Object.sql (...\node_modules\postgres\cjs\src\index.js:112:11)
rytido commented 12 months ago

I've only seen this when there's a nested in (which you can get around with a cte). There also seems to be something going on with whether or not there's whitespace after the in:

sql.unsafe(`drop table if exists t; create table t as (select 1 x);`).then(() => {
  // success: not nested
  sql`select x from t where x in ${sql([1])}`
    .then((data) => console.log(data)) // Result [{x: 1}]
    .catch((err) => console.log(err.message));

  // failure: nested without space after outer 'in'
  sql`select x from t where x in(select x from t where x in ${sql([1])})`
    .then((data) => console.log(data))
    .catch((err) => console.log(err.message)); // str.replace is not a function

  // failure: nested without space after inner 'in'
  sql`select x from t where x in (select x from t where x in${sql([1])})`
    .then((data) => console.log(data))
    .catch((err) => console.log(err.message)); // str.replace is not a function

  // success: space after outer and inner 'in'
  sql`select x from t where x in (select x from t where x in ${sql([1])})`
    .then((data) => console.log(data)) // Result [{x: 1}]
    .catch((err) => console.log(err.message));
});
porsager commented 11 months ago

@Nepherpitou just tried exactly your example and didn't see any issue. Do you have something I can repro?

porsager commented 11 months ago

Very nice tests @rytido !! Thank you - I'll look at fixing those and including your tests.

alonrbar commented 6 months ago

For what it worth, I had encountered this issue too and the comment by @rytido helped me work around it. This was my original query:

const val1 = "1";
const valuesArray = [
  ["2", "3", "4"]
];

sql`
    DELETE FROM my_table
    WHERE
        col1 = ${val1} AND
        (col2, col3, col4) IN (${sql(valuesArray)})
`;

Removing the space directly after the "IN" made it work:

sql`
    DELETE FROM my_table
    WHERE
        col1 = ${val1} AND
        (col2, col3, col4) IN(${sql(valuesArray)})
`;