kern-orm 0.6.0 → 0.7.0
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/{database-CP4oapsQ.mjs → database-B7Kf5Q9X.mjs} +261 -31
- package/dist/{index-CoKpXcBp.d.mts → index-CLKi9gZL.d.mts} +239 -36
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +21 -3
- package/dist/{text-Cti9QLK7.mjs → text-CIUxPWPl.mjs} +1 -0
- package/dist/{types-DfoL8Oi9.mjs → types-D3d35qR2.mjs} +4 -13
- package/dist/types.d.mts +3 -8
- package/dist/types.mjs +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as noOrder, C as toNode, D as isType, E as defineType, F as createNode, H as oneNode, I as createSelectNode, L as falseNode, M as valuesQueryNode, O as Column, P as createList, R as noFilters, S as toFunctionOperandNode, T as getBooleanType, U as trueNode, V as noProjections, _ as isOperandSource, a as stringifyJson, b as toCastValueNode, d as orderingOperators, g as isExpression, h as getType, j as isEqualityCompatible, m as createIsNullNode, n as text, p as compareNodes, s as encodeBytea, v as operandNode, w as toValueNode, x as toExpression, y as toBooleanExpression, z as noJoins } from "./text-
|
|
1
|
+
import { B as noOrder, C as toNode, D as isType, E as defineType, F as createNode, H as oneNode, I as createSelectNode, L as falseNode, M as valuesQueryNode, O as Column, P as createList, R as noFilters, S as toFunctionOperandNode, T as getBooleanType, U as trueNode, V as noProjections, _ as isOperandSource, a as stringifyJson, b as toCastValueNode, d as orderingOperators, g as isExpression, h as getType, j as isEqualityCompatible, m as createIsNullNode, n as text, p as compareNodes, s as encodeBytea, v as operandNode, w as toValueNode, x as toExpression, y as toBooleanExpression, z as noJoins } from "./text-CIUxPWPl.mjs";
|
|
2
2
|
import { types } from "node:util";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
// src/ast/boolean.ts
|
|
@@ -648,11 +648,14 @@ function createTable(name, definitions, options, deriveColumnName) {
|
|
|
648
648
|
columns[key] = column;
|
|
649
649
|
}
|
|
650
650
|
if (deriveColumnName) nameColumns(name, columns, deriveColumnName);
|
|
651
|
+
const singleton = options?.singleton;
|
|
652
|
+
if (singleton !== void 0 && typeof singleton !== "boolean") throw new TypeError(`The singleton option of table "${name}" must be a boolean`);
|
|
651
653
|
return Object.freeze({
|
|
652
654
|
kind: "table",
|
|
653
655
|
name,
|
|
654
656
|
columns: Object.freeze(columns),
|
|
655
|
-
keys: defineKeys(name, columns, options)
|
|
657
|
+
keys: defineKeys(name, columns, options),
|
|
658
|
+
singleton: singleton === true
|
|
656
659
|
});
|
|
657
660
|
}
|
|
658
661
|
/**
|
|
@@ -731,8 +734,14 @@ function findColumn(table, key) {
|
|
|
731
734
|
}
|
|
732
735
|
/** Tells whether the columns of `table` under `columns` are exactly a key of it, primary or unique, in any order, as `IsKey` does in types. */
|
|
733
736
|
function isKey(table, columns) {
|
|
737
|
+
return findKey(table, columns) !== void 0;
|
|
738
|
+
}
|
|
739
|
+
/** Returns the key of `table`, primary or unique, whose columns are exactly those under `columns` in any order, as `isKey` tells, or `undefined`. */
|
|
740
|
+
function findKey(table, columns) {
|
|
734
741
|
const listed = new Set(columns);
|
|
735
|
-
|
|
742
|
+
const isListed = (key) => key.length === listed.size && isKeyAmong(key, listed);
|
|
743
|
+
const { primaryKey, unique } = table.keys;
|
|
744
|
+
return primaryKey !== void 0 && isListed(primaryKey) ? primaryKey : unique.find(isListed);
|
|
736
745
|
}
|
|
737
746
|
/** Tells whether the columns of `table` under `columns` include a key of it, primary or unique, as `ContainsKey` does in types. */
|
|
738
747
|
function containsKey(table, columns) {
|
|
@@ -967,25 +976,20 @@ function compileStatement(node, options = {}, tables, db) {
|
|
|
967
976
|
}
|
|
968
977
|
}
|
|
969
978
|
}
|
|
970
|
-
/** Compiles a mutation, whose expressions see
|
|
979
|
+
/** Compiles a mutation, whose expressions see the occurrence of its table, apart from the values of an insert, which see no source, and the values of `DO UPDATE SET`, which also see the row proposed for insertion as `excluded`. */
|
|
971
980
|
function compileMutation(context, node) {
|
|
981
|
+
if (node.kind === "insert" && node.conflict?.update !== void 0) context.createAlias(excludedAlias);
|
|
972
982
|
const scope = declareSource(context, void 0, node.target);
|
|
973
983
|
const target = compileSource(context, void 0, scope);
|
|
974
984
|
let text;
|
|
975
985
|
switch (node.kind) {
|
|
976
986
|
case "insert":
|
|
977
|
-
text = `INSERT INTO ${target}${compileValues(context, node)}`;
|
|
987
|
+
text = `INSERT INTO ${target}${compileValues(context, node)}${compileConflict(context, scope, node)}`;
|
|
978
988
|
break;
|
|
979
|
-
case "update":
|
|
980
|
-
text = `UPDATE ${target} SET`;
|
|
981
|
-
const { set } = node;
|
|
982
|
-
for (let index = 0; index < set.length; index++) {
|
|
983
|
-
const { key, value } = set[index];
|
|
984
|
-
text += `${index === 0 ? " " : ", "}${compileColumnName(node.target, key)} = ${compileExpression(context, scope, value)}`;
|
|
985
|
-
}
|
|
989
|
+
case "update":
|
|
990
|
+
text = `UPDATE ${target} SET${compileAssignments(context, scope, node.target, node.set)}`;
|
|
986
991
|
text += compileWhere(context, scope, node.filters);
|
|
987
992
|
break;
|
|
988
|
-
}
|
|
989
993
|
case "delete":
|
|
990
994
|
text = `DELETE FROM ${target}${compileWhere(context, scope, node.filters)}`;
|
|
991
995
|
break;
|
|
@@ -1016,7 +1020,41 @@ function compileValues(context, node) {
|
|
|
1016
1020
|
}
|
|
1017
1021
|
return text;
|
|
1018
1022
|
}
|
|
1019
|
-
/**
|
|
1023
|
+
/** Alias of the row proposed for insertion in `DO UPDATE` of `ON CONFLICT`, which PostgreSQL names `EXCLUDED`. */
|
|
1024
|
+
const excludedAlias = "excluded";
|
|
1025
|
+
/**
|
|
1026
|
+
* Compiles `ON CONFLICT` of an insert, which names the physical columns of its key, or the expression `(true)` of the unique index of a singleton table, or nothing for an insert without it.
|
|
1027
|
+
*
|
|
1028
|
+
* The values of `DO UPDATE SET` see the existing row in `scope`, the occurrence of the table of the insert, and the row proposed for insertion as `excluded`.
|
|
1029
|
+
*/
|
|
1030
|
+
function compileConflict(context, scope, node) {
|
|
1031
|
+
const { conflict } = node;
|
|
1032
|
+
if (conflict === void 0) return "";
|
|
1033
|
+
const { target, update } = conflict;
|
|
1034
|
+
let text = " ON CONFLICT";
|
|
1035
|
+
if (target === "singleton") text += " ((true))";
|
|
1036
|
+
else {
|
|
1037
|
+
for (let index = 0; index < target.length; index++) text += `${index === 0 ? " (" : ", "}${compileColumnName(node.target, target[index])}`;
|
|
1038
|
+
if (target.length > 0) text += ")";
|
|
1039
|
+
}
|
|
1040
|
+
if (update === void 0) return `${text} DO NOTHING`;
|
|
1041
|
+
const excluded = {
|
|
1042
|
+
source: update.excluded,
|
|
1043
|
+
alias: excludedAlias,
|
|
1044
|
+
outer: scope
|
|
1045
|
+
};
|
|
1046
|
+
return `${text} DO UPDATE SET${compileAssignments(context, excluded, node.target, update.set)}`;
|
|
1047
|
+
}
|
|
1048
|
+
/** Compiles the assignments of `SET` of an update or of `DO UPDATE` to the columns of the table of `target`, whose values see the sources of `scope`. */
|
|
1049
|
+
function compileAssignments(context, scope, target, set) {
|
|
1050
|
+
let text = "";
|
|
1051
|
+
for (let index = 0; index < set.length; index++) {
|
|
1052
|
+
const { key, value } = set[index];
|
|
1053
|
+
text += `${index === 0 ? " " : ", "}${compileColumnName(target, key)} = ${compileExpression(context, scope, value)}`;
|
|
1054
|
+
}
|
|
1055
|
+
return text;
|
|
1056
|
+
}
|
|
1057
|
+
/** Compiles the physical name of the column of the table of `target` under `key`, as `SET`, the column list of an insert, and the target of `ON CONFLICT` name it, without an alias. */
|
|
1020
1058
|
function compileColumnName(target, key) {
|
|
1021
1059
|
return quoteIdentifier(requireColumn(target.table, key).attributes.name ?? key);
|
|
1022
1060
|
}
|
|
@@ -1314,6 +1352,10 @@ function isRelationDeclaration(value) {
|
|
|
1314
1352
|
return typeof value === "object" && value !== null && declarationBrand in value;
|
|
1315
1353
|
}
|
|
1316
1354
|
// src/query/rows.ts
|
|
1355
|
+
/** Returns the rows mode of a query that starts from `table`, as `TableQueryState` gives it in types: `optional` of the one row of a singleton table, and `many` of the rows of another table. */
|
|
1356
|
+
function getTableRows(table) {
|
|
1357
|
+
return table.singleton ? "optional" : "many";
|
|
1358
|
+
}
|
|
1317
1359
|
/** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
|
|
1318
1360
|
function getRelationRows(relation) {
|
|
1319
1361
|
if (relation.kind === "many") return "many";
|
|
@@ -1333,8 +1375,15 @@ function toValueRows(rows) {
|
|
|
1333
1375
|
return rows === "nullable" ? "optional" : rows;
|
|
1334
1376
|
}
|
|
1335
1377
|
// src/query/node.ts
|
|
1336
|
-
/**
|
|
1378
|
+
/** Empty values of the key without columns of a query that starts from a singleton table. */
|
|
1379
|
+
const singletonKey = /* @__PURE__ */ Object.freeze({});
|
|
1380
|
+
/**
|
|
1381
|
+
* Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation.
|
|
1382
|
+
*
|
|
1383
|
+
* A query of a singleton table without `relation` is a query of its one row, as of `find` of a key without columns, which `upsert` applies to.
|
|
1384
|
+
*/
|
|
1337
1385
|
function createQueryNode(from, relation, filters = noFilters) {
|
|
1386
|
+
const singleton = relation === void 0 && from.table.singleton;
|
|
1338
1387
|
return Object.freeze({
|
|
1339
1388
|
kind: "query",
|
|
1340
1389
|
from,
|
|
@@ -1350,16 +1399,17 @@ function createQueryNode(from, relation, filters = noFilters) {
|
|
|
1350
1399
|
distinctOn: [],
|
|
1351
1400
|
distinctFields: void 0,
|
|
1352
1401
|
lock: void 0,
|
|
1353
|
-
rows: relation === void 0 ?
|
|
1402
|
+
rows: relation === void 0 ? getTableRows(from.table) : getRelationRows(relation),
|
|
1354
1403
|
value: false,
|
|
1355
1404
|
guaranteed: relation?.required ?? false,
|
|
1356
1405
|
all: false,
|
|
1357
|
-
conversion: void 0
|
|
1406
|
+
conversion: void 0,
|
|
1407
|
+
foundKey: singleton ? singletonKey : void 0
|
|
1358
1408
|
});
|
|
1359
1409
|
}
|
|
1360
|
-
/** Tells whether `node` is a query of a table without clauses, such as a table of a `Db`: no relation, filters, selected fields, keys of groups, order, limit, offset, `distinct`, keys of `distinctOn`, row lock, rows other than `many
|
|
1410
|
+
/** Tells whether `node` is a query of a table without clauses, such as a table of a `Db`: no relation, filters, selected fields, keys of groups, order, limit, offset, `distinct`, keys of `distinctOn`, row lock, rows other than those that the table starts with, `many` or `optional` of a singleton table, `all`, or conversion. */
|
|
1361
1411
|
function isTableQuery(node) {
|
|
1362
|
-
return node.relation === void 0 && node.projections.length === 0 && node.filters.relation === void 0 && node.filters.scope === void 0 && node.filters.user === void 0 && node.groupBy.length === 0 && node.orderBy.length === 0 && node.limit === void 0 && node.offset === void 0 && !node.distinct && node.distinctOn.length === 0 && node.lock === void 0 && node.rows ===
|
|
1412
|
+
return node.relation === void 0 && node.projections.length === 0 && node.filters.relation === void 0 && node.filters.scope === void 0 && node.filters.user === void 0 && node.groupBy.length === 0 && node.orderBy.length === 0 && node.limit === void 0 && node.offset === void 0 && !node.distinct && node.distinctOn.length === 0 && node.lock === void 0 && node.rows === getTableRows(node.from.table) && !node.all && node.conversion === void 0;
|
|
1363
1413
|
}
|
|
1364
1414
|
/** Tells whether a selected field is the rows of a nested query. */
|
|
1365
1415
|
function isNestedSelection(node) {
|
|
@@ -2260,12 +2310,14 @@ function lowerMutation(node, context) {
|
|
|
2260
2310
|
switch (node.kind) {
|
|
2261
2311
|
case "insert": {
|
|
2262
2312
|
const values = lowerEach(node.values, context, lowerRow);
|
|
2263
|
-
|
|
2313
|
+
const conflict = lowerConflict(node.conflict, context);
|
|
2314
|
+
if (lowered === returning && values === node.values && conflict === node.conflict) return node;
|
|
2264
2315
|
return Object.freeze({
|
|
2265
2316
|
kind: "insert",
|
|
2266
2317
|
target,
|
|
2267
2318
|
columns: node.columns,
|
|
2268
2319
|
values,
|
|
2320
|
+
conflict,
|
|
2269
2321
|
returning: lowered,
|
|
2270
2322
|
recordTemplate,
|
|
2271
2323
|
conversion,
|
|
@@ -2310,6 +2362,19 @@ function lowerMutation(node, context) {
|
|
|
2310
2362
|
function lowerRow(row, context) {
|
|
2311
2363
|
return lowerEach(row, context, lowerValue);
|
|
2312
2364
|
}
|
|
2365
|
+
/** Lowers the values of `DO UPDATE` of `ON CONFLICT` of an insert, keeping a conflict without queries in them as it is. */
|
|
2366
|
+
function lowerConflict(conflict, context) {
|
|
2367
|
+
if (conflict?.update === void 0) return conflict;
|
|
2368
|
+
const { target, update } = conflict;
|
|
2369
|
+
const set = lowerEach(update.set, context, lowerAssignment);
|
|
2370
|
+
return set === update.set ? conflict : Object.freeze({
|
|
2371
|
+
target,
|
|
2372
|
+
update: Object.freeze({
|
|
2373
|
+
set,
|
|
2374
|
+
excluded: update.excluded
|
|
2375
|
+
})
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2313
2378
|
function lowerAssignment(assignment, context) {
|
|
2314
2379
|
const value = lowerExpression(assignment.value, context);
|
|
2315
2380
|
return value === assignment.value ? assignment : Object.freeze({
|
|
@@ -2594,7 +2659,8 @@ var TransformedQuery = class {
|
|
|
2594
2659
|
function transformQuery(node, db, callback) {
|
|
2595
2660
|
return new TransformedQuery(Object.freeze({
|
|
2596
2661
|
...node,
|
|
2597
|
-
conversion: addTransform(node.conversion, callback)
|
|
2662
|
+
conversion: addTransform(node.conversion, callback),
|
|
2663
|
+
foundKey: void 0
|
|
2598
2664
|
}), db);
|
|
2599
2665
|
}
|
|
2600
2666
|
/**
|
|
@@ -2739,6 +2805,19 @@ function toKeyValues(table, values) {
|
|
|
2739
2805
|
}
|
|
2740
2806
|
return filter;
|
|
2741
2807
|
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Returns the target of `onConflict` of `columns`: the key of `table` of exactly these columns, primary or unique, in the order of the key, or none for a conflict on any key when there are no columns.
|
|
2810
|
+
*
|
|
2811
|
+
* It rejects columns that are not exactly a key of `table`, as `isKey` tells.
|
|
2812
|
+
*/
|
|
2813
|
+
function toConflictTarget(table, columns) {
|
|
2814
|
+
if (columns.length === 0) return noColumns;
|
|
2815
|
+
const key = findKey(table, columns);
|
|
2816
|
+
if (key === void 0) throw new Error(`onConflict takes the columns of one key of table "${table.name}", primary or unique, or none for any key, not columns ${quoteColumns(columns)}`);
|
|
2817
|
+
return key;
|
|
2818
|
+
}
|
|
2819
|
+
/** Columns of the target of a conflict on any key. */
|
|
2820
|
+
const noColumns = /* @__PURE__ */ Object.freeze([]);
|
|
2742
2821
|
/** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
|
|
2743
2822
|
function requireKeyValue(table, column, value, method) {
|
|
2744
2823
|
if (value === null || value === void 0) throw new TypeError(`${method} got ${value} for column "${column}" of table "${table.name}"; a value of a key is never null or undefined`);
|
|
@@ -3241,14 +3320,14 @@ function toInsertRows(table, records, method) {
|
|
|
3241
3320
|
};
|
|
3242
3321
|
}
|
|
3243
3322
|
/**
|
|
3244
|
-
* Returns the assignments of an update of the rows of `source` that `set` gives: the values of its columns, or a callback that receives the references of `source` with the relations of `context` and returns them, read when it is called.
|
|
3323
|
+
* Returns the assignments of an update of the rows of `source` that `set` gives: the values of its columns, or a callback that receives the references of `source` with the relations of `context`, and those of `excluded` for `DO UPDATE` of a conflict, and returns them, read when it is called.
|
|
3245
3324
|
*
|
|
3246
3325
|
* A value of `undefined` keeps its column, and an update must set at least one column.
|
|
3247
3326
|
* It rejects a key that is not a column of the table of `source`, and a query other than of one value.
|
|
3248
3327
|
*/
|
|
3249
|
-
function toAssignments(source, set, context) {
|
|
3328
|
+
function toAssignments(source, set, context, excluded) {
|
|
3250
3329
|
const { table } = source;
|
|
3251
|
-
const values = typeof set
|
|
3330
|
+
const values = typeof set !== "function" ? set : excluded === void 0 ? set(createSelectionRefs(source, context)) : set(createSelectionRefs(source, context), createSelectionRefs(excluded, noContext));
|
|
3252
3331
|
if (typeof values !== "object" || values === null) throw new TypeError(`update takes the values of the columns of table "${table.name}" or a callback that returns them`);
|
|
3253
3332
|
const assignments = [];
|
|
3254
3333
|
for (const key of Object.keys(values)) {
|
|
@@ -3319,6 +3398,12 @@ var MutationQuery = class MutationQuery {
|
|
|
3319
3398
|
if (this.node.returning === void 0 && result.node.returning?.length === 0) throw new Error("The callback of use must select at least one field, or not call select to return the number of the rows");
|
|
3320
3399
|
return result;
|
|
3321
3400
|
}
|
|
3401
|
+
onConflict(...columns) {
|
|
3402
|
+
const { node } = this;
|
|
3403
|
+
if (node.kind !== "insert") throw new Error("onConflict applies only to insert and insertMany");
|
|
3404
|
+
if (node.returning !== void 0 || node.conversion !== void 0 || node.conflict !== void 0) throw new Error("onConflict applies right after insert or insertMany, once; call select, get, pluck, map, and transform after its action");
|
|
3405
|
+
return new ConflictTarget(this, toConflictTarget(this.table, columns));
|
|
3406
|
+
}
|
|
3322
3407
|
toSQL(options) {
|
|
3323
3408
|
return compileToSQL(this, options);
|
|
3324
3409
|
}
|
|
@@ -3349,13 +3434,149 @@ var MutationQuery = class MutationQuery {
|
|
|
3349
3434
|
}
|
|
3350
3435
|
};
|
|
3351
3436
|
/**
|
|
3437
|
+
* Conflict target of an insert of state `S` on the key of columns `K`, or without columns for `never`, with methods that choose the action and return the mutation: without columns, `doNothing` takes any key, and `merge` and `update` the row of a singleton table.
|
|
3438
|
+
*
|
|
3439
|
+
* It is no query: awaiting it is a type error and rejects, so that an insert without an action does not resolve without running.
|
|
3440
|
+
*/
|
|
3441
|
+
var ConflictTarget = class {
|
|
3442
|
+
insert;
|
|
3443
|
+
target;
|
|
3444
|
+
constructor(insert, target) {
|
|
3445
|
+
this.insert = insert;
|
|
3446
|
+
this.target = target;
|
|
3447
|
+
Object.freeze(this);
|
|
3448
|
+
}
|
|
3449
|
+
doNothing() {
|
|
3450
|
+
const rows = this.insert.node.rows;
|
|
3451
|
+
return this.derive(Object.freeze({ target: this.target }), rows === "one" ? "optional" : rows);
|
|
3452
|
+
}
|
|
3453
|
+
merge(...columns) {
|
|
3454
|
+
const { insert } = this;
|
|
3455
|
+
const target = this.target;
|
|
3456
|
+
const updateTarget = toUpdateTarget(insert.table, target, "merge");
|
|
3457
|
+
const node = this.insertNode();
|
|
3458
|
+
const merged = columns.length === 0 ? node.columns.filter((key) => !target.includes(key)) : columns;
|
|
3459
|
+
if (merged.length === 0) throw new Error("merge has no column to set, since the insert gives no column apart from the key of onConflict; use doNothing");
|
|
3460
|
+
const excluded = createExcluded(insert.table);
|
|
3461
|
+
const set = [];
|
|
3462
|
+
for (const key of merged) {
|
|
3463
|
+
requireColumn(insert.table, key);
|
|
3464
|
+
if (target.includes(key)) throw new Error(`merge cannot set "${key}", a column of the key of onConflict`);
|
|
3465
|
+
if (set.some((assignment) => assignment.key === key)) throw new Error(`merge takes each column once, not "${key}" twice`);
|
|
3466
|
+
const index = node.columns.indexOf(key);
|
|
3467
|
+
if (index === -1 || node.values.some((row) => row[index] === void 0)) throw new Error(`merge cannot set "${key}", which a record does not give, so that its default would replace the value of the record; give it in every record or leave it out of merge`);
|
|
3468
|
+
set.push(Object.freeze({
|
|
3469
|
+
key,
|
|
3470
|
+
value: createNode({
|
|
3471
|
+
kind: "column",
|
|
3472
|
+
source: excluded,
|
|
3473
|
+
key
|
|
3474
|
+
})
|
|
3475
|
+
}));
|
|
3476
|
+
}
|
|
3477
|
+
return this.derive(Object.freeze({
|
|
3478
|
+
target: updateTarget,
|
|
3479
|
+
update: Object.freeze({
|
|
3480
|
+
set,
|
|
3481
|
+
excluded
|
|
3482
|
+
})
|
|
3483
|
+
}), node.rows);
|
|
3484
|
+
}
|
|
3485
|
+
update(set) {
|
|
3486
|
+
const { insert } = this;
|
|
3487
|
+
const target = toUpdateTarget(insert.table, this.target, "update");
|
|
3488
|
+
const node = this.insertNode();
|
|
3489
|
+
const excluded = createExcluded(insert.table);
|
|
3490
|
+
const assignments = toAssignments(node.target, set, insert.context, excluded);
|
|
3491
|
+
return this.derive(Object.freeze({
|
|
3492
|
+
target,
|
|
3493
|
+
update: Object.freeze({
|
|
3494
|
+
set: assignments,
|
|
3495
|
+
excluded
|
|
3496
|
+
})
|
|
3497
|
+
}), node.rows);
|
|
3498
|
+
}
|
|
3499
|
+
/** Rejects awaiting the conflict target, which needs an action, such as `doNothing()`. */
|
|
3500
|
+
then(_action) {
|
|
3501
|
+
throw new Error("onConflict needs an action before the mutation runs, such as doNothing()");
|
|
3502
|
+
}
|
|
3503
|
+
/** Returns the node of the insert, which `onConflict` takes only of an insert. */
|
|
3504
|
+
insertNode() {
|
|
3505
|
+
return this.insert.node;
|
|
3506
|
+
}
|
|
3507
|
+
/** Creates the insert of the same kind and `Db` with `conflict`, changing rows `rows`. */
|
|
3508
|
+
derive(conflict, rows) {
|
|
3509
|
+
const { insert } = this;
|
|
3510
|
+
const node = this.insertNode();
|
|
3511
|
+
return new MutationQuery(insert.table, Object.freeze({
|
|
3512
|
+
kind: "insert",
|
|
3513
|
+
target: node.target,
|
|
3514
|
+
columns: node.columns,
|
|
3515
|
+
values: node.values,
|
|
3516
|
+
conflict,
|
|
3517
|
+
returning: void 0,
|
|
3518
|
+
rows,
|
|
3519
|
+
value: false
|
|
3520
|
+
}), insert.context, insert.db);
|
|
3521
|
+
}
|
|
3522
|
+
};
|
|
3523
|
+
/**
|
|
3524
|
+
* Returns the target of `DO UPDATE` of `method` of a conflict on the columns `target` of a key of `table`: the columns, or without them the unique index of `(true)` of a singleton table.
|
|
3525
|
+
*
|
|
3526
|
+
* It rejects a target without columns of another table, since PostgreSQL requires an explicit conflict target for `DO UPDATE`.
|
|
3527
|
+
*/
|
|
3528
|
+
function toUpdateTarget(table, target, method) {
|
|
3529
|
+
if (target.length > 0) return target;
|
|
3530
|
+
if (table.singleton) return "singleton";
|
|
3531
|
+
throw new Error(`${method} needs the columns of a key in onConflict, since PostgreSQL updates a record only on a conflict of a named key`);
|
|
3532
|
+
}
|
|
3533
|
+
/** Creates the occurrence of `table` that stands for the row proposed for insertion in `DO UPDATE`, `EXCLUDED` of PostgreSQL. */
|
|
3534
|
+
function createExcluded(table) {
|
|
3535
|
+
return Object.freeze({
|
|
3536
|
+
kind: "table",
|
|
3537
|
+
table
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
/**
|
|
3352
3541
|
* Creates the insert of `records` into the table of `query`, which must have no clauses, in rows mode `rows`: `one` for `insert` and `many` for `insertMany`, named by `method`.
|
|
3353
3542
|
*
|
|
3354
3543
|
* It returns the number of the inserted rows, until a selection returns their fields.
|
|
3544
|
+
* It rejects an insert of rows `many` into a singleton table, which holds one row.
|
|
3355
3545
|
*/
|
|
3356
3546
|
function createInsert(query, records, rows, method) {
|
|
3547
|
+
if (!isTableQuery(query.node)) throw new Error(`${method} applies only to a query of a table without clauses, such as filters or selected fields; select the fields that it returns after it`);
|
|
3548
|
+
const { table } = query;
|
|
3549
|
+
if (rows === "many" && table.singleton) throw new Error(`${method} cannot insert into singleton table "${table.name}", which holds one row; use insert or upsert`);
|
|
3550
|
+
return createInsertInto(query, records, rows, method);
|
|
3551
|
+
}
|
|
3552
|
+
/**
|
|
3553
|
+
* Creates the upsert of `values` by `query`, a query of `find` or `findBy` of a table without other clauses, or of a singleton table without clauses.
|
|
3554
|
+
*
|
|
3555
|
+
* It inserts the values of the key of `query` with `values`, and on a conflict on that key, or on the row of a singleton table, sets the columns of `values` of the existing record, as `merge` of `onConflict` does.
|
|
3556
|
+
* It rejects another query, values that are not an object, a value of a column of the key, and values without a value other than `undefined`.
|
|
3557
|
+
*/
|
|
3558
|
+
function createUpsert(query, values) {
|
|
3559
|
+
const { table } = query;
|
|
3560
|
+
const { foundKey } = query.node;
|
|
3561
|
+
if (foundKey === void 0) throw new Error("upsert applies only to a query of a singleton table or right after find or findBy of a query of a table, without clauses such as filters, optional, or selected fields; select the fields that it returns after it");
|
|
3562
|
+
if (typeof values !== "object" || values === null) throw new TypeError(`upsert takes the values of the columns of table "${table.name}"`);
|
|
3563
|
+
const key = Object.keys(foundKey);
|
|
3564
|
+
const record = {};
|
|
3565
|
+
for (const column of key) record[column] = foundKey[column];
|
|
3566
|
+
let hasValues = false;
|
|
3567
|
+
for (const column of Object.keys(values)) {
|
|
3568
|
+
const value = values[column];
|
|
3569
|
+
if (value === void 0) continue;
|
|
3570
|
+
if (Object.hasOwn(foundKey, column)) throw new Error(`upsert takes the values of the columns other than the key of find or findBy, not of "${column}"`);
|
|
3571
|
+
record[column] = value;
|
|
3572
|
+
hasValues = true;
|
|
3573
|
+
}
|
|
3574
|
+
if (!hasValues) throw new Error(key.length === 0 ? "upsert takes the value of at least one column; to insert the row unless it exists, use insert(...).onConflict().doNothing()" : "upsert takes the value of at least one column other than the key; to insert the record of the key unless it exists, use insert(...).onConflict(...).doNothing()");
|
|
3575
|
+
return new ConflictTarget(createInsertInto(query, [record], "one", "upsert"), key.length === 0 ? key : findKey(table, key)).merge();
|
|
3576
|
+
}
|
|
3577
|
+
/** Creates the insert of `records` into the occurrence of the table of `query`, whose clauses it does not read, in rows mode `rows` of the method named by `method`. */
|
|
3578
|
+
function createInsertInto(query, records, rows, method) {
|
|
3357
3579
|
const { node } = query;
|
|
3358
|
-
if (!isTableQuery(node)) throw new Error(`${method} applies only to a query of a table without clauses, such as filters or selected fields; select the fields that it returns after it`);
|
|
3359
3580
|
const { columns, values } = toInsertRows(node.from.table, records, method);
|
|
3360
3581
|
return new MutationQuery(query.table, Object.freeze({
|
|
3361
3582
|
kind: "insert",
|
|
@@ -3395,7 +3616,7 @@ function createDelete(query) {
|
|
|
3395
3616
|
}), query.context, query.db);
|
|
3396
3617
|
}
|
|
3397
3618
|
/**
|
|
3398
|
-
* Rejects a query whose rows `method`, `update` or `delete`, cannot change: one of a relation, with selected fields, which a mutation selects after it, with a conversion of `map`, which a mutation takes after it too, with keys of groups, whose rows SQL does not give, with an order, a limit, an offset, `distinct`, or `distinctOn`, with a row lock, which a mutation takes on its rows itself, or without a filter that restricts its rows unless `all` allows it.
|
|
3619
|
+
* Rejects a query whose rows `method`, `update` or `delete`, cannot change: one of a relation, with selected fields, which a mutation selects after it, with a conversion of `map`, which a mutation takes after it too, with keys of groups, whose rows SQL does not give, with an order, a limit, an offset, `distinct`, or `distinctOn`, with a row lock, which a mutation takes on its rows itself, or without a filter that restricts its rows unless `all` allows it or the table is a singleton table.
|
|
3399
3620
|
*
|
|
3400
3621
|
* A filter simplified to `TRUE`, such as `where({})`, restricts no row.
|
|
3401
3622
|
*/
|
|
@@ -3406,8 +3627,9 @@ function checkChangedRows(node, method) {
|
|
|
3406
3627
|
if (node.groupBy.length > 0) throw new Error(`${method} changes rows rather than groups; choose them with where, find, or findBy instead of groupBy`);
|
|
3407
3628
|
if (node.orderBy.length > 0 || node.limit !== void 0 || node.offset !== void 0 || node.distinct || node.distinctOn.length > 0) throw new Error(`${method} cannot sort or limit the rows it changes; choose them with where, find, or findBy instead of take, limit, offset, orderBy, distinct, or distinctOn`);
|
|
3408
3629
|
if (node.lock !== void 0) throw new Error(`${method} locks the rows it changes itself, so its query takes no row lock`);
|
|
3630
|
+
if (node.all || node.from.table.singleton) return;
|
|
3409
3631
|
const predicate = combineFilters(node.filters);
|
|
3410
|
-
if (
|
|
3632
|
+
if (predicate.kind === "boolean" && predicate.value) throw new Error(`${method} requires a filter that restricts the rows, such as of where or find; call all() to ${method} every row`);
|
|
3411
3633
|
}
|
|
3412
3634
|
// src/query/pagination.ts
|
|
3413
3635
|
/** Creates the parameter of the number of rows of `limit` or `offset`, named by `method`, of query `node`, rejecting a query of one row and a count that is not a non-negative safe integer. */
|
|
@@ -3422,7 +3644,7 @@ function createPageCount(node, method, count) {
|
|
|
3422
3644
|
}
|
|
3423
3645
|
/** Creates the hint of the error of `limit` or `offset` of query `node` of one row: the method to call them before, if any. */
|
|
3424
3646
|
function createPaginationHint(node) {
|
|
3425
|
-
if (node.relation
|
|
3647
|
+
if (node.relation === void 0 ? node.from.table.singleton : node.relation.kind === "one") return "";
|
|
3426
3648
|
if (isAggregateQuery(node)) return "; call it before count or another aggregate to aggregate the rows of a page";
|
|
3427
3649
|
if (isExistsQuery(node)) return "; call it before exists to check the rows of a page";
|
|
3428
3650
|
return "; call it before take or get";
|
|
@@ -3593,6 +3815,9 @@ var SelectQuery = class SelectQuery {
|
|
|
3593
3815
|
if (!Array.isArray(records)) throw new TypeError("insertMany takes an array of records");
|
|
3594
3816
|
return createInsert(this, records, "many", "insertMany");
|
|
3595
3817
|
}
|
|
3818
|
+
upsert(values) {
|
|
3819
|
+
return createUpsert(this, values);
|
|
3820
|
+
}
|
|
3596
3821
|
update(set) {
|
|
3597
3822
|
return createUpdate(this, set);
|
|
3598
3823
|
}
|
|
@@ -3645,10 +3870,11 @@ var SelectQuery = class SelectQuery {
|
|
|
3645
3870
|
db: this.db
|
|
3646
3871
|
});
|
|
3647
3872
|
}
|
|
3648
|
-
/** Creates a query of the same kind and `Db` with the node changes applied. */
|
|
3873
|
+
/** Creates a query of the same kind and `Db` with the node changes applied, dropping the key of `find` and `findBy` unless the changes give it. */
|
|
3649
3874
|
derive(changes) {
|
|
3650
3875
|
return new SelectQuery(this.table, Object.freeze({
|
|
3651
3876
|
...this.node,
|
|
3877
|
+
foundKey: void 0,
|
|
3652
3878
|
...changes
|
|
3653
3879
|
}), this.context, this.db);
|
|
3654
3880
|
}
|
|
@@ -3699,10 +3925,15 @@ var SelectQuery = class SelectQuery {
|
|
|
3699
3925
|
const { node } = this;
|
|
3700
3926
|
return toFilteredRows(node.rows, node.value && node.conversion === void 0, node.guaranteed);
|
|
3701
3927
|
}
|
|
3702
|
-
/**
|
|
3928
|
+
/**
|
|
3929
|
+
* Creates the query of `find` and `findBy`: `where` of `values`, an object of the values of the columns of a key, returning one row like `take`.
|
|
3930
|
+
*
|
|
3931
|
+
* On a query of a table without clauses, it keeps `values` as the key that `upsert` inserts.
|
|
3932
|
+
*/
|
|
3703
3933
|
takeWhere(values) {
|
|
3704
3934
|
const changes = this.addFilter(values);
|
|
3705
3935
|
if (changes.rows === "many") changes.rows = "one";
|
|
3936
|
+
if (isTableQuery(this.node)) changes.foundKey = Object.freeze(values);
|
|
3706
3937
|
return this.derive(changes);
|
|
3707
3938
|
}
|
|
3708
3939
|
/** Creates the query of one row with rows mode `rows` that `method` returns, rejecting a query of many rows. */
|
|
@@ -3750,7 +3981,6 @@ function requireDerived(query, result, subject) {
|
|
|
3750
3981
|
function bindQuery(query, db) {
|
|
3751
3982
|
return new SelectQuery(query.table, query.node, query.context, db);
|
|
3752
3983
|
}
|
|
3753
|
-
/** Starts a query over a new occurrence of the table. */
|
|
3754
3984
|
function from(table) {
|
|
3755
3985
|
return startQuery(table, noContext);
|
|
3756
3986
|
}
|