kern-orm 0.5.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.
@@ -1,4 +1,5 @@
1
- import { B as trueNode, C as toNode, D as isType, E as defineType, F as createNode, I as createSelectNode, L as falseNode, M as valuesQueryNode, O as Column, P as createList, R as noFilters, S as toFunctionOperandNode, T as getBooleanType, _ 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 oneNode } from "./text-BTFErHIi.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
+ import { types } from "node:util";
2
3
  import { AsyncLocalStorage } from "node:async_hooks";
3
4
  // src/ast/boolean.ts
4
5
  /** Combines predicate nodes with `AND` or `OR`, flattening nested groups of the same kind. */
@@ -173,6 +174,12 @@ const aggregateDefinitions = /* @__PURE__ */ Object.freeze({
173
174
  type: toArgumentType
174
175
  }
175
176
  });
177
+ /** Argument `*` of `count(*)`. */
178
+ const allRowsNode = /* @__PURE__ */ createNode({
179
+ kind: "raw",
180
+ fragments: ["*"],
181
+ values: []
182
+ });
176
183
  /** Projection of `count(*)`, which `count` without an argument selects; its value does not depend on the values or the order of the rows. */
177
184
  const countProjection = /* @__PURE__ */ Object.freeze({
178
185
  key: "count",
@@ -180,12 +187,9 @@ const countProjection = /* @__PURE__ */ Object.freeze({
180
187
  kind: "aggregate",
181
188
  name: "count",
182
189
  distinct: false,
183
- arguments: [/* @__PURE__ */ createNode({
184
- kind: "raw",
185
- fragments: ["*"],
186
- values: []
187
- })],
188
- orderBy: []
190
+ arguments: [allRowsNode],
191
+ orderBy: noOrder,
192
+ filter: void 0
189
193
  }),
190
194
  type: countType,
191
195
  ordered: false,
@@ -197,27 +201,51 @@ const countProjection = /* @__PURE__ */ Object.freeze({
197
201
  * `delimiter` is the delimiter of `stringAgg`, sent as a parameter.
198
202
  */
199
203
  function projectAggregate(method, argument, delimiter) {
204
+ return Object.freeze({
205
+ key: method,
206
+ expression: createAggregateCall(method, argument.expression, delimiter),
207
+ type: toAggregateType(method, argument.type),
208
+ ordered: aggregateDefinitions[method].ordered,
209
+ fallbacks: noFallbacks
210
+ });
211
+ }
212
+ /**
213
+ * Creates the call of the aggregate function of `method` over the values of `argument`.
214
+ *
215
+ * `delimiter` is the delimiter of `stringAgg`, sent as a parameter, `orderBy` the order of the rows that the function takes, and `filter` their condition.
216
+ */
217
+ function createAggregateCall(method, argument, delimiter, orderBy = noOrder, filter) {
200
218
  const definition = aggregateDefinitions[method];
201
- const args = [argument.expression];
219
+ const args = [argument];
202
220
  if (delimiter !== void 0) args.push(createNode({
203
221
  kind: "param",
204
222
  value: delimiter,
205
223
  type: void 0
206
224
  }));
207
- return Object.freeze({
208
- key: method,
209
- expression: createNode({
210
- kind: "aggregate",
211
- name: definition.name,
212
- distinct: definition.distinct,
213
- arguments: args,
214
- orderBy: []
215
- }),
216
- type: definition.type(argument.type),
217
- ordered: definition.ordered,
218
- fallbacks: noFallbacks
225
+ return createNode({
226
+ kind: "aggregate",
227
+ name: definition.name,
228
+ distinct: definition.distinct,
229
+ arguments: args,
230
+ orderBy,
231
+ filter
232
+ });
233
+ }
234
+ /** Creates the call of `count(*)` over the rows that `filter` takes. */
235
+ function createCountCall(filter) {
236
+ return createNode({
237
+ kind: "aggregate",
238
+ name: "count",
239
+ distinct: false,
240
+ arguments: [allRowsNode],
241
+ orderBy: noOrder,
242
+ filter
219
243
  });
220
244
  }
245
+ /** Returns the type that decodes the value of the aggregate of `method` over the values of type `argument`, rejecting what `method` cannot decode, such as `avg` of a type of the SQL type `bigint`. */
246
+ function toAggregateType(method, argument) {
247
+ return aggregateDefinitions[method].type(argument);
248
+ }
221
249
  /** Creates the projection of the aggregate of `field` whose `NULL` `fallback` replaces after its earlier fallbacks. */
222
250
  function addFallback(field, fallback) {
223
251
  const fallbacks = field.fallbacks.slice();
@@ -234,9 +262,9 @@ function addFallback(field, fallback) {
234
262
  function isAggregateQuery(node) {
235
263
  return node.value && isAggregateProjection(node.projections[0]);
236
264
  }
237
- /** Tells whether `node`, a nested query, is selected as its value rather than as packed rows: an aggregate query as a scalar subquery of its statement, and a query of `exists` as `EXISTS` of its rows. */
265
+ /** Tells whether `node`, a nested query, is selected as its value rather than as packed rows: an aggregate query as a scalar subquery of its statement, and a query of `exists` as `EXISTS` of its rows, unless `map` or `transform` converts its value. */
238
266
  function isSelectedAsValue(node) {
239
- return isAggregateQuery(node) || isExistsQuery(node);
267
+ return node.conversion === void 0 && (isAggregateQuery(node) || isExistsQuery(node));
240
268
  }
241
269
  /** Tells whether a selected field is the projection of an aggregate, which only the aggregate methods select. */
242
270
  function isAggregateProjection(field) {
@@ -289,6 +317,21 @@ function createSelectionRefs(source, context) {
289
317
  }), columns[key].type);
290
318
  return Object.defineProperties(refs, context.relations);
291
319
  }
320
+ // src/ast/projections.ts
321
+ /**
322
+ * Returns the position of the projection that has the value of `expression`, such as a sort expression or a key of a group: a projection of the same value, as `isSameValue` tells, or `-1`.
323
+ */
324
+ function findValueProjection(projections, expression) {
325
+ return projections.findIndex((projection) => isSameValue(projection.expression, expression));
326
+ }
327
+ /**
328
+ * Tells whether `left` and `right` are sure to have the same value: they are the same column of the same source or the same expression node.
329
+ *
330
+ * Only the same node or column guarantees one value: PostgreSQL may or may not evaluate equal expressions built apart once, and gives each its own parameters.
331
+ */
332
+ function isSameValue(left, right) {
333
+ return left.kind === "column" && right.kind === "column" ? left.source === right.source && left.key === right.key : left === right;
334
+ }
292
335
  // src/result/result.ts
293
336
  /** Error thrown when a query of one row finds no row, executed or nested, unless a required relation guarantees the row. */
294
337
  var NotFoundError = class extends Error {
@@ -315,7 +358,7 @@ var RequiredRelationError = class extends Error {
315
358
  this.path = path;
316
359
  }
317
360
  };
318
- /** Error thrown when a row does not match the result fields or a value fails to decode; the failure of the value is its cause. */
361
+ /** Error thrown when a row does not match the result fields, a value fails to decode, or a callback of `transform` or `map` fails; the failure is its cause. */
319
362
  var ResultDecodeError = class extends Error {
320
363
  path;
321
364
  name = "ResultDecodeError";
@@ -334,9 +377,13 @@ function isRecordKey(key) {
334
377
  * A statement of one row that returns its number or a record without fields rejects as a query of one row does: with `NotFoundError` without a row, unless it is optional or nullable, and with several rows.
335
378
  */
336
379
  function decodeStatement(plan, result) {
337
- const { counted } = plan;
380
+ const { counted, conversion } = plan;
338
381
  if (counted === void 0) return decodeResult(plan, plan.named ? selectColumns(plan, result) : result.rows);
339
- const { count } = result;
382
+ const decoded = decodeCount(plan, counted, result.count);
383
+ return conversion === void 0 ? decoded : convertTopResult(plan, conversion, decoded, counted === "records" && plan.rows !== "many" && result.count === 0);
384
+ }
385
+ /** Returns what a statement resolves to by `count`, the number of the rows of its command tag, as `counted` tells: the number, or as many records without fields. */
386
+ function decodeCount(plan, counted, count) {
340
387
  if (plan.rows !== "many") {
341
388
  if (count > 1) throw new Error(plan.named ? `Query of one row reported ${count} rows` : `Query of one row changed ${count} rows`);
342
389
  if (count === 0) {
@@ -382,11 +429,47 @@ function selectColumns(plan, result) {
382
429
  return selected;
383
430
  }
384
431
  /**
385
- * Decodes driver rows into the result of a query by its result plan.
432
+ * Decodes driver rows into the result of a query by its result plan, and converts the result as its conversion tells.
386
433
  *
387
434
  * It rejects a row whose values do not match the result fields, and names the path of the value that fails to decode from its result field, such as `fields[2].label`, after the index of the row of rows `many`, such as `[2].fields[2].label`.
388
435
  */
389
436
  function decodeResult(plan, rows) {
437
+ const result = decodeRows(plan, rows);
438
+ const { conversion } = plan;
439
+ return conversion === void 0 ? result : convertTopResult(plan, conversion, result, rows.length === 0);
440
+ }
441
+ /** Converts `result`, which `plan` decoded for a statement rather than for nested rows, as `convertResult` does, rejecting with the error of the statement. */
442
+ function convertTopResult(plan, conversion, result, missing) {
443
+ try {
444
+ return convertResult(plan, conversion, result, missing);
445
+ } catch (error) {
446
+ throw error instanceof NestedFailure ? createRowError("", error) : new ResultDecodeError(`Cannot convert the result${describeCause(error)}`, void 0, { cause: error });
447
+ }
448
+ }
449
+ /**
450
+ * Converts `result`, which `plan` decoded, by `conversion`.
451
+ *
452
+ * It tells a missing row by `missing` rather than by the value, since a row may decode to `null` or `undefined`.
453
+ * A failure of `map` of rows `many` is a `NestedFailure` of the index of its row; any other failure is the error of the callback, which the enclosing field adds its path to.
454
+ */
455
+ function convertResult(plan, conversion, result, missing) {
456
+ const { map, transform } = conversion;
457
+ let converted = result;
458
+ if (map !== void 0) {
459
+ if (plan.rows === "many") {
460
+ const rows = result;
461
+ let index = 0;
462
+ try {
463
+ for (; index < rows.length; index++) rows[index] = map(rows[index]);
464
+ } catch (error) {
465
+ throw new NestedFailure(`[${index}]`, error);
466
+ }
467
+ } else if (!missing) converted = map(result);
468
+ }
469
+ return transform === void 0 ? converted : transform(converted);
470
+ }
471
+ /** Decodes driver rows into the result of a query before its conversion: an array of rows `many`, one row, or the result of a missing row. */
472
+ function decodeRows(plan, rows) {
390
473
  if (plan.rows === "many") {
391
474
  const results = [];
392
475
  let index = 0;
@@ -450,7 +533,11 @@ function createRowError(prefix, error) {
450
533
  const path = prefix + error.path;
451
534
  const { cause } = error;
452
535
  if (cause instanceof MissingRow) return cause.guaranteedBy === void 0 ? new NotFoundError(`Nested query of result field "${path}" returned no rows`, path) : new RequiredRelationError(cause.guaranteedBy, path);
453
- return new ResultDecodeError(`Cannot decode result field "${path}"${cause instanceof Error ? `: ${cause.message}` : ""}`, path, { cause });
536
+ return new ResultDecodeError(`Cannot decode result field "${path}"${describeCause(cause)}`, path, { cause });
537
+ }
538
+ /** Describes the failure `cause` at the end of the message of an error. */
539
+ function describeCause(cause) {
540
+ return cause instanceof Error ? `: ${cause.message}` : "";
454
541
  }
455
542
  /**
456
543
  * Decodes a row into a record keyed by the result fields, or into the value of its only field.
@@ -500,8 +587,14 @@ function decodePackedValue(field, value) {
500
587
  if (typeof value !== "string") throw new TypeError(`Nested value must be text, not ${describeJson(value)}`);
501
588
  return field.type.decode(value);
502
589
  }
503
- /** Decodes the rows of a nested query from JSON: an array of packed rows for rows `many`, and a packed row or `null` otherwise. */
590
+ /** Decodes the rows of a nested query from JSON, as `decodeNestedRows` does, and converts them as the conversion of `plan` tells. */
504
591
  function decodeNested(plan, json) {
592
+ const result = decodeNestedRows(plan, json);
593
+ const { conversion } = plan;
594
+ return conversion === void 0 ? result : convertResult(plan, conversion, result, json === null);
595
+ }
596
+ /** Decodes the rows of a nested query from JSON: an array of packed rows for rows `many`, and a packed row or `null` otherwise. */
597
+ function decodeNestedRows(plan, json) {
505
598
  if (plan.rows === "many") {
506
599
  if (!Array.isArray(json)) throw new TypeError(`Nested rows must be an array, not ${describeJson(json)}`);
507
600
  const results = [];
@@ -532,6 +625,20 @@ function describeJson(value) {
532
625
  }
533
626
  // src/table/table.ts
534
627
  function defineTable(name, definitions, options) {
628
+ return createTable(name, definitions, options, void 0);
629
+ }
630
+ /**
631
+ * Creates a table-definition function with the given naming convention, for an application to define its tables with.
632
+ *
633
+ * Its tables have the same types as those of `defineTable`.
634
+ */
635
+ function createTableFactory(factoryOptions) {
636
+ const deriveColumnName = factoryOptions.columnName;
637
+ if (typeof deriveColumnName !== "function") throw new TypeError("The columnName option of createTableFactory must be a function");
638
+ return ((name, definitions, options) => createTable(name, definitions, options, deriveColumnName));
639
+ }
640
+ /** Creates the table that `defineTable` defines, naming its columns by `deriveColumnName` if given. */
641
+ function createTable(name, definitions, options, deriveColumnName) {
535
642
  const columns = {};
536
643
  for (const [key, definition] of Object.entries(definitions)) {
537
644
  if (key.startsWith("$")) throw new Error(`Column key "${key}" of table "${name}" must not start with "$"`);
@@ -540,14 +647,42 @@ function defineTable(name, definitions, options) {
540
647
  if (!column) throw new TypeError(`Column "${key}" of table "${name}" must be a type or a column`);
541
648
  columns[key] = column;
542
649
  }
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`);
543
653
  return Object.freeze({
544
654
  kind: "table",
545
655
  name,
546
656
  columns: Object.freeze(columns),
547
- keys: defineKeys(name, columns, options)
657
+ keys: defineKeys(name, columns, options),
658
+ singleton: singleton === true
548
659
  });
549
660
  }
550
661
  /**
662
+ * Replaces unnamed columns of table `name` with copies whose physical names are derived from their keys, so that a column shared with other tables stays as it is.
663
+ *
664
+ * It calls `deriveColumnName`, which an application gives, once for each such column.
665
+ * It rejects a derived name that is not a non-empty string, which PostgreSQL rejects only when a query runs, and a derived name that another column has, since two keys would silently read one column.
666
+ * Duplicate names set with `columnName` are allowed, as in `defineTable`.
667
+ */
668
+ function nameColumns(name, columns, deriveColumnName) {
669
+ const keys = Object.keys(columns);
670
+ const derivedKeys = [];
671
+ for (const key of keys) {
672
+ const column = columns[key];
673
+ if (column.attributes.name === void 0) {
674
+ const columnName = deriveColumnName(key);
675
+ if (typeof columnName !== "string" || columnName === "") throw new TypeError(`The name derived for column "${key}" of table "${name}" must be a non-empty string`);
676
+ columns[key] = column.columnName(columnName);
677
+ derivedKeys.push(key);
678
+ }
679
+ }
680
+ for (const key of derivedKeys) {
681
+ const columnName = columns[key].attributes.name;
682
+ for (const other of keys) if (other !== key && columns[other].attributes.name === columnName) throw new Error(`Columns "${key}" and "${other}" of table "${name}" have one physical name "${columnName}"; set another name with columnName()`);
683
+ }
684
+ }
685
+ /**
551
686
  * Defines the keys of table `name` of `columns` from the keys marked on the columns and those of `options`.
552
687
  *
553
688
  * The unique keys are those of the columns in their order, followed by those of the options.
@@ -599,8 +734,14 @@ function findColumn(table, key) {
599
734
  }
600
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. */
601
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) {
602
741
  const listed = new Set(columns);
603
- 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);
604
745
  }
605
746
  /** Tells whether the columns of `table` under `columns` include a key of it, primary or unique, as `ContainsKey` does in types. */
606
747
  function containsKey(table, columns) {
@@ -631,108 +772,6 @@ function toColumn(definition) {
631
772
  hasDefault: false
632
773
  }) : void 0;
633
774
  }
634
- // src/query/resolve-values.ts
635
- /**
636
- * Replaces objects of column values in a predicate with conditions on the columns of `source`.
637
- *
638
- * Groups are rebuilt, so constants that appear after resolution, such as an empty object, are simplified.
639
- */
640
- function resolveValues(node, source) {
641
- switch (node.kind) {
642
- case "values": {
643
- const conditions = [];
644
- for (const key of Object.keys(node.values)) conditions.push(matchColumn(source, key, node.values[key]));
645
- return combineNodes("and", conditions);
646
- }
647
- case "and":
648
- case "or": {
649
- const operands = [];
650
- for (const operand of node.operands) operands.push(resolveValues(operand, source));
651
- return combineNodes(node.kind, operands);
652
- }
653
- case "not": return negateNode(resolveValues(node.operand, source));
654
- case "raw": return createNode({
655
- ...node,
656
- values: node.values.map((value) => resolveValues(value, source))
657
- });
658
- default: return node;
659
- }
660
- }
661
- /** Creates the condition of column `key` of `source` in an object of column values: `IS NULL` of `null`, or the `eq` operator of its type for another value. */
662
- function matchColumn(source, key, value) {
663
- const type = findColumn(source.table, key)?.type;
664
- const column = createNode({
665
- kind: "column",
666
- source,
667
- key
668
- });
669
- if (value === null) return createIsNullNode(column);
670
- if (type) return matchValue(source, column, type, value);
671
- return compareNodes("=", column, value, void 0);
672
- }
673
- /** Matches a column with a value of an object condition by calling the `eq` operator of its type, whichever operator it is. */
674
- function matchValue(source, column, type, value) {
675
- const expression = toExpression(column, type);
676
- if (typeof expression.eq !== "function") throw new TypeError(`Column "${column.key}" of table "${source.table.name}" has no eq operator; an object condition can only match it with null`);
677
- const predicate = expression.eq(value);
678
- if (!isExpression(predicate)) throw new TypeError(`The eq operator of column "${column.key}" of table "${source.table.name}" must return an expression`);
679
- return toNode(predicate);
680
- }
681
- // src/query/filters.ts
682
- /** Creates query filters with the filter of `where` over `source` with the relations of `context` combined with `AND` with the user filters of `filters`. */
683
- function addUserFilter(filters, filter, source, context) {
684
- const predicate = resolveValues(toConditionNode(typeof filter === "function" ? filter(createSelectionRefs(source, context)) : filter), source);
685
- const { user } = filters;
686
- return Object.freeze({
687
- ...filters,
688
- user: user ? combineNodes("and", [user, predicate]) : predicate
689
- });
690
- }
691
- // src/query/find.ts
692
- /**
693
- * Returns the object of column values that `find` filters a query of `table` by: `values` of the columns of its primary key, in their order.
694
- *
695
- * It rejects a table without a primary key, a number of values other than the number of its columns, and `null` and `undefined`.
696
- */
697
- function toPrimaryKeyValues(table, values) {
698
- const { primaryKey } = table.keys;
699
- if (primaryKey === void 0) throw new Error(`find takes the values of a primary key, which table "${table.name}" does not have; use findBy with a unique key`);
700
- if (values.length !== primaryKey.length) throw new Error(`find takes one value for each column of the primary key of table "${table.name}", ${quoteColumns(primaryKey)}, not ${values.length}`);
701
- const filter = {};
702
- for (let index = 0; index < primaryKey.length; index++) {
703
- const column = primaryKey[index];
704
- filter[column] = requireKeyValue(table, column, values[index], "find");
705
- }
706
- return filter;
707
- }
708
- /**
709
- * Returns the object of column values that `findBy` filters a query of `table` by: a copy of `values`, read once.
710
- *
711
- * It rejects columns that are not exactly a key of `table`, primary or unique, as `isKey` tells, and `null` and `undefined`.
712
- */
713
- function toKeyValues(table, values) {
714
- if (typeof values !== "object" || values === null) throw new TypeError(`findBy takes an object of the columns of a key of table "${table.name}"`);
715
- const columns = Object.keys(values);
716
- if (!isKey(table, columns)) {
717
- const listed = columns.length === 0 ? "an object without columns" : `columns ${quoteColumns(columns)}`;
718
- throw new Error(`findBy takes the columns of one key of table "${table.name}", primary or unique, not ${listed}`);
719
- }
720
- const filter = {};
721
- for (const column of columns) {
722
- const value = values[column];
723
- filter[column] = requireKeyValue(table, column, value, "findBy");
724
- }
725
- return filter;
726
- }
727
- /** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
728
- function requireKeyValue(table, column, value, method) {
729
- 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`);
730
- return value;
731
- }
732
- /** Lists `columns` in quotes, separated by commas. */
733
- function quoteColumns(columns) {
734
- return columns.map((column) => `"${column}"`).join(", ");
735
- }
736
775
  // src/type/untyped.ts
737
776
  /** Encodes a parameter without a type, such as a value interpolated into `sql`, by its JavaScript type. */
738
777
  function encodeUntyped(value) {
@@ -785,7 +824,12 @@ var CompileContext = class {
785
824
  tables;
786
825
  db;
787
826
  parameters = [];
827
+ /** Whether a select statement of the compilation locks rows. */
828
+ locksRows = false;
829
+ /** Whether the clauses being compiled belong to a query of groups, which binds the parameter of a cast once by `bindOnce`. */
830
+ grouped = false;
788
831
  #aliases = /* @__PURE__ */ new Set();
832
+ #placeholders;
789
833
  constructor(schemaName, tables = void 0, db = void 0) {
790
834
  this.schemaName = schemaName;
791
835
  this.tables = tables;
@@ -809,6 +853,16 @@ var CompileContext = class {
809
853
  this.parameters.push(value);
810
854
  return `$${this.parameters.length}`;
811
855
  }
856
+ /** Binds `value` as the parameter of `cast` unless `bindOnce` has bound the cast before, and returns its placeholder. */
857
+ bindOnce(cast, value) {
858
+ this.#placeholders ??= /* @__PURE__ */ new Map();
859
+ let placeholder = this.#placeholders.get(cast);
860
+ if (placeholder === void 0) {
861
+ placeholder = this.bind(value);
862
+ this.#placeholders.set(cast, placeholder);
863
+ }
864
+ return placeholder;
865
+ }
812
866
  };
813
867
  // src/compiler/postgres/parentheses.ts
814
868
  /** Tells whether an operand of `kind` needs parentheses at `position`, so that the SQL keeps the structure of the tree. */
@@ -885,10 +939,12 @@ function compileStatement(node, options = {}, tables, db) {
885
939
  parameters: context.parameters,
886
940
  fields: node.projections,
887
941
  recordTemplate: node.recordTemplate,
942
+ conversion: node.conversion,
888
943
  rows: node.rows,
889
944
  value: node.value,
890
945
  counted: void 0,
891
- named: false
946
+ named: false,
947
+ locksRows: context.locksRows
892
948
  };
893
949
  case "text": {
894
950
  const { fields } = node;
@@ -899,7 +955,8 @@ function compileStatement(node, options = {}, tables, db) {
899
955
  rows: node.rows,
900
956
  value: node.value,
901
957
  counted: fields === void 0 ? "number" : void 0,
902
- named: true
958
+ named: true,
959
+ locksRows: false
903
960
  };
904
961
  }
905
962
  default: {
@@ -909,33 +966,30 @@ function compileStatement(node, options = {}, tables, db) {
909
966
  parameters: context.parameters,
910
967
  fields: returning ?? noFields,
911
968
  recordTemplate: node.recordTemplate,
969
+ conversion: node.conversion,
912
970
  rows: node.rows,
913
971
  value: node.value,
914
972
  counted: returning === void 0 ? "number" : returning.length === 0 ? "records" : void 0,
915
- named: false
973
+ named: false,
974
+ locksRows: false
916
975
  };
917
976
  }
918
977
  }
919
978
  }
920
- /** 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`. */
921
980
  function compileMutation(context, node) {
981
+ if (node.kind === "insert" && node.conflict?.update !== void 0) context.createAlias(excludedAlias);
922
982
  const scope = declareSource(context, void 0, node.target);
923
983
  const target = compileSource(context, void 0, scope);
924
984
  let text;
925
985
  switch (node.kind) {
926
986
  case "insert":
927
- text = `INSERT INTO ${target}${compileValues(context, node)}`;
987
+ text = `INSERT INTO ${target}${compileValues(context, node)}${compileConflict(context, scope, node)}`;
928
988
  break;
929
- case "update": {
930
- text = `UPDATE ${target} SET`;
931
- const { set } = node;
932
- for (let index = 0; index < set.length; index++) {
933
- const { key, value } = set[index];
934
- text += `${index === 0 ? " " : ", "}${compileColumnName(node.target, key)} = ${compileExpression(context, scope, value)}`;
935
- }
989
+ case "update":
990
+ text = `UPDATE ${target} SET${compileAssignments(context, scope, node.target, node.set)}`;
936
991
  text += compileWhere(context, scope, node.filters);
937
992
  break;
938
- }
939
993
  case "delete":
940
994
  text = `DELETE FROM ${target}${compileWhere(context, scope, node.filters)}`;
941
995
  break;
@@ -966,7 +1020,41 @@ function compileValues(context, node) {
966
1020
  }
967
1021
  return text;
968
1022
  }
969
- /** 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. */
970
1058
  function compileColumnName(target, key) {
971
1059
  return quoteIdentifier(requireColumn(target.table, key).attributes.name ?? key);
972
1060
  }
@@ -981,7 +1069,14 @@ function compileQuery(context, outer, node) {
981
1069
  scope = declareSource(context, scope, join.source);
982
1070
  joinScopes.push(scope);
983
1071
  }
984
- let text = `SELECT${compileProjections(context, scope, node.projections)}`;
1072
+ const { groupBy, having } = node;
1073
+ const { grouped } = context;
1074
+ context.grouped = groupBy !== void 0;
1075
+ let text = "SELECT";
1076
+ if (node.distinct) text += " DISTINCT";
1077
+ const { distinctOn } = node;
1078
+ if (distinctOn !== void 0) text += ` DISTINCT ON (${compileKeys(context, scope, distinctOn, node.projections)})`;
1079
+ text += compileProjections(context, scope, node.projections);
985
1080
  if (fromScope !== void 0) {
986
1081
  text += ` FROM ${compileSource(context, outer, fromScope)}`;
987
1082
  for (let index = 0; index < node.joins.length; index++) {
@@ -990,9 +1085,18 @@ function compileQuery(context, outer, node) {
990
1085
  }
991
1086
  }
992
1087
  text += compileWhere(context, scope, node.filters);
1088
+ if (groupBy !== void 0) text += ` GROUP BY ${compileKeys(context, scope, groupBy, node.projections)}`;
1089
+ if (having !== void 0) text += ` HAVING ${compileExpression(context, scope, having)}`;
993
1090
  if (node.orderBy.length > 0) text += ` ${compileOrderBy(context, scope, node.orderBy)}`;
994
1091
  if (node.limit) text += ` LIMIT ${compileExpression(context, outer, node.limit)}`;
995
1092
  if (node.offset) text += ` OFFSET ${compileExpression(context, outer, node.offset)}`;
1093
+ const { lock } = node;
1094
+ if (lock !== void 0) {
1095
+ context.locksRows = true;
1096
+ text += ` FOR ${lock.strength}`;
1097
+ if (lock.onLocked !== void 0) text += ` ${lock.onLocked}`;
1098
+ }
1099
+ context.grouped = grouped;
996
1100
  return text;
997
1101
  }
998
1102
  /** Compiles the projections of a select list or of `RETURNING`, each after a space or a comma. */
@@ -1009,6 +1113,16 @@ function compileWhere(context, scope, filters) {
1009
1113
  const where = combineFilters(filters);
1010
1114
  return where.kind === "boolean" && where.value ? "" : ` WHERE ${compileExpression(context, scope, where)}`;
1011
1115
  }
1116
+ /** Compiles the keys of `GROUP BY` or `DISTINCT ON` of a query of `projections`, its final select list, also with those that lowering adds, such as of a sort key of packed rows: each key is the position of the projection that has its value, or its expression without one. */
1117
+ function compileKeys(context, scope, keys, projections) {
1118
+ let text = "";
1119
+ for (let index = 0; index < keys.length; index++) {
1120
+ const key = keys[index];
1121
+ const position = findValueProjection(projections, key);
1122
+ text += `${index === 0 ? "" : ", "}${position === -1 ? compileExpression(context, scope, key) : String(position + 1)}`;
1123
+ }
1124
+ return text;
1125
+ }
1012
1126
  /** Compiles the `ORDER BY` clause of a query or an aggregate, which has at least one sort key. */
1013
1127
  function compileOrderBy(context, scope, orderBy) {
1014
1128
  let text = "ORDER BY";
@@ -1055,7 +1169,10 @@ function compileExpression(context, scope, node) {
1055
1169
  switch (node.kind) {
1056
1170
  case "column": return compileColumn(scope, node);
1057
1171
  case "param": return context.bind(encodeParameter(node));
1058
- case "cast": return `${compileOperand(context, scope, node.operand, "value")}::${node.type.castType}`;
1172
+ case "cast": {
1173
+ const { operand } = node;
1174
+ return `${context.grouped && operand.kind === "param" ? context.bindOnce(node, encodeParameter(operand)) : compileOperand(context, scope, operand, "value")}::${node.type.castType}`;
1175
+ }
1059
1176
  case "boolean": return node.value ? "TRUE" : "FALSE";
1060
1177
  case "and":
1061
1178
  case "or": return node.operands.map((operand) => compileOperand(context, scope, operand, "condition")).join(node.kind === "and" ? " AND " : " OR ");
@@ -1087,7 +1204,9 @@ function compileExpression(context, scope, node) {
1087
1204
  let text = `${node.name}(${node.distinct ? "DISTINCT " : ""}`;
1088
1205
  for (let index = 0; index < node.arguments.length; index++) text += `${index === 0 ? "" : ", "}${compileExpression(context, scope, node.arguments[index])}`;
1089
1206
  if (node.orderBy.length > 0) text += ` ${compileOrderBy(context, scope, node.orderBy)}`;
1090
- return `${text})`;
1207
+ text += ")";
1208
+ if (node.filter !== void 0) text += ` FILTER (WHERE ${compileExpression(context, scope, node.filter)})`;
1209
+ return text;
1091
1210
  }
1092
1211
  default: return node;
1093
1212
  }
@@ -1117,173 +1236,40 @@ function createStatement(text, parameters = []) {
1117
1236
  parameters
1118
1237
  };
1119
1238
  }
1120
- // src/ast/references.ts
1121
- /** Adds to `keys` the key of each column reference to `source` in `node` at any depth, including the queries nested in it. */
1122
- function collectColumnKeys(node, source, keys) {
1123
- visitNodes(node, true, (child) => {
1124
- if (child.kind === "column" && child.source === source) keys.add(child.key);
1125
- });
1239
+ // src/schema/relation.ts
1240
+ /** Runtime mark of the declarations of `many` and `one`. */
1241
+ const declarationBrand = Symbol("kern-orm.relation");
1242
+ /** Returns the name of relation `name` of the table under key `key` in messages and errors, such as `field.form`. */
1243
+ function formatRelationName(key, name) {
1244
+ return `${key}.${name}`;
1126
1245
  }
1127
1246
  /**
1128
- * Tells whether `node` holds, at any depth, a SQL fragment that refers to a column of `source` outside the queries nested in the fragment.
1247
+ * Declares a relation of a table to the records of `target` whose columns `to` equal its columns `from`, for `$relations` of a schema.
1129
1248
  *
1130
- * PostgreSQL assigns an aggregate whose arguments refer only to the sources of an enclosing query to that query ([PostgreSQL](https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-AGGREGATES)).
1131
- * Lowering does not parse SQL fragments to identify their aggregates, so such a fragment in a nested query, such as `sum` of a column of the parent row, may aggregate the rows of the query of `source`.
1249
+ * A composite key lists its columns in arrays of the same length.
1132
1250
  */
1133
- function holdsFragmentOfSource(node, source) {
1134
- let found = false;
1135
- visitNodes(node, true, (child) => {
1136
- if (found || child.kind !== "raw") return;
1137
- visitNodes(child, false, (value) => {
1138
- if (value.kind === "column" && value.source === source) found = true;
1139
- });
1140
- });
1141
- return found;
1142
- }
1143
- /** Calls `visit` for `node` and each expression node in it, also in the queries nested in it when `deep` is set. */
1144
- function visitNodes(node, deep, visit) {
1145
- visit(node);
1146
- switch (node.kind) {
1147
- case "column":
1148
- case "param":
1149
- case "boolean":
1150
- case "tableName":
1151
- case "values":
1152
- case "nestedQuery": return;
1153
- case "cast":
1154
- case "not":
1155
- case "isNull":
1156
- visitNodes(node.operand, deep, visit);
1157
- return;
1158
- case "and":
1159
- case "or":
1160
- for (const operand of node.operands) visitNodes(operand, deep, visit);
1161
- return;
1162
- case "comparison":
1163
- visitNodes(node.left, deep, visit);
1164
- visitNodes(node.right, deep, visit);
1165
- return;
1166
- case "in":
1167
- visitNodes(node.operand, deep, visit);
1168
- for (const item of node.list) visitNodes(item, deep, visit);
1169
- return;
1170
- case "inQuery":
1171
- visitNodes(node.operand, deep, visit);
1172
- if (deep && node.query.kind === "select") visitQuery(node.query, visit);
1173
- return;
1174
- case "raw":
1175
- for (const value of node.values) visitNodes(value, deep, visit);
1176
- return;
1177
- case "subquery":
1178
- case "exists":
1179
- case "textSubquery":
1180
- if (deep) visitQuery(node.query, visit);
1181
- return;
1182
- case "aggregate":
1183
- for (const argument of node.arguments) visitNodes(argument, deep, visit);
1184
- for (const order of node.orderBy) visitNodes(order.expression, deep, visit);
1185
- return;
1186
- }
1187
- }
1188
- /** Calls `visit` for each expression node in the clauses and the derived sources of `query` at any depth. */
1189
- function visitQuery(query, visit) {
1190
- const { from, filters } = query;
1191
- if (from?.kind === "derived") visitQuery(from.query, visit);
1192
- for (const join of query.joins) {
1193
- if (join.source.kind === "derived") visitQuery(join.source.query, visit);
1194
- visitNodes(join.on, true, visit);
1195
- }
1196
- for (const projection of query.projections) visitNodes(projection.expression, true, visit);
1197
- if (filters.relation !== void 0) visitNodes(filters.relation, true, visit);
1198
- if (filters.scope !== void 0) visitNodes(filters.scope, true, visit);
1199
- if (filters.user !== void 0) visitNodes(filters.user, true, visit);
1200
- for (const order of query.orderBy) visitNodes(order.expression, true, visit);
1201
- if (query.limit !== void 0) visitNodes(query.limit, true, visit);
1202
- if (query.offset !== void 0) visitNodes(query.offset, true, visit);
1203
- }
1204
- // src/query/rows.ts
1205
- /** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
1206
- function getRelationRows(relation) {
1207
- if (relation.kind === "many") return "many";
1208
- return relation.required ? "one" : "nullable";
1209
- }
1210
- /** Returns the rows mode after a filter of a query of rows mode `rows`, of values when `value`, whose row is `guaranteed`, as `FilteredStateRows` gives it in types. */
1211
- function toFilteredRows(rows, value, guaranteed) {
1212
- if (!guaranteed || rows !== "one") return rows;
1213
- return value ? "optional" : "nullable";
1214
- }
1215
- /** Returns the limit of a query of many rows that becomes a query of one row, given its limit `limit`: one row, or none under a limit of zero. */
1216
- function limitToOne(limit) {
1217
- return limit?.kind === "param" && limit.value === 0 ? limit : oneNode;
1218
- }
1219
- /** Returns the rows mode after `pluck` of a query of rows mode `rows`, as `ValueRows` gives it in types. */
1220
- function toValueRows(rows) {
1221
- return rows === "nullable" ? "optional" : rows;
1222
- }
1223
- // src/query/node.ts
1224
- /** Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation. */
1225
- function createQueryNode(from, relation, filters = noFilters) {
1226
- return Object.freeze({
1227
- kind: "query",
1228
- from,
1229
- relation,
1230
- projections: [],
1231
- filters,
1232
- orderBy: [],
1233
- limit: void 0,
1234
- offset: void 0,
1235
- rows: relation === void 0 ? "many" : getRelationRows(relation),
1236
- value: false,
1237
- guaranteed: relation?.required ?? false,
1238
- all: false
1239
- });
1240
- }
1241
- /** Tells whether `node` is a query of a table without clauses, such as a table of a `Db`: no relation, filters, selected fields, order, limit, offset, rows other than `many`, or `all`. */
1242
- function isTableQuery(node) {
1243
- 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.orderBy.length === 0 && node.limit === void 0 && node.offset === void 0 && node.rows === "many" && !node.all;
1244
- }
1245
- /** Tells whether a selected field is the rows of a nested query. */
1246
- function isNestedSelection(node) {
1247
- return "query" in node;
1248
- }
1249
- /** Tells whether a selected field is a literal. */
1250
- function isLiteralSelection(node) {
1251
- return "literal" in node;
1252
- }
1253
- // src/schema/relation.ts
1254
- /** Runtime mark of the declarations of `many` and `one`. */
1255
- const declarationBrand = Symbol("kern-orm.relation");
1256
- /** Returns the name of relation `name` of the table under key `key` in messages and errors, such as `field.form`. */
1257
- function formatRelationName(key, name) {
1258
- return `${key}.${name}`;
1259
- }
1260
- /**
1261
- * Declares a relation of a table to the records of `target` whose columns `to` equal its columns `from`, for `$relations` of a schema.
1262
- *
1263
- * A composite key lists its columns in arrays of the same length.
1264
- */
1265
- function many(target, keys) {
1266
- return declareRelation("many", target, keys, false);
1267
- }
1268
- /**
1269
- * Declares a relation of a table to the record of `target` whose columns `to` equal its columns `from`, for `$relations` of a schema.
1270
- *
1271
- * A composite key lists its columns in arrays of the same length.
1272
- * The columns `to` must include a key of `target`, primary or unique, so that they match at most one record.
1273
- * Without the record, a query of the relation gives `null`, and a query of a relation declared with `required: true` rejects unless it filters the record.
1274
- * Set `required: true` only where the database guarantees the record, such as by a foreign key on columns `from` that are not nullable.
1275
- */
1276
- function one(target, keys) {
1277
- return declareRelation("one", target, keys, keys.required === true);
1278
- }
1279
- function declareRelation(kind, target, keys, required) {
1280
- return Object.freeze({
1281
- [declarationBrand]: true,
1282
- kind,
1283
- target,
1284
- from: toKeyList(keys.from),
1285
- to: toKeyList(keys.to),
1286
- required
1251
+ function many(target, keys) {
1252
+ return declareRelation("many", target, keys, false);
1253
+ }
1254
+ /**
1255
+ * Declares a relation of a table to the record of `target` whose columns `to` equal its columns `from`, for `$relations` of a schema.
1256
+ *
1257
+ * A composite key lists its columns in arrays of the same length.
1258
+ * The columns `to` must include a key of `target`, primary or unique, so that they match at most one record.
1259
+ * Without the record, a query of the relation gives `null`, and a query of a relation declared with `required: true` rejects unless it filters the record.
1260
+ * Set `required: true` only where the database guarantees the record, such as by a foreign key on columns `from` that are not nullable.
1261
+ */
1262
+ function one(target, keys) {
1263
+ return declareRelation("one", target, keys, keys.required === true);
1264
+ }
1265
+ function declareRelation(kind, target, keys, required) {
1266
+ return Object.freeze({
1267
+ [declarationBrand]: true,
1268
+ kind,
1269
+ target,
1270
+ from: toKeyList(keys.from),
1271
+ to: toKeyList(keys.to),
1272
+ required
1287
1273
  });
1288
1274
  }
1289
1275
  function toKeyList(key) {
@@ -1365,260 +1351,976 @@ function freezeRelations(relations) {
1365
1351
  function isRelationDeclaration(value) {
1366
1352
  return typeof value === "object" && value !== null && declarationBrand in value;
1367
1353
  }
1368
- // src/lowering/order.ts
1369
- /**
1370
- * Returns the position of the projection that has the value of the sort expression `expression`: a projection of the same column or of the same expression node, or `-1`.
1371
- *
1372
- * Only the same node or column guarantees one value: PostgreSQL may or may not evaluate equal expressions built apart once, and gives each its own parameters.
1373
- */
1374
- function findSortProjection(projections, expression) {
1375
- return projections.findIndex((projection) => {
1376
- const selected = projection.expression;
1377
- return expression.kind === "column" && selected.kind === "column" ? selected.source === expression.source && selected.key === expression.key : selected === expression;
1378
- });
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";
1379
1358
  }
1380
- /** Creates the number of the projection at `position`, by which `ORDER BY` sorts by its value instead of evaluating an expression again. */
1381
- function createPositionNode(position) {
1382
- return createNode({
1383
- kind: "raw",
1384
- fragments: [String(position + 1)],
1385
- values: []
1386
- });
1359
+ /** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
1360
+ function getRelationRows(relation) {
1361
+ if (relation.kind === "many") return "many";
1362
+ return relation.required ? "one" : "nullable";
1387
1363
  }
1388
- /** Creates the order of a statement that sorts by the positions of the projections among `projections` that have the values of its sort expressions, and by the other expressions as they are. */
1389
- function sortByPositions(orderBy, projections) {
1390
- const result = [];
1391
- for (const order of orderBy) {
1392
- const position = findSortProjection(projections, order.expression);
1393
- result.push(position === -1 ? order : Object.freeze({
1394
- expression: createPositionNode(position),
1395
- direction: order.direction,
1396
- nulls: order.nulls
1397
- }));
1398
- }
1399
- return result;
1364
+ /** Returns the rows mode after a filter of a query of rows mode `rows`, of values when `value`, whose row is `guaranteed`, as `FilteredStateRows` gives it in types. */
1365
+ function toFilteredRows(rows, value, guaranteed) {
1366
+ if (!guaranteed || rows !== "one") return rows;
1367
+ return value ? "optional" : "nullable";
1400
1368
  }
1369
+ /** Returns the limit of a query of many rows that becomes a query of one row, given its limit `limit`: one row, or none under a limit of zero. */
1370
+ function limitToOne(limit) {
1371
+ return limit?.kind === "param" && limit.value === 0 ? limit : oneNode;
1372
+ }
1373
+ /** Returns the rows mode after `pluck` of a query of rows mode `rows`, as `ValueRows` gives it in types. */
1374
+ function toValueRows(rows) {
1375
+ return rows === "nullable" ? "optional" : rows;
1376
+ }
1377
+ // src/query/node.ts
1378
+ /** Empty values of the key without columns of a query that starts from a singleton table. */
1379
+ const singletonKey = /* @__PURE__ */ Object.freeze({});
1401
1380
  /**
1402
- * Creates the derived source of the rows of `query`, and, when `ordered`, the order of the query over the projections of the source, in which its reader takes the rows, such as an aggregate.
1381
+ * Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation.
1403
1382
  *
1404
- * The order of a derived source does not carry into the query that reads it, so the reader sorts by sort keys: the projections that have the values of the sort expressions, as for an executed query, or projections of the sort expressions added to the source.
1405
- * With a limit or an offset, the source sorts by the positions of the sort keys, so that the rows it chooses, their values, and their order in the reader come from one evaluation; without them, it has no order.
1406
- * `offset` replaces the offset of `query` in the source, such as `OFFSET 0` that keeps PostgreSQL from merging it, and the source replaces the occurrence of a table `replaces` for its reader.
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.
1407
1384
  */
1408
- function deriveOrderedSource(query, ordered, offset = query.offset, replaces) {
1409
- const { orderBy } = query;
1410
- const projections = query.projections.slice();
1411
- const paginated = query.limit !== void 0 || query.offset !== void 0;
1412
- const sortKeys = [];
1413
- const positions = [];
1414
- if (orderBy.length > 0 && (ordered || paginated)) for (let index = 0; index < orderBy.length; index++) {
1415
- const { expression, direction, nulls } = orderBy[index];
1416
- const position = projectSortKey(projections, expression, index);
1417
- if (ordered) sortKeys.push({
1418
- key: projections[position].key,
1419
- direction,
1420
- nulls
1421
- });
1422
- if (paginated) positions.push(Object.freeze({
1423
- expression: createPositionNode(position),
1424
- direction,
1425
- nulls
1426
- }));
1427
- }
1428
- const derivedQuery = Object.freeze({
1429
- ...query,
1430
- projections,
1431
- orderBy: positions,
1432
- offset
1433
- });
1434
- const source = Object.freeze(replaces === void 0 ? {
1435
- kind: "derived",
1436
- query: derivedQuery,
1437
- lateral: false
1438
- } : {
1439
- kind: "derived",
1440
- query: derivedQuery,
1441
- lateral: false,
1442
- replaces
1385
+ function createQueryNode(from, relation, filters = noFilters) {
1386
+ const singleton = relation === void 0 && from.table.singleton;
1387
+ return Object.freeze({
1388
+ kind: "query",
1389
+ from,
1390
+ relation,
1391
+ projections: [],
1392
+ filters,
1393
+ groupBy: [],
1394
+ having: void 0,
1395
+ orderBy: [],
1396
+ limit: void 0,
1397
+ offset: void 0,
1398
+ distinct: false,
1399
+ distinctOn: [],
1400
+ distinctFields: void 0,
1401
+ lock: void 0,
1402
+ rows: relation === void 0 ? getTableRows(from.table) : getRelationRows(relation),
1403
+ value: false,
1404
+ guaranteed: relation?.required ?? false,
1405
+ all: false,
1406
+ conversion: void 0,
1407
+ foundKey: singleton ? singletonKey : void 0
1443
1408
  });
1444
- const order = [];
1445
- for (const { key, direction, nulls } of sortKeys) order.push(Object.freeze({
1446
- expression: createNode({
1447
- kind: "column",
1448
- source,
1449
- key
1450
- }),
1451
- direction,
1452
- nulls
1453
- }));
1454
- return {
1455
- source,
1456
- order
1457
- };
1458
1409
  }
1459
- /**
1460
- * Returns the position of the projection that sorts by `expression`: the projection of `findSortProjection`, or one added to `projections`.
1461
- *
1462
- * `index` is the position of the expression in the order.
1463
- */
1464
- function projectSortKey(projections, expression, index) {
1465
- const position = findSortProjection(projections, expression);
1466
- if (position !== -1) return position;
1467
- projections.push(Object.freeze({
1468
- key: createSortProjectionKey(projections, index),
1469
- expression,
1470
- type: text
1471
- }));
1472
- return projections.length - 1;
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. */
1411
+ function isTableQuery(node) {
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;
1473
1413
  }
1474
- /** Creates the key of the sort key projection at `index` of the sort keys, which no other projection has. */
1475
- function createSortProjectionKey(projections, index) {
1476
- const base = `$order${index + 1}`;
1477
- let key = base;
1478
- for (let number = 2; projections.some((projection) => projection.key === key); number++) key = `${base}_${number}`;
1479
- return key;
1414
+ /** Tells whether a selected field is the rows of a nested query. */
1415
+ function isNestedSelection(node) {
1416
+ return "query" in node;
1480
1417
  }
1481
- // src/lowering/nested.ts
1482
- /** Maximum number of arguments of a PostgreSQL function, `FUNC_MAX_ARGS` of its default build. */
1483
- const maxFunctionArguments = 100;
1484
- /** Number `0`, the offset that keeps PostgreSQL from merging a derived source into its query. */
1485
- const zeroNode = /* @__PURE__ */ createNode({
1486
- kind: "raw",
1487
- fragments: ["0"],
1488
- values: []
1489
- });
1418
+ /** Tells whether a selected field is a literal. */
1419
+ function isLiteralSelection(node) {
1420
+ return "literal" in node;
1421
+ }
1422
+ // src/lowering/context.ts
1423
+ /** Rejects a query of a relation outside the definitions of `context`. */
1424
+ function checkRelation(node, context) {
1425
+ const { relation } = node;
1426
+ const { definitions } = context;
1427
+ if (relation !== void 0 && definitions !== void 0 && !definitions.has(relation)) throw new Error(`Relation "${formatRelationName(relation.key, relation.name)}" is not part of the schema`);
1428
+ }
1429
+ /** Rejects a nested query bound to `db`, which the error names as `subject`, unless `db` is the `Db` of `context` or `undefined`. */
1430
+ function checkDb(db, context, subject) {
1431
+ if (db !== void 0 && db !== context.db) throw new Error(context.db === void 0 ? `${subject} is bound to a Db, so only that Db can compile the query` : `${subject} is bound to another Db`);
1432
+ }
1433
+ // src/lowering/expression.ts
1490
1434
  /**
1491
- * Creates the projection of the rows of `query` packed as JSON under `key`, selected as a scalar subquery and decoded by the result plan of `query`.
1435
+ * Lowers an expression, replacing each query in it and checking the query as a nested query.
1436
+ * A query of `exists`, such as in a condition, becomes `EXISTS` of its rows, another query of one value a scalar subquery of its value, the query of the values of `IN` its statement, and another query in SQL text its statement in parentheses.
1492
1437
  *
1493
- * Rows `many` are one array, and the other rows modes are one packed row or `NULL` without a row; a scalar subquery fails with several rows.
1438
+ * It returns `node` itself when it holds no query, and creates only the nodes that hold one.
1494
1439
  */
1495
- function projectRows(key, query) {
1496
- const packed = packRows(query);
1497
- const { nested } = packed.projections[0];
1498
- return Object.freeze({
1499
- key,
1500
- expression: createNode({
1501
- kind: "subquery",
1440
+ function lowerExpression(node, context) {
1441
+ switch (node.kind) {
1442
+ case "column":
1443
+ case "param":
1444
+ case "boolean":
1445
+ case "values":
1446
+ case "tableName":
1447
+ case "subquery":
1448
+ case "exists":
1449
+ case "textSubquery": return node;
1450
+ case "nestedQuery": return lowerNestedQuery(node, context);
1451
+ case "cast": {
1452
+ const operand = lowerExpression(node.operand, context);
1453
+ return operand === node.operand ? node : createNode({
1454
+ kind: "cast",
1455
+ operand,
1456
+ type: node.type
1457
+ });
1458
+ }
1459
+ case "not": {
1460
+ const operand = lowerExpression(node.operand, context);
1461
+ return operand === node.operand ? node : createNode({
1462
+ kind: "not",
1463
+ operand
1464
+ });
1465
+ }
1466
+ case "isNull": {
1467
+ const operand = lowerExpression(node.operand, context);
1468
+ return operand === node.operand ? node : createNode({
1469
+ kind: "isNull",
1470
+ operand
1471
+ });
1472
+ }
1473
+ case "and":
1474
+ case "or": {
1475
+ const operands = lowerEach(node.operands, context, lowerExpression);
1476
+ return operands === node.operands ? node : createNode({
1477
+ kind: node.kind,
1478
+ operands
1479
+ });
1480
+ }
1481
+ case "comparison": {
1482
+ const left = lowerExpression(node.left, context);
1483
+ const right = lowerExpression(node.right, context);
1484
+ return left === node.left && right === node.right ? node : createNode({
1485
+ kind: "comparison",
1486
+ operator: node.operator,
1487
+ left,
1488
+ right
1489
+ });
1490
+ }
1491
+ case "in": {
1492
+ const operand = lowerExpression(node.operand, context);
1493
+ const list = lowerEach(node.list, context, lowerExpression);
1494
+ return operand === node.operand && list === node.list ? node : createNode({
1495
+ kind: "in",
1496
+ operand,
1497
+ list
1498
+ });
1499
+ }
1500
+ case "inQuery": {
1501
+ const operand = lowerExpression(node.operand, context);
1502
+ const query = node.query.kind === "nestedQuery" ? lowerQueryOfValues(node.query, context) : node.query;
1503
+ return operand === node.operand && query === node.query ? node : createNode({
1504
+ kind: "inQuery",
1505
+ operand,
1506
+ query
1507
+ });
1508
+ }
1509
+ case "raw": {
1510
+ const values = lowerEach(node.values, context, lowerExpression);
1511
+ return values === node.values ? node : createNode({
1512
+ kind: "raw",
1513
+ fragments: node.fragments,
1514
+ values
1515
+ });
1516
+ }
1517
+ case "aggregate": {
1518
+ const args = lowerEach(node.arguments, context, lowerExpression);
1519
+ const orderBy = lowerOrder(node.orderBy, context);
1520
+ const filter = lowerValue(node.filter, context);
1521
+ return args === node.arguments && orderBy === node.orderBy && filter === node.filter ? node : createNode({
1522
+ kind: "aggregate",
1523
+ name: node.name,
1524
+ distinct: node.distinct,
1525
+ arguments: args,
1526
+ orderBy,
1527
+ filter
1528
+ });
1529
+ }
1530
+ default: return node;
1531
+ }
1532
+ }
1533
+ /** Lowers an optional expression, such as `having` of a query or a value of a row of an insert, as `lowerExpression` does. */
1534
+ function lowerValue(node, context) {
1535
+ return node === void 0 ? void 0 : lowerExpression(node, context);
1536
+ }
1537
+ /**
1538
+ * Lowers a query in an expression to `EXISTS` of the rows of a query of `exists`, to a scalar subquery of the value of another query of one value, or to its statement in parentheses for a query of records or of rows `many`, which only SQL text holds.
1539
+ *
1540
+ * In SQL text, a query of `exists` stays `EXISTS` rather than a subquery of its value, so that PostgreSQL can join its rows in a condition, as a semi-join.
1541
+ */
1542
+ function lowerNestedQuery(node, context) {
1543
+ checkDb(node.db, context, "A query in an expression");
1544
+ const { query } = node;
1545
+ if (isExistsQuery(query)) return context.lowerExists(query);
1546
+ return createNode({
1547
+ kind: query.value && query.rows !== "many" ? "subquery" : "textSubquery",
1548
+ query: context.lowerSelect(query, sortsByPositions(query))
1549
+ });
1550
+ }
1551
+ /** Lowers the query of the values of `IN` to its statement, whatever its rows; it tells positions as `sortsByPositions` does. */
1552
+ function lowerQueryOfValues(node, context) {
1553
+ checkDb(node.db, context, "A query in an expression");
1554
+ return context.lowerSelect(node.query, sortsByPositions(node.query));
1555
+ }
1556
+ /** Tells whether the statement of `node` in a subquery sorts by the positions of its projections: PostgreSQL finds a sort expression of `SELECT DISTINCT` or `GROUP BY` only when it is equal to a selected one or a key, and the keys of `DISTINCT ON` with the order only when they are equal, while a repeated expression has aliases of its own and, apart from the parameters of casts in a query of groups, parameters of its own. */
1557
+ function sortsByPositions(node) {
1558
+ return node.distinct || node.distinctOn.length > 0 || node.groupBy.length > 0;
1559
+ }
1560
+ /** Lowers the filters of a query, replacing the queries in their expressions; without queries, it returns `filters` itself. */
1561
+ function lowerFilters(filters, context) {
1562
+ const { relation, scope, user } = filters;
1563
+ const loweredRelation = relation === void 0 ? void 0 : lowerExpression(relation, context);
1564
+ const loweredScope = scope === void 0 ? void 0 : lowerExpression(scope, context);
1565
+ const loweredUser = user === void 0 ? void 0 : lowerExpression(user, context);
1566
+ if (loweredRelation === relation && loweredScope === scope && loweredUser === user) return filters;
1567
+ const lowered = {};
1568
+ if (loweredRelation !== void 0) lowered.relation = loweredRelation;
1569
+ if (loweredScope !== void 0) lowered.scope = loweredScope;
1570
+ if (loweredUser !== void 0) lowered.user = loweredUser;
1571
+ return Object.freeze(lowered);
1572
+ }
1573
+ /**
1574
+ * Lowers the sort keys of a query, replacing the queries in their expressions; without queries, it returns `orderBy` itself.
1575
+ *
1576
+ * Each sort key is lowered by `lowerSelectedValue` over `selected`, the fields of the query, and `projections`, their lowered projections.
1577
+ */
1578
+ function lowerOrder(orderBy, context, selected, projections) {
1579
+ let lowered;
1580
+ for (let index = 0; index < orderBy.length; index++) {
1581
+ const order = orderBy[index];
1582
+ const { expression } = order;
1583
+ const sorted = lowerSelectedValue(expression, context, selected, projections);
1584
+ if (lowered === void 0 && sorted === expression) continue;
1585
+ lowered ??= orderBy.slice(0, index);
1586
+ lowered.push(sorted === expression ? order : Object.freeze({
1587
+ expression: sorted,
1588
+ direction: order.direction,
1589
+ nulls: order.nulls
1590
+ }));
1591
+ }
1592
+ return lowered ?? orderBy;
1593
+ }
1594
+ /**
1595
+ * Lowers the keys of `groupBy` or `distinctOn` of a query, or `undefined` for a query without them.
1596
+ *
1597
+ * Each key is lowered by `lowerSelectedValue` over `selected`, the fields of the query, and `projections`, their lowered projections.
1598
+ */
1599
+ function lowerKeys(keys, context, selected, projections) {
1600
+ return keys.length === 0 ? void 0 : lowerEach(keys, context, (key, keyContext) => lowerSelectedValue(key, keyContext, selected, projections));
1601
+ }
1602
+ /**
1603
+ * Lowers `expression`, a sort key, a key of a group, or the argument of an aggregate: the value of a field of `selected` gets the expression of its lowered projection among `projections`, and another expression is lowered as it is.
1604
+ *
1605
+ * Lowering a field that holds a query gives its projection a new node, so reusing that node keeps the projection identifiable by its position.
1606
+ * A field of an aggregate or of `exists` is identified by the node of its reference, which holds the field.
1607
+ */
1608
+ function lowerSelectedValue(expression, context, selected, projections) {
1609
+ const position = selected === void 0 || projections === void 0 || projections === selected ? -1 : selected.findIndex((field) => isFieldValue(field, expression));
1610
+ return position === -1 ? lowerExpression(expression, context) : projections[position].expression;
1611
+ }
1612
+ /** Tells whether `expression`, a sort key, a key of a group, or the argument of an aggregate, is the value of `field`: its expression, or for a field of an aggregate or of `exists`, the node of its reference in the callback of `orderBy` or `groupBy`. */
1613
+ function isFieldValue(field, expression) {
1614
+ return isNestedSelection(field) ? expression.kind === "nestedQuery" && expression.field === field : field.expression === expression;
1615
+ }
1616
+ /** Lowers each of `items` with `lower`; when none changes, it returns `items` itself. */
1617
+ function lowerEach(items, context, lower) {
1618
+ let lowered;
1619
+ for (let index = 0; index < items.length; index++) {
1620
+ const item = items[index];
1621
+ const result = lower(item, context);
1622
+ if (lowered !== void 0) lowered.push(result);
1623
+ else if (result !== item) {
1624
+ lowered = items.slice(0, index);
1625
+ lowered.push(result);
1626
+ }
1627
+ }
1628
+ return lowered ?? items;
1629
+ }
1630
+ // src/lowering/order.ts
1631
+ /** Creates the number of the projection at `position`, by which `ORDER BY` sorts by its value instead of evaluating an expression again. */
1632
+ function createPositionNode(position) {
1633
+ return createNode({
1634
+ kind: "raw",
1635
+ fragments: [String(position + 1)],
1636
+ values: []
1637
+ });
1638
+ }
1639
+ /** Creates the order of a statement that sorts by the positions of the projections among `projections` that have the values of its sort expressions, and by the other expressions as they are. */
1640
+ function sortByPositions(orderBy, projections) {
1641
+ const result = [];
1642
+ for (const order of orderBy) {
1643
+ const position = findValueProjection(projections, order.expression);
1644
+ result.push(position === -1 ? order : Object.freeze({
1645
+ expression: createPositionNode(position),
1646
+ direction: order.direction,
1647
+ nulls: order.nulls
1648
+ }));
1649
+ }
1650
+ return result;
1651
+ }
1652
+ /**
1653
+ * Creates the derived source of the rows of `query`, and, when `ordered`, the order of the query over the projections of the source, in which its reader takes the rows, such as an aggregate.
1654
+ *
1655
+ * The order of a derived source does not carry into the query that reads it, so the reader sorts by sort keys: the projections that have the values of the sort expressions, as for an executed query, or projections of the sort expressions added to the source.
1656
+ * With a limit or an offset, the source sorts by the positions of the sort keys, so that the rows it chooses, their values, and their order in the reader come from one evaluation; so it does with `DISTINCT ON`, whose order chooses the first row of each group, and with a row lock, which takes the rows in the order of its query.
1657
+ * Without them, it has no order.
1658
+ * `offset` replaces the offset of `query` in the source, such as `OFFSET 0` that keeps PostgreSQL from merging it, and the source replaces the occurrence of a table `replaces` for its reader.
1659
+ */
1660
+ function deriveOrderedSource(query, ordered, offset = query.offset, replaces) {
1661
+ const { orderBy } = query;
1662
+ const projections = query.projections.slice();
1663
+ const sorted = query.limit !== void 0 || query.offset !== void 0 || query.distinctOn !== void 0 || query.lock !== void 0;
1664
+ const sortKeys = [];
1665
+ const positions = [];
1666
+ if (orderBy.length > 0 && (ordered || sorted)) for (let index = 0; index < orderBy.length; index++) {
1667
+ const { expression, direction, nulls } = orderBy[index];
1668
+ const position = projectSortKey(projections, expression, index);
1669
+ if (ordered) sortKeys.push({
1670
+ key: projections[position].key,
1671
+ direction,
1672
+ nulls
1673
+ });
1674
+ if (sorted) positions.push(Object.freeze({
1675
+ expression: createPositionNode(position),
1676
+ direction,
1677
+ nulls
1678
+ }));
1679
+ }
1680
+ const derivedQuery = Object.freeze({
1681
+ ...query,
1682
+ projections,
1683
+ orderBy: positions,
1684
+ offset
1685
+ });
1686
+ const source = Object.freeze(replaces === void 0 ? {
1687
+ kind: "derived",
1688
+ query: derivedQuery,
1689
+ lateral: false
1690
+ } : {
1691
+ kind: "derived",
1692
+ query: derivedQuery,
1693
+ lateral: false,
1694
+ replaces
1695
+ });
1696
+ const order = [];
1697
+ for (const { key, direction, nulls } of sortKeys) order.push(Object.freeze({
1698
+ expression: createNode({
1699
+ kind: "column",
1700
+ source,
1701
+ key
1702
+ }),
1703
+ direction,
1704
+ nulls
1705
+ }));
1706
+ return {
1707
+ source,
1708
+ order
1709
+ };
1710
+ }
1711
+ /**
1712
+ * Returns the position of the projection that sorts by `expression`: the projection of `findValueProjection`, or one added to `projections`.
1713
+ *
1714
+ * `index` is the position of the expression in the order.
1715
+ */
1716
+ function projectSortKey(projections, expression, index) {
1717
+ const position = findValueProjection(projections, expression);
1718
+ if (position !== -1) return position;
1719
+ projections.push(Object.freeze({
1720
+ key: createSortProjectionKey(projections, index),
1721
+ expression,
1722
+ type: text
1723
+ }));
1724
+ return projections.length - 1;
1725
+ }
1726
+ /** Creates the key of the sort key projection at `index` of the sort keys, which no other projection has. */
1727
+ function createSortProjectionKey(projections, index) {
1728
+ const base = `$order${index + 1}`;
1729
+ let key = base;
1730
+ for (let number = 2; projections.some((projection) => projection.key === key); number++) key = `${base}_${number}`;
1731
+ return key;
1732
+ }
1733
+ // src/lowering/nested.ts
1734
+ /** Maximum number of arguments of a PostgreSQL function, `FUNC_MAX_ARGS` of its default build. */
1735
+ const maxFunctionArguments = 100;
1736
+ /** Number `0`, the offset that keeps PostgreSQL from merging a derived source into its query. */
1737
+ const zeroNode = /* @__PURE__ */ createNode({
1738
+ kind: "raw",
1739
+ fragments: ["0"],
1740
+ values: []
1741
+ });
1742
+ /**
1743
+ * Creates the projection of the rows of `query` packed as JSON under `key`, selected as a scalar subquery and decoded by the result plan of `query`.
1744
+ *
1745
+ * Rows `many` are one array, and the other rows modes are one packed row or `NULL` without a row; a scalar subquery fails with several rows.
1746
+ */
1747
+ function projectRows(key, query) {
1748
+ const packed = packRows(query);
1749
+ const { nested } = packed.projections[0];
1750
+ return Object.freeze({
1751
+ key,
1752
+ expression: createNode({
1753
+ kind: "subquery",
1502
1754
  query: packed
1503
1755
  }),
1504
- nested
1756
+ nested
1757
+ });
1758
+ }
1759
+ /**
1760
+ * Creates a query whose only projection is the rows of `query` packed as JSON, a field decoded by the result plan of `query`.
1761
+ *
1762
+ * Rows `many` are packed as an array in the order of `query`, and the created query returns one row.
1763
+ * Otherwise the created query returns a packed row for each row of `query`, so a scalar subquery of it gives the packed row, `NULL` without a row, and fails with several rows.
1764
+ */
1765
+ function packRows(query) {
1766
+ const { projections } = query;
1767
+ const many = query.rows === "many";
1768
+ const { source, order } = deriveOrderedSource(query, many, !query.distinct && query.distinctOn === void 0 && query.groupBy === void 0 && query.lock === void 0 && query.limit === void 0 && query.offset === void 0 && projections.some((projection) => projection.nested === void 0 && !isTableColumn(projection.expression)) ? zeroNode : query.offset);
1769
+ const wide = projections.length > maxFunctionArguments;
1770
+ const values = [];
1771
+ for (const projection of projections) {
1772
+ const value = createRef(source, projection.key);
1773
+ if (projection.nested !== void 0) values.push(value);
1774
+ else values.push(wide ? createList("to_json(", [packScalar(value)], ")") : packScalar(value));
1775
+ }
1776
+ const row = wide ? createList("array_to_json(ARRAY[", values, "])") : createList("json_build_array(", values, ")");
1777
+ let packed = row;
1778
+ if (many) packed = createNode({
1779
+ kind: "raw",
1780
+ fragments: ["coalesce(", ", '[]'::json)"],
1781
+ values: [createNode({
1782
+ kind: "aggregate",
1783
+ name: "json_agg",
1784
+ distinct: false,
1785
+ arguments: [row],
1786
+ orderBy: order,
1787
+ filter: void 0
1788
+ })]
1789
+ });
1790
+ const plan = Object.freeze({
1791
+ fields: projections,
1792
+ rows: query.rows,
1793
+ value: query.value,
1794
+ guaranteedBy: query.guaranteedBy,
1795
+ recordTemplate: query.recordTemplate,
1796
+ conversion: query.conversion
1797
+ });
1798
+ const projection = Object.freeze({
1799
+ key: "rows",
1800
+ expression: packed,
1801
+ nested: plan
1802
+ });
1803
+ return createSelectNode(source, [projection]);
1804
+ }
1805
+ /** Tells whether `node` is a column of a table, which PostgreSQL reads rather than evaluates, so it can be repeated. */
1806
+ function isTableColumn(node) {
1807
+ return node.kind === "column" && node.source.kind === "table";
1808
+ }
1809
+ /**
1810
+ * Packs a scalar as its PostgreSQL text representation, or `NULL`.
1811
+ *
1812
+ * `format` uses the output function of the type, as the driver receives the values of a row; a cast to `text` differs from it for types such as `boolean`, `inet`, and `char`.
1813
+ */
1814
+ function packScalar(value) {
1815
+ return createNode({
1816
+ kind: "raw",
1817
+ fragments: [
1818
+ "CASE WHEN ",
1819
+ " IS NOT DISTINCT FROM NULL THEN NULL ELSE format('%s', ",
1820
+ ") END"
1821
+ ],
1822
+ values: [value, value]
1823
+ });
1824
+ }
1825
+ function createRef(source, key) {
1826
+ return createNode({
1827
+ kind: "column",
1828
+ source,
1829
+ key
1830
+ });
1831
+ }
1832
+ // src/lowering/projection.ts
1833
+ /**
1834
+ * Lowers the fields of a query to projections, packing the rows of each nested query into the value of its field, or selecting the value of an aggregate query or `EXISTS` of a query of `exists`, and replacing the queries in the expressions of the other fields; checks them as `lowerQuery` does.
1835
+ *
1836
+ * Without queries, it returns `selected` itself.
1837
+ */
1838
+ function lowerProjections(selected, context) {
1839
+ let lowered;
1840
+ for (let index = 0; index < selected.length; index++) {
1841
+ const field = selected[index];
1842
+ let projection;
1843
+ if (isNestedSelection(field)) {
1844
+ checkDb(field.db, context, `Selected field "${field.key}"`);
1845
+ projection = projectQuery(field.key, field.query, context);
1846
+ } else {
1847
+ const expression = lowerExpression(field.expression, context);
1848
+ projection = expression === field.expression ? field : Object.freeze({
1849
+ key: field.key,
1850
+ expression,
1851
+ type: field.type
1852
+ });
1853
+ }
1854
+ if (lowered !== void 0) lowered.push(projection);
1855
+ else if (projection !== field) {
1856
+ lowered = selected.slice(0, index);
1857
+ lowered.push(projection);
1858
+ }
1859
+ }
1860
+ return lowered ?? selected;
1861
+ }
1862
+ /** Creates the projection of the value of `node`, a nested query, under `key`: `EXISTS` of a query of `exists`, the value of an aggregate query, or the packed rows of another query or of one whose result code converts. */
1863
+ function projectQuery(key, node, context) {
1864
+ if (!isSelectedAsValue(node)) return projectRows(key, context.lowerSelect(node, false));
1865
+ return isExistsQuery(node) ? projectExists(key, node, context) : projectAggregateValue(key, context.lowerSelect(node, false));
1866
+ }
1867
+ /** Creates the projection of `EXISTS` of the rows of `node`, a query of `exists`, under `key`, decoded by the type of the projection of `exists`. */
1868
+ function projectExists(key, node, context) {
1869
+ return Object.freeze({
1870
+ key,
1871
+ expression: context.lowerExists(node),
1872
+ type: node.projections[0].type
1873
+ });
1874
+ }
1875
+ /** Creates the projection of the value of `query`, the statement of an aggregate query, under `key`, selected as a scalar subquery and decoded by the type of the aggregate. */
1876
+ function projectAggregateValue(key, query) {
1877
+ return Object.freeze({
1878
+ key,
1879
+ expression: createNode({
1880
+ kind: "subquery",
1881
+ query
1882
+ }),
1883
+ type: query.projections[0].type
1884
+ });
1885
+ }
1886
+ /** Returns the fields of `selected` that a statement selects, or `selected` itself without literals. */
1887
+ function dropLiterals(selected) {
1888
+ if (!selected.some(isLiteralSelection)) return selected;
1889
+ const fields = [];
1890
+ for (const field of selected) if (!isLiteralSelection(field)) fields.push(field);
1891
+ return fields;
1892
+ }
1893
+ /** Creates the template of the records of `selected`: every field in its order with the value of a literal, or `undefined` without literals. */
1894
+ function createRecordTemplate(selected) {
1895
+ if (!selected.some(isLiteralSelection)) return;
1896
+ const template = [];
1897
+ for (const field of selected) template.push(Object.freeze({
1898
+ key: field.key,
1899
+ value: isLiteralSelection(field) ? field.literal : void 0
1900
+ }));
1901
+ return template;
1902
+ }
1903
+ // src/lowering/distinct.ts
1904
+ /**
1905
+ * Rejects a query of `distinct` of the lowered `projections` and `orderBy` that `SELECT DISTINCT` cannot give the rows of, with an error that does not depend on where the query is:
1906
+ *
1907
+ * - A query without projections, such as of only literals, has nothing to compare, and PostgreSQL has no syntax for it.
1908
+ * - Nested rows are JSON, which PostgreSQL cannot compare.
1909
+ * - PostgreSQL rejects a sort key that is not a projection, since duplicates can differ in its value, whereas a packed query would add it to the projections of its derived source and compare it too.
1910
+ * Only a projection of the same column or of the same expression node is sure to have its value, as in `findValueProjection`.
1911
+ */
1912
+ function checkDistinct(projections, orderBy) {
1913
+ if (projections.length === 0) throw new Error("distinct requires at least one selected column or SQL expression; literal fields are not compared");
1914
+ for (const { key, nested } of projections) if (nested !== void 0) throw new Error(`distinct cannot compare the nested rows of the selected field "${key}"`);
1915
+ for (const { expression } of orderBy) if (findValueProjection(projections, expression) === -1) throw new Error("A query of distinct sorts only by its selected fields, since duplicates can differ in any other value; select the value to sort by it");
1916
+ }
1917
+ /**
1918
+ * Rejects a query of `distinctOn` of the lowered `projections`, `keys`, and `orderBy` that `SELECT DISTINCT ON` cannot give the rows of, with an error that does not depend on where the query is: a query without projections, such as of only literals, since PostgreSQL requires a select list with `DISTINCT ON`, and an order that `checkDistinctOnOrder` rejects.
1919
+ */
1920
+ function checkDistinctOn(projections, keys, orderBy) {
1921
+ if (projections.length === 0) throw new Error("distinctOn requires at least one selected column or SQL expression; literal fields are not selected");
1922
+ checkDistinctOnOrder(projections, keys, orderBy);
1923
+ }
1924
+ /**
1925
+ * Rejects an order of a query of `distinctOn` of the lowered `projections`, `keys`, and `orderBy` that sorts by a value other than the keys before it sorts by all of them, in any order and direction, which PostgreSQL rejects, since the order sorts the groups apart and then chooses the first row of each; an order of only keys may omit some, which PostgreSQL adds.
1926
+ *
1927
+ * A sort key is a key when it is the same column, or the same expression node that a projection of the query has, as `findValueProjection` tells, so that both compile to the same column or position.
1928
+ * Another expression may compile at each place with parameters and aliases of its own, so PostgreSQL may find the two unequal; the check runs before packing, which would add the expression to the projections of its derived source and make both positions, so that it counts as another value wherever the query is.
1929
+ */
1930
+ function checkDistinctOnOrder(projections, keys, orderBy) {
1931
+ const isKey = (key, expression) => isSameValue(key, expression) && (key.kind === "column" || findValueProjection(projections, key) !== -1);
1932
+ let leading = 0;
1933
+ while (leading < orderBy.length && keys.some((key) => isKey(key, orderBy[leading].expression))) leading++;
1934
+ if (leading === orderBy.length) return;
1935
+ for (const key of keys) {
1936
+ let sorted = false;
1937
+ for (let index = 0; index < leading && !sorted; index++) sorted = isKey(key, orderBy[index].expression);
1938
+ if (!sorted) throw new Error("A query of distinctOn must sort by all its keys before any other value; sort by a key of an expression through its selected field");
1939
+ }
1940
+ }
1941
+ /**
1942
+ * Lowers the rows of `node`, a query of `distinct` or `distinctOn` made by an aggregate method or `exists`, to the statement that gives them, which an aggregate reads from a derived source and `EXISTS` reads as it is.
1943
+ *
1944
+ * It lowers the fields that the query kept, its keys, its order, and `argument`, the first argument of the aggregate, through those fields, as `lowerSelectedValue` does, so that each value of a selected field is its projection, and checks the query as the statement of the query of those fields is checked, except that a query of `distinctOn` needs no selected field, since the statement selects its keys.
1945
+ *
1946
+ * - The statement of `distinct` selects the fields, which `DISTINCT` compares, and `argument` must be one of them, as a sort key must be, since duplicates can differ in any other value.
1947
+ * - The statement of `distinctOn` selects `argument` and each key that it does not select otherwise, so that it has a select list, which PostgreSQL requires with `DISTINCT ON`.
1948
+ * It selects no other field, since an aggregate reads the rows of the source rather than of the select list, as for a query without `distinctOn`, so it does not lower the fields of nested rows, which neither a key nor a sort key reads.
1949
+ *
1950
+ * The statement has the order of the query when `sorted` is set; the number of the rows, which `count(*)` and `EXISTS` read, does not depend on it.
1951
+ */
1952
+ function lowerDistinctRows(node, context, argument, sorted) {
1953
+ const fields = dropLiterals(node.distinctFields);
1954
+ let selected = fields;
1955
+ if (!node.distinct) {
1956
+ const scalars = [];
1957
+ for (const field of fields) if (!isNestedSelection(field) || isSelectedAsValue(field.query)) scalars.push(field);
1958
+ selected = scalars;
1959
+ }
1960
+ const projections = lowerProjections(selected, context);
1961
+ const orderBy = lowerOrder(node.orderBy, context, selected, projections);
1962
+ const groupBy = lowerKeys(node.groupBy, context, selected, projections);
1963
+ const keys = lowerKeys(node.distinctOn, context, selected, projections);
1964
+ const value = argument === void 0 ? void 0 : lowerSelectedValue(argument, context, selected, projections);
1965
+ let sourced;
1966
+ let valueKey;
1967
+ if (keys === void 0) {
1968
+ checkDistinct(projections, orderBy);
1969
+ sourced = projections;
1970
+ if (value !== void 0) {
1971
+ const position = findValueProjection(projections, value);
1972
+ if (position === -1) throw new Error("An aggregate of a query of distinct reads only its selected fields, since duplicates can differ in any other value; select the value to aggregate it");
1973
+ valueKey = projections[position].key;
1974
+ }
1975
+ } else {
1976
+ checkDistinctOnOrder(projections, keys, orderBy);
1977
+ const keyed = [];
1978
+ if (value !== void 0) {
1979
+ valueKey = "value";
1980
+ keyed.push(Object.freeze({
1981
+ key: valueKey,
1982
+ expression: value,
1983
+ type: text
1984
+ }));
1985
+ }
1986
+ for (let index = 0; index < keys.length; index++) {
1987
+ const key = keys[index];
1988
+ if (findValueProjection(keyed, key) === -1) keyed.push(Object.freeze({
1989
+ key: `$key${index + 1}`,
1990
+ expression: key,
1991
+ type: text
1992
+ }));
1993
+ }
1994
+ sourced = keyed;
1995
+ }
1996
+ return {
1997
+ query: Object.freeze({
1998
+ kind: "select",
1999
+ from: node.from,
2000
+ joins: noJoins,
2001
+ projections: sourced,
2002
+ filters: lowerFilters(node.filters, context),
2003
+ groupBy,
2004
+ having: lowerValue(node.having, context),
2005
+ orderBy: sorted ? orderBy : noOrder,
2006
+ limit: node.limit,
2007
+ offset: node.offset,
2008
+ distinct: node.distinct,
2009
+ distinctOn: keys,
2010
+ rows: "many",
2011
+ value: false,
2012
+ guaranteedBy: void 0
2013
+ }),
2014
+ valueKey
2015
+ };
2016
+ }
2017
+ // src/lowering/aggregate.ts
2018
+ /** Lowers an aggregate query to a statement of the value of its aggregate over the rows that its filters, limit, and offset choose, also the distinct rows of `distinct` and the first rows of `distinctOn`. */
2019
+ function lowerAggregate(node, context) {
2020
+ const field = node.projections[0];
2021
+ const aggregate = field.expression;
2022
+ const counted = aggregate === countProjection.expression;
2023
+ let from = node.from;
2024
+ let filters = noFilters;
2025
+ let args = aggregate.arguments;
2026
+ let order = noOrder;
2027
+ let rows;
2028
+ let valueKey;
2029
+ if (node.distinctFields !== void 0) {
2030
+ const distinctRows = lowerDistinctRows(node, context, counted ? void 0 : aggregate.arguments[0], !counted);
2031
+ rows = distinctRows.query;
2032
+ valueKey = distinctRows.valueKey;
2033
+ } else {
2034
+ const orderBy = lowerOrder(node.orderBy, context);
2035
+ const groupBy = lowerKeys(node.groupBy, context);
2036
+ filters = lowerFilters(node.filters, context);
2037
+ args = lowerEach(aggregate.arguments, context, lowerExpression);
2038
+ order = field.ordered ? orderBy : noOrder;
2039
+ if (groupBy !== void 0 || node.limit !== void 0 || node.offset !== void 0 || !counted && !refersToSource(args[0], node.from)) {
2040
+ const projections = [];
2041
+ if (!counted) {
2042
+ valueKey = "value";
2043
+ projections.push(Object.freeze({
2044
+ key: valueKey,
2045
+ expression: args[0],
2046
+ type: field.type
2047
+ }));
2048
+ }
2049
+ rows = {
2050
+ kind: "select",
2051
+ from: node.from,
2052
+ joins: noJoins,
2053
+ projections,
2054
+ filters,
2055
+ groupBy,
2056
+ having: lowerValue(node.having, context),
2057
+ orderBy: counted ? noOrder : orderBy,
2058
+ limit: node.limit,
2059
+ offset: node.offset,
2060
+ rows: "many",
2061
+ value: false,
2062
+ guaranteedBy: void 0
2063
+ };
2064
+ }
2065
+ }
2066
+ if (rows !== void 0) {
2067
+ const derived = deriveOrderedSource(rows, field.ordered);
2068
+ from = derived.source;
2069
+ filters = noFilters;
2070
+ if (valueKey !== void 0) {
2071
+ const sourceArgs = [createNode({
2072
+ kind: "column",
2073
+ source: from,
2074
+ key: valueKey
2075
+ })];
2076
+ for (let index = 1; index < args.length; index++) sourceArgs.push(args[index]);
2077
+ args = sourceArgs;
2078
+ }
2079
+ order = derived.order;
2080
+ }
2081
+ const call = args === aggregate.arguments && order.length === 0 ? aggregate : createNode({
2082
+ kind: "aggregate",
2083
+ name: aggregate.name,
2084
+ distinct: aggregate.distinct,
2085
+ arguments: args,
2086
+ orderBy: order,
2087
+ filter: aggregate.filter
2088
+ });
2089
+ const fallbacks = lowerEach(field.fallbacks, context, lowerExpression);
2090
+ let value = call;
2091
+ if (fallbacks.length > 0) {
2092
+ const values = [call];
2093
+ for (const fallback of fallbacks) values.push(fallback);
2094
+ value = createList("coalesce(", values, ")");
2095
+ }
2096
+ return Object.freeze({
2097
+ kind: "select",
2098
+ from,
2099
+ joins: noJoins,
2100
+ projections: [value === aggregate ? field : Object.freeze({
2101
+ key: field.key,
2102
+ expression: value,
2103
+ type: field.type
2104
+ })],
2105
+ filters,
2106
+ orderBy: noOrder,
2107
+ limit: void 0,
2108
+ offset: void 0,
2109
+ rows: node.rows,
2110
+ value: true,
2111
+ guaranteedBy: void 0
2112
+ });
2113
+ }
2114
+ /**
2115
+ * Tells whether `node` refers to `source` outside the queries nested in it.
2116
+ *
2117
+ * A reference inside a nested query is not counted, so an argument that refers to the source only there is read from a derived source, which is correct either way.
2118
+ */
2119
+ function refersToSource(node, source) {
2120
+ switch (node.kind) {
2121
+ case "column": return node.source === source;
2122
+ case "param":
2123
+ case "boolean":
2124
+ case "values":
2125
+ case "nestedQuery":
2126
+ case "tableName":
2127
+ case "subquery":
2128
+ case "exists":
2129
+ case "textSubquery": return false;
2130
+ case "cast":
2131
+ case "not":
2132
+ case "isNull":
2133
+ case "inQuery": return refersToSource(node.operand, source);
2134
+ case "and":
2135
+ case "or": return node.operands.some((operand) => refersToSource(operand, source));
2136
+ case "comparison": return refersToSource(node.left, source) || refersToSource(node.right, source);
2137
+ case "in": return refersToSource(node.operand, source) || node.list.some((item) => refersToSource(item, source));
2138
+ case "raw": return node.values.some((value) => refersToSource(value, source));
2139
+ case "aggregate": return node.arguments.some((argument) => refersToSource(argument, source)) || node.filter !== void 0 && refersToSource(node.filter, source);
2140
+ default: return node;
2141
+ }
2142
+ }
2143
+ // src/lowering/exists.ts
2144
+ /** Lowers a query of `exists` to `EXISTS` of the rows that its filters, limit, and offset choose, also the distinct rows of `distinct` and the first rows of `distinctOn`. */
2145
+ function lowerExists(node, context) {
2146
+ checkRelation(node, context);
2147
+ return createNode({
2148
+ kind: "exists",
2149
+ query: node.distinctFields === void 0 ? Object.freeze({
2150
+ kind: "select",
2151
+ from: node.from,
2152
+ joins: noJoins,
2153
+ projections: noProjections,
2154
+ filters: lowerFilters(node.filters, context),
2155
+ groupBy: lowerKeys(node.groupBy, context),
2156
+ having: lowerValue(node.having, context),
2157
+ orderBy: noOrder,
2158
+ limit: node.limit,
2159
+ offset: node.offset,
2160
+ rows: "many",
2161
+ value: false,
2162
+ guaranteedBy: void 0
2163
+ }) : lowerDistinctRows(node, context, void 0, false).query
2164
+ });
2165
+ }
2166
+ // src/ast/references.ts
2167
+ /** Adds to `keys` the key of each column reference to `source` in `node` at any depth, including the queries nested in it. */
2168
+ function collectColumnKeys(node, source, keys) {
2169
+ visitNodes(node, (child) => {
2170
+ if (child.kind === "column" && child.source === source) keys.add(child.key);
1505
2171
  });
1506
2172
  }
1507
2173
  /**
1508
- * Creates a query whose only projection is the rows of `query` packed as JSON, a field decoded by the result plan of `query`.
2174
+ * Tells whether `node` holds, at any depth, what may aggregate the rows of the query of `source`: an `aggregate` node or a SQL fragment that refers to a column of `source` at any depth, also in the queries nested in it.
1509
2175
  *
1510
- * Rows `many` are packed as an array in the order of `query`, and the created query returns one row.
1511
- * Otherwise the created query returns a packed row for each row of `query`, so a scalar subquery of it gives the packed row, `NULL` without a row, and fails with several rows.
2176
+ * PostgreSQL assigns an aggregate whose arguments and filter refer only to the sources of an enclosing query to that query, also through a query nested in them ([PostgreSQL](https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-AGGREGATES)), so such a call in a nested query, such as `sum` of a column of the parent row, aggregates the rows of the query of `source`.
2177
+ * Lowering does not parse SQL fragments to identify their aggregates, so a fragment counts as such a call.
2178
+ *
2179
+ * It is conservative: an aggregate whose arguments read both `source` and its own query belongs to its own query, yet it counts, since this walk cannot tell a source of the query that holds the aggregate from one of an enclosing query.
2180
+ * So does a scalar fragment, also one that reads `source` only through a query correlated with it, such as of a relation.
1512
2181
  */
1513
- function packRows(query) {
1514
- const { projections } = query;
1515
- const many = query.rows === "many";
1516
- const { source, order } = deriveOrderedSource(query, many, query.limit === void 0 && query.offset === void 0 && projections.some((projection) => projection.nested === void 0 && !isTableColumn(projection.expression)) ? zeroNode : query.offset);
1517
- const wide = projections.length > maxFunctionArguments;
1518
- const values = [];
1519
- for (const projection of projections) {
1520
- const value = createRef(source, projection.key);
1521
- if (projection.nested !== void 0) values.push(value);
1522
- else values.push(wide ? createList("to_json(", [packScalar(value)], ")") : packScalar(value));
1523
- }
1524
- const row = wide ? createList("array_to_json(ARRAY[", values, "])") : createList("json_build_array(", values, ")");
1525
- let packed = row;
1526
- if (many) packed = createNode({
1527
- kind: "raw",
1528
- fragments: ["coalesce(", ", '[]'::json)"],
1529
- values: [createNode({
1530
- kind: "aggregate",
1531
- name: "json_agg",
1532
- distinct: false,
1533
- arguments: [row],
1534
- orderBy: order
1535
- })]
1536
- });
1537
- const plan = Object.freeze({
1538
- fields: projections,
1539
- rows: query.rows,
1540
- value: query.value,
1541
- guaranteedBy: query.guaranteedBy,
1542
- recordTemplate: query.recordTemplate
1543
- });
1544
- const projection = Object.freeze({
1545
- key: "rows",
1546
- expression: packed,
1547
- nested: plan
2182
+ function mayAggregateSource(node, source) {
2183
+ let found = false;
2184
+ const check = (value) => {
2185
+ if (value.kind === "column" && value.source === source) found = true;
2186
+ };
2187
+ visitNodes(node, (child) => {
2188
+ if (!found && (child.kind === "aggregate" || child.kind === "raw")) visitNodes(child, check);
1548
2189
  });
1549
- return createSelectNode(source, [projection]);
1550
- }
1551
- /** Tells whether `node` is a column of a table, which PostgreSQL reads rather than evaluates, so it can be repeated. */
1552
- function isTableColumn(node) {
1553
- return node.kind === "column" && node.source.kind === "table";
2190
+ return found;
1554
2191
  }
1555
2192
  /**
1556
- * Packs a scalar as its PostgreSQL text representation, or `NULL`.
2193
+ * Tells whether an aggregate belongs to an enclosing query rather than the query of `source`.
1557
2194
  *
1558
- * `format` uses the output function of the type, as the driver receives the values of a row; a cast to `text` differs from it for types such as `boolean`, `inet`, and `char`.
2195
+ * Only references free in its arguments, order, and filter decide its owner: columns bound by a subquery do not count, but references through that subquery to its enclosing queries do.
2196
+ * Without such references, an aggregate of constants belongs to the query that contains it.
2197
+ * The node must have been lowered, so that subqueries expose their sources.
1559
2198
  */
1560
- function packScalar(value) {
1561
- return createNode({
1562
- kind: "raw",
1563
- fragments: [
1564
- "CASE WHEN ",
1565
- " IS NOT DISTINCT FROM NULL THEN NULL ELSE format('%s', ",
1566
- ") END"
1567
- ],
1568
- values: [value, value]
1569
- });
2199
+ function aggregatesOuterSource(node, source) {
2200
+ let local = false;
2201
+ let outer = false;
2202
+ const queries = [];
2203
+ visitNodes(node, (child) => {
2204
+ if (child.kind !== "column" || local) return;
2205
+ for (const query of queries) {
2206
+ if (bindsSource(query.from, child.source)) return;
2207
+ for (const join of query.joins) if (bindsSource(join.source, child.source)) return;
2208
+ }
2209
+ if (child.source === source) local = true;
2210
+ else outer = true;
2211
+ }, queries);
2212
+ return outer && !local;
1570
2213
  }
1571
- function createRef(source, key) {
1572
- return createNode({
1573
- kind: "column",
1574
- source,
1575
- key
1576
- });
2214
+ /** Tells whether a declared source binds a reference, including the table occurrence a derived source replaces. */
2215
+ function bindsSource(declared, source) {
2216
+ return declared === source || declared?.kind === "derived" && declared.replaces === source;
1577
2217
  }
1578
- // src/lowering/lower.ts
1579
- const noJoins = /* @__PURE__ */ Object.freeze([]);
1580
- const noOrder = /* @__PURE__ */ Object.freeze([]);
1581
- const noProjections = /* @__PURE__ */ Object.freeze([]);
1582
- function lowerStatement(node, definitions, db) {
1583
- const context = {
1584
- definitions,
1585
- db
1586
- };
2218
+ /**
2219
+ * Calls `visit` for `node` and each expression node in it at any depth, including the queries nested in it.
2220
+ * When supplied, `queries` holds the nested queries enclosing each node passed to `visit`.
2221
+ */
2222
+ function visitNodes(node, visit, queries) {
2223
+ visit(node);
1587
2224
  switch (node.kind) {
1588
- case "query": return lowerSelect(node, context, true);
1589
- case "record": return lowerRecord(node, context);
1590
- case "text": return lowerText(node, context);
1591
- default: return lowerMutation(node, context);
2225
+ case "column":
2226
+ case "param":
2227
+ case "boolean":
2228
+ case "tableName":
2229
+ case "values":
2230
+ case "nestedQuery": return;
2231
+ case "cast":
2232
+ case "not":
2233
+ case "isNull":
2234
+ visitNodes(node.operand, visit, queries);
2235
+ return;
2236
+ case "and":
2237
+ case "or":
2238
+ for (const operand of node.operands) visitNodes(operand, visit, queries);
2239
+ return;
2240
+ case "comparison":
2241
+ visitNodes(node.left, visit, queries);
2242
+ visitNodes(node.right, visit, queries);
2243
+ return;
2244
+ case "in":
2245
+ visitNodes(node.operand, visit, queries);
2246
+ for (const item of node.list) visitNodes(item, visit, queries);
2247
+ return;
2248
+ case "inQuery":
2249
+ visitNodes(node.operand, visit, queries);
2250
+ if (node.query.kind === "select") visitQuery(node.query, visit, queries);
2251
+ return;
2252
+ case "raw":
2253
+ for (const value of node.values) visitNodes(value, visit, queries);
2254
+ return;
2255
+ case "subquery":
2256
+ case "exists":
2257
+ case "textSubquery":
2258
+ visitQuery(node.query, visit, queries);
2259
+ return;
2260
+ case "aggregate":
2261
+ for (const argument of node.arguments) visitNodes(argument, visit, queries);
2262
+ for (const order of node.orderBy) visitNodes(order.expression, visit, queries);
2263
+ if (node.filter !== void 0) visitNodes(node.filter, visit, queries);
2264
+ return;
1592
2265
  }
1593
2266
  }
1594
- /** Lowers a statement of SQL text, replacing the queries in the expressions interpolated into it. */
1595
- function lowerText(node, context) {
1596
- const sql = lowerExpression(node.sql, context);
1597
- if (sql === node.sql) return node;
1598
- return Object.freeze({
1599
- kind: "text",
1600
- sql,
1601
- fields: node.fields,
1602
- rows: node.rows,
1603
- value: node.value
1604
- });
2267
+ /**
2268
+ * Calls `visit` for each expression node in the clauses and the derived sources of `query` at any depth.
2269
+ * When supplied, `queries` holds the nested queries enclosing each node passed to `visit`; this call adds `query` for its duration.
2270
+ */
2271
+ function visitQuery(query, visit, queries) {
2272
+ queries?.push(query);
2273
+ const { from, filters } = query;
2274
+ if (from?.kind === "derived") visitQuery(from.query, visit, queries);
2275
+ for (const join of query.joins) {
2276
+ if (join.source.kind === "derived") visitQuery(join.source.query, visit, queries);
2277
+ visitNodes(join.on, visit, queries);
2278
+ }
2279
+ if (query.distinctOn !== void 0) for (const key of query.distinctOn) visitNodes(key, visit, queries);
2280
+ for (const projection of query.projections) visitNodes(projection.expression, visit, queries);
2281
+ if (filters.relation !== void 0) visitNodes(filters.relation, visit, queries);
2282
+ if (filters.scope !== void 0) visitNodes(filters.scope, visit, queries);
2283
+ if (filters.user !== void 0) visitNodes(filters.user, visit, queries);
2284
+ if (query.groupBy !== void 0) for (const key of query.groupBy) visitNodes(key, visit, queries);
2285
+ if (query.having !== void 0) visitNodes(query.having, visit, queries);
2286
+ for (const order of query.orderBy) visitNodes(order.expression, visit, queries);
2287
+ if (query.limit !== void 0) visitNodes(query.limit, visit, queries);
2288
+ if (query.offset !== void 0) visitNodes(query.offset, visit, queries);
2289
+ queries?.pop();
2290
+ }
2291
+ // src/lowering/group.ts
2292
+ /**
2293
+ * Rejects directly selected, unfiltered aggregates of an enclosing query in a query of groups.
2294
+ *
2295
+ * Types give these aggregates the nullability of their arguments in a nonempty group, but an enclosing query can have no rows.
2296
+ * A typed SQL fragment keeps its declared nullability and is the explicit escape hatch for an outer aggregate.
2297
+ */
2298
+ function checkGroupAggregates(projections, query) {
2299
+ for (const field of projections) {
2300
+ const expression = field.expression;
2301
+ if (expression.kind === "aggregate" && expression.name !== "count" && expression.filter === void 0 && aggregatesOuterSource(expression, query.from)) throw new Error(`An aggregate function of ${query.value ? "the grouped value" : `grouped field ${JSON.stringify(field.key)}`} refers only to enclosing queries; use an explicitly nullable sql expression for an outer aggregate`);
2302
+ }
1605
2303
  }
2304
+ // src/lowering/mutation.ts
1606
2305
  /** Lowers a mutation to its statement, replacing the queries in its expressions, and packing the rows of each nested query of `RETURNING` into the value of its field, or selecting the value of a nested aggregate query, as a select list does. */
1607
2306
  function lowerMutation(node, context) {
1608
- const { target, returning, rows, value } = node;
2307
+ const { target, returning, rows, value, conversion } = node;
1609
2308
  const recordTemplate = returning === void 0 ? void 0 : createRecordTemplate(returning);
1610
2309
  const lowered = returning === void 0 ? void 0 : lowerProjections(dropLiterals(returning), context);
1611
2310
  switch (node.kind) {
1612
2311
  case "insert": {
1613
2312
  const values = lowerEach(node.values, context, lowerRow);
1614
- 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;
1615
2315
  return Object.freeze({
1616
2316
  kind: "insert",
1617
2317
  target,
1618
2318
  columns: node.columns,
1619
2319
  values,
2320
+ conflict,
1620
2321
  returning: lowered,
1621
2322
  recordTemplate,
2323
+ conversion,
1622
2324
  rows,
1623
2325
  value
1624
2326
  });
@@ -1634,6 +2336,7 @@ function lowerMutation(node, context) {
1634
2336
  filters,
1635
2337
  returning: lowered,
1636
2338
  recordTemplate,
2339
+ conversion,
1637
2340
  rows,
1638
2341
  value
1639
2342
  });
@@ -1647,6 +2350,7 @@ function lowerMutation(node, context) {
1647
2350
  filters,
1648
2351
  returning: lowered,
1649
2352
  recordTemplate,
2353
+ conversion,
1650
2354
  rows,
1651
2355
  value
1652
2356
  });
@@ -1658,8 +2362,18 @@ function lowerMutation(node, context) {
1658
2362
  function lowerRow(row, context) {
1659
2363
  return lowerEach(row, context, lowerValue);
1660
2364
  }
1661
- function lowerValue(node, context) {
1662
- return node === void 0 ? void 0 : lowerExpression(node, context);
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
+ });
1663
2377
  }
1664
2378
  function lowerAssignment(assignment, context) {
1665
2379
  const value = lowerExpression(assignment.value, context);
@@ -1668,95 +2382,31 @@ function lowerAssignment(assignment, context) {
1668
2382
  value
1669
2383
  });
1670
2384
  }
1671
- /** Lowers a query as `lowerQuery` does, or as `lowerStatement` does when `positions` is set. */
1672
- function lowerSelect(node, context, positions) {
1673
- if (isExistsQuery(node)) return selectExists(node, context);
1674
- checkRelation(node, context);
1675
- if (isAggregateQuery(node)) return lowerAggregate(node, context);
1676
- const recordTemplate = createRecordTemplate(node.projections);
1677
- const selected = dropLiterals(node.projections);
1678
- const projections = lowerProjections(selected, context);
1679
- const filters = lowerFilters(node.filters, context);
1680
- const orderBy = lowerOrder(node.orderBy, context, selected, projections);
1681
- const { relation } = node;
1682
- const guaranteedBy = node.guaranteed && relation !== void 0 ? formatRelationName(relation.key, relation.name) : void 0;
1683
- const page = node.offset !== void 0 && selected.some((field, index) => isPagedField(field, projections[index], orderBy)) ? lowerPage(node, selected, projections, filters, orderBy, positions, guaranteedBy, recordTemplate) : void 0;
1684
- if (page !== void 0) return page;
1685
- return Object.freeze({
1686
- kind: "select",
1687
- from: node.from,
1688
- joins: noJoins,
1689
- projections,
1690
- filters,
1691
- orderBy: positions && orderBy.length > 0 ? sortByPositions(orderBy, projections) : orderBy,
1692
- limit: node.limit,
1693
- offset: node.offset,
1694
- rows: node.rows,
1695
- value: node.value,
1696
- guaranteedBy,
1697
- recordTemplate
1698
- });
1699
- }
1700
- /** Returns the fields of `selected` that a statement selects, or `selected` itself without literals. */
1701
- function dropLiterals(selected) {
1702
- if (!selected.some(isLiteralSelection)) return selected;
1703
- const fields = [];
1704
- for (const field of selected) if (!isLiteralSelection(field)) fields.push(field);
1705
- return fields;
1706
- }
1707
- /** Creates the template of the records of `selected`: every field in its order with the value of a literal, or `undefined` without literals. */
1708
- function createRecordTemplate(selected) {
1709
- if (!selected.some(isLiteralSelection)) return;
1710
- const template = [];
1711
- for (const field of selected) template.push(Object.freeze({
1712
- key: field.key,
1713
- value: isLiteralSelection(field) ? field.literal : void 0
1714
- }));
1715
- return template;
1716
- }
1717
- /**
1718
- * Tells whether `field`, a field of a query lowered to `projection`, is a nested query that a page after an offset evaluates only over its rows: one that no sort key of `orderBy`, the lowered order, sorts by.
1719
- *
1720
- * The query evaluates a field that it sorts by, of an aggregate or of `exists`, for every row anyway.
1721
- */
1722
- function isPagedField(field, projection, orderBy) {
1723
- if (!isNestedSelection(field)) return false;
1724
- const { expression } = projection;
1725
- return !orderBy.some((order) => order.expression === expression);
1726
- }
1727
- /** Rejects a query of a relation outside the definitions of `context`. */
1728
- function checkRelation(node, context) {
1729
- const { relation } = node;
1730
- const { definitions } = context;
1731
- if (relation !== void 0 && definitions !== void 0 && !definitions.has(relation)) throw new Error(`Relation "${formatRelationName(relation.key, relation.name)}" is not part of the schema`);
1732
- }
1733
- /** Rejects a nested query bound to `db`, which the error names as `subject`, unless `db` is the `Db` of `context` or `undefined`. */
1734
- function checkDb(db, context, subject) {
1735
- if (db !== void 0 && db !== context.db) throw new Error(context.db === void 0 ? `${subject} is bound to a Db, so only that Db can compile the query` : `${subject} is bound to another Db`);
1736
- }
2385
+ // src/lowering/page.ts
1737
2386
  /**
1738
- * Lowers `node` with `projections`, `filters`, and `orderBy`, the lowered parts of the node, to a statement that evaluates its nested queries, apart from those that it sorts by, over the rows of a derived page.
1739
- * `queryFields` are the fields of the query without literals, and `projections` their lowered projections in the same order.
1740
- * The derived source applies the filters, the order, the limit, and the offset, and replaces the occurrence of the table.
2387
+ * Lowers `query`, the statement of a query on its table that sorts by its sort expressions, to a statement that evaluates its nested queries, apart from those that it sorts by, groups by, or compares, over the rows of a derived page, or returns `undefined` for a query that keeps its page on its table.
2388
+ * `queryFields` are the fields of the query without literals, whose lowered projections the statement has in the same order.
2389
+ * The derived source applies `DISTINCT ON`, the filters, the keys and the condition of the groups, the order, the limit, and the offset, and replaces the occurrence of the table.
2390
+ * It selects each key of `distinctOn` that it does not select otherwise, so that it has a select list, which PostgreSQL requires with `DISTINCT ON`, and evaluates each key once; a key of `groupBy` needs no projection of its own, since it compiles as the position of a projection that has its value or as its expression.
1741
2391
  *
1742
- * It returns `undefined` when a nested query that it evaluates over the rows of the page holds a SQL fragment that refers to a column of the table outside the queries nested in the fragment.
1743
- * Such a fragment may hold an aggregate of the query, which the limit and the offset apply after.
2392
+ * A query keeps its page on its table without an offset and `distinctOn`, without nested queries that the page would evaluate over its rows, or when `mayAggregateSource` tells that such a nested query may aggregate the rows of the query, which the limit and the offset apply after.
1744
2393
  *
1745
- * The source selects the columns of the table that the fields and the nested queries of the query read, under their keys, and the other scalar fields of the query, which it evaluates before the limit and the offset as the query would, such as a window function over all its rows.
1746
- * A field of an aggregate or of `exists` that the query sorts by is such a scalar field: the order needs its value for every row.
2394
+ * The source selects the columns of the table that the fields and the nested queries of the query read, under their keys, and the other scalar fields of the query, which it evaluates before the limit and the offset as the query would, such as a window function over all its rows or an aggregate of a group.
2395
+ * A field of an aggregate or of `exists` that the query sorts by or that is a key is such a scalar field: the order and the keys need its value for every row.
1747
2396
  * A column that no field reads stays out of the source, which could not select it beside an aggregate of a SQL fragment, such as `count(*)`.
1748
2397
  * The order of a derived source does not carry into the query that reads it, so the statement sorts the rows of the page again by the sort keys of the source, by positions when `positions` is set.
1749
2398
  */
1750
- function lowerPage(node, queryFields, projections, filters, orderBy, positions, guaranteedBy, recordTemplate) {
1751
- const { from } = node;
2399
+ function lowerPage(query, queryFields, positions) {
2400
+ const { from, projections, orderBy, groupBy, distinctOn, offset } = query;
2401
+ if (offset === void 0 && distinctOn === void 0 || query.distinct || query.lock !== void 0 || !queryFields.some((field, index) => isPagedField(field, projections[index], orderBy, groupBy, distinctOn))) return;
1752
2402
  const columnKeys = /* @__PURE__ */ new Set();
1753
2403
  const fields = [];
1754
2404
  const fieldKeys = [];
1755
2405
  for (let index = 0; index < projections.length; index++) {
1756
2406
  const { expression } = projections[index];
1757
2407
  let key;
1758
- if (isPagedField(queryFields[index], projections[index], orderBy)) {
1759
- if (holdsFragmentOfSource(expression, from)) return;
2408
+ if (isPagedField(queryFields[index], projections[index], orderBy, groupBy, distinctOn)) {
2409
+ if (mayAggregateSource(expression, from)) return;
1760
2410
  collectColumnKeys(expression, from, columnKeys);
1761
2411
  } else if (expression.kind === "column" && expression.source === from) columnKeys.add(expression.key);
1762
2412
  else {
@@ -1780,19 +2430,30 @@ function lowerPage(node, queryFields, projections, filters, orderBy, positions,
1780
2430
  type: text
1781
2431
  }));
1782
2432
  for (const field of fields) sourced.push(field);
2433
+ if (distinctOn !== void 0) for (let index = 0; index < distinctOn.length; index++) {
2434
+ const key = distinctOn[index];
2435
+ if (findValueProjection(sourced, key) === -1) sourced.push(Object.freeze({
2436
+ key: `$key${index + 1}`,
2437
+ expression: key,
2438
+ type: text
2439
+ }));
2440
+ }
1783
2441
  const page = deriveOrderedSource({
1784
2442
  kind: "select",
1785
2443
  from,
1786
2444
  joins: noJoins,
1787
2445
  projections: sourced,
1788
- filters,
2446
+ filters: query.filters,
2447
+ groupBy,
2448
+ having: query.having,
1789
2449
  orderBy,
1790
- limit: node.limit,
1791
- offset: node.offset,
2450
+ limit: query.limit,
2451
+ offset,
2452
+ distinctOn,
1792
2453
  rows: "many",
1793
2454
  value: false,
1794
2455
  guaranteedBy: void 0
1795
- }, true, node.offset, from);
2456
+ }, true, offset, from);
1796
2457
  const selected = [];
1797
2458
  for (let index = 0; index < projections.length; index++) {
1798
2459
  const projection = projections[index];
@@ -1810,142 +2471,111 @@ function lowerPage(node, queryFields, projections, filters, orderBy, positions,
1810
2471
  return Object.freeze({
1811
2472
  kind: "select",
1812
2473
  from: page.source,
1813
- joins: noJoins,
1814
- projections: selected,
1815
- filters: noFilters,
1816
- orderBy: positions ? sortByPositions(page.order, selected) : page.order,
1817
- limit: void 0,
1818
- offset: void 0,
1819
- rows: node.rows,
1820
- value: node.value,
1821
- guaranteedBy,
1822
- recordTemplate
1823
- });
1824
- }
1825
- /** Lowers an aggregate query to a statement of the value of its aggregate over the rows that its filters, limit, and offset choose. */
1826
- function lowerAggregate(node, context) {
1827
- const field = node.projections[0];
1828
- const aggregate = field.expression;
1829
- const orderBy = lowerOrder(node.orderBy, context);
1830
- let from = node.from;
1831
- let filters = lowerFilters(node.filters, context);
1832
- let args = lowerEach(aggregate.arguments, context, lowerExpression);
1833
- let order = field.ordered ? orderBy : noOrder;
1834
- const counted = aggregate === countProjection.expression;
1835
- if (node.limit !== void 0 || node.offset !== void 0 || !counted && !refersToSource(args[0], node.from)) {
1836
- const projections = [];
1837
- if (!counted) projections.push(Object.freeze({
1838
- key: "value",
1839
- expression: args[0],
1840
- type: field.type
1841
- }));
1842
- const rows = deriveOrderedSource({
1843
- kind: "select",
1844
- from: node.from,
1845
- joins: noJoins,
1846
- projections,
1847
- filters,
1848
- orderBy: counted ? noOrder : orderBy,
1849
- limit: node.limit,
1850
- offset: node.offset,
1851
- rows: "many",
1852
- value: false,
1853
- guaranteedBy: void 0
1854
- }, field.ordered);
1855
- from = rows.source;
1856
- filters = noFilters;
1857
- if (!counted) {
1858
- const sourceArgs = [createNode({
1859
- kind: "column",
1860
- source: from,
1861
- key: "value"
1862
- })];
1863
- for (let index = 1; index < args.length; index++) sourceArgs.push(args[index]);
1864
- args = sourceArgs;
1865
- }
1866
- order = rows.order;
1867
- }
1868
- const call = args === aggregate.arguments && order.length === 0 ? aggregate : createNode({
1869
- kind: "aggregate",
1870
- name: aggregate.name,
1871
- distinct: aggregate.distinct,
1872
- arguments: args,
1873
- orderBy: order
1874
- });
1875
- const fallbacks = lowerEach(field.fallbacks, context, lowerExpression);
1876
- let value = call;
1877
- if (fallbacks.length > 0) {
1878
- const values = [call];
1879
- for (const fallback of fallbacks) values.push(fallback);
1880
- value = createList("coalesce(", values, ")");
1881
- }
1882
- return Object.freeze({
1883
- kind: "select",
1884
- from,
1885
- joins: noJoins,
1886
- projections: [value === aggregate ? field : Object.freeze({
1887
- key: field.key,
1888
- expression: value,
1889
- type: field.type
1890
- })],
1891
- filters,
1892
- orderBy: noOrder,
2474
+ joins: noJoins,
2475
+ projections: selected,
2476
+ filters: noFilters,
2477
+ orderBy: positions ? sortByPositions(page.order, selected) : page.order,
1893
2478
  limit: void 0,
1894
2479
  offset: void 0,
1895
- rows: node.rows,
1896
- value: true,
1897
- guaranteedBy: void 0
2480
+ rows: query.rows,
2481
+ value: query.value,
2482
+ guaranteedBy: query.guaranteedBy,
2483
+ recordTemplate: query.recordTemplate
1898
2484
  });
1899
2485
  }
1900
2486
  /**
1901
- * Tells whether `node` refers to `source` outside the queries nested in it.
2487
+ * Tells whether `field`, a field of a query lowered to `projection`, is a nested query that a derived page evaluates only over its rows: one that no sort key of `orderBy`, the lowered order, sorts by, and that is none of `groupBy` and `distinctOn`, the lowered keys of the query.
1902
2488
  *
1903
- * A reference inside a nested query is not counted, so an argument that refers to the source only there is read from a derived source, which is correct either way.
2489
+ * The query evaluates a field that it sorts by, groups by, or compares, of an aggregate or of `exists`, for every row anyway.
1904
2490
  */
1905
- function refersToSource(node, source) {
2491
+ function isPagedField(field, projection, orderBy, groupBy, distinctOn) {
2492
+ if (!isNestedSelection(field)) return false;
2493
+ const { expression } = projection;
2494
+ return !orderBy.some((order) => order.expression === expression) && (groupBy === void 0 || !groupBy.includes(expression)) && (distinctOn === void 0 || !distinctOn.includes(expression));
2495
+ }
2496
+ // src/lowering/lower.ts
2497
+ function lowerStatement(node, definitions, db) {
2498
+ const context = createContext(definitions, db);
1906
2499
  switch (node.kind) {
1907
- case "column": return node.source === source;
1908
- case "param":
1909
- case "boolean":
1910
- case "values":
1911
- case "nestedQuery":
1912
- case "tableName":
1913
- case "subquery":
1914
- case "exists":
1915
- case "textSubquery": return false;
1916
- case "cast":
1917
- case "not":
1918
- case "isNull":
1919
- case "inQuery": return refersToSource(node.operand, source);
1920
- case "and":
1921
- case "or": return node.operands.some((operand) => refersToSource(operand, source));
1922
- case "comparison": return refersToSource(node.left, source) || refersToSource(node.right, source);
1923
- case "in": return refersToSource(node.operand, source) || node.list.some((item) => refersToSource(item, source));
1924
- case "raw": return node.values.some((value) => refersToSource(value, source));
1925
- case "aggregate": return node.arguments.some((argument) => refersToSource(argument, source));
1926
- default: return node;
2500
+ case "query": return lowerSelect(node, context, true);
2501
+ case "record": return lowerRecord(node, context);
2502
+ case "text": return lowerText(node, context);
2503
+ default: return lowerMutation(node, context);
1927
2504
  }
1928
2505
  }
1929
- /** Lowers a query of `exists` to `EXISTS` of the rows that its filters, limit, and offset choose. */
1930
- function lowerExists(node, context) {
1931
- checkRelation(node, context);
1932
- return createNode({
1933
- kind: "exists",
1934
- query: Object.freeze({
1935
- kind: "select",
1936
- from: node.from,
1937
- joins: noJoins,
1938
- projections: noProjections,
1939
- filters: lowerFilters(node.filters, context),
1940
- orderBy: noOrder,
1941
- limit: node.limit,
1942
- offset: node.offset,
1943
- rows: "many",
1944
- value: false,
1945
- guaranteedBy: void 0
1946
- })
2506
+ /** Creates the context of lowering a statement with `definitions` and `db`, whose nested queries `lowerSelect` lowers. */
2507
+ function createContext(definitions, db) {
2508
+ const context = {
2509
+ definitions,
2510
+ db,
2511
+ lowerSelect: (node, positions) => lowerSelect(node, context, positions),
2512
+ lowerExists: (node) => lowerExists(node, context)
2513
+ };
2514
+ return context;
2515
+ }
2516
+ /** Lowers a statement of SQL text, replacing the queries in the expressions interpolated into it. */
2517
+ function lowerText(node, context) {
2518
+ const sql = lowerExpression(node.sql, context);
2519
+ if (sql === node.sql) return node;
2520
+ return Object.freeze({
2521
+ kind: "text",
2522
+ sql,
2523
+ fields: node.fields,
2524
+ rows: node.rows,
2525
+ value: node.value
2526
+ });
2527
+ }
2528
+ /** Lowers a query as `lowerQuery` does, or as `lowerStatement` does when `positions` is set, with the conversion of its result. */
2529
+ function lowerSelect(node, context, positions) {
2530
+ const statement = lowerSelectStatement(node, context, positions);
2531
+ const { conversion } = node;
2532
+ return conversion === void 0 ? statement : Object.freeze({
2533
+ ...statement,
2534
+ conversion
1947
2535
  });
1948
2536
  }
2537
+ /** Lowers a query to its statement, whichever it is: of `exists`, of an aggregate, of a page that evaluates its nested queries only over its rows, or of the rows of its source. */
2538
+ function lowerSelectStatement(node, context, positions) {
2539
+ if (isExistsQuery(node)) return selectExists(node, context);
2540
+ checkRelation(node, context);
2541
+ if (isAggregateQuery(node)) return lowerAggregate(node, context);
2542
+ const recordTemplate = createRecordTemplate(node.projections);
2543
+ const selected = dropLiterals(node.projections);
2544
+ const projections = lowerProjections(selected, context);
2545
+ if (node.groupBy.length > 0) checkGroupAggregates(projections, node);
2546
+ const filters = lowerFilters(node.filters, context);
2547
+ const orderBy = lowerOrder(node.orderBy, context, selected, projections);
2548
+ const groupBy = lowerKeys(node.groupBy, context, selected, projections);
2549
+ const having = lowerValue(node.having, context);
2550
+ const distinctOn = lowerKeys(node.distinctOn, context, selected, projections);
2551
+ if (node.distinct) checkDistinct(projections, orderBy);
2552
+ if (distinctOn !== void 0) checkDistinctOn(projections, distinctOn, orderBy);
2553
+ const { relation } = node;
2554
+ const guaranteedBy = node.guaranteed && relation !== void 0 ? formatRelationName(relation.key, relation.name) : void 0;
2555
+ const statement = {
2556
+ kind: "select",
2557
+ from: node.from,
2558
+ joins: noJoins,
2559
+ projections,
2560
+ filters,
2561
+ groupBy,
2562
+ having,
2563
+ orderBy,
2564
+ limit: node.limit,
2565
+ offset: node.offset,
2566
+ distinct: node.distinct,
2567
+ distinctOn,
2568
+ lock: node.lock,
2569
+ rows: node.rows,
2570
+ value: node.value,
2571
+ guaranteedBy,
2572
+ recordTemplate
2573
+ };
2574
+ const page = lowerPage(statement, selected, positions);
2575
+ if (page !== void 0) return page;
2576
+ if (positions && orderBy.length > 0) statement.orderBy = sortByPositions(orderBy, projections);
2577
+ return Object.freeze(statement);
2578
+ }
1949
2579
  /** Lowers a query of `exists` to a statement without a source of one row of `EXISTS` of its rows. */
1950
2580
  function selectExists(node, context) {
1951
2581
  return Object.freeze({
@@ -1962,14 +2592,6 @@ function selectExists(node, context) {
1962
2592
  guaranteedBy: void 0
1963
2593
  });
1964
2594
  }
1965
- /** Creates the projection of `EXISTS` of the rows of `node`, a query of `exists`, under `key`, decoded by the type of the projection of `exists`. */
1966
- function projectExists(key, node, context) {
1967
- return Object.freeze({
1968
- key,
1969
- expression: lowerExists(node, context),
1970
- type: node.projections[0].type
1971
- });
1972
- }
1973
2595
  /** Lowers a query of `$select` to a statement without a source, which returns one row of its fields. */
1974
2596
  function lowerRecord(node, context) {
1975
2597
  return Object.freeze({
@@ -1987,236 +2609,433 @@ function lowerRecord(node, context) {
1987
2609
  recordTemplate: createRecordTemplate(node.projections)
1988
2610
  });
1989
2611
  }
2612
+ // src/query/kind.ts
2613
+ /** `then` of every bound query, which executes the query on its `Db`. */
2614
+ function executeOnAwait(onFulfilled, onRejected) {
2615
+ return this.db.$execute(this).then(onFulfilled, onRejected);
2616
+ }
2617
+ /** Compiles `query` as its `toSQL` does: an unbound query with `options`, and a bound query as its `Db` executes it, which takes no options. */
2618
+ function compileToSQL(query, options) {
2619
+ const { db } = query;
2620
+ if (db === void 0) {
2621
+ const { text, parameters } = compileStatement(lowerStatement(query.node), options);
2622
+ return createStatement(text, parameters);
2623
+ }
2624
+ if (options !== void 0) throw new Error("A query bound to a Db compiles with the options of its Db");
2625
+ return db.$compile(query);
2626
+ }
2627
+ // src/query/conversion.ts
2628
+ /**
2629
+ * Immutable query of a table of state `S` whose whole result `transform` converts after decoding, unbound or bound to a `Db`.
2630
+ *
2631
+ * It executes and nests in a selection as the query it comes from, with the same SQL, so it has only `transform`, which converts the result further: another method would change what the callback receives, and an expression does not take it, since SQL does not see the conversion.
2632
+ * An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
2633
+ * A bound query executes on its `Db` when awaited.
2634
+ */
2635
+ var TransformedQuery = class {
2636
+ node;
2637
+ /** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
2638
+ db;
2639
+ /** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
2640
+ then;
2641
+ constructor(node, db) {
2642
+ this.node = node;
2643
+ this.db = db;
2644
+ this.then = db === void 0 ? void 0 : executeOnAwait;
2645
+ Object.freeze(this);
2646
+ }
2647
+ transform(callback) {
2648
+ return transformQuery(this.node, this.db, callback);
2649
+ }
2650
+ toSQL(options) {
2651
+ return compileToSQL(this, options);
2652
+ }
2653
+ /** Rejects the query as an operand of an expression, whose value SQL reads without its conversion. */
2654
+ [operandNode]() {
2655
+ throw new TypeError("An expression takes a query of one value of a table, such as of get, an aggregate, or exists, not a query of transform");
2656
+ }
2657
+ };
2658
+ /** Creates the query of `transform` of `node`, a query of a table bound to `db` when given, whose whole result `callback` converts after the earlier conversions. */
2659
+ function transformQuery(node, db, callback) {
2660
+ return new TransformedQuery(Object.freeze({
2661
+ ...node,
2662
+ conversion: addTransform(node.conversion, callback),
2663
+ foundKey: void 0
2664
+ }), db);
2665
+ }
2666
+ /**
2667
+ * Returns `conversion` of a query with `callback` of `map` after its earlier one.
2668
+ *
2669
+ * It rejects a conversion of `transform`, whose result has no rows to convert.
2670
+ */
2671
+ function addMap(conversion, callback) {
2672
+ if (conversion?.transform !== void 0) throw new Error("map cannot be used after transform; convert the rows in its callback instead");
2673
+ return Object.freeze({
2674
+ map: chainConversions(conversion?.map, createConversion(callback, "map")),
2675
+ transform: void 0
2676
+ });
2677
+ }
2678
+ /** Returns `conversion` of a query with `callback` of `transform` after its earlier one. */
2679
+ function addTransform(conversion, callback) {
2680
+ return Object.freeze({
2681
+ map: conversion?.map,
2682
+ transform: chainConversions(conversion?.transform, createConversion(callback, "transform"))
2683
+ });
2684
+ }
2685
+ /** Returns the conversion that applies `next` after `previous`, if any. */
2686
+ function chainConversions(previous, next) {
2687
+ return previous === void 0 ? next : (value) => next(previous(value));
2688
+ }
2689
+ /**
2690
+ * Creates the conversion of `callback` of `method`, which rejects a Promise or another thenable returned by the callback: `SynchronousResult` rejects it in types, and a callback may still return one through `any`.
2691
+ *
2692
+ * It rejects a `callback` that is not a function when it is called, since decoding calls it long after, where its error would read as a failure of a row.
2693
+ */
2694
+ function createConversion(callback, method) {
2695
+ if (typeof callback !== "function") throw new TypeError(`${method} takes a callback`);
2696
+ return (value) => {
2697
+ const result = callback(value);
2698
+ if (typeof result?.then === "function") {
2699
+ if (types.isPromise(result)) result.catch(ignoreRejection);
2700
+ throw new TypeError(`The callback of ${method} must return its result synchronously, not a Promise or a query`);
2701
+ }
2702
+ return result;
2703
+ };
2704
+ }
2705
+ /** Handles the rejection of a Promise that a callback returned, whose query has failed already. */
2706
+ function ignoreRejection() {}
2707
+ // src/query/resolve-values.ts
2708
+ /**
2709
+ * Replaces objects of column values in a predicate with conditions on the columns of `source`.
2710
+ *
2711
+ * Groups are rebuilt, so constants that appear after resolution, such as an empty object, are simplified.
2712
+ */
2713
+ function resolveValues(node, source) {
2714
+ switch (node.kind) {
2715
+ case "values": {
2716
+ const conditions = [];
2717
+ for (const key of Object.keys(node.values)) conditions.push(matchColumn(source, key, node.values[key]));
2718
+ return combineNodes("and", conditions);
2719
+ }
2720
+ case "and":
2721
+ case "or": {
2722
+ const operands = [];
2723
+ for (const operand of node.operands) operands.push(resolveValues(operand, source));
2724
+ return combineNodes(node.kind, operands);
2725
+ }
2726
+ case "not": return negateNode(resolveValues(node.operand, source));
2727
+ case "raw": return createNode({
2728
+ ...node,
2729
+ values: node.values.map((value) => resolveValues(value, source))
2730
+ });
2731
+ default: return node;
2732
+ }
2733
+ }
2734
+ /** Creates the condition of column `key` of `source` in an object of column values: `IS NULL` of `null`, or the `eq` operator of its type for another value. */
2735
+ function matchColumn(source, key, value) {
2736
+ const type = findColumn(source.table, key)?.type;
2737
+ const column = createNode({
2738
+ kind: "column",
2739
+ source,
2740
+ key
2741
+ });
2742
+ if (value === null) return createIsNullNode(column);
2743
+ if (type) return matchValue(source, column, type, value);
2744
+ return compareNodes("=", column, value, void 0);
2745
+ }
2746
+ /** Matches a column with a value of an object condition by calling the `eq` operator of its type, whichever operator it is. */
2747
+ function matchValue(source, column, type, value) {
2748
+ const expression = toExpression(column, type);
2749
+ if (typeof expression.eq !== "function") throw new TypeError(`Column "${column.key}" of table "${source.table.name}" has no eq operator; an object condition can only match it with null`);
2750
+ const predicate = expression.eq(value);
2751
+ if (!isExpression(predicate)) throw new TypeError(`The eq operator of column "${column.key}" of table "${source.table.name}" must return an expression`);
2752
+ return toNode(predicate);
2753
+ }
2754
+ // src/query/filters.ts
2755
+ /** Creates query filters with the filter of `where` over `source` with the relations of `context` combined with `AND` with the user filters of `filters`. */
2756
+ function addUserFilter(filters, filter, source, context) {
2757
+ const predicate = toFilterNode(filter, source, context);
2758
+ const { user } = filters;
2759
+ return Object.freeze({
2760
+ ...filters,
2761
+ user: user ? combineNodes("and", [user, predicate]) : predicate
2762
+ });
2763
+ }
2764
+ /**
2765
+ * Converts a filter of `where` or `having` over `source` with the relations of `context` into the node of its condition, whose objects of column values it resolves.
2766
+ *
2767
+ * A callback receives the columns with the relations, and what `readSelected` returns, such as the selected fields for `having`.
2768
+ */
2769
+ function toFilterNode(filter, source, context, readSelected) {
2770
+ return resolveValues(toConditionNode(typeof filter === "function" ? filter(createSelectionRefs(source, context), readSelected?.()) : filter), source);
2771
+ }
2772
+ // src/query/find.ts
2773
+ /**
2774
+ * Returns the object of column values that `find` filters a query of `table` by: `values` of the columns of its primary key, in their order.
2775
+ *
2776
+ * It rejects a table without a primary key, a number of values other than the number of its columns, and `null` and `undefined`.
2777
+ */
2778
+ function toPrimaryKeyValues(table, values) {
2779
+ const { primaryKey } = table.keys;
2780
+ if (primaryKey === void 0) throw new Error(`find takes the values of a primary key, which table "${table.name}" does not have; use findBy with a unique key`);
2781
+ if (values.length !== primaryKey.length) throw new Error(`find takes one value for each column of the primary key of table "${table.name}", ${quoteColumns(primaryKey)}, not ${values.length}`);
2782
+ const filter = {};
2783
+ for (let index = 0; index < primaryKey.length; index++) {
2784
+ const column = primaryKey[index];
2785
+ filter[column] = requireKeyValue(table, column, values[index], "find");
2786
+ }
2787
+ return filter;
2788
+ }
2789
+ /**
2790
+ * Returns the object of column values that `findBy` filters a query of `table` by: a copy of `values`, read once.
2791
+ *
2792
+ * It rejects columns that are not exactly a key of `table`, primary or unique, as `isKey` tells, and `null` and `undefined`.
2793
+ */
2794
+ function toKeyValues(table, values) {
2795
+ if (typeof values !== "object" || values === null) throw new TypeError(`findBy takes an object of the columns of a key of table "${table.name}"`);
2796
+ const columns = Object.keys(values);
2797
+ if (!isKey(table, columns)) {
2798
+ const listed = columns.length === 0 ? "an object without columns" : `columns ${quoteColumns(columns)}`;
2799
+ throw new Error(`findBy takes the columns of one key of table "${table.name}", primary or unique, not ${listed}`);
2800
+ }
2801
+ const filter = {};
2802
+ for (const column of columns) {
2803
+ const value = values[column];
2804
+ filter[column] = requireKeyValue(table, column, value, "findBy");
2805
+ }
2806
+ return filter;
2807
+ }
1990
2808
  /**
1991
- * Lowers the fields of a query to projections, packing the rows of each nested query into the value of its field, or selecting the value of an aggregate query or `EXISTS` of a query of `exists`, and replacing the queries in the expressions of the other fields; checks them as `lowerQuery` does.
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.
1992
2810
  *
1993
- * Without queries, it returns `selected` itself.
2811
+ * It rejects columns that are not exactly a key of `table`, as `isKey` tells.
1994
2812
  */
1995
- function lowerProjections(selected, context) {
1996
- let lowered;
1997
- for (let index = 0; index < selected.length; index++) {
1998
- const field = selected[index];
1999
- let projection;
2000
- if (isNestedSelection(field)) {
2001
- checkDb(field.db, context, `Selected field "${field.key}"`);
2002
- projection = projectQuery(field.key, field.query, context);
2003
- } else {
2004
- const expression = lowerExpression(field.expression, context);
2005
- projection = expression === field.expression ? field : Object.freeze({
2006
- key: field.key,
2007
- expression,
2008
- type: field.type
2009
- });
2010
- }
2011
- if (lowered !== void 0) lowered.push(projection);
2012
- else if (projection !== field) {
2013
- lowered = selected.slice(0, index);
2014
- lowered.push(projection);
2015
- }
2016
- }
2017
- return lowered ?? selected;
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;
2018
2818
  }
2019
- /** Creates the projection of the value of `node`, a nested query, under `key`: `EXISTS` of a query of `exists`, the value of an aggregate query, or the packed rows of another query. */
2020
- function projectQuery(key, node, context) {
2021
- if (isExistsQuery(node)) return projectExists(key, node, context);
2022
- const query = lowerSelect(node, context, false);
2023
- return isAggregateQuery(node) ? projectAggregateValue(key, query) : projectRows(key, query);
2819
+ /** Columns of the target of a conflict on any key. */
2820
+ const noColumns = /* @__PURE__ */ Object.freeze([]);
2821
+ /** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
2822
+ function requireKeyValue(table, column, value, method) {
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`);
2824
+ return value;
2024
2825
  }
2025
- /** Creates the projection of the value of `query`, the statement of an aggregate query, under `key`, selected as a scalar subquery and decoded by the type of the aggregate. */
2026
- function projectAggregateValue(key, query) {
2027
- return Object.freeze({
2028
- key,
2029
- expression: createNode({
2030
- kind: "subquery",
2031
- query
2032
- }),
2033
- type: query.projections[0].type
2034
- });
2826
+ /** Lists `columns` in quotes, separated by commas. */
2827
+ function quoteColumns(columns) {
2828
+ return columns.map((column) => `"${column}"`).join(", ");
2035
2829
  }
2036
- /** Lowers the filters of a query, replacing the queries in their expressions; without queries, it returns `filters` itself. */
2037
- function lowerFilters(filters, context) {
2038
- const { relation, scope, user } = filters;
2039
- const loweredRelation = relation === void 0 ? void 0 : lowerExpression(relation, context);
2040
- const loweredScope = scope === void 0 ? void 0 : lowerExpression(scope, context);
2041
- const loweredUser = user === void 0 ? void 0 : lowerExpression(user, context);
2042
- if (loweredRelation === relation && loweredScope === scope && loweredUser === user) return filters;
2043
- const lowered = {};
2044
- if (loweredRelation !== void 0) lowered.relation = loweredRelation;
2045
- if (loweredScope !== void 0) lowered.scope = loweredScope;
2046
- if (loweredUser !== void 0) lowered.user = loweredUser;
2047
- return Object.freeze(lowered);
2830
+ // src/query/order.ts
2831
+ /** Direction and placement of `NULL` of a sort key node, by `OrderDirection`. */
2832
+ const directions = /* @__PURE__ */ Object.freeze({
2833
+ asc: {
2834
+ direction: "asc",
2835
+ nulls: void 0
2836
+ },
2837
+ desc: {
2838
+ direction: "desc",
2839
+ nulls: void 0
2840
+ },
2841
+ "asc nulls first": {
2842
+ direction: "asc",
2843
+ nulls: "first"
2844
+ },
2845
+ "asc nulls last": {
2846
+ direction: "asc",
2847
+ nulls: "last"
2848
+ },
2849
+ "desc nulls first": {
2850
+ direction: "desc",
2851
+ nulls: "first"
2852
+ },
2853
+ "desc nulls last": {
2854
+ direction: "desc",
2855
+ nulls: "last"
2856
+ }
2857
+ });
2858
+ const orderingNode = Symbol("kern-orm.ordering.node");
2859
+ /** Runtime class of an expression with a direction, which holds its sort key node. */
2860
+ var OrderingObject = class {
2861
+ [orderingNode];
2862
+ constructor(node) {
2863
+ this[orderingNode] = node;
2864
+ Object.freeze(this);
2865
+ }
2866
+ };
2867
+ /** Sorts by `value`, an expression or a query of one value, in ascending order, in the callback of `orderBy`. */
2868
+ function asc(value) {
2869
+ return orderBy(value, "asc");
2870
+ }
2871
+ /** Sorts by `value`, an expression or a query of one value, in descending order, in the callback of `orderBy`. */
2872
+ function desc(value) {
2873
+ return orderBy(value, "desc");
2874
+ }
2875
+ /** Sorts by `value`, an expression or a query of one value, in `direction`, such as `"desc nulls last"`, in the callback of `orderBy`. */
2876
+ function orderBy(value, direction) {
2877
+ if (!isOperandSource(value)) throw new TypeError("A sort key must be an expression or a query of one value");
2878
+ return new OrderingObject(createOrderNode(value[operandNode](), direction));
2048
2879
  }
2049
2880
  /**
2050
- * Lowers the sort keys of a query, replacing the queries in their expressions; without queries, it returns `orderBy` itself.
2881
+ * Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
2051
2882
  *
2052
- * A sort key of a field of `selected`, the fields of the query, takes the expression of its projection among `projections`, the lowered fields, so that the statement still finds the projection that has its value.
2053
- * A field of an aggregate or of `exists` is identified by the node of its reference, which holds the field.
2883
+ * `args` are column keys and objects of columns with directions, or a callback that receives the columns with the relations of `context` and the selected fields, and returns one sort key or an array of them.
2054
2884
  */
2055
- function lowerOrder(orderBy, context, selected, projections) {
2056
- let lowered;
2057
- for (let index = 0; index < orderBy.length; index++) {
2058
- const order = orderBy[index];
2059
- const { expression } = order;
2060
- const position = selected === void 0 || projections === void 0 || projections === selected ? -1 : selected.findIndex((field) => isFieldValue(field, expression));
2061
- const sorted = position === -1 ? lowerExpression(expression, context) : projections[position].expression;
2062
- if (lowered === void 0 && sorted === expression) continue;
2063
- lowered ??= orderBy.slice(0, index);
2064
- lowered.push(sorted === expression ? order : Object.freeze({
2065
- expression: sorted,
2066
- direction: order.direction,
2067
- nulls: order.nulls
2068
- }));
2885
+ function addOrder(node, context, args) {
2886
+ const order = node.orderBy.slice();
2887
+ const first = args[0];
2888
+ if (typeof first === "function") {
2889
+ addTerms(order, first(createSelectionRefs(node.from, context), createSelectedRefs(node, "sorted by")), "The callback of orderBy must return");
2890
+ return order;
2069
2891
  }
2070
- return lowered ?? orderBy;
2892
+ for (const arg of args) if (typeof arg === "string") order.push(createOrderNode(createColumnNode(node.from, arg), "asc"));
2893
+ else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
2894
+ const columns = arg;
2895
+ for (const key of Object.keys(columns)) order.push(createOrderNode(createColumnNode(node.from, key), columns[key]));
2896
+ } else throw new TypeError("orderBy takes column keys and objects of columns with directions, or a callback");
2897
+ return order;
2071
2898
  }
2072
- /** Tells whether the sort expression `expression` is the value of `field`: its expression, or for a field of an aggregate or of `exists`, the node of its reference in the callback of `orderBy`. */
2073
- function isFieldValue(field, expression) {
2074
- return isNestedSelection(field) ? expression.kind === "nestedQuery" && expression.field === field : field.expression === expression;
2899
+ /** Adds to `order` the sort key nodes of `terms`, `OrderTerms` checked at runtime; `subject` starts the error of another value, such as `The callback of orderBy must return`. */
2900
+ function addTerms(order, terms, subject) {
2901
+ if (Array.isArray(terms)) for (const term of terms) order.push(readTerm$1(term, subject));
2902
+ else order.push(readTerm$1(terms, subject));
2075
2903
  }
2076
2904
  /**
2077
- * Lowers an expression, replacing each query in it and checking the query as a nested query.
2078
- * A query of `exists`, such as in a condition, becomes `EXISTS` of its rows, another query of one value a scalar subquery of its value, the query of the values of `IN` its statement, and another query in SQL text its statement in parentheses.
2905
+ * Creates the sort key node of a sort key, `OrderTerm` checked at runtime, whose error starts with `subject`.
2079
2906
  *
2080
- * It returns `node` itself when it holds no query, and creates only the nodes that hold one.
2907
+ * A query is converted by the method of `operandNode`, which rejects a query other than of one value.
2081
2908
  */
2082
- function lowerExpression(node, context) {
2083
- switch (node.kind) {
2084
- case "column":
2085
- case "param":
2086
- case "boolean":
2087
- case "values":
2088
- case "tableName":
2089
- case "subquery":
2090
- case "exists":
2091
- case "textSubquery": return node;
2092
- case "nestedQuery": return lowerNestedQuery(node, context);
2093
- case "cast": {
2094
- const operand = lowerExpression(node.operand, context);
2095
- return operand === node.operand ? node : createNode({
2096
- kind: "cast",
2097
- operand,
2098
- type: node.type
2099
- });
2100
- }
2101
- case "not": {
2102
- const operand = lowerExpression(node.operand, context);
2103
- return operand === node.operand ? node : createNode({
2104
- kind: "not",
2105
- operand
2106
- });
2107
- }
2108
- case "isNull": {
2109
- const operand = lowerExpression(node.operand, context);
2110
- return operand === node.operand ? node : createNode({
2111
- kind: "isNull",
2112
- operand
2113
- });
2114
- }
2115
- case "and":
2116
- case "or": {
2117
- const operands = lowerEach(node.operands, context, lowerExpression);
2118
- return operands === node.operands ? node : createNode({
2119
- kind: node.kind,
2120
- operands
2121
- });
2122
- }
2123
- case "comparison": {
2124
- const left = lowerExpression(node.left, context);
2125
- const right = lowerExpression(node.right, context);
2126
- return left === node.left && right === node.right ? node : createNode({
2127
- kind: "comparison",
2128
- operator: node.operator,
2129
- left,
2130
- right
2131
- });
2132
- }
2133
- case "in": {
2134
- const operand = lowerExpression(node.operand, context);
2135
- const list = lowerEach(node.list, context, lowerExpression);
2136
- return operand === node.operand && list === node.list ? node : createNode({
2137
- kind: "in",
2138
- operand,
2139
- list
2140
- });
2141
- }
2142
- case "inQuery": {
2143
- const operand = lowerExpression(node.operand, context);
2144
- const query = node.query.kind === "nestedQuery" ? lowerQueryOfValues(node.query, context) : node.query;
2145
- return operand === node.operand && query === node.query ? node : createNode({
2146
- kind: "inQuery",
2147
- operand,
2148
- query
2149
- });
2150
- }
2151
- case "raw": {
2152
- const values = lowerEach(node.values, context, lowerExpression);
2153
- return values === node.values ? node : createNode({
2154
- kind: "raw",
2155
- fragments: node.fragments,
2156
- values
2157
- });
2158
- }
2159
- case "aggregate": {
2160
- const args = lowerEach(node.arguments, context, lowerExpression);
2161
- const orderBy = lowerOrder(node.orderBy, context);
2162
- return args === node.arguments && orderBy === node.orderBy ? node : createNode({
2163
- kind: "aggregate",
2164
- name: node.name,
2165
- distinct: node.distinct,
2166
- arguments: args,
2167
- orderBy
2909
+ function readTerm$1(term, subject) {
2910
+ if (term instanceof OrderingObject) return term[orderingNode];
2911
+ if (isOperandSource(term)) return createOrderNode(term[operandNode](), "asc");
2912
+ throw new TypeError(`${subject} expressions, queries of one value, or sort keys of asc, desc, and orderBy`);
2913
+ }
2914
+ /** Creates the sort key node of `expression` in `direction`, an `OrderDirection` checked at runtime. */
2915
+ function createOrderNode(expression, direction) {
2916
+ const placement = typeof direction === "string" && Object.hasOwn(directions, direction) ? directions[direction] : void 0;
2917
+ if (placement === void 0) throw new TypeError(`Unknown sort direction ${JSON.stringify(direction)}`);
2918
+ return Object.freeze({
2919
+ expression,
2920
+ direction: placement.direction,
2921
+ nulls: placement.nulls
2922
+ });
2923
+ }
2924
+ /** Creates a reference to the column `key` of the table of `source`. */
2925
+ function createColumnNode(source, key) {
2926
+ requireColumn(source.table, key);
2927
+ return createNode({
2928
+ kind: "column",
2929
+ source,
2930
+ key
2931
+ });
2932
+ }
2933
+ /**
2934
+ * Creates the references of the callback of `orderBy`, `groupBy`, `distinctOn`, or `having` to the fields selected by `node`; `purpose` completes the error of a field that cannot be read, such as `sorted by`.
2935
+ *
2936
+ * Each is an expression over the node of its projection, so that a sort key or a key of a group of it is the projection itself; `HAVING` compiles the expression again, since it cannot refer to a projection.
2937
+ * A field of an aggregate or of `exists` is an expression of its query that holds the field, by which lowering finds the projection of the field.
2938
+ */
2939
+ function createSelectedRefs(node, purpose) {
2940
+ const { projections } = node;
2941
+ if (node.value) return toSelectedExpression(projections[0]);
2942
+ const refs = {};
2943
+ for (const projection of projections) {
2944
+ const { key } = projection;
2945
+ if (isLiteralSelection(projection)) Object.defineProperty(refs, key, {
2946
+ get() {
2947
+ throw new Error(`Field "${key}" holds a literal and cannot be ${purpose}`);
2948
+ },
2949
+ enumerable: true
2950
+ });
2951
+ else if (isNestedSelection(projection)) {
2952
+ if (isSelectedAsValue(projection.query)) refs[key] = toQueryExpression(projection);
2953
+ else Object.defineProperty(refs, key, {
2954
+ get() {
2955
+ throw new Error(`Field "${key}" holds nested rows and cannot be ${purpose}`);
2956
+ },
2957
+ enumerable: true
2168
2958
  });
2169
- }
2170
- default: return node;
2959
+ } else refs[key] = toSelectedExpression(projection);
2171
2960
  }
2961
+ return refs;
2962
+ }
2963
+ /** Creates an expression over the node of a projection of a query, which has a type: only lowering adds projections of nested queries. */
2964
+ function toSelectedExpression(projection) {
2965
+ return toExpression(projection.expression, projection.type);
2966
+ }
2967
+ /** Creates an expression of the value of `field`, a selected aggregate query or query of `exists`, with the type of its value. */
2968
+ function toQueryExpression(field) {
2969
+ const { query } = field;
2970
+ return toExpression(createNode({
2971
+ kind: "nestedQuery",
2972
+ query,
2973
+ db: field.db,
2974
+ field
2975
+ }), query.projections[0].type);
2172
2976
  }
2977
+ // src/query/group.ts
2978
+ /** Method that takes the keys of groups, and the phrase of what it does with a field that cannot be a key, for its errors. */
2979
+ const keyMethods = {
2980
+ groupBy: "grouped by",
2981
+ distinctOn: "a key of distinctOn"
2982
+ };
2173
2983
  /**
2174
- * Lowers a query in an expression to `EXISTS` of the rows of a query of `exists`, to a scalar subquery of the value of another query of one value, or to its statement in parentheses for a query of records or of rows `many`, which only SQL text holds.
2984
+ * Creates the keys of `method`, `groupBy` or `distinctOn`, of `node` with the keys of the arguments of the call after its own.
2175
2985
  *
2176
- * In SQL text, a query of `exists` stays `EXISTS` rather than a subquery of its value, so that PostgreSQL can join its rows in a condition, as a semi-join.
2986
+ * `args` are column keys, or a callback that receives the columns with the relations of `context` and the selected fields, and returns one key or an array of them.
2987
+ * A string names a column, never a selected field, whose key names only the field of a record.
2988
+ * It rejects a call without keys, which would leave the rows of a query ungrouped, or keep all of them, when a list built in code is empty.
2177
2989
  */
2178
- function lowerNestedQuery(node, context) {
2179
- checkDb(node.db, context, "A query in an expression");
2180
- const { query } = node;
2181
- if (isExistsQuery(query)) return lowerExists(query, context);
2182
- return createNode({
2183
- kind: query.value && query.rows !== "many" ? "subquery" : "textSubquery",
2184
- query: lowerSelect(query, context, false)
2185
- });
2186
- }
2187
- /** Lowers the query of the values of `IN` to its statement, whatever its rows. */
2188
- function lowerQueryOfValues(node, context) {
2189
- checkDb(node.db, context, "A query in an expression");
2190
- return lowerSelect(node.query, context, false);
2191
- }
2192
- /** Lowers each of `items` with `lower`; when none changes, it returns `items` itself. */
2193
- function lowerEach(items, context, lower) {
2194
- let lowered;
2195
- for (let index = 0; index < items.length; index++) {
2196
- const item = items[index];
2197
- const result = lower(item, context);
2198
- if (lowered !== void 0) lowered.push(result);
2199
- else if (result !== item) {
2200
- lowered = items.slice(0, index);
2201
- lowered.push(result);
2202
- }
2990
+ function addGroupKeys(method, node, context, args) {
2991
+ const existing = node[method];
2992
+ const keys = existing.slice();
2993
+ const first = args[0];
2994
+ if (typeof first === "function") {
2995
+ const terms = first(createSelectionRefs(node.from, context), createSelectedRefs(node, keyMethods[method]));
2996
+ if (Array.isArray(terms)) for (const term of terms) keys.push(readTerm(term, method));
2997
+ else keys.push(readTerm(terms, method));
2998
+ } else for (const arg of args) {
2999
+ if (typeof arg !== "string") throw new TypeError(`${method} takes column keys or a callback`);
3000
+ keys.push(createColumnNode(node.from, arg));
2203
3001
  }
2204
- return lowered ?? items;
3002
+ if (keys.length === existing.length) throw new TypeError(`${method} requires at least one key`);
3003
+ return keys;
2205
3004
  }
2206
- // src/query/kind.ts
2207
- /** `then` of every bound query, which executes the query on its `Db`. */
2208
- function executeOnAwait(onFulfilled, onRejected) {
2209
- return this.db.$execute(this).then(onFulfilled, onRejected);
3005
+ /**
3006
+ * Creates the condition of the groups of `node` with `filter`, what `where` takes, combined with `AND` with its earlier condition.
3007
+ *
3008
+ * A callback also receives the fields selected so far, as `HavingFields` tells, so that a condition reads an aggregate or a key of an expression by its field.
3009
+ * The condition stands apart from the filters of the rows, so it requires keys: without them, `having` would read as a filter of rows while PostgreSQL takes the whole query for one group.
3010
+ */
3011
+ function addHaving(node, filter, context) {
3012
+ if (node.groupBy.length === 0) throw new Error("having requires an earlier groupBy; filter the rows with where");
3013
+ const predicate = toFilterNode(filter, node.from, context, () => node.conversion !== void 0 || isAggregateQuery(node) || isExistsQuery(node) ? void 0 : createSelectedRefs(node, "read in having"));
3014
+ return node.having === void 0 ? predicate : combineNodes("and", [node.having, predicate]);
2210
3015
  }
2211
- /** Compiles `query` as its `toSQL` does: an unbound query with `options`, and a bound query as its `Db` executes it, which takes no options. */
2212
- function compileToSQL(query, options) {
2213
- const { db } = query;
2214
- if (db === void 0) {
2215
- const { text, parameters } = compileStatement(lowerStatement(query.node), options);
2216
- return createStatement(text, parameters);
2217
- }
2218
- if (options !== void 0) throw new Error("A query bound to a Db compiles with the options of its Db");
2219
- return db.$compile(query);
3016
+ /**
3017
+ * Creates the node of a key returned by the callback of `method`.
3018
+ *
3019
+ * A query is converted by the method of `operandNode`, which rejects a query other than of one value.
3020
+ */
3021
+ function readTerm(term, method) {
3022
+ if (isOperandSource(term)) return term[operandNode]();
3023
+ throw new TypeError(`The callback of ${method} must return expressions or queries of one value`);
3024
+ }
3025
+ // src/query/lock.ts
3026
+ /** Creates the row lock of `strength` for `method`, rejecting a query whose rows it cannot lock and options that contradict each other. */
3027
+ function createRowLock(node, method, strength, options = {}) {
3028
+ if (isAggregateQuery(node)) throw new Error(`${method} cannot be used after an aggregate`);
3029
+ if (isExistsQuery(node)) throw new Error(`${method} cannot be used after exists`);
3030
+ if (node.distinct) throw new Error(`${method} cannot be used after distinct, since a row without duplicates comes from several rows of the table`);
3031
+ if (node.distinctOn.length > 0) throw new Error(`${method} cannot be used after distinctOn; PostgreSQL does not allow row locks with DISTINCT ON`);
3032
+ if (node.groupBy.length > 0) throw new Error(`${method} cannot be used after groupBy, since a group comes from several rows of the table`);
3033
+ const { noWait, skipLocked } = options;
3034
+ if (noWait && skipLocked) throw new Error(`${method} takes either noWait or skipLocked, not both`);
3035
+ return Object.freeze({
3036
+ strength,
3037
+ onLocked: noWait ? "NOWAIT" : skipLocked ? "SKIP LOCKED" : void 0
3038
+ });
2220
3039
  }
2221
3040
  // src/query/record.ts
2222
3041
  /**
@@ -2422,7 +3241,7 @@ function selectValue(key, value) {
2422
3241
  key,
2423
3242
  literal: value.literal
2424
3243
  });
2425
- if (value instanceof SelectQuery) return Object.freeze({
3244
+ if (value instanceof SelectQuery || value instanceof TransformedQuery) return Object.freeze({
2426
3245
  key,
2427
3246
  query: value.node,
2428
3247
  db: value.db
@@ -2501,14 +3320,14 @@ function toInsertRows(table, records, method) {
2501
3320
  };
2502
3321
  }
2503
3322
  /**
2504
- * 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.
2505
3324
  *
2506
3325
  * A value of `undefined` keeps its column, and an update must set at least one column.
2507
3326
  * It rejects a key that is not a column of the table of `source`, and a query other than of one value.
2508
3327
  */
2509
- function toAssignments(source, set, context) {
3328
+ function toAssignments(source, set, context, excluded) {
2510
3329
  const { table } = source;
2511
- 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));
2512
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`);
2513
3332
  const assignments = [];
2514
3333
  for (const key of Object.keys(values)) {
@@ -2529,7 +3348,7 @@ function resolveValue(table, key, value) {
2529
3348
  /**
2530
3349
  * Immutable query of state `S` that inserts, updates, or deletes rows, unbound or bound to a `Db`.
2531
3350
  *
2532
- * Without a selection, it resolves to the number of the rows it changes; `select`, `get`, and `pluck` return the fields of those rows instead.
3351
+ * Without a selection, it resolves to the number of the rows it changes; `select`, `get`, and `pluck` return the fields of those rows instead, and `map` and `transform` convert them in code.
2533
3352
  * An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
2534
3353
  * A bound query executes on its `Db` when awaited.
2535
3354
  */
@@ -2550,6 +3369,7 @@ var MutationQuery = class MutationQuery {
2550
3369
  Object.freeze(this);
2551
3370
  }
2552
3371
  select(...args) {
3372
+ this.rejectConverted("select");
2553
3373
  const { node } = this;
2554
3374
  const { returning } = node;
2555
3375
  if (returning !== void 0 && node.value) throw new Error("select cannot be used after get or pluck; call get or pluck to replace the value");
@@ -2565,6 +3385,25 @@ var MutationQuery = class MutationQuery {
2565
3385
  pluck(arg) {
2566
3386
  return this.deriveValue(arg, "pluck");
2567
3387
  }
3388
+ map(callback) {
3389
+ const { node } = this;
3390
+ if (node.returning === void 0) throw new Error("map applies only to a mutation that returns the fields of its rows; select them before map, or call transform to convert the number of the rows");
3391
+ return this.derive({ conversion: addMap(node.conversion, callback) });
3392
+ }
3393
+ transform(callback) {
3394
+ return this.derive({ conversion: addTransform(this.node.conversion, callback) });
3395
+ }
3396
+ use(callback, ...args) {
3397
+ const result = requireDerived(this, callback(this, ...args), "The callback of use");
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");
3399
+ return result;
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
+ }
2568
3407
  toSQL(options) {
2569
3408
  return compileToSQL(this, options);
2570
3409
  }
@@ -2581,6 +3420,7 @@ var MutationQuery = class MutationQuery {
2581
3420
  }
2582
3421
  /** Creates the mutation that returns the value of `arg` of `method`, a column key or a callback that receives the columns and the relations and returns an expression or a query of one value, of each changed row. */
2583
3422
  deriveValue(arg, method) {
3423
+ this.rejectConverted(method);
2584
3424
  const { node } = this;
2585
3425
  return this.derive({
2586
3426
  returning: [projectValue(node.target, arg, method, this.context)],
@@ -2588,15 +3428,155 @@ var MutationQuery = class MutationQuery {
2588
3428
  rows: toValueRows(node.rows)
2589
3429
  });
2590
3430
  }
3431
+ /** Rejects `method`, which changes the fields that the mutation returns, on a mutation that `map` or `transform` converts. */
3432
+ rejectConverted(method) {
3433
+ if (this.node.conversion !== void 0) throw new Error(`${method} cannot be used after map or transform; call it before them`);
3434
+ }
3435
+ };
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
+ }
2591
3522
  };
2592
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
+ /**
2593
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`.
2594
3542
  *
2595
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.
2596
3545
  */
2597
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) {
2598
3579
  const { node } = query;
2599
- 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`);
2600
3580
  const { columns, values } = toInsertRows(node.from.table, records, method);
2601
3581
  return new MutationQuery(query.table, Object.freeze({
2602
3582
  kind: "insert",
@@ -2636,160 +3616,20 @@ function createDelete(query) {
2636
3616
  }), query.context, query.db);
2637
3617
  }
2638
3618
  /**
2639
- * 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 an order, a limit, or an offset, 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.
2640
3620
  *
2641
3621
  * A filter simplified to `TRUE`, such as `where({})`, restricts no row.
2642
3622
  */
2643
3623
  function checkChangedRows(node, method) {
2644
3624
  if (node.relation !== void 0) throw new Error(`${method} applies only to a query of a table, not of a relation`);
2645
3625
  if (node.projections.length > 0) throw new Error(`${method} returns the fields selected after it, not before; call select, get, or pluck after ${method}`);
2646
- if (node.orderBy.length > 0 || node.limit !== void 0 || node.offset !== void 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, or orderBy`);
3626
+ if (node.conversion !== void 0) throw new Error(`${method} returns what map converts after it, not before; call map after ${method}`);
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`);
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`);
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;
2647
3631
  const predicate = combineFilters(node.filters);
2648
- 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`);
2649
- }
2650
- // src/query/order.ts
2651
- /** Direction and placement of `NULL` of a sort key node, by `OrderDirection`. */
2652
- const directions = /* @__PURE__ */ Object.freeze({
2653
- asc: {
2654
- direction: "asc",
2655
- nulls: void 0
2656
- },
2657
- desc: {
2658
- direction: "desc",
2659
- nulls: void 0
2660
- },
2661
- "asc nulls first": {
2662
- direction: "asc",
2663
- nulls: "first"
2664
- },
2665
- "asc nulls last": {
2666
- direction: "asc",
2667
- nulls: "last"
2668
- },
2669
- "desc nulls first": {
2670
- direction: "desc",
2671
- nulls: "first"
2672
- },
2673
- "desc nulls last": {
2674
- direction: "desc",
2675
- nulls: "last"
2676
- }
2677
- });
2678
- const orderingNode = Symbol("kern-orm.ordering.node");
2679
- /** Runtime class of an expression with a direction, which holds its sort key node. */
2680
- var OrderingObject = class {
2681
- [orderingNode];
2682
- constructor(node) {
2683
- this[orderingNode] = node;
2684
- Object.freeze(this);
2685
- }
2686
- };
2687
- /** Sorts by `value`, an expression or a query of one value, in ascending order, in the callback of `orderBy`. */
2688
- function asc(value) {
2689
- return orderBy(value, "asc");
2690
- }
2691
- /** Sorts by `value`, an expression or a query of one value, in descending order, in the callback of `orderBy`. */
2692
- function desc(value) {
2693
- return orderBy(value, "desc");
2694
- }
2695
- /** Sorts by `value`, an expression or a query of one value, in `direction`, such as `"desc nulls last"`, in the callback of `orderBy`. */
2696
- function orderBy(value, direction) {
2697
- if (!isOperandSource(value)) throw new TypeError("A sort key must be an expression or a query of one value");
2698
- return new OrderingObject(createOrderNode(value[operandNode](), direction));
2699
- }
2700
- /**
2701
- * Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
2702
- *
2703
- * `args` are column keys and objects of columns with directions, or a callback that receives the columns with the relations of `context` and the selected fields, and returns one sort key or an array of them.
2704
- */
2705
- function addOrder(node, context, args) {
2706
- const order = node.orderBy.slice();
2707
- const first = args[0];
2708
- if (typeof first === "function") {
2709
- const terms = first(createSelectionRefs(node.from, context), createSelectedRefs(node));
2710
- if (Array.isArray(terms)) for (const term of terms) order.push(readTerm(term));
2711
- else order.push(readTerm(terms));
2712
- return order;
2713
- }
2714
- for (const arg of args) if (typeof arg === "string") order.push(createOrderNode(createColumnNode(node.from, arg), "asc"));
2715
- else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
2716
- const columns = arg;
2717
- for (const key of Object.keys(columns)) order.push(createOrderNode(createColumnNode(node.from, key), columns[key]));
2718
- } else throw new TypeError("orderBy takes column keys and objects of columns with directions, or a callback");
2719
- return order;
2720
- }
2721
- /**
2722
- * Creates the sort key node of a sort key returned by the callback of `orderBy`.
2723
- *
2724
- * A query is converted by the method of `operandNode`, which rejects a query other than of one value.
2725
- */
2726
- function readTerm(term) {
2727
- if (term instanceof OrderingObject) return term[orderingNode];
2728
- if (isOperandSource(term)) return createOrderNode(term[operandNode](), "asc");
2729
- throw new TypeError("The callback of orderBy must return expressions, queries of one value, or sort keys of asc, desc, and orderBy");
2730
- }
2731
- /** Creates the sort key node of `expression` in `direction`, an `OrderDirection` checked at runtime. */
2732
- function createOrderNode(expression, direction) {
2733
- const placement = typeof direction === "string" && Object.hasOwn(directions, direction) ? directions[direction] : void 0;
2734
- if (placement === void 0) throw new TypeError(`Unknown sort direction ${JSON.stringify(direction)}`);
2735
- return Object.freeze({
2736
- expression,
2737
- direction: placement.direction,
2738
- nulls: placement.nulls
2739
- });
2740
- }
2741
- /** Creates a reference to the column `key` of the table of `source`. */
2742
- function createColumnNode(source, key) {
2743
- requireColumn(source.table, key);
2744
- return createNode({
2745
- kind: "column",
2746
- source,
2747
- key
2748
- });
2749
- }
2750
- /**
2751
- * Creates the references of the callback of `orderBy` to the fields selected by `node`.
2752
- *
2753
- * Each is an expression over the node of its projection, so that a sort key of it is the projection itself.
2754
- * A field of an aggregate or of `exists` is an expression of its query that holds the field, by which lowering finds the projection of the field.
2755
- */
2756
- function createSelectedRefs(node) {
2757
- const { projections } = node;
2758
- if (node.value) return toSelectedExpression(projections[0]);
2759
- const refs = {};
2760
- for (const projection of projections) {
2761
- const { key } = projection;
2762
- if (isLiteralSelection(projection)) Object.defineProperty(refs, key, {
2763
- get() {
2764
- throw new Error(`Field "${key}" holds a literal and cannot be sorted by`);
2765
- },
2766
- enumerable: true
2767
- });
2768
- else if (isNestedSelection(projection)) {
2769
- if (isSelectedAsValue(projection.query)) refs[key] = toQueryExpression(projection);
2770
- else Object.defineProperty(refs, key, {
2771
- get() {
2772
- throw new Error(`Field "${key}" holds nested rows and cannot be sorted by`);
2773
- },
2774
- enumerable: true
2775
- });
2776
- } else refs[key] = toSelectedExpression(projection);
2777
- }
2778
- return refs;
2779
- }
2780
- /** Creates an expression over the node of a projection of a query, which has a type: only lowering adds projections of nested queries. */
2781
- function toSelectedExpression(projection) {
2782
- return toExpression(projection.expression, projection.type);
2783
- }
2784
- /** Creates an expression of the value of `field`, a selected aggregate query or query of `exists`, with the type of its value. */
2785
- function toQueryExpression(field) {
2786
- const { query } = field;
2787
- return toExpression(createNode({
2788
- kind: "nestedQuery",
2789
- query,
2790
- db: field.db,
2791
- field
2792
- }), query.projections[0].type);
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`);
2793
3633
  }
2794
3634
  // src/query/pagination.ts
2795
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. */
@@ -2804,7 +3644,7 @@ function createPageCount(node, method, count) {
2804
3644
  }
2805
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. */
2806
3646
  function createPaginationHint(node) {
2807
- if (node.relation?.kind === "one") return "";
3647
+ if (node.relation === void 0 ? node.from.table.singleton : node.relation.kind === "one") return "";
2808
3648
  if (isAggregateQuery(node)) return "; call it before count or another aggregate to aggregate the rows of a page";
2809
3649
  if (isExistsQuery(node)) return "; call it before exists to check the rows of a page";
2810
3650
  return "; call it before take or get";
@@ -2871,7 +3711,7 @@ var SelectQuery = class SelectQuery {
2871
3711
  }
2872
3712
  count(arg) {
2873
3713
  if (arg !== void 0) return this.aggregateBy("count", arg);
2874
- this.rejectAggregate("count");
3714
+ this.rejectAggregation("count");
2875
3715
  return this.deriveAggregate(countProjection);
2876
3716
  }
2877
3717
  countDistinct(arg) {
@@ -2899,10 +3739,11 @@ var SelectQuery = class SelectQuery {
2899
3739
  return this.aggregateBy("stringAgg", arg, delimiter);
2900
3740
  }
2901
3741
  exists() {
2902
- this.rejectAggregate("exists");
3742
+ this.rejectAggregation("exists");
2903
3743
  return this.deriveAggregate(getExistsProjection());
2904
3744
  }
2905
3745
  coalesce(fallback) {
3746
+ this.rejectMapped("coalesce");
2906
3747
  const { node } = this;
2907
3748
  if (!node.value) throw new Error("coalesce applies only to a query of values, such as one made by get, pluck, or an aggregate");
2908
3749
  return this.derive({ projections: [coalesceValue(node.projections[0], fallback)] });
@@ -2911,6 +3752,18 @@ var SelectQuery = class SelectQuery {
2911
3752
  const value = this[operandNode]();
2912
3753
  return toExpression(value, this.node.projections[0].type);
2913
3754
  }
3755
+ groupBy(...args) {
3756
+ this.rejectAggregate("groupBy");
3757
+ if (this.node.lock !== void 0) throw new Error("groupBy cannot be used after a row lock, since a group comes from several rows of the table");
3758
+ return this.derive({ groupBy: addGroupKeys("groupBy", this.node, this.context, args) });
3759
+ }
3760
+ having(filter) {
3761
+ return this.derive({
3762
+ having: addHaving(this.node, filter, this.context),
3763
+ rows: this.getFilteredRows(),
3764
+ guaranteed: false
3765
+ });
3766
+ }
2914
3767
  orderBy(...args) {
2915
3768
  return this.derive({ orderBy: addOrder(this.node, this.context, args) });
2916
3769
  }
@@ -2920,11 +3773,36 @@ var SelectQuery = class SelectQuery {
2920
3773
  offset(count) {
2921
3774
  return this.derive({ offset: createPageCount(this.node, "offset", count) });
2922
3775
  }
3776
+ distinct() {
3777
+ if (this.node.rows !== "many") throw new Error("distinct applies only to a query of many rows");
3778
+ if (this.node.distinctOn.length > 0) throw new Error("distinct cannot be used after distinctOn, which compares only its keys");
3779
+ if (this.node.lock !== void 0) throw new Error("distinct cannot be used after a row lock, since a row without duplicates comes from several rows of the table");
3780
+ return this.derive({ distinct: true });
3781
+ }
3782
+ distinctOn(...args) {
3783
+ const { node } = this;
3784
+ if (node.rows !== "many") throw new Error("distinctOn applies only to a query of many rows");
3785
+ if (node.distinct) throw new Error("distinctOn cannot be used after distinct, which compares every selected field");
3786
+ if (node.lock !== void 0) throw new Error("distinctOn cannot be used after a row lock; PostgreSQL does not allow row locks with DISTINCT ON");
3787
+ return this.derive({ distinctOn: addGroupKeys("distinctOn", node, this.context, args) });
3788
+ }
3789
+ forUpdate(options) {
3790
+ return this.derive({ lock: createRowLock(this.node, "forUpdate", "UPDATE", options) });
3791
+ }
3792
+ forNoKeyUpdate(options) {
3793
+ return this.derive({ lock: createRowLock(this.node, "forNoKeyUpdate", "NO KEY UPDATE", options) });
3794
+ }
3795
+ forShare(options) {
3796
+ return this.derive({ lock: createRowLock(this.node, "forShare", "SHARE", options) });
3797
+ }
3798
+ forKeyShare(options) {
3799
+ return this.derive({ lock: createRowLock(this.node, "forKeyShare", "KEY SHARE", options) });
3800
+ }
2923
3801
  optional() {
2924
3802
  return this.changeOneRow("optional", "optional");
2925
3803
  }
2926
3804
  nullable() {
2927
- if (this.node.value) throw new Error("nullable applies only to a query of records; use optional after get, pluck, or an aggregate");
3805
+ if (this.node.value && this.node.conversion === void 0) throw new Error("nullable applies only to a query of records; use optional after get, pluck, or an aggregate");
2928
3806
  return this.changeOneRow("nullable", "nullable");
2929
3807
  }
2930
3808
  required() {
@@ -2937,6 +3815,9 @@ var SelectQuery = class SelectQuery {
2937
3815
  if (!Array.isArray(records)) throw new TypeError("insertMany takes an array of records");
2938
3816
  return createInsert(this, records, "many", "insertMany");
2939
3817
  }
3818
+ upsert(values) {
3819
+ return createUpsert(this, values);
3820
+ }
2940
3821
  update(set) {
2941
3822
  return createUpdate(this, set);
2942
3823
  }
@@ -2947,6 +3828,12 @@ var SelectQuery = class SelectQuery {
2947
3828
  if (this.node.rows !== "many") throw new Error("all applies only to a query of many rows");
2948
3829
  return this.derive({ all: true });
2949
3830
  }
3831
+ map(callback) {
3832
+ return this.derive({ conversion: addMap(this.node.conversion, callback) });
3833
+ }
3834
+ transform(callback) {
3835
+ return transformQuery(this.node, this.db, callback);
3836
+ }
2950
3837
  use(callback, ...args) {
2951
3838
  return requireDerived(this, callback(this, ...args), "The callback of use");
2952
3839
  }
@@ -2961,6 +3848,7 @@ var SelectQuery = class SelectQuery {
2961
3848
  [operandNode]() {
2962
3849
  const { node } = this;
2963
3850
  if (!node.value || node.rows === "many") throw new TypeError("An expression takes a query of one value, such as of get, an aggregate, or exists, not a query of records or of an array");
3851
+ if (node.conversion !== void 0) throw new TypeError("An expression takes a query of one value, such as of get, an aggregate, or exists, not a query of map");
2964
3852
  return createNode({
2965
3853
  kind: "nestedQuery",
2966
3854
  query: node,
@@ -2975,16 +3863,18 @@ var SelectQuery = class SelectQuery {
2975
3863
  [valuesQueryNode]() {
2976
3864
  const { node } = this;
2977
3865
  if (!node.value) throw new TypeError("in takes a query of values, such as of pluck, not a query of records");
3866
+ if (node.conversion !== void 0) throw new TypeError("in takes a query of values, such as of pluck, not a query of map");
2978
3867
  return createNode({
2979
3868
  kind: "nestedQuery",
2980
3869
  query: node,
2981
3870
  db: this.db
2982
3871
  });
2983
3872
  }
2984
- /** 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. */
2985
3874
  derive(changes) {
2986
3875
  return new SelectQuery(this.table, Object.freeze({
2987
3876
  ...this.node,
3877
+ foundKey: void 0,
2988
3878
  ...changes
2989
3879
  }), this.context, this.db);
2990
3880
  }
@@ -3000,18 +3890,25 @@ var SelectQuery = class SelectQuery {
3000
3890
  }
3001
3891
  return this.derive(changes);
3002
3892
  }
3003
- /** Creates the query of the value of `projection`, of an aggregate or of `exists` over the rows of this query, which always returns one row. */
3893
+ /**
3894
+ * Creates the query of the value of `projection`, of an aggregate or of `exists` over the rows of this query, which always returns one row.
3895
+ *
3896
+ * A query of `distinct` or `distinctOn` keeps the fields that `projection` replaces: `distinct` compares them, and the keys, the order, and the argument of an aggregate read a selected field through its projection.
3897
+ */
3004
3898
  deriveAggregate(projection) {
3005
- return this.derive({
3899
+ const { node } = this;
3900
+ const changes = {
3006
3901
  projections: [projection],
3007
3902
  rows: "one",
3008
3903
  value: true,
3009
3904
  guaranteed: false
3010
- });
3905
+ };
3906
+ if (node.distinct || node.distinctOn.length > 0) changes.distinctFields = node.projections;
3907
+ return this.derive(changes);
3011
3908
  }
3012
3909
  /** Creates the query of the aggregate of `method` over `arg`, a column key or a callback that receives the columns and the relations and returns an expression or a query of one value; `delimiter` is the delimiter of `stringAgg`. */
3013
3910
  aggregateBy(method, arg, delimiter) {
3014
- this.rejectAggregate(method);
3911
+ this.rejectAggregation(method);
3015
3912
  return this.deriveAggregate(projectAggregate(method, projectValue(this.node.from, arg, method, this.context), delimiter));
3016
3913
  }
3017
3914
  /** Returns the changes of the node that add `filter` with `AND`, ending the guarantee of the row as `FilteredState` does in types. */
@@ -3019,14 +3916,24 @@ var SelectQuery = class SelectQuery {
3019
3916
  const { node } = this;
3020
3917
  return {
3021
3918
  filters: addUserFilter(node.filters, filter, node.from, this.context),
3022
- rows: toFilteredRows(node.rows, node.value, node.guaranteed),
3919
+ rows: this.getFilteredRows(),
3023
3920
  guaranteed: false
3024
3921
  };
3025
3922
  }
3026
- /** Creates the query of `find` and `findBy`: `where` of `values`, an object of column values, returning one row like `take`. */
3923
+ /** Returns the rows mode of this query after a filter, as `FilteredStateRows` gives it in types. */
3924
+ getFilteredRows() {
3925
+ const { node } = this;
3926
+ return toFilteredRows(node.rows, node.value && node.conversion === void 0, node.guaranteed);
3927
+ }
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
+ */
3027
3933
  takeWhere(values) {
3028
3934
  const changes = this.addFilter(values);
3029
3935
  if (changes.rows === "many") changes.rows = "one";
3936
+ if (isTableQuery(this.node)) changes.foundKey = Object.freeze(values);
3030
3937
  return this.derive(changes);
3031
3938
  }
3032
3939
  /** Creates the query of one row with rows mode `rows` that `method` returns, rejecting a query of many rows. */
@@ -3037,33 +3944,43 @@ var SelectQuery = class SelectQuery {
3037
3944
  guaranteed: false
3038
3945
  });
3039
3946
  }
3947
+ /** Rejects `method`, which reads or changes the fields of the query, on a query that `map` converts. */
3948
+ rejectMapped(method) {
3949
+ if (this.node.conversion !== void 0) throw new Error(`${method} cannot be used after map; call it before map`);
3950
+ }
3040
3951
  /** Rejects a change of the selection by `method` of a query made by `get`, `pluck`, or an aggregate method. */
3041
3952
  rejectValue(method) {
3953
+ this.rejectMapped(method);
3042
3954
  if (!this.node.value) return;
3043
3955
  this.rejectAggregate(method);
3044
3956
  throw new Error(`${method} cannot be used after get or pluck; call get or pluck to replace the value`);
3045
3957
  }
3046
3958
  /** Rejects `method`, which replaces the value or the selection, on a query made by an aggregate method or `exists`. */
3047
3959
  rejectAggregate(method) {
3960
+ this.rejectMapped(method);
3048
3961
  const { node } = this;
3049
3962
  if (isAggregateQuery(node)) throw new Error(`${method} cannot be used after an aggregate`);
3050
3963
  if (isExistsQuery(node)) throw new Error(`${method} cannot be used after exists`);
3051
3964
  }
3965
+ /** Rejects `method`, an aggregate method or `exists`, on a query made by one of them or with a row lock. */
3966
+ rejectAggregation(method) {
3967
+ this.rejectAggregate(method);
3968
+ if (this.node.lock !== void 0) throw new Error(`${method} cannot be used after a row lock, since it returns no rows of the table to lock`);
3969
+ }
3052
3970
  };
3053
3971
  /**
3054
- * Returns `result`, which a callback named by `subject` returned for `query`, after checking that it is `query` or a query derived from it.
3972
+ * Returns `result`, which a callback named by `subject` returned for `query`, a query of a table or a mutation, after checking that it is `query` or a query of the same class derived from it.
3055
3973
  *
3056
3974
  * An ancestor of `query` also passes, since only the source occurrence is compared.
3057
3975
  */
3058
3976
  function requireDerived(query, result, subject) {
3059
- if (!(result instanceof SelectQuery) || result.node.from !== query.node.from) throw new Error(`${subject} must return the query it receives or one derived from it`);
3977
+ if (!(query instanceof SelectQuery ? result instanceof SelectQuery && result.node.from === query.node.from : result instanceof MutationQuery && result.node.target === query.node.target)) throw new Error(`${subject} must return the query it receives or one derived from it`);
3060
3978
  return result;
3061
3979
  }
3062
3980
  /** Binds a query to `db`, so that awaiting it executes it there. */
3063
3981
  function bindQuery(query, db) {
3064
3982
  return new SelectQuery(query.table, query.node, query.context, db);
3065
3983
  }
3066
- /** Starts a query over a new occurrence of the table. */
3067
3984
  function from(table) {
3068
3985
  return startQuery(table, noContext);
3069
3986
  }
@@ -3247,6 +4164,7 @@ function toRawSqlNode(strings, values) {
3247
4164
  function toInterpolatedNode(value) {
3248
4165
  if (value instanceof SelectQuery) {
3249
4166
  const { node } = value;
4167
+ if (node.conversion !== void 0) throw new TypeError("sql takes a query of a table, such as db.form or a query of its records, not a query of map or transform");
3250
4168
  return isTableQuery(node) ? createNode({
3251
4169
  kind: "tableName",
3252
4170
  table: value.table,
@@ -3257,7 +4175,7 @@ function toInterpolatedNode(value) {
3257
4175
  db: value.db
3258
4176
  });
3259
4177
  }
3260
- if (value instanceof RecordQuery || value instanceof MutationQuery || value instanceof SqlQuery) throw new TypeError("sql takes a query of a table, such as db.form or a query of its records, not a query of $select, a mutation, or a query of SQL text");
4178
+ if (value instanceof RecordQuery || value instanceof MutationQuery || value instanceof SqlQuery || value instanceof TransformedQuery) throw new TypeError("sql takes a query of a table, such as db.form or a query of its records, not a query of $select, a mutation, a query of SQL text, or a query of map or transform");
3261
4179
  return toValueNode(value);
3262
4180
  }
3263
4181
  // src/execution/db.ts
@@ -3284,6 +4202,7 @@ function bindSchema(schema, database, options = {}) {
3284
4202
  },
3285
4203
  async $execute(query) {
3286
4204
  const compiled = compile(query);
4205
+ if (compiled.locksRows && !database.isInTransaction()) throw new Error("Cannot lock the rows of a query outside a transaction, where the locks would end with its statement; a test transaction alone does not count");
3287
4206
  return decodeStatement(compiled, await database.execute(compiled));
3288
4207
  },
3289
4208
  $select: (selection) => startRecordQuery(selection, db),
@@ -3303,124 +4222,316 @@ function bindSchema(schema, database, options = {}) {
3303
4222
  const db = defineEntries(methods, schema.$tables, (key) => bindQuery(startQuery(schema.$tables[key], contexts[key]), db));
3304
4223
  return db;
3305
4224
  }
3306
- // src/execution/transaction.ts
4225
+ // src/execution/transaction/abort.ts
3307
4226
  /**
3308
- * Routes statements and scripts to the ambient transaction of the current async context or to the pool.
4227
+ * Runs `start` and settles as its promise, or rejects with the reason of `signal` when it aborts first and `abort`, called on the abort, returns `true`.
3309
4228
  *
3310
- * This is the only place that decides which connection executes a statement.
4229
+ * It listens from before `start`, which may abort the signal before it returns, and stops once the promise of `start` settles, such as for the signal that `node:test` aborts after every test.
4230
+ * A later rejection of that promise stays handled, and a value it resolves with after the rejection goes to `discard`, which handles its own failures.
3311
4231
  */
3312
- var TransactionCoordinator = class {
3313
- driver;
3314
- test;
3315
- #storage = new AsyncLocalStorage();
3316
- /** Open test fixtures, the innermost last. */
3317
- #fixtures = [];
3318
- /** Changes of the fixture stack, run one after another so that each one sees the stack the previous one left. */
3319
- #transitions = Promise.resolve();
3320
- /**
3321
- * The `close` functions of the test fixtures that are still opening, from the call that starts the opening until the fixture enters the stack or the opening ends otherwise.
3322
- *
3323
- * An opening holds the queue of the fixture stack while its seed runs, so a closing of the stack queued behind it would wait for a seed that may never settle.
3324
- * Closing the stack therefore calls these first: each one tells its opening to stop waiting for the seed.
3325
- */
3326
- #openings = /* @__PURE__ */ new Set();
3327
- /**
3328
- * Outermost fixture in the stack whose closing is queued but has not finished.
3329
- *
3330
- * Openings that start after that closing is requested nest outside of the fixture once it closes, so a repeated closing of it, or a closing of a fixture nested in it, must not close them.
3331
- */
3332
- #closing;
3333
- /** Closing of the database, set by the first `close`; from then on, operations outside a transaction reject. */
3334
- #closed;
3335
- constructor(driver, test = false) {
3336
- this.driver = driver;
3337
- this.test = test;
4232
+ function settleUnlessAborted(start, signal, abort, discard) {
4233
+ const settled = Promise.withResolvers();
4234
+ let abandoned = false;
4235
+ const onAbort = () => {
4236
+ if (abort()) {
4237
+ abandoned = true;
4238
+ settled.reject(signal.reason);
4239
+ }
4240
+ };
4241
+ signal.addEventListener("abort", onAbort, { once: true });
4242
+ let running;
4243
+ try {
4244
+ running = start();
4245
+ } catch (error) {
4246
+ signal.removeEventListener("abort", onAbort);
4247
+ settled.reject(error);
4248
+ return settled.promise;
3338
4249
  }
3339
- execute(statement) {
3340
- const target = this.#route("execute a query");
3341
- return target instanceof Error ? Promise.reject(target) : target.execute(statement);
4250
+ running.then((value) => {
4251
+ signal.removeEventListener("abort", onAbort);
4252
+ if (abandoned) discard?.(value);
4253
+ else settled.resolve(value);
4254
+ }, (error) => {
4255
+ signal.removeEventListener("abort", onAbort);
4256
+ settled.reject(error);
4257
+ });
4258
+ return settled.promise;
4259
+ }
4260
+ /**
4261
+ * Acquires a connection for a test transaction, rejecting with the reason of `signal` when it aborts first.
4262
+ *
4263
+ * A connection acquired after the abort returns to the pool as best-effort cleanup: the test transaction has already rejected, so nothing awaits the release, and its failure is ignored.
4264
+ */
4265
+ function acquireUnlessAborted(driver, signal) {
4266
+ if (signal.aborted) return Promise.reject(signal.reason);
4267
+ return settleUnlessAborted(() => driver.acquire(), signal, () => true, (connection) => {
4268
+ Promise.resolve().then(() => connection.release()).catch(() => {});
4269
+ });
4270
+ }
4271
+ // src/execution/transaction/settings.ts
4272
+ /** Settings of a level of `transaction` with `options`, or `undefined` when it has none. */
4273
+ function getLevelSettings(options) {
4274
+ return options?.searchPath ? [{
4275
+ name: "search_path",
4276
+ value: options.searchPath.map(quoteIdentifier).join(", ")
4277
+ }] : void 0;
4278
+ }
4279
+ /**
4280
+ * Applies `settings` to a level whose `BEGIN` or `SAVEPOINT` completed, unless its cleanup has started.
4281
+ *
4282
+ * A nested level first reads the values they replace, which it restores before its `RELEASE`: PostgreSQL keeps a setting of a released savepoint until the transaction ends, while `ROLLBACK TO` restores it itself.
4283
+ */
4284
+ async function applySettings(level, settings) {
4285
+ if (level.cleanup) return;
4286
+ if (level.parent) {
4287
+ let text = "SELECT";
4288
+ const names = [];
4289
+ for (const { name } of settings) {
4290
+ names.push(name);
4291
+ text += `${names.length > 1 ? "," : ""} current_setting($${names.length})`;
4292
+ }
4293
+ const values = (await level.connection.execute(createStatement(text, names))).rows[0];
4294
+ level.restore = createSettingStatement(settings.map(({ name }, index) => ({
4295
+ name,
4296
+ value: values[index]
4297
+ })));
4298
+ if (level.cleanup) return;
3342
4299
  }
3343
- /** Executes a script of statements without parameters where `execute` executes a statement. */
3344
- executeScript(text) {
3345
- const target = this.#route("execute a script");
3346
- return target instanceof Error ? Promise.reject(target) : target.executeScript(text);
4300
+ await level.connection.execute(createSettingStatement(settings));
4301
+ }
4302
+ /** Creates the statement that sets `settings` until the transaction ends or rolls back to an earlier savepoint, as `SET LOCAL` does. */
4303
+ function createSettingStatement(settings) {
4304
+ let text = "SELECT";
4305
+ const parameters = [];
4306
+ for (const { name, value } of settings) {
4307
+ parameters.push(name, value);
4308
+ text += `${parameters.length > 2 ? "," : ""} set_config($${parameters.length - 1}, $${parameters.length}, true)`;
3347
4309
  }
3348
- /** Returns the connection of the ambient transaction or the driver of the pool that runs `operation`, or the error that rejects it. */
3349
- #route(operation) {
3350
- const level = this.#storage.getStore();
3351
- if (!level) {
3352
- if (this.#closed) return createDatabaseClosedError(operation);
3353
- return this.test ? new Error(describeOutsideTest(operation)) : this.driver;
4310
+ return createStatement(text, parameters);
4311
+ }
4312
+ // src/execution/transaction/level.ts
4313
+ /** Statement that starts the transaction of a level without a parent: a transaction of `transaction`, a test transaction, or the first test fixture. */
4314
+ const beginStatement = /* @__PURE__ */ createStatement("BEGIN");
4315
+ const commitStatement = /* @__PURE__ */ createStatement("COMMIT");
4316
+ const rollbackStatement = /* @__PURE__ */ createStatement("ROLLBACK");
4317
+ /**
4318
+ * Creates the state of a `starting` level on `connection`, nested in `parent` if given.
4319
+ *
4320
+ * It sends no SQL and does not reserve the level as the `child` of `parent`; the caller does both.
4321
+ * `transaction` tells whether the level is a transaction of `transaction` rather than a level of the test helpers.
4322
+ */
4323
+ function createLevel(connection, parent, transaction) {
4324
+ return {
4325
+ connection,
4326
+ parent,
4327
+ depth: parent ? parent.depth + 1 : 0,
4328
+ inTransaction: transaction || (parent?.inTransaction ?? false),
4329
+ state: "starting",
4330
+ child: void 0,
4331
+ rollbackOnly: void 0,
4332
+ cleanup: void 0,
4333
+ endedBy: void 0,
4334
+ restore: void 0
4335
+ };
4336
+ }
4337
+ /**
4338
+ * Reserves a nested level of `parent`, sends its `SAVEPOINT`, and applies its `settings`, rejecting when one of them fails.
4339
+ *
4340
+ * `transaction` tells whether the level is a transaction of `transaction` rather than a level of the test helpers.
4341
+ * A level whose settings fail rolls back to its savepoint first.
4342
+ * The failure suppresses the reason of `signal` when the signal aborted before it.
4343
+ */
4344
+ async function startNestedLevel(parent, transaction, signal, settings) {
4345
+ const level = createLevel(parent.connection, parent, transaction);
4346
+ parent.child = level;
4347
+ try {
4348
+ await level.connection.execute(createStatement(`SAVEPOINT ${getSavepointName(level)}`));
4349
+ } catch (error) {
4350
+ if (!level.cleanup) {
4351
+ closeStarting(level);
4352
+ throw suppressAbortReason(error, signal);
3354
4353
  }
3355
- return createUnavailableError(level, operation) ?? level.connection;
4354
+ throw suppressAbortReason(combineRejections({ error }, await level.cleanup).error, signal);
3356
4355
  }
3357
- /**
3358
- * Runs `callback` in a transaction that commits when it resolves and rolls back when it rejects, with the settings of `options`.
3359
- *
3360
- * Inside a transaction, it runs `callback` in a savepoint nested in it, which restores the settings it changed when it ends.
3361
- * A failed cleanup after an error rejects with a `SuppressedError` that keeps the original error in its `suppressed` chain.
3362
- */
3363
- transaction(callback, options) {
3364
- const settings = getLevelSettings(options);
3365
- const parent = this.#storage.getStore();
3366
- if (parent) return this.#runNested(parent, callback, true, void 0, settings);
3367
- if (this.#closed) return Promise.reject(createDatabaseClosedError("start a transaction"));
3368
- return this.test ? Promise.reject(new Error(describeOutsideTest("start a transaction"))) : this.#runRoot(callback, true, void 0, settings);
4356
+ if (settings) try {
4357
+ await applySettings(level, settings);
4358
+ } catch (error) {
4359
+ throw suppressAbortReason(combineRejections({ error }, await endLevel(level, false)).error, signal);
3369
4360
  }
3370
- /**
3371
- * Runs `callback` in the current transaction, or in a new transaction outside of one.
3372
- *
3373
- * Joining starts no savepoint, so a rejection of `callback` does not undo its changes.
3374
- * It rejects without calling `callback` when the current level accepts no statements, as a nested transaction would.
3375
- */
3376
- async ensureTransaction(callback) {
3377
- const level = this.#storage.getStore();
3378
- if (!level?.inTransaction) return this.transaction(callback);
3379
- const unavailable = createUnavailableError(level, "join the transaction");
3380
- if (unavailable) throw unavailable;
3381
- return callback();
4361
+ return level;
4362
+ }
4363
+ /**
4364
+ * Runs `callback` in the context of a started `level` in `storage`, then ends the level.
4365
+ *
4366
+ * A level that `commits` commits only when the callback resolves with no nested level running and no nested level that failed to end.
4367
+ * When `signal` aborts, the level ends at once without waiting for the callback, and its outcome is a rejection with the reason of the signal.
4368
+ */
4369
+ async function runLevel(storage, level, callback, commits, signal) {
4370
+ if (level.cleanup) return getEndedOutcome(level.cleanup, createClosedError(level, void 0));
4371
+ let result;
4372
+ let rejection;
4373
+ if (signal?.aborted) endLevel(level, false, "abort");
4374
+ else {
4375
+ level.state = "open";
4376
+ try {
4377
+ result = await (signal ? settleUnlessAborted(() => storage.run(level, callback), signal, () => {
4378
+ endLevel(level, false, "abort");
4379
+ return true;
4380
+ }) : storage.run(level, callback));
4381
+ } catch (error) {
4382
+ rejection = { error };
4383
+ }
3382
4384
  }
3383
- /**
3384
- * Tells whether the current async context runs in a transaction of `transaction`.
3385
- *
3386
- * A test transaction and the seed of a test fixture are not transactions, unless they run inside one, so that code under test behaves as in production.
3387
- */
3388
- isInTransaction() {
3389
- return this.#storage.getStore()?.inTransaction ?? false;
4385
+ if (level.cleanup) return getEndedOutcome(level.cleanup, level.endedBy === "abort" ? signal?.reason : createClosedError(level, rejection));
4386
+ if (!rejection && level.child) rejection = { error: /* @__PURE__ */ new Error("Transaction was rolled back: a nested transaction was still running when its callback finished") };
4387
+ if (!rejection && level.rollbackOnly) rejection = { error: new Error("Transaction was rolled back: a nested transaction failed to end", { cause: level.rollbackOnly.error }) };
4388
+ const cleanup = await endLevel(level, commits && !rejection);
4389
+ return {
4390
+ result,
4391
+ rejection: combineRejections(rejection, cleanup),
4392
+ failure: cleanup.failure
4393
+ };
4394
+ }
4395
+ /**
4396
+ * Ends `level` once, whoever ends it: later calls return the cleanup already started.
4397
+ *
4398
+ * Statements of the level and of all levels nested in it fail from the start of the cleanup, and the nested levels end before it.
4399
+ */
4400
+ function endLevel(level, commit, endedBy) {
4401
+ if (level.cleanup) return level.cleanup;
4402
+ level.endedBy = endedBy;
4403
+ level.state = "closing";
4404
+ level.cleanup = cleanUpLevel(level, commit, level.child && endLevel(level.child, false, "enclosing level"));
4405
+ return level.cleanup;
4406
+ }
4407
+ async function cleanUpLevel(level, commit, nested) {
4408
+ const nestedCleanup = await nested;
4409
+ const { parent } = level;
4410
+ const cleanup = parent ? await endSavepoint(level, commit && !level.rollbackOnly) : await endTransaction(level.connection, commit);
4411
+ level.state = "closed";
4412
+ if (parent) {
4413
+ if (parent.child === level) parent.child = void 0;
4414
+ parent.rollbackOnly ??= cleanup.rejection;
4415
+ }
4416
+ return {
4417
+ rejection: combineRejections(nestedCleanup?.rejection, cleanup),
4418
+ failure: cleanup.failure
4419
+ };
4420
+ }
4421
+ /** Ends a transaction with `COMMIT` or `ROLLBACK`. */
4422
+ async function endTransaction(connection, commit) {
4423
+ try {
4424
+ const { command } = await connection.execute(commit ? commitStatement : rollbackStatement);
4425
+ if (commit && command !== "COMMIT") return { rejection: { error: /* @__PURE__ */ new Error("Transaction was rolled back: a statement in it failed") } };
4426
+ return {};
4427
+ } catch (error) {
4428
+ return {
4429
+ rejection: { error },
4430
+ failure: { error }
4431
+ };
4432
+ }
4433
+ }
4434
+ /**
4435
+ * Ends the savepoint of a nested level, releasing it on commit and rolling back to it otherwise.
4436
+ *
4437
+ * Before a release, it restores the settings that the level changed; a rollback restores them itself.
4438
+ */
4439
+ async function endSavepoint(level, commit) {
4440
+ const name = getSavepointName(level);
4441
+ try {
4442
+ if (!commit) await level.connection.execute(createStatement(`ROLLBACK TO SAVEPOINT ${name}`));
4443
+ else if (level.restore) await level.connection.execute(level.restore);
4444
+ await level.connection.execute(createStatement(`RELEASE SAVEPOINT ${name}`));
4445
+ return {};
4446
+ } catch (error) {
4447
+ return { rejection: { error } };
4448
+ }
4449
+ }
4450
+ /** Name of the savepoint of a nested level; a level has at most one nested level, so the depth makes it unique. */
4451
+ function getSavepointName(level) {
4452
+ return `kern_${level.depth}`;
4453
+ }
4454
+ /** Closes a nested level whose `SAVEPOINT` failed, freeing its parent for other statements. */
4455
+ function closeStarting(level) {
4456
+ level.state = "closed";
4457
+ if (level.parent?.child === level) level.parent.child = void 0;
4458
+ }
4459
+ /**
4460
+ * Creates the error of `operation` on a level that rejects statements and nested levels, or returns `undefined` when it accepts them.
4461
+ *
4462
+ * A level that can only roll back keeps the failure of its nested level as the cause.
4463
+ */
4464
+ function createUnavailableError(level, operation) {
4465
+ if (level.state !== "open") return /* @__PURE__ */ new Error(`Cannot ${operation}: its transaction is already finished`);
4466
+ if (level.child) return /* @__PURE__ */ new Error(`Cannot ${operation}: a nested transaction is running; await it first`);
4467
+ if (level.rollbackOnly) return new Error(`Cannot ${operation}: a nested transaction failed to end, so the transaction can only roll back`, { cause: level.rollbackOnly.error });
4468
+ }
4469
+ /** Error of a level that another level ended, keeping the rejection of its callback as the cause. */
4470
+ function createClosedError(level, rejection) {
4471
+ return new Error(level.endedBy === "preemption" ? "Transaction was rolled back: its test fixture started another level before it finished" : "Transaction was rolled back: its enclosing transaction ended first", rejection && { cause: rejection.error });
4472
+ }
4473
+ /** Outcome of a level that something other than its callback ended: a rejection with `error`, which a failed cleanup suppresses. */
4474
+ async function getEndedOutcome(cleanup, error) {
4475
+ const ended = await cleanup;
4476
+ return {
4477
+ rejection: combineRejections({ error }, ended),
4478
+ failure: ended.failure
4479
+ };
4480
+ }
4481
+ /** Returns the connection of a transaction to the driver, keeping a failed release as a rejection that suppresses `rejection`. */
4482
+ async function releaseConnection(connection, failure, rejection) {
4483
+ try {
4484
+ await connection.release(failure);
4485
+ return rejection;
4486
+ } catch (error) {
4487
+ return suppress(error, rejection, "Releasing the transaction connection failed");
3390
4488
  }
4489
+ }
4490
+ /** Error of a failure to start a test transaction, which suppresses the reason of `signal` when the signal aborted before the failure. */
4491
+ function suppressAbortReason(error, signal) {
4492
+ return signal?.aborted ? suppress(error, { error: signal.reason }, "Starting the test transaction failed").error : error;
4493
+ }
4494
+ /** Rejection with the error of a cleanup, which suppresses the `earlier` rejection if there is one. */
4495
+ function combineRejections(earlier, cleanup) {
4496
+ return cleanup.rejection ? suppress(cleanup.rejection.error, earlier, "Transaction rollback failed") : earlier;
4497
+ }
4498
+ /** Rejection with `error`, which suppresses the `earlier` rejection if there is one. */
4499
+ function suppress(error, earlier, message) {
4500
+ return { error: earlier ? new SuppressedError(error, earlier.error, message) : error };
4501
+ }
4502
+ // src/execution/transaction/fixture-stack.ts
4503
+ /** Open test fixtures of a transaction coordinator and the queue of the changes of their stack. */
4504
+ var TestFixtureStack = class {
4505
+ #driver;
4506
+ /** Async context of the levels of the coordinator, which tells whether a call runs inside a level. */
4507
+ #storage;
4508
+ /** Open test fixtures, the innermost last. */
4509
+ #fixtures = [];
4510
+ /** Changes of the fixture stack, run one after another so that each one sees the stack the previous one left. */
4511
+ #transitions = Promise.resolve();
3391
4512
  /**
3392
- * Runs `callback` in a transaction that always rolls back, on a coordinator created for tests.
4513
+ * The `close` functions of the test fixtures that are still opening, from the call that starts the opening until the fixture enters the stack or the opening ends otherwise.
3393
4514
  *
3394
- * Inside a transaction, it runs `callback` in a savepoint nested in it that always rolls back.
3395
- * When `signal` aborts, the level stops accepting statements at once and ends without waiting for `callback`.
3396
- * Once the level ends, the transaction rejects with the reason of the signal.
3397
- * A failure to start or end the level after the abort suppresses the reason in a `SuppressedError`.
3398
- * Aborted while it waits for its turn in the fixture stack or for a connection, it rejects at once; after the abort, it never calls `callback`.
4515
+ * An opening holds the queue of the fixture stack while its seed runs, so a closing of the stack queued behind it would wait for a seed that may never settle.
4516
+ * Closing the stack therefore calls these first: each one tells its opening to stop waiting for the seed.
3399
4517
  */
3400
- testTransaction(callback, { signal } = {}) {
3401
- if (!this.test) return Promise.reject(/* @__PURE__ */ new Error("testTransaction requires a database created with test: true"));
3402
- if (signal?.aborted) return Promise.reject(signal.reason);
3403
- const parent = this.#storage.getStore();
3404
- if (parent) return this.#runNested(parent, callback, false, signal);
3405
- if (this.#closed) return Promise.reject(createDatabaseClosedError("start a test transaction"));
3406
- return this.#startTestLevel(signal).then((level) => level ? this.#runStarted(level, callback, false, signal) : this.#runRoot(callback, false, signal));
3407
- }
4518
+ #openings = /* @__PURE__ */ new Set();
3408
4519
  /**
3409
- * Opens a test fixture: a transaction on a reserved connection, or a savepoint in the innermost open fixture, and runs `seed` in it.
4520
+ * Outermost fixture in the stack whose closing is queued but has not finished.
3410
4521
  *
3411
- * Test transactions started outside any transaction run in the innermost fixture.
3412
- * A fixture whose `seed` rejects rolls back and closes.
4522
+ * Openings that start after that closing is requested nest outside of the fixture once it closes, so a repeated closing of it, or a closing of a fixture nested in it, must not close them.
3413
4523
  */
3414
- createTestFixture(seed) {
3415
- return this.startTestFixture(seed).fixture;
4524
+ #closing;
4525
+ constructor(driver, storage) {
4526
+ this.#driver = driver;
4527
+ this.#storage = storage;
3416
4528
  }
3417
4529
  /**
3418
- * Starts opening a test fixture, see `createTestFixture`, and returns it with a `close` that can be called before it opens.
4530
+ * Starts opening a test fixture, see `startTestFixture` of the coordinator, or, given `refusal`, returns a fixture that rejects with it.
3419
4531
  *
3420
- * Closing before the fixture opens rejects the opening without waiting for `seed` and rolls the fixture back, so a fixture or test transaction started after `close` never nests in it, even when its `seed` never settles.
3421
- * Closing an open fixture also closes the fixtures opening in it.
4532
+ * The `close` of a refused fixture resolves once the earlier changes of the stack end.
3422
4533
  */
3423
- startTestFixture(seed) {
4534
+ start(seed, refusal) {
3424
4535
  let level;
3425
4536
  let closeRequested = false;
3426
4537
  const closing = Promise.withResolvers();
@@ -3447,16 +4558,8 @@ var TransactionCoordinator = class {
3447
4558
  if (rejection) throw rejection.error;
3448
4559
  });
3449
4560
  };
3450
- if (!this.test) return {
3451
- fixture: Promise.reject(/* @__PURE__ */ new Error("createTestFixture requires a database created with test: true")),
3452
- close
3453
- };
3454
- if (this.#storage.getStore()) return {
3455
- fixture: Promise.reject(/* @__PURE__ */ new Error("Cannot create a test fixture inside a transaction")),
3456
- close
3457
- };
3458
- if (this.#closed) return {
3459
- fixture: Promise.reject(createDatabaseClosedError("create a test fixture")),
4561
+ if (refusal) return {
4562
+ fixture: Promise.reject(refusal),
3460
4563
  close
3461
4564
  };
3462
4565
  /**
@@ -3506,34 +4609,31 @@ var TransactionCoordinator = class {
3506
4609
  };
3507
4610
  }
3508
4611
  /**
3509
- * Closes the test fixtures, including those still opening, and then the driver, which waits for the running transactions to end.
4612
+ * Starts the level of a test transaction in the innermost test fixture as a change of the fixture stack, resolving to `undefined` when no fixture is open.
3510
4613
  *
3511
- * A driver waits for every reserved connection, so closing it under an open fixture would wait forever.
3512
- * A failed closing of the driver suppresses the failures of closing the fixtures.
3513
- * From the first call, statements, transactions, and fixtures started outside a transaction reject, while the levels already running can still end; a repeated call returns the same closing.
4614
+ * Aborted while it waits for its turn, it rejects at once and its change starts nothing, so that a test that has already ended never preempts the active level of the fixture.
4615
+ * From its turn on, it settles with its change.
3514
4616
  */
3515
- close() {
3516
- if (this.#storage.getStore()) return Promise.reject(/* @__PURE__ */ new Error("Cannot close the database inside a transaction"));
3517
- this.#closed ??= this.#close();
3518
- return this.#closed;
4617
+ startTestLevel(signal) {
4618
+ let waiting = true;
4619
+ const start = () => this.#enqueue(() => {
4620
+ if (signal?.aborted) return;
4621
+ waiting = false;
4622
+ const fixture = this.#fixtures.at(-1);
4623
+ return fixture && this.#startInFixture(fixture, signal);
4624
+ });
4625
+ return signal ? settleUnlessAborted(start, signal, () => waiting) : start();
3519
4626
  }
3520
- /** Closes the test fixtures and then the driver, see `close`. */
3521
- async #close() {
3522
- let rejection;
3523
- try {
3524
- await this.#closeAfterOpenings(() => this.#enqueue(() => {
3525
- const root = this.#fixtures[0];
3526
- return root && this.#closeFixture(root);
3527
- }));
3528
- } catch (error) {
3529
- rejection = { error };
3530
- }
3531
- try {
3532
- await this.driver.close();
3533
- } catch (error) {
3534
- rejection = suppress(error, rejection, "Closing the driver failed");
3535
- }
3536
- if (rejection) throw rejection.error;
4627
+ /**
4628
+ * Closes every fixture, including those still opening, rejecting with the failures of all of them.
4629
+ *
4630
+ * Unlike closing a fixture, it closes every opening, including one started after a closing of the stack was requested, since nothing may stay open.
4631
+ */
4632
+ close() {
4633
+ return this.#closeAfterOpenings(() => this.#enqueue(() => {
4634
+ const root = this.#fixtures[0];
4635
+ return root && this.#closeFixture(root);
4636
+ }));
3537
4637
  }
3538
4638
  /**
3539
4639
  * Closes the fixtures still opening and runs `closeStack`, rejecting with the failures of all of them.
@@ -3555,7 +4655,7 @@ var TransactionCoordinator = class {
3555
4655
  */
3556
4656
  async #seedFixture(fixture, seed) {
3557
4657
  try {
3558
- return await this.#run(await this.#startNested(fixture, false), seed, true);
4658
+ return await runLevel(this.#storage, await startNestedLevel(fixture, false), seed, true);
3559
4659
  } catch (error) {
3560
4660
  return { rejection: { error } };
3561
4661
  }
@@ -3574,56 +4674,179 @@ var TransactionCoordinator = class {
3574
4674
  }
3575
4675
  /** Rolls back a fixture and releases the connection of the first one, returning the error to reject with. */
3576
4676
  async #endFixture(fixture) {
3577
- const cleanup = await this.#end(fixture, false);
4677
+ const cleanup = await endLevel(fixture, false);
3578
4678
  return fixture.parent ? cleanup.rejection : releaseConnection(fixture.connection, cleanup.failure, cleanup.rejection);
3579
4679
  }
3580
- /**
3581
- * Starts the level of a test transaction in the innermost test fixture as a change of the fixture stack, resolving to `undefined` when no fixture is open.
3582
- *
3583
- * Aborted while it waits for its turn, it rejects at once and its change starts nothing, so that a test that has already ended never preempts the active level of the fixture.
3584
- * From its turn on, it settles with its change.
3585
- */
3586
- #startTestLevel(signal) {
3587
- let waiting = true;
3588
- const start = () => this.#enqueue(() => {
3589
- if (signal?.aborted) return;
3590
- waiting = false;
3591
- const fixture = this.#fixtures.at(-1);
3592
- return fixture && this.#startInFixture(fixture, signal);
3593
- });
3594
- return signal ? settleUnlessAborted(start, signal, () => waiting) : start();
3595
- }
3596
4680
  /** Runs a change of the fixture stack after the earlier ones. */
3597
4681
  #enqueue(transition) {
3598
4682
  const result = this.#transitions.then(transition);
3599
4683
  this.#transitions = result.catch(() => {});
3600
4684
  return result;
3601
4685
  }
3602
- /** Starts the first test fixture: a transaction on a reserved connection. */
3603
- async #beginFixture() {
3604
- const connection = await this.driver.acquire();
3605
- try {
3606
- await connection.execute(beginStatement);
3607
- } catch (error) {
3608
- throw (await releaseConnection(connection, { error }, { error })).error;
3609
- }
3610
- return createLevel(connection, void 0, false);
4686
+ /** Starts the first test fixture: a transaction on a reserved connection. */
4687
+ async #beginFixture() {
4688
+ const connection = await this.#driver.acquire();
4689
+ try {
4690
+ await connection.execute(beginStatement);
4691
+ } catch (error) {
4692
+ throw (await releaseConnection(connection, { error }, { error })).error;
4693
+ }
4694
+ return createLevel(connection, void 0, false);
4695
+ }
4696
+ /**
4697
+ * Starts a nested level of a test fixture, preempting its active one.
4698
+ *
4699
+ * A fixture runs one nested level at a time, so a new one ends the previous one, such as a test that timed out.
4700
+ * A level whose `signal` aborts while it preempts rejects with its reason instead of starting, and a failure to start after the abort suppresses the reason.
4701
+ */
4702
+ async #startInFixture(fixture, signal) {
4703
+ if (fixture.child) {
4704
+ const cleanup = await endLevel(fixture.child, false, "preemption");
4705
+ if (cleanup.rejection) throw suppressAbortReason(new Error("Cannot start a level of the test fixture: ending its active level failed", { cause: cleanup.rejection.error }), signal);
4706
+ }
4707
+ const unavailable = createUnavailableError(fixture, "start a level of the test fixture");
4708
+ if (unavailable) throw suppressAbortReason(unavailable, signal);
4709
+ signal?.throwIfAborted();
4710
+ return startNestedLevel(fixture, false, signal);
4711
+ }
4712
+ };
4713
+ // src/execution/transaction/coordinator.ts
4714
+ /**
4715
+ * Routes statements and scripts to the ambient transaction of the current async context or to the pool.
4716
+ *
4717
+ * This is the only place that decides which connection executes a statement.
4718
+ */
4719
+ var TransactionCoordinator = class {
4720
+ driver;
4721
+ test;
4722
+ #storage = new AsyncLocalStorage();
4723
+ #fixtures;
4724
+ /** Closing of the database, set by the first `close`; from then on, operations outside a transaction reject. */
4725
+ #closed;
4726
+ constructor(driver, test = false) {
4727
+ this.driver = driver;
4728
+ this.test = test;
4729
+ this.#fixtures = new TestFixtureStack(driver, this.#storage);
4730
+ }
4731
+ execute(statement) {
4732
+ const target = this.#route("execute a query");
4733
+ return target instanceof Error ? Promise.reject(target) : target.execute(statement);
4734
+ }
4735
+ /** Executes a script of statements without parameters where `execute` executes a statement. */
4736
+ executeScript(text) {
4737
+ const target = this.#route("execute a script");
4738
+ return target instanceof Error ? Promise.reject(target) : target.executeScript(text);
4739
+ }
4740
+ /** Returns the connection of the ambient transaction or the driver of the pool that runs `operation`, or the error that rejects it. */
4741
+ #route(operation) {
4742
+ const level = this.#storage.getStore();
4743
+ if (!level) {
4744
+ if (this.#closed) return createDatabaseClosedError(operation);
4745
+ return this.test ? new Error(describeOutsideTest(operation)) : this.driver;
4746
+ }
4747
+ return createUnavailableError(level, operation) ?? level.connection;
4748
+ }
4749
+ /**
4750
+ * Runs `callback` in a transaction that commits when it resolves and rolls back when it rejects, with the settings of `options`.
4751
+ *
4752
+ * Inside a transaction, it runs `callback` in a savepoint nested in it, which restores the settings it changed when it ends.
4753
+ * A failed cleanup after an error rejects with a `SuppressedError` that keeps the original error in its `suppressed` chain.
4754
+ */
4755
+ transaction(callback, options) {
4756
+ const settings = getLevelSettings(options);
4757
+ const parent = this.#storage.getStore();
4758
+ if (parent) return this.#runNested(parent, callback, true, void 0, settings);
4759
+ if (this.#closed) return Promise.reject(createDatabaseClosedError("start a transaction"));
4760
+ return this.test ? Promise.reject(new Error(describeOutsideTest("start a transaction"))) : this.#runRoot(callback, true, void 0, settings);
4761
+ }
4762
+ /**
4763
+ * Runs `callback` in the current transaction, or in a new transaction outside of one.
4764
+ *
4765
+ * Joining starts no savepoint, so a rejection of `callback` does not undo its changes.
4766
+ * It rejects without calling `callback` when the current level accepts no statements, as a nested transaction would.
4767
+ */
4768
+ async ensureTransaction(callback) {
4769
+ const level = this.#storage.getStore();
4770
+ if (!level?.inTransaction) return this.transaction(callback);
4771
+ const unavailable = createUnavailableError(level, "join the transaction");
4772
+ if (unavailable) throw unavailable;
4773
+ return callback();
4774
+ }
4775
+ /**
4776
+ * Tells whether the current async context runs in a transaction of `transaction`.
4777
+ *
4778
+ * A test transaction and the seed of a test fixture are not transactions, unless they run inside one, so that code under test behaves as in production.
4779
+ */
4780
+ isInTransaction() {
4781
+ return this.#storage.getStore()?.inTransaction ?? false;
4782
+ }
4783
+ /**
4784
+ * Runs `callback` in a transaction that always rolls back, on a coordinator created for tests.
4785
+ *
4786
+ * Inside a transaction, it runs `callback` in a savepoint nested in it that always rolls back.
4787
+ * When `signal` aborts, the level stops accepting statements at once and ends without waiting for `callback`.
4788
+ * Once the level ends, the transaction rejects with the reason of the signal.
4789
+ * A failure to start or end the level after the abort suppresses the reason in a `SuppressedError`.
4790
+ * Aborted while it waits for its turn in the fixture stack or for a connection, it rejects at once; after the abort, it never calls `callback`.
4791
+ */
4792
+ testTransaction(callback, { signal } = {}) {
4793
+ if (!this.test) return Promise.reject(/* @__PURE__ */ new Error("testTransaction requires a database created with test: true"));
4794
+ if (signal?.aborted) return Promise.reject(signal.reason);
4795
+ const parent = this.#storage.getStore();
4796
+ if (parent) return this.#runNested(parent, callback, false, signal);
4797
+ if (this.#closed) return Promise.reject(createDatabaseClosedError("start a test transaction"));
4798
+ return this.#fixtures.startTestLevel(signal).then((level) => level ? this.#runStarted(level, callback, false, signal) : this.#runRoot(callback, false, signal));
4799
+ }
4800
+ /**
4801
+ * Opens a test fixture: a transaction on a reserved connection, or a savepoint in the innermost open fixture, and runs `seed` in it.
4802
+ *
4803
+ * Test transactions started outside any transaction run in the innermost fixture.
4804
+ * A fixture whose `seed` rejects rolls back and closes.
4805
+ */
4806
+ createTestFixture(seed) {
4807
+ return this.startTestFixture(seed).fixture;
4808
+ }
4809
+ /**
4810
+ * Starts opening a test fixture, see `createTestFixture`, and returns it with a `close` that can be called before it opens.
4811
+ *
4812
+ * Closing before the fixture opens rejects the opening without waiting for `seed` and rolls the fixture back, so a fixture or test transaction started after `close` never nests in it, even when its `seed` never settles.
4813
+ * Closing an open fixture also closes the fixtures opening in it.
4814
+ */
4815
+ startTestFixture(seed) {
4816
+ return this.#fixtures.start(seed, this.#getFixtureRefusal());
4817
+ }
4818
+ /** Returns the error that rejects a test fixture created now, or `undefined` when it can open. */
4819
+ #getFixtureRefusal() {
4820
+ if (!this.test) return /* @__PURE__ */ new Error("createTestFixture requires a database created with test: true");
4821
+ if (this.#storage.getStore()) return /* @__PURE__ */ new Error("Cannot create a test fixture inside a transaction");
4822
+ if (this.#closed) return createDatabaseClosedError("create a test fixture");
3611
4823
  }
3612
4824
  /**
3613
- * Starts a nested level of a test fixture, preempting its active one.
4825
+ * Closes the test fixtures, including those still opening, and then the driver, which waits for the running transactions to end.
3614
4826
  *
3615
- * A fixture runs one nested level at a time, so a new one ends the previous one, such as a test that timed out.
3616
- * A level whose `signal` aborts while it preempts rejects with its reason instead of starting, and a failure to start after the abort suppresses the reason.
4827
+ * A driver waits for every reserved connection, so closing it under an open fixture would wait forever.
4828
+ * A failed closing of the driver suppresses the failures of closing the fixtures.
4829
+ * From the first call, statements, transactions, and fixtures started outside a transaction reject, while the levels already running can still end; a repeated call returns the same closing.
3617
4830
  */
3618
- async #startInFixture(fixture, signal) {
3619
- if (fixture.child) {
3620
- const cleanup = await this.#end(fixture.child, false, "preemption");
3621
- if (cleanup.rejection) throw suppressAbortReason(new Error("Cannot start a level of the test fixture: ending its active level failed", { cause: cleanup.rejection.error }), signal);
4831
+ close() {
4832
+ if (this.#storage.getStore()) return Promise.reject(/* @__PURE__ */ new Error("Cannot close the database inside a transaction"));
4833
+ this.#closed ??= this.#close();
4834
+ return this.#closed;
4835
+ }
4836
+ /** Closes the test fixtures and then the driver, see `close`. */
4837
+ async #close() {
4838
+ let rejection;
4839
+ try {
4840
+ await this.#fixtures.close();
4841
+ } catch (error) {
4842
+ rejection = { error };
3622
4843
  }
3623
- const unavailable = createUnavailableError(fixture, "start a level of the test fixture");
3624
- if (unavailable) throw suppressAbortReason(unavailable, signal);
3625
- signal?.throwIfAborted();
3626
- return this.#startNested(fixture, false, signal);
4844
+ try {
4845
+ await this.driver.close();
4846
+ } catch (error) {
4847
+ rejection = suppress(error, rejection, "Closing the driver failed");
4848
+ }
4849
+ if (rejection) throw rejection.error;
3627
4850
  }
3628
4851
  async #runRoot(callback, commits, signal, settings) {
3629
4852
  const connection = await (signal ? acquireUnlessAborted(this.driver, signal) : this.driver.acquire());
@@ -3642,9 +4865,9 @@ var TransactionCoordinator = class {
3642
4865
  if (!outcome && settings) try {
3643
4866
  await applySettings(level, settings);
3644
4867
  } catch (error) {
3645
- outcome = await getEndedOutcome(this.#end(level, false), error);
4868
+ outcome = await getEndedOutcome(endLevel(level, false), error);
3646
4869
  }
3647
- outcome ??= await this.#run(level, callback, commits, signal);
4870
+ outcome ??= await runLevel(this.#storage, level, callback, commits, signal);
3648
4871
  const rejection = await releaseConnection(connection, outcome.failure, outcome.rejection);
3649
4872
  if (rejection) throw rejection.error;
3650
4873
  return outcome.result;
@@ -3652,171 +4875,15 @@ var TransactionCoordinator = class {
3652
4875
  async #runNested(parent, callback, commits, signal, settings) {
3653
4876
  const unavailable = createUnavailableError(parent, "start a nested transaction");
3654
4877
  if (unavailable) throw unavailable;
3655
- return this.#runStarted(await this.#startNested(parent, commits, signal, settings), callback, commits, signal);
3656
- }
3657
- /**
3658
- * Reserves a nested level of `parent`, sends its `SAVEPOINT`, and applies its `settings`, rejecting when one of them fails.
3659
- *
3660
- * `transaction` tells whether the level is a transaction of `transaction` rather than a level of the test helpers.
3661
- * A level whose settings fail rolls back to its savepoint first.
3662
- * The failure suppresses the reason of `signal` when the signal aborted before it.
3663
- */
3664
- async #startNested(parent, transaction, signal, settings) {
3665
- const level = createLevel(parent.connection, parent, transaction);
3666
- parent.child = level;
3667
- try {
3668
- await level.connection.execute(createStatement(`SAVEPOINT ${getSavepointName(level)}`));
3669
- } catch (error) {
3670
- if (!level.cleanup) {
3671
- closeStarting(level);
3672
- throw suppressAbortReason(error, signal);
3673
- }
3674
- throw suppressAbortReason(combineRejections({ error }, await level.cleanup).error, signal);
3675
- }
3676
- if (settings) try {
3677
- await applySettings(level, settings);
3678
- } catch (error) {
3679
- throw suppressAbortReason(combineRejections({ error }, await this.#end(level, false)).error, signal);
3680
- }
3681
- return level;
4878
+ return this.#runStarted(await startNestedLevel(parent, commits, signal, settings), callback, commits, signal);
3682
4879
  }
3683
4880
  /** Runs `callback` in a started level, returning its result or throwing its rejection. */
3684
4881
  async #runStarted(level, callback, commits, signal) {
3685
- const { result, rejection } = await this.#run(level, callback, commits, signal);
4882
+ const { result, rejection } = await runLevel(this.#storage, level, callback, commits, signal);
3686
4883
  if (rejection) throw rejection.error;
3687
4884
  return result;
3688
4885
  }
3689
- /**
3690
- * Runs `callback` in the context of a started `level`, then ends the level.
3691
- *
3692
- * A level that `commits` commits only when the callback resolves with no nested level running and no nested level that failed to end.
3693
- * When `signal` aborts, the level ends at once without waiting for the callback, and its outcome is a rejection with the reason of the signal.
3694
- */
3695
- async #run(level, callback, commits, signal) {
3696
- if (level.cleanup) return getEndedOutcome(level.cleanup, createClosedError(level, void 0));
3697
- let result;
3698
- let rejection;
3699
- if (signal?.aborted) this.#end(level, false, "abort");
3700
- else {
3701
- level.state = "open";
3702
- try {
3703
- result = await (signal ? settleUnlessAborted(() => this.#storage.run(level, callback), signal, () => {
3704
- this.#end(level, false, "abort");
3705
- return true;
3706
- }) : this.#storage.run(level, callback));
3707
- } catch (error) {
3708
- rejection = { error };
3709
- }
3710
- }
3711
- if (level.cleanup) return getEndedOutcome(level.cleanup, level.endedBy === "abort" ? signal?.reason : createClosedError(level, rejection));
3712
- if (!rejection && level.child) rejection = { error: /* @__PURE__ */ new Error("Transaction was rolled back: a nested transaction was still running when its callback finished") };
3713
- if (!rejection && level.rollbackOnly) rejection = { error: new Error("Transaction was rolled back: a nested transaction failed to end", { cause: level.rollbackOnly.error }) };
3714
- const cleanup = await this.#end(level, commits && !rejection);
3715
- return {
3716
- result,
3717
- rejection: combineRejections(rejection, cleanup),
3718
- failure: cleanup.failure
3719
- };
3720
- }
3721
- /**
3722
- * Ends `level` once, whoever ends it: later calls return the cleanup already started.
3723
- *
3724
- * Statements of the level and of all levels nested in it fail from the start of the cleanup, and the nested levels end before it.
3725
- */
3726
- #end(level, commit, endedBy) {
3727
- if (level.cleanup) return level.cleanup;
3728
- level.endedBy = endedBy;
3729
- level.state = "closing";
3730
- const nested = level.child && this.#end(level.child, false, "enclosing level");
3731
- level.cleanup = this.#cleanUp(level, commit, nested);
3732
- return level.cleanup;
3733
- }
3734
- async #cleanUp(level, commit, nested) {
3735
- const nestedCleanup = await nested;
3736
- const { parent } = level;
3737
- const cleanup = parent ? await endSavepoint(level, commit && !level.rollbackOnly) : await endTransaction(level.connection, commit);
3738
- level.state = "closed";
3739
- if (parent) {
3740
- if (parent.child === level) parent.child = void 0;
3741
- parent.rollbackOnly ??= cleanup.rejection;
3742
- }
3743
- return {
3744
- rejection: combineRejections(nestedCleanup?.rejection, cleanup),
3745
- failure: cleanup.failure
3746
- };
3747
- }
3748
4886
  };
3749
- const beginStatement = createStatement("BEGIN");
3750
- const commitStatement = createStatement("COMMIT");
3751
- const rollbackStatement = createStatement("ROLLBACK");
3752
- function createLevel(connection, parent, transaction) {
3753
- return {
3754
- connection,
3755
- parent,
3756
- depth: parent ? parent.depth + 1 : 0,
3757
- inTransaction: transaction || (parent?.inTransaction ?? false),
3758
- state: "starting",
3759
- child: void 0,
3760
- rollbackOnly: void 0,
3761
- cleanup: void 0,
3762
- endedBy: void 0,
3763
- restore: void 0
3764
- };
3765
- }
3766
- /** Settings of a level of `transaction` with `options`, or `undefined` when it has none. */
3767
- function getLevelSettings(options) {
3768
- return options?.searchPath ? [{
3769
- name: "search_path",
3770
- value: options.searchPath.map(quoteIdentifier).join(", ")
3771
- }] : void 0;
3772
- }
3773
- /**
3774
- * Applies `settings` to a level whose `BEGIN` or `SAVEPOINT` completed, unless its cleanup has started.
3775
- *
3776
- * A nested level first reads the values they replace, which it restores before its `RELEASE`: PostgreSQL keeps a setting of a released savepoint until the transaction ends, while `ROLLBACK TO` restores it itself.
3777
- */
3778
- async function applySettings(level, settings) {
3779
- if (level.cleanup) return;
3780
- if (level.parent) {
3781
- let text = "SELECT";
3782
- const names = [];
3783
- for (const { name } of settings) {
3784
- names.push(name);
3785
- text += `${names.length > 1 ? "," : ""} current_setting($${names.length})`;
3786
- }
3787
- const values = (await level.connection.execute(createStatement(text, names))).rows[0];
3788
- level.restore = createSettingStatement(settings.map(({ name }, index) => ({
3789
- name,
3790
- value: values[index]
3791
- })));
3792
- if (level.cleanup) return;
3793
- }
3794
- await level.connection.execute(createSettingStatement(settings));
3795
- }
3796
- /** Creates the statement that sets `settings` until the transaction ends or rolls back to an earlier savepoint, as `SET LOCAL` does. */
3797
- function createSettingStatement(settings) {
3798
- let text = "SELECT";
3799
- const parameters = [];
3800
- for (const { name, value } of settings) {
3801
- parameters.push(name, value);
3802
- text += `${parameters.length > 2 ? "," : ""} set_config($${parameters.length - 1}, $${parameters.length}, true)`;
3803
- }
3804
- return createStatement(text, parameters);
3805
- }
3806
- /** Name of the savepoint of a nested level; a level has at most one nested level, so the depth makes it unique. */
3807
- function getSavepointName(level) {
3808
- return `kern_${level.depth}`;
3809
- }
3810
- /**
3811
- * Creates the error of `operation` on a level that rejects statements and nested levels, or returns `undefined` when it accepts them.
3812
- *
3813
- * A level that can only roll back keeps the failure of its nested level as the cause.
3814
- */
3815
- function createUnavailableError(level, operation) {
3816
- if (level.state !== "open") return /* @__PURE__ */ new Error(`Cannot ${operation}: its transaction is already finished`);
3817
- if (level.child) return /* @__PURE__ */ new Error(`Cannot ${operation}: a nested transaction is running; await it first`);
3818
- if (level.rollbackOnly) return new Error(`Cannot ${operation}: a nested transaction failed to end, so the transaction can only roll back`, { cause: level.rollbackOnly.error });
3819
- }
3820
4887
  /** Error message of an operation outside a test transaction on a coordinator created for tests. */
3821
4888
  function describeOutsideTest(operation) {
3822
4889
  return `Cannot ${operation} outside a test transaction: the database was created with test: true, so wrap the test in testTransaction`;
@@ -3825,118 +4892,6 @@ function describeOutsideTest(operation) {
3825
4892
  function createDatabaseClosedError(operation) {
3826
4893
  return /* @__PURE__ */ new Error(`Cannot ${operation}: the database is closed`);
3827
4894
  }
3828
- /** Closes a nested level whose `SAVEPOINT` failed, freeing its parent for other statements. */
3829
- function closeStarting(level) {
3830
- level.state = "closed";
3831
- if (level.parent?.child === level) level.parent.child = void 0;
3832
- }
3833
- /** Error of a level that another level ended, keeping the rejection of its callback as the cause. */
3834
- function createClosedError(level, rejection) {
3835
- return new Error(level.endedBy === "preemption" ? "Transaction was rolled back: its test fixture started another level before it finished" : "Transaction was rolled back: its enclosing transaction ended first", rejection && { cause: rejection.error });
3836
- }
3837
- /** Error of a failure to start a test transaction, which suppresses the reason of `signal` when the signal aborted before the failure. */
3838
- function suppressAbortReason(error, signal) {
3839
- return signal?.aborted ? suppress(error, { error: signal.reason }, "Starting the test transaction failed").error : error;
3840
- }
3841
- /** Outcome of a level that something other than its callback ended: a rejection with `error`, which a failed cleanup suppresses. */
3842
- async function getEndedOutcome(cleanup, error) {
3843
- const ended = await cleanup;
3844
- return {
3845
- rejection: combineRejections({ error }, ended),
3846
- failure: ended.failure
3847
- };
3848
- }
3849
- /**
3850
- * Runs `start` and settles as its promise, or rejects with the reason of `signal` when it aborts first and `abort`, called on the abort, returns `true`.
3851
- *
3852
- * It listens from before `start`, which may abort the signal before it returns, and stops once the promise of `start` settles, such as for the signal that `node:test` aborts after every test.
3853
- * A later rejection of that promise stays handled, and a value it resolves with after the rejection goes to `discard`, which handles its own failures.
3854
- */
3855
- function settleUnlessAborted(start, signal, abort, discard) {
3856
- const settled = Promise.withResolvers();
3857
- let abandoned = false;
3858
- const onAbort = () => {
3859
- if (abort()) {
3860
- abandoned = true;
3861
- settled.reject(signal.reason);
3862
- }
3863
- };
3864
- signal.addEventListener("abort", onAbort, { once: true });
3865
- let running;
3866
- try {
3867
- running = start();
3868
- } catch (error) {
3869
- signal.removeEventListener("abort", onAbort);
3870
- settled.reject(error);
3871
- return settled.promise;
3872
- }
3873
- running.then((value) => {
3874
- signal.removeEventListener("abort", onAbort);
3875
- if (abandoned) discard?.(value);
3876
- else settled.resolve(value);
3877
- }, (error) => {
3878
- signal.removeEventListener("abort", onAbort);
3879
- settled.reject(error);
3880
- });
3881
- return settled.promise;
3882
- }
3883
- /**
3884
- * Acquires a connection for a test transaction, rejecting with the reason of `signal` when it aborts first.
3885
- *
3886
- * A connection acquired after the abort returns to the pool as best-effort cleanup: the test transaction has already rejected, so nothing awaits the release, and its failure is ignored.
3887
- */
3888
- function acquireUnlessAborted(driver, signal) {
3889
- if (signal.aborted) return Promise.reject(signal.reason);
3890
- return settleUnlessAborted(() => driver.acquire(), signal, () => true, (connection) => {
3891
- Promise.resolve().then(() => connection.release()).catch(() => {});
3892
- });
3893
- }
3894
- /** Returns the connection of a transaction to the driver, keeping a failed release as a rejection that suppresses `rejection`. */
3895
- async function releaseConnection(connection, failure, rejection) {
3896
- try {
3897
- await connection.release(failure);
3898
- return rejection;
3899
- } catch (error) {
3900
- return suppress(error, rejection, "Releasing the transaction connection failed");
3901
- }
3902
- }
3903
- /** Ends a transaction with `COMMIT` or `ROLLBACK`. */
3904
- async function endTransaction(connection, commit) {
3905
- try {
3906
- const { command } = await connection.execute(commit ? commitStatement : rollbackStatement);
3907
- if (commit && command !== "COMMIT") return { rejection: { error: /* @__PURE__ */ new Error("Transaction was rolled back: a statement in it failed") } };
3908
- return {};
3909
- } catch (error) {
3910
- return {
3911
- rejection: { error },
3912
- failure: { error }
3913
- };
3914
- }
3915
- }
3916
- /**
3917
- * Ends the savepoint of a nested level, releasing it on commit and rolling back to it otherwise.
3918
- *
3919
- * Before a release, it restores the settings that the level changed; a rollback restores them itself.
3920
- */
3921
- async function endSavepoint(level, commit) {
3922
- const name = getSavepointName(level);
3923
- try {
3924
- if (!commit) await level.connection.execute(createStatement(`ROLLBACK TO SAVEPOINT ${name}`));
3925
- else if (level.restore) await level.connection.execute(level.restore);
3926
- await level.connection.execute(createStatement(`RELEASE SAVEPOINT ${name}`));
3927
- return {};
3928
- } catch (error) {
3929
- return { rejection: { error } };
3930
- }
3931
- }
3932
- /** Rejection with the error of a cleanup, which suppresses the `earlier` rejection if there is one. */
3933
- function combineRejections(earlier, cleanup) {
3934
- return cleanup.rejection ? suppress(cleanup.rejection.error, earlier, "Transaction rollback failed") : earlier;
3935
- }
3936
- /** Rejection with `error`, which suppresses the `earlier` rejection if there is one. */
3937
- function suppress(error, earlier, message) {
3938
- return { error: earlier ? new SuppressedError(error, earlier.error, message) : error };
3939
- }
3940
4895
  // src/execution/database.ts
3941
4896
  /** Creates a database over a driver; its transactions are shared by every schema bound to it. */
3942
4897
  function createDatabase({ driver, test }) {
@@ -3965,4 +4920,4 @@ function createDatabase({ driver, test }) {
3965
4920
  function createLockStatement(key) {
3966
4921
  return createStatement("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [key]);
3967
4922
  }
3968
- export { not as C, isColumnValues as S, combineNodes as T, NotFoundError as _, defineSchema as a, literal as b, requireDerived as c, orderBy as d, many as f, defineTable as g, matchColumn as h, sql as i, asc as l, createStatement as m, identifier as n, SelectQuery as o, one as p, param as r, from as s, createDatabase as t, desc as u, RequiredRelationError as v, or as w, and as x, ResultDecodeError as y };
4923
+ 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 };