kern-orm 0.6.0 → 0.8.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.
@@ -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-Cti9QLK7.mjs";
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
- return someKey(table, (key) => key.length === listed.size && isKeyAmong(key, listed));
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 only the occurrence of its table, apart from the values of an insert, which see no source. */
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
- /** Compiles the physical name of the column of the table of `target` under `key`, as `SET` and the column list of an insert name it, without an alias. */
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
- /** Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation. */
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 ? "many" : getRelationRows(relation),
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`, `all`, or conversion. */
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 === "many" && !node.all && node.conversion === void 0;
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
- if (lowered === returning && values === node.values) return node;
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 === "function" ? set(createSelectionRefs(source, context)) : 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 (!node.all && 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`);
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?.kind === "one") return "";
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
- /** Creates the query of `find` and `findBy`: `where` of `values`, an object of column values, returning one row like `take`. */
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
  }
@@ -4690,4 +4920,91 @@ function createDatabase({ driver, test }) {
4690
4920
  function createLockStatement(key) {
4691
4921
  return createStatement("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [key]);
4692
4922
  }
4693
- export { not as A, countType as C, literal as D, toAggregateType as E, combineNodes as M, and as O, countProjection as S, createCountCall as T, createTableFactory as _, defineSchema as a, RequiredRelationError as b, requireDerived as c, desc as d, orderBy as f, createStatement as g, one as h, sql as i, or as j, isColumnValues as k, addTerms as l, many as m, identifier as n, SelectQuery as o, matchColumn as p, param as r, from as s, createDatabase as t, asc as u, defineTable as v, createAggregateCall as w, ResultDecodeError as x, NotFoundError as y };
4923
+ // src/execution/driver-error.ts
4924
+ /** Marks an instance of `QueryError` of any copy of kern-orm, which `instanceof QueryError` of another copy recognizes. */
4925
+ const queryErrorBrand = /* @__PURE__ */ Symbol.for("kern-orm.QueryError");
4926
+ /** Marks an instance of `ConnectionError` of any copy of kern-orm. */
4927
+ const connectionErrorBrand = /* @__PURE__ */ Symbol.for("kern-orm.ConnectionError");
4928
+ /**
4929
+ * Tells whether `value` is an instance of `errorClass`: by the brand of any copy of kern-orm for `errorClass` itself, and by the prototype chain for its subclasses.
4930
+ *
4931
+ * An application can load several copies of kern-orm, such as one of a driver, and an error of one copy is then an instance of the class of another.
4932
+ */
4933
+ function isInstance(errorClass, root, brand, value) {
4934
+ if (errorClass !== root) return Function.prototype[Symbol.hasInstance].call(errorClass, value);
4935
+ return typeof value === "object" && value !== null && value[brand] === true;
4936
+ }
4937
+ /** Sets the brand of `error`, which is not enumerable, so that it does not show among the fields of the error. */
4938
+ function setBrand(error, brand) {
4939
+ Object.defineProperty(error, brand, { value: true });
4940
+ }
4941
+ /**
4942
+ * Error of a statement that PostgreSQL rejected, such as for a violated constraint; the connection of the statement stays usable.
4943
+ *
4944
+ * A driver whose client gives the severity of an error only in the language of the server, such as node-postgres, tells such an error by its SQLSTATE when the server translates its messages.
4945
+ * An error that ends the session with a code that a statement also gets, such as `40001` of a conflict with recovery on a replica, then rejects with `QueryError`, and the next statement on the connection with `ConnectionError`.
4946
+ */
4947
+ var QueryError = class QueryError extends Error {
4948
+ name = "QueryError";
4949
+ /** SQLSTATE of the error, such as `23505` for a violated unique constraint. */
4950
+ code;
4951
+ /** Name of the constraint that the statement violated, if any. */
4952
+ constraint;
4953
+ constructor(message, fields) {
4954
+ super(message, "cause" in fields ? { cause: fields.cause } : void 0);
4955
+ this.code = fields.code;
4956
+ this.constraint = fields.constraint;
4957
+ setBrand(this, queryErrorBrand);
4958
+ }
4959
+ /** Tells whether `value` is an instance of the class, including one of `QueryError` of another copy of kern-orm. */
4960
+ static [Symbol.hasInstance](value) {
4961
+ return isInstance(this, QueryError, queryErrorBrand, value);
4962
+ }
4963
+ };
4964
+ /**
4965
+ * Error of a statement or connection that failed for another reason than PostgreSQL rejecting a statement, such as a lost connection, a timeout of the client, or a closed driver.
4966
+ *
4967
+ * The statement may have run or not, and its connection may be lost or in an unknown state of its transaction.
4968
+ */
4969
+ var ConnectionError = class ConnectionError extends Error {
4970
+ name = "ConnectionError";
4971
+ constructor(message, options) {
4972
+ super(message, options);
4973
+ setBrand(this, connectionErrorBrand);
4974
+ }
4975
+ /** Tells whether `value` is an instance of the class, including one of `ConnectionError` of another copy of kern-orm. */
4976
+ static [Symbol.hasInstance](value) {
4977
+ return isInstance(this, ConnectionError, connectionErrorBrand, value);
4978
+ }
4979
+ };
4980
+ /** SQLSTATE of five digits or uppercase letters; clients also give codes of their own, such as `ECONNRESET` or `CONNECTION_CLOSED`. */
4981
+ const sqlState = /^[0-9A-Z]{5}$/;
4982
+ /** SQLSTATE of an error that ends the session: of the classes of connection exceptions, authorization, operator intervention, and internal errors, and the timeouts of an idle transaction and of a transaction. */
4983
+ const sessionEndingCode = /^(?:08|28|57P|XX|25P03$|25P04$)/;
4984
+ /** Tells whether an error of the server of `severity` and SQLSTATE `code` ends its session. */
4985
+ function isSessionEnding(severity, code) {
4986
+ if (severity === "ERROR") return false;
4987
+ if (severity === "FATAL" || severity === "PANIC") return true;
4988
+ return sessionEndingCode.test(code);
4989
+ }
4990
+ /**
4991
+ * Classifies an error of a client as `QueryError` or `ConnectionError`, by the fields of the PostgreSQL error it carries, for a driver to reject with.
4992
+ *
4993
+ * An error that kern-orm has already classified returns as it is.
4994
+ * An error without a severity or a SQLSTATE, such as of a client that lost its connection, is a `ConnectionError`, and so is one of severity `FATAL` or `PANIC`, which ends the session.
4995
+ * An error of severity `ERROR` is a `QueryError`.
4996
+ * With a translated severity, the SQLSTATE tells an error that ends the session, so a few errors of other codes that end it give `QueryError`.
4997
+ */
4998
+ function toDriverError(error, fields) {
4999
+ if (error instanceof QueryError || error instanceof ConnectionError) return error;
5000
+ const message = error instanceof Error ? error.message : String(error);
5001
+ const { severity, code, constraint } = fields ?? {};
5002
+ if (typeof severity !== "string" || typeof code !== "string" || !sqlState.test(code)) return new ConnectionError(message, { cause: error });
5003
+ if (isSessionEnding(severity, code)) return new ConnectionError(message, { cause: error });
5004
+ return new QueryError(message, {
5005
+ code,
5006
+ constraint: typeof constraint === "string" ? constraint : void 0,
5007
+ cause: error
5008
+ });
5009
+ }
5010
+ export { literal as A, RequiredRelationError as C, createAggregateCall as D, countType as E, combineNodes as F, isColumnValues as M, not as N, createCountCall as O, or as P, NotFoundError as S, countProjection as T, many as _, identifier as a, createTableFactory as b, defineSchema as c, requireDerived as d, addTerms as f, matchColumn as g, orderBy as h, createDatabase as i, and as j, toAggregateType as k, SelectQuery as l, desc as m, QueryError as n, param as o, asc as p, toDriverError as r, sql as s, ConnectionError as t, from as u, one as v, ResultDecodeError as w, defineTable as x, createStatement as y };