Support for array parameter in "WHERE IN" clause
Shogobg opened this issue · 1 comments
Hello.
I have the following code:
import { Client as LibsqlClient, createClient } from '@libsql/client/web';
...
const res = await client.execute({
sql: 'SELECT * FROM watched_videos WHERE videoId IN (:videos) LIMIT 10;',
args: { videos: ['--0zLNUmhRI', '--1AEHcLmuk'] },
});
Expected behavior:
Given the table has more than 2 records and the records with videoId '--0zLNUmhRI' and '--1AEHcLmuk' exist, the query should return two rows containing the corresponding videoId.
Actual result - no records are returned:
{
"columns": [
"videoId",
"status"
],
"columnTypes": [
"varchar(255)",
"INT",
],
"rows": [],
"rowsAffected": 0,
"lastInsertRowid": null
}
I've looked at the code libsql, and it seems that currently there is no support for an array of values as argument, like the above.
https://github.com/tursodatabase/libsql-client-ts/blob/main/packages/libsql-core/src/api.ts#L422
The expected args
type for the .execute()
function seems to be defined here:
libsql-client-ts/packages/libsql-core/src/api.ts
Lines 65 to 89 in 10cd150
libsql-client-ts/packages/libsql-core/src/api.ts
Lines 469 to 474 in 10cd150
From what I'm understanding, the args
interface is coming from the libsql package, because .execute()
is internally calling executeStmt()
libsql-client-ts/packages/libsql-client/src/sqlite3.ts
Lines 125 to 142 in 10cd150
and executeStmt()
is passing the args
down to the libsql db object via the .all()
and/or the .run()
functions:
libsql-client-ts/packages/libsql-client/src/sqlite3.ts
Lines 324 to 391 in 10cd150
I'm not smart enough to understand whats happening after, but it seems to be related to these files:
Interestingly, this file contains an example using the 'WHERE IN' clause, but I'm not sure if we can use this somehow.
I ended up writing a custom util/helper function to transform the named placeholders:
export function transformNamedArgsToPositionalArgs({
sql,
args,
}: {
sql: string;
args: Record<string, InValue | string[]>;
}): { sql: string; args: InValue[] } {
const positionalArgs: InValue[] = [];
const transformedSql = sql.replace(/(:\w+)/g, (match) => {
const argName = match.substring(1);
const argValue = args[argName];
if (argValue === undefined) {
throw new Error(`Missing argument for ${argName}`);
}
if (Array.isArray(argValue)) {
positionalArgs.push(...argValue);
return argValue.map(() => '?').join(', ');
}
positionalArgs.push(argValue);
return '?';
});
return { sql: transformedSql, args: positionalArgs };
}
Usage:
const res = await client.execute(transformNamedArgsToPositionalArgs({
sql: 'SELECT * FROM watched_videos WHERE videoId IN (:videos) LIMIT 10;',
args: { videos: ['--0zLNUmhRI', '--1AEHcLmuk'] },
}));