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