joist-orm 2.3.0-next.42 → 2.3.0-next.44
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.
- package/build/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/_virtual/_rolldown/runtime.js +5 -0
- package/build/codegen.cjs +19 -0
- package/build/codegen.cjs.map +1 -0
- package/build/codegen.d.cts +1 -0
- package/build/codegen.d.mts +1 -0
- package/build/codegen.js +11 -28
- package/build/codegen.js.map +1 -1
- package/build/drivers/PostgresDriver.cjs +290 -0
- package/build/drivers/PostgresDriver.cjs.map +1 -0
- package/build/drivers/PostgresDriver.d.cts +77 -0
- package/build/drivers/PostgresDriver.d.cts.map +1 -0
- package/build/drivers/PostgresDriver.d.mts +77 -0
- package/build/drivers/PostgresDriver.d.mts.map +1 -0
- package/build/drivers/PostgresDriver.js +249 -325
- package/build/drivers/PostgresDriver.js.map +1 -1
- package/build/drivers/WireRowData.cjs +374 -0
- package/build/drivers/WireRowData.cjs.map +1 -0
- package/build/drivers/WireRowData.d.cts +111 -0
- package/build/drivers/WireRowData.d.cts.map +1 -0
- package/build/drivers/WireRowData.d.mts +111 -0
- package/build/drivers/WireRowData.d.mts.map +1 -0
- package/build/drivers/WireRowData.js +352 -412
- package/build/drivers/WireRowData.js.map +1 -1
- package/build/drivers/binaryParsers.cjs +422 -0
- package/build/drivers/binaryParsers.cjs.map +1 -0
- package/build/drivers/{binaryParsers.d.ts → binaryParsers.d.cts} +18 -14
- package/build/drivers/binaryParsers.d.cts.map +1 -0
- package/build/drivers/binaryParsers.d.mts +80 -0
- package/build/drivers/binaryParsers.d.mts.map +1 -0
- package/build/drivers/binaryParsers.js +315 -385
- package/build/drivers/binaryParsers.js.map +1 -1
- package/build/drivers/patchPgProtocol.cjs +255 -0
- package/build/drivers/patchPgProtocol.cjs.map +1 -0
- package/build/drivers/{patchPgProtocol.d.ts → patchPgProtocol.d.cts} +18 -14
- package/build/drivers/patchPgProtocol.d.cts.map +1 -0
- package/build/drivers/patchPgProtocol.d.mts +47 -0
- package/build/drivers/patchPgProtocol.d.mts.map +1 -0
- package/build/drivers/patchPgProtocol.js +211 -232
- package/build/drivers/patchPgProtocol.js.map +1 -1
- package/build/graphql-codegen-export.cjs +10 -0
- package/build/graphql-codegen-export.d.cts +1 -0
- package/build/graphql-codegen-export.d.mts +1 -0
- package/build/graphql-codegen-export.js +2 -19
- package/build/graphql-export.cjs +10 -0
- package/build/graphql-export.d.cts +1 -0
- package/build/graphql-export.d.mts +1 -0
- package/build/graphql-export.js +2 -19
- package/build/index.cjs +41 -0
- package/build/index.d.cts +3 -0
- package/build/index.d.mts +3 -0
- package/build/index.js +3 -27
- package/build/knex-export.cjs +10 -0
- package/build/knex-export.d.cts +1 -0
- package/build/knex-export.d.mts +1 -0
- package/build/knex-export.js +2 -19
- package/build/pg-export.cjs +13 -0
- package/build/pg-export.d.cts +4 -0
- package/build/pg-export.d.mts +4 -0
- package/build/pg-export.js +4 -20
- package/build/pg-migrate.cjs +22 -0
- package/build/pg-migrate.cjs.map +1 -0
- package/build/pg-migrate.d.cts +1 -0
- package/build/pg-migrate.d.mts +1 -0
- package/build/pg-migrate.js +13 -28
- package/build/pg-migrate.js.map +1 -1
- package/build/seed.cjs +49 -0
- package/build/seed.cjs.map +1 -0
- package/build/{seed.d.ts → seed.d.cts} +5 -1
- package/build/seed.d.cts.map +1 -0
- package/build/seed.d.mts +24 -0
- package/build/seed.d.mts.map +1 -0
- package/build/seed.js +39 -45
- package/build/seed.js.map +1 -1
- package/build/tests-export.cjs +10 -0
- package/build/tests-export.d.cts +1 -0
- package/build/tests-export.d.mts +1 -0
- package/build/tests-export.js +2 -19
- package/package.json +150 -28
- package/build/codegen.d.ts +0 -2
- package/build/drivers/PostgresDriver.d.ts +0 -73
- package/build/drivers/WireRowData.d.ts +0 -107
- package/build/graphql-codegen-export.d.ts +0 -1
- package/build/graphql-codegen-export.js.map +0 -1
- package/build/graphql-export.d.ts +0 -1
- package/build/graphql-export.js.map +0 -1
- package/build/index.d.ts +0 -3
- package/build/index.js.map +0 -1
- package/build/knex-export.d.ts +0 -1
- package/build/knex-export.js.map +0 -1
- package/build/pg-export.d.ts +0 -3
- package/build/pg-export.js.map +0 -1
- package/build/pg-migrate.d.ts +0 -2
- package/build/tests-export.d.ts +0 -1
- package/build/tests-export.js.map +0 -1
|
@@ -1,361 +1,285 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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 ${
|
|
136
|
+
INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(", ")})
|
|
164
137
|
SELECT * FROM data
|
|
165
|
-
`);
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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 ${
|
|
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 ${
|
|
193
|
-
RETURNING ${
|
|
152
|
+
WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}
|
|
153
|
+
RETURNING ${kq(tableName)}.id
|
|
194
154
|
`;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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 (${
|
|
191
|
+
ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id
|
|
243
192
|
RETURNING id;
|
|
244
|
-
`);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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 (${
|
|
262
|
-
`);
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
351
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|