pqb 0.61.13 → 0.62.1
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/dist/index.d.ts +4780 -5231
- package/dist/index.js +1754 -1751
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1749 -1521
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +663 -0
- package/dist/internal.js.map +1 -0
- package/dist/internal.mjs +2 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/node-postgres.d.ts +7 -6
- package/dist/node-postgres.js +43 -27
- package/dist/node-postgres.js.map +1 -1
- package/dist/node-postgres.mjs +38 -22
- package/dist/node-postgres.mjs.map +1 -1
- package/dist/postgres-js.d.ts +7 -6
- package/dist/postgres-js.js +40 -29
- package/dist/postgres-js.js.map +1 -1
- package/dist/postgres-js.mjs +33 -22
- package/dist/postgres-js.mjs.map +1 -1
- package/dist/public.d.ts +1 -0
- package/dist/public.js +35 -0
- package/dist/public.js.map +1 -0
- package/dist/public.mjs +2 -0
- package/dist/public.mjs.map +1 -0
- package/package.json +28 -19
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import url from 'url';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import { addTopCteSql as addTopCteSql$1, addTopCte as addTopCte$1, pushQueryValueImmutable as pushQueryValueImmutable$1, prepareSubQueryForSql as prepareSubQueryForSql$3 } from 'pqb';
|
|
3
|
+
import { addTopCteSql as addTopCteSql$1, addTopCte as addTopCte$1, emptyArray as emptyArray$1, pushQueryValueImmutable as pushQueryValueImmutable$1, prepareSubQueryForSql as prepareSubQueryForSql$3, _clone as _clone$1 } from 'pqb/internal';
|
|
5
4
|
import { inspect } from 'node:util';
|
|
6
5
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
|
+
import { setTimeout } from 'timers/promises';
|
|
7
7
|
|
|
8
8
|
function applyMixins(targetClass, mixinClasses) {
|
|
9
9
|
const target = targetClass.prototype;
|
|
@@ -155,7 +155,7 @@ const getCallerFilePath = (stack = getStackTrace()) => {
|
|
|
155
155
|
} else {
|
|
156
156
|
try {
|
|
157
157
|
file = new URL(file).pathname;
|
|
158
|
-
} catch
|
|
158
|
+
} catch {
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
return file;
|
|
@@ -230,32 +230,7 @@ const colors = {
|
|
|
230
230
|
pale: (s) => `\x1B[2m${s}\x1B[0m`
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
-
const
|
|
234
|
-
adapter.connectRetryConfig = {
|
|
235
|
-
attempts: config.attempts ?? 10,
|
|
236
|
-
strategy: typeof config.strategy === "function" ? config.strategy : defaultConnectRetryStrategy(config.strategy ?? emptyObject)
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
const wrapAdapterFnWithConnectRetry = (adapter, fn) => {
|
|
240
|
-
return async function(...args) {
|
|
241
|
-
let attempt = 1;
|
|
242
|
-
for (; ; ) {
|
|
243
|
-
try {
|
|
244
|
-
return await fn.call(this, ...args);
|
|
245
|
-
} catch (err) {
|
|
246
|
-
const config = adapter.connectRetryConfig;
|
|
247
|
-
if (!err || typeof err !== "object" || err.code !== "ECONNREFUSED" || !config || attempt >= config.attempts) {
|
|
248
|
-
throw err;
|
|
249
|
-
}
|
|
250
|
-
await config.strategy(attempt, config.attempts);
|
|
251
|
-
attempt++;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
};
|
|
256
|
-
const defaultConnectRetryStrategy = (param) => {
|
|
257
|
-
return (attempt) => setTimeout((param.factor ?? 1.5) ** (attempt - 1) * (param.delay ?? 50));
|
|
258
|
-
};
|
|
233
|
+
const snakeCaseKey = Symbol("snakeCase");
|
|
259
234
|
|
|
260
235
|
class Expression {
|
|
261
236
|
// Produce SQL string by calling `makeSQL` and applying operators from the `q.chain`, push query variables into given `values` array.
|
|
@@ -468,6 +443,108 @@ class UnsafeSqlExpression extends Expression {
|
|
|
468
443
|
}
|
|
469
444
|
}
|
|
470
445
|
|
|
446
|
+
const makeIndex = (columns, first, second) => {
|
|
447
|
+
if (typeof first === "string") {
|
|
448
|
+
const options = { ...second, name: first };
|
|
449
|
+
return {
|
|
450
|
+
index: { columns, options }
|
|
451
|
+
};
|
|
452
|
+
} else {
|
|
453
|
+
const options = first ?? {};
|
|
454
|
+
return {
|
|
455
|
+
index: { columns, options }
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const tableDataMethods = {
|
|
460
|
+
primaryKey(columns, name) {
|
|
461
|
+
return { primaryKey: { columns, name } };
|
|
462
|
+
},
|
|
463
|
+
unique(columns, ...args) {
|
|
464
|
+
const [first, second] = args;
|
|
465
|
+
const input = makeIndex(columns, first, second);
|
|
466
|
+
input.index.options.unique = true;
|
|
467
|
+
return input;
|
|
468
|
+
},
|
|
469
|
+
index: makeIndex,
|
|
470
|
+
searchIndex(columns, ...args) {
|
|
471
|
+
var _a;
|
|
472
|
+
const [first, second] = args;
|
|
473
|
+
const input = makeIndex(columns, first, second);
|
|
474
|
+
(_a = input.index.options).using ?? (_a.using = "gin");
|
|
475
|
+
input.index.options.tsVector = true;
|
|
476
|
+
return input;
|
|
477
|
+
},
|
|
478
|
+
exclude(columns, ...args) {
|
|
479
|
+
const [first, second] = args;
|
|
480
|
+
if (typeof first === "string") {
|
|
481
|
+
const options = second ?? {};
|
|
482
|
+
return {
|
|
483
|
+
exclude: { columns, options: { ...options, name: first } }
|
|
484
|
+
};
|
|
485
|
+
} else {
|
|
486
|
+
const options = first ?? {};
|
|
487
|
+
return {
|
|
488
|
+
exclude: { columns, options }
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
foreignKey(columns, fnOrTable, foreignColumns, options) {
|
|
493
|
+
return {
|
|
494
|
+
constraint: {
|
|
495
|
+
name: options?.name,
|
|
496
|
+
references: { columns, fnOrTable, foreignColumns, options }
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
},
|
|
500
|
+
check(check, name) {
|
|
501
|
+
return { constraint: { check, name } };
|
|
502
|
+
},
|
|
503
|
+
sql: sqlFn
|
|
504
|
+
};
|
|
505
|
+
const parseTableData = (dataFn) => {
|
|
506
|
+
const tableData = {};
|
|
507
|
+
if (dataFn) {
|
|
508
|
+
const input = dataFn(tableDataMethods);
|
|
509
|
+
if (Array.isArray(input)) {
|
|
510
|
+
for (const item of input) {
|
|
511
|
+
parseTableDataInput(tableData, item);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
parseTableDataInput(tableData, input);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return tableData;
|
|
518
|
+
};
|
|
519
|
+
const parseTableDataInput = (tableData, item) => {
|
|
520
|
+
if (item.primaryKey) {
|
|
521
|
+
tableData.primaryKey = item.primaryKey;
|
|
522
|
+
} else if (item.index) {
|
|
523
|
+
(tableData.indexes ?? (tableData.indexes = [])).push(
|
|
524
|
+
parseIndexOrExclude(item.index)
|
|
525
|
+
);
|
|
526
|
+
} else if (item.exclude) {
|
|
527
|
+
(tableData.excludes ?? (tableData.excludes = [])).push(
|
|
528
|
+
parseIndexOrExclude(item.exclude)
|
|
529
|
+
);
|
|
530
|
+
} else if (item.constraint) {
|
|
531
|
+
(tableData.constraints ?? (tableData.constraints = [])).push(item.constraint);
|
|
532
|
+
if (item.constraint.references?.options?.dropMode) {
|
|
533
|
+
item.constraint.dropMode = item.constraint.references.options.dropMode;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
const parseIndexOrExclude = (item) => {
|
|
538
|
+
for (let i = item.columns.length - 1; i >= 0; i--) {
|
|
539
|
+
if (typeof item.columns[i] === "string") {
|
|
540
|
+
item.columns[i] = {
|
|
541
|
+
column: item.columns[i]
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return item;
|
|
546
|
+
};
|
|
547
|
+
|
|
471
548
|
function makeColumnNullable(column, inputSchema, outputSchema, querySchema) {
|
|
472
549
|
const c = setColumnData(column, "isNullable", true);
|
|
473
550
|
c.inputSchema = inputSchema;
|
|
@@ -512,9 +589,6 @@ const setDefaultNowFn = (sql) => {
|
|
|
512
589
|
currentNowFn = sql;
|
|
513
590
|
};
|
|
514
591
|
const getDefaultNowFn = () => currentNowFn;
|
|
515
|
-
const resetDefaultNowFn = () => {
|
|
516
|
-
currentNowFn = defaultNowFn;
|
|
517
|
-
};
|
|
518
592
|
let defaultLanguage = "english";
|
|
519
593
|
const setDefaultLanguage = (lang) => {
|
|
520
594
|
defaultLanguage = lang || "english";
|
|
@@ -1463,7 +1537,6 @@ const excludeInnerToCode = (item, t) => {
|
|
|
1463
1537
|
const excludeToCode = indexOrExcludeToCode(excludeInnerToCode);
|
|
1464
1538
|
const constraintToCode = (item, t, m, prefix) => {
|
|
1465
1539
|
const code = constraintInnerToCode(item, t, m);
|
|
1466
|
-
if (prefix) code[0] = prefix + code[0];
|
|
1467
1540
|
const last = code[code.length - 1];
|
|
1468
1541
|
if (typeof last === "string" && !last.endsWith(","))
|
|
1469
1542
|
code[code.length - 1] += ",";
|
|
@@ -1845,7 +1918,7 @@ const make = (_op) => {
|
|
|
1845
1918
|
// function to turn the operator expression into SQL
|
|
1846
1919
|
_op
|
|
1847
1920
|
}
|
|
1848
|
-
//
|
|
1921
|
+
//
|
|
1849
1922
|
);
|
|
1850
1923
|
};
|
|
1851
1924
|
const makeVarArg = (_op) => {
|
|
@@ -1867,7 +1940,7 @@ const makeVarArg = (_op) => {
|
|
|
1867
1940
|
// function to turn the operator expression into SQL
|
|
1868
1941
|
_op
|
|
1869
1942
|
}
|
|
1870
|
-
//
|
|
1943
|
+
//
|
|
1871
1944
|
);
|
|
1872
1945
|
};
|
|
1873
1946
|
const prepareOpArg = (q, arg) => {
|
|
@@ -3189,10 +3262,6 @@ const typmodType = (typmod) => {
|
|
|
3189
3262
|
const typmodSrid = (typmod) => {
|
|
3190
3263
|
return typmod < 0 ? 0 : (typmod & 268435200) - (typmod & 268435456) >> 8;
|
|
3191
3264
|
};
|
|
3192
|
-
const postgisTypmodToSql = (typmod) => {
|
|
3193
|
-
const srid = typmodSrid(typmod);
|
|
3194
|
-
return typmodType(typmod) + (srid === defaultSrid ? "" : ", " + srid);
|
|
3195
|
-
};
|
|
3196
3265
|
let byteToHex;
|
|
3197
3266
|
function uint8ArrToHex(arr) {
|
|
3198
3267
|
if (!byteToHex) {
|
|
@@ -3418,8 +3487,6 @@ const anyShape = new Proxy(emptyObject, {
|
|
|
3418
3487
|
}
|
|
3419
3488
|
});
|
|
3420
3489
|
|
|
3421
|
-
const snakeCaseKey = Symbol("snakeCase");
|
|
3422
|
-
|
|
3423
3490
|
const pushQueryValueImmutable = (q, key, value) => {
|
|
3424
3491
|
pushOrNewArrayToObjectImmutable(
|
|
3425
3492
|
q.q,
|
|
@@ -3446,447 +3513,354 @@ class QueryClone {
|
|
|
3446
3513
|
}
|
|
3447
3514
|
}
|
|
3448
3515
|
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3516
|
+
const before = (q, key, cb) => pushQueryValueImmutable(q, `before${key}`, cb);
|
|
3517
|
+
const after = (query, key, select, cb, commit) => {
|
|
3518
|
+
const q = query;
|
|
3519
|
+
pushQueryValueImmutable(
|
|
3520
|
+
q,
|
|
3521
|
+
`after${key}${commit ? "Commit" : ""}`,
|
|
3522
|
+
cb
|
|
3523
|
+
);
|
|
3524
|
+
if (key === "Save") {
|
|
3525
|
+
addSelect(q, "Create", select);
|
|
3526
|
+
addSelect(q, "Update", select);
|
|
3527
|
+
} else {
|
|
3528
|
+
addSelect(q, key, select);
|
|
3529
|
+
}
|
|
3530
|
+
return query;
|
|
3456
3531
|
};
|
|
3457
|
-
const
|
|
3458
|
-
const
|
|
3459
|
-
const
|
|
3460
|
-
|
|
3532
|
+
const addSelect = (q, key, select) => {
|
|
3533
|
+
const prop = `after${key}Select`;
|
|
3534
|
+
const set = q.q[prop] = new Set(q.q[prop]);
|
|
3535
|
+
for (const column of select) {
|
|
3536
|
+
set.add(column);
|
|
3537
|
+
}
|
|
3461
3538
|
};
|
|
3462
|
-
const
|
|
3463
|
-
return q
|
|
3539
|
+
const _queryHookBeforeQuery = (q, cb) => {
|
|
3540
|
+
return pushQueryValueImmutable(q, "before", cb);
|
|
3464
3541
|
};
|
|
3465
|
-
const
|
|
3466
|
-
return q
|
|
3542
|
+
const _queryHookAfterQuery = (q, cb) => {
|
|
3543
|
+
return pushQueryValueImmutable(q, "after", cb);
|
|
3467
3544
|
};
|
|
3468
|
-
const
|
|
3469
|
-
const
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3545
|
+
const _hookSelectColumns = (query, columns, asFn) => {
|
|
3546
|
+
const hookSelect = query.q.hookSelect = new Map(
|
|
3547
|
+
query.q.hookSelect && [...query.q.hookSelect]
|
|
3548
|
+
);
|
|
3549
|
+
const aliases = [];
|
|
3550
|
+
const addAlias = (as) => {
|
|
3551
|
+
aliases.push(as);
|
|
3552
|
+
if (aliases.length === columns.length) {
|
|
3553
|
+
asFn(aliases);
|
|
3554
|
+
}
|
|
3474
3555
|
};
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
};
|
|
3483
|
-
const _applyRelationAliases = (query, relQueryData) => {
|
|
3484
|
-
const aliases = query.q.as ? { ...query.q.aliases } : { ...query.q.aliases, [query.table]: query.table };
|
|
3485
|
-
const relAliases = relQueryData.aliases;
|
|
3486
|
-
for (const as in relAliases) {
|
|
3487
|
-
aliases[as] = getFreeAlias(aliases, as);
|
|
3488
|
-
}
|
|
3489
|
-
relQueryData.as = aliases[relQueryData.as];
|
|
3490
|
-
relQueryData.aliases = aliases;
|
|
3491
|
-
};
|
|
3492
|
-
const _copyQueryAliasToQuery = (fromQuery, toQuery, key) => {
|
|
3493
|
-
const name = _getQueryAliasOrName(fromQuery.q, key);
|
|
3494
|
-
if (name !== key) {
|
|
3495
|
-
_setQueryAlias(toQuery, name, key);
|
|
3556
|
+
for (const column of columns) {
|
|
3557
|
+
const item = hookSelect.get(column);
|
|
3558
|
+
hookSelect.set(column, {
|
|
3559
|
+
...item,
|
|
3560
|
+
select: column,
|
|
3561
|
+
onAs: [...item?.onAs || emptyArray, addAlias]
|
|
3562
|
+
});
|
|
3496
3563
|
}
|
|
3497
|
-
return name;
|
|
3498
3564
|
};
|
|
3499
|
-
class
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
return _setQueryAs(_clone(this), as);
|
|
3565
|
+
class QueryHookUtils {
|
|
3566
|
+
constructor(query, columns, key) {
|
|
3567
|
+
this.query = query;
|
|
3568
|
+
this.columns = columns;
|
|
3569
|
+
this.key = key;
|
|
3570
|
+
this.set = (data) => {
|
|
3571
|
+
const set = {};
|
|
3572
|
+
for (const key in data) {
|
|
3573
|
+
if (data[key] !== void 0) {
|
|
3574
|
+
set[key] = data[key];
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
pushQueryValueImmutable(this.query, this.key, set);
|
|
3578
|
+
};
|
|
3514
3579
|
}
|
|
3515
3580
|
}
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3581
|
+
const finalizeNestedHookSelect = (batches, returnType, tempColumns, renames, key) => {
|
|
3582
|
+
if (renames) {
|
|
3583
|
+
for (const { data } of batches) {
|
|
3584
|
+
for (const record of data) {
|
|
3585
|
+
if (record) {
|
|
3586
|
+
for (const a in renames) {
|
|
3587
|
+
record[a] = record[renames[a]];
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3525
3591
|
}
|
|
3526
3592
|
}
|
|
3527
|
-
|
|
3528
|
-
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
3536
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
3537
|
-
var _query, _query2, _columnsCache;
|
|
3538
|
-
class OrchidOrmError extends Error {
|
|
3539
|
-
}
|
|
3540
|
-
class NotFoundError extends OrchidOrmError {
|
|
3541
|
-
constructor(query, message = "Record is not found") {
|
|
3542
|
-
super(message);
|
|
3543
|
-
// `#query` is private to prevent it from serializing to not cause problems to test runner reports
|
|
3544
|
-
// it is exposed with `getQuery` method which prevents this problem from both Vitest and Jest.
|
|
3545
|
-
// Exposing it with `get query()` still leaves the issue in Vitest.
|
|
3546
|
-
__privateAdd(this, _query);
|
|
3547
|
-
__privateSet(this, _query, query);
|
|
3548
|
-
}
|
|
3549
|
-
getQuery() {
|
|
3550
|
-
return __privateGet(this, _query);
|
|
3551
|
-
}
|
|
3552
|
-
}
|
|
3553
|
-
_query = new WeakMap();
|
|
3554
|
-
class OrchidOrmInternalError extends Error {
|
|
3555
|
-
constructor(query, message, data) {
|
|
3556
|
-
super(message);
|
|
3557
|
-
this.data = data;
|
|
3558
|
-
// `#query` is private to prevent it from serializing to not cause problems to test runner reports
|
|
3559
|
-
// it is exposed with `getQuery` method which prevents this problem from both Vitest and Jest.
|
|
3560
|
-
// Exposing it with `get query()` still leaves the issue in Vitest.
|
|
3561
|
-
__privateAdd(this, _query2);
|
|
3562
|
-
__privateSet(this, _query2, query);
|
|
3563
|
-
}
|
|
3564
|
-
getQuery() {
|
|
3565
|
-
return __privateGet(this, _query2);
|
|
3566
|
-
}
|
|
3567
|
-
}
|
|
3568
|
-
_query2 = new WeakMap();
|
|
3569
|
-
class QueryError extends OrchidOrmInternalError {
|
|
3570
|
-
constructor() {
|
|
3571
|
-
super(...arguments);
|
|
3572
|
-
__privateAdd(this, _columnsCache);
|
|
3573
|
-
}
|
|
3574
|
-
get isUnique() {
|
|
3575
|
-
return this.code === "23505";
|
|
3576
|
-
}
|
|
3577
|
-
get columns() {
|
|
3578
|
-
if (__privateGet(this, _columnsCache)) return __privateGet(this, _columnsCache);
|
|
3579
|
-
const columns = {};
|
|
3580
|
-
if (this.detail) {
|
|
3581
|
-
const list = this.detail.match(/\((.*)\)=/)?.[1];
|
|
3582
|
-
if (list) {
|
|
3583
|
-
list.split(", ").forEach((item) => {
|
|
3584
|
-
const column = item.startsWith('"') ? item.slice(1, -1) : item;
|
|
3585
|
-
const key = queryColumnNameToKey(this.getQuery(), column) ?? column;
|
|
3586
|
-
columns[key] = true;
|
|
3587
|
-
});
|
|
3593
|
+
if (tempColumns?.size) {
|
|
3594
|
+
for (const { data } of batches) {
|
|
3595
|
+
for (const record of data) {
|
|
3596
|
+
if (record) {
|
|
3597
|
+
for (const key2 of tempColumns) {
|
|
3598
|
+
delete record[key2];
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3588
3601
|
}
|
|
3589
3602
|
}
|
|
3590
|
-
return __privateSet(this, _columnsCache, columns);
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3593
|
-
_columnsCache = new WeakMap();
|
|
3594
|
-
class MoreThanOneRowError extends OrchidOrmInternalError {
|
|
3595
|
-
constructor(query, message) {
|
|
3596
|
-
super(query, message);
|
|
3597
3603
|
}
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3604
|
+
if (returnType === "one" || returnType === "oneOrThrow") {
|
|
3605
|
+
for (const batch of batches) {
|
|
3606
|
+
batch.data = batch.data[0];
|
|
3607
|
+
}
|
|
3608
|
+
} else if (returnType === "pluck") {
|
|
3609
|
+
for (const { data } of batches) {
|
|
3610
|
+
for (let i = 0; i < data.length; i++) {
|
|
3611
|
+
data[i] = data[i][key];
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
} else if (returnType === "value" || returnType === "valueOrThrow") {
|
|
3615
|
+
for (const item of batches) {
|
|
3616
|
+
item.parent[item.key] = item.data[0]?.[key];
|
|
3617
|
+
}
|
|
3602
3618
|
}
|
|
3603
|
-
}
|
|
3604
|
-
|
|
3605
|
-
const pushQueryArrayImmutable = (q, key, value) => {
|
|
3606
|
-
const arr = q.q[key];
|
|
3607
|
-
q.q[key] = arr ? [...arr, ...value] : value;
|
|
3608
|
-
return q;
|
|
3609
3619
|
};
|
|
3610
|
-
const
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3620
|
+
const _queryHookBeforeCreate = (q, cb) => {
|
|
3621
|
+
return before(
|
|
3622
|
+
q,
|
|
3623
|
+
"Create",
|
|
3624
|
+
(q2) => cb(new QueryHookUtils(q2, q2.q.columns, "hookCreateSet"))
|
|
3625
|
+
);
|
|
3616
3626
|
};
|
|
3617
|
-
const
|
|
3618
|
-
|
|
3619
|
-
throw new OrchidOrmInternalError(
|
|
3620
|
-
q,
|
|
3621
|
-
`Dangerous ${method} without conditions`
|
|
3622
|
-
);
|
|
3623
|
-
}
|
|
3627
|
+
const _queryHookAfterCreate = (q, select, cb) => {
|
|
3628
|
+
return after(q, "Create", select, cb);
|
|
3624
3629
|
};
|
|
3625
|
-
const
|
|
3626
|
-
|
|
3627
|
-
throw new OrchidOrmInternalError(
|
|
3628
|
-
q,
|
|
3629
|
-
`Cannot join a complex query in ${method}`
|
|
3630
|
-
);
|
|
3631
|
-
}
|
|
3630
|
+
const _queryHookAfterCreateCommit = (q, select, cb) => {
|
|
3631
|
+
return after(q, "Create", select, cb, true);
|
|
3632
3632
|
};
|
|
3633
|
-
const
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3633
|
+
const _queryHookBeforeUpdate = (q, cb) => {
|
|
3634
|
+
return before(q, "Update", (q2) => {
|
|
3635
|
+
const columns = /* @__PURE__ */ new Set();
|
|
3636
|
+
if (q2.q.updateData) {
|
|
3637
|
+
for (const item of q2.q.updateData) {
|
|
3638
|
+
if (typeof item === "object") {
|
|
3639
|
+
for (const key in item) {
|
|
3640
|
+
columns.add(key);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
if (q2.q.updateMany) {
|
|
3646
|
+
for (const key of q2.q.updateMany.setColumns) {
|
|
3647
|
+
columns.add(key);
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
return cb(new QueryHookUtils(q2, [...columns], "hookUpdateSet"));
|
|
3651
|
+
});
|
|
3641
3652
|
};
|
|
3642
|
-
const
|
|
3643
|
-
|
|
3644
|
-
as = getFreeAlias(shapes, as);
|
|
3645
|
-
setQueryObjectValueImmutable(q, key, as, emptyObject);
|
|
3646
|
-
return as;
|
|
3653
|
+
const _queryHookAfterUpdate = (q, select, cb) => {
|
|
3654
|
+
return after(q, "Update", select, cb);
|
|
3647
3655
|
};
|
|
3648
|
-
const
|
|
3649
|
-
|
|
3650
|
-
base.baseQuery = base;
|
|
3651
|
-
Object.assign(base, methods);
|
|
3652
|
-
const cloned = Object.create(base);
|
|
3653
|
-
cloned.q = getClonedQueryData(q.q);
|
|
3654
|
-
return cloned;
|
|
3656
|
+
const _queryHookAfterUpdateCommit = (q, select, cb) => {
|
|
3657
|
+
return after(q, "Update", select, cb, true);
|
|
3655
3658
|
};
|
|
3656
|
-
const
|
|
3657
|
-
q
|
|
3658
|
-
q.q.all = true;
|
|
3659
|
-
return q;
|
|
3659
|
+
const _queryHookBeforeSave = (q, cb) => {
|
|
3660
|
+
return _queryHookBeforeUpdate(_queryHookBeforeCreate(q, cb), cb);
|
|
3660
3661
|
};
|
|
3661
|
-
const
|
|
3662
|
-
|
|
3663
|
-
switch (q.returnType) {
|
|
3664
|
-
case "valueOrThrow":
|
|
3665
|
-
case "pluck":
|
|
3666
|
-
case "void":
|
|
3667
|
-
break;
|
|
3668
|
-
case "value": {
|
|
3669
|
-
q.returnType = "valueOrThrow";
|
|
3670
|
-
break;
|
|
3671
|
-
}
|
|
3672
|
-
default: {
|
|
3673
|
-
q.returnType = "oneOrThrow";
|
|
3674
|
-
}
|
|
3675
|
-
}
|
|
3676
|
-
return query;
|
|
3662
|
+
const _queryHookAfterSave = (q, select, cb) => {
|
|
3663
|
+
return after(q, "Save", select, cb);
|
|
3677
3664
|
};
|
|
3678
|
-
const
|
|
3679
|
-
|
|
3680
|
-
switch (q.returnType) {
|
|
3681
|
-
case "value":
|
|
3682
|
-
case "pluck":
|
|
3683
|
-
case "void":
|
|
3684
|
-
break;
|
|
3685
|
-
case "valueOrThrow": {
|
|
3686
|
-
q.returnType = "value";
|
|
3687
|
-
break;
|
|
3688
|
-
}
|
|
3689
|
-
default: {
|
|
3690
|
-
q.returnType = "one";
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
return query;
|
|
3665
|
+
const _queryAfterSaveCommit = (q, select, cb) => {
|
|
3666
|
+
return after(q, "Save", select, cb, true);
|
|
3694
3667
|
};
|
|
3695
|
-
const
|
|
3696
|
-
q
|
|
3697
|
-
return q;
|
|
3668
|
+
const _queryHookBeforeDelete = (q, cb) => {
|
|
3669
|
+
return before(q, "Delete", cb);
|
|
3698
3670
|
};
|
|
3699
|
-
const
|
|
3700
|
-
q
|
|
3701
|
-
return q;
|
|
3671
|
+
const _queryHookAfterDelete = (q, select, cb) => {
|
|
3672
|
+
return after(q, "Delete", select, cb);
|
|
3702
3673
|
};
|
|
3703
|
-
const
|
|
3704
|
-
|
|
3705
|
-
return as && table !== as && _checkIfAliased(q, table, as) ? as : table;
|
|
3674
|
+
const _queryHookAfterDeleteCommit = (q, select, cb) => {
|
|
3675
|
+
return after(q, "Delete", select, cb, true);
|
|
3706
3676
|
};
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3677
|
+
class QueryHooks {
|
|
3678
|
+
/**
|
|
3679
|
+
* Run the function before any kind of query.
|
|
3680
|
+
*
|
|
3681
|
+
* @param cb - function to call, first argument is a query object
|
|
3682
|
+
*/
|
|
3683
|
+
beforeQuery(cb) {
|
|
3684
|
+
return _queryHookBeforeQuery(_clone(this), cb);
|
|
3712
3685
|
}
|
|
3713
|
-
let beforeAction = subQuery.q.type ? subQuery.q.type === "insert" ? subQuery.q.beforeCreate : subQuery.q.type === "update" ? subQuery.q.beforeUpdate : subQuery.q.type === "upsert" ? subQuery.q.upsertUpdate && subQuery.q.updateData ? subQuery.q.beforeUpdate && subQuery.q.beforeCreate ? [...subQuery.q.beforeUpdate, ...subQuery.q.beforeCreate] : subQuery.q.beforeUpdate || subQuery.q.beforeCreate : subQuery.q.beforeCreate : subQuery.q.type === "delete" ? subQuery.q.beforeDelete : void 0 : void 0;
|
|
3714
|
-
const { beforeSet } = subQuery.q;
|
|
3715
|
-
beforeAction = beforeAction && beforeSet ? [...beforeAction, ...beforeSet] : beforeSet ? [...beforeSet] : beforeAction;
|
|
3716
|
-
if (beforeAction) {
|
|
3717
|
-
const newSet = new Set(mainQuery.q.beforeSet);
|
|
3718
|
-
const filteredHooks = [];
|
|
3719
|
-
for (const hook of beforeAction) {
|
|
3720
|
-
if (!newSet.has(hook)) {
|
|
3721
|
-
newSet.add(hook);
|
|
3722
|
-
filteredHooks.push(hook);
|
|
3723
|
-
}
|
|
3724
|
-
}
|
|
3725
|
-
mainQuery.q.beforeSet = newSet;
|
|
3726
|
-
beforeAction = filteredHooks;
|
|
3727
|
-
if (beforeAction.length) {
|
|
3728
|
-
pushQueryArrayImmutable(
|
|
3729
|
-
mainQuery,
|
|
3730
|
-
"before",
|
|
3731
|
-
beforeAction.map((fn) => () => fn(subQuery))
|
|
3732
|
-
);
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
return subQuery;
|
|
3736
|
-
};
|
|
3737
|
-
setPrepareSubQueryForSql(prepareSubQueryForSql);
|
|
3738
|
-
setRawSqlPrepareSubQueryForSql(prepareSubQueryForSql);
|
|
3739
|
-
|
|
3740
|
-
const _queryUnion = (base, args, k, p, m) => {
|
|
3741
|
-
const query = base.baseQuery.clone();
|
|
3742
|
-
const u = args.map(
|
|
3743
|
-
(a) => ({
|
|
3744
|
-
a: prepareSubQueryForSql(
|
|
3745
|
-
base,
|
|
3746
|
-
typeof a === "function" ? a(query) : a
|
|
3747
|
-
),
|
|
3748
|
-
k,
|
|
3749
|
-
m
|
|
3750
|
-
})
|
|
3751
|
-
);
|
|
3752
|
-
const { q } = query;
|
|
3753
|
-
const baseQ = base.q;
|
|
3754
|
-
q.union = baseQ.union ? {
|
|
3755
|
-
...baseQ.union,
|
|
3756
|
-
u: [...baseQ.union.u, ...u]
|
|
3757
|
-
} : {
|
|
3758
|
-
b: base,
|
|
3759
|
-
u,
|
|
3760
|
-
p
|
|
3761
|
-
};
|
|
3762
|
-
return query;
|
|
3763
|
-
};
|
|
3764
|
-
class Union {
|
|
3765
3686
|
/**
|
|
3766
|
-
*
|
|
3767
|
-
*
|
|
3768
|
-
*
|
|
3769
|
-
* import { sql } from './baseTable';
|
|
3770
|
-
*
|
|
3771
|
-
* // The first query of the union
|
|
3772
|
-
* db.one
|
|
3773
|
-
* .select('id', 'name')
|
|
3774
|
-
* // add two more queries to the union
|
|
3775
|
-
* .union(
|
|
3776
|
-
* db.two.select('id', 'name'),
|
|
3777
|
-
* (q = sql`SELECT id, name FROM "thirdTable"`),
|
|
3778
|
-
* )
|
|
3779
|
-
* // sub-sequent `union` is equivalent to passing multiple queries into a single `union`
|
|
3780
|
-
* .union(db.three.select('id', 'name'));
|
|
3781
|
-
* ```
|
|
3687
|
+
* Run the function after any kind of query.
|
|
3688
|
+
* Enforces wrapping the query into a transaction.
|
|
3689
|
+
* The function will run after the query is succeeded, but before the transaction commit.
|
|
3782
3690
|
*
|
|
3783
|
-
*
|
|
3691
|
+
* @param cb - function to call, first argument is the query result of type `unknown`, second argument is a query object
|
|
3692
|
+
*/
|
|
3693
|
+
afterQuery(cb) {
|
|
3694
|
+
return _queryHookAfterQuery(_clone(this), cb);
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* Run the function before a `create` kind of query.
|
|
3784
3698
|
*
|
|
3785
|
-
*
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
*
|
|
3792
|
-
*
|
|
3793
|
-
*
|
|
3794
|
-
*
|
|
3795
|
-
* .limit(3)
|
|
3796
|
-
* .offset(3);
|
|
3797
|
-
* ```
|
|
3699
|
+
* @param cb - function to call, first argument is a query object
|
|
3700
|
+
*/
|
|
3701
|
+
beforeCreate(cb) {
|
|
3702
|
+
return _queryHookBeforeCreate(_clone(this), cb);
|
|
3703
|
+
}
|
|
3704
|
+
/**
|
|
3705
|
+
* Run the function after a `create` kind of query.
|
|
3706
|
+
* Enforces wrapping the query into a transaction.
|
|
3707
|
+
* The function will run after the query is succeeded, but before the transaction commit.
|
|
3708
|
+
* Queries inside the function will run in the same transaction as the target query.
|
|
3798
3709
|
*
|
|
3799
|
-
*
|
|
3710
|
+
* @param select - list of columns to select for the hook
|
|
3711
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3712
|
+
*/
|
|
3713
|
+
afterCreate(select, cb) {
|
|
3714
|
+
return _queryHookAfterCreate(_clone(this), select, cb);
|
|
3715
|
+
}
|
|
3716
|
+
/**
|
|
3717
|
+
* Run the function after transaction for a `create` kind of query will be committed.
|
|
3718
|
+
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
3800
3719
|
*
|
|
3801
|
-
*
|
|
3802
|
-
*
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3720
|
+
* @param select - list of columns to select for the hook
|
|
3721
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3722
|
+
*/
|
|
3723
|
+
afterCreateCommit(select, cb) {
|
|
3724
|
+
return _queryHookAfterCreateCommit(
|
|
3725
|
+
_clone(this),
|
|
3726
|
+
select,
|
|
3727
|
+
cb
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
/**
|
|
3731
|
+
* Run the function before an `update` kind of query.
|
|
3809
3732
|
*
|
|
3810
|
-
*
|
|
3733
|
+
* @param cb - function to call, first argument is a query object
|
|
3734
|
+
*/
|
|
3735
|
+
beforeUpdate(cb) {
|
|
3736
|
+
return _queryHookBeforeUpdate(_clone(this), cb);
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Run the function after an `update` kind of query.
|
|
3740
|
+
* Enforces wrapping the query into a transaction.
|
|
3741
|
+
* The function will run after the query is succeeded, but before the transaction commit.
|
|
3742
|
+
* Queries inside the function will run in the same transaction as the target query.
|
|
3743
|
+
* If no records were updated, the hook *won't* run.
|
|
3811
3744
|
*
|
|
3812
|
-
*
|
|
3813
|
-
*
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3745
|
+
* @param select - list of columns to select for the hook
|
|
3746
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3747
|
+
*/
|
|
3748
|
+
afterUpdate(select, cb) {
|
|
3749
|
+
return _queryHookAfterUpdate(_clone(this), select, cb);
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* Run the function after transaction for an `update` kind of query will be committed.
|
|
3753
|
+
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
3754
|
+
* If no records were updated, the hook *won't* run.
|
|
3818
3755
|
*
|
|
3819
|
-
* @param
|
|
3756
|
+
* @param select - list of columns to select for the hook
|
|
3757
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3820
3758
|
*/
|
|
3821
|
-
|
|
3822
|
-
return
|
|
3759
|
+
afterUpdateCommit(select, cb) {
|
|
3760
|
+
return _queryHookAfterUpdateCommit(
|
|
3823
3761
|
_clone(this),
|
|
3824
|
-
|
|
3825
|
-
|
|
3762
|
+
select,
|
|
3763
|
+
cb
|
|
3826
3764
|
);
|
|
3827
3765
|
}
|
|
3828
3766
|
/**
|
|
3829
|
-
*
|
|
3767
|
+
* Run the function before a `create` or an `update` kind of query.
|
|
3830
3768
|
*
|
|
3831
|
-
* @param
|
|
3769
|
+
* @param cb - function to call, first argument is a query object
|
|
3832
3770
|
*/
|
|
3833
|
-
|
|
3834
|
-
return
|
|
3835
|
-
_clone(this),
|
|
3836
|
-
args,
|
|
3837
|
-
"UNION ALL"
|
|
3838
|
-
);
|
|
3771
|
+
beforeSave(cb) {
|
|
3772
|
+
return _queryHookBeforeSave(_clone(this), cb);
|
|
3839
3773
|
}
|
|
3840
3774
|
/**
|
|
3841
|
-
*
|
|
3775
|
+
* Run the function after a `create` or an `update` kind of query.
|
|
3776
|
+
* Enforces wrapping the query into a transaction.
|
|
3777
|
+
* The function will run after the query is succeeded, but before the transaction commit.
|
|
3778
|
+
* Queries inside the function will run in the same transaction as the target query.
|
|
3779
|
+
* For the `update` query, if no records were updated, the hook *won't* run.
|
|
3842
3780
|
*
|
|
3843
|
-
* @param
|
|
3781
|
+
* @param select - list of columns to select for the hook
|
|
3782
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3844
3783
|
*/
|
|
3845
|
-
|
|
3846
|
-
return
|
|
3847
|
-
_clone(this),
|
|
3848
|
-
args,
|
|
3849
|
-
"INTERSECT"
|
|
3850
|
-
);
|
|
3784
|
+
afterSave(select, cb) {
|
|
3785
|
+
return _queryHookAfterSave(_clone(this), select, cb);
|
|
3851
3786
|
}
|
|
3852
3787
|
/**
|
|
3853
|
-
*
|
|
3788
|
+
* Run the function after transaction for a `create` or an `update` kind of query will be committed.
|
|
3789
|
+
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
3790
|
+
* For the `update` query, if no records were updated, the hook *won't* run.
|
|
3854
3791
|
*
|
|
3855
|
-
* @param
|
|
3792
|
+
* @param select - list of columns to select for the hook
|
|
3793
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3856
3794
|
*/
|
|
3857
|
-
|
|
3858
|
-
return
|
|
3859
|
-
_clone(this),
|
|
3860
|
-
args,
|
|
3861
|
-
"INTERSECT ALL"
|
|
3862
|
-
);
|
|
3795
|
+
afterSaveCommit(select, cb) {
|
|
3796
|
+
return _queryAfterSaveCommit(_clone(this), select, cb);
|
|
3863
3797
|
}
|
|
3864
3798
|
/**
|
|
3865
|
-
*
|
|
3799
|
+
* Run the function before a `delete` kind of query.
|
|
3866
3800
|
*
|
|
3867
|
-
* @param
|
|
3801
|
+
* @param cb - function to call, first argument is a query object
|
|
3868
3802
|
*/
|
|
3869
|
-
|
|
3870
|
-
return
|
|
3871
|
-
_clone(this),
|
|
3872
|
-
args,
|
|
3873
|
-
"EXCEPT"
|
|
3874
|
-
);
|
|
3803
|
+
beforeDelete(cb) {
|
|
3804
|
+
return _queryHookBeforeDelete(_clone(this), cb);
|
|
3875
3805
|
}
|
|
3876
3806
|
/**
|
|
3877
|
-
*
|
|
3807
|
+
* Run the function after a `delete` kind of query.
|
|
3808
|
+
* Enforces wrapping the query into a transaction.
|
|
3809
|
+
* The function will run after the query is succeeded, but before the transaction commit.
|
|
3810
|
+
* Queries inside the function will run in the same transaction as the target query.
|
|
3811
|
+
* If no records were deleted, the hook *won't* run.
|
|
3878
3812
|
*
|
|
3879
|
-
* @param
|
|
3813
|
+
* @param select - list of columns to select for the hook
|
|
3814
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3880
3815
|
*/
|
|
3881
|
-
|
|
3882
|
-
return
|
|
3816
|
+
afterDelete(select, cb) {
|
|
3817
|
+
return _queryHookAfterDelete(_clone(this), select, cb);
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* Run the function after transaction for a `delete` kind of query will be committed.
|
|
3821
|
+
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
3822
|
+
* If no records were deleted, the hook *won't* run.
|
|
3823
|
+
*
|
|
3824
|
+
* @param select - list of columns to select for the hook
|
|
3825
|
+
* @param cb - function to call, first argument is the query result with selected columns, second argument is a query object
|
|
3826
|
+
*/
|
|
3827
|
+
afterDeleteCommit(select, cb) {
|
|
3828
|
+
return _queryHookAfterDeleteCommit(
|
|
3883
3829
|
_clone(this),
|
|
3884
|
-
|
|
3885
|
-
|
|
3830
|
+
select,
|
|
3831
|
+
cb
|
|
3886
3832
|
);
|
|
3887
3833
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3834
|
+
/**
|
|
3835
|
+
* Add `catchAfterCommitError` to the query to catch possible errors that are coming from after commit hooks.
|
|
3836
|
+
*
|
|
3837
|
+
* When it is used, the transaction will return its result disregarding of a failed hook.
|
|
3838
|
+
*
|
|
3839
|
+
* Without `catchAfterCommitError`, the transaction function throws and won't return result.
|
|
3840
|
+
* Result is still accessible from the error object [AfterCommitError](#AfterCommitError).
|
|
3841
|
+
*
|
|
3842
|
+
* ```ts
|
|
3843
|
+
* const result = await db
|
|
3844
|
+
* .$transaction(async () => {
|
|
3845
|
+
* return db.table.create(data);
|
|
3846
|
+
* })
|
|
3847
|
+
* .catchAfterCommitError((err) => {
|
|
3848
|
+
* // err is instance of AfterCommitError (see below)
|
|
3849
|
+
* })
|
|
3850
|
+
* // can be added multiple times, all catchers will be executed
|
|
3851
|
+
* .catchAfterCommitError((err) => {});
|
|
3852
|
+
*
|
|
3853
|
+
* // result is available even if an after commit hook has failed
|
|
3854
|
+
* result.id;
|
|
3855
|
+
* ```
|
|
3856
|
+
*/
|
|
3857
|
+
catchAfterCommitError(fn) {
|
|
3858
|
+
const q = _clone(this);
|
|
3859
|
+
pushQueryValueImmutable(q, "catchAfterCommitErrors", fn);
|
|
3860
|
+
return q;
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
|
|
3890
3864
|
const setParserToQuery = (query, key, parser) => {
|
|
3891
3865
|
if (parser) {
|
|
3892
3866
|
if (query.parsers) query.parsers[key] = parser;
|
|
@@ -3912,6 +3886,94 @@ const getQueryParsers = (q, hookSelect) => {
|
|
|
3912
3886
|
return q.q.select ? q.q.parsers : q.q.defaultParsers;
|
|
3913
3887
|
};
|
|
3914
3888
|
|
|
3889
|
+
const queryColumnNameToKey = (q, name) => {
|
|
3890
|
+
let map = q.internal.columnNameToKeyMap;
|
|
3891
|
+
if (!map) {
|
|
3892
|
+
q.internal.columnNameToKeyMap = map = /* @__PURE__ */ new Map();
|
|
3893
|
+
const { shape } = q;
|
|
3894
|
+
for (const key in q.shape) {
|
|
3895
|
+
const column = shape[key];
|
|
3896
|
+
map.set(column.data.name ?? key, key);
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
return map.get(name);
|
|
3900
|
+
};
|
|
3901
|
+
|
|
3902
|
+
var __typeError = (msg) => {
|
|
3903
|
+
throw TypeError(msg);
|
|
3904
|
+
};
|
|
3905
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
3906
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
3907
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
3908
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
3909
|
+
var _query, _query2, _columnsCache;
|
|
3910
|
+
class OrchidOrmError extends Error {
|
|
3911
|
+
}
|
|
3912
|
+
class NotFoundError extends OrchidOrmError {
|
|
3913
|
+
constructor(query, message = "Record is not found") {
|
|
3914
|
+
super(message);
|
|
3915
|
+
// `#query` is private to prevent it from serializing to not cause problems to test runner reports
|
|
3916
|
+
// it is exposed with `getQuery` method which prevents this problem from both Vitest and Jest.
|
|
3917
|
+
// Exposing it with `get query()` still leaves the issue in Vitest.
|
|
3918
|
+
__privateAdd(this, _query);
|
|
3919
|
+
__privateSet(this, _query, query);
|
|
3920
|
+
}
|
|
3921
|
+
getQuery() {
|
|
3922
|
+
return __privateGet(this, _query);
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
_query = new WeakMap();
|
|
3926
|
+
class OrchidOrmInternalError extends Error {
|
|
3927
|
+
constructor(query, message, data) {
|
|
3928
|
+
super(message);
|
|
3929
|
+
this.data = data;
|
|
3930
|
+
// `#query` is private to prevent it from serializing to not cause problems to test runner reports
|
|
3931
|
+
// it is exposed with `getQuery` method which prevents this problem from both Vitest and Jest.
|
|
3932
|
+
// Exposing it with `get query()` still leaves the issue in Vitest.
|
|
3933
|
+
__privateAdd(this, _query2);
|
|
3934
|
+
__privateSet(this, _query2, query);
|
|
3935
|
+
}
|
|
3936
|
+
getQuery() {
|
|
3937
|
+
return __privateGet(this, _query2);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
_query2 = new WeakMap();
|
|
3941
|
+
class QueryError extends OrchidOrmInternalError {
|
|
3942
|
+
constructor() {
|
|
3943
|
+
super(...arguments);
|
|
3944
|
+
__privateAdd(this, _columnsCache);
|
|
3945
|
+
}
|
|
3946
|
+
get isUnique() {
|
|
3947
|
+
return this.code === "23505";
|
|
3948
|
+
}
|
|
3949
|
+
get columns() {
|
|
3950
|
+
if (__privateGet(this, _columnsCache)) return __privateGet(this, _columnsCache);
|
|
3951
|
+
const columns = {};
|
|
3952
|
+
if (this.detail) {
|
|
3953
|
+
const list = this.detail.match(/\((.*)\)=/)?.[1];
|
|
3954
|
+
if (list) {
|
|
3955
|
+
list.split(", ").forEach((item) => {
|
|
3956
|
+
const column = item.startsWith('"') ? item.slice(1, -1) : item;
|
|
3957
|
+
const key = queryColumnNameToKey(this.getQuery(), column) ?? column;
|
|
3958
|
+
columns[key] = true;
|
|
3959
|
+
});
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
return __privateSet(this, _columnsCache, columns);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
_columnsCache = new WeakMap();
|
|
3966
|
+
class MoreThanOneRowError extends OrchidOrmInternalError {
|
|
3967
|
+
constructor(query, message) {
|
|
3968
|
+
super(query, message);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
class UnhandledTypeError extends OrchidOrmInternalError {
|
|
3972
|
+
constructor(query, value) {
|
|
3973
|
+
super(query, `Unhandled type: ${JSON.stringify(value)} received`);
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3915
3977
|
const escape = (value, migration, nested) => {
|
|
3916
3978
|
const type = typeof value;
|
|
3917
3979
|
if (type === "number" || type === "bigint") return String(value);
|
|
@@ -4163,525 +4225,177 @@ class QueryTransaction {
|
|
|
4163
4225
|
} finally {
|
|
4164
4226
|
state.transactionId = transactionId - 1;
|
|
4165
4227
|
}
|
|
4166
|
-
}
|
|
4167
|
-
}
|
|
4168
|
-
/**
|
|
4169
|
-
* Use the `$ensureTransaction` when you want to ensure the sequence of queries is running in a transaction, but there is no need for Postgres [savepoints](https://www.postgresql.org/docs/current/sql-savepoint.html).
|
|
4170
|
-
*
|
|
4171
|
-
* ```ts
|
|
4172
|
-
* async function updateUserBalance(userId: string, amount: number) {
|
|
4173
|
-
* await db.$ensureTransaction(async () => {
|
|
4174
|
-
* await db.transfer.create({ userId, amount })
|
|
4175
|
-
* await db.user.find(userId).increment({ balance: amount })
|
|
4176
|
-
* })
|
|
4177
|
-
* }
|
|
4178
|
-
*
|
|
4179
|
-
* async function saveDeposit(userId: string, deposit: { ... }) {
|
|
4180
|
-
* await db.$ensureTransaction(async () => {
|
|
4181
|
-
* await db.deposit.create(deposit)
|
|
4182
|
-
* // transaction in updateUserBalance won't be started
|
|
4183
|
-
* await updateUserBalance(userId, deposit.amount)
|
|
4184
|
-
* })
|
|
4185
|
-
* }
|
|
4186
|
-
* ```
|
|
4187
|
-
*/
|
|
4188
|
-
ensureTransaction(cb) {
|
|
4189
|
-
const trx = this.internal.asyncStorage.getStore();
|
|
4190
|
-
if (trx) return cb();
|
|
4191
|
-
return QueryTransaction.prototype.transaction.call(this, cb);
|
|
4192
|
-
}
|
|
4193
|
-
isInTransaction() {
|
|
4194
|
-
const trx = this.internal.asyncStorage.getStore();
|
|
4195
|
-
return isInUserTransaction(trx);
|
|
4196
|
-
}
|
|
4197
|
-
/**
|
|
4198
|
-
* Schedules a hook to run after the outermost transaction commits:
|
|
4199
|
-
*
|
|
4200
|
-
* ```ts
|
|
4201
|
-
* await db.$transaction(async () => {
|
|
4202
|
-
* await db.table.create(data)
|
|
4203
|
-
* await db.table.where({ ...conditions }).update({ key: 'value' })
|
|
4204
|
-
*
|
|
4205
|
-
* db.$afterCommit(() => { // can be sync or async
|
|
4206
|
-
* console.log('after commit')
|
|
4207
|
-
* })
|
|
4208
|
-
* })
|
|
4209
|
-
* ```
|
|
4210
|
-
*
|
|
4211
|
-
* If used outside the transaction, the hook will be executed almost immediately, on the next microtask:
|
|
4212
|
-
*
|
|
4213
|
-
* ```ts
|
|
4214
|
-
* db.$afterCommit(async () => { // can be sync or async
|
|
4215
|
-
* console.log('after commit')
|
|
4216
|
-
* })
|
|
4217
|
-
* ```
|
|
4218
|
-
*
|
|
4219
|
-
* If the callback has no `try/catch` and throws an error,
|
|
4220
|
-
* this will cause `uncaughtException` if the callback is sync and `unhandledRejection` if it is async.
|
|
4221
|
-
*/
|
|
4222
|
-
afterCommit(hook) {
|
|
4223
|
-
const trx = this.internal.asyncStorage.getStore();
|
|
4224
|
-
if (isInUserTransaction(trx)) {
|
|
4225
|
-
(trx.afterCommit ?? (trx.afterCommit = [])).push(hook);
|
|
4226
|
-
} else {
|
|
4227
|
-
queueMicrotask(hook);
|
|
4228
|
-
}
|
|
4229
|
-
}
|
|
4230
|
-
/**
|
|
4231
|
-
* Whenever a query in a transaction fails, the transaction is transitioned to a failed state, no further queries are possible.
|
|
4232
|
-
*
|
|
4233
|
-
* Use [catchUniqueError](/guide/error-handling.html#catchuniqueerror) to handle uniqueness errors,
|
|
4234
|
-
* the following examples do not use it to illustrate error handling.
|
|
4235
|
-
*
|
|
4236
|
-
* ```ts
|
|
4237
|
-
* // This transaction is going to fail
|
|
4238
|
-
* db.$transaction(async () => {
|
|
4239
|
-
* try {
|
|
4240
|
-
* // imagine it fails because the username is already taken
|
|
4241
|
-
* await db.user.insert({ username: 'taken' });
|
|
4242
|
-
* } catch (err) {
|
|
4243
|
-
* // even though the query is wrapped in a try-catch, the transaction fails anyway
|
|
4244
|
-
* }
|
|
4245
|
-
* });
|
|
4246
|
-
* ```
|
|
4247
|
-
*
|
|
4248
|
-
* You can use `catch` method on a query instead of `try-catch` to surpass the problem.
|
|
4249
|
-
* The following transaction won't fail:
|
|
4250
|
-
*
|
|
4251
|
-
* ```ts
|
|
4252
|
-
* // Transaction succeeds
|
|
4253
|
-
* db.$transaction(async () => {
|
|
4254
|
-
* const result = await db.user.insert({ username: 'taken' }).catch(() => 'failed');
|
|
4255
|
-
*
|
|
4256
|
-
* if (result === 'failed') {
|
|
4257
|
-
* await db.user.insert({ username: 'another' });
|
|
4258
|
-
* }
|
|
4259
|
-
* });
|
|
4260
|
-
* ```
|
|
4261
|
-
*
|
|
4262
|
-
* This is because when using `catch` method, `OrchidORM` will wrap the query with a savepoint.
|
|
4263
|
-
*
|
|
4264
|
-
* Alternatively, you can use the `recoverable()` method:
|
|
4265
|
-
*
|
|
4266
|
-
* ```ts
|
|
4267
|
-
* // Transaction succeeds
|
|
4268
|
-
* db.$transaction(async () => {
|
|
4269
|
-
* try {
|
|
4270
|
-
* await db.user.insert({ username: 'taken' }).recoverable();
|
|
4271
|
-
* } catch (err) {
|
|
4272
|
-
* await db.user.insert({ username: 'another' });
|
|
4273
|
-
* }
|
|
4274
|
-
* });
|
|
4275
|
-
* ```
|
|
4276
|
-
*/
|
|
4277
|
-
recoverable() {
|
|
4278
|
-
const q = _clone(this);
|
|
4279
|
-
q.q.catch = true;
|
|
4280
|
-
return q;
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
const runAfterCommit = (afterCommit, result) => {
|
|
4284
|
-
queueMicrotask(async () => {
|
|
4285
|
-
if (afterCommit) {
|
|
4286
|
-
const promises = [];
|
|
4287
|
-
let catchAfterCommitErrors;
|
|
4288
|
-
for (let i = 0, len = afterCommit.length; i < len; ) {
|
|
4289
|
-
const first = afterCommit[i];
|
|
4290
|
-
if (typeof first === "function") {
|
|
4291
|
-
try {
|
|
4292
|
-
promises.push(first());
|
|
4293
|
-
} catch (err) {
|
|
4294
|
-
promises.push(Promise.reject(err));
|
|
4295
|
-
}
|
|
4296
|
-
i++;
|
|
4297
|
-
} else {
|
|
4298
|
-
const q = afterCommit[i + 1];
|
|
4299
|
-
if (q.q.catchAfterCommitErrors) {
|
|
4300
|
-
(catchAfterCommitErrors ?? (catchAfterCommitErrors = [])).push(...q.q.catchAfterCommitErrors);
|
|
4301
|
-
}
|
|
4302
|
-
for (const fn of afterCommit[i + 2]) {
|
|
4303
|
-
try {
|
|
4304
|
-
promises.push(fn(first, q));
|
|
4305
|
-
} catch (err) {
|
|
4306
|
-
promises.push(Promise.reject(err));
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
i += 3;
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
const getHookNames = () => {
|
|
4313
|
-
const hookNames = [];
|
|
4314
|
-
for (let i = 0, len = afterCommit.length; i < len; ) {
|
|
4315
|
-
const first = afterCommit[i];
|
|
4316
|
-
if (typeof first === "function") {
|
|
4317
|
-
hookNames.push(first.name);
|
|
4318
|
-
i++;
|
|
4319
|
-
} else {
|
|
4320
|
-
for (const fn of afterCommit[i + 2]) {
|
|
4321
|
-
hookNames.push(fn.name);
|
|
4322
|
-
}
|
|
4323
|
-
i += 3;
|
|
4324
|
-
}
|
|
4325
|
-
}
|
|
4326
|
-
return hookNames;
|
|
4327
|
-
};
|
|
4328
|
-
await _runAfterCommitHooks(
|
|
4329
|
-
result,
|
|
4330
|
-
promises,
|
|
4331
|
-
getHookNames,
|
|
4332
|
-
catchAfterCommitErrors
|
|
4333
|
-
);
|
|
4334
|
-
}
|
|
4335
|
-
});
|
|
4336
|
-
};
|
|
4337
|
-
|
|
4338
|
-
const before = (q, key, cb) => pushQueryValueImmutable(q, `before${key}`, cb);
|
|
4339
|
-
const after = (query, key, select, cb, commit) => {
|
|
4340
|
-
const q = query;
|
|
4341
|
-
pushQueryValueImmutable(
|
|
4342
|
-
q,
|
|
4343
|
-
`after${key}${commit ? "Commit" : ""}`,
|
|
4344
|
-
cb
|
|
4345
|
-
);
|
|
4346
|
-
if (key === "Save") {
|
|
4347
|
-
addSelect(q, "Create", select);
|
|
4348
|
-
addSelect(q, "Update", select);
|
|
4349
|
-
} else {
|
|
4350
|
-
addSelect(q, key, select);
|
|
4351
|
-
}
|
|
4352
|
-
return query;
|
|
4353
|
-
};
|
|
4354
|
-
const addSelect = (q, key, select) => {
|
|
4355
|
-
const prop = `after${key}Select`;
|
|
4356
|
-
const set = q.q[prop] = new Set(q.q[prop]);
|
|
4357
|
-
for (const column of select) {
|
|
4358
|
-
set.add(column);
|
|
4359
|
-
}
|
|
4360
|
-
};
|
|
4361
|
-
const _queryHookBeforeQuery = (q, cb) => {
|
|
4362
|
-
return pushQueryValueImmutable(q, "before", cb);
|
|
4363
|
-
};
|
|
4364
|
-
const _queryHookAfterQuery = (q, cb) => {
|
|
4365
|
-
return pushQueryValueImmutable(q, "after", cb);
|
|
4366
|
-
};
|
|
4367
|
-
const _hookSelectColumns = (query, columns, asFn) => {
|
|
4368
|
-
const hookSelect = query.q.hookSelect = new Map(
|
|
4369
|
-
query.q.hookSelect && [...query.q.hookSelect]
|
|
4370
|
-
);
|
|
4371
|
-
const aliases = [];
|
|
4372
|
-
const addAlias = (as) => {
|
|
4373
|
-
aliases.push(as);
|
|
4374
|
-
if (aliases.length === columns.length) {
|
|
4375
|
-
asFn(aliases);
|
|
4376
|
-
}
|
|
4377
|
-
};
|
|
4378
|
-
for (const column of columns) {
|
|
4379
|
-
const item = hookSelect.get(column);
|
|
4380
|
-
hookSelect.set(column, {
|
|
4381
|
-
...item,
|
|
4382
|
-
select: column,
|
|
4383
|
-
onAs: [...item?.onAs || emptyArray, addAlias]
|
|
4384
|
-
});
|
|
4385
|
-
}
|
|
4386
|
-
};
|
|
4387
|
-
class QueryHookUtils {
|
|
4388
|
-
constructor(query, columns, key) {
|
|
4389
|
-
this.query = query;
|
|
4390
|
-
this.columns = columns;
|
|
4391
|
-
this.key = key;
|
|
4392
|
-
this.set = (data) => {
|
|
4393
|
-
const set = {};
|
|
4394
|
-
for (const key in data) {
|
|
4395
|
-
if (data[key] !== void 0) {
|
|
4396
|
-
set[key] = data[key];
|
|
4397
|
-
}
|
|
4398
|
-
}
|
|
4399
|
-
pushQueryValueImmutable(this.query, this.key, set);
|
|
4400
|
-
};
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4403
|
-
const finalizeNestedHookSelect = (batches, returnType, tempColumns, renames, key) => {
|
|
4404
|
-
if (renames) {
|
|
4405
|
-
for (const { data } of batches) {
|
|
4406
|
-
for (const record of data) {
|
|
4407
|
-
if (record) {
|
|
4408
|
-
for (const a in renames) {
|
|
4409
|
-
record[a] = record[renames[a]];
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4413
|
-
}
|
|
4414
|
-
}
|
|
4415
|
-
if (tempColumns?.size) {
|
|
4416
|
-
for (const { data } of batches) {
|
|
4417
|
-
for (const record of data) {
|
|
4418
|
-
if (record) {
|
|
4419
|
-
for (const key2 of tempColumns) {
|
|
4420
|
-
delete record[key2];
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
}
|
|
4426
|
-
if (returnType === "one" || returnType === "oneOrThrow") {
|
|
4427
|
-
for (const batch of batches) {
|
|
4428
|
-
batch.data = batch.data[0];
|
|
4429
|
-
}
|
|
4430
|
-
} else if (returnType === "pluck") {
|
|
4431
|
-
for (const { data } of batches) {
|
|
4432
|
-
for (let i = 0; i < data.length; i++) {
|
|
4433
|
-
data[i] = data[i][key];
|
|
4434
|
-
}
|
|
4435
|
-
}
|
|
4436
|
-
} else if (returnType === "value" || returnType === "valueOrThrow") {
|
|
4437
|
-
for (const item of batches) {
|
|
4438
|
-
item.parent[item.key] = item.data[0]?.[key];
|
|
4439
|
-
}
|
|
4440
|
-
}
|
|
4441
|
-
};
|
|
4442
|
-
const _queryHookBeforeCreate = (q, cb) => {
|
|
4443
|
-
return before(
|
|
4444
|
-
q,
|
|
4445
|
-
"Create",
|
|
4446
|
-
(q2) => cb(new QueryHookUtils(q2, q2.q.columns, "hookCreateSet"))
|
|
4447
|
-
);
|
|
4448
|
-
};
|
|
4449
|
-
const _queryHookAfterCreate = (q, select, cb) => {
|
|
4450
|
-
return after(q, "Create", select, cb);
|
|
4451
|
-
};
|
|
4452
|
-
const _queryHookAfterCreateCommit = (q, select, cb) => {
|
|
4453
|
-
return after(q, "Create", select, cb, true);
|
|
4454
|
-
};
|
|
4455
|
-
const _queryHookBeforeUpdate = (q, cb) => {
|
|
4456
|
-
return before(q, "Update", (q2) => {
|
|
4457
|
-
const columns = /* @__PURE__ */ new Set();
|
|
4458
|
-
if (q2.q.updateData) {
|
|
4459
|
-
for (const item of q2.q.updateData) {
|
|
4460
|
-
if (typeof item === "object") {
|
|
4461
|
-
for (const key in item) {
|
|
4462
|
-
columns.add(key);
|
|
4463
|
-
}
|
|
4464
|
-
}
|
|
4465
|
-
}
|
|
4466
|
-
}
|
|
4467
|
-
if (q2.q.updateMany) {
|
|
4468
|
-
for (const key of q2.q.updateMany.setColumns) {
|
|
4469
|
-
columns.add(key);
|
|
4470
|
-
}
|
|
4471
|
-
}
|
|
4472
|
-
return cb(new QueryHookUtils(q2, [...columns], "hookUpdateSet"));
|
|
4473
|
-
});
|
|
4474
|
-
};
|
|
4475
|
-
const _queryHookAfterUpdate = (q, select, cb) => {
|
|
4476
|
-
return after(q, "Update", select, cb);
|
|
4477
|
-
};
|
|
4478
|
-
const _queryHookAfterUpdateCommit = (q, select, cb) => {
|
|
4479
|
-
return after(q, "Update", select, cb, true);
|
|
4480
|
-
};
|
|
4481
|
-
const _queryHookBeforeSave = (q, cb) => {
|
|
4482
|
-
return _queryHookBeforeUpdate(_queryHookBeforeCreate(q, cb), cb);
|
|
4483
|
-
};
|
|
4484
|
-
const _queryHookAfterSave = (q, select, cb) => {
|
|
4485
|
-
return after(q, "Save", select, cb);
|
|
4486
|
-
};
|
|
4487
|
-
const _queryAfterSaveCommit = (q, select, cb) => {
|
|
4488
|
-
return after(q, "Save", select, cb, true);
|
|
4489
|
-
};
|
|
4490
|
-
const _queryHookBeforeDelete = (q, cb) => {
|
|
4491
|
-
return before(q, "Delete", cb);
|
|
4492
|
-
};
|
|
4493
|
-
const _queryHookAfterDelete = (q, select, cb) => {
|
|
4494
|
-
return after(q, "Delete", select, cb);
|
|
4495
|
-
};
|
|
4496
|
-
const _queryHookAfterDeleteCommit = (q, select, cb) => {
|
|
4497
|
-
return after(q, "Delete", select, cb, true);
|
|
4498
|
-
};
|
|
4499
|
-
class QueryHooks {
|
|
4500
|
-
/**
|
|
4501
|
-
* Run the function before any kind of query.
|
|
4502
|
-
*
|
|
4503
|
-
* @param cb - function to call, first argument is a query object
|
|
4504
|
-
*/
|
|
4505
|
-
beforeQuery(cb) {
|
|
4506
|
-
return _queryHookBeforeQuery(_clone(this), cb);
|
|
4507
|
-
}
|
|
4508
|
-
/**
|
|
4509
|
-
* Run the function after any kind of query.
|
|
4510
|
-
* Enforces wrapping the query into a transaction.
|
|
4511
|
-
* The function will run after the query is succeeded, but before the transaction commit.
|
|
4512
|
-
*
|
|
4513
|
-
* @param cb - function to call, first argument is the query result of type `unknown`, second argument is a query object
|
|
4514
|
-
*/
|
|
4515
|
-
afterQuery(cb) {
|
|
4516
|
-
return _queryHookAfterQuery(_clone(this), cb);
|
|
4517
|
-
}
|
|
4518
|
-
/**
|
|
4519
|
-
* Run the function before a `create` kind of query.
|
|
4520
|
-
*
|
|
4521
|
-
* @param cb - function to call, first argument is a query object
|
|
4522
|
-
*/
|
|
4523
|
-
beforeCreate(cb) {
|
|
4524
|
-
return _queryHookBeforeCreate(_clone(this), cb);
|
|
4228
|
+
}
|
|
4525
4229
|
}
|
|
4526
4230
|
/**
|
|
4527
|
-
*
|
|
4528
|
-
* Enforces wrapping the query into a transaction.
|
|
4529
|
-
* The function will run after the query is succeeded, but before the transaction commit.
|
|
4530
|
-
* Queries inside the function will run in the same transaction as the target query.
|
|
4231
|
+
* Use the `$ensureTransaction` when you want to ensure the sequence of queries is running in a transaction, but there is no need for Postgres [savepoints](https://www.postgresql.org/docs/current/sql-savepoint.html).
|
|
4531
4232
|
*
|
|
4532
|
-
*
|
|
4533
|
-
*
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
* Run the function after transaction for a `create` kind of query will be committed.
|
|
4540
|
-
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
4233
|
+
* ```ts
|
|
4234
|
+
* async function updateUserBalance(userId: string, amount: number) {
|
|
4235
|
+
* await db.$ensureTransaction(async () => {
|
|
4236
|
+
* await db.transfer.create({ userId, amount })
|
|
4237
|
+
* await db.user.find(userId).increment({ balance: amount })
|
|
4238
|
+
* })
|
|
4239
|
+
* }
|
|
4541
4240
|
*
|
|
4542
|
-
*
|
|
4543
|
-
*
|
|
4241
|
+
* async function saveDeposit(userId: string, deposit: { ... }) {
|
|
4242
|
+
* await db.$ensureTransaction(async () => {
|
|
4243
|
+
* await db.deposit.create(deposit)
|
|
4244
|
+
* // transaction in updateUserBalance won't be started
|
|
4245
|
+
* await updateUserBalance(userId, deposit.amount)
|
|
4246
|
+
* })
|
|
4247
|
+
* }
|
|
4248
|
+
* ```
|
|
4544
4249
|
*/
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
cb
|
|
4550
|
-
);
|
|
4250
|
+
ensureTransaction(cb) {
|
|
4251
|
+
const trx = this.internal.asyncStorage.getStore();
|
|
4252
|
+
if (trx) return cb();
|
|
4253
|
+
return QueryTransaction.prototype.transaction.call(this, cb);
|
|
4551
4254
|
}
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
* @param cb - function to call, first argument is a query object
|
|
4556
|
-
*/
|
|
4557
|
-
beforeUpdate(cb) {
|
|
4558
|
-
return _queryHookBeforeUpdate(_clone(this), cb);
|
|
4255
|
+
isInTransaction() {
|
|
4256
|
+
const trx = this.internal.asyncStorage.getStore();
|
|
4257
|
+
return isInUserTransaction(trx);
|
|
4559
4258
|
}
|
|
4560
4259
|
/**
|
|
4561
|
-
*
|
|
4562
|
-
* Enforces wrapping the query into a transaction.
|
|
4563
|
-
* The function will run after the query is succeeded, but before the transaction commit.
|
|
4564
|
-
* Queries inside the function will run in the same transaction as the target query.
|
|
4565
|
-
* If no records were updated, the hook *won't* run.
|
|
4260
|
+
* Schedules a hook to run after the outermost transaction commits:
|
|
4566
4261
|
*
|
|
4567
|
-
*
|
|
4568
|
-
*
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
return _queryHookAfterUpdate(_clone(this), select, cb);
|
|
4572
|
-
}
|
|
4573
|
-
/**
|
|
4574
|
-
* Run the function after transaction for an `update` kind of query will be committed.
|
|
4575
|
-
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
4576
|
-
* If no records were updated, the hook *won't* run.
|
|
4262
|
+
* ```ts
|
|
4263
|
+
* await db.$transaction(async () => {
|
|
4264
|
+
* await db.table.create(data)
|
|
4265
|
+
* await db.table.where({ ...conditions }).update({ key: 'value' })
|
|
4577
4266
|
*
|
|
4578
|
-
*
|
|
4579
|
-
*
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
_clone(this),
|
|
4584
|
-
select,
|
|
4585
|
-
cb
|
|
4586
|
-
);
|
|
4587
|
-
}
|
|
4588
|
-
/**
|
|
4589
|
-
* Run the function before a `create` or an `update` kind of query.
|
|
4267
|
+
* db.$afterCommit(() => { // can be sync or async
|
|
4268
|
+
* console.log('after commit')
|
|
4269
|
+
* })
|
|
4270
|
+
* })
|
|
4271
|
+
* ```
|
|
4590
4272
|
*
|
|
4591
|
-
*
|
|
4592
|
-
*/
|
|
4593
|
-
beforeSave(cb) {
|
|
4594
|
-
return _queryHookBeforeSave(_clone(this), cb);
|
|
4595
|
-
}
|
|
4596
|
-
/**
|
|
4597
|
-
* Run the function after a `create` or an `update` kind of query.
|
|
4598
|
-
* Enforces wrapping the query into a transaction.
|
|
4599
|
-
* The function will run after the query is succeeded, but before the transaction commit.
|
|
4600
|
-
* Queries inside the function will run in the same transaction as the target query.
|
|
4601
|
-
* For the `update` query, if no records were updated, the hook *won't* run.
|
|
4273
|
+
* If used outside the transaction, the hook will be executed almost immediately, on the next microtask:
|
|
4602
4274
|
*
|
|
4603
|
-
*
|
|
4604
|
-
*
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
}
|
|
4609
|
-
/**
|
|
4610
|
-
* Run the function after transaction for a `create` or an `update` kind of query will be committed.
|
|
4611
|
-
* If the query wasn't wrapped in a transaction, will run after the query.
|
|
4612
|
-
* For the `update` query, if no records were updated, the hook *won't* run.
|
|
4275
|
+
* ```ts
|
|
4276
|
+
* db.$afterCommit(async () => { // can be sync or async
|
|
4277
|
+
* console.log('after commit')
|
|
4278
|
+
* })
|
|
4279
|
+
* ```
|
|
4613
4280
|
*
|
|
4614
|
-
*
|
|
4615
|
-
*
|
|
4281
|
+
* If the callback has no `try/catch` and throws an error,
|
|
4282
|
+
* this will cause `uncaughtException` if the callback is sync and `unhandledRejection` if it is async.
|
|
4616
4283
|
*/
|
|
4617
|
-
|
|
4618
|
-
|
|
4284
|
+
afterCommit(hook) {
|
|
4285
|
+
const trx = this.internal.asyncStorage.getStore();
|
|
4286
|
+
if (isInUserTransaction(trx)) {
|
|
4287
|
+
(trx.afterCommit ?? (trx.afterCommit = [])).push(hook);
|
|
4288
|
+
} else {
|
|
4289
|
+
queueMicrotask(hook);
|
|
4290
|
+
}
|
|
4619
4291
|
}
|
|
4620
4292
|
/**
|
|
4621
|
-
*
|
|
4293
|
+
* Whenever a query in a transaction fails, the transaction is transitioned to a failed state, no further queries are possible.
|
|
4622
4294
|
*
|
|
4623
|
-
*
|
|
4624
|
-
|
|
4625
|
-
beforeDelete(cb) {
|
|
4626
|
-
return _queryHookBeforeDelete(_clone(this), cb);
|
|
4627
|
-
}
|
|
4628
|
-
/**
|
|
4629
|
-
* Run the function after a `delete` kind of query.
|
|
4630
|
-
* Enforces wrapping the query into a transaction.
|
|
4631
|
-
* The function will run after the query is succeeded, but before the transaction commit.
|
|
4632
|
-
* Queries inside the function will run in the same transaction as the target query.
|
|
4633
|
-
* If no records were deleted, the hook *won't* run.
|
|
4295
|
+
* Use [catchUniqueError](/guide/error-handling.html#catchuniqueerror) to handle uniqueness errors,
|
|
4296
|
+
* the following examples do not use it to illustrate error handling.
|
|
4634
4297
|
*
|
|
4635
|
-
*
|
|
4636
|
-
*
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
*
|
|
4643
|
-
*
|
|
4644
|
-
*
|
|
4298
|
+
* ```ts
|
|
4299
|
+
* // This transaction is going to fail
|
|
4300
|
+
* db.$transaction(async () => {
|
|
4301
|
+
* try {
|
|
4302
|
+
* // imagine it fails because the username is already taken
|
|
4303
|
+
* await db.user.insert({ username: 'taken' });
|
|
4304
|
+
* } catch (err) {
|
|
4305
|
+
* // even though the query is wrapped in a try-catch, the transaction fails anyway
|
|
4306
|
+
* }
|
|
4307
|
+
* });
|
|
4308
|
+
* ```
|
|
4645
4309
|
*
|
|
4646
|
-
*
|
|
4647
|
-
*
|
|
4648
|
-
*/
|
|
4649
|
-
afterDeleteCommit(select, cb) {
|
|
4650
|
-
return _queryHookAfterDeleteCommit(
|
|
4651
|
-
_clone(this),
|
|
4652
|
-
select,
|
|
4653
|
-
cb
|
|
4654
|
-
);
|
|
4655
|
-
}
|
|
4656
|
-
/**
|
|
4657
|
-
* Add `catchAfterCommitError` to the query to catch possible errors that are coming from after commit hooks.
|
|
4310
|
+
* You can use `catch` method on a query instead of `try-catch` to surpass the problem.
|
|
4311
|
+
* The following transaction won't fail:
|
|
4658
4312
|
*
|
|
4659
|
-
*
|
|
4313
|
+
* ```ts
|
|
4314
|
+
* // Transaction succeeds
|
|
4315
|
+
* db.$transaction(async () => {
|
|
4316
|
+
* const result = await db.user.insert({ username: 'taken' }).catch(() => 'failed');
|
|
4660
4317
|
*
|
|
4661
|
-
*
|
|
4662
|
-
*
|
|
4318
|
+
* if (result === 'failed') {
|
|
4319
|
+
* await db.user.insert({ username: 'another' });
|
|
4320
|
+
* }
|
|
4321
|
+
* });
|
|
4322
|
+
* ```
|
|
4663
4323
|
*
|
|
4664
|
-
*
|
|
4665
|
-
* const result = await db
|
|
4666
|
-
* .$transaction(async () => {
|
|
4667
|
-
* return db.table.create(data);
|
|
4668
|
-
* })
|
|
4669
|
-
* .catchAfterCommitError((err) => {
|
|
4670
|
-
* // err is instance of AfterCommitError (see below)
|
|
4671
|
-
* })
|
|
4672
|
-
* // can be added multiple times, all catchers will be executed
|
|
4673
|
-
* .catchAfterCommitError((err) => {});
|
|
4324
|
+
* This is because when using `catch` method, `OrchidORM` will wrap the query with a savepoint.
|
|
4674
4325
|
*
|
|
4675
|
-
*
|
|
4676
|
-
*
|
|
4326
|
+
* Alternatively, you can use the `recoverable()` method:
|
|
4327
|
+
*
|
|
4328
|
+
* ```ts
|
|
4329
|
+
* // Transaction succeeds
|
|
4330
|
+
* db.$transaction(async () => {
|
|
4331
|
+
* try {
|
|
4332
|
+
* await db.user.insert({ username: 'taken' }).recoverable();
|
|
4333
|
+
* } catch (err) {
|
|
4334
|
+
* await db.user.insert({ username: 'another' });
|
|
4335
|
+
* }
|
|
4336
|
+
* });
|
|
4677
4337
|
* ```
|
|
4678
4338
|
*/
|
|
4679
|
-
|
|
4339
|
+
recoverable() {
|
|
4680
4340
|
const q = _clone(this);
|
|
4681
|
-
|
|
4341
|
+
q.q.catch = true;
|
|
4682
4342
|
return q;
|
|
4683
4343
|
}
|
|
4684
4344
|
}
|
|
4345
|
+
const runAfterCommit = (afterCommit, result) => {
|
|
4346
|
+
queueMicrotask(async () => {
|
|
4347
|
+
if (afterCommit) {
|
|
4348
|
+
const promises = [];
|
|
4349
|
+
let catchAfterCommitErrors;
|
|
4350
|
+
for (let i = 0, len = afterCommit.length; i < len; ) {
|
|
4351
|
+
const first = afterCommit[i];
|
|
4352
|
+
if (typeof first === "function") {
|
|
4353
|
+
try {
|
|
4354
|
+
promises.push(first());
|
|
4355
|
+
} catch (err) {
|
|
4356
|
+
promises.push(Promise.reject(err));
|
|
4357
|
+
}
|
|
4358
|
+
i++;
|
|
4359
|
+
} else {
|
|
4360
|
+
const q = afterCommit[i + 1];
|
|
4361
|
+
if (q.q.catchAfterCommitErrors) {
|
|
4362
|
+
(catchAfterCommitErrors ?? (catchAfterCommitErrors = [])).push(...q.q.catchAfterCommitErrors);
|
|
4363
|
+
}
|
|
4364
|
+
for (const fn of afterCommit[i + 2]) {
|
|
4365
|
+
try {
|
|
4366
|
+
promises.push(fn(first, q));
|
|
4367
|
+
} catch (err) {
|
|
4368
|
+
promises.push(Promise.reject(err));
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
i += 3;
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
const getHookNames = () => {
|
|
4375
|
+
const hookNames = [];
|
|
4376
|
+
for (let i = 0, len = afterCommit.length; i < len; ) {
|
|
4377
|
+
const first = afterCommit[i];
|
|
4378
|
+
if (typeof first === "function") {
|
|
4379
|
+
hookNames.push(first.name);
|
|
4380
|
+
i++;
|
|
4381
|
+
} else {
|
|
4382
|
+
for (const fn of afterCommit[i + 2]) {
|
|
4383
|
+
hookNames.push(fn.name);
|
|
4384
|
+
}
|
|
4385
|
+
i += 3;
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
return hookNames;
|
|
4389
|
+
};
|
|
4390
|
+
await _runAfterCommitHooks(
|
|
4391
|
+
result,
|
|
4392
|
+
promises,
|
|
4393
|
+
getHookNames,
|
|
4394
|
+
catchAfterCommitErrors
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
4397
|
+
});
|
|
4398
|
+
};
|
|
4685
4399
|
|
|
4686
4400
|
const applyTransforms = (queryData, returnType, fns, result) => {
|
|
4687
4401
|
for (const fn of fns) {
|
|
@@ -4938,6 +4652,127 @@ const collectPrimaryKeys = (q) => {
|
|
|
4938
4652
|
return primaryKeys;
|
|
4939
4653
|
};
|
|
4940
4654
|
|
|
4655
|
+
const _unscope = (q, scope) => {
|
|
4656
|
+
if (q.q.scopes) {
|
|
4657
|
+
q.q.scopes = { ...q.q.scopes };
|
|
4658
|
+
delete q.q.scopes[scope];
|
|
4659
|
+
for (const _ in q.q.scopes) {
|
|
4660
|
+
return q;
|
|
4661
|
+
}
|
|
4662
|
+
delete q.q.scopes;
|
|
4663
|
+
}
|
|
4664
|
+
return q;
|
|
4665
|
+
};
|
|
4666
|
+
class QueryScope {
|
|
4667
|
+
/**
|
|
4668
|
+
* See {@link QueryScope}
|
|
4669
|
+
*
|
|
4670
|
+
* Use the `scope` method to apply a pre-defined scope.
|
|
4671
|
+
*
|
|
4672
|
+
* ```ts
|
|
4673
|
+
* // use the `active` scope that is defined in the table:
|
|
4674
|
+
* await db.some.scope('active');
|
|
4675
|
+
* ```
|
|
4676
|
+
*
|
|
4677
|
+
* @param scope - name of the scope to apply
|
|
4678
|
+
*/
|
|
4679
|
+
scope(scope) {
|
|
4680
|
+
const q = _clone(this);
|
|
4681
|
+
if (!q.q.scopes?.[scope]) {
|
|
4682
|
+
const s = q.internal.scopes[scope];
|
|
4683
|
+
if (!s) throw new Error(`Scope ${scope} is not defined`);
|
|
4684
|
+
setObjectValueImmutable(q.q, "scopes", scope, s);
|
|
4685
|
+
}
|
|
4686
|
+
return q;
|
|
4687
|
+
}
|
|
4688
|
+
/**
|
|
4689
|
+
* See {@link QueryScope}
|
|
4690
|
+
*
|
|
4691
|
+
* Remove conditions that were added by the scope from the query.
|
|
4692
|
+
*
|
|
4693
|
+
* ```ts
|
|
4694
|
+
* // SomeTable has a default scope, ignore it for this query:
|
|
4695
|
+
* await db.some.unscope('default');
|
|
4696
|
+
* ```
|
|
4697
|
+
*
|
|
4698
|
+
* @param scope - name of the scope to remove from the query
|
|
4699
|
+
*/
|
|
4700
|
+
unscope(scope) {
|
|
4701
|
+
return _unscope(_clone(this), scope);
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
|
|
4705
|
+
const checkIfNeedResultAllForMutativeQueriesSelectRelations = (sql) => {
|
|
4706
|
+
return sql.mutativeQueriesSelectRelationsState?.value;
|
|
4707
|
+
};
|
|
4708
|
+
const checkIfShouldReleaseSavepointForMutativeQueriesSelectRelations = (sql) => {
|
|
4709
|
+
return sql.mutativeQueriesSelectRelationsState?.value;
|
|
4710
|
+
};
|
|
4711
|
+
const loadMutativeQueriesSelectRelations = (sql, result, adapter, startingSavepoint, renames) => sql.mutativeQueriesSelectRelationsState?.value ? loadRelations(
|
|
4712
|
+
sql.mutativeQueriesSelectRelationsState,
|
|
4713
|
+
result,
|
|
4714
|
+
adapter,
|
|
4715
|
+
startingSavepoint,
|
|
4716
|
+
renames
|
|
4717
|
+
) : void 0;
|
|
4718
|
+
const loadRelations = async (state, result, adapter, startingSavepoint, renames) => {
|
|
4719
|
+
var _a;
|
|
4720
|
+
const q = state.query;
|
|
4721
|
+
const primaryKeys = requirePrimaryKeys(
|
|
4722
|
+
q,
|
|
4723
|
+
"Cannot select a relation of a table that has no primary keys"
|
|
4724
|
+
);
|
|
4725
|
+
const selectQuery = _unscope(q, "nonDeleted");
|
|
4726
|
+
selectQuery.q.type = selectQuery.q.returnType = void 0;
|
|
4727
|
+
const matchSourceTableIds = {};
|
|
4728
|
+
for (const pkey of primaryKeys) {
|
|
4729
|
+
matchSourceTableIds[pkey] = {
|
|
4730
|
+
in: result.map((row) => row[pkey])
|
|
4731
|
+
};
|
|
4732
|
+
}
|
|
4733
|
+
((_a = selectQuery.q).and ?? (_a.and = [])).push(matchSourceTableIds);
|
|
4734
|
+
const relationsSelect = state.value;
|
|
4735
|
+
const selectAs = { ...relationsSelect };
|
|
4736
|
+
const select = [{ selectAs }];
|
|
4737
|
+
const relationKeyAliases = primaryKeys.map((key) => {
|
|
4738
|
+
if (key in selectAs) {
|
|
4739
|
+
const as = getFreeAlias(selectAs, key);
|
|
4740
|
+
selectAs[as] = key;
|
|
4741
|
+
return as;
|
|
4742
|
+
} else {
|
|
4743
|
+
select.push(key);
|
|
4744
|
+
return key;
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4747
|
+
selectQuery.q.select = select;
|
|
4748
|
+
const relationsResult = await maybeWrappedThen.call(
|
|
4749
|
+
selectQuery,
|
|
4750
|
+
void 0,
|
|
4751
|
+
async (err) => {
|
|
4752
|
+
await adapter.arrays(`ROLLBACK TO SAVEPOINT "${startingSavepoint}"`);
|
|
4753
|
+
throw err;
|
|
4754
|
+
},
|
|
4755
|
+
startingSavepoint
|
|
4756
|
+
);
|
|
4757
|
+
for (const row of result) {
|
|
4758
|
+
const relationRow = relationsResult.find((relationRow2) => {
|
|
4759
|
+
return !primaryKeys.some(
|
|
4760
|
+
(key, i) => relationRow2[relationKeyAliases[i]] !== row[key]
|
|
4761
|
+
);
|
|
4762
|
+
});
|
|
4763
|
+
if (relationRow) {
|
|
4764
|
+
Object.assign(row, relationRow);
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
if (renames) {
|
|
4768
|
+
for (const key in relationsSelect) {
|
|
4769
|
+
if (key in renames) {
|
|
4770
|
+
delete renames[key];
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
};
|
|
4775
|
+
|
|
4941
4776
|
const queryMethodByReturnType = {
|
|
4942
4777
|
undefined: "query",
|
|
4943
4778
|
all: "query",
|
|
@@ -4950,7 +4785,6 @@ const queryMethodByReturnType = {
|
|
|
4950
4785
|
void: "arrays"
|
|
4951
4786
|
};
|
|
4952
4787
|
class Then {
|
|
4953
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4954
4788
|
catch(fn) {
|
|
4955
4789
|
const q = _clone(this);
|
|
4956
4790
|
q.q.catch = true;
|
|
@@ -4982,7 +4816,7 @@ Object.defineProperty(Then.prototype, "then", {
|
|
|
4982
4816
|
});
|
|
4983
4817
|
}
|
|
4984
4818
|
});
|
|
4985
|
-
function maybeWrappedThen(resolve, reject) {
|
|
4819
|
+
function maybeWrappedThen(resolve, reject, parentSavepoint) {
|
|
4986
4820
|
const { q } = this;
|
|
4987
4821
|
const shouldCatch = q.catch;
|
|
4988
4822
|
let beforeActionHooks;
|
|
@@ -5037,7 +4871,8 @@ function maybeWrappedThen(resolve, reject) {
|
|
|
5037
4871
|
afterSaveCommitHooks,
|
|
5038
4872
|
resolve2,
|
|
5039
4873
|
reject2,
|
|
5040
|
-
shouldCatch
|
|
4874
|
+
shouldCatch,
|
|
4875
|
+
parentSavepoint
|
|
5041
4876
|
);
|
|
5042
4877
|
})
|
|
5043
4878
|
).then(resolve, reject);
|
|
@@ -5053,7 +4888,8 @@ function maybeWrappedThen(resolve, reject) {
|
|
|
5053
4888
|
afterSaveCommitHooks,
|
|
5054
4889
|
resolve,
|
|
5055
4890
|
reject,
|
|
5056
|
-
shouldCatch
|
|
4891
|
+
shouldCatch,
|
|
4892
|
+
parentSavepoint
|
|
5057
4893
|
);
|
|
5058
4894
|
}
|
|
5059
4895
|
}
|
|
@@ -5063,8 +4899,7 @@ const callAfterHook = function(cb) {
|
|
|
5063
4899
|
return cb(this[0], this[1]);
|
|
5064
4900
|
};
|
|
5065
4901
|
const beginSql = { text: "BEGIN" };
|
|
5066
|
-
const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks, afterCommitHooks, afterSaveCommitHooks, resolve, reject, shouldCatch) => {
|
|
5067
|
-
var _a;
|
|
4902
|
+
const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks, afterCommitHooks, afterSaveCommitHooks, resolve, reject, shouldCatch, parentSavepoint) => {
|
|
5068
4903
|
const { q: query } = q;
|
|
5069
4904
|
let sql;
|
|
5070
4905
|
let logData;
|
|
@@ -5088,12 +4923,16 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5088
4923
|
}
|
|
5089
4924
|
if (promises) await Promise.all(promises);
|
|
5090
4925
|
}
|
|
5091
|
-
const { tableHook, cteHooks
|
|
4926
|
+
const { tableHook, cteHooks } = sql;
|
|
5092
4927
|
const { returnType = "all" } = query;
|
|
5093
|
-
const tempReturnType = tableHook?.select || cteHooks?.hasSelect || returnType === "rows" && q.q.batchParsers ||
|
|
4928
|
+
const tempReturnType = tableHook?.select || cteHooks?.hasSelect || returnType === "rows" && q.q.batchParsers || checkIfNeedResultAllForMutativeQueriesSelectRelations(sql) ? "all" : returnType;
|
|
5094
4929
|
let result;
|
|
5095
4930
|
let queryResult;
|
|
5096
4931
|
let cteData;
|
|
4932
|
+
const startingSavepoint = setCatchingSavepoint(
|
|
4933
|
+
!parentSavepoint && shouldCatch && state
|
|
4934
|
+
);
|
|
4935
|
+
const releasingSavepoint = checkIfShouldReleaseSavepointForMutativeQueriesSelectRelations(sql) || parentSavepoint ? void 0 : startingSavepoint;
|
|
5097
4936
|
if ("text" in sql) {
|
|
5098
4937
|
if (query.autoPreparedStatements) {
|
|
5099
4938
|
sql.name = queriesNames[sql.text] || (queriesNames[sql.text] = (nameI++).toString(36));
|
|
@@ -5102,7 +4941,13 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5102
4941
|
logData = log.beforeQuery(sql);
|
|
5103
4942
|
}
|
|
5104
4943
|
const method = queryMethodByReturnType[tempReturnType];
|
|
5105
|
-
queryResult = await execQuery(
|
|
4944
|
+
queryResult = await execQuery(
|
|
4945
|
+
adapter,
|
|
4946
|
+
method,
|
|
4947
|
+
sql,
|
|
4948
|
+
startingSavepoint,
|
|
4949
|
+
releasingSavepoint
|
|
4950
|
+
);
|
|
5106
4951
|
const { runAfterQuery } = sql;
|
|
5107
4952
|
if (log) {
|
|
5108
4953
|
log.afterQuery(sql, logData);
|
|
@@ -5136,8 +4981,9 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5136
4981
|
} else {
|
|
5137
4982
|
const queryMethod = queryMethodByReturnType[tempReturnType];
|
|
5138
4983
|
const queryBatch = async (batch) => {
|
|
5139
|
-
|
|
5140
|
-
|
|
4984
|
+
const last = batch.length - 1;
|
|
4985
|
+
for (let i = 0; i <= last; i++) {
|
|
4986
|
+
sql = batch[i];
|
|
5141
4987
|
if (log) {
|
|
5142
4988
|
logData = log.beforeQuery(sql);
|
|
5143
4989
|
}
|
|
@@ -5145,7 +4991,8 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5145
4991
|
adapter,
|
|
5146
4992
|
queryMethod,
|
|
5147
4993
|
sql,
|
|
5148
|
-
|
|
4994
|
+
i === 0 ? startingSavepoint : void 0,
|
|
4995
|
+
i === last ? releasingSavepoint : void 0
|
|
5149
4996
|
);
|
|
5150
4997
|
if (queryResult) {
|
|
5151
4998
|
queryResult.rowCount += result2.rowCount;
|
|
@@ -5177,8 +5024,11 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5177
5024
|
let tempColumns;
|
|
5178
5025
|
let renames;
|
|
5179
5026
|
if (tableHook?.select) {
|
|
5180
|
-
for (const
|
|
5181
|
-
|
|
5027
|
+
for (const [
|
|
5028
|
+
column,
|
|
5029
|
+
{ as, temp, notLoaded }
|
|
5030
|
+
] of tableHook.select.entries()) {
|
|
5031
|
+
if (notLoaded) continue;
|
|
5182
5032
|
if (as) {
|
|
5183
5033
|
(renames ?? (renames = {}))[column] = as;
|
|
5184
5034
|
}
|
|
@@ -5264,7 +5114,7 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5264
5114
|
const afterCommitKey = `after${purpose2}Commit`;
|
|
5265
5115
|
const afterCommit = tableHook2[afterCommitKey];
|
|
5266
5116
|
if (afterCommit) {
|
|
5267
|
-
const arr =
|
|
5117
|
+
const arr = cteAfterCommitHooks ?? (cteAfterCommitHooks = []);
|
|
5268
5118
|
for (const fn of afterCommit) {
|
|
5269
5119
|
const hookData = addedAfterCommitHooks.has(fn);
|
|
5270
5120
|
if (!hookData) {
|
|
@@ -5306,7 +5156,13 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5306
5156
|
afterActionAndSaveCommit
|
|
5307
5157
|
);
|
|
5308
5158
|
}
|
|
5309
|
-
if (cteAfterCommitHooks)
|
|
5159
|
+
if (cteAfterCommitHooks) {
|
|
5160
|
+
(state.afterCommit ?? (state.afterCommit = [])).push(
|
|
5161
|
+
result,
|
|
5162
|
+
q,
|
|
5163
|
+
cteAfterCommitHooks
|
|
5164
|
+
);
|
|
5165
|
+
}
|
|
5310
5166
|
} else {
|
|
5311
5167
|
const localResult = result;
|
|
5312
5168
|
queueMicrotask(async () => {
|
|
@@ -5322,7 +5178,15 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5322
5178
|
}
|
|
5323
5179
|
}
|
|
5324
5180
|
}
|
|
5325
|
-
if (cteAfterCommitHooks)
|
|
5181
|
+
if (cteAfterCommitHooks) {
|
|
5182
|
+
for (const fn of cteAfterCommitHooks) {
|
|
5183
|
+
try {
|
|
5184
|
+
promises.push(fn());
|
|
5185
|
+
} catch (err) {
|
|
5186
|
+
promises.push(Promise.reject(err));
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5326
5190
|
await _runAfterCommitHooks(
|
|
5327
5191
|
localResult,
|
|
5328
5192
|
promises,
|
|
@@ -5336,56 +5200,22 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5336
5200
|
}
|
|
5337
5201
|
}
|
|
5338
5202
|
}
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
const relationsSelect = delayedRelationSelect.value;
|
|
5355
|
-
const selectAs = { ...relationsSelect };
|
|
5356
|
-
const select = [{ selectAs }];
|
|
5357
|
-
const relationKeyAliases = primaryKeys.map((key) => {
|
|
5358
|
-
if (key in selectAs) {
|
|
5359
|
-
const as = getFreeAlias(selectAs, key);
|
|
5360
|
-
selectAs[as] = key;
|
|
5361
|
-
return as;
|
|
5362
|
-
} else {
|
|
5363
|
-
select.push(key);
|
|
5364
|
-
return key;
|
|
5365
|
-
}
|
|
5366
|
-
});
|
|
5367
|
-
selectQuery.q.select = select;
|
|
5368
|
-
const relationsResult = await selectQuery;
|
|
5369
|
-
for (const row of result) {
|
|
5370
|
-
const relationRow = relationsResult.find((relationRow2) => {
|
|
5371
|
-
return !primaryKeys.some(
|
|
5372
|
-
(key, i) => relationRow2[relationKeyAliases[i]] !== row[key]
|
|
5373
|
-
);
|
|
5374
|
-
});
|
|
5375
|
-
if (relationRow) {
|
|
5376
|
-
Object.assign(row, relationRow);
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
|
-
if (renames) {
|
|
5380
|
-
for (const key in relationsSelect) {
|
|
5381
|
-
if (key in renames) {
|
|
5382
|
-
delete renames[key];
|
|
5383
|
-
}
|
|
5384
|
-
}
|
|
5385
|
-
}
|
|
5203
|
+
const promise = loadMutativeQueriesSelectRelations(
|
|
5204
|
+
localSql,
|
|
5205
|
+
result,
|
|
5206
|
+
adapter,
|
|
5207
|
+
startingSavepoint,
|
|
5208
|
+
renames
|
|
5209
|
+
) || /**
|
|
5210
|
+
* In case when we have MutativeQueriesSelectRelations, the first query does insert/update/delete,
|
|
5211
|
+
* it must not run batchParsers because it doesn't have the data yet.
|
|
5212
|
+
* The second query loads data and performs batchParsers.
|
|
5213
|
+
*/
|
|
5214
|
+
//
|
|
5215
|
+
parseBatch(q, queryResult);
|
|
5216
|
+
if (promise) {
|
|
5217
|
+
await promise;
|
|
5386
5218
|
}
|
|
5387
|
-
const promise = parseBatch(q, queryResult, delayedRelationSelect);
|
|
5388
|
-
if (promise) await promise;
|
|
5389
5219
|
if (tableHook?.select || tempReturnType !== returnType) {
|
|
5390
5220
|
if (renames) {
|
|
5391
5221
|
const renamedResult = Array.from({
|
|
@@ -5404,7 +5234,7 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5404
5234
|
result = filterResult(
|
|
5405
5235
|
q,
|
|
5406
5236
|
returnType,
|
|
5407
|
-
//
|
|
5237
|
+
//
|
|
5408
5238
|
queryResult,
|
|
5409
5239
|
result,
|
|
5410
5240
|
tempColumns,
|
|
@@ -5451,12 +5281,15 @@ const then = async (q, adapter, state, beforeHooks, afterHooks, afterSaveHooks,
|
|
|
5451
5281
|
throw error;
|
|
5452
5282
|
}
|
|
5453
5283
|
};
|
|
5454
|
-
const
|
|
5455
|
-
|
|
5284
|
+
const setCatchingSavepoint = (catchTrx) => {
|
|
5285
|
+
return catchTrx ? `s${catchTrx.catchI = (catchTrx.catchI || 0) + 1}` : void 0;
|
|
5286
|
+
};
|
|
5287
|
+
const execQuery = (adapter, method, sql, startingSavepoint, releasingSavepoint) => {
|
|
5456
5288
|
return adapter[method](
|
|
5457
5289
|
sql.text,
|
|
5458
5290
|
sql.values,
|
|
5459
|
-
|
|
5291
|
+
startingSavepoint,
|
|
5292
|
+
releasingSavepoint
|
|
5460
5293
|
).then((result) => {
|
|
5461
5294
|
if (result.rowCount && !result.rows.length) {
|
|
5462
5295
|
result.rows.length = result.rowCount;
|
|
@@ -5523,9 +5356,9 @@ const handleResult = (q, returnType, result, sql, isSubQuery) => {
|
|
|
5523
5356
|
}
|
|
5524
5357
|
}
|
|
5525
5358
|
};
|
|
5526
|
-
const parseBatch = (q, queryResult
|
|
5359
|
+
const parseBatch = (q, queryResult) => {
|
|
5527
5360
|
let promises;
|
|
5528
|
-
if (q.q.batchParsers
|
|
5361
|
+
if (q.q.batchParsers) {
|
|
5529
5362
|
for (const parser of q.q.batchParsers) {
|
|
5530
5363
|
const res = parser.fn(parser.path, queryResult);
|
|
5531
5364
|
if (res) (promises ?? (promises = [])).push(res);
|
|
@@ -5615,27 +5448,193 @@ const getFirstResultKey = (q, queryResult) => {
|
|
|
5615
5448
|
return key;
|
|
5616
5449
|
}
|
|
5617
5450
|
}
|
|
5618
|
-
return;
|
|
5451
|
+
return;
|
|
5452
|
+
};
|
|
5453
|
+
const filterAllResult = (result, tempColumns, hasAfterHook) => {
|
|
5454
|
+
if (tempColumns?.size) {
|
|
5455
|
+
if (hasAfterHook) {
|
|
5456
|
+
return result.map((data) => {
|
|
5457
|
+
const record = { ...data };
|
|
5458
|
+
for (const key of tempColumns) {
|
|
5459
|
+
delete record[key];
|
|
5460
|
+
}
|
|
5461
|
+
return record;
|
|
5462
|
+
});
|
|
5463
|
+
} else {
|
|
5464
|
+
for (const record of result) {
|
|
5465
|
+
for (const key of tempColumns) {
|
|
5466
|
+
delete record[key];
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
return result;
|
|
5472
|
+
};
|
|
5473
|
+
|
|
5474
|
+
const getQueryAs = (q) => {
|
|
5475
|
+
return q.q.as || q.table;
|
|
5476
|
+
};
|
|
5477
|
+
const _getQueryAs = (q) => q.q.as;
|
|
5478
|
+
const _getQueryFreeAlias = (q, as) => q.aliases ? getFreeAlias(q.aliases, as) : as;
|
|
5479
|
+
const _checkIfAliased = (q, as, name) => {
|
|
5480
|
+
return q.q.aliases?.[as] === name;
|
|
5481
|
+
};
|
|
5482
|
+
const _getQueryAliasOrName = (q, as) => {
|
|
5483
|
+
return q.aliases?.[as] || as;
|
|
5484
|
+
};
|
|
5485
|
+
const _getQueryOuterAliases = (q) => {
|
|
5486
|
+
return q.outerAliases;
|
|
5487
|
+
};
|
|
5488
|
+
const _setQueryAs = (self, as) => {
|
|
5489
|
+
const { q } = self;
|
|
5490
|
+
q.as = as;
|
|
5491
|
+
q.aliases = {
|
|
5492
|
+
...q.aliases,
|
|
5493
|
+
[as]: _getQueryFreeAlias(q, as)
|
|
5494
|
+
};
|
|
5495
|
+
return self;
|
|
5496
|
+
};
|
|
5497
|
+
const _setQueryAlias = (q, name, as) => {
|
|
5498
|
+
q.q.aliases = { ...q.q.aliases, [as]: name };
|
|
5499
|
+
};
|
|
5500
|
+
const _setSubQueryAliases = (q) => {
|
|
5501
|
+
q.q.outerAliases = q.q.aliases;
|
|
5502
|
+
};
|
|
5503
|
+
const _applyRelationAliases = (query, relQueryData) => {
|
|
5504
|
+
const aliases = query.q.as ? { ...query.q.aliases } : { ...query.q.aliases, [query.table]: query.table };
|
|
5505
|
+
const relAliases = relQueryData.aliases;
|
|
5506
|
+
for (const as in relAliases) {
|
|
5507
|
+
aliases[as] = getFreeAlias(aliases, as);
|
|
5508
|
+
}
|
|
5509
|
+
relQueryData.as = aliases[relQueryData.as];
|
|
5510
|
+
relQueryData.aliases = aliases;
|
|
5511
|
+
};
|
|
5512
|
+
const _copyQueryAliasToQuery = (fromQuery, toQuery, key) => {
|
|
5513
|
+
const name = _getQueryAliasOrName(fromQuery.q, key);
|
|
5514
|
+
if (name !== key) {
|
|
5515
|
+
_setQueryAlias(toQuery, name, key);
|
|
5516
|
+
}
|
|
5517
|
+
return name;
|
|
5518
|
+
};
|
|
5519
|
+
class QueryAsMethods {
|
|
5520
|
+
/**
|
|
5521
|
+
* Sets table alias:
|
|
5522
|
+
*
|
|
5523
|
+
* ```ts
|
|
5524
|
+
* db.table.as('u').select('u.name');
|
|
5525
|
+
*
|
|
5526
|
+
* // Can be used in the join:
|
|
5527
|
+
* db.table.join(db.profile.as('p'), 'p.userId', 'user.id');
|
|
5528
|
+
* ```
|
|
5529
|
+
*
|
|
5530
|
+
* @param as - alias for the table of this query
|
|
5531
|
+
*/
|
|
5532
|
+
as(as) {
|
|
5533
|
+
return _setQueryAs(_clone(this), as);
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
|
|
5537
|
+
const pushQueryArrayImmutable = (q, key, value) => {
|
|
5538
|
+
const arr = q.q[key];
|
|
5539
|
+
q.q[key] = arr ? [...arr, ...value] : value;
|
|
5540
|
+
return q;
|
|
5541
|
+
};
|
|
5542
|
+
const setQueryObjectValueImmutable = (q, object, key, value) => {
|
|
5543
|
+
q.q[object] = {
|
|
5544
|
+
...q.q[object],
|
|
5545
|
+
[key]: value
|
|
5546
|
+
};
|
|
5547
|
+
return q;
|
|
5548
|
+
};
|
|
5549
|
+
const throwIfNoWhere = (q, method) => {
|
|
5550
|
+
if (!q.q.or && !q.q.and && !q.q.scopes && !q.q.all) {
|
|
5551
|
+
throw new OrchidOrmInternalError(
|
|
5552
|
+
q,
|
|
5553
|
+
`Dangerous ${method} without conditions`
|
|
5554
|
+
);
|
|
5555
|
+
}
|
|
5556
|
+
};
|
|
5557
|
+
const throwIfJoinLateral = (q, method) => {
|
|
5558
|
+
if (q.q.join?.some((x) => Array.isArray(x) || "s" in x.args && x.args.s)) {
|
|
5559
|
+
throw new OrchidOrmInternalError(
|
|
5560
|
+
q,
|
|
5561
|
+
`Cannot join a complex query in ${method}`
|
|
5562
|
+
);
|
|
5563
|
+
}
|
|
5564
|
+
};
|
|
5565
|
+
const throwOnReadOnlyUpdate = (query, column, key) => {
|
|
5566
|
+
if (column.data.appReadOnly || column.data.readOnly) {
|
|
5567
|
+
throw new OrchidOrmInternalError(
|
|
5568
|
+
query,
|
|
5569
|
+
"Trying to update a readonly column",
|
|
5570
|
+
{ column: key }
|
|
5571
|
+
);
|
|
5572
|
+
}
|
|
5573
|
+
};
|
|
5574
|
+
const saveAliasedShape = (q, as, key) => {
|
|
5575
|
+
const shapes = q.q[key];
|
|
5576
|
+
as = getFreeAlias(shapes, as);
|
|
5577
|
+
setQueryObjectValueImmutable(q, key, as, emptyObject);
|
|
5578
|
+
return as;
|
|
5579
|
+
};
|
|
5580
|
+
const extendQuery = (q, methods) => {
|
|
5581
|
+
const base = Object.create(q.baseQuery);
|
|
5582
|
+
base.baseQuery = base;
|
|
5583
|
+
Object.assign(base, methods);
|
|
5584
|
+
const cloned = Object.create(base);
|
|
5585
|
+
cloned.q = getClonedQueryData(q.q);
|
|
5586
|
+
return cloned;
|
|
5587
|
+
};
|
|
5588
|
+
const _queryAll = (q) => {
|
|
5589
|
+
q.q.returnType = "all";
|
|
5590
|
+
q.q.all = true;
|
|
5591
|
+
return q;
|
|
5592
|
+
};
|
|
5593
|
+
const _queryTake = (query) => {
|
|
5594
|
+
const q = query.q;
|
|
5595
|
+
switch (q.returnType) {
|
|
5596
|
+
case "valueOrThrow":
|
|
5597
|
+
case "pluck":
|
|
5598
|
+
case "void":
|
|
5599
|
+
break;
|
|
5600
|
+
case "value": {
|
|
5601
|
+
q.returnType = "valueOrThrow";
|
|
5602
|
+
break;
|
|
5603
|
+
}
|
|
5604
|
+
default: {
|
|
5605
|
+
q.returnType = "oneOrThrow";
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
return query;
|
|
5619
5609
|
};
|
|
5620
|
-
const
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
}
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
delete record[key];
|
|
5634
|
-
}
|
|
5635
|
-
}
|
|
5610
|
+
const _queryTakeOptional = (query) => {
|
|
5611
|
+
const q = query.q;
|
|
5612
|
+
switch (q.returnType) {
|
|
5613
|
+
case "value":
|
|
5614
|
+
case "pluck":
|
|
5615
|
+
case "void":
|
|
5616
|
+
break;
|
|
5617
|
+
case "valueOrThrow": {
|
|
5618
|
+
q.returnType = "value";
|
|
5619
|
+
break;
|
|
5620
|
+
}
|
|
5621
|
+
default: {
|
|
5622
|
+
q.returnType = "one";
|
|
5636
5623
|
}
|
|
5637
5624
|
}
|
|
5638
|
-
return
|
|
5625
|
+
return query;
|
|
5626
|
+
};
|
|
5627
|
+
const _queryExec = (q) => {
|
|
5628
|
+
q.q.returnType = "void";
|
|
5629
|
+
return q;
|
|
5630
|
+
};
|
|
5631
|
+
const _queryRows = (q) => {
|
|
5632
|
+
q.q.returnType = "rows";
|
|
5633
|
+
return q;
|
|
5634
|
+
};
|
|
5635
|
+
const getFullColumnTable = (q, column, index, as) => {
|
|
5636
|
+
const table = column.slice(0, index);
|
|
5637
|
+
return as && table !== as && _checkIfAliased(q, table, as) ? as : table;
|
|
5639
5638
|
};
|
|
5640
5639
|
|
|
5641
5640
|
const noneResult = (q, queryData, type) => {
|
|
@@ -5685,6 +5684,38 @@ const _queryNone = (q) => {
|
|
|
5685
5684
|
};
|
|
5686
5685
|
const isQueryNone = (q) => q.then === noneMethods.then;
|
|
5687
5686
|
|
|
5687
|
+
const prepareSubQueryForSql = (mainQuery, subQuery) => {
|
|
5688
|
+
if (subQuery.dynamicBefore) {
|
|
5689
|
+
pushQueryValueImmutable(mainQuery, "dynamicBefore", subQuery.q);
|
|
5690
|
+
return subQuery;
|
|
5691
|
+
}
|
|
5692
|
+
let beforeAction = subQuery.q.type ? subQuery.q.type === "insert" ? subQuery.q.beforeCreate : subQuery.q.type === "update" ? subQuery.q.beforeUpdate : subQuery.q.type === "upsert" ? subQuery.q.upsertUpdate && subQuery.q.updateData ? subQuery.q.beforeUpdate && subQuery.q.beforeCreate ? [...subQuery.q.beforeUpdate, ...subQuery.q.beforeCreate] : subQuery.q.beforeUpdate || subQuery.q.beforeCreate : subQuery.q.beforeCreate : subQuery.q.type === "delete" ? subQuery.q.beforeDelete : void 0 : void 0;
|
|
5693
|
+
const { beforeSet } = subQuery.q;
|
|
5694
|
+
beforeAction = beforeAction && beforeSet ? [...beforeAction, ...beforeSet] : beforeSet ? [...beforeSet] : beforeAction;
|
|
5695
|
+
if (beforeAction) {
|
|
5696
|
+
const newSet = new Set(mainQuery.q.beforeSet);
|
|
5697
|
+
const filteredHooks = [];
|
|
5698
|
+
for (const hook of beforeAction) {
|
|
5699
|
+
if (!newSet.has(hook)) {
|
|
5700
|
+
newSet.add(hook);
|
|
5701
|
+
filteredHooks.push(hook);
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
mainQuery.q.beforeSet = newSet;
|
|
5705
|
+
beforeAction = filteredHooks;
|
|
5706
|
+
if (beforeAction.length) {
|
|
5707
|
+
pushQueryArrayImmutable(
|
|
5708
|
+
mainQuery,
|
|
5709
|
+
"before",
|
|
5710
|
+
beforeAction.map((fn) => () => fn(subQuery))
|
|
5711
|
+
);
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
return subQuery;
|
|
5715
|
+
};
|
|
5716
|
+
setPrepareSubQueryForSql(prepareSubQueryForSql);
|
|
5717
|
+
setRawSqlPrepareSubQueryForSql(prepareSubQueryForSql);
|
|
5718
|
+
|
|
5688
5719
|
const addWithParsers = (w, parsers) => {
|
|
5689
5720
|
for (const key in w.shape) {
|
|
5690
5721
|
const { _parse } = w.shape[key];
|
|
@@ -6960,7 +6991,9 @@ const wrapMainQueryInCte = (ctx, q, isSubSql) => {
|
|
|
6960
6991
|
if (!isSubSql && !ctx.cteName) {
|
|
6961
6992
|
as = addTopCteSql$1(ctx, ctx.wrapAs, ctx.sql.join(" "));
|
|
6962
6993
|
}
|
|
6963
|
-
q.appendQueries?.forEach(
|
|
6994
|
+
q.appendQueries?.forEach(
|
|
6995
|
+
(query) => addTopCte$1("after", ctx, query, query.q.type)
|
|
6996
|
+
);
|
|
6964
6997
|
if (!isSubSql && !ctx.cteName) {
|
|
6965
6998
|
const addNull = ctx.topCtx.cteHooks?.hasSelect;
|
|
6966
6999
|
ctx.sql = [`SELECT *${addNull ? ", NULL" : ""} FROM ${as}`];
|
|
@@ -6980,9 +7013,6 @@ const makeSql = (ctx, type, isSubSql, runAfterQuery) => {
|
|
|
6980
7013
|
runAfterQuery
|
|
6981
7014
|
};
|
|
6982
7015
|
};
|
|
6983
|
-
const quoteSchemaAndTable = (schema, table) => {
|
|
6984
|
-
return schema ? `"${schema}"."${table}"` : `"${table}"`;
|
|
6985
|
-
};
|
|
6986
7016
|
const requireTableOrStringFrom = (query) => {
|
|
6987
7017
|
const table = query.table || (typeof query.q.from === "string" ? query.q.from : void 0);
|
|
6988
7018
|
if (!table) {
|
|
@@ -7314,7 +7344,7 @@ const _joinLateral = (self, type, joinQuery, as, innerJoinLateral) => {
|
|
|
7314
7344
|
existingValue.q.q.select = [
|
|
7315
7345
|
{
|
|
7316
7346
|
selectAs: {
|
|
7317
|
-
...
|
|
7347
|
+
...existingValue.q.q.select[0].selectAs,
|
|
7318
7348
|
[joinAs]: joinValueSelect
|
|
7319
7349
|
}
|
|
7320
7350
|
}
|
|
@@ -8054,17 +8084,6 @@ const pushQueryOrOn = (q, joinFrom, joinTo, ...on) => {
|
|
|
8054
8084
|
makeOnItem(joinFrom, joinTo, on)
|
|
8055
8085
|
]);
|
|
8056
8086
|
};
|
|
8057
|
-
const addQueryOn = (query, joinFrom, joinTo, ...args) => {
|
|
8058
|
-
const cloned = _clone(query);
|
|
8059
|
-
const { q } = cloned;
|
|
8060
|
-
setObjectValueImmutable(
|
|
8061
|
-
q,
|
|
8062
|
-
"joinedShapes",
|
|
8063
|
-
joinFrom.q.as || joinFrom.table,
|
|
8064
|
-
joinFrom.q.shape
|
|
8065
|
-
);
|
|
8066
|
-
return pushQueryOn(cloned, joinFrom, joinTo, ...args);
|
|
8067
|
-
};
|
|
8068
8087
|
const _queryJoinOn = (q, args) => {
|
|
8069
8088
|
return pushQueryOn(
|
|
8070
8089
|
q,
|
|
@@ -8131,11 +8150,12 @@ class OnMethods {
|
|
|
8131
8150
|
}
|
|
8132
8151
|
}
|
|
8133
8152
|
|
|
8134
|
-
const _addToHookSelect = (query, selects) => {
|
|
8153
|
+
const _addToHookSelect = (query, selects, notLoaded) => {
|
|
8135
8154
|
const { q } = query;
|
|
8136
8155
|
const map = q.hookSelect = new Map(q.hookSelect);
|
|
8137
8156
|
for (const key of selects) {
|
|
8138
|
-
map.
|
|
8157
|
+
const item = map.get(key) || { select: key };
|
|
8158
|
+
map.set(key, notLoaded ? { ...item, notLoaded } : item);
|
|
8139
8159
|
}
|
|
8140
8160
|
};
|
|
8141
8161
|
const _addToHookSelectWithTable = (query, selects, table) => {
|
|
@@ -8146,6 +8166,10 @@ const _addToHookSelectWithTable = (query, selects, table) => {
|
|
|
8146
8166
|
}
|
|
8147
8167
|
};
|
|
8148
8168
|
|
|
8169
|
+
const setSelectRelation = (q) => {
|
|
8170
|
+
q.selectRelation = true;
|
|
8171
|
+
};
|
|
8172
|
+
|
|
8149
8173
|
const addParserForRawExpression = (q, key, raw) => {
|
|
8150
8174
|
if (raw.result.value) addColumnParserToQuery(q.q, key, raw.result.value);
|
|
8151
8175
|
};
|
|
@@ -8404,7 +8428,8 @@ const processSelectArg = (q, as, arg, columnAs) => {
|
|
|
8404
8428
|
if (isRelationQuery(value) && // `subQuery = 1` case is when callback returns the same query as it gets,
|
|
8405
8429
|
// for example `q => q.get('name')`.
|
|
8406
8430
|
value.q.subQuery !== 1) {
|
|
8407
|
-
|
|
8431
|
+
joinQuery = true;
|
|
8432
|
+
setSelectRelation(query.q);
|
|
8408
8433
|
value = value.joinQuery(value, q);
|
|
8409
8434
|
let subQuery;
|
|
8410
8435
|
const { returnType, innerJoinLateral } = value.q;
|
|
@@ -8588,48 +8613,198 @@ const getShapeFromSelect = (q, isSubQuery) => {
|
|
|
8588
8613
|
}
|
|
8589
8614
|
}
|
|
8590
8615
|
}
|
|
8591
|
-
return result;
|
|
8592
|
-
};
|
|
8593
|
-
const addColumnToShapeFromSelect = (q, arg, shape, query, result, isSubQuery, key) => {
|
|
8594
|
-
const index = arg.indexOf(".");
|
|
8595
|
-
if (index !== -1) {
|
|
8596
|
-
const as = q.q.as || q.table;
|
|
8597
|
-
const table = getFullColumnTable(q, arg, index, as);
|
|
8598
|
-
const column = arg.slice(index + 1);
|
|
8599
|
-
if (table === as) {
|
|
8600
|
-
result[key || column] = shape[column];
|
|
8601
|
-
} else {
|
|
8602
|
-
const it = query.joinedShapes?.[table]?.[column];
|
|
8603
|
-
if (it)
|
|
8604
|
-
result[key || column] = mapSubSelectColumn(
|
|
8605
|
-
it,
|
|
8606
|
-
isSubQuery
|
|
8607
|
-
);
|
|
8608
|
-
}
|
|
8609
|
-
} else if (arg === "*") {
|
|
8610
|
-
for (const key2 in shape) {
|
|
8611
|
-
if (!shape[key2].data.explicitSelect) {
|
|
8612
|
-
result[key2] = mapSubSelectColumn(
|
|
8613
|
-
shape[key2],
|
|
8614
|
-
isSubQuery
|
|
8615
|
-
);
|
|
8616
|
-
}
|
|
8617
|
-
}
|
|
8618
|
-
} else {
|
|
8619
|
-
result[key || arg] = mapSubSelectColumn(
|
|
8620
|
-
shape[arg],
|
|
8621
|
-
isSubQuery
|
|
8616
|
+
return result;
|
|
8617
|
+
};
|
|
8618
|
+
const addColumnToShapeFromSelect = (q, arg, shape, query, result, isSubQuery, key) => {
|
|
8619
|
+
const index = arg.indexOf(".");
|
|
8620
|
+
if (index !== -1) {
|
|
8621
|
+
const as = q.q.as || q.table;
|
|
8622
|
+
const table = getFullColumnTable(q, arg, index, as);
|
|
8623
|
+
const column = arg.slice(index + 1);
|
|
8624
|
+
if (table === as) {
|
|
8625
|
+
result[key || column] = shape[column];
|
|
8626
|
+
} else {
|
|
8627
|
+
const it = query.joinedShapes?.[table]?.[column];
|
|
8628
|
+
if (it)
|
|
8629
|
+
result[key || column] = mapSubSelectColumn(
|
|
8630
|
+
it,
|
|
8631
|
+
isSubQuery
|
|
8632
|
+
);
|
|
8633
|
+
}
|
|
8634
|
+
} else if (arg === "*") {
|
|
8635
|
+
for (const key2 in shape) {
|
|
8636
|
+
if (!shape[key2].data.explicitSelect) {
|
|
8637
|
+
result[key2] = mapSubSelectColumn(
|
|
8638
|
+
shape[key2],
|
|
8639
|
+
isSubQuery
|
|
8640
|
+
);
|
|
8641
|
+
}
|
|
8642
|
+
}
|
|
8643
|
+
} else {
|
|
8644
|
+
result[key || arg] = mapSubSelectColumn(
|
|
8645
|
+
shape[arg],
|
|
8646
|
+
isSubQuery
|
|
8647
|
+
);
|
|
8648
|
+
}
|
|
8649
|
+
};
|
|
8650
|
+
const mapSubSelectColumn = (column, isSubQuery) => {
|
|
8651
|
+
if (!isSubQuery || !column || !column.data.name && !column.data.explicitSelect) {
|
|
8652
|
+
return column;
|
|
8653
|
+
}
|
|
8654
|
+
const cloned = Object.create(column);
|
|
8655
|
+
cloned.data = { ...column.data, name: void 0, explicitSelect: void 0 };
|
|
8656
|
+
return cloned;
|
|
8657
|
+
};
|
|
8658
|
+
|
|
8659
|
+
const _queryUnion = (base, args, k, p, m) => {
|
|
8660
|
+
const query = base.baseQuery.clone();
|
|
8661
|
+
const u = args.map(
|
|
8662
|
+
(a) => ({
|
|
8663
|
+
a: prepareSubQueryForSql(
|
|
8664
|
+
base,
|
|
8665
|
+
typeof a === "function" ? a(query) : a
|
|
8666
|
+
),
|
|
8667
|
+
k,
|
|
8668
|
+
m
|
|
8669
|
+
})
|
|
8670
|
+
);
|
|
8671
|
+
const { q } = query;
|
|
8672
|
+
const baseQ = base.q;
|
|
8673
|
+
q.union = baseQ.union ? {
|
|
8674
|
+
...baseQ.union,
|
|
8675
|
+
u: [...baseQ.union.u, ...u]
|
|
8676
|
+
} : {
|
|
8677
|
+
b: base,
|
|
8678
|
+
u,
|
|
8679
|
+
p
|
|
8680
|
+
};
|
|
8681
|
+
return query;
|
|
8682
|
+
};
|
|
8683
|
+
class Union {
|
|
8684
|
+
/**
|
|
8685
|
+
* Creates a union query, takes one or more queries or SQL expressions.
|
|
8686
|
+
*
|
|
8687
|
+
* ```ts
|
|
8688
|
+
* import { sql } from './baseTable';
|
|
8689
|
+
*
|
|
8690
|
+
* // The first query of the union
|
|
8691
|
+
* db.one
|
|
8692
|
+
* .select('id', 'name')
|
|
8693
|
+
* // add two more queries to the union
|
|
8694
|
+
* .union(
|
|
8695
|
+
* db.two.select('id', 'name'),
|
|
8696
|
+
* (q = sql`SELECT id, name FROM "thirdTable"`),
|
|
8697
|
+
* )
|
|
8698
|
+
* // sub-sequent `union` is equivalent to passing multiple queries into a single `union`
|
|
8699
|
+
* .union(db.three.select('id', 'name'));
|
|
8700
|
+
* ```
|
|
8701
|
+
*
|
|
8702
|
+
* `order`, `limit`, `offset` are special, it matters if you place them **before** or **after** the `union`, it also have a meaning to place them before and after.
|
|
8703
|
+
*
|
|
8704
|
+
* ```ts
|
|
8705
|
+
* // order, limit, offset are applied ONLY to 'one'
|
|
8706
|
+
* db.one
|
|
8707
|
+
* .order('x')
|
|
8708
|
+
* .limit(1)
|
|
8709
|
+
* .offset(1)
|
|
8710
|
+
* // 'two' also has order, limit, and offset
|
|
8711
|
+
* .unionAll(db.two.order('y').limit(2).offset(2))
|
|
8712
|
+
* // sets order, limit, offset for all records
|
|
8713
|
+
* .order('z')
|
|
8714
|
+
* .limit(3)
|
|
8715
|
+
* .offset(3);
|
|
8716
|
+
* ```
|
|
8717
|
+
*
|
|
8718
|
+
* Equivalent SQL:
|
|
8719
|
+
*
|
|
8720
|
+
* ```sql
|
|
8721
|
+
* -- both union parts have their own order, limit, offset
|
|
8722
|
+
* ( SELECT * FROM one ORDER x ASC LIMIT 1 OFFSET 1 )
|
|
8723
|
+
* UNION ALL
|
|
8724
|
+
* ( SELECT * FROM two ORDER y ASC LIMIT 2 OFFSET 2 )
|
|
8725
|
+
* -- order, limit, offset of the whole query
|
|
8726
|
+
* ORDER BY z ASC LIMIT 3 OFFSET 3
|
|
8727
|
+
* ```
|
|
8728
|
+
*
|
|
8729
|
+
* All the listed methods have the same signature, they are only different by SQL keyword:
|
|
8730
|
+
*
|
|
8731
|
+
* - `union` - union of all queries, performs deduplication
|
|
8732
|
+
* - `unionAll` - `union` that allows duplicated rows
|
|
8733
|
+
* - `intersect` - get only rows that are present in all queries
|
|
8734
|
+
* - `intersectAll` - `intersect` that allows duplicated rows
|
|
8735
|
+
* - `except` - get only rows that are in the first query but not in the second
|
|
8736
|
+
* - `exceptAll` - `except` that allows duplicated rows
|
|
8737
|
+
*
|
|
8738
|
+
* @param args - array of queries or SQL expressions
|
|
8739
|
+
*/
|
|
8740
|
+
union(...args) {
|
|
8741
|
+
return _queryUnion(
|
|
8742
|
+
_clone(this),
|
|
8743
|
+
args,
|
|
8744
|
+
"UNION"
|
|
8745
|
+
);
|
|
8746
|
+
}
|
|
8747
|
+
/**
|
|
8748
|
+
* Same as {@link union}, but allows duplicated rows.
|
|
8749
|
+
*
|
|
8750
|
+
* @param args - array of queries or SQL expressions
|
|
8751
|
+
*/
|
|
8752
|
+
unionAll(...args) {
|
|
8753
|
+
return _queryUnion(
|
|
8754
|
+
_clone(this),
|
|
8755
|
+
args,
|
|
8756
|
+
"UNION ALL"
|
|
8757
|
+
);
|
|
8758
|
+
}
|
|
8759
|
+
/**
|
|
8760
|
+
* Same as {@link union}, but uses a `INTERSECT` SQL keyword instead
|
|
8761
|
+
*
|
|
8762
|
+
* @param args - array of queries or SQL expressions
|
|
8763
|
+
*/
|
|
8764
|
+
intersect(...args) {
|
|
8765
|
+
return _queryUnion(
|
|
8766
|
+
_clone(this),
|
|
8767
|
+
args,
|
|
8768
|
+
"INTERSECT"
|
|
8769
|
+
);
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8772
|
+
* Same as {@link intersect}, but allows duplicated rows.
|
|
8773
|
+
*
|
|
8774
|
+
* @param args - array of queries or SQL expressions
|
|
8775
|
+
*/
|
|
8776
|
+
intersectAll(...args) {
|
|
8777
|
+
return _queryUnion(
|
|
8778
|
+
_clone(this),
|
|
8779
|
+
args,
|
|
8780
|
+
"INTERSECT ALL"
|
|
8622
8781
|
);
|
|
8623
8782
|
}
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8783
|
+
/**
|
|
8784
|
+
* Same as {@link union}, but uses an `EXCEPT` SQL keyword instead
|
|
8785
|
+
*
|
|
8786
|
+
* @param args - array of queries or SQL expressions
|
|
8787
|
+
*/
|
|
8788
|
+
except(...args) {
|
|
8789
|
+
return _queryUnion(
|
|
8790
|
+
_clone(this),
|
|
8791
|
+
args,
|
|
8792
|
+
"EXCEPT"
|
|
8793
|
+
);
|
|
8628
8794
|
}
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8795
|
+
/**
|
|
8796
|
+
* Same as {@link except}, but allows duplicated rows.
|
|
8797
|
+
*
|
|
8798
|
+
* @param args - array of queries or SQL expressions
|
|
8799
|
+
*/
|
|
8800
|
+
exceptAll(...args) {
|
|
8801
|
+
return _queryUnion(
|
|
8802
|
+
_clone(this),
|
|
8803
|
+
args,
|
|
8804
|
+
"EXCEPT ALL"
|
|
8805
|
+
);
|
|
8806
|
+
}
|
|
8807
|
+
}
|
|
8633
8808
|
|
|
8634
8809
|
const _addCte = (query, item) => pushOrNewArrayToObjectImmutable(query.q, "with", item);
|
|
8635
8810
|
const _prependWith = (q, name, queryArg) => _with(q, name, queryArg, void 0, true);
|
|
@@ -8740,9 +8915,6 @@ const columnToSql = (ctx, data, shape, column, quotedAs, select) => {
|
|
|
8740
8915
|
select
|
|
8741
8916
|
);
|
|
8742
8917
|
}
|
|
8743
|
-
if (!select && data.joinedShapes?.[column]) {
|
|
8744
|
-
return `"${column}"."${column}"`;
|
|
8745
|
-
}
|
|
8746
8918
|
return simpleColumnToSQL(ctx, column, shape[column], quotedAs);
|
|
8747
8919
|
};
|
|
8748
8920
|
const maybeSelectedColumnToSql = (ctx, data, column, quotedAs) => {
|
|
@@ -9080,97 +9252,285 @@ const _getSelectableColumn = (q, arg) => {
|
|
|
9080
9252
|
}
|
|
9081
9253
|
}
|
|
9082
9254
|
}
|
|
9083
|
-
return type;
|
|
9255
|
+
return type;
|
|
9256
|
+
};
|
|
9257
|
+
const _get = (query, returnType, arg) => {
|
|
9258
|
+
const q = query.q;
|
|
9259
|
+
if (q.returning) q.returning = void 0;
|
|
9260
|
+
q.returnType = returnType;
|
|
9261
|
+
let type;
|
|
9262
|
+
if (typeof arg === "string") {
|
|
9263
|
+
const joinedAs = q.valuesJoinedAs?.[arg];
|
|
9264
|
+
type = joinedAs ? q.joinedShapes?.[joinedAs]?.value : _getSelectableColumn(query, arg);
|
|
9265
|
+
q.getColumn = type;
|
|
9266
|
+
const selected = setParserForSelectedString(
|
|
9267
|
+
query,
|
|
9268
|
+
joinedAs ? joinedAs + "." + arg : arg,
|
|
9269
|
+
getQueryAs(query),
|
|
9270
|
+
getValueKey
|
|
9271
|
+
);
|
|
9272
|
+
q.select = selected ? [q.expr = new SelectItemExpression(query, selected, type)] : void 0;
|
|
9273
|
+
} else {
|
|
9274
|
+
type = arg.result.value;
|
|
9275
|
+
q.getColumn = type;
|
|
9276
|
+
addParserForRawExpression(query, getValueKey, arg);
|
|
9277
|
+
q.select = [q.expr = arg];
|
|
9278
|
+
}
|
|
9279
|
+
return setQueryOperators(
|
|
9280
|
+
query,
|
|
9281
|
+
type?.operators || Operators.any
|
|
9282
|
+
);
|
|
9283
|
+
};
|
|
9284
|
+
function _queryGet(self, arg) {
|
|
9285
|
+
return _get(self, "valueOrThrow", arg);
|
|
9286
|
+
}
|
|
9287
|
+
function _queryGetOptional(self, arg) {
|
|
9288
|
+
return _get(self, "value", arg);
|
|
9289
|
+
}
|
|
9290
|
+
|
|
9291
|
+
class RowToJsonExpression extends Expression {
|
|
9292
|
+
constructor(from, one, coalesce) {
|
|
9293
|
+
super();
|
|
9294
|
+
this.from = from;
|
|
9295
|
+
this.one = one;
|
|
9296
|
+
this.coalesce = coalesce;
|
|
9297
|
+
this.result = { value: UnknownColumn.instance };
|
|
9298
|
+
this.q = { expr: this };
|
|
9299
|
+
}
|
|
9300
|
+
makeSQL(ctx) {
|
|
9301
|
+
const q = this.from;
|
|
9302
|
+
const aliases = [];
|
|
9303
|
+
const jsonList = {};
|
|
9304
|
+
const select = selectToSql(
|
|
9305
|
+
ctx,
|
|
9306
|
+
q,
|
|
9307
|
+
q.q,
|
|
9308
|
+
`"${getQueryAs(q)}"`,
|
|
9309
|
+
q.q.hookSelect,
|
|
9310
|
+
void 0,
|
|
9311
|
+
aliases,
|
|
9312
|
+
jsonList
|
|
9313
|
+
);
|
|
9314
|
+
q.q.selectCache = { sql: select, aliases };
|
|
9315
|
+
let rowToJson;
|
|
9316
|
+
if (Object.values(jsonList).some((x) => x?.data.jsonCast)) {
|
|
9317
|
+
rowToJson = `json_build_object(${Object.entries(jsonList).map(
|
|
9318
|
+
([key, column]) => `'${key}', t."${key}"${column?.data.jsonCast ? `::${column.data.jsonCast}` : ""}`
|
|
9319
|
+
).join(", ")})`;
|
|
9320
|
+
} else {
|
|
9321
|
+
rowToJson = "row_to_json(t.*)";
|
|
9322
|
+
}
|
|
9323
|
+
return this.one ? rowToJson : this.coalesce !== false ? `COALESCE(json_agg(${rowToJson}), '[]')` : `json_agg(${rowToJson})`;
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
function queryJson(self, coalesce) {
|
|
9327
|
+
const inner = self.clone();
|
|
9328
|
+
const q = queryWrap(inner, cloneQueryBaseUnscoped(inner));
|
|
9329
|
+
_queryGetOptional(
|
|
9330
|
+
q,
|
|
9331
|
+
new RowToJsonExpression(
|
|
9332
|
+
inner,
|
|
9333
|
+
queryTypeWithLimitOne[self.q.returnType],
|
|
9334
|
+
coalesce
|
|
9335
|
+
)
|
|
9336
|
+
);
|
|
9337
|
+
q.q.returnsOne = true;
|
|
9338
|
+
return q;
|
|
9339
|
+
}
|
|
9340
|
+
|
|
9341
|
+
const moveQueryToCte = (ctx, query, type, dontAddTableHook) => {
|
|
9342
|
+
const { returnType } = query.q;
|
|
9343
|
+
let valueAs;
|
|
9344
|
+
if (returnType === "value" || returnType === "valueOrThrow" || returnType === "pluck") {
|
|
9345
|
+
const first = query.q.select[0];
|
|
9346
|
+
if (first instanceof SelectItemExpression && typeof first.item === "string") {
|
|
9347
|
+
valueAs = first.item;
|
|
9348
|
+
} else {
|
|
9349
|
+
query = _clone(query);
|
|
9350
|
+
query.q.returnType = "one";
|
|
9351
|
+
query.q.select = [{ selectAs: { value: query.q.select[0] } }];
|
|
9352
|
+
valueAs = "value";
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9355
|
+
const as = addTopCte("before", ctx, query, type, void 0, dontAddTableHook);
|
|
9356
|
+
const makeSelectList = (isSubSql) => {
|
|
9357
|
+
const list = [];
|
|
9358
|
+
let selectedCount = 0;
|
|
9359
|
+
if (valueAs) {
|
|
9360
|
+
selectedCount = 1;
|
|
9361
|
+
list.push(`"${as}"."${valueAs}"`);
|
|
9362
|
+
} else if (returnType !== "void") {
|
|
9363
|
+
const shape = getShapeFromSelect(query, true);
|
|
9364
|
+
const keys = Object.keys(shape);
|
|
9365
|
+
selectedCount = keys.length;
|
|
9366
|
+
list.push(...keys.map((key) => `"${as}"."${key}"`));
|
|
9367
|
+
}
|
|
9368
|
+
if (!isSubSql && ctx.topCtx.cteHooks?.hasSelect) {
|
|
9369
|
+
list.push("NULL::json");
|
|
9370
|
+
ctx.selectedCount = selectedCount;
|
|
9371
|
+
}
|
|
9372
|
+
return list;
|
|
9373
|
+
};
|
|
9374
|
+
return {
|
|
9375
|
+
as,
|
|
9376
|
+
makeSelectList
|
|
9377
|
+
};
|
|
9378
|
+
};
|
|
9379
|
+
const moveMutativeQueryToCteBase = (toSql, ctx, query, type = query.q.type) => {
|
|
9380
|
+
if (!query.q.type) {
|
|
9381
|
+
const as2 = getQueryAs(query);
|
|
9382
|
+
return {
|
|
9383
|
+
as: as2,
|
|
9384
|
+
makeSql: () => getSqlText(toSql(query, query.q.type, ctx, true))
|
|
9385
|
+
};
|
|
9386
|
+
}
|
|
9387
|
+
const { as, makeSelectList } = moveQueryToCte(ctx, query, type);
|
|
9388
|
+
return {
|
|
9389
|
+
as,
|
|
9390
|
+
// need to be called lazily for the upsert case because `ctx.cteHooks?.hasSelect` can change after the first query
|
|
9391
|
+
makeSql(isSubSql) {
|
|
9392
|
+
return "SELECT " + makeSelectList(isSubSql) + ` FROM "${as}"`;
|
|
9393
|
+
}
|
|
9394
|
+
};
|
|
9395
|
+
};
|
|
9396
|
+
|
|
9397
|
+
const addTableHook = (ctx, q, data, select, hookPurpose, dontAddTableHook) => {
|
|
9398
|
+
var _a, _b;
|
|
9399
|
+
if (data.ensureCount !== void 0 && ctx.cteName) {
|
|
9400
|
+
const cteHooks = setCteHooks(ctx, true);
|
|
9401
|
+
(cteHooks.ensureCount ?? (cteHooks.ensureCount = {}))[ctx.cteName] = { count: data.ensureCount };
|
|
9402
|
+
}
|
|
9403
|
+
const afterCreate = data.afterCreate;
|
|
9404
|
+
const afterUpdate = data.afterUpdate;
|
|
9405
|
+
const afterSave = data.afterSave;
|
|
9406
|
+
const afterDelete = data.afterDelete;
|
|
9407
|
+
const afterCreateCommit = data.afterCreateCommit;
|
|
9408
|
+
const afterUpdateCommit = data.afterUpdateCommit;
|
|
9409
|
+
const afterSaveCommit = data.afterSaveCommit;
|
|
9410
|
+
const afterDeleteCommit = data.afterDeleteCommit;
|
|
9411
|
+
const throwOnNotFound = (
|
|
9412
|
+
// Assuming that Create always returns a record, though needs to be cautions with onConflictDoNothing
|
|
9413
|
+
hookPurpose !== "Create" && (data.returnType === "oneOrThrow" || data.returnType === "valueOrThrow")
|
|
9414
|
+
);
|
|
9415
|
+
const hasAfterHook = afterCreate || afterUpdate || afterSave || afterDelete || afterCreateCommit || afterUpdateCommit || afterSaveCommit || afterDeleteCommit;
|
|
9416
|
+
if (!select && !hasAfterHook && !throwOnNotFound) {
|
|
9417
|
+
return;
|
|
9418
|
+
}
|
|
9419
|
+
const tableHook = {
|
|
9420
|
+
hookPurpose,
|
|
9421
|
+
select,
|
|
9422
|
+
afterCreate,
|
|
9423
|
+
afterUpdate,
|
|
9424
|
+
afterSave,
|
|
9425
|
+
afterDelete,
|
|
9426
|
+
afterCreateCommit,
|
|
9427
|
+
afterUpdateCommit,
|
|
9428
|
+
afterSaveCommit,
|
|
9429
|
+
afterDeleteCommit
|
|
9430
|
+
};
|
|
9431
|
+
if (ctx.cteName && !dontAddTableHook) {
|
|
9432
|
+
if (tableHook && (hasAfterHook || throwOnNotFound)) {
|
|
9433
|
+
const shape = {};
|
|
9434
|
+
if (tableHook.select) {
|
|
9435
|
+
for (const key of tableHook.select.keys()) {
|
|
9436
|
+
shape[key] = q.shape[key];
|
|
9437
|
+
}
|
|
9438
|
+
}
|
|
9439
|
+
const item = {
|
|
9440
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
9441
|
+
table: q.table,
|
|
9442
|
+
shape,
|
|
9443
|
+
tableHook,
|
|
9444
|
+
throwOnNotFound
|
|
9445
|
+
};
|
|
9446
|
+
const hasSelect = throwOnNotFound || !!tableHook.select;
|
|
9447
|
+
const cteHooks = setCteHooks(ctx, hasSelect);
|
|
9448
|
+
(_a = cteHooks.tableHooks ?? (cteHooks.tableHooks = {}))[_b = ctx.cteName] ?? (_a[_b] = item);
|
|
9449
|
+
}
|
|
9450
|
+
} else {
|
|
9451
|
+
ctx.topCtx.tableHook = tableHook;
|
|
9452
|
+
}
|
|
9084
9453
|
};
|
|
9085
|
-
const
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
if (
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
q.getColumn = type;
|
|
9094
|
-
const selected = setParserForSelectedString(
|
|
9095
|
-
query,
|
|
9096
|
-
joinedAs ? joinedAs + "." + arg : arg,
|
|
9097
|
-
getQueryAs(query),
|
|
9098
|
-
getValueKey
|
|
9099
|
-
);
|
|
9100
|
-
q.select = selected ? [q.expr = new SelectItemExpression(query, selected, type)] : void 0;
|
|
9454
|
+
const setCteHooks = (ctx, hasSelect) => {
|
|
9455
|
+
if (hasSelect && ctx.topCtx.selectList && ctx.topCtx === ctx.topCtx.topCtx && !ctx.topCtx.cteHookTopNullSelectAppended) {
|
|
9456
|
+
ctx.topCtx.selectList.push("NULL");
|
|
9457
|
+
ctx.topCtx.cteHookTopNullSelectAppended = true;
|
|
9458
|
+
}
|
|
9459
|
+
if (ctx.topCtx.cteHooks) {
|
|
9460
|
+
if (hasSelect) ctx.topCtx.cteHooks.hasSelect = true;
|
|
9461
|
+
return ctx.topCtx.cteHooks;
|
|
9101
9462
|
} else {
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
q.select = [q.expr = arg];
|
|
9463
|
+
return ctx.topCtx.cteHooks = {
|
|
9464
|
+
hasSelect
|
|
9465
|
+
};
|
|
9106
9466
|
}
|
|
9107
|
-
return setQueryOperators(
|
|
9108
|
-
query,
|
|
9109
|
-
type?.operators || Operators.any
|
|
9110
|
-
);
|
|
9111
9467
|
};
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
}
|
|
9115
|
-
|
|
9116
|
-
return _get(self, "value", arg);
|
|
9117
|
-
}
|
|
9468
|
+
const ensureCTECount = (ctx, cteName, countItem) => {
|
|
9469
|
+
var _a;
|
|
9470
|
+
((_a = setCteHooks(ctx, true)).ensureCount ?? (_a.ensureCount = {}))[cteName] = countItem;
|
|
9471
|
+
};
|
|
9118
9472
|
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9473
|
+
const newMutativeQueriesSelectRelationsSqlState = (query) => query.q.selectRelation && {
|
|
9474
|
+
query
|
|
9475
|
+
};
|
|
9476
|
+
const setMutativeQueriesSelectRelationsSqlState = (d, as, rel) => {
|
|
9477
|
+
(d.value ?? (d.value = {}))[as] = rel;
|
|
9478
|
+
};
|
|
9479
|
+
const handleInsertAndUpdateSelectRelationsSqlState = (ctx, state) => {
|
|
9480
|
+
if (state) {
|
|
9481
|
+
ctx.topCtx.mutativeQueriesSelectRelationsSqlState = state;
|
|
9127
9482
|
}
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9483
|
+
};
|
|
9484
|
+
const handleDeleteSelectRelationsSqlState = (ctx, query, relationSelectState, returning) => {
|
|
9485
|
+
const selectRelations = relationSelectState?.value;
|
|
9486
|
+
if (!selectRelations) return;
|
|
9487
|
+
const selectPrimaryKeysQuery = prepareSubQueryForSql(query, _clone(query));
|
|
9488
|
+
const primaryKeys = requirePrimaryKeys(
|
|
9489
|
+
query,
|
|
9490
|
+
"primary keys are required for selecting relation in delete"
|
|
9491
|
+
);
|
|
9492
|
+
_addToHookSelect(selectPrimaryKeysQuery, primaryKeys, true);
|
|
9493
|
+
const { as: cteAs } = moveQueryToCte(
|
|
9494
|
+
ctx,
|
|
9495
|
+
selectPrimaryKeysQuery,
|
|
9496
|
+
void 0,
|
|
9497
|
+
true
|
|
9498
|
+
);
|
|
9499
|
+
const relKeys = Object.keys(selectRelations);
|
|
9500
|
+
ctx.selectedCount = (returning ? ctx.selectedCount : 0) + relKeys.length;
|
|
9501
|
+
const hookSelect = selectPrimaryKeysQuery.q.hookSelect;
|
|
9502
|
+
const join = {
|
|
9503
|
+
type: "JOIN",
|
|
9504
|
+
args: {
|
|
9505
|
+
w: cteAs,
|
|
9506
|
+
a: [
|
|
9507
|
+
Object.fromEntries(
|
|
9508
|
+
primaryKeys.map((key) => {
|
|
9509
|
+
const selected = hookSelect.get(key);
|
|
9510
|
+
return [
|
|
9511
|
+
cteAs + "." + (selected.as || selected.select),
|
|
9512
|
+
key
|
|
9513
|
+
];
|
|
9514
|
+
})
|
|
9515
|
+
)
|
|
9516
|
+
]
|
|
9517
|
+
}
|
|
9518
|
+
};
|
|
9519
|
+
for (const relKey in selectRelations) {
|
|
9520
|
+
const rel = selectRelations[relKey];
|
|
9521
|
+
if (rel.q.returnType === "oneOrThrow" || rel.q.returnType === "valueOrThrow") {
|
|
9522
|
+
ensureCTECount(ctx, cteAs, { jsonNotNull: relKey });
|
|
9150
9523
|
}
|
|
9151
|
-
return this.one ? rowToJson : this.coalesce !== false ? `COALESCE(json_agg(${rowToJson}), '[]')` : `json_agg(${rowToJson})`;
|
|
9152
9524
|
}
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
coalesce
|
|
9163
|
-
)
|
|
9164
|
-
);
|
|
9165
|
-
q.q.returnsOne = true;
|
|
9166
|
-
return q;
|
|
9167
|
-
}
|
|
9168
|
-
|
|
9169
|
-
const newDelayedRelationSelect = (query) => ({
|
|
9170
|
-
query
|
|
9171
|
-
});
|
|
9172
|
-
const setDelayedRelation = (d, as, value) => {
|
|
9173
|
-
(d.value ?? (d.value = {}))[as] = value;
|
|
9525
|
+
return {
|
|
9526
|
+
join,
|
|
9527
|
+
joinedShape: cteAs,
|
|
9528
|
+
movedWhereToCte: true,
|
|
9529
|
+
addReturning: relKeys.map((key) => `${cteAs}."${key}"`).join(", ")
|
|
9530
|
+
};
|
|
9531
|
+
};
|
|
9532
|
+
const setMutativeQueriesSelectRelationsStateOnSql = (ctx, sql) => {
|
|
9533
|
+
sql.mutativeQueriesSelectRelationsState = ctx.topCtx.mutativeQueriesSelectRelationsSqlState;
|
|
9174
9534
|
};
|
|
9175
9535
|
|
|
9176
9536
|
const setSqlCtxSelectList = (ctx, table, query, quotedAs, isSubSql, aliases) => {
|
|
@@ -9269,7 +9629,11 @@ const selectToSqlList = (ctx, table, query, quotedAs, hookSelect = query.hookSel
|
|
|
9269
9629
|
}
|
|
9270
9630
|
aliases?.push(as);
|
|
9271
9631
|
} else if (delayedRelationSelect && isRelationQuery(value)) {
|
|
9272
|
-
|
|
9632
|
+
setMutativeQueriesSelectRelationsSqlState(
|
|
9633
|
+
delayedRelationSelect,
|
|
9634
|
+
as,
|
|
9635
|
+
value
|
|
9636
|
+
);
|
|
9273
9637
|
} else {
|
|
9274
9638
|
pushSubQuerySql(ctx, query, value, as, list, quotedAs, aliases);
|
|
9275
9639
|
if (jsonList) {
|
|
@@ -9401,13 +9765,13 @@ const internalSelectAllSql = (ctx, query, quotedAs, jsonList) => {
|
|
|
9401
9765
|
}
|
|
9402
9766
|
let columnsCount;
|
|
9403
9767
|
if (query.shape !== anyShape) {
|
|
9404
|
-
|
|
9768
|
+
columnsCount = 0;
|
|
9405
9769
|
for (const key in query.shape) {
|
|
9406
9770
|
if (!query.shape[key].data.explicitSelect) {
|
|
9407
|
-
|
|
9771
|
+
columnsCount++;
|
|
9408
9772
|
}
|
|
9409
9773
|
}
|
|
9410
|
-
ctx.selectedCount +=
|
|
9774
|
+
ctx.selectedCount += columnsCount;
|
|
9411
9775
|
}
|
|
9412
9776
|
return selectAllSql(query, quotedAs, columnsCount);
|
|
9413
9777
|
};
|
|
@@ -9778,134 +10142,6 @@ const pushIn = (ctx, query, ands, quotedAs, arg) => {
|
|
|
9778
10142
|
|
|
9779
10143
|
const MAX_BINDING_PARAMS = 65536;
|
|
9780
10144
|
|
|
9781
|
-
const addTableHook = (ctx, q, data, select, hookPurpose) => {
|
|
9782
|
-
var _a, _b;
|
|
9783
|
-
if (data.ensureCount !== void 0 && ctx.cteName) {
|
|
9784
|
-
const cteHooks = setCteHooks(ctx, true);
|
|
9785
|
-
(cteHooks.ensureCount ?? (cteHooks.ensureCount = {}))[ctx.cteName] = data.ensureCount;
|
|
9786
|
-
}
|
|
9787
|
-
const afterCreate = data.afterCreate;
|
|
9788
|
-
const afterUpdate = data.afterUpdate;
|
|
9789
|
-
const afterSave = data.afterSave;
|
|
9790
|
-
const afterDelete = data.afterDelete;
|
|
9791
|
-
const afterCreateCommit = data.afterCreateCommit;
|
|
9792
|
-
const afterUpdateCommit = data.afterUpdateCommit;
|
|
9793
|
-
const afterSaveCommit = data.afterSaveCommit;
|
|
9794
|
-
const afterDeleteCommit = data.afterDeleteCommit;
|
|
9795
|
-
const throwOnNotFound = (
|
|
9796
|
-
// Assuming that Create always returns a record, though needs to be cautions with onConflictDoNothing
|
|
9797
|
-
hookPurpose !== "Create" && (data.returnType === "oneOrThrow" || data.returnType === "valueOrThrow")
|
|
9798
|
-
);
|
|
9799
|
-
const hasAfterHook = afterCreate || afterUpdate || afterSave || afterDelete || afterCreateCommit || afterUpdateCommit || afterSaveCommit || afterDeleteCommit;
|
|
9800
|
-
if (!select && !hasAfterHook && !throwOnNotFound) {
|
|
9801
|
-
return;
|
|
9802
|
-
}
|
|
9803
|
-
const tableHook = {
|
|
9804
|
-
hookPurpose,
|
|
9805
|
-
select,
|
|
9806
|
-
afterCreate,
|
|
9807
|
-
afterUpdate,
|
|
9808
|
-
afterSave,
|
|
9809
|
-
afterDelete,
|
|
9810
|
-
afterCreateCommit,
|
|
9811
|
-
afterUpdateCommit,
|
|
9812
|
-
afterSaveCommit,
|
|
9813
|
-
afterDeleteCommit
|
|
9814
|
-
};
|
|
9815
|
-
if (ctx.cteName) {
|
|
9816
|
-
if (tableHook && (hasAfterHook || throwOnNotFound)) {
|
|
9817
|
-
const shape = {};
|
|
9818
|
-
if (tableHook.select) {
|
|
9819
|
-
for (const key of tableHook.select.keys()) {
|
|
9820
|
-
shape[key] = q.shape[key];
|
|
9821
|
-
}
|
|
9822
|
-
}
|
|
9823
|
-
const item = {
|
|
9824
|
-
table: q.table,
|
|
9825
|
-
shape,
|
|
9826
|
-
tableHook,
|
|
9827
|
-
throwOnNotFound
|
|
9828
|
-
};
|
|
9829
|
-
const hasSelect = throwOnNotFound || !!tableHook.select;
|
|
9830
|
-
const cteHooks = setCteHooks(ctx, hasSelect);
|
|
9831
|
-
(_a = cteHooks.tableHooks)[_b = ctx.cteName] ?? (_a[_b] = item);
|
|
9832
|
-
}
|
|
9833
|
-
} else {
|
|
9834
|
-
ctx.topCtx.tableHook = tableHook;
|
|
9835
|
-
}
|
|
9836
|
-
};
|
|
9837
|
-
const setCteHooks = (ctx, hasSelect) => {
|
|
9838
|
-
if (hasSelect && ctx.topCtx.selectList && ctx.topCtx === ctx.topCtx.topCtx && !ctx.topCtx.cteHookTopNullSelectAppended) {
|
|
9839
|
-
ctx.topCtx.selectList.push("NULL");
|
|
9840
|
-
ctx.topCtx.cteHookTopNullSelectAppended = true;
|
|
9841
|
-
}
|
|
9842
|
-
if (ctx.topCtx.cteHooks) {
|
|
9843
|
-
if (hasSelect) ctx.topCtx.cteHooks.hasSelect = true;
|
|
9844
|
-
return ctx.topCtx.cteHooks;
|
|
9845
|
-
} else {
|
|
9846
|
-
return ctx.topCtx.cteHooks = {
|
|
9847
|
-
hasSelect,
|
|
9848
|
-
tableHooks: {}
|
|
9849
|
-
};
|
|
9850
|
-
}
|
|
9851
|
-
};
|
|
9852
|
-
|
|
9853
|
-
const moveQueryToCte = (ctx, query, type = query.q.type) => {
|
|
9854
|
-
const { returnType } = query.q;
|
|
9855
|
-
let valueAs;
|
|
9856
|
-
if (returnType === "value" || returnType === "valueOrThrow" || returnType === "pluck") {
|
|
9857
|
-
const first = query.q.select[0];
|
|
9858
|
-
if (first instanceof SelectItemExpression && typeof first.item === "string") {
|
|
9859
|
-
valueAs = first.item;
|
|
9860
|
-
} else {
|
|
9861
|
-
query = _clone(query);
|
|
9862
|
-
query.q.returnType = "one";
|
|
9863
|
-
query.q.select = [{ selectAs: { value: query.q.select[0] } }];
|
|
9864
|
-
valueAs = "value";
|
|
9865
|
-
}
|
|
9866
|
-
}
|
|
9867
|
-
const as = addTopCte("before", ctx, query, void 0, type);
|
|
9868
|
-
const makeSelectList = (isSubSql) => {
|
|
9869
|
-
const list = [];
|
|
9870
|
-
let selectedCount = 0;
|
|
9871
|
-
if (valueAs) {
|
|
9872
|
-
selectedCount = 1;
|
|
9873
|
-
list.push(`"${as}"."${valueAs}"`);
|
|
9874
|
-
} else if (returnType !== "void") {
|
|
9875
|
-
const shape = getShapeFromSelect(query, true);
|
|
9876
|
-
const keys = Object.keys(shape);
|
|
9877
|
-
selectedCount = keys.length;
|
|
9878
|
-
list.push(...keys.map((key) => `"${as}"."${key}"`));
|
|
9879
|
-
}
|
|
9880
|
-
if (!isSubSql && ctx.topCtx.cteHooks?.hasSelect) {
|
|
9881
|
-
list.push("NULL::json");
|
|
9882
|
-
ctx.selectedCount = selectedCount;
|
|
9883
|
-
}
|
|
9884
|
-
return list;
|
|
9885
|
-
};
|
|
9886
|
-
return {
|
|
9887
|
-
as,
|
|
9888
|
-
makeSelectList
|
|
9889
|
-
};
|
|
9890
|
-
};
|
|
9891
|
-
const moveMutativeQueryToCteBase = (toSql, ctx, query, type = query.q.type) => {
|
|
9892
|
-
if (!query.q.type) {
|
|
9893
|
-
const as2 = getQueryAs(query);
|
|
9894
|
-
return {
|
|
9895
|
-
as: as2,
|
|
9896
|
-
makeSql: () => getSqlText(toSql(query, query.q.type, ctx, true))
|
|
9897
|
-
};
|
|
9898
|
-
}
|
|
9899
|
-
const { as, makeSelectList } = moveQueryToCte(ctx, query, type);
|
|
9900
|
-
return {
|
|
9901
|
-
as,
|
|
9902
|
-
// need to be called lazily for the upsert case because `ctx.cteHooks?.hasSelect` can change after the first query
|
|
9903
|
-
makeSql(isSubSql) {
|
|
9904
|
-
return "SELECT " + makeSelectList(isSubSql) + ` FROM "${as}"`;
|
|
9905
|
-
}
|
|
9906
|
-
};
|
|
9907
|
-
};
|
|
9908
|
-
|
|
9909
10145
|
const makeInsertSql = (ctx, q, query, quotedAs, isSubSql) => {
|
|
9910
10146
|
let { columns } = query;
|
|
9911
10147
|
const { shape, hookCreateSet } = query;
|
|
@@ -9974,7 +10210,7 @@ const makeInsertSql = (ctx, q, query, quotedAs, isSubSql) => {
|
|
|
9974
10210
|
if (upsert || insertFrom && !isRelationQuery(q) || hasOnConflictWhere) {
|
|
9975
10211
|
pushWhereStatementSql(ctx, q, query, quotedAs);
|
|
9976
10212
|
}
|
|
9977
|
-
sqlState.
|
|
10213
|
+
sqlState.relationSelectState = newMutativeQueriesSelectRelationsSqlState(q);
|
|
9978
10214
|
sqlState.returningPos = ctx.sql.length;
|
|
9979
10215
|
let insertManyFromValuesAs;
|
|
9980
10216
|
if (insertFrom) {
|
|
@@ -9998,7 +10234,11 @@ const makeInsertSql = (ctx, q, query, quotedAs, isSubSql) => {
|
|
|
9998
10234
|
}
|
|
9999
10235
|
ctx.sql[1] = moveMutativeQueryToCte(ctx, q2);
|
|
10000
10236
|
} else {
|
|
10001
|
-
const { as, makeSelectList } = moveQueryToCte(
|
|
10237
|
+
const { as, makeSelectList } = moveQueryToCte(
|
|
10238
|
+
ctx,
|
|
10239
|
+
insertFrom,
|
|
10240
|
+
insertFrom.q.type
|
|
10241
|
+
);
|
|
10002
10242
|
const selectList = makeSelectList(true);
|
|
10003
10243
|
insertManyFromValuesAs = query.insertValuesAs;
|
|
10004
10244
|
selectList.push(
|
|
@@ -10064,18 +10304,20 @@ const makeInsertSql = (ctx, q, query, quotedAs, isSubSql) => {
|
|
|
10064
10304
|
}
|
|
10065
10305
|
applySqlState(sqlState);
|
|
10066
10306
|
batch.push(composeCteSingleSql(ctx));
|
|
10067
|
-
|
|
10068
|
-
ctx
|
|
10069
|
-
|
|
10307
|
+
handleInsertAndUpdateSelectRelationsSqlState(
|
|
10308
|
+
ctx,
|
|
10309
|
+
sqlState.relationSelectState
|
|
10310
|
+
);
|
|
10070
10311
|
return {
|
|
10071
10312
|
batch
|
|
10072
10313
|
};
|
|
10073
10314
|
}
|
|
10074
10315
|
}
|
|
10075
10316
|
applySqlState(sqlState);
|
|
10076
|
-
|
|
10077
|
-
ctx
|
|
10078
|
-
|
|
10317
|
+
handleInsertAndUpdateSelectRelationsSqlState(
|
|
10318
|
+
ctx,
|
|
10319
|
+
sqlState.relationSelectState
|
|
10320
|
+
);
|
|
10079
10321
|
return {
|
|
10080
10322
|
text: ctx.sql.join(" "),
|
|
10081
10323
|
values: ctx.values
|
|
@@ -10162,7 +10404,7 @@ const applySqlState = (sqlState) => {
|
|
|
10162
10404
|
sqlState.q,
|
|
10163
10405
|
sqlState.query,
|
|
10164
10406
|
sqlState.quotedAs,
|
|
10165
|
-
sqlState.
|
|
10407
|
+
sqlState.relationSelectState,
|
|
10166
10408
|
"Create",
|
|
10167
10409
|
void 0,
|
|
10168
10410
|
sqlState.isSubSql || !!ctx.topCtx.cteHooks
|
|
@@ -10345,7 +10587,7 @@ const encodeValue = (ctx, values, QueryClass, value, quotedAs) => {
|
|
|
10345
10587
|
}
|
|
10346
10588
|
return value === void 0 ? "DEFAULT" : addValue(values, value);
|
|
10347
10589
|
};
|
|
10348
|
-
const makeReturningSql = (ctx, q, data, quotedAs,
|
|
10590
|
+
const makeReturningSql = (ctx, q, data, quotedAs, relationSelectState, hookPurpose, addHookPurpose, isSubSql) => {
|
|
10349
10591
|
const hookSelect = hookPurpose && data[`after${hookPurpose}Select`];
|
|
10350
10592
|
const { select } = data;
|
|
10351
10593
|
if (!q.q.hookSelect && !hookSelect?.size && !select?.length && !hookPurpose) {
|
|
@@ -10354,7 +10596,7 @@ const makeReturningSql = (ctx, q, data, quotedAs, delayedRelationSelect, hookPur
|
|
|
10354
10596
|
ctx.selectedCount = 1;
|
|
10355
10597
|
return returnSqlOrNull(ctx, q, isSubSql);
|
|
10356
10598
|
}
|
|
10357
|
-
const otherCTEHookSelect = addHookPurpose
|
|
10599
|
+
const otherCTEHookSelect = addHookPurpose;
|
|
10358
10600
|
let tempSelect;
|
|
10359
10601
|
if (q.q.hookSelect || hookSelect || otherCTEHookSelect || q.q.selectRelation) {
|
|
10360
10602
|
tempSelect = new Map(q.q.hookSelect);
|
|
@@ -10365,14 +10607,7 @@ const makeReturningSql = (ctx, q, data, quotedAs, delayedRelationSelect, hookPur
|
|
|
10365
10607
|
}
|
|
10366
10608
|
}
|
|
10367
10609
|
}
|
|
10368
|
-
if (
|
|
10369
|
-
for (const column of otherCTEHookSelect) {
|
|
10370
|
-
if (!tempSelect.has(column)) {
|
|
10371
|
-
tempSelect.set(column, { select: column });
|
|
10372
|
-
}
|
|
10373
|
-
}
|
|
10374
|
-
}
|
|
10375
|
-
if (q.q.selectRelation) {
|
|
10610
|
+
if (q.q.selectRelation && hookPurpose !== "Delete") {
|
|
10376
10611
|
for (const column of getPrimaryKeys(q)) {
|
|
10377
10612
|
if (!tempSelect.has(column)) {
|
|
10378
10613
|
tempSelect.set(column, { select: column });
|
|
@@ -10391,7 +10626,7 @@ const makeReturningSql = (ctx, q, data, quotedAs, delayedRelationSelect, hookPur
|
|
|
10391
10626
|
isSubSql,
|
|
10392
10627
|
void 0,
|
|
10393
10628
|
void 0,
|
|
10394
|
-
|
|
10629
|
+
relationSelectState
|
|
10395
10630
|
);
|
|
10396
10631
|
}
|
|
10397
10632
|
addTableHook(ctx, q, data, tempSelect, hookPurpose);
|
|
@@ -10435,7 +10670,7 @@ const pushUpdateSql = (ctx, query, q, quotedAs, isSubSql) => {
|
|
|
10435
10670
|
const hookSet = q.hookUpdateSet ? Object.fromEntries(
|
|
10436
10671
|
q.hookUpdateSet.flatMap((item) => Object.entries(item))
|
|
10437
10672
|
) : emptyObject;
|
|
10438
|
-
const
|
|
10673
|
+
const relationSelectState = newMutativeQueriesSelectRelationsSqlState(query);
|
|
10439
10674
|
const usedSetKeys = /* @__PURE__ */ new Set();
|
|
10440
10675
|
if (q.hookUpdateSet) {
|
|
10441
10676
|
applySet(ctx, query, set, hookSet, emptyObject, usedSetKeys, quotedAs);
|
|
@@ -10467,7 +10702,7 @@ const pushUpdateSql = (ctx, query, q, quotedAs, isSubSql) => {
|
|
|
10467
10702
|
from,
|
|
10468
10703
|
isSubSql,
|
|
10469
10704
|
updateManyValuesSql,
|
|
10470
|
-
|
|
10705
|
+
relationSelectState
|
|
10471
10706
|
);
|
|
10472
10707
|
} else {
|
|
10473
10708
|
ctx.sql.push(`UPDATE ${from}`);
|
|
@@ -10491,16 +10726,14 @@ const pushUpdateSql = (ctx, query, q, quotedAs, isSubSql) => {
|
|
|
10491
10726
|
q,
|
|
10492
10727
|
quotedAs,
|
|
10493
10728
|
"RETURNING",
|
|
10494
|
-
|
|
10729
|
+
relationSelectState,
|
|
10495
10730
|
isSubSql
|
|
10496
10731
|
);
|
|
10497
10732
|
}
|
|
10498
|
-
|
|
10499
|
-
ctx.topCtx.delayedRelationSelect = delayedRelationSelect;
|
|
10500
|
-
}
|
|
10733
|
+
handleInsertAndUpdateSelectRelationsSqlState(ctx, relationSelectState);
|
|
10501
10734
|
return makeSql(ctx, "update", isSubSql);
|
|
10502
10735
|
};
|
|
10503
|
-
const pushSelectForEmptySet = (ctx, query, q, quotedAs, from, isSubSql, updateManyValuesSql,
|
|
10736
|
+
const pushSelectForEmptySet = (ctx, query, q, quotedAs, from, isSubSql, updateManyValuesSql, relationSelectState) => {
|
|
10504
10737
|
if (!q.select) {
|
|
10505
10738
|
q.select = countSelect;
|
|
10506
10739
|
}
|
|
@@ -10510,7 +10743,7 @@ const pushSelectForEmptySet = (ctx, query, q, quotedAs, from, isSubSql, updateMa
|
|
|
10510
10743
|
q,
|
|
10511
10744
|
quotedAs,
|
|
10512
10745
|
"SELECT",
|
|
10513
|
-
|
|
10746
|
+
relationSelectState,
|
|
10514
10747
|
isSubSql
|
|
10515
10748
|
);
|
|
10516
10749
|
let fromSql = `FROM ${from}`;
|
|
@@ -10522,15 +10755,9 @@ const pushSelectForEmptySet = (ctx, query, q, quotedAs, from, isSubSql, updateMa
|
|
|
10522
10755
|
pushLimitSQL(ctx.sql, ctx.values, q);
|
|
10523
10756
|
};
|
|
10524
10757
|
const addUpdateManyCteForStrict = (ctx, updateMany) => {
|
|
10525
|
-
var _a;
|
|
10526
10758
|
const wrapAs = setFreeTopCteAs(ctx);
|
|
10527
10759
|
ctx.wrapAs = wrapAs;
|
|
10528
|
-
|
|
10529
|
-
hasSelect: true,
|
|
10530
|
-
tableHooks: {}
|
|
10531
|
-
});
|
|
10532
|
-
cteHooks.hasSelect = true;
|
|
10533
|
-
(cteHooks.ensureCount ?? (cteHooks.ensureCount = {}))[wrapAs] = updateMany.data.length;
|
|
10760
|
+
ensureCTECount(ctx, wrapAs, { count: updateMany.data.length });
|
|
10534
10761
|
};
|
|
10535
10762
|
const pushUpdateFromSql = (ctx, query, q, quotedAs, updateFrom) => {
|
|
10536
10763
|
const { target, on } = processJoinItem(ctx, query, q, updateFrom, quotedAs);
|
|
@@ -10566,13 +10793,13 @@ const pushUpdateWhereSql = (ctx, query, q, quotedAs, fromWhereSql) => {
|
|
|
10566
10793
|
ctx.sql.push("WHERE", whereSql);
|
|
10567
10794
|
}
|
|
10568
10795
|
};
|
|
10569
|
-
const pushUpdateReturning = (ctx, query, q, quotedAs, keyword,
|
|
10796
|
+
const pushUpdateReturning = (ctx, query, q, quotedAs, keyword, relationSelectState, isSubSql) => {
|
|
10570
10797
|
const returning = makeReturningSql(
|
|
10571
10798
|
ctx,
|
|
10572
10799
|
query,
|
|
10573
10800
|
q,
|
|
10574
10801
|
quotedAs,
|
|
10575
|
-
|
|
10802
|
+
relationSelectState,
|
|
10576
10803
|
"Update",
|
|
10577
10804
|
void 0,
|
|
10578
10805
|
isSubSql
|
|
@@ -10698,12 +10925,41 @@ const pushDeleteSql = (ctx, query, q, quotedAs, isSubSql) => {
|
|
|
10698
10925
|
if (q.as && query.table !== q.as) {
|
|
10699
10926
|
ctx.sql.push(quotedAs);
|
|
10700
10927
|
}
|
|
10928
|
+
const relationSelectState = newMutativeQueriesSelectRelationsSqlState(query);
|
|
10929
|
+
let returning = makeReturningSql(
|
|
10930
|
+
ctx,
|
|
10931
|
+
query,
|
|
10932
|
+
q,
|
|
10933
|
+
quotedAs,
|
|
10934
|
+
relationSelectState,
|
|
10935
|
+
"Delete",
|
|
10936
|
+
void 0,
|
|
10937
|
+
isSubSql
|
|
10938
|
+
);
|
|
10939
|
+
const selectRelations = handleDeleteSelectRelationsSqlState(
|
|
10940
|
+
ctx,
|
|
10941
|
+
query,
|
|
10942
|
+
relationSelectState,
|
|
10943
|
+
returning
|
|
10944
|
+
);
|
|
10945
|
+
let join = q.join;
|
|
10946
|
+
if (selectRelations) {
|
|
10947
|
+
returning = (returning ? returning + ", " : "") + selectRelations.addReturning;
|
|
10948
|
+
join = join ? [...join, selectRelations.join] : [selectRelations.join];
|
|
10949
|
+
q = {
|
|
10950
|
+
...q,
|
|
10951
|
+
joinedShapes: {
|
|
10952
|
+
...q.joinedShapes,
|
|
10953
|
+
[selectRelations.joinedShape]: anyShape
|
|
10954
|
+
}
|
|
10955
|
+
};
|
|
10956
|
+
}
|
|
10701
10957
|
let conditions;
|
|
10702
|
-
if (
|
|
10958
|
+
if (join?.length) {
|
|
10703
10959
|
const targets = [];
|
|
10704
10960
|
const ons = [];
|
|
10705
|
-
const joinSet =
|
|
10706
|
-
for (const item of
|
|
10961
|
+
const joinSet = join.length > 1 ? /* @__PURE__ */ new Set() : null;
|
|
10962
|
+
for (const item of join) {
|
|
10707
10963
|
const lateral = "l" in item.args && item.args.l;
|
|
10708
10964
|
if (lateral) {
|
|
10709
10965
|
if (isRelationQuery(lateral)) {
|
|
@@ -10714,42 +10970,32 @@ const pushDeleteSql = (ctx, query, q, quotedAs, isSubSql) => {
|
|
|
10714
10970
|
"Join lateral is not supported in delete"
|
|
10715
10971
|
);
|
|
10716
10972
|
}
|
|
10717
|
-
const
|
|
10718
|
-
const key = `${
|
|
10973
|
+
const join2 = processJoinItem(ctx, query, q, item.args, quotedAs);
|
|
10974
|
+
const key = `${join2.target}${join2.on}`;
|
|
10719
10975
|
if (joinSet) {
|
|
10720
10976
|
if (joinSet.has(key)) continue;
|
|
10721
10977
|
joinSet.add(key);
|
|
10722
10978
|
}
|
|
10723
|
-
targets.push(
|
|
10724
|
-
if (
|
|
10979
|
+
targets.push(join2.target);
|
|
10980
|
+
if (join2.on) ons.push(join2.on);
|
|
10725
10981
|
}
|
|
10726
10982
|
if (targets.length) {
|
|
10727
10983
|
ctx.sql.push(`USING ${targets.join(", ")}`);
|
|
10728
10984
|
}
|
|
10729
10985
|
conditions = ons.join(" AND ");
|
|
10730
10986
|
}
|
|
10731
|
-
|
|
10987
|
+
if (!selectRelations?.movedWhereToCte) {
|
|
10988
|
+
pushWhereStatementSql(ctx, query, q, quotedAs);
|
|
10989
|
+
}
|
|
10732
10990
|
if (conditions) {
|
|
10733
|
-
if (q.and?.length || q.or?.length || q.scopes) {
|
|
10991
|
+
if (!selectRelations?.movedWhereToCte && (q.and?.length || q.or?.length || q.scopes)) {
|
|
10734
10992
|
ctx.sql.push("AND", conditions);
|
|
10735
10993
|
} else {
|
|
10736
10994
|
ctx.sql.push("WHERE", conditions);
|
|
10737
10995
|
}
|
|
10738
10996
|
}
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
ctx,
|
|
10742
|
-
query,
|
|
10743
|
-
q,
|
|
10744
|
-
quotedAs,
|
|
10745
|
-
delayedRelationSelect,
|
|
10746
|
-
"Delete",
|
|
10747
|
-
void 0,
|
|
10748
|
-
isSubSql
|
|
10749
|
-
);
|
|
10750
|
-
if (returning) ctx.sql.push("RETURNING", returning);
|
|
10751
|
-
if (delayedRelationSelect) {
|
|
10752
|
-
ctx.topCtx.delayedRelationSelect = delayedRelationSelect;
|
|
10997
|
+
if (returning) {
|
|
10998
|
+
ctx.sql.push("RETURNING", returning);
|
|
10753
10999
|
}
|
|
10754
11000
|
return makeSql(ctx, "delete", isSubSql);
|
|
10755
11001
|
};
|
|
@@ -11009,7 +11255,19 @@ const pushForSql = (ctx, q, type, quotedAs) => {
|
|
|
11009
11255
|
}
|
|
11010
11256
|
};
|
|
11011
11257
|
|
|
11012
|
-
const
|
|
11258
|
+
const newToSqlCtx = (query) => {
|
|
11259
|
+
const ctx = {
|
|
11260
|
+
topCtx: void 0,
|
|
11261
|
+
qb: query.qb,
|
|
11262
|
+
q: query.q,
|
|
11263
|
+
sql: [],
|
|
11264
|
+
values: [],
|
|
11265
|
+
selectedCount: 0
|
|
11266
|
+
};
|
|
11267
|
+
ctx.topCtx = ctx;
|
|
11268
|
+
return ctx;
|
|
11269
|
+
};
|
|
11270
|
+
const toSql = (table, type, topCtx, isSubSql, cteName, calledByThen, dontAddTableHook) => {
|
|
11013
11271
|
const query = table.q;
|
|
11014
11272
|
const sql = [];
|
|
11015
11273
|
const values = topCtx?.values || [];
|
|
@@ -11189,7 +11447,14 @@ const toSql = (table, type, topCtx, isSubSql, cteName, calledByThen) => {
|
|
|
11189
11447
|
}
|
|
11190
11448
|
pushLimitOffsetSql(ctx, query, fromQuery);
|
|
11191
11449
|
pushForSql(ctx, query, type, quotedAs);
|
|
11192
|
-
addTableHook(
|
|
11450
|
+
addTableHook(
|
|
11451
|
+
ctx,
|
|
11452
|
+
table,
|
|
11453
|
+
query,
|
|
11454
|
+
query.hookSelect,
|
|
11455
|
+
void 0,
|
|
11456
|
+
dontAddTableHook
|
|
11457
|
+
);
|
|
11193
11458
|
if (selectSqlPos !== void 0 && ctx.selectList?.length) {
|
|
11194
11459
|
sql[selectSqlPos] += " " + ctx.selectList.join(", ");
|
|
11195
11460
|
}
|
|
@@ -11202,7 +11467,7 @@ const toSql = (table, type, topCtx, isSubSql, cteName, calledByThen) => {
|
|
|
11202
11467
|
if (!ctx.cteName) {
|
|
11203
11468
|
result.tableHook = ctx.topCtx.tableHook;
|
|
11204
11469
|
if (!topCtx) {
|
|
11205
|
-
result
|
|
11470
|
+
setMutativeQueriesSelectRelationsStateOnSql(ctx, result);
|
|
11206
11471
|
}
|
|
11207
11472
|
}
|
|
11208
11473
|
if (!isSubSql) {
|
|
@@ -11212,21 +11477,20 @@ const toSql = (table, type, topCtx, isSubSql, cteName, calledByThen) => {
|
|
|
11212
11477
|
if (prependedSelectParenthesis) {
|
|
11213
11478
|
result.text += ")";
|
|
11214
11479
|
}
|
|
11215
|
-
const
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
data
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
}
|
|
11480
|
+
const { tableHooks, ensureCount } = ctx.topCtx.cteHooks;
|
|
11481
|
+
const keyValues = [
|
|
11482
|
+
...tableHooks ? Object.entries(tableHooks).map(
|
|
11483
|
+
([cteName2, data]) => `'${cteName2}', (SELECT json_agg(${makeRowToJson(
|
|
11484
|
+
cteName2,
|
|
11485
|
+
data.shape,
|
|
11486
|
+
false,
|
|
11487
|
+
true
|
|
11488
|
+
)}) FROM "${cteName2}")`
|
|
11489
|
+
) : emptyArray$1,
|
|
11490
|
+
...ensureCount ? Object.entries(ensureCount).map(
|
|
11491
|
+
([cteName2, item]) => `'#${cteName2}', CASE WHEN ${"count" in item ? `(SELECT count(*) FROM "${cteName2}") < ${item.count}` : `(SELECT "${cteName2}"."${item.jsonNotNull}" FROM "${cteName2}") IS NULL`} THEN (SELECT 'not-found')::int END`
|
|
11492
|
+
) : emptyArray$1
|
|
11493
|
+
];
|
|
11230
11494
|
result.text += ` UNION ALL SELECT ${"NULL, ".repeat(
|
|
11231
11495
|
ctx.selectedCount || 0
|
|
11232
11496
|
)}json_build_object(${keyValues.join(", ")})`;
|
|
@@ -11259,13 +11523,13 @@ const ctesToSql = (ctx, ctes) => {
|
|
|
11259
11523
|
for (const item of ctes) {
|
|
11260
11524
|
const place = item.p ? "before" : "after";
|
|
11261
11525
|
if (ctx !== ctx.topCtx && item.q?.q.type) {
|
|
11262
|
-
addTopCte(place, ctx, item.q, item.n);
|
|
11526
|
+
addTopCte(place, ctx, item.q, item.q.q.type, item.n);
|
|
11263
11527
|
} else {
|
|
11264
|
-
addTopCteInternal(place, ctx, item);
|
|
11528
|
+
addTopCteInternal(place, ctx, item, item.q?.q.type);
|
|
11265
11529
|
}
|
|
11266
11530
|
}
|
|
11267
11531
|
};
|
|
11268
|
-
const cteToSqlGiveAs = (ctx, item, type) => {
|
|
11532
|
+
const cteToSqlGiveAs = (ctx, item, type, dontAddTableHook) => {
|
|
11269
11533
|
var _a;
|
|
11270
11534
|
let inner;
|
|
11271
11535
|
let as;
|
|
@@ -11282,13 +11546,7 @@ const cteToSqlGiveAs = (ctx, item, type) => {
|
|
|
11282
11546
|
}
|
|
11283
11547
|
if (item.q) {
|
|
11284
11548
|
inner = getSqlText(
|
|
11285
|
-
toSql(
|
|
11286
|
-
item.q,
|
|
11287
|
-
type === void 0 ? item.q.q.type : type,
|
|
11288
|
-
ctx.topCtx,
|
|
11289
|
-
true,
|
|
11290
|
-
as
|
|
11291
|
-
)
|
|
11549
|
+
toSql(item.q, type, ctx.topCtx, true, as, void 0, dontAddTableHook)
|
|
11292
11550
|
);
|
|
11293
11551
|
} else {
|
|
11294
11552
|
inner = item.s.toSQL(ctx.topCtx, `"${as}"`);
|
|
@@ -11299,7 +11557,7 @@ const cteToSqlGiveAs = (ctx, item, type) => {
|
|
|
11299
11557
|
sql: `${o.recursive ? "RECURSIVE " : ""}"${as}"${o.columns ? `(${o.columns.map((x) => `"${x}"`).join(", ")})` : ""} AS ${o.materialized ? "MATERIALIZED " : o.notMaterialized ? "NOT MATERIALIZED " : ""}(${inner})`
|
|
11300
11558
|
};
|
|
11301
11559
|
};
|
|
11302
|
-
const cteToSql = (ctx, item, type) => cteToSqlGiveAs(ctx, item, type).sql;
|
|
11560
|
+
const cteToSql = (ctx, item, type, dontAddTableHook) => cteToSqlGiveAs(ctx, item, type, dontAddTableHook).sql;
|
|
11303
11561
|
const setFreeTopCteAs = (ctx) => {
|
|
11304
11562
|
var _a;
|
|
11305
11563
|
const topCTE = (_a = ctx.topCtx).topCTE ?? (_a.topCTE = newTopCte(ctx));
|
|
@@ -11316,7 +11574,7 @@ const addTopCteSql = (ctx, as, sql) => {
|
|
|
11316
11574
|
target.push(as + " AS (" + sql + ")");
|
|
11317
11575
|
return as;
|
|
11318
11576
|
};
|
|
11319
|
-
const addTopCte = (place, ctx, q, as,
|
|
11577
|
+
const addTopCte = (place, ctx, q, type, as, dontAddTableHook) => {
|
|
11320
11578
|
var _a;
|
|
11321
11579
|
const topCTE = (_a = ctx.topCtx).topCTE ?? (_a.topCTE = newTopCte(ctx));
|
|
11322
11580
|
if (typeof as !== "string") {
|
|
@@ -11326,16 +11584,16 @@ const addTopCte = (place, ctx, q, as, type) => {
|
|
|
11326
11584
|
}
|
|
11327
11585
|
as = name;
|
|
11328
11586
|
}
|
|
11329
|
-
addTopCteInternal(place, ctx, { n: as, q }, type);
|
|
11587
|
+
addTopCteInternal(place, ctx, { n: as, q }, type, dontAddTableHook);
|
|
11330
11588
|
return as;
|
|
11331
11589
|
};
|
|
11332
|
-
const addTopCteInternal = (place, ctx, item, type) => {
|
|
11590
|
+
const addTopCteInternal = (place, ctx, item, type, dontAddTableHook) => {
|
|
11333
11591
|
var _a;
|
|
11334
11592
|
const topCTE = (_a = ctx.topCtx).topCTE ?? (_a.topCTE = newTopCte(ctx));
|
|
11335
11593
|
const target = place === "before" && topCTE.stack[topCTE.stack.length - 1] || (topCTE.append[topCTE.append.length] = []);
|
|
11336
11594
|
const prepend = [];
|
|
11337
11595
|
topCTE.stack.push(prepend);
|
|
11338
|
-
const sql = cteToSql(ctx, item, type);
|
|
11596
|
+
const sql = cteToSql(ctx, item, type, dontAddTableHook);
|
|
11339
11597
|
target.push(...prepend, sql);
|
|
11340
11598
|
topCTE.stack.pop();
|
|
11341
11599
|
};
|
|
@@ -14194,54 +14452,6 @@ class MergeQueryMethods {
|
|
|
14194
14452
|
}
|
|
14195
14453
|
}
|
|
14196
14454
|
|
|
14197
|
-
class QueryScope {
|
|
14198
|
-
/**
|
|
14199
|
-
* See {@link QueryScope}
|
|
14200
|
-
*
|
|
14201
|
-
* Use the `scope` method to apply a pre-defined scope.
|
|
14202
|
-
*
|
|
14203
|
-
* ```ts
|
|
14204
|
-
* // use the `active` scope that is defined in the table:
|
|
14205
|
-
* await db.some.scope('active');
|
|
14206
|
-
* ```
|
|
14207
|
-
*
|
|
14208
|
-
* @param scope - name of the scope to apply
|
|
14209
|
-
*/
|
|
14210
|
-
scope(scope) {
|
|
14211
|
-
const q = _clone(this);
|
|
14212
|
-
if (!q.q.scopes?.[scope]) {
|
|
14213
|
-
const s = q.internal.scopes[scope];
|
|
14214
|
-
if (!s) throw new Error(`Scope ${scope} is not defined`);
|
|
14215
|
-
setObjectValueImmutable(q.q, "scopes", scope, s);
|
|
14216
|
-
}
|
|
14217
|
-
return q;
|
|
14218
|
-
}
|
|
14219
|
-
/**
|
|
14220
|
-
* See {@link QueryScope}
|
|
14221
|
-
*
|
|
14222
|
-
* Remove conditions that were added by the scope from the query.
|
|
14223
|
-
*
|
|
14224
|
-
* ```ts
|
|
14225
|
-
* // SomeTable has a default scope, ignore it for this query:
|
|
14226
|
-
* await db.some.unscope('default');
|
|
14227
|
-
* ```
|
|
14228
|
-
*
|
|
14229
|
-
* @param scope - name of the scope to remove from the query
|
|
14230
|
-
*/
|
|
14231
|
-
unscope(scope) {
|
|
14232
|
-
const q = _clone(this);
|
|
14233
|
-
if (q.q.scopes) {
|
|
14234
|
-
q.q.scopes = { ...q.q.scopes };
|
|
14235
|
-
delete q.q.scopes[scope];
|
|
14236
|
-
for (const _ in q.q.scopes) {
|
|
14237
|
-
return q;
|
|
14238
|
-
}
|
|
14239
|
-
delete q.q.scopes;
|
|
14240
|
-
}
|
|
14241
|
-
return q;
|
|
14242
|
-
}
|
|
14243
|
-
}
|
|
14244
|
-
|
|
14245
14455
|
const DEFAULT_PRIVILEGE = {
|
|
14246
14456
|
OBJECT_TYPES: [
|
|
14247
14457
|
"TABLES",
|
|
@@ -14792,7 +15002,7 @@ class SoftDeleteMethods {
|
|
|
14792
15002
|
* ```
|
|
14793
15003
|
*/
|
|
14794
15004
|
includeDeleted() {
|
|
14795
|
-
return this
|
|
15005
|
+
return _unscope(_clone(this), "nonDeleted");
|
|
14796
15006
|
}
|
|
14797
15007
|
/**
|
|
14798
15008
|
* `hardDelete` deletes records bypassing the `softDelete` behavior:
|
|
@@ -14802,7 +15012,7 @@ class SoftDeleteMethods {
|
|
|
14802
15012
|
* ```
|
|
14803
15013
|
*/
|
|
14804
15014
|
hardDelete(..._args) {
|
|
14805
|
-
return _queryDelete(_clone(this)
|
|
15015
|
+
return _queryDelete(_unscope(_clone(this), "nonDeleted"));
|
|
14806
15016
|
}
|
|
14807
15017
|
}
|
|
14808
15018
|
|
|
@@ -15560,108 +15770,6 @@ applyMixins(QueryMethods, [
|
|
|
15560
15770
|
QueryWindow
|
|
15561
15771
|
]);
|
|
15562
15772
|
|
|
15563
|
-
const makeIndex = (columns, first, second) => {
|
|
15564
|
-
if (typeof first === "string") {
|
|
15565
|
-
const options = { ...second, name: first };
|
|
15566
|
-
return {
|
|
15567
|
-
index: { columns, options }
|
|
15568
|
-
};
|
|
15569
|
-
} else {
|
|
15570
|
-
const options = first ?? {};
|
|
15571
|
-
return {
|
|
15572
|
-
index: { columns, options }
|
|
15573
|
-
};
|
|
15574
|
-
}
|
|
15575
|
-
};
|
|
15576
|
-
const tableDataMethods = {
|
|
15577
|
-
primaryKey(columns, name) {
|
|
15578
|
-
return { primaryKey: { columns, name } };
|
|
15579
|
-
},
|
|
15580
|
-
unique(columns, ...args) {
|
|
15581
|
-
const [first, second] = args;
|
|
15582
|
-
const input = makeIndex(columns, first, second);
|
|
15583
|
-
input.index.options.unique = true;
|
|
15584
|
-
return input;
|
|
15585
|
-
},
|
|
15586
|
-
index: makeIndex,
|
|
15587
|
-
searchIndex(columns, ...args) {
|
|
15588
|
-
var _a;
|
|
15589
|
-
const [first, second] = args;
|
|
15590
|
-
const input = makeIndex(columns, first, second);
|
|
15591
|
-
(_a = input.index.options).using ?? (_a.using = "gin");
|
|
15592
|
-
input.index.options.tsVector = true;
|
|
15593
|
-
return input;
|
|
15594
|
-
},
|
|
15595
|
-
exclude(columns, ...args) {
|
|
15596
|
-
const [first, second] = args;
|
|
15597
|
-
if (typeof first === "string") {
|
|
15598
|
-
const options = second ?? {};
|
|
15599
|
-
return {
|
|
15600
|
-
exclude: { columns, options: { ...options, name: first } }
|
|
15601
|
-
};
|
|
15602
|
-
} else {
|
|
15603
|
-
const options = first ?? {};
|
|
15604
|
-
return {
|
|
15605
|
-
exclude: { columns, options }
|
|
15606
|
-
};
|
|
15607
|
-
}
|
|
15608
|
-
},
|
|
15609
|
-
foreignKey(columns, fnOrTable, foreignColumns, options) {
|
|
15610
|
-
return {
|
|
15611
|
-
constraint: {
|
|
15612
|
-
name: options?.name,
|
|
15613
|
-
references: { columns, fnOrTable, foreignColumns, options }
|
|
15614
|
-
}
|
|
15615
|
-
};
|
|
15616
|
-
},
|
|
15617
|
-
check(check, name) {
|
|
15618
|
-
return { constraint: { check, name } };
|
|
15619
|
-
},
|
|
15620
|
-
sql: sqlFn
|
|
15621
|
-
};
|
|
15622
|
-
const parseTableData = (dataFn) => {
|
|
15623
|
-
const tableData = {};
|
|
15624
|
-
if (dataFn) {
|
|
15625
|
-
const input = dataFn(tableDataMethods);
|
|
15626
|
-
if (Array.isArray(input)) {
|
|
15627
|
-
for (const item of input) {
|
|
15628
|
-
parseTableDataInput(tableData, item);
|
|
15629
|
-
}
|
|
15630
|
-
} else {
|
|
15631
|
-
parseTableDataInput(tableData, input);
|
|
15632
|
-
}
|
|
15633
|
-
}
|
|
15634
|
-
return tableData;
|
|
15635
|
-
};
|
|
15636
|
-
const parseTableDataInput = (tableData, item) => {
|
|
15637
|
-
if (item.primaryKey) {
|
|
15638
|
-
tableData.primaryKey = item.primaryKey;
|
|
15639
|
-
} else if (item.index) {
|
|
15640
|
-
(tableData.indexes ?? (tableData.indexes = [])).push(
|
|
15641
|
-
parseIndexOrExclude(item.index)
|
|
15642
|
-
);
|
|
15643
|
-
} else if (item.exclude) {
|
|
15644
|
-
(tableData.excludes ?? (tableData.excludes = [])).push(
|
|
15645
|
-
parseIndexOrExclude(item.exclude)
|
|
15646
|
-
);
|
|
15647
|
-
} else if (item.constraint) {
|
|
15648
|
-
(tableData.constraints ?? (tableData.constraints = [])).push(item.constraint);
|
|
15649
|
-
if (item.constraint.references?.options?.dropMode) {
|
|
15650
|
-
item.constraint.dropMode = item.constraint.references.options.dropMode;
|
|
15651
|
-
}
|
|
15652
|
-
}
|
|
15653
|
-
};
|
|
15654
|
-
const parseIndexOrExclude = (item) => {
|
|
15655
|
-
for (let i = item.columns.length - 1; i >= 0; i--) {
|
|
15656
|
-
if (typeof item.columns[i] === "string") {
|
|
15657
|
-
item.columns[i] = {
|
|
15658
|
-
column: item.columns[i]
|
|
15659
|
-
};
|
|
15660
|
-
}
|
|
15661
|
-
}
|
|
15662
|
-
return item;
|
|
15663
|
-
};
|
|
15664
|
-
|
|
15665
15773
|
const performQuery = async (q, args, method) => {
|
|
15666
15774
|
const trx = q.internal.asyncStorage.getStore();
|
|
15667
15775
|
let sql;
|
|
@@ -16083,6 +16191,126 @@ const _initQueryBuilder = (adapter, columnTypes, asyncStorage, commonOptions, op
|
|
|
16083
16191
|
return qb.qb = qb;
|
|
16084
16192
|
};
|
|
16085
16193
|
|
|
16194
|
+
const setConnectRetryConfig = (adapter, config) => {
|
|
16195
|
+
adapter.connectRetryConfig = {
|
|
16196
|
+
attempts: config.attempts ?? 10,
|
|
16197
|
+
strategy: typeof config.strategy === "function" ? config.strategy : defaultConnectRetryStrategy(config.strategy ?? emptyObject)
|
|
16198
|
+
};
|
|
16199
|
+
};
|
|
16200
|
+
const wrapAdapterFnWithConnectRetry = (adapter, fn) => {
|
|
16201
|
+
return async function(...args) {
|
|
16202
|
+
let attempt = 1;
|
|
16203
|
+
for (; ; ) {
|
|
16204
|
+
try {
|
|
16205
|
+
return await fn.call(this, ...args);
|
|
16206
|
+
} catch (err) {
|
|
16207
|
+
const config = adapter.connectRetryConfig;
|
|
16208
|
+
if (!err || typeof err !== "object" || err.code !== "ECONNREFUSED" || !config || attempt >= config.attempts) {
|
|
16209
|
+
throw err;
|
|
16210
|
+
}
|
|
16211
|
+
await config.strategy(attempt, config.attempts);
|
|
16212
|
+
attempt++;
|
|
16213
|
+
}
|
|
16214
|
+
}
|
|
16215
|
+
};
|
|
16216
|
+
};
|
|
16217
|
+
const defaultConnectRetryStrategy = (param) => {
|
|
16218
|
+
return (attempt) => setTimeout((param.factor ?? 1.5) ** (attempt - 1) * (param.delay ?? 50));
|
|
16219
|
+
};
|
|
16220
|
+
|
|
16221
|
+
const makeColumnInfoSql = (query, column) => {
|
|
16222
|
+
const values = [];
|
|
16223
|
+
const schema = getQuerySchema(query);
|
|
16224
|
+
let text = `SELECT * FROM information_schema.columns WHERE table_name = ${addValue(
|
|
16225
|
+
values,
|
|
16226
|
+
query.table
|
|
16227
|
+
)} AND table_catalog = current_database() AND table_schema = ${schema ? addValue(values, schema) : "current_schema()"}`;
|
|
16228
|
+
if (column) {
|
|
16229
|
+
text += ` AND column_name = ${addValue(
|
|
16230
|
+
values,
|
|
16231
|
+
query.q.shape[column]?.data.name || column
|
|
16232
|
+
)}`;
|
|
16233
|
+
}
|
|
16234
|
+
return {
|
|
16235
|
+
text,
|
|
16236
|
+
values
|
|
16237
|
+
};
|
|
16238
|
+
};
|
|
16239
|
+
|
|
16240
|
+
const rowToColumnInfo = (row) => {
|
|
16241
|
+
const typed = row;
|
|
16242
|
+
return {
|
|
16243
|
+
defaultValue: typed.column_default,
|
|
16244
|
+
type: typed.data_type,
|
|
16245
|
+
maxLength: typed.character_maximum_length,
|
|
16246
|
+
nullable: typed.is_nullable === "YES"
|
|
16247
|
+
};
|
|
16248
|
+
};
|
|
16249
|
+
function getColumnInfo(query, column) {
|
|
16250
|
+
const q = Object.create(_clone$1(query));
|
|
16251
|
+
q.toSQL = () => makeColumnInfoSql(query, column);
|
|
16252
|
+
q.q.handleResult = (_, _t, result) => {
|
|
16253
|
+
if (column) {
|
|
16254
|
+
return rowToColumnInfo(result.rows[0]);
|
|
16255
|
+
} else {
|
|
16256
|
+
const info = {};
|
|
16257
|
+
result.rows.forEach((row) => {
|
|
16258
|
+
info[row.column_name] = rowToColumnInfo(row);
|
|
16259
|
+
});
|
|
16260
|
+
return info;
|
|
16261
|
+
}
|
|
16262
|
+
};
|
|
16263
|
+
return q;
|
|
16264
|
+
}
|
|
16265
|
+
|
|
16266
|
+
const makeCopySql = (table, copy) => {
|
|
16267
|
+
const ctx = newToSqlCtx(table);
|
|
16268
|
+
const { q } = table;
|
|
16269
|
+
const quotedAs = `"${q.as || table.table}"`;
|
|
16270
|
+
const columns = copy.columns ? `(${copy.columns.map((item) => `"${q.shape[item]?.data.name || item}"`).join(", ")})` : "";
|
|
16271
|
+
const target = "from" in copy ? copy.from : copy.to;
|
|
16272
|
+
const quotedTable = quoteTableWithSchema(table);
|
|
16273
|
+
ctx.sql.push(
|
|
16274
|
+
`COPY ${quotedTable}${columns} ${"from" in copy ? "FROM" : "TO"} ${typeof target === "string" ? escapeString(target) : `PROGRAM ${escapeString(target.program)}`}`
|
|
16275
|
+
);
|
|
16276
|
+
if (Object.keys(copy).length > (copy.columns ? 2 : 1)) {
|
|
16277
|
+
const options = [];
|
|
16278
|
+
if (copy.format) options.push(`FORMAT ${copy.format}`);
|
|
16279
|
+
if (copy.freeze) options.push(`FREEZE ${copy.freeze}`);
|
|
16280
|
+
if (copy.delimiter)
|
|
16281
|
+
options.push(`DELIMITER ${escapeString(copy.delimiter)}`);
|
|
16282
|
+
if (copy.null) options.push(`NULL ${escapeString(copy.null)}`);
|
|
16283
|
+
if (copy.header) options.push(`HEADER ${copy.header}`);
|
|
16284
|
+
if (copy.quote) options.push(`QUOTE ${escapeString(copy.quote)}`);
|
|
16285
|
+
if (copy.escape) options.push(`ESCAPE ${escapeString(copy.escape)}`);
|
|
16286
|
+
if (copy.forceQuote)
|
|
16287
|
+
options.push(
|
|
16288
|
+
`FORCE_QUOTE ${copy.forceQuote === "*" ? "*" : `(${copy.forceQuote.map((x) => `"${x}"`).join(", ")})`}`
|
|
16289
|
+
);
|
|
16290
|
+
if (copy.forceNotNull)
|
|
16291
|
+
options.push(
|
|
16292
|
+
`FORCE_NOT_NULL (${copy.forceNotNull.map((x) => `"${x}"`).join(", ")})`
|
|
16293
|
+
);
|
|
16294
|
+
if (copy.forceNull)
|
|
16295
|
+
options.push(
|
|
16296
|
+
`FORCE_NULL (${copy.forceNull.map((x) => `"${x}"`).join(", ")})`
|
|
16297
|
+
);
|
|
16298
|
+
if (copy.encoding) options.push(`ENCODING ${escapeString(copy.encoding)}`);
|
|
16299
|
+
ctx.sql.push(`WITH (${options.join(", ")})`);
|
|
16300
|
+
}
|
|
16301
|
+
pushWhereStatementSql(ctx, table, q, quotedAs);
|
|
16302
|
+
return {
|
|
16303
|
+
text: ctx.sql.join(" "),
|
|
16304
|
+
values: ctx.values
|
|
16305
|
+
};
|
|
16306
|
+
};
|
|
16307
|
+
|
|
16308
|
+
function copyTableData(query, arg) {
|
|
16309
|
+
const q = Object.create(query);
|
|
16310
|
+
q.toSQL = () => makeCopySql(q, arg);
|
|
16311
|
+
return q;
|
|
16312
|
+
}
|
|
16313
|
+
|
|
16086
16314
|
class Rollback extends Error {
|
|
16087
16315
|
}
|
|
16088
16316
|
const trxForTest = Symbol("trxForTest");
|
|
@@ -16167,5 +16395,5 @@ const testTransaction = {
|
|
|
16167
16395
|
}
|
|
16168
16396
|
};
|
|
16169
16397
|
|
|
16170
|
-
export {
|
|
16398
|
+
export { ArrayColumn, BigIntColumn, BigSerialColumn, BitColumn, BitVaryingColumn, BooleanColumn, BoxColumn, ByteaColumn, CidrColumn, CircleColumn, CitextColumn, Column, CustomTypeColumn, DateBaseColumn, DateColumn, DateTimeBaseClass, DateTimeTzBaseClass, Db, DecimalColumn, DomainColumn, DoublePrecisionColumn, DynamicRawSQL, EnumColumn, Expression, InetColumn, IntegerBaseColumn, IntegerColumn, IntervalColumn, JSONColumn, JSONTextColumn, LimitedTextBaseColumn, LineColumn, LsegColumn, MacAddr8Column, MacAddrColumn, MoneyColumn, NotFoundError, NumberAsStringBaseColumn, NumberBaseColumn, Operators, OrchidOrmInternalError, PathColumn, PointColumn, PolygonColumn, PostgisGeographyPointColumn, QueryError, QueryHookUtils, QueryHooks, RawSql, RealColumn, SerialColumn, SmallIntColumn, SmallSerialColumn, StringColumn, TextBaseColumn, TextColumn, TimeColumn, TimestampColumn, TimestampTZColumn, TsQueryColumn, TsVectorColumn, UUIDColumn, UnknownColumn, VarCharColumn, VirtualColumn, XMLColumn, _appendQuery, _clone, _createDbSqlMethod, _hookSelectColumns, _initQueryBuilder, _orCreate, _prependWith, _queryCreate, _queryCreateMany, _queryCreateManyFrom, _queryDefaults, _queryDelete, _queryFindBy, _queryFindByOptional, _queryHookAfterCreate, _queryHookAfterUpdate, _queryInsert, _queryInsertMany, _queryJoinOn, _queryRows, _querySelect, _queryTake, _queryTakeOptional, _queryUpdate, _queryUpdateOrThrow, _queryUpsert, _queryWhere, _queryWhereExists, _queryWhereIn, addCode, addTopCte, addTopCteSql, applyMixins, assignDbDataToColumn, backtickQuote, cloneQueryBaseUnscoped, codeToString, colors, columnsShapeToCode, constraintInnerToCode, consumeColumnName, copyTableData, createDbWithAdapter, deepCompare, defaultSchemaConfig, emptyArray, emptyObject, escapeForMigration, escapeString, excludeInnerToCode, exhaustive, getCallerFilePath, getClonedQueryData, getColumnBaseType, getColumnInfo, getColumnTypes, getFreeAlias, getFreeSetAlias, getImportPath, getPrimaryKeys, getQueryAs, getQuerySchema, getShapeFromSelect, getSqlText, getStackTrace, getSupportedDefaultPrivileges, indexInnerToCode, isExpression, isQueryReturnsAll, isRawSQL, logColors, logParamToLogObject, makeColumnNullable, makeColumnTypes, makeColumnsByType, noop, objectHasValues, omit, parseTableData, parseTableDataInput, pathToLog, pick, pluralize, prepareSubQueryForSql, primaryKeyInnerToCode, pushQueryOnForOuter, pushQueryValueImmutable, pushTableDataCode, quoteObjectKey, quoteTableWithSchema, raw$1 as raw, rawSqlToCode, referencesArgsToCode, returnArg, setColumnData, setColumnEncode, setColumnParse, setColumnParseNull, setConnectRetryConfig, setCurrentColumnName, setDataValue, setDefaultLanguage, setFreeAlias, setQueryObjectValueImmutable, singleQuote, snakeCaseKey, tableDataMethods, testTransaction, toArray, toCamelCase, toPascalCase, toSnakeCase, wrapAdapterFnWithConnectRetry };
|
|
16171
16399
|
//# sourceMappingURL=index.mjs.map
|