joist-orm 2.3.0-next.41 → 2.3.0-next.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/build/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/build/_virtual/_rolldown/runtime.js +5 -0
  3. package/build/codegen.cjs +19 -0
  4. package/build/codegen.cjs.map +1 -0
  5. package/build/codegen.d.cts +1 -0
  6. package/build/codegen.d.mts +1 -0
  7. package/build/codegen.js +11 -28
  8. package/build/codegen.js.map +1 -1
  9. package/build/drivers/PostgresDriver.cjs +290 -0
  10. package/build/drivers/PostgresDriver.cjs.map +1 -0
  11. package/build/drivers/PostgresDriver.d.cts +77 -0
  12. package/build/drivers/PostgresDriver.d.cts.map +1 -0
  13. package/build/drivers/PostgresDriver.d.mts +77 -0
  14. package/build/drivers/PostgresDriver.d.mts.map +1 -0
  15. package/build/drivers/PostgresDriver.js +249 -325
  16. package/build/drivers/PostgresDriver.js.map +1 -1
  17. package/build/drivers/WireRowData.cjs +374 -0
  18. package/build/drivers/WireRowData.cjs.map +1 -0
  19. package/build/drivers/WireRowData.d.cts +111 -0
  20. package/build/drivers/WireRowData.d.cts.map +1 -0
  21. package/build/drivers/WireRowData.d.mts +111 -0
  22. package/build/drivers/WireRowData.d.mts.map +1 -0
  23. package/build/drivers/WireRowData.js +352 -412
  24. package/build/drivers/WireRowData.js.map +1 -1
  25. package/build/drivers/binaryParsers.cjs +422 -0
  26. package/build/drivers/binaryParsers.cjs.map +1 -0
  27. package/build/drivers/{binaryParsers.d.ts → binaryParsers.d.cts} +18 -14
  28. package/build/drivers/binaryParsers.d.cts.map +1 -0
  29. package/build/drivers/binaryParsers.d.mts +80 -0
  30. package/build/drivers/binaryParsers.d.mts.map +1 -0
  31. package/build/drivers/binaryParsers.js +315 -385
  32. package/build/drivers/binaryParsers.js.map +1 -1
  33. package/build/drivers/patchPgProtocol.cjs +255 -0
  34. package/build/drivers/patchPgProtocol.cjs.map +1 -0
  35. package/build/drivers/{patchPgProtocol.d.ts → patchPgProtocol.d.cts} +18 -14
  36. package/build/drivers/patchPgProtocol.d.cts.map +1 -0
  37. package/build/drivers/patchPgProtocol.d.mts +47 -0
  38. package/build/drivers/patchPgProtocol.d.mts.map +1 -0
  39. package/build/drivers/patchPgProtocol.js +211 -232
  40. package/build/drivers/patchPgProtocol.js.map +1 -1
  41. package/build/graphql-codegen-export.cjs +10 -0
  42. package/build/graphql-codegen-export.d.cts +1 -0
  43. package/build/graphql-codegen-export.d.mts +1 -0
  44. package/build/graphql-codegen-export.js +2 -19
  45. package/build/graphql-export.cjs +10 -0
  46. package/build/graphql-export.d.cts +1 -0
  47. package/build/graphql-export.d.mts +1 -0
  48. package/build/graphql-export.js +2 -19
  49. package/build/index.cjs +41 -0
  50. package/build/index.d.cts +3 -0
  51. package/build/index.d.mts +3 -0
  52. package/build/index.js +3 -27
  53. package/build/knex-export.cjs +10 -0
  54. package/build/knex-export.d.cts +1 -0
  55. package/build/knex-export.d.mts +1 -0
  56. package/build/knex-export.js +2 -19
  57. package/build/pg-export.cjs +13 -0
  58. package/build/pg-export.d.cts +4 -0
  59. package/build/pg-export.d.mts +4 -0
  60. package/build/pg-export.js +4 -20
  61. package/build/pg-migrate.cjs +22 -0
  62. package/build/pg-migrate.cjs.map +1 -0
  63. package/build/pg-migrate.d.cts +1 -0
  64. package/build/pg-migrate.d.mts +1 -0
  65. package/build/pg-migrate.js +13 -28
  66. package/build/pg-migrate.js.map +1 -1
  67. package/build/seed.cjs +49 -0
  68. package/build/seed.cjs.map +1 -0
  69. package/build/{seed.d.ts → seed.d.cts} +5 -1
  70. package/build/seed.d.cts.map +1 -0
  71. package/build/seed.d.mts +24 -0
  72. package/build/seed.d.mts.map +1 -0
  73. package/build/seed.js +39 -45
  74. package/build/seed.js.map +1 -1
  75. package/build/tests-export.cjs +10 -0
  76. package/build/tests-export.d.cts +1 -0
  77. package/build/tests-export.d.mts +1 -0
  78. package/build/tests-export.js +2 -19
  79. package/package.json +150 -28
  80. package/build/codegen.d.ts +0 -2
  81. package/build/drivers/PostgresDriver.d.ts +0 -73
  82. package/build/drivers/WireRowData.d.ts +0 -107
  83. package/build/graphql-codegen-export.d.ts +0 -1
  84. package/build/graphql-codegen-export.js.map +0 -1
  85. package/build/graphql-export.d.ts +0 -1
  86. package/build/graphql-export.js.map +0 -1
  87. package/build/index.d.ts +0 -3
  88. package/build/index.js.map +0 -1
  89. package/build/knex-export.d.ts +0 -1
  90. package/build/knex-export.js.map +0 -1
  91. package/build/pg-export.d.ts +0 -3
  92. package/build/pg-export.js.map +0 -1
  93. package/build/pg-migrate.d.ts +0 -2
  94. package/build/tests-export.d.ts +0 -1
  95. package/build/tests-export.js.map +0 -1
@@ -1,361 +1,285 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PostgresDriver = void 0;
7
- exports.setupLatestPgTypes = setupLatestPgTypes;
8
- const joist_core_1 = require("joist-core");
9
- const pg_1 = __importDefault(require("pg"));
10
- const pg_types_1 = require("pg-types");
11
- const postgres_array_1 = __importDefault(require("postgres-array"));
12
- const binaryParsers_1 = require("./binaryParsers");
13
- const patchPgProtocol_1 = require("./patchPgProtocol");
14
- const WireRowData_1 = require("./WireRowData");
1
+ import { registerDatabaseBinaryParsers, registerTemporalBinaryParsers } from "./binaryParsers.js";
2
+ import { ensureLazyDataRows } from "./patchPgProtocol.js";
3
+ import { executeRowDataQuery, isRowDataCapableClient } from "./WireRowData.js";
4
+ import { JoinRowOperation, PojoRowData, SequenceIdAssigner, buildRawQuery, cleanSql, driverAfterBegin, driverAfterCommit, driverBeforeBegin, driverBeforeCommit, ensureRectangularArraySizes, fail, generateOps, getRuntimeConfig, kq, kqDot, partition } from "joist-core";
5
+ import pg from "pg";
6
+ import { builtins, getTypeParser } from "pg-types";
7
+ import array from "postgres-array";
8
+ //#region src/drivers/PostgresDriver.ts
15
9
  /**
16
- * Implements the `Driver` interface for Postgres.
17
- *
18
- * This is the canonical driver implementation and leverages several aspects of
19
- * Postgres for the best performance, i.e.:
20
- *
21
- * - Deferred foreign key constraints are used to allow bulk inserting/updating
22
- * all entities without any topographic sorting/ordering issues.
23
- *
24
- * - Sequences are used to bulk-assign + then bulk-insert all entities, to again
25
- * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
26
- *
27
- * - We use a pg-specific bulk update syntax.
28
- */
29
- class PostgresDriver {
30
- pool;
31
- #idAssigner;
32
- #preloadPlugin;
33
- #onQuery;
34
- lazyRows;
35
- /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */
36
- executeFindRowData;
37
- #databaseBinaryParsers = undefined;
38
- constructor(pool, opts) {
39
- this.pool = pool;
40
- this.#idAssigner =
41
- opts?.idAssigner ??
42
- new joist_core_1.SequenceIdAssigner(async (sql) => {
43
- this.#onQuery?.(sql);
44
- return (await pool.query(sql)).rows;
45
- });
46
- this.#preloadPlugin = opts?.preloadPlugin;
47
- this.#onQuery = opts?.onQuery;
48
- // Lazy rows require pg-protocol to emit lazy DataRows, which we patch in at runtime; if the
49
- // patch cannot be applied/verified (i.e. future pg-protocol internals changed), stay classic
50
- this.lazyRows = (opts?.lazyRows ?? false) && (0, patchPgProtocol_1.ensureLazyDataRows)();
51
- if (opts?.lazyRows && !this.lazyRows) {
52
- console.warn("joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.");
53
- }
54
- this.executeFindRowData = this.lazyRows
55
- ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings)
56
- : undefined;
57
- setupLatestPgTypes((0, joist_core_1.getRuntimeConfig)().temporal);
58
- }
59
- async executeFind(em, parsed, settings) {
60
- const { sql, bindings } = (0, joist_core_1.buildRawQuery)(parsed, { limit: em.entityLimit, ...settings });
61
- return this.executeQuery(em, sql, bindings);
62
- }
63
- async #executeFindRowData(em, parsed, settings) {
64
- // Auto-register binary parsers for the db's dynamic-oid text-likes (native enums, citext)
65
- // once, before our first lazy query; on failure, reset so the next query retries
66
- await (this.#databaseBinaryParsers ??= (0, binaryParsers_1.registerDatabaseBinaryParsers)(this.pool).catch((err) => {
67
- this.#databaseBinaryParsers = undefined;
68
- throw err;
69
- }));
70
- const { sql, bindings } = (0, joist_core_1.buildRawQuery)(parsed, { limit: em.entityLimit, ...settings });
71
- const pgSql = toPgParams(sql);
72
- this.#onQuery?.(pgSql);
73
- // Centralize client checkout here (pg-pool rejects Submittables in pool.query), and decide
74
- // classic-vs-lazy *before* submitting anything, i.e. unsupported clients like pg-native fall
75
- // back to classic rows rather than failing mid-query
76
- const txnClient = em.txn;
77
- const client = txnClient ?? (await this.pool.connect());
78
- try {
79
- if (!(0, WireRowData_1.isRowDataCapableClient)(client)) {
80
- warnUnsupportedClientOnce();
81
- return new joist_core_1.PojoRowData((await client.query(pgSql, bindings)).rows);
82
- }
83
- return await (0, WireRowData_1.executeRowDataQuery)(client, pgSql, bindings);
84
- }
85
- finally {
86
- if (!txnClient)
87
- client.release();
88
- }
89
- }
90
- async executeQuery(em, sql, bindings) {
91
- const pgSql = toPgParams(sql);
92
- this.#onQuery?.(pgSql);
93
- const client = this.getMaybeInTxnClient(em);
94
- return client.query(pgSql, bindings).then((result) => result.rows);
95
- }
96
- async transaction(em, fn) {
97
- if (em.txn) {
98
- return fn(em.txn);
99
- }
100
- const client = await this.pool.connect();
101
- let result;
102
- try {
103
- await (0, joist_core_1.driverBeforeBegin)(em, client);
104
- this.#onQuery?.("BEGIN;");
105
- await client.query("BEGIN");
106
- em.txn = client;
107
- try {
108
- await (0, joist_core_1.driverAfterBegin)(em, client);
109
- result = await fn(client);
110
- await (0, joist_core_1.driverBeforeCommit)(em, client);
111
- this.#onQuery?.("COMMIT;");
112
- await client.query("COMMIT");
113
- }
114
- catch (e) {
115
- await client.query("ROLLBACK");
116
- throw e;
117
- }
118
- finally {
119
- em.txn = undefined;
120
- }
121
- await (0, joist_core_1.driverAfterCommit)(em, client);
122
- }
123
- finally {
124
- client.release();
125
- }
126
- return result;
127
- }
128
- async assignNewIds(_, todos) {
129
- return this.#idAssigner.assignNewIds(todos);
130
- }
131
- async flush(em, entityTodos, joinRows) {
132
- const client = (em.txn ?? (0, joist_core_1.fail)("Expected EntityManager.txn to be set"));
133
- await this.#idAssigner.assignNewIds(entityTodos);
134
- const ops = (0, joist_core_1.generateOps)(entityTodos);
135
- const onQuery = this.#onQuery;
136
- // Do INSERTs+UPDATEs first so that we avoid DELETE cascades invalidating oplocks
137
- // See https://github.com/joist-orm/joist-orm/issues/591
138
- await Promise.all([
139
- ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),
140
- ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),
141
- ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),
142
- ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {
143
- return [
144
- m2mBatchInsert(client, joinTableName, todo, onQuery),
145
- m2mBatchDelete(client, joinTableName, todo, onQuery),
146
- ];
147
- }),
148
- ]);
149
- }
150
- get defaultPlugins() {
151
- return { preloadPlugin: this.#preloadPlugin };
152
- }
153
- getMaybeInTxnClient(em) {
154
- return em.txn || this.pool;
155
- }
156
- }
157
- exports.PostgresDriver = PostgresDriver;
10
+ * Implements the `Driver` interface for Postgres.
11
+ *
12
+ * This is the canonical driver implementation and leverages several aspects of
13
+ * Postgres for the best performance, i.e.:
14
+ *
15
+ * - Deferred foreign key constraints are used to allow bulk inserting/updating
16
+ * all entities without any topographic sorting/ordering issues.
17
+ *
18
+ * - Sequences are used to bulk-assign + then bulk-insert all entities, to again
19
+ * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
20
+ *
21
+ * - We use a pg-specific bulk update syntax.
22
+ */
23
+ var PostgresDriver = class {
24
+ pool;
25
+ #idAssigner;
26
+ #preloadPlugin;
27
+ #onQuery;
28
+ lazyRows;
29
+ /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */
30
+ executeFindRowData;
31
+ #databaseBinaryParsers = void 0;
32
+ constructor(pool, opts) {
33
+ this.pool = pool;
34
+ this.#idAssigner = opts?.idAssigner ?? new SequenceIdAssigner(async (sql) => {
35
+ this.#onQuery?.(sql);
36
+ return (await pool.query(sql)).rows;
37
+ });
38
+ this.#preloadPlugin = opts?.preloadPlugin;
39
+ this.#onQuery = opts?.onQuery;
40
+ this.lazyRows = (opts?.lazyRows ?? false) && ensureLazyDataRows();
41
+ if (opts?.lazyRows && !this.lazyRows) console.warn("joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.");
42
+ this.executeFindRowData = this.lazyRows ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings) : void 0;
43
+ setupLatestPgTypes(getRuntimeConfig().temporal);
44
+ }
45
+ async executeFind(em, parsed, settings) {
46
+ const { sql, bindings } = buildRawQuery(parsed, {
47
+ limit: em.entityLimit,
48
+ ...settings
49
+ });
50
+ return this.executeQuery(em, sql, bindings);
51
+ }
52
+ async #executeFindRowData(em, parsed, settings) {
53
+ await (this.#databaseBinaryParsers ??= registerDatabaseBinaryParsers(this.pool).catch((err) => {
54
+ this.#databaseBinaryParsers = void 0;
55
+ throw err;
56
+ }));
57
+ const { sql, bindings } = buildRawQuery(parsed, {
58
+ limit: em.entityLimit,
59
+ ...settings
60
+ });
61
+ const pgSql = toPgParams(sql);
62
+ this.#onQuery?.(pgSql);
63
+ const txnClient = em.txn;
64
+ const client = txnClient ?? await this.pool.connect();
65
+ try {
66
+ if (!isRowDataCapableClient(client)) {
67
+ warnUnsupportedClientOnce();
68
+ return new PojoRowData((await client.query(pgSql, bindings)).rows);
69
+ }
70
+ return await executeRowDataQuery(client, pgSql, bindings);
71
+ } finally {
72
+ if (!txnClient) client.release();
73
+ }
74
+ }
75
+ async executeQuery(em, sql, bindings) {
76
+ const pgSql = toPgParams(sql);
77
+ this.#onQuery?.(pgSql);
78
+ return this.getMaybeInTxnClient(em).query(pgSql, bindings).then((result) => result.rows);
79
+ }
80
+ async transaction(em, fn) {
81
+ if (em.txn) return fn(em.txn);
82
+ const client = await this.pool.connect();
83
+ let result;
84
+ try {
85
+ await driverBeforeBegin(em, client);
86
+ this.#onQuery?.("BEGIN;");
87
+ await client.query("BEGIN");
88
+ em.txn = client;
89
+ try {
90
+ await driverAfterBegin(em, client);
91
+ result = await fn(client);
92
+ await driverBeforeCommit(em, client);
93
+ this.#onQuery?.("COMMIT;");
94
+ await client.query("COMMIT");
95
+ } catch (e) {
96
+ await client.query("ROLLBACK");
97
+ throw e;
98
+ } finally {
99
+ em.txn = void 0;
100
+ }
101
+ await driverAfterCommit(em, client);
102
+ } finally {
103
+ client.release();
104
+ }
105
+ return result;
106
+ }
107
+ async assignNewIds(_, todos) {
108
+ return this.#idAssigner.assignNewIds(todos);
109
+ }
110
+ async flush(em, entityTodos, joinRows) {
111
+ const client = em.txn ?? fail("Expected EntityManager.txn to be set");
112
+ await this.#idAssigner.assignNewIds(entityTodos);
113
+ const ops = generateOps(entityTodos);
114
+ const onQuery = this.#onQuery;
115
+ await Promise.all([
116
+ ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),
117
+ ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),
118
+ ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),
119
+ ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {
120
+ return [m2mBatchInsert(client, joinTableName, todo, onQuery), m2mBatchDelete(client, joinTableName, todo, onQuery)];
121
+ })
122
+ ]);
123
+ }
124
+ get defaultPlugins() {
125
+ return { preloadPlugin: this.#preloadPlugin };
126
+ }
127
+ getMaybeInTxnClient(em) {
128
+ return em.txn || this.pool;
129
+ }
130
+ };
158
131
  async function batchInsert(client, op, onQuery) {
159
- const { tableName, columns, columnValues } = op;
160
- const [cte, bindings] = buildUnnestCte("data", columns, columnValues);
161
- const sql = (0, joist_core_1.cleanSql)(`
132
+ const { tableName, columns, columnValues } = op;
133
+ const [cte, bindings] = buildUnnestCte("data", columns, columnValues);
134
+ const pgSql = toPgParams(cleanSql(`
162
135
  ${cte}
163
- INSERT INTO ${(0, joist_core_1.kq)(tableName)} (${columns.map((c) => (0, joist_core_1.kq)(c.columnName)).join(", ")})
136
+ INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(", ")})
164
137
  SELECT * FROM data
165
- `);
166
- const pgSql = toPgParams(sql);
167
- onQuery?.(pgSql);
168
- return client.query(pgSql, bindings);
138
+ `));
139
+ onQuery?.(pgSql);
140
+ return client.query(pgSql, bindings);
169
141
  }
170
142
  async function batchUpdate(client, op, onQuery) {
171
- const { tableName, columns, columnValues, updatedAt } = op;
172
- const [cte, bindings] = buildUnnestCte("data", columns, columnValues);
173
- // JS Dates only have millisecond-level precision, so we may have dropped/lost accuracy when
174
- // reading Postgres's microsecond-level `timestamptz` values; using `date_trunc` "downgrades"
175
- // the pg data to match what we have in the JS Date.
176
- //
177
- // ...but Temporal's types don't have this flaw.
178
- const truncateToMills = !(0, joist_core_1.getRuntimeConfig)().temporal;
179
- const maybeUpdatedAt = updatedAt && truncateToMills
180
- ? ` AND date_trunc('milliseconds', ${(0, joist_core_1.kqDot)(tableName, updatedAt)}) = data.__original_updated_at`
181
- : updatedAt
182
- ? ` AND ${(0, joist_core_1.kqDot)(tableName, updatedAt)} = data.__original_updated_at`
183
- : "";
184
- const sql = `
143
+ const { tableName, columns, columnValues, updatedAt } = op;
144
+ const [cte, bindings] = buildUnnestCte("data", columns, columnValues);
145
+ const truncateToMills = !getRuntimeConfig().temporal;
146
+ const maybeUpdatedAt = updatedAt && truncateToMills ? ` AND date_trunc('milliseconds', ${kqDot(tableName, updatedAt)}) = data.__original_updated_at` : updatedAt ? ` AND ${kqDot(tableName, updatedAt)} = data.__original_updated_at` : "";
147
+ const sql = `
185
148
  ${cte}
186
- UPDATE ${(0, joist_core_1.kq)(tableName)}
187
- SET ${columns
188
- .filter((c) => c.columnName !== "id" && c.columnName !== "__original_updated_at")
189
- .map((c) => `${(0, joist_core_1.kq)(c.columnName)} = data.${(0, joist_core_1.kq)(c.columnName)}`)
190
- .join(", ")}
149
+ UPDATE ${kq(tableName)}
150
+ SET ${columns.filter((c) => c.columnName !== "id" && c.columnName !== "__original_updated_at").map((c) => `${kq(c.columnName)} = data.${kq(c.columnName)}`).join(", ")}
191
151
  FROM data
192
- WHERE ${(0, joist_core_1.kq)(tableName)}.id = data.id ${maybeUpdatedAt}
193
- RETURNING ${(0, joist_core_1.kq)(tableName)}.id
152
+ WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}
153
+ RETURNING ${kq(tableName)}.id
194
154
  `;
195
- const pgSql = toPgParams((0, joist_core_1.cleanSql)(sql));
196
- onQuery?.(pgSql);
197
- const result = await client.query(pgSql, bindings);
198
- const results = result.rows;
199
- const ids = columnValues[0]; // assume id is the 1st column
200
- if (results.length !== ids.length) {
201
- const updated = new Set(results.map((r) => r.id));
202
- const missing = ids.filter((id) => !updated.has(id));
203
- throw new Error(`Oplock failure for ${tableName} rows ${missing.join(", ")}`);
204
- }
155
+ const pgSql = toPgParams(cleanSql(sql));
156
+ onQuery?.(pgSql);
157
+ const results = (await client.query(pgSql, bindings)).rows;
158
+ const ids = columnValues[0];
159
+ if (results.length !== ids.length) {
160
+ const updated = new Set(results.map((r) => r.id));
161
+ const missing = ids.filter((id) => !updated.has(id));
162
+ throw new Error(`Oplock failure for ${tableName} rows ${missing.join(", ")}`);
163
+ }
205
164
  }
206
165
  async function batchDelete(client, op, onQuery) {
207
- const { tableName, ids } = op;
208
- const pgSql = toPgParams(`DELETE FROM ${(0, joist_core_1.kq)(tableName)} WHERE id = ANY(?)`);
209
- onQuery?.(pgSql);
210
- await client.query(pgSql, [ids]);
166
+ const { tableName, ids } = op;
167
+ const pgSql = toPgParams(`DELETE FROM ${kq(tableName)} WHERE id = ANY(?)`);
168
+ onQuery?.(pgSql);
169
+ await client.query(pgSql, [ids]);
211
170
  }
212
171
  /** Creates a CTE named `tableName` that bulk-injects the `columnValues` into a SQL query. */
213
172
  function buildUnnestCte(tableName, columns, columnValues) {
214
- (0, joist_core_1.ensureRectangularArraySizes)(columns, columnValues);
215
- const selects = columns.map((c) => {
216
- if (c.dbType.endsWith("[]")) {
217
- if (c.isNullableArray) {
218
- return `unnest_arrays(?::${c.dbType}[], true) as ${(0, joist_core_1.kq)(c.columnName)}`;
219
- }
220
- else {
221
- return `unnest_arrays(?::${c.dbType}[]) as ${(0, joist_core_1.kq)(c.columnName)}`;
222
- }
223
- }
224
- else {
225
- return `unnest(?::${c.dbType}[]) as ${(0, joist_core_1.kq)(c.columnName)}`;
226
- }
227
- });
228
- const sql = `WITH ${tableName} AS (SELECT ${selects.join(", ")})`;
229
- return [sql, columnValues];
173
+ ensureRectangularArraySizes(columns, columnValues);
174
+ return [`WITH ${tableName} AS (SELECT ${columns.map((c) => {
175
+ if (c.dbType.endsWith("[]")) {
176
+ if (c.isNullableArray) return `unnest_arrays(?::${c.dbType}[], true) as ${kq(c.columnName)}`;
177
+ else return `unnest_arrays(?::${c.dbType}[]) as ${kq(c.columnName)}`;
178
+ } else return `unnest(?::${c.dbType}[]) as ${kq(c.columnName)}`;
179
+ }).join(", ")})`, columnValues];
230
180
  }
231
181
  async function m2mBatchInsert(client, joinTableName, todo, onQuery) {
232
- const { m2m, newRows } = todo;
233
- if (newRows.length === 0)
234
- return;
235
- const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));
236
- const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));
237
- if (m2m.hasJoinTableId) {
238
- const sql = (0, joist_core_1.cleanSql)(`
239
- WITH data AS (SELECT unnest(?::int[]) as ${(0, joist_core_1.kq)(m2m.columnName)}, unnest(?::int[]) as ${(0, joist_core_1.kq)(m2m.otherColumnName)})
240
- INSERT INTO ${(0, joist_core_1.kq)(joinTableName)} (${(0, joist_core_1.kq)(m2m.columnName)}, ${(0, joist_core_1.kq)(m2m.otherColumnName)})
182
+ const { m2m, newRows } = todo;
183
+ if (newRows.length === 0) return;
184
+ const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));
185
+ const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));
186
+ if (m2m.hasJoinTableId) {
187
+ const pgSql = toPgParams(cleanSql(`
188
+ WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})
189
+ INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})
241
190
  SELECT * FROM data
242
- ON CONFLICT (${(0, joist_core_1.kq)(m2m.columnName)}, ${(0, joist_core_1.kq)(m2m.otherColumnName)}) DO UPDATE SET id = ${(0, joist_core_1.kq)(joinTableName)}.id
191
+ ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id
243
192
  RETURNING id;
244
- `);
245
- const pgSql = toPgParams(sql);
246
- onQuery?.(pgSql);
247
- const { rows } = await client.query(pgSql, [col1Values, col2Values]);
248
- for (let i = 0; i < rows.length; i++) {
249
- newRows[i].id = rows[i].id;
250
- newRows[i].op = joist_core_1.JoinRowOperation.Flushed;
251
- newRows[i].persisted = true;
252
- }
253
- }
254
- else {
255
- // Id-less join tables have no surrogate id to return; the FK pair is the PK, so just
256
- // insert and let any duplicate be a no-op.
257
- const sql = (0, joist_core_1.cleanSql)(`
258
- WITH data AS (SELECT unnest(?::int[]) as ${(0, joist_core_1.kq)(m2m.columnName)}, unnest(?::int[]) as ${(0, joist_core_1.kq)(m2m.otherColumnName)})
259
- INSERT INTO ${(0, joist_core_1.kq)(joinTableName)} (${(0, joist_core_1.kq)(m2m.columnName)}, ${(0, joist_core_1.kq)(m2m.otherColumnName)})
193
+ `));
194
+ onQuery?.(pgSql);
195
+ const { rows } = await client.query(pgSql, [col1Values, col2Values]);
196
+ for (let i = 0; i < rows.length; i++) {
197
+ newRows[i].id = rows[i].id;
198
+ newRows[i].op = JoinRowOperation.Flushed;
199
+ newRows[i].persisted = true;
200
+ }
201
+ } else {
202
+ const pgSql = toPgParams(cleanSql(`
203
+ WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})
204
+ INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})
260
205
  SELECT * FROM data
261
- ON CONFLICT (${(0, joist_core_1.kq)(m2m.columnName)}, ${(0, joist_core_1.kq)(m2m.otherColumnName)}) DO NOTHING;
262
- `);
263
- const pgSql = toPgParams(sql);
264
- onQuery?.(pgSql);
265
- await client.query(pgSql, [col1Values, col2Values]);
266
- for (const row of newRows) {
267
- row.op = joist_core_1.JoinRowOperation.Flushed;
268
- row.persisted = true;
269
- }
270
- }
206
+ ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO NOTHING;
207
+ `));
208
+ onQuery?.(pgSql);
209
+ await client.query(pgSql, [col1Values, col2Values]);
210
+ for (const row of newRows) {
211
+ row.op = JoinRowOperation.Flushed;
212
+ row.persisted = true;
213
+ }
214
+ }
271
215
  }
272
216
  async function m2mBatchDelete(client, joinTableName, todo, onQuery) {
273
- const { m2m, deletedRows } = todo;
274
- if (deletedRows.length === 0)
275
- return;
276
- // Rows with a surrogate id are deleted by id; rows without one — id-less tables, or `remove`s
277
- // done against an unloaded ManyToManyCollection — are deleted by their (col1, col2) composite.
278
- const [haveIds, noIds] = (0, joist_core_1.partition)(deletedRows, (r) => {
279
- // We used to use `id !== -1` as a marker for "row is/is-not persisted in the db" -- but when adding support
280
- // for id-column-less m2m tables, we couldn't use `id === -1` as this marker anymore, so now rely on a dedicated
281
- // `persisted` key instead.
282
- //
283
- // So, ideally, this `id !== -1` could go away _except_ that our internal verisoning plugin leverages this "m2m rows
284
- // with an id=-1 delete by their FK values" to handle our versioned m2m tables. And for now it's easier to
285
- // keep/restore this `id !== -1` than refactor that.
286
- return r.id !== undefined && r.id !== -1;
287
- });
288
- if (haveIds.length > 0) {
289
- const pgSql = toPgParams(`DELETE FROM ${(0, joist_core_1.kq)(joinTableName)} WHERE id = ANY(?)`);
290
- onQuery?.(pgSql);
291
- await client.query(pgSql, [haveIds.map((r) => r.id)]);
292
- }
293
- if (noIds.length > 0) {
294
- const data = noIds
295
- // Watch for m2m rows that got added-then-removed to entities that were themselves added-then-removed,
296
- // as the deTagId will be undefined for those, as we're skipping adding them to the database.
297
- .filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName))
298
- .map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)]);
299
- if (data.length > 0) {
300
- const pgSql = toPgParams(`
301
- DELETE FROM ${(0, joist_core_1.kq)(joinTableName)}
302
- WHERE (${(0, joist_core_1.kq)(m2m.columnName)}, ${(0, joist_core_1.kq)(m2m.otherColumnName)}) IN (
217
+ const { m2m, deletedRows } = todo;
218
+ if (deletedRows.length === 0) return;
219
+ const [haveIds, noIds] = partition(deletedRows, (r) => {
220
+ return r.id !== void 0 && r.id !== -1;
221
+ });
222
+ if (haveIds.length > 0) {
223
+ const pgSql = toPgParams(`DELETE FROM ${kq(joinTableName)} WHERE id = ANY(?)`);
224
+ onQuery?.(pgSql);
225
+ await client.query(pgSql, [haveIds.map((r) => r.id)]);
226
+ }
227
+ if (noIds.length > 0) {
228
+ const data = noIds.filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName)).map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)]);
229
+ if (data.length > 0) {
230
+ const pgSql = toPgParams(`
231
+ DELETE FROM ${kq(joinTableName)}
232
+ WHERE (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) IN (
303
233
  SELECT (data->>0)::int, (data->>1)::int FROM jsonb_array_elements(?) data
304
234
  )`);
305
- onQuery?.(pgSql);
306
- await client.query(pgSql, [JSON.stringify(data)]);
307
- }
308
- }
309
- deletedRows.forEach((row) => {
310
- row.id = undefined;
311
- row.persisted = false;
312
- row.op = joist_core_1.JoinRowOperation.Flushed;
313
- });
235
+ onQuery?.(pgSql);
236
+ await client.query(pgSql, [JSON.stringify(data)]);
237
+ }
238
+ }
239
+ deletedRows.forEach((row) => {
240
+ row.id = void 0;
241
+ row.persisted = false;
242
+ row.op = JoinRowOperation.Flushed;
243
+ });
314
244
  }
315
245
  /**
316
- * Configures the `pg` driver with the best type parsers.
317
- *
318
- * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just
319
- * pulling in the latest `pg-types` and installing the fixed parser makes a noticable
320
- * difference.
321
- */
246
+ * Configures the `pg` driver with the best type parsers.
247
+ *
248
+ * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just
249
+ * pulling in the latest `pg-types` and installing the fixed parser makes a noticable
250
+ * difference.
251
+ */
322
252
  function setupLatestPgTypes(temporal) {
323
- if (temporal) {
324
- // Don't eagerly parse the strings, instead defer to the serde logic. This only governs
325
- // classic *text* results (knex, pool.query, non-lazy drivers); binary lazy cells construct
326
- // Temporal values directly, with no strings involved (see registerTemporalBinaryParsers).
327
- const noop = (s) => s;
328
- const noopArray = (s) => postgres_array_1.default.parse(s, noop);
329
- const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg_1.default.types.builtins;
330
- pg_1.default.types.setTypeParser(DATE, noop);
331
- pg_1.default.types.setTypeParser(TIMESTAMP, noop);
332
- pg_1.default.types.setTypeParser(TIMESTAMPTZ, noop);
333
- // Use `as number` b/c the typings of shadowed pg-types from `pg` and `pg-types` top-level don't line up
334
- pg_1.default.types.setTypeParser(1182, noopArray); // date[]
335
- pg_1.default.types.setTypeParser(1115, noopArray); // timestamp[]
336
- pg_1.default.types.setTypeParser(1185, noopArray); // timestamptz[]
337
- (0, binaryParsers_1.registerTemporalBinaryParsers)();
338
- }
339
- else {
340
- pg_1.default.types.setTypeParser(pg_1.default.types.builtins.TIMESTAMPTZ, (0, pg_types_1.getTypeParser)(pg_types_1.builtins.TIMESTAMPTZ));
341
- // The binary registry's date/timestamp/timestamptz builtins already decode to Dates
342
- }
253
+ if (temporal) {
254
+ const noop = (s) => s;
255
+ const noopArray = (s) => array.parse(s, noop);
256
+ const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg.types.builtins;
257
+ pg.types.setTypeParser(DATE, noop);
258
+ pg.types.setTypeParser(TIMESTAMP, noop);
259
+ pg.types.setTypeParser(TIMESTAMPTZ, noop);
260
+ pg.types.setTypeParser(1182, noopArray);
261
+ pg.types.setTypeParser(1115, noopArray);
262
+ pg.types.setTypeParser(1185, noopArray);
263
+ registerTemporalBinaryParsers();
264
+ } else pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, getTypeParser(builtins.TIMESTAMPTZ));
343
265
  }
344
266
  const questionMarks = /(?<!@)\?/g;
345
267
  /**
346
- * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.
347
- *
348
- * We skipping postgres operators like `@?`. */
268
+ * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.
269
+ *
270
+ * We skipping postgres operators like `@?`. */
349
271
  function toPgParams(sql) {
350
- let i = 0;
351
- return sql.replace(questionMarks, () => `$${++i}`);
272
+ let i = 0;
273
+ return sql.replace(questionMarks, () => `$${++i}`);
352
274
  }
353
275
  let warnedUnsupportedClient = false;
354
276
  /** Warns once when lazyRows degrades to classic rows for an unsupported client, i.e. pg-native. */
355
277
  function warnUnsupportedClientOnce() {
356
- if (warnedUnsupportedClient)
357
- return;
358
- warnedUnsupportedClient = true;
359
- console.warn("joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.");
278
+ if (warnedUnsupportedClient) return;
279
+ warnedUnsupportedClient = true;
280
+ console.warn("joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.");
360
281
  }
282
+ //#endregion
283
+ export { PostgresDriver, setupLatestPgTypes };
284
+
361
285
  //# sourceMappingURL=PostgresDriver.js.map