From d6a8cb59bb0b7270027565430e25d89fa808a34e Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 10:33:37 -0500 Subject: [PATCH 01/10] feat: sql.join --- README.md | 5 +++++ src/index.js | 5 +++++ tests/index.js | 8 ++++++++ types/index.d.ts | 1 + 4 files changed, 19 insertions(+) diff --git a/README.md b/README.md index 421d19a0..12a88288 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,11 @@ select * from users select * from users where user_id = $1 ``` +Multiple fragments can be joined together with `sql.join` to create more complex dynamic queries: +```js +const columns = ['id', 'name', 'age'].map(name => sql(name)) +sql`select ${sql.join(', ', columns)} from users where + ### SQL functions Using keywords or calling functions dynamically is also possible by using ``` sql`` ``` fragments. ```js diff --git a/src/index.js b/src/index.js index 0573e2bc..ee6c0393 100644 --- a/src/index.js +++ b/src/index.js @@ -98,6 +98,7 @@ function Postgres(a, b) { notify, array, json, + join, file }) @@ -318,6 +319,10 @@ function Postgres(a, b) { return new Parameter(x, 3802) } + function join(sep, xs) { + return xs.flatMap((x, i) => i ? [sep, x] : x) + } + function array(x, type) { if (!Array.isArray(x)) return array(Array.from(arguments)) diff --git a/tests/index.js b/tests/index.js index bf81b036..8643d1df 100644 --- a/tests/index.js +++ b/tests/index.js @@ -2469,6 +2469,14 @@ t('Supports arrays of fragments', async() => { ] }) +t("Joins fragments with a separator", () => { + const fs = [sql`a = ${1}`, sql`b = ${"test"}`] + return [ + sql`select * from t where ${ sql.join(sql` and `, fs) }; `.describe().string, + 'select * from t where a = $1 and b = $2' + ] +}); + t('Does not try rollback when commit errors', async() => { let notice = null const sql = postgres({ ...options, onnotice: x => notice = x }) diff --git a/types/index.d.ts b/types/index.d.ts index 8989ff47..29f09478 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -702,6 +702,7 @@ declare namespace postgres { file<T extends readonly any[] = Row[]>(path: string | Buffer | URL | number, options?: { cache?: boolean | undefined } | undefined): PendingQuery<T>; file<T extends readonly any[] = Row[]>(path: string | Buffer | URL | number, args: (ParameterOrJSON<TTypes[keyof TTypes]>)[], options?: { cache?: boolean | undefined } | undefined): PendingQuery<T>; json(value: JSONValue): Parameter; + join<T extends any[], U extends any[]>(a: PendingQuery<T>, p: PendingQuery<U>): PendingQuery<T> reserve(): Promise<ReservedSql<TTypes>> } From 680e6ec74d8f5625b329b2c7896ce3f0c000be9d Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 11:08:40 -0500 Subject: [PATCH 02/10] add: flatten arrays passed to sql.join --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index ee6c0393..23a48cd6 100644 --- a/src/index.js +++ b/src/index.js @@ -320,7 +320,7 @@ function Postgres(a, b) { } function join(sep, xs) { - return xs.flatMap((x, i) => i ? [sep, x] : x) + return xs.flatMap((x, i) => i ? Array.isArray(x) ? [sep, ...x] : [sep, x]) } function array(x, type) { From c7e352aa50b90f0f9beeb4b26be51d1e5916e909 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 12:26:08 -0500 Subject: [PATCH 03/10] fix: broken ternary into ifs --- src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 23a48cd6..5e06ce49 100644 --- a/src/index.js +++ b/src/index.js @@ -320,7 +320,11 @@ function Postgres(a, b) { } function join(sep, xs) { - return xs.flatMap((x, i) => i ? Array.isArray(x) ? [sep, ...x] : [sep, x]) + return xs.flatMap((x, i) => { + if (i === 0) return x + if (Array.isArray(x)) return [sep, ...x] + return [sep, x] + }) } function array(x, type) { From 63b14d4d7874a2b44970bbf9501e6c5c7b20f033 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:10:13 -0500 Subject: [PATCH 04/10] fix: readme docs for sql.join --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12a88288..7e623fed 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ select * from users where user_id = $1 Multiple fragments can be joined together with `sql.join` to create more complex dynamic queries: ```js const columns = ['id', 'name', 'age'].map(name => sql(name)) -sql`select ${sql.join(', ', columns)} from users where +sql`select ${sql.join(sql`, `, columns)} from users where ### SQL functions Using keywords or calling functions dynamically is also possible by using ``` sql`` ``` fragments. From 3dcbbc180c075f68b2a30a009f6fa57e073441ec Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:20:46 -0500 Subject: [PATCH 05/10] fix: markdown rendering --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e623fed..dba876e3 100644 --- a/README.md +++ b/README.md @@ -340,12 +340,13 @@ await sql` select * from users // Or select * from users where user_id = $1 -``` Multiple fragments can be joined together with `sql.join` to create more complex dynamic queries: + ```js const columns = ['id', 'name', 'age'].map(name => sql(name)) sql`select ${sql.join(sql`, `, columns)} from users where +``` ### SQL functions Using keywords or calling functions dynamically is also possible by using ``` sql`` ``` fragments. From a397785e137653b60b9f060b5ca32ea62f421d60 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:21:52 -0500 Subject: [PATCH 06/10] fix: markdown rendering --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dba876e3..0c60b855 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,7 @@ await sql` select * from users // Or select * from users where user_id = $1 +``` Multiple fragments can be joined together with `sql.join` to create more complex dynamic queries: From a670fd500fd556b7e29c2df8f4e0360c2e0af000 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:22:58 -0500 Subject: [PATCH 07/10] fix: markdown rendering --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c60b855..cd9569c0 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,7 @@ Multiple fragments can be joined together with `sql.join` to create more complex ```js const columns = ['id', 'name', 'age'].map(name => sql(name)) -sql`select ${sql.join(sql`, `, columns)} from users where +sql`select ${sql.join(sql`, `, columns)} from users where` ``` ### SQL functions From f238b09d04bb65f6c5ead39ad816c491c1375495 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:26:47 -0500 Subject: [PATCH 08/10] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd9569c0..31ffefcb 100644 --- a/README.md +++ b/README.md @@ -342,11 +342,14 @@ select * from users select * from users where user_id = $1 ``` +### Dynamic fragments Multiple fragments can be joined together with `sql.join` to create more complex dynamic queries: ```js -const columns = ['id', 'name', 'age'].map(name => sql(name)) -sql`select ${sql.join(sql`, `, columns)} from users where` +// this could be built dynamically +const expressions = [sql`name is not null`, sql`age > 50`] +const separator = and ? sql` and ` : sql` or ` +sql`select * from from users where ${sql.join(separator, expressions)}` ``` ### SQL functions From c3ea620cac66cced80d8e50acd7f8d1965f09264 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:30:19 -0500 Subject: [PATCH 09/10] fix: extra trailing chars in test --- tests/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index 8643d1df..8dd191b7 100644 --- a/tests/index.js +++ b/tests/index.js @@ -2472,7 +2472,7 @@ t('Supports arrays of fragments', async() => { t("Joins fragments with a separator", () => { const fs = [sql`a = ${1}`, sql`b = ${"test"}`] return [ - sql`select * from t where ${ sql.join(sql` and `, fs) }; `.describe().string, + sql`select * from t where ${ sql.join(sql` and `, fs) }`.describe().string, 'select * from t where a = $1 and b = $2' ] }); From 78b54e647c802e0c9fc068b1f71dfc5f38185933 Mon Sep 17 00:00:00 2001 From: DanielFGray <danielfgray@gmail.com> Date: Thu, 27 Jun 2024 17:36:49 -0500 Subject: [PATCH 10/10] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 31ffefcb..0f18150a 100644 --- a/README.md +++ b/README.md @@ -347,9 +347,11 @@ Multiple fragments can be joined together with `sql.join` to create more complex ```js // this could be built dynamically -const expressions = [sql`name is not null`, sql`age > 50`] +let expressions = [sql`name is not null`, sql`age > 50`] const separator = and ? sql` and ` : sql` or ` -sql`select * from from users where ${sql.join(separator, expressions)}` +sql`select * from from users where ${ + sql.join(separator, expressions) +}` ``` ### SQL functions