kern-orm 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as falseNode, C as equalityOperators, D as createList, E as defineGenericOperator, M as oneNode, N as trueNode, O as createNode, S as Column, T as isEqualityCompatible, _ as toNode, a as stringifyJson, b as defineType, c as textOperators, d as compareNodes, f as createIsNullNode, g as toExpression, h as toBooleanExpression, j as noFilters, k as createSelectNode, l as orderingOperators, m as isExpression, n as text, p as getType, s as encodeBytea, u as compare, v as toValueNode, x as isType } from "./text-4Xs09pIf.mjs";
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, N as defineGenericOperator, O as Column, P as createList, R as noFilters, S as toFunctionOperandNode, T as getBooleanType, _ as isOperandSource, a as stringifyJson, b as toCastValueNode, c as textOperators, d as orderingOperators, f as compare, g as isExpression, h as getType, j as isEqualityCompatible, k as equalityOperators, 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";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  // src/ast/boolean.ts
4
4
  /** Combines predicate nodes with `AND` or `OR`, flattening nested groups of the same kind. */
@@ -33,13 +33,21 @@ function combineFilters(filters) {
33
33
  return combineNodes("and", operands);
34
34
  }
35
35
  // src/expression/predicates.ts
36
- /** Converts a condition operand into a node; an object of column values stays unresolved until `where`. */
36
+ /** Converts a condition operand into a node: a query nests in it, and an object of column values stays unresolved until `where`; it rejects any other value. */
37
37
  function toConditionNode(operand) {
38
- return isExpression(operand) ? toNode(operand) : createNode({
38
+ if (isOperandSource(operand)) return operand[operandNode]();
39
+ if (!isColumnValues(operand)) throw new TypeError("A condition must be an expression, such as TRUE or FALSE, a query of one Boolean value, or an object of column values");
40
+ return createNode({
39
41
  kind: "values",
40
42
  values: operand
41
43
  });
42
44
  }
45
+ /** Tells whether `value` can be an object of column values: a plain object, of this or another realm, or an object that has keys. */
46
+ function isColumnValues(value) {
47
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
48
+ const prototype = Object.getPrototypeOf(value);
49
+ return prototype === null || Object.getPrototypeOf(prototype) === null || Object.keys(value).length > 0;
50
+ }
43
51
  function and(...operands) {
44
52
  return combineConditions("and", operands);
45
53
  }
@@ -54,6 +62,244 @@ function combineConditions(kind, operands) {
54
62
  for (const operand of operands) nodes.push(toConditionNode(operand));
55
63
  return toBooleanExpression(combineNodes(kind, nodes));
56
64
  }
65
+ // src/expression/constants.ts
66
+ /** Condition that always holds, such as for a branch of `and` built in code: `and(check, isAdmin ? TRUE : other)`. */
67
+ const TRUE = /* @__PURE__ */ toExpression(trueNode, /* @__PURE__ */ getBooleanType());
68
+ /** Condition that never holds, such as for a branch of `or` built in code: `or(check, isAdmin ? other : FALSE)`. */
69
+ const FALSE = /* @__PURE__ */ toExpression(falseNode, /* @__PURE__ */ getBooleanType());
70
+ // src/query/literal.ts
71
+ /**
72
+ * Field of a selection whose value is `T`, made by `literal`.
73
+ *
74
+ * It is not an expression: it has no SQL, so only a selection takes it.
75
+ */
76
+ var Literal = class {
77
+ literal;
78
+ constructor(literal) {
79
+ this.literal = literal;
80
+ Object.freeze(this);
81
+ }
82
+ /** Rejects the literal as an operand of an expression: a statement has no SQL of it. */
83
+ [operandNode]() {
84
+ throw new TypeError("A literal is only a selected field and has no SQL; pass a value to an expression through param");
85
+ }
86
+ };
87
+ /**
88
+ * Creates a field of a selection with `value` in every record, without selecting anything in SQL: the query returns the value itself, not a copy of it, whatever it is.
89
+ *
90
+ * It lets a branch of a selection skip a column or a nested query, such as `withHint ? field.hint : literal(null)`.
91
+ * To use a value in SQL, such as in a condition, pass it through `param` instead.
92
+ */
93
+ function literal(value) {
94
+ return new Literal(value);
95
+ }
96
+ // src/query/exists.ts
97
+ let existsProjection;
98
+ /** Returns the projection of whether a query has a row, the only projection of a query made by `exists`. */
99
+ function getExistsProjection() {
100
+ existsProjection ??= Object.freeze({
101
+ key: "exists",
102
+ expression: trueNode,
103
+ type: getBooleanType()
104
+ });
105
+ return existsProjection;
106
+ }
107
+ /** Tells whether `node` is a query of `exists`, whose value is whether the rows that its filters, limit, and offset choose exist. */
108
+ function isExistsQuery(node) {
109
+ return node.value && isExistsProjection(node.projections[0]);
110
+ }
111
+ /** Tells whether a selected field is the projection of `exists`, which only `exists` selects. */
112
+ function isExistsProjection(field) {
113
+ return field === existsProjection;
114
+ }
115
+ // src/query/aggregate.ts
116
+ /** Type of a count: a `bigint` of PostgreSQL decoded as a number, rejecting a count that a number cannot hold exactly. */
117
+ const countType = /* @__PURE__ */ defineType({ sqlType: "bigint" }).transform({
118
+ decode: (text) => decodeSafeInteger(text, "Count"),
119
+ encode: (value) => String(value)
120
+ }).withOperators(orderingOperators);
121
+ /** Types of the sums of the values of `smallint` and `integer` types, by the type of the values, created once for each. */
122
+ const sumTypes = /* @__PURE__ */ new WeakMap();
123
+ const noFallbacks = /* @__PURE__ */ Object.freeze([]);
124
+ /** Definitions of the aggregate methods that take a column or an expression. */
125
+ const aggregateDefinitions = /* @__PURE__ */ Object.freeze({
126
+ count: {
127
+ name: "count",
128
+ distinct: false,
129
+ ordered: false,
130
+ type: toCountType
131
+ },
132
+ countDistinct: {
133
+ name: "count",
134
+ distinct: true,
135
+ ordered: false,
136
+ type: toCountType
137
+ },
138
+ min: {
139
+ name: "min",
140
+ distinct: false,
141
+ ordered: false,
142
+ type: toArgumentType
143
+ },
144
+ max: {
145
+ name: "max",
146
+ distinct: false,
147
+ ordered: false,
148
+ type: toArgumentType
149
+ },
150
+ sum: {
151
+ name: "sum",
152
+ distinct: false,
153
+ ordered: false,
154
+ type: toSumType
155
+ },
156
+ avg: {
157
+ name: "avg",
158
+ distinct: false,
159
+ ordered: false,
160
+ type: toAverageType
161
+ },
162
+ every: {
163
+ name: "bool_and",
164
+ distinct: false,
165
+ ordered: false,
166
+ type: toArgumentType
167
+ },
168
+ some: {
169
+ name: "bool_or",
170
+ distinct: false,
171
+ ordered: false,
172
+ type: toArgumentType
173
+ },
174
+ stringAgg: {
175
+ name: "string_agg",
176
+ distinct: false,
177
+ ordered: true,
178
+ type: toArgumentType
179
+ }
180
+ });
181
+ /** Projection of `count(*)`, which `count` without an argument selects; its value does not depend on the values or the order of the rows. */
182
+ const countProjection = /* @__PURE__ */ Object.freeze({
183
+ key: "count",
184
+ expression: /* @__PURE__ */ createNode({
185
+ kind: "aggregate",
186
+ name: "count",
187
+ distinct: false,
188
+ arguments: [/* @__PURE__ */ createNode({
189
+ kind: "raw",
190
+ fragments: ["*"],
191
+ values: []
192
+ })],
193
+ orderBy: []
194
+ }),
195
+ type: countType,
196
+ ordered: false,
197
+ fallbacks: noFallbacks
198
+ });
199
+ /**
200
+ * Creates the projection of the aggregate of `method` over the values of `argument`, the projection of a column or an expression of each row.
201
+ *
202
+ * `delimiter` is the delimiter of `stringAgg`, sent as a parameter.
203
+ */
204
+ function projectAggregate(method, argument, delimiter) {
205
+ const definition = aggregateDefinitions[method];
206
+ const args = [argument.expression];
207
+ if (delimiter !== void 0) args.push(createNode({
208
+ kind: "param",
209
+ value: delimiter,
210
+ type: void 0
211
+ }));
212
+ return Object.freeze({
213
+ key: method,
214
+ expression: createNode({
215
+ kind: "aggregate",
216
+ name: definition.name,
217
+ distinct: definition.distinct,
218
+ arguments: args,
219
+ orderBy: []
220
+ }),
221
+ type: definition.type(argument.type),
222
+ ordered: definition.ordered,
223
+ fallbacks: noFallbacks
224
+ });
225
+ }
226
+ /** Creates the projection of the aggregate of `field` whose `NULL` `fallback` replaces after its earlier fallbacks. */
227
+ function addFallback(field, fallback) {
228
+ const fallbacks = field.fallbacks.slice();
229
+ fallbacks.push(fallback);
230
+ return Object.freeze({
231
+ key: field.key,
232
+ expression: field.expression,
233
+ type: field.type,
234
+ ordered: field.ordered,
235
+ fallbacks
236
+ });
237
+ }
238
+ /** Tells whether `node` is an aggregate query, whose value is the aggregate over the rows that its filters, limit, and offset choose. */
239
+ function isAggregateQuery(node) {
240
+ return node.value && isAggregateProjection(node.projections[0]);
241
+ }
242
+ /** 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. */
243
+ function isSelectedAsValue(node) {
244
+ return isAggregateQuery(node) || isExistsQuery(node);
245
+ }
246
+ /** Tells whether a selected field is the projection of an aggregate, which only the aggregate methods select. */
247
+ function isAggregateProjection(field) {
248
+ return "ordered" in field;
249
+ }
250
+ function toCountType() {
251
+ return countType;
252
+ }
253
+ function toArgumentType(argument) {
254
+ return argument;
255
+ }
256
+ /** Returns the type of the sum of the values of type `argument`: for `smallint` and `integer` values, a `bigint` with the operators of `argument` that rejects a sum that a number cannot hold exactly, and `argument` for others. */
257
+ function toSumType(argument) {
258
+ if (argument.castType !== "smallint" && argument.castType !== "integer") return argument;
259
+ let type = sumTypes.get(argument);
260
+ if (type === void 0) {
261
+ type = defineType({ sqlType: "bigint" }).transform({
262
+ decode: (text) => {
263
+ decodeSafeInteger(text, "Sum");
264
+ return argument.decode(text);
265
+ },
266
+ encode: (value) => argument.encode(value)
267
+ }).withOperators(argument.operators);
268
+ sumTypes.set(argument, type);
269
+ }
270
+ return type;
271
+ }
272
+ /** Returns the type of the average of the values of type `argument`, which decodes a fractional value where PostgreSQL gives one, such as `Number` of `integer`; rejects a type of the SQL type `bigint`. */
273
+ function toAverageType(argument) {
274
+ if (argument.castType === "bigint") throw new TypeError("avg of bigint values requires a type that can represent fractional values; average a sql fragment of the column with t.doublePrecision or t.numeric");
275
+ return argument;
276
+ }
277
+ /** Decodes `text` of a `bigint` as a number, rejecting one that a number cannot hold exactly; `subject` names it in the error. */
278
+ function decodeSafeInteger(text, subject) {
279
+ const number = Number(text);
280
+ if (!Number.isSafeInteger(number)) throw new RangeError(`${subject} ${text} is outside the safe integer range of numbers`);
281
+ return number;
282
+ }
283
+ // src/query/column-refs.ts
284
+ /** Context of a query whose table has no relations, such as one of `from(table)`. */
285
+ const noContext = /* @__PURE__ */ Object.freeze({ relations: /* @__PURE__ */ Object.freeze({}) });
286
+ /** Creates expressions of the columns of `source`, each with the type of its column. */
287
+ function createColumnRefs(source) {
288
+ return addColumns({}, source);
289
+ }
290
+ /** Creates the references of the callbacks of `select`, `where`, and `orderBy` over `source`: its columns, and the relations of `context`. */
291
+ function createSelectionRefs(source, context) {
292
+ return Object.defineProperties(addColumns({}, source), context.relations);
293
+ }
294
+ function addColumns(refs, source) {
295
+ const { columns } = source.table;
296
+ for (const key of Object.keys(columns)) refs[key] = toExpression(createNode({
297
+ kind: "column",
298
+ source,
299
+ key
300
+ }), columns[key].type);
301
+ return refs;
302
+ }
57
303
  // src/result/result.ts
58
304
  /** Error thrown when a query of one row finds no row, executed or nested, unless a required relation guarantees the row. */
59
305
  var NotFoundError = class extends Error {
@@ -110,9 +356,9 @@ function decodeStatement(plan, result) {
110
356
  }
111
357
  }
112
358
  if (counted === "number") return count;
113
- if (plan.rows !== "many") return {};
359
+ if (plan.rows !== "many") return createRecord(plan);
114
360
  const records = [];
115
- for (let index = 0; index < count; index++) records.push({});
361
+ for (let index = 0; index < count; index++) records.push(createRecord(plan));
116
362
  return records;
117
363
  }
118
364
  /**
@@ -231,7 +477,7 @@ function decodeRow(plan, row, packed) {
231
477
  let index = 0;
232
478
  try {
233
479
  if (plan.value) return packed ? decodePackedValue(fields[0], row[0]) : decodeDriverValue(fields[0], row[0]);
234
- const record = {};
480
+ const record = createRecord(plan);
235
481
  for (; index < fields.length; index++) {
236
482
  const field = fields[index];
237
483
  record[field.key] = packed ? decodePackedValue(field, row[index]) : decodeDriverValue(field, row[index]);
@@ -242,6 +488,16 @@ function decodeRow(plan, row, packed) {
242
488
  throw prefixPath(packed ? `.${key}` : key, error);
243
489
  }
244
490
  }
491
+ /** Creates a record of `plan` before the values of a row are assigned: empty, or with the fields of its template. */
492
+ function createRecord(plan) {
493
+ const record = {};
494
+ const { recordTemplate } = plan;
495
+ if (recordTemplate !== void 0) for (let index = 0; index < recordTemplate.length; index++) {
496
+ const { key, value } = recordTemplate[index];
497
+ record[key] = value;
498
+ }
499
+ return record;
500
+ }
245
501
  /** Decodes the value of a field in a row of the driver: a scalar from its text, keeping `NULL` as `null`, or nested rows from JSON text. */
246
502
  function decodeDriverValue(field, value) {
247
503
  if (value === void 0) throw new Error("Driver returned no value");
@@ -386,63 +642,167 @@ function toColumn(definition) {
386
642
  hasDefault: false
387
643
  }) : void 0;
388
644
  }
389
- // src/type/untyped.ts
390
- /** Encodes a parameter without a type, such as a value interpolated into `sql`, by its JavaScript type. */
391
- function encodeUntyped(value) {
392
- if (typeof value === "string") return value;
393
- if (typeof value === "number" || typeof value === "bigint") return String(value);
394
- if (typeof value === "boolean") return value ? "t" : "f";
395
- if (value instanceof Date) return value.toISOString();
396
- if (value instanceof Uint8Array) return encodeBytea(value);
397
- if (Array.isArray(value)) throw new TypeError("Pass an array with param(type, value), so that its elements are encoded by type");
398
- return stringifyJson(value);
399
- }
400
- // src/compiler/postgres/identifier.ts
401
- /** Most bytes a character other than ASCII takes in a server encoding, such as UTF-8 and EUC_TW ([PostgreSQL](https://www.postgresql.org/docs/current/multibyte.html)). */
402
- const maxCharacterBytes = 4;
403
- function quoteIdentifier(name) {
404
- return `"${name.replaceAll("\"", "\"\"")}"`;
405
- }
406
- /**
407
- * Returns the longest prefix of `name` that takes at most `maxBytes` bytes in any server encoding.
408
- *
409
- * Every server encoding takes one byte for an ASCII character, and a character other than ASCII counts as the most bytes it can take.
410
- */
411
- function cutIdentifier(name, maxBytes) {
412
- if (name.length * maxCharacterBytes <= maxBytes) return name;
413
- let bytes = 0;
414
- for (let index = 0; index < name.length;) {
415
- const code = name.codePointAt(index);
416
- bytes += code < 128 ? 1 : maxCharacterBytes;
417
- if (bytes > maxBytes) return name.slice(0, index);
418
- index += code < 65536 ? 1 : 2;
419
- }
420
- return name;
421
- }
645
+ // src/query/resolve-values.ts
422
646
  /**
423
- * Returns the SQL name of the projection with `key` at `index` of its query.
647
+ * Replaces objects of column values in a predicate with conditions on the columns of `source`.
424
648
  *
425
- * A key of up to 50 bytes is the name itself.
426
- * A longer key is named by its prefix of at most 54 bytes, which takes more than 50 since a character takes at most 4, and by its position, so the name differs from the names of the other projections.
649
+ * Groups are rebuilt, so constants that appear after resolution, such as an empty object, are simplified.
427
650
  */
428
- function nameProjection(key, index) {
429
- if (cutIdentifier(key, 50).length === key.length) return key;
430
- return `${cutIdentifier(key, 54)}_${index + 1}`;
431
- }
432
- // src/compiler/postgres/context.ts
433
- /** Most parameters that PostgreSQL takes in a statement. */
434
- const maxParameters = 65535;
435
- /** State of one compilation shared by all queries of the statement, never by reusable nodes: parameters and the aliases of all sources. */
436
- var CompileContext = class {
437
- schemaName;
438
- tables;
439
- db;
440
- parameters = [];
441
- #aliases = /* @__PURE__ */ new Set();
442
- constructor(schemaName, tables = void 0, db = void 0) {
443
- this.schemaName = schemaName;
444
- this.tables = tables;
445
- this.db = db;
651
+ function resolveValues(node, source) {
652
+ switch (node.kind) {
653
+ case "values": {
654
+ const conditions = [];
655
+ for (const key of Object.keys(node.values)) {
656
+ const value = node.values[key];
657
+ const type = findColumn(source.table, key)?.type;
658
+ const column = createNode({
659
+ kind: "column",
660
+ source,
661
+ key
662
+ });
663
+ if (value === null) conditions.push(createIsNullNode(column));
664
+ else if (type) conditions.push(matchValue(source, column, type, value));
665
+ else conditions.push(compareNodes("=", column, value, void 0));
666
+ }
667
+ return combineNodes("and", conditions);
668
+ }
669
+ case "and":
670
+ case "or": {
671
+ const operands = [];
672
+ for (const operand of node.operands) operands.push(resolveValues(operand, source));
673
+ return combineNodes(node.kind, operands);
674
+ }
675
+ case "not": return negateNode(resolveValues(node.operand, source));
676
+ case "raw": return createNode({
677
+ ...node,
678
+ values: node.values.map((value) => resolveValues(value, source))
679
+ });
680
+ default: return node;
681
+ }
682
+ }
683
+ /** Matches a column with a value of an object condition by calling the `eq` operator of its type, whichever operator it is. */
684
+ function matchValue(source, column, type, value) {
685
+ const expression = toExpression(column, type);
686
+ 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`);
687
+ const predicate = expression.eq(value);
688
+ if (!isExpression(predicate)) throw new TypeError(`The eq operator of column "${column.key}" of table "${source.table.name}" must return an expression`);
689
+ return toNode(predicate);
690
+ }
691
+ // src/query/filters.ts
692
+ /** Creates query filters with the filter of `where` over `source` with the relations of `context` combined with `AND` with the user filters of `filters`. */
693
+ function addUserFilter(filters, filter, source, context) {
694
+ const predicate = resolveValues(toConditionNode(typeof filter === "function" ? filter(createSelectionRefs(source, context)) : filter), source);
695
+ const { user } = filters;
696
+ return Object.freeze({
697
+ ...filters,
698
+ user: user ? combineNodes("and", [user, predicate]) : predicate
699
+ });
700
+ }
701
+ function defineFilter(_table, filter) {
702
+ return filter;
703
+ }
704
+ // src/query/find.ts
705
+ /**
706
+ * 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.
707
+ *
708
+ * It rejects a table without a primary key, a number of values other than the number of its columns, and `null` and `undefined`.
709
+ */
710
+ function toPrimaryKeyValues(table, values) {
711
+ const { primaryKey } = table.keys;
712
+ 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`);
713
+ 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}`);
714
+ const filter = {};
715
+ for (let index = 0; index < primaryKey.length; index++) {
716
+ const column = primaryKey[index];
717
+ filter[column] = requireKeyValue(table, column, values[index], "find");
718
+ }
719
+ return filter;
720
+ }
721
+ /**
722
+ * Returns the object of column values that `findBy` filters a query of `table` by: a copy of `values`, read once.
723
+ *
724
+ * It rejects columns that are not exactly a key of `table`, primary or unique, as `isKey` tells, and `null` and `undefined`.
725
+ */
726
+ function toKeyValues(table, values) {
727
+ if (typeof values !== "object" || values === null) throw new TypeError(`findBy takes an object of the columns of a key of table "${table.name}"`);
728
+ const columns = Object.keys(values);
729
+ if (!isKey(table, columns)) {
730
+ const listed = columns.length === 0 ? "an object without columns" : `columns ${quoteColumns(columns)}`;
731
+ throw new Error(`findBy takes the columns of one key of table "${table.name}", primary or unique, not ${listed}`);
732
+ }
733
+ const filter = {};
734
+ for (const column of columns) {
735
+ const value = values[column];
736
+ filter[column] = requireKeyValue(table, column, value, "findBy");
737
+ }
738
+ return filter;
739
+ }
740
+ /** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
741
+ function requireKeyValue(table, column, value, method) {
742
+ 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`);
743
+ return value;
744
+ }
745
+ /** Lists `columns` in quotes, separated by commas. */
746
+ function quoteColumns(columns) {
747
+ return columns.map((column) => `"${column}"`).join(", ");
748
+ }
749
+ // src/type/untyped.ts
750
+ /** Encodes a parameter without a type, such as a value interpolated into `sql`, by its JavaScript type. */
751
+ function encodeUntyped(value) {
752
+ if (typeof value === "string") return value;
753
+ if (typeof value === "number" || typeof value === "bigint") return String(value);
754
+ if (typeof value === "boolean") return value ? "t" : "f";
755
+ if (value instanceof Date) return value.toISOString();
756
+ if (value instanceof Uint8Array) return encodeBytea(value);
757
+ if (Array.isArray(value)) throw new TypeError("Pass an array with param(type, value), so that its elements are encoded by type");
758
+ return stringifyJson(value);
759
+ }
760
+ // src/compiler/postgres/identifier.ts
761
+ /** Most bytes a character other than ASCII takes in a server encoding, such as UTF-8 and EUC_TW ([PostgreSQL](https://www.postgresql.org/docs/current/multibyte.html)). */
762
+ const maxCharacterBytes = 4;
763
+ function quoteIdentifier(name) {
764
+ return `"${name.replaceAll("\"", "\"\"")}"`;
765
+ }
766
+ /**
767
+ * Returns the longest prefix of `name` that takes at most `maxBytes` bytes in any server encoding.
768
+ *
769
+ * Every server encoding takes one byte for an ASCII character, and a character other than ASCII counts as the most bytes it can take.
770
+ */
771
+ function cutIdentifier(name, maxBytes) {
772
+ if (name.length * maxCharacterBytes <= maxBytes) return name;
773
+ let bytes = 0;
774
+ for (let index = 0; index < name.length;) {
775
+ const code = name.codePointAt(index);
776
+ bytes += code < 128 ? 1 : maxCharacterBytes;
777
+ if (bytes > maxBytes) return name.slice(0, index);
778
+ index += code < 65536 ? 1 : 2;
779
+ }
780
+ return name;
781
+ }
782
+ /**
783
+ * Returns the SQL name of the projection with `key` at `index` of its query.
784
+ *
785
+ * A key of up to 50 bytes is the name itself.
786
+ * A longer key is named by its prefix of at most 54 bytes, which takes more than 50 since a character takes at most 4, and by its position, so the name differs from the names of the other projections.
787
+ */
788
+ function nameProjection(key, index) {
789
+ if (cutIdentifier(key, 50).length === key.length) return key;
790
+ return `${cutIdentifier(key, 54)}_${index + 1}`;
791
+ }
792
+ // src/compiler/postgres/context.ts
793
+ /** Most parameters that PostgreSQL takes in a statement. */
794
+ const maxParameters = 65535;
795
+ /** State of one compilation shared by all queries of the statement, never by reusable nodes: parameters and the aliases of all sources. */
796
+ var CompileContext = class {
797
+ schemaName;
798
+ tables;
799
+ db;
800
+ parameters = [];
801
+ #aliases = /* @__PURE__ */ new Set();
802
+ constructor(schemaName, tables = void 0, db = void 0) {
803
+ this.schemaName = schemaName;
804
+ this.tables = tables;
805
+ this.db = db;
446
806
  }
447
807
  /**
448
808
  * Creates an alias based on `name` that is unique in the statement, so that no source shadows another one in SQL.
@@ -471,9 +831,11 @@ function needsParentheses(kind, position) {
471
831
  case "param":
472
832
  case "boolean":
473
833
  case "values":
834
+ case "nestedQuery":
474
835
  case "tableName":
475
836
  case "subquery":
476
837
  case "exists":
838
+ case "textSubquery":
477
839
  case "aggregate": return false;
478
840
  case "cast": return position === "interpolation";
479
841
  case "raw": return position !== "interpolation";
@@ -482,6 +844,7 @@ function needsParentheses(kind, position) {
482
844
  case "not":
483
845
  case "comparison":
484
846
  case "in":
847
+ case "inQuery":
485
848
  case "isNull": return position !== "condition";
486
849
  default: return kind;
487
850
  }
@@ -534,6 +897,7 @@ function compileStatement(node, options = {}, tables, db) {
534
897
  text: compileQuery(context, void 0, node),
535
898
  parameters: context.parameters,
536
899
  fields: node.projections,
900
+ recordTemplate: node.recordTemplate,
537
901
  rows: node.rows,
538
902
  value: node.value,
539
903
  counted: void 0,
@@ -557,6 +921,7 @@ function compileStatement(node, options = {}, tables, db) {
557
921
  text: compileMutation(context, node),
558
922
  parameters: context.parameters,
559
923
  fields: returning ?? noFields,
924
+ recordTemplate: node.recordTemplate,
560
925
  rows: node.rows,
561
926
  value: node.value,
562
927
  counted: returning === void 0 ? "number" : returning.length === 0 ? "records" : void 0,
@@ -709,12 +1074,18 @@ function compileExpression(context, scope, node) {
709
1074
  case "or": return node.operands.map((operand) => compileOperand(context, scope, operand, "condition")).join(node.kind === "and" ? " AND " : " OR ");
710
1075
  case "not": return `NOT ${compileOperand(context, scope, node.operand, "condition")}`;
711
1076
  case "values": throw new Error("An object condition can only be used in `where`");
1077
+ case "nestedQuery": throw new Error("A query in an expression must be lowered before compilation");
712
1078
  case "comparison": return `${compileOperand(context, scope, node.left, "value")} ${node.operator} ${compileOperand(context, scope, node.right, "value")}`;
713
1079
  case "in": {
714
1080
  let text = `${compileOperand(context, scope, node.operand, "value")} IN (${compileOperand(context, scope, node.list[0], "value")}`;
715
1081
  for (let index = 1; index < node.list.length; index++) text += `, ${compileOperand(context, scope, node.list[index], "value")}`;
716
1082
  return `${text})`;
717
1083
  }
1084
+ case "inQuery": {
1085
+ const { query } = node;
1086
+ if (query.kind === "nestedQuery") throw new Error("A query in an expression must be lowered before compilation");
1087
+ return `${compileOperand(context, scope, node.operand, "value")} IN ${compileSubquery(context, scope, query, "A subquery of IN")}`;
1088
+ }
718
1089
  case "isNull": return `${compileOperand(context, scope, node.operand, "value")} IS NULL`;
719
1090
  case "raw": {
720
1091
  let text = node.fragments[0];
@@ -722,12 +1093,9 @@ function compileExpression(context, scope, node) {
722
1093
  return text;
723
1094
  }
724
1095
  case "tableName": return compileInterpolatedTable(context, node);
725
- case "subquery": {
726
- const { length } = node.query.projections;
727
- if (length !== 1) throw new Error(`A scalar subquery must select one field, not ${length}`);
728
- return `(${compileQuery(context, scope, node.query)})`;
729
- }
1096
+ case "subquery": return compileSubquery(context, scope, node.query, "A scalar subquery");
730
1097
  case "exists": return `EXISTS (${compileQuery(context, scope, node.query)})`;
1098
+ case "textSubquery": return `(${compileQuery(context, scope, node.query)})`;
731
1099
  case "aggregate": {
732
1100
  let text = `${node.name}(${node.distinct ? "DISTINCT " : ""}`;
733
1101
  for (let index = 0; index < node.arguments.length; index++) text += `${index === 0 ? "" : ", "}${compileExpression(context, scope, node.arguments[index])}`;
@@ -737,6 +1105,12 @@ function compileExpression(context, scope, node) {
737
1105
  default: return node;
738
1106
  }
739
1107
  }
1108
+ /** Compiles `query` in parentheses as a subquery of one field, and rejects a query of another number of fields, naming the subquery as `subject`. */
1109
+ function compileSubquery(context, scope, query, subject) {
1110
+ const { length } = query.projections;
1111
+ if (length !== 1) throw new Error(`${subject} must select one field, not ${length}`);
1112
+ return `(${compileQuery(context, scope, query)})`;
1113
+ }
740
1114
  /** Encodes the value of a parameter node with its type, or by its JavaScript type without one. */
741
1115
  function encodeParameter(node) {
742
1116
  if (node.value === void 0) throw new TypeError("Query parameter is undefined; use null for NULL");
@@ -787,7 +1161,8 @@ function visitNodes(node, deep, visit) {
787
1161
  case "param":
788
1162
  case "boolean":
789
1163
  case "tableName":
790
- case "values": return;
1164
+ case "values":
1165
+ case "nestedQuery": return;
791
1166
  case "cast":
792
1167
  case "not":
793
1168
  case "isNull":
@@ -805,11 +1180,16 @@ function visitNodes(node, deep, visit) {
805
1180
  visitNodes(node.operand, deep, visit);
806
1181
  for (const item of node.list) visitNodes(item, deep, visit);
807
1182
  return;
1183
+ case "inQuery":
1184
+ visitNodes(node.operand, deep, visit);
1185
+ if (deep && node.query.kind === "select") visitQuery(node.query, visit);
1186
+ return;
808
1187
  case "raw":
809
1188
  for (const value of node.values) visitNodes(value, deep, visit);
810
1189
  return;
811
1190
  case "subquery":
812
1191
  case "exists":
1192
+ case "textSubquery":
813
1193
  if (deep) visitQuery(node.query, visit);
814
1194
  return;
815
1195
  case "aggregate":
@@ -834,170 +1214,6 @@ function visitQuery(query, visit) {
834
1214
  if (query.limit !== void 0) visitNodes(query.limit, true, visit);
835
1215
  if (query.offset !== void 0) visitNodes(query.offset, true, visit);
836
1216
  }
837
- // src/query/aggregate.ts
838
- /** Type of a count: a `bigint` of PostgreSQL decoded as a number, rejecting a count that a number cannot hold exactly. */
839
- const countType = /* @__PURE__ */ defineType({ sqlType: "bigint" }).transform({
840
- decode: (text) => decodeSafeInteger(text, "Count"),
841
- encode: (value) => String(value)
842
- });
843
- /** Types of the sums of the values of `smallint` and `integer` types, by the type of the values, created once for each. */
844
- const sumTypes = /* @__PURE__ */ new WeakMap();
845
- const noFallbacks = /* @__PURE__ */ Object.freeze([]);
846
- /** Definitions of the aggregate methods that take a column or an expression. */
847
- const aggregateDefinitions = /* @__PURE__ */ Object.freeze({
848
- count: {
849
- name: "count",
850
- distinct: false,
851
- ordered: false,
852
- type: toCountType
853
- },
854
- countDistinct: {
855
- name: "count",
856
- distinct: true,
857
- ordered: false,
858
- type: toCountType
859
- },
860
- min: {
861
- name: "min",
862
- distinct: false,
863
- ordered: false,
864
- type: toArgumentType
865
- },
866
- max: {
867
- name: "max",
868
- distinct: false,
869
- ordered: false,
870
- type: toArgumentType
871
- },
872
- sum: {
873
- name: "sum",
874
- distinct: false,
875
- ordered: false,
876
- type: toSumType
877
- },
878
- avg: {
879
- name: "avg",
880
- distinct: false,
881
- ordered: false,
882
- type: toAverageType
883
- },
884
- every: {
885
- name: "bool_and",
886
- distinct: false,
887
- ordered: false,
888
- type: toArgumentType
889
- },
890
- some: {
891
- name: "bool_or",
892
- distinct: false,
893
- ordered: false,
894
- type: toArgumentType
895
- },
896
- stringAgg: {
897
- name: "string_agg",
898
- distinct: false,
899
- ordered: true,
900
- type: toArgumentType
901
- }
902
- });
903
- /** Projection of `count(*)`, which `count` without an argument selects; its value does not depend on the values or the order of the rows. */
904
- const countProjection = /* @__PURE__ */ Object.freeze({
905
- key: "count",
906
- expression: /* @__PURE__ */ createNode({
907
- kind: "aggregate",
908
- name: "count",
909
- distinct: false,
910
- arguments: [/* @__PURE__ */ createNode({
911
- kind: "raw",
912
- fragments: ["*"],
913
- values: []
914
- })],
915
- orderBy: []
916
- }),
917
- type: countType,
918
- ordered: false,
919
- fallbacks: noFallbacks
920
- });
921
- /**
922
- * Creates the projection of the aggregate of `method` over the values of `argument`, the projection of a column or an expression of each row.
923
- *
924
- * `delimiter` is the delimiter of `stringAgg`, sent as a parameter.
925
- */
926
- function projectAggregate(method, argument, delimiter) {
927
- const definition = aggregateDefinitions[method];
928
- const args = [argument.expression];
929
- if (delimiter !== void 0) args.push(createNode({
930
- kind: "param",
931
- value: delimiter,
932
- type: void 0
933
- }));
934
- return Object.freeze({
935
- key: method,
936
- expression: createNode({
937
- kind: "aggregate",
938
- name: definition.name,
939
- distinct: definition.distinct,
940
- arguments: args,
941
- orderBy: []
942
- }),
943
- type: definition.type(argument.type),
944
- ordered: definition.ordered,
945
- fallbacks: noFallbacks
946
- });
947
- }
948
- /** Creates the projection of the aggregate of `field` whose `NULL` `fallback` replaces after its earlier fallbacks. */
949
- function addFallback(field, fallback) {
950
- const fallbacks = field.fallbacks.slice();
951
- fallbacks.push(fallback);
952
- return Object.freeze({
953
- key: field.key,
954
- expression: field.expression,
955
- type: field.type,
956
- ordered: field.ordered,
957
- fallbacks
958
- });
959
- }
960
- /** Tells whether `node` is an aggregate query, whose value is the aggregate over the rows that its filters, limit, and offset choose. */
961
- function isAggregateQuery(node) {
962
- return node.value && isAggregateProjection(node.projections[0]);
963
- }
964
- /** Tells whether a selected field is the projection of an aggregate, which only the aggregate methods select. */
965
- function isAggregateProjection(field) {
966
- return "ordered" in field;
967
- }
968
- function toCountType() {
969
- return countType;
970
- }
971
- function toArgumentType(argument) {
972
- return argument;
973
- }
974
- /** Returns the type of the sum of the values of type `argument`: for `smallint` and `integer` values, `argument` that rejects a sum that a number cannot hold exactly, and `argument` for others. */
975
- function toSumType(argument) {
976
- if (argument.castType !== "smallint" && argument.castType !== "integer") return argument;
977
- let type = sumTypes.get(argument);
978
- if (type === void 0) {
979
- type = defineType({ sqlType: "bigint" }).transform({
980
- decode: (text) => {
981
- decodeSafeInteger(text, "Sum");
982
- return argument.decode(text);
983
- },
984
- encode: (value) => argument.encode(value)
985
- });
986
- sumTypes.set(argument, type);
987
- }
988
- return type;
989
- }
990
- /** Returns the type of the average of the values of type `argument`, which decodes a fractional value where PostgreSQL gives one, such as `Number` of `integer`; rejects a type of the SQL type `bigint`. */
991
- function toAverageType(argument) {
992
- if (argument.castType === "bigint") throw new TypeError("avg of bigint values requires a type that can represent fractional values; average a sql fragment of the column with t.doublePrecision or t.numeric");
993
- return argument;
994
- }
995
- /** Decodes `text` of a `bigint` as a number, rejecting one that a number cannot hold exactly; `subject` names it in the error. */
996
- function decodeSafeInteger(text, subject) {
997
- const number = Number(text);
998
- if (!Number.isSafeInteger(number)) throw new RangeError(`${subject} ${text} is outside the safe integer range of numbers`);
999
- return number;
1000
- }
1001
1217
  // src/query/rows.ts
1002
1218
  /** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
1003
1219
  function getRelationRows(relation) {
@@ -1043,6 +1259,10 @@ function isTableQuery(node) {
1043
1259
  function isNestedSelection(node) {
1044
1260
  return "query" in node;
1045
1261
  }
1262
+ /** Tells whether a selected field is a literal. */
1263
+ function isLiteralSelection(node) {
1264
+ return "literal" in node;
1265
+ }
1046
1266
  // src/schema/relation.ts
1047
1267
  /** Runtime mark of the declarations of `many` and `one`. */
1048
1268
  const declarationBrand = Symbol("kern-orm.relation");
@@ -1331,7 +1551,8 @@ function packRows(query) {
1331
1551
  fields: projections,
1332
1552
  rows: query.rows,
1333
1553
  value: query.value,
1334
- guaranteedBy: query.guaranteedBy
1554
+ guaranteedBy: query.guaranteedBy,
1555
+ recordTemplate: query.recordTemplate
1335
1556
  });
1336
1557
  const projection = Object.freeze({
1337
1558
  key: "rows",
@@ -1370,85 +1591,176 @@ function createRef(source, key) {
1370
1591
  // src/lowering/lower.ts
1371
1592
  const noJoins = /* @__PURE__ */ Object.freeze([]);
1372
1593
  const noOrder = /* @__PURE__ */ Object.freeze([]);
1594
+ const noProjections = /* @__PURE__ */ Object.freeze([]);
1373
1595
  function lowerStatement(node, definitions, db) {
1596
+ const context = {
1597
+ definitions,
1598
+ db
1599
+ };
1374
1600
  switch (node.kind) {
1375
- case "query": return lowerSelect(node, definitions, db, true);
1376
- case "record": return lowerRecord(node, definitions, db);
1377
- case "text": return node;
1378
- default: return lowerMutation(node, definitions, db);
1601
+ case "query": return lowerSelect(node, context, true);
1602
+ case "record": return lowerRecord(node, context);
1603
+ case "text": return lowerText(node, context);
1604
+ default: return lowerMutation(node, context);
1379
1605
  }
1380
1606
  }
1381
- /** Lowers a mutation to its statement, 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. */
1382
- function lowerMutation(node, definitions, db) {
1383
- const { returning } = node;
1384
- const lowered = returning === void 0 ? void 0 : lowerProjections(returning, definitions, db);
1385
- if (lowered === returning) return node;
1386
- const { target, rows, value } = node;
1607
+ /** Lowers a statement of SQL text, replacing the queries in the expressions interpolated into it. */
1608
+ function lowerText(node, context) {
1609
+ const sql = lowerExpression(node.sql, context);
1610
+ if (sql === node.sql) return node;
1611
+ return Object.freeze({
1612
+ kind: "text",
1613
+ sql,
1614
+ fields: node.fields,
1615
+ rows: node.rows,
1616
+ value: node.value
1617
+ });
1618
+ }
1619
+ /** 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. */
1620
+ function lowerMutation(node, context) {
1621
+ const { target, returning, rows, value } = node;
1622
+ const recordTemplate = returning === void 0 ? void 0 : createRecordTemplate(returning);
1623
+ const lowered = returning === void 0 ? void 0 : lowerProjections(dropLiterals(returning), context);
1387
1624
  switch (node.kind) {
1388
- case "insert": return Object.freeze({
1389
- kind: "insert",
1390
- target,
1391
- columns: node.columns,
1392
- values: node.values,
1393
- returning: lowered,
1394
- rows,
1395
- value
1396
- });
1397
- case "update": return Object.freeze({
1398
- kind: "update",
1399
- target,
1400
- set: node.set,
1401
- filters: node.filters,
1402
- returning: lowered,
1403
- rows,
1404
- value
1405
- });
1406
- case "delete": return Object.freeze({
1407
- kind: "delete",
1408
- target,
1409
- filters: node.filters,
1410
- returning: lowered,
1411
- rows,
1412
- value
1413
- });
1625
+ case "insert": {
1626
+ const values = lowerEach(node.values, context, lowerRow);
1627
+ if (lowered === returning && values === node.values) return node;
1628
+ return Object.freeze({
1629
+ kind: "insert",
1630
+ target,
1631
+ columns: node.columns,
1632
+ values,
1633
+ returning: lowered,
1634
+ recordTemplate,
1635
+ rows,
1636
+ value
1637
+ });
1638
+ }
1639
+ case "update": {
1640
+ const set = lowerEach(node.set, context, lowerAssignment);
1641
+ const filters = lowerFilters(node.filters, context);
1642
+ if (lowered === returning && set === node.set && filters === node.filters) return node;
1643
+ return Object.freeze({
1644
+ kind: "update",
1645
+ target,
1646
+ set,
1647
+ filters,
1648
+ returning: lowered,
1649
+ recordTemplate,
1650
+ rows,
1651
+ value
1652
+ });
1653
+ }
1654
+ case "delete": {
1655
+ const filters = lowerFilters(node.filters, context);
1656
+ if (lowered === returning && filters === node.filters) return node;
1657
+ return Object.freeze({
1658
+ kind: "delete",
1659
+ target,
1660
+ filters,
1661
+ returning: lowered,
1662
+ recordTemplate,
1663
+ rows,
1664
+ value
1665
+ });
1666
+ }
1414
1667
  default: return node;
1415
1668
  }
1416
1669
  }
1670
+ /** Lowers a row of `VALUES` of an insert, whose `undefined` stands for `DEFAULT`. */
1671
+ function lowerRow(row, context) {
1672
+ return lowerEach(row, context, lowerValue);
1673
+ }
1674
+ function lowerValue(node, context) {
1675
+ return node === void 0 ? void 0 : lowerExpression(node, context);
1676
+ }
1677
+ function lowerAssignment(assignment, context) {
1678
+ const value = lowerExpression(assignment.value, context);
1679
+ return value === assignment.value ? assignment : Object.freeze({
1680
+ key: assignment.key,
1681
+ value
1682
+ });
1683
+ }
1417
1684
  /** Lowers a query as `lowerQuery` does, or as `lowerStatement` does when `positions` is set. */
1418
- function lowerSelect(node, definitions, db, positions) {
1685
+ function lowerSelect(node, context, positions) {
1686
+ if (isExistsQuery(node)) return selectExists(node, context);
1687
+ checkRelation(node, context);
1688
+ if (isAggregateQuery(node)) return lowerAggregate(node, context);
1689
+ const recordTemplate = createRecordTemplate(node.projections);
1690
+ const selected = dropLiterals(node.projections);
1691
+ const projections = lowerProjections(selected, context);
1692
+ const filters = lowerFilters(node.filters, context);
1693
+ const orderBy = lowerOrder(node.orderBy, context, selected, projections);
1419
1694
  const { relation } = node;
1420
- 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`);
1421
- if (isAggregateQuery(node)) return lowerAggregate(node);
1422
- const projections = lowerProjections(node.projections, definitions, db);
1423
1695
  const guaranteedBy = node.guaranteed && relation !== void 0 ? formatRelationName(relation.key, relation.name) : void 0;
1424
- const page = node.offset !== void 0 && projections !== node.projections ? lowerPage(node, projections, positions, guaranteedBy) : void 0;
1696
+ 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;
1425
1697
  if (page !== void 0) return page;
1426
1698
  return Object.freeze({
1427
1699
  kind: "select",
1428
1700
  from: node.from,
1429
1701
  joins: noJoins,
1430
1702
  projections,
1431
- filters: node.filters,
1432
- orderBy: positions && node.orderBy.length > 0 ? sortByPositions(node.orderBy, projections) : node.orderBy,
1703
+ filters,
1704
+ orderBy: positions && orderBy.length > 0 ? sortByPositions(orderBy, projections) : orderBy,
1433
1705
  limit: node.limit,
1434
1706
  offset: node.offset,
1435
1707
  rows: node.rows,
1436
1708
  value: node.value,
1437
- guaranteedBy
1709
+ guaranteedBy,
1710
+ recordTemplate
1438
1711
  });
1439
1712
  }
1713
+ /** Returns the fields of `selected` that a statement selects, or `selected` itself without literals. */
1714
+ function dropLiterals(selected) {
1715
+ if (!selected.some(isLiteralSelection)) return selected;
1716
+ const fields = [];
1717
+ for (const field of selected) if (!isLiteralSelection(field)) fields.push(field);
1718
+ return fields;
1719
+ }
1720
+ /** Creates the template of the records of `selected`: every field in its order with the value of a literal, or `undefined` without literals. */
1721
+ function createRecordTemplate(selected) {
1722
+ if (!selected.some(isLiteralSelection)) return;
1723
+ const template = [];
1724
+ for (const field of selected) template.push(Object.freeze({
1725
+ key: field.key,
1726
+ value: isLiteralSelection(field) ? field.literal : void 0
1727
+ }));
1728
+ return template;
1729
+ }
1730
+ /**
1731
+ * 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.
1732
+ *
1733
+ * The query evaluates a field that it sorts by, of an aggregate or of `exists`, for every row anyway.
1734
+ */
1735
+ function isPagedField(field, projection, orderBy) {
1736
+ if (!isNestedSelection(field)) return false;
1737
+ const { expression } = projection;
1738
+ return !orderBy.some((order) => order.expression === expression);
1739
+ }
1740
+ /** Rejects a query of a relation outside the definitions of `context`. */
1741
+ function checkRelation(node, context) {
1742
+ const { relation } = node;
1743
+ const { definitions } = context;
1744
+ 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`);
1745
+ }
1746
+ /** Rejects a nested query bound to `db`, which the error names as `subject`, unless `db` is the `Db` of `context` or `undefined`. */
1747
+ function checkDb(db, context, subject) {
1748
+ 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`);
1749
+ }
1440
1750
  /**
1441
- * Lowers `node` with `projections` to a statement that evaluates its nested queries over the rows of a derived page.
1751
+ * 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.
1752
+ * `queryFields` are the fields of the query without literals, and `projections` their lowered projections in the same order.
1442
1753
  * The derived source applies the filters, the order, the limit, and the offset, and replaces the occurrence of the table.
1443
1754
  *
1444
- * It returns `undefined` when a nested query holds a SQL fragment that refers to a column of the table outside the queries nested in the fragment.
1755
+ * 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.
1445
1756
  * Such a fragment may hold an aggregate of the query, which the limit and the offset apply after.
1446
1757
  *
1447
1758
  * 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.
1759
+ * 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.
1448
1760
  * 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(*)`.
1449
1761
  * 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.
1450
1762
  */
1451
- function lowerPage(node, projections, positions, guaranteedBy) {
1763
+ function lowerPage(node, queryFields, projections, filters, orderBy, positions, guaranteedBy, recordTemplate) {
1452
1764
  const { from } = node;
1453
1765
  const columnKeys = /* @__PURE__ */ new Set();
1454
1766
  const fields = [];
@@ -1456,7 +1768,7 @@ function lowerPage(node, projections, positions, guaranteedBy) {
1456
1768
  for (let index = 0; index < projections.length; index++) {
1457
1769
  const { expression } = projections[index];
1458
1770
  let key;
1459
- if (isNestedSelection(node.projections[index])) {
1771
+ if (isPagedField(queryFields[index], projections[index], orderBy)) {
1460
1772
  if (holdsFragmentOfSource(expression, from)) return;
1461
1773
  collectColumnKeys(expression, from, columnKeys);
1462
1774
  } else if (expression.kind === "column" && expression.source === from) columnKeys.add(expression.key);
@@ -1486,8 +1798,8 @@ function lowerPage(node, projections, positions, guaranteedBy) {
1486
1798
  from,
1487
1799
  joins: noJoins,
1488
1800
  projections: sourced,
1489
- filters: node.filters,
1490
- orderBy: node.orderBy,
1801
+ filters,
1802
+ orderBy,
1491
1803
  limit: node.limit,
1492
1804
  offset: node.offset,
1493
1805
  rows: "many",
@@ -1519,17 +1831,19 @@ function lowerPage(node, projections, positions, guaranteedBy) {
1519
1831
  offset: void 0,
1520
1832
  rows: node.rows,
1521
1833
  value: node.value,
1522
- guaranteedBy
1834
+ guaranteedBy,
1835
+ recordTemplate
1523
1836
  });
1524
1837
  }
1525
1838
  /** Lowers an aggregate query to a statement of the value of its aggregate over the rows that its filters, limit, and offset choose. */
1526
- function lowerAggregate(node) {
1839
+ function lowerAggregate(node, context) {
1527
1840
  const field = node.projections[0];
1528
1841
  const aggregate = field.expression;
1842
+ const orderBy = lowerOrder(node.orderBy, context);
1529
1843
  let from = node.from;
1530
- let filters = node.filters;
1531
- let args = aggregate.arguments;
1532
- let order = field.ordered ? node.orderBy : noOrder;
1844
+ let filters = lowerFilters(node.filters, context);
1845
+ let args = lowerEach(aggregate.arguments, context, lowerExpression);
1846
+ let order = field.ordered ? orderBy : noOrder;
1533
1847
  const counted = aggregate === countProjection.expression;
1534
1848
  if (node.limit !== void 0 || node.offset !== void 0 || !counted && !refersToSource(args[0], node.from)) {
1535
1849
  const projections = [];
@@ -1543,8 +1857,8 @@ function lowerAggregate(node) {
1543
1857
  from: node.from,
1544
1858
  joins: noJoins,
1545
1859
  projections,
1546
- filters: node.filters,
1547
- orderBy: counted ? noOrder : node.orderBy,
1860
+ filters,
1861
+ orderBy: counted ? noOrder : orderBy,
1548
1862
  limit: node.limit,
1549
1863
  offset: node.offset,
1550
1864
  rows: "many",
@@ -1571,7 +1885,7 @@ function lowerAggregate(node) {
1571
1885
  arguments: args,
1572
1886
  orderBy: order
1573
1887
  });
1574
- const { fallbacks } = field;
1888
+ const fallbacks = lowerEach(field.fallbacks, context, lowerExpression);
1575
1889
  let value = call;
1576
1890
  if (fallbacks.length > 0) {
1577
1891
  const values = [call];
@@ -1607,12 +1921,15 @@ function refersToSource(node, source) {
1607
1921
  case "param":
1608
1922
  case "boolean":
1609
1923
  case "values":
1924
+ case "nestedQuery":
1610
1925
  case "tableName":
1611
1926
  case "subquery":
1612
- case "exists": return false;
1927
+ case "exists":
1928
+ case "textSubquery": return false;
1613
1929
  case "cast":
1614
1930
  case "not":
1615
- case "isNull": return refersToSource(node.operand, source);
1931
+ case "isNull":
1932
+ case "inQuery": return refersToSource(node.operand, source);
1616
1933
  case "and":
1617
1934
  case "or": return node.operands.some((operand) => refersToSource(operand, source));
1618
1935
  case "comparison": return refersToSource(node.left, source) || refersToSource(node.right, source);
@@ -1622,40 +1939,102 @@ function refersToSource(node, source) {
1622
1939
  default: return node;
1623
1940
  }
1624
1941
  }
1942
+ /** Lowers a query of `exists` to `EXISTS` of the rows that its filters, limit, and offset choose. */
1943
+ function lowerExists(node, context) {
1944
+ checkRelation(node, context);
1945
+ return createNode({
1946
+ kind: "exists",
1947
+ query: Object.freeze({
1948
+ kind: "select",
1949
+ from: node.from,
1950
+ joins: noJoins,
1951
+ projections: noProjections,
1952
+ filters: lowerFilters(node.filters, context),
1953
+ orderBy: noOrder,
1954
+ limit: node.limit,
1955
+ offset: node.offset,
1956
+ rows: "many",
1957
+ value: false,
1958
+ guaranteedBy: void 0
1959
+ })
1960
+ });
1961
+ }
1962
+ /** Lowers a query of `exists` to a statement without a source of one row of `EXISTS` of its rows. */
1963
+ function selectExists(node, context) {
1964
+ return Object.freeze({
1965
+ kind: "select",
1966
+ from: void 0,
1967
+ joins: noJoins,
1968
+ projections: [projectExists(node.projections[0].key, node, context)],
1969
+ filters: noFilters,
1970
+ orderBy: noOrder,
1971
+ limit: void 0,
1972
+ offset: void 0,
1973
+ rows: node.rows,
1974
+ value: true,
1975
+ guaranteedBy: void 0
1976
+ });
1977
+ }
1978
+ /** 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`. */
1979
+ function projectExists(key, node, context) {
1980
+ return Object.freeze({
1981
+ key,
1982
+ expression: lowerExists(node, context),
1983
+ type: node.projections[0].type
1984
+ });
1985
+ }
1625
1986
  /** Lowers a query of `$select` to a statement without a source, which returns one row of its fields. */
1626
- function lowerRecord(node, definitions, db) {
1987
+ function lowerRecord(node, context) {
1627
1988
  return Object.freeze({
1628
1989
  kind: "select",
1629
1990
  from: void 0,
1630
1991
  joins: noJoins,
1631
- projections: lowerProjections(node.projections, definitions, db),
1992
+ projections: lowerProjections(dropLiterals(node.projections), context),
1632
1993
  filters: noFilters,
1633
1994
  orderBy: noOrder,
1634
1995
  limit: void 0,
1635
1996
  offset: void 0,
1636
1997
  rows: "one",
1637
1998
  value: false,
1638
- guaranteedBy: void 0
1999
+ guaranteedBy: void 0,
2000
+ recordTemplate: createRecordTemplate(node.projections)
1639
2001
  });
1640
2002
  }
1641
2003
  /**
1642
- * 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; checks them as `lowerQuery` does.
2004
+ * 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.
1643
2005
  *
1644
- * Without nested queries, it returns `selected` itself.
2006
+ * Without queries, it returns `selected` itself.
1645
2007
  */
1646
- function lowerProjections(selected, definitions, db) {
2008
+ function lowerProjections(selected, context) {
1647
2009
  let lowered;
1648
2010
  for (let index = 0; index < selected.length; index++) {
1649
2011
  const field = selected[index];
2012
+ let projection;
1650
2013
  if (isNestedSelection(field)) {
1651
- if (field.db !== void 0 && field.db !== db) throw new Error(db === void 0 ? `Selected field "${field.key}" is bound to a Db, so only that Db can compile the query` : `Selected field "${field.key}" is bound to another Db`);
1652
- lowered ??= selected.slice(0, index);
1653
- const query = lowerSelect(field.query, definitions, db, false);
1654
- lowered.push(isAggregateQuery(field.query) ? projectAggregateValue(field.key, query) : projectRows(field.key, query));
1655
- } else if (lowered !== void 0) lowered.push(field);
2014
+ checkDb(field.db, context, `Selected field "${field.key}"`);
2015
+ projection = projectQuery(field.key, field.query, context);
2016
+ } else {
2017
+ const expression = lowerExpression(field.expression, context);
2018
+ projection = expression === field.expression ? field : Object.freeze({
2019
+ key: field.key,
2020
+ expression,
2021
+ type: field.type
2022
+ });
2023
+ }
2024
+ if (lowered !== void 0) lowered.push(projection);
2025
+ else if (projection !== field) {
2026
+ lowered = selected.slice(0, index);
2027
+ lowered.push(projection);
2028
+ }
1656
2029
  }
1657
2030
  return lowered ?? selected;
1658
2031
  }
2032
+ /** 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. */
2033
+ function projectQuery(key, node, context) {
2034
+ if (isExistsQuery(node)) return projectExists(key, node, context);
2035
+ const query = lowerSelect(node, context, false);
2036
+ return isAggregateQuery(node) ? projectAggregateValue(key, query) : projectRows(key, query);
2037
+ }
1659
2038
  /** 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. */
1660
2039
  function projectAggregateValue(key, query) {
1661
2040
  return Object.freeze({
@@ -1667,508 +2046,230 @@ function projectAggregateValue(key, query) {
1667
2046
  type: query.projections[0].type
1668
2047
  });
1669
2048
  }
1670
- // src/query/kind.ts
1671
- /** `then` of every bound query, which executes the query on its `Db`. */
1672
- function executeOnAwait(onFulfilled, onRejected) {
1673
- return this.db.$execute(this).then(onFulfilled, onRejected);
2049
+ /** Lowers the filters of a query, replacing the queries in their expressions; without queries, it returns `filters` itself. */
2050
+ function lowerFilters(filters, context) {
2051
+ const { relation, scope, user } = filters;
2052
+ const loweredRelation = relation === void 0 ? void 0 : lowerExpression(relation, context);
2053
+ const loweredScope = scope === void 0 ? void 0 : lowerExpression(scope, context);
2054
+ const loweredUser = user === void 0 ? void 0 : lowerExpression(user, context);
2055
+ if (loweredRelation === relation && loweredScope === scope && loweredUser === user) return filters;
2056
+ const lowered = {};
2057
+ if (loweredRelation !== void 0) lowered.relation = loweredRelation;
2058
+ if (loweredScope !== void 0) lowered.scope = loweredScope;
2059
+ if (loweredUser !== void 0) lowered.user = loweredUser;
2060
+ return Object.freeze(lowered);
1674
2061
  }
1675
- /** Compiles `query` as its `toSQL` does: an unbound query with `options`, and a bound query as its `Db` executes it, which takes no options. */
1676
- function compileToSQL(query, options) {
1677
- const { db } = query;
1678
- if (db === void 0) {
1679
- const { text, parameters } = compileStatement(lowerStatement(query.node), options);
1680
- return createStatement(text, parameters);
2062
+ /**
2063
+ * Lowers the sort keys of a query, replacing the queries in their expressions; without queries, it returns `orderBy` itself.
2064
+ *
2065
+ * 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.
2066
+ * A field of an aggregate or of `exists` is identified by the node of its reference, which holds the field.
2067
+ */
2068
+ function lowerOrder(orderBy, context, selected, projections) {
2069
+ let lowered;
2070
+ for (let index = 0; index < orderBy.length; index++) {
2071
+ const order = orderBy[index];
2072
+ const { expression } = order;
2073
+ const position = selected === void 0 || projections === void 0 || projections === selected ? -1 : selected.findIndex((field) => isFieldValue(field, expression));
2074
+ const sorted = position === -1 ? lowerExpression(expression, context) : projections[position].expression;
2075
+ if (lowered === void 0 && sorted === expression) continue;
2076
+ lowered ??= orderBy.slice(0, index);
2077
+ lowered.push(sorted === expression ? order : Object.freeze({
2078
+ expression: sorted,
2079
+ direction: order.direction,
2080
+ nulls: order.nulls
2081
+ }));
1681
2082
  }
1682
- if (options !== void 0) throw new Error("A query bound to a Db compiles with the options of its Db");
1683
- return db.$compile(query);
1684
- }
1685
- // src/query/column-refs.ts
1686
- /** Context of a query whose table has no relations, such as one of `from(table)`. */
1687
- const noContext = /* @__PURE__ */ Object.freeze({ relations: /* @__PURE__ */ Object.freeze({}) });
1688
- /** Creates expressions of the columns of `source`, each with the type of its column. */
1689
- function createColumnRefs(source) {
1690
- return addColumns({}, source);
2083
+ return lowered ?? orderBy;
1691
2084
  }
1692
- /** Creates the references of the callback of `select` over `source`: its columns, and the relations of `context`. */
1693
- function createSelectionRefs(source, context) {
1694
- return Object.defineProperties(addColumns({}, source), context.relations);
1695
- }
1696
- function addColumns(refs, source) {
1697
- const { columns } = source.table;
1698
- for (const key of Object.keys(columns)) refs[key] = toExpression(createNode({
1699
- kind: "column",
1700
- source,
1701
- key
1702
- }), columns[key].type);
1703
- return refs;
2085
+ /** 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`. */
2086
+ function isFieldValue(field, expression) {
2087
+ return isNestedSelection(field) ? expression.kind === "nestedQuery" && expression.field === field : field.expression === expression;
1704
2088
  }
1705
- // src/query/resolve-values.ts
1706
2089
  /**
1707
- * Replaces objects of column values in a predicate with conditions on the columns of `source`.
2090
+ * Lowers an expression, replacing each query in it and checking the query as a nested query.
2091
+ * 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.
1708
2092
  *
1709
- * Groups are rebuilt, so constants that appear after resolution, such as an empty object, are simplified.
2093
+ * It returns `node` itself when it holds no query, and creates only the nodes that hold one.
1710
2094
  */
1711
- function resolveValues(node, source) {
2095
+ function lowerExpression(node, context) {
1712
2096
  switch (node.kind) {
1713
- case "values": {
1714
- const conditions = [];
1715
- for (const key of Object.keys(node.values)) {
1716
- const value = node.values[key];
1717
- const type = findColumn(source.table, key)?.type;
1718
- const column = createNode({
1719
- kind: "column",
1720
- source,
1721
- key
1722
- });
1723
- if (value === null) conditions.push(createIsNullNode(column));
1724
- else if (type) conditions.push(matchValue(source, column, type, value));
1725
- else conditions.push(compareNodes("=", column, value, void 0));
1726
- }
1727
- return combineNodes("and", conditions);
2097
+ case "column":
2098
+ case "param":
2099
+ case "boolean":
2100
+ case "values":
2101
+ case "tableName":
2102
+ case "subquery":
2103
+ case "exists":
2104
+ case "textSubquery": return node;
2105
+ case "nestedQuery": return lowerNestedQuery(node, context);
2106
+ case "cast": {
2107
+ const operand = lowerExpression(node.operand, context);
2108
+ return operand === node.operand ? node : createNode({
2109
+ kind: "cast",
2110
+ operand,
2111
+ type: node.type
2112
+ });
2113
+ }
2114
+ case "not": {
2115
+ const operand = lowerExpression(node.operand, context);
2116
+ return operand === node.operand ? node : createNode({
2117
+ kind: "not",
2118
+ operand
2119
+ });
2120
+ }
2121
+ case "isNull": {
2122
+ const operand = lowerExpression(node.operand, context);
2123
+ return operand === node.operand ? node : createNode({
2124
+ kind: "isNull",
2125
+ operand
2126
+ });
1728
2127
  }
1729
2128
  case "and":
1730
2129
  case "or": {
1731
- const operands = [];
1732
- for (const operand of node.operands) operands.push(resolveValues(operand, source));
1733
- return combineNodes(node.kind, operands);
2130
+ const operands = lowerEach(node.operands, context, lowerExpression);
2131
+ return operands === node.operands ? node : createNode({
2132
+ kind: node.kind,
2133
+ operands
2134
+ });
2135
+ }
2136
+ case "comparison": {
2137
+ const left = lowerExpression(node.left, context);
2138
+ const right = lowerExpression(node.right, context);
2139
+ return left === node.left && right === node.right ? node : createNode({
2140
+ kind: "comparison",
2141
+ operator: node.operator,
2142
+ left,
2143
+ right
2144
+ });
2145
+ }
2146
+ case "in": {
2147
+ const operand = lowerExpression(node.operand, context);
2148
+ const list = lowerEach(node.list, context, lowerExpression);
2149
+ return operand === node.operand && list === node.list ? node : createNode({
2150
+ kind: "in",
2151
+ operand,
2152
+ list
2153
+ });
2154
+ }
2155
+ case "inQuery": {
2156
+ const operand = lowerExpression(node.operand, context);
2157
+ const query = node.query.kind === "nestedQuery" ? lowerQueryOfValues(node.query, context) : node.query;
2158
+ return operand === node.operand && query === node.query ? node : createNode({
2159
+ kind: "inQuery",
2160
+ operand,
2161
+ query
2162
+ });
2163
+ }
2164
+ case "raw": {
2165
+ const values = lowerEach(node.values, context, lowerExpression);
2166
+ return values === node.values ? node : createNode({
2167
+ kind: "raw",
2168
+ fragments: node.fragments,
2169
+ values
2170
+ });
2171
+ }
2172
+ case "aggregate": {
2173
+ const args = lowerEach(node.arguments, context, lowerExpression);
2174
+ const orderBy = lowerOrder(node.orderBy, context);
2175
+ return args === node.arguments && orderBy === node.orderBy ? node : createNode({
2176
+ kind: "aggregate",
2177
+ name: node.name,
2178
+ distinct: node.distinct,
2179
+ arguments: args,
2180
+ orderBy
2181
+ });
1734
2182
  }
1735
- case "not": return negateNode(resolveValues(node.operand, source));
1736
- case "raw": return createNode({
1737
- ...node,
1738
- values: node.values.map((value) => resolveValues(value, source))
1739
- });
1740
2183
  default: return node;
1741
2184
  }
1742
2185
  }
1743
- /** Matches a column with a value of an object condition by calling the `eq` operator of its type, whichever operator it is. */
1744
- function matchValue(source, column, type, value) {
1745
- const expression = toExpression(column, type);
1746
- 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`);
1747
- const predicate = expression.eq(value);
1748
- if (!isExpression(predicate)) throw new TypeError(`The eq operator of column "${column.key}" of table "${source.table.name}" must return an expression`);
1749
- return toNode(predicate);
1750
- }
1751
- // src/query/filters.ts
1752
- /** Creates query filters with the filter of `where` over `source` combined with `AND` with the user filters of `filters`. */
1753
- function addUserFilter(filters, filter, source) {
1754
- const predicate = resolveValues(toConditionNode(typeof filter === "function" ? filter(createColumnRefs(source)) : filter), source);
1755
- const { user } = filters;
1756
- return Object.freeze({
1757
- ...filters,
1758
- user: user ? combineNodes("and", [user, predicate]) : predicate
2186
+ /**
2187
+ * 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.
2188
+ *
2189
+ * 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.
2190
+ */
2191
+ function lowerNestedQuery(node, context) {
2192
+ checkDb(node.db, context, "A query in an expression");
2193
+ const { query } = node;
2194
+ if (isExistsQuery(query)) return lowerExists(query, context);
2195
+ return createNode({
2196
+ kind: query.value && query.rows !== "many" ? "subquery" : "textSubquery",
2197
+ query: lowerSelect(query, context, false)
1759
2198
  });
1760
2199
  }
1761
- function defineFilter(_table, filter) {
1762
- return filter;
2200
+ /** Lowers the query of the values of `IN` to its statement, whatever its rows. */
2201
+ function lowerQueryOfValues(node, context) {
2202
+ checkDb(node.db, context, "A query in an expression");
2203
+ return lowerSelect(node.query, context, false);
1763
2204
  }
1764
- // src/query/find.ts
1765
- /**
1766
- * 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.
1767
- *
1768
- * It rejects a table without a primary key, a number of values other than the number of its columns, and `null` and `undefined`.
1769
- */
1770
- function toPrimaryKeyValues(table, values) {
1771
- const { primaryKey } = table.keys;
1772
- 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`);
1773
- 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}`);
1774
- const filter = {};
1775
- for (let index = 0; index < primaryKey.length; index++) {
1776
- const column = primaryKey[index];
1777
- filter[column] = requireKeyValue(table, column, values[index], "find");
1778
- }
1779
- return filter;
1780
- }
1781
- /**
1782
- * Returns the object of column values that `findBy` filters a query of `table` by: a copy of `values`, read once.
1783
- *
1784
- * It rejects columns that are not exactly a key of `table`, primary or unique, as `isKey` tells, and `null` and `undefined`.
1785
- */
1786
- function toKeyValues(table, values) {
1787
- if (typeof values !== "object" || values === null) throw new TypeError(`findBy takes an object of the columns of a key of table "${table.name}"`);
1788
- const columns = Object.keys(values);
1789
- if (!isKey(table, columns)) {
1790
- const listed = columns.length === 0 ? "an object without columns" : `columns ${quoteColumns(columns)}`;
1791
- throw new Error(`findBy takes the columns of one key of table "${table.name}", primary or unique, not ${listed}`);
1792
- }
1793
- const filter = {};
1794
- for (const column of columns) {
1795
- const value = values[column];
1796
- filter[column] = requireKeyValue(table, column, value, "findBy");
1797
- }
1798
- return filter;
1799
- }
1800
- /** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
1801
- function requireKeyValue(table, column, value, method) {
1802
- 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`);
1803
- return value;
1804
- }
1805
- /** Lists `columns` in quotes, separated by commas. */
1806
- function quoteColumns(columns) {
1807
- return columns.map((column) => `"${column}"`).join(", ");
1808
- }
1809
- // src/query/order.ts
1810
- /** Direction and placement of `NULL` of a sort key node, by `OrderDirection`. */
1811
- const directions = /* @__PURE__ */ Object.freeze({
1812
- asc: {
1813
- direction: "asc",
1814
- nulls: void 0
1815
- },
1816
- desc: {
1817
- direction: "desc",
1818
- nulls: void 0
1819
- },
1820
- "asc nulls first": {
1821
- direction: "asc",
1822
- nulls: "first"
1823
- },
1824
- "asc nulls last": {
1825
- direction: "asc",
1826
- nulls: "last"
1827
- },
1828
- "desc nulls first": {
1829
- direction: "desc",
1830
- nulls: "first"
1831
- },
1832
- "desc nulls last": {
1833
- direction: "desc",
1834
- nulls: "last"
1835
- }
1836
- });
1837
- const orderingNode = Symbol("kern-orm.ordering.node");
1838
- /** Runtime class of an expression with a direction, which holds its sort key node. */
1839
- var OrderingObject = class {
1840
- [orderingNode];
1841
- constructor(node) {
1842
- this[orderingNode] = node;
1843
- Object.freeze(this);
1844
- }
1845
- };
1846
- /** Sorts by `expression` in ascending order, in the callback of `orderBy`. */
1847
- function asc(expression) {
1848
- return orderBy(expression, "asc");
1849
- }
1850
- /** Sorts by `expression` in descending order, in the callback of `orderBy`. */
1851
- function desc(expression) {
1852
- return orderBy(expression, "desc");
1853
- }
1854
- /** Sorts by `expression` in `direction`, such as `"desc nulls last"`, in the callback of `orderBy`. */
1855
- function orderBy(expression, direction) {
1856
- if (!isExpression(expression)) throw new TypeError("A sort key must be an expression");
1857
- return new OrderingObject(createOrderNode(toNode(expression), direction));
1858
- }
1859
- /**
1860
- * Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
1861
- *
1862
- * `args` are column keys and objects of columns with directions, or a callback that receives the columns and the selected fields and returns one sort key or an array of them.
1863
- */
1864
- function addOrder(node, args) {
1865
- const order = node.orderBy.slice();
1866
- const first = args[0];
1867
- if (typeof first === "function") {
1868
- const terms = first(createColumnRefs(node.from), createSelectedRefs(node));
1869
- if (Array.isArray(terms)) for (const term of terms) order.push(readTerm(term));
1870
- else order.push(readTerm(terms));
1871
- return order;
2205
+ /** Lowers each of `items` with `lower`; when none changes, it returns `items` itself. */
2206
+ function lowerEach(items, context, lower) {
2207
+ let lowered;
2208
+ for (let index = 0; index < items.length; index++) {
2209
+ const item = items[index];
2210
+ const result = lower(item, context);
2211
+ if (lowered !== void 0) lowered.push(result);
2212
+ else if (result !== item) {
2213
+ lowered = items.slice(0, index);
2214
+ lowered.push(result);
2215
+ }
1872
2216
  }
1873
- for (const arg of args) if (typeof arg === "string") order.push(createOrderNode(createColumnNode(node.from, arg), "asc"));
1874
- else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
1875
- const columns = arg;
1876
- for (const key of Object.keys(columns)) order.push(createOrderNode(createColumnNode(node.from, key), columns[key]));
1877
- } else throw new TypeError("orderBy takes column keys and objects of columns with directions, or a callback");
1878
- return order;
2217
+ return lowered ?? items;
1879
2218
  }
1880
- /** Creates the sort key node of a sort key returned by the callback of `orderBy`. */
1881
- function readTerm(term) {
1882
- if (term instanceof OrderingObject) return term[orderingNode];
1883
- if (isExpression(term)) return createOrderNode(toNode(term), "asc");
1884
- throw new TypeError("The callback of orderBy must return expressions, or sort keys of asc, desc, and orderBy");
1885
- }
1886
- /** Creates the sort key node of `expression` in `direction`, an `OrderDirection` checked at runtime. */
1887
- function createOrderNode(expression, direction) {
1888
- const placement = typeof direction === "string" && Object.hasOwn(directions, direction) ? directions[direction] : void 0;
1889
- if (placement === void 0) throw new TypeError(`Unknown sort direction ${JSON.stringify(direction)}`);
1890
- return Object.freeze({
1891
- expression,
1892
- direction: placement.direction,
1893
- nulls: placement.nulls
1894
- });
1895
- }
1896
- /** Creates a reference to the column `key` of the table of `source`. */
1897
- function createColumnNode(source, key) {
1898
- requireColumn(source.table, key);
1899
- return createNode({
1900
- kind: "column",
1901
- source,
1902
- key
1903
- });
2219
+ // src/query/kind.ts
2220
+ /** `then` of every bound query, which executes the query on its `Db`. */
2221
+ function executeOnAwait(onFulfilled, onRejected) {
2222
+ return this.db.$execute(this).then(onFulfilled, onRejected);
1904
2223
  }
1905
- /**
1906
- * Creates the references of the callback of `orderBy` to the fields selected by `node`.
1907
- *
1908
- * Each is an expression over the node of its projection, so that a sort key of it is the projection itself.
1909
- */
1910
- function createSelectedRefs(node) {
1911
- const { projections } = node;
1912
- if (node.value) return toSelectedExpression(projections[0]);
1913
- const refs = {};
1914
- for (const projection of projections) {
1915
- const { key } = projection;
1916
- if (isNestedSelection(projection)) Object.defineProperty(refs, key, {
1917
- get() {
1918
- throw new Error(`Field "${key}" holds a nested query and cannot be sorted by`);
1919
- },
1920
- enumerable: true
1921
- });
1922
- else refs[key] = toSelectedExpression(projection);
2224
+ /** Compiles `query` as its `toSQL` does: an unbound query with `options`, and a bound query as its `Db` executes it, which takes no options. */
2225
+ function compileToSQL(query, options) {
2226
+ const { db } = query;
2227
+ if (db === void 0) {
2228
+ const { text, parameters } = compileStatement(lowerStatement(query.node), options);
2229
+ return createStatement(text, parameters);
1923
2230
  }
1924
- return refs;
1925
- }
1926
- /** Creates an expression over the node of a projection of a query, which has a type: only lowering adds projections of nested queries. */
1927
- function toSelectedExpression(projection) {
1928
- return toExpression(projection.expression, projection.type);
1929
- }
1930
- // src/query/pagination.ts
1931
- /** 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. */
1932
- function createPageCount(node, method, count) {
1933
- if (node.rows !== "many") throw new Error(`${method} applies only to a query of many rows${createPaginationHint(node)}`);
1934
- if (!Number.isSafeInteger(count) || count < 0) throw new RangeError(`${method} takes a non-negative integer, not ${count}`);
1935
- return createNode({
1936
- kind: "param",
1937
- value: count,
1938
- type: void 0
1939
- });
1940
- }
1941
- /** Creates the hint of the error of `limit` or `offset` of query `node` of one row: the method to call them before, if any. */
1942
- function createPaginationHint(node) {
1943
- if (node.relation?.kind === "one") return "";
1944
- return isAggregateQuery(node) ? "; call it before count or another aggregate to aggregate the rows of a page" : "; call it before take or get";
2231
+ if (options !== void 0) throw new Error("A query bound to a Db compiles with the options of its Db");
2232
+ return db.$compile(query);
1945
2233
  }
1946
- // src/query/select.ts
2234
+ // src/query/record.ts
1947
2235
  /**
1948
- * Immutable select query of state `S`, unbound or bound to a `Db`.
2236
+ * Immutable query of `$select` of state `S`, unbound or bound to a `Db`: one record whose fields are the values of expressions and of literals and the results of independent queries, selected in one statement.
1949
2237
  *
1950
- * Every method returns a query of the same kind and leaves this query unchanged.
1951
2238
  * An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
1952
2239
  * A bound query executes on its `Db` when awaited.
1953
2240
  */
1954
- var SelectQuery = class SelectQuery {
1955
- table;
2241
+ var RecordQuery = class {
1956
2242
  node;
1957
- context;
1958
2243
  /** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
1959
2244
  db;
1960
2245
  /** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
1961
2246
  then;
1962
- constructor(table, node, context, db) {
1963
- this.table = table;
2247
+ constructor(node, db) {
1964
2248
  this.node = node;
1965
- this.context = context;
1966
2249
  this.db = db;
1967
2250
  this.then = db === void 0 ? void 0 : executeOnAwait;
1968
2251
  Object.freeze(this);
1969
2252
  }
1970
- where(filter) {
1971
- return this.derive(this.addFilter(filter));
1972
- }
1973
- select(...args) {
1974
- this.rejectValue("select");
1975
- return this.derive({ projections: mergeProjections(this.node.projections, this.node.from, args, this.context) });
1976
- }
1977
- clearSelect() {
1978
- this.rejectValue("clearSelect");
1979
- return this.derive({ projections: [] });
1980
- }
1981
- take() {
1982
- return this.deriveTaken({});
1983
- }
1984
- find(...values) {
1985
- return this.takeWhere(toPrimaryKeyValues(this.table, values));
1986
- }
1987
- findBy(values) {
1988
- return this.takeWhere(toKeyValues(this.table, values));
1989
- }
1990
- get(arg) {
1991
- this.rejectAggregate("get");
1992
- return this.deriveTaken({
1993
- projections: [projectValue(this.node.from, arg, "get")],
1994
- value: true,
1995
- rows: toValueRows(this.node.rows)
1996
- });
1997
- }
1998
- pluck(arg) {
1999
- this.rejectAggregate("pluck");
2000
- return this.derive({
2001
- projections: [projectValue(this.node.from, arg, "pluck")],
2002
- value: true,
2003
- rows: toValueRows(this.node.rows)
2004
- });
2005
- }
2006
- count(arg) {
2007
- if (arg !== void 0) return this.aggregateBy("count", arg);
2008
- this.rejectAggregate("count");
2009
- return this.deriveAggregate(countProjection);
2010
- }
2011
- countDistinct(arg) {
2012
- return this.aggregateBy("countDistinct", arg);
2013
- }
2014
- min(arg) {
2015
- return this.aggregateBy("min", arg);
2016
- }
2017
- max(arg) {
2018
- return this.aggregateBy("max", arg);
2019
- }
2020
- sum(arg) {
2021
- return this.aggregateBy("sum", arg);
2022
- }
2023
- avg(arg) {
2024
- return this.aggregateBy("avg", arg);
2025
- }
2026
- every(arg) {
2027
- return this.aggregateBy("every", arg);
2028
- }
2029
- some(arg) {
2030
- return this.aggregateBy("some", arg);
2031
- }
2032
- stringAgg(arg, delimiter) {
2033
- return this.aggregateBy("stringAgg", arg, delimiter);
2034
- }
2035
- coalesce(fallback) {
2036
- const { node } = this;
2037
- if (!node.value) throw new Error("coalesce applies only to a query of values, such as one made by get, pluck, or an aggregate");
2038
- return this.derive({ projections: [coalesceValue(node.projections[0], fallback)] });
2039
- }
2040
- orderBy(...args) {
2041
- return this.derive({ orderBy: addOrder(this.node, args) });
2042
- }
2043
- limit(count) {
2044
- return this.derive({ limit: createPageCount(this.node, "limit", count) });
2045
- }
2046
- offset(count) {
2047
- return this.derive({ offset: createPageCount(this.node, "offset", count) });
2048
- }
2049
- optional() {
2050
- return this.changeOneRow("optional", "optional");
2051
- }
2052
- nullable() {
2053
- if (this.node.value) throw new Error("nullable applies only to a query of records; use optional after get, pluck, or an aggregate");
2054
- return this.changeOneRow("nullable", "nullable");
2055
- }
2056
- required() {
2057
- return this.changeOneRow("required", "one");
2058
- }
2059
- insert(values) {
2060
- return createInsert(this, [values], "one", "insert");
2061
- }
2062
- insertMany(records) {
2063
- if (!Array.isArray(records)) throw new TypeError("insertMany takes an array of records");
2064
- return createInsert(this, records, "many", "insertMany");
2065
- }
2066
- update(set) {
2067
- return createUpdate(this, set);
2068
- }
2069
- delete() {
2070
- return createDelete(this);
2071
- }
2072
- all() {
2073
- if (this.node.rows !== "many") throw new Error("all applies only to a query of many rows");
2074
- return this.derive({ all: true });
2075
- }
2076
- use(callback, ...args) {
2077
- return requireDerived(this, callback(this, ...args), "The callback of use");
2078
- }
2079
2253
  toSQL(options) {
2080
2254
  return compileToSQL(this, options);
2081
2255
  }
2082
- /** Creates a query of the same kind and `Db` with the node changes applied. */
2083
- derive(changes) {
2084
- return new SelectQuery(this.table, Object.freeze({
2085
- ...this.node,
2086
- ...changes
2087
- }), this.context, this.db);
2088
- }
2089
- /**
2090
- * Creates a query of the same kind and `Db` with the node changes applied and then those of `take`, in one derived query.
2091
- *
2092
- * It adds the changes of `take` to `changes`, which must leave the limit as it is.
2093
- */
2094
- deriveTaken(changes) {
2095
- if ((changes.rows ?? this.node.rows) === "many") {
2096
- changes.limit = limitToOne(this.node.limit);
2097
- changes.rows = "one";
2098
- }
2099
- return this.derive(changes);
2100
- }
2101
- /** Creates the query of the value of `projection`, an aggregate over the rows of this query, which always returns one row. */
2102
- deriveAggregate(projection) {
2103
- return this.derive({
2104
- projections: [projection],
2105
- rows: "one",
2106
- value: true,
2107
- guaranteed: false
2108
- });
2109
- }
2110
- /** Creates the query of the aggregate of `method` over `arg`, a column key or a callback that receives the columns and returns an expression; `delimiter` is the delimiter of `stringAgg`. */
2111
- aggregateBy(method, arg, delimiter) {
2112
- this.rejectAggregate(method);
2113
- return this.deriveAggregate(projectAggregate(method, projectValue(this.node.from, arg, method), delimiter));
2114
- }
2115
- /** Returns the changes of the node that add `filter` with `AND`, ending the guarantee of the row as `FilteredState` does in types. */
2116
- addFilter(filter) {
2117
- const { node } = this;
2118
- return {
2119
- filters: addUserFilter(node.filters, filter, node.from),
2120
- rows: toFilteredRows(node.rows, node.value, node.guaranteed),
2121
- guaranteed: false
2122
- };
2123
- }
2124
- /** Creates the query of `find` and `findBy`: `where` of `values`, an object of column values, returning one row like `take`. */
2125
- takeWhere(values) {
2126
- const changes = this.addFilter(values);
2127
- if (changes.rows === "many") changes.rows = "one";
2128
- return this.derive(changes);
2129
- }
2130
- /** Creates the query of one row with rows mode `rows` that `method` returns, rejecting a query of many rows. */
2131
- changeOneRow(method, rows) {
2132
- if (this.node.rows === "many") throw new Error(`${method} applies only to a query of one row, such as one made by take`);
2133
- return this.derive({
2134
- rows,
2135
- guaranteed: false
2136
- });
2137
- }
2138
- /** Rejects a change of the selection by `method` of a query made by `get`, `pluck`, or an aggregate method. */
2139
- rejectValue(method) {
2140
- if (!this.node.value) return;
2141
- this.rejectAggregate(method);
2142
- throw new Error(`${method} cannot be used after get or pluck; call get or pluck to replace the value`);
2143
- }
2144
- /** Rejects `method`, which replaces the value or the selection, on a query made by an aggregate method. */
2145
- rejectAggregate(method) {
2146
- if (isAggregateQuery(this.node)) throw new Error(`${method} cannot be used after an aggregate`);
2256
+ /** Rejects the query as an operand of an expression, which takes a query of one value of a table. */
2257
+ [operandNode]() {
2258
+ 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 $select");
2147
2259
  }
2148
2260
  };
2149
2261
  /**
2150
- * Returns `result`, which a callback named by `subject` returned for `query`, after checking that it is `query` or a query derived from it.
2262
+ * Starts the query of `$select` of `selection`, an object of expressions, queries, and literals by key, bound to `db` when given.
2151
2263
  *
2152
- * An ancestor of `query` also passes, since only the source occurrence is compared.
2264
+ * It reads the keys and values of `selection` when it is called.
2153
2265
  */
2154
- function requireDerived(query, result, subject) {
2155
- 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`);
2156
- return result;
2157
- }
2158
- /** Binds a query to `db`, so that awaiting it executes it there. */
2159
- function bindQuery(query, db) {
2160
- return new SelectQuery(query.table, query.node, query.context, db);
2161
- }
2162
- /** Starts a query over a new occurrence of the table. */
2163
- function from(table) {
2164
- return startQuery(table, noContext);
2165
- }
2166
- /** Starts a query over a new occurrence of the table with the relations of `context`, such as a query of a table of a schema. */
2167
- function startQuery(table, context) {
2168
- return new SelectQuery(table, createQueryNode(Object.freeze({
2169
- kind: "table",
2170
- table
2171
- })), context);
2266
+ function startRecordQuery(selection, db) {
2267
+ const projections = [];
2268
+ addSelection(projections, selection);
2269
+ return new RecordQuery(Object.freeze({
2270
+ kind: "record",
2271
+ projections
2272
+ }), db);
2172
2273
  }
2173
2274
  // src/query/sql-query.ts
2174
2275
  /**
@@ -2230,6 +2331,10 @@ var SqlQuery = class SqlQuery {
2230
2331
  toSQL() {
2231
2332
  return this.db.$compile(this);
2232
2333
  }
2334
+ /** Rejects the query as an operand of an expression, which takes a query of one value of a table. */
2335
+ [operandNode]() {
2336
+ 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 $query");
2337
+ }
2233
2338
  /** Creates a query of the same `Db` with the node changes applied. */
2234
2339
  derive(changes) {
2235
2340
  return new SqlQuery(Object.freeze({
@@ -2265,10 +2370,112 @@ function startSqlQuery(sql, db) {
2265
2370
  value: false
2266
2371
  }), db);
2267
2372
  }
2268
- /** Returns `value` that `what` takes as its type, rejecting a value that is not a type, such as a column. */
2269
- function requireType(value, what) {
2270
- if (!isType(value)) throw new TypeError(`${what} takes a type, such as t.integer, not a column or another value`);
2271
- return value;
2373
+ /** Returns `value` that `what` takes as its type, rejecting a value that is not a type, such as a column. */
2374
+ function requireType(value, what) {
2375
+ if (!isType(value)) throw new TypeError(`${what} takes a type, such as t.integer, not a column or another value`);
2376
+ return value;
2377
+ }
2378
+ // src/query/projections.ts
2379
+ /**
2380
+ * Creates the fields of `select` over `source`, appending new keys and replacing existing ones in place.
2381
+ *
2382
+ * `args` are the arguments of `select`: `"*"`, column keys, or a callback that receives the references of `source` with the relations of `context` and returns a selection.
2383
+ */
2384
+ function mergeProjections(projections, source, args, context) {
2385
+ const result = projections.slice();
2386
+ const first = args[0];
2387
+ if (typeof first === "function") addSelection(result, first(createSelectionRefs(source, context)));
2388
+ else {
2389
+ const keys = first === "*" ? Object.keys(source.table.columns) : args;
2390
+ for (const key of keys) setProjection(result, projectColumn(source, key));
2391
+ }
2392
+ return result;
2393
+ }
2394
+ /** Adds the fields of `selection`, expressions, queries, and literals by key, to `projections`, replacing existing keys in place. */
2395
+ function addSelection(projections, selection) {
2396
+ for (const key of Reflect.ownKeys(selection)) {
2397
+ if (typeof key === "symbol") throw new TypeError("A selected field must have a string key");
2398
+ setProjection(projections, selectValue(key, selection[key]));
2399
+ }
2400
+ }
2401
+ /**
2402
+ * Creates the projection of the value of `get` or `pluck`, or of the argument of an aggregate method, such as `max`, over `source`.
2403
+ *
2404
+ * `arg` is the argument of `method`: a column key, or a callback that receives the columns and returns an expression.
2405
+ */
2406
+ function projectValue(source, arg, method) {
2407
+ if (typeof arg !== "function") return projectColumn(source, arg);
2408
+ const value = arg(createColumnRefs(source));
2409
+ if (!isExpression(value)) throw new TypeError(`The callback of ${method} must return an expression`);
2410
+ const type = getType(value);
2411
+ if (!type) throw new TypeError(`The expression of ${method} has no type to decode its values; build a sql fragment with a type, such as sql(t.integer)`);
2412
+ return projectExpression("value", value, type);
2413
+ }
2414
+ /**
2415
+ * Creates the projection of the value of `field`, the only projection of a query of values, whose `NULL` `fallback` replaces: a value that the type of the field encodes, an expression, or a query of one value.
2416
+ *
2417
+ * The projection of an aggregate keeps its call, which lowering builds, and adds the fallback after its earlier ones.
2418
+ * The projection of `exists` stays as it is, since `EXISTS` is never `NULL`.
2419
+ */
2420
+ function coalesceValue(field, fallback) {
2421
+ const value = toFunctionOperandNode(fallback, field.type, "coalesce");
2422
+ if (isExistsProjection(field)) return field;
2423
+ if (isAggregateProjection(field)) return addFallback(field, value);
2424
+ return Object.freeze({
2425
+ key: field.key,
2426
+ expression: createList("coalesce(", [field.expression, value], ")"),
2427
+ type: field.type
2428
+ });
2429
+ }
2430
+ /** Creates the field of a value of a selection under `key`: an expression with a type, a query whose rows become its value, or a literal. */
2431
+ function selectValue(key, value) {
2432
+ if (value instanceof Literal) return Object.freeze({
2433
+ key,
2434
+ literal: value.literal
2435
+ });
2436
+ if (value instanceof SelectQuery) return Object.freeze({
2437
+ key,
2438
+ query: value.node,
2439
+ db: value.db
2440
+ });
2441
+ if (value instanceof RecordQuery) throw new TypeError(`Selected field "${key}" is a query of $select, which cannot be nested; select its fields directly instead`);
2442
+ if (value instanceof MutationQuery) throw new TypeError(`Selected field "${key}" is a query of insert, update, or delete, which cannot be nested`);
2443
+ if (value instanceof SqlQuery) throw new TypeError(`Selected field "${key}" is a query of $query, which cannot be nested`);
2444
+ if (!isExpression(value)) throw new TypeError(`Selected field "${key}" must be an expression, a query, or a literal`);
2445
+ const type = getType(value);
2446
+ if (!type) throw new TypeError(`Selected field "${key}" has no type to decode its values; build a sql fragment with a type, such as sql(t.integer)`);
2447
+ return projectExpression(key, value, type);
2448
+ }
2449
+ /** Creates the projection of a column of the table of `source` under its key. */
2450
+ function projectColumn(source, key) {
2451
+ const { type } = requireColumn(source.table, key);
2452
+ return Object.freeze({
2453
+ key,
2454
+ expression: createNode({
2455
+ kind: "column",
2456
+ source,
2457
+ key
2458
+ }),
2459
+ type
2460
+ });
2461
+ }
2462
+ /** Creates the projection of an expression under `key`, decoded with `type`, the type of the expression. */
2463
+ function projectExpression(key, value, type) {
2464
+ return Object.freeze({
2465
+ key,
2466
+ expression: toNode(value),
2467
+ type
2468
+ });
2469
+ }
2470
+ /** Adds or replaces a field by key, rejecting a key that a record cannot have. */
2471
+ function setProjection(projections, projection) {
2472
+ const { key } = projection;
2473
+ if (!isRecordKey(key)) throw new Error(`Field "${key}" cannot be selected: it is not a valid record key`);
2474
+ for (let index = 0; index < projections.length; index++) if (projections[index].key === key) {
2475
+ projections[index] = projection;
2476
+ return;
2477
+ }
2478
+ projections.push(projection);
2272
2479
  }
2273
2480
  // src/query/values.ts
2274
2481
  /**
@@ -2374,6 +2581,10 @@ var MutationQuery = class MutationQuery {
2374
2581
  toSQL(options) {
2375
2582
  return compileToSQL(this, options);
2376
2583
  }
2584
+ /** Rejects the query as an operand of an expression, which takes a query of one value of a table. */
2585
+ [operandNode]() {
2586
+ 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 insert, update, or delete");
2587
+ }
2377
2588
  /** Creates a mutation of the same kind and `Db` with the node changes applied. */
2378
2589
  derive(changes) {
2379
2590
  return new MutationQuery(this.table, Object.freeze({
@@ -2449,138 +2660,481 @@ function checkChangedRows(node, method) {
2449
2660
  const predicate = combineFilters(node.filters);
2450
2661
  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`);
2451
2662
  }
2452
- // src/query/projections.ts
2453
- /**
2454
- * Creates the fields of `select` over `source`, appending new keys and replacing existing ones in place.
2455
- *
2456
- * `args` are the arguments of `select`: `"*"`, column keys, or a callback that receives the references of `source` with the relations of `context` and returns a selection.
2457
- */
2458
- function mergeProjections(projections, source, args, context) {
2459
- const result = projections.slice();
2460
- const first = args[0];
2461
- if (typeof first === "function") addSelection(result, first(createSelectionRefs(source, context)));
2462
- else {
2463
- const keys = first === "*" ? Object.keys(source.table.columns) : args;
2464
- for (const key of keys) setProjection(result, projectColumn(source, key));
2663
+ // src/query/order.ts
2664
+ /** Direction and placement of `NULL` of a sort key node, by `OrderDirection`. */
2665
+ const directions = /* @__PURE__ */ Object.freeze({
2666
+ asc: {
2667
+ direction: "asc",
2668
+ nulls: void 0
2669
+ },
2670
+ desc: {
2671
+ direction: "desc",
2672
+ nulls: void 0
2673
+ },
2674
+ "asc nulls first": {
2675
+ direction: "asc",
2676
+ nulls: "first"
2677
+ },
2678
+ "asc nulls last": {
2679
+ direction: "asc",
2680
+ nulls: "last"
2681
+ },
2682
+ "desc nulls first": {
2683
+ direction: "desc",
2684
+ nulls: "first"
2685
+ },
2686
+ "desc nulls last": {
2687
+ direction: "desc",
2688
+ nulls: "last"
2465
2689
  }
2466
- return result;
2467
- }
2468
- /** Adds the fields of `selection`, expressions and queries by key, to `projections`, replacing existing keys in place. */
2469
- function addSelection(projections, selection) {
2470
- for (const key of Reflect.ownKeys(selection)) {
2471
- if (typeof key === "symbol") throw new TypeError("A selected field must have a string key");
2472
- setProjection(projections, selectValue(key, selection[key]));
2690
+ });
2691
+ const orderingNode = Symbol("kern-orm.ordering.node");
2692
+ /** Runtime class of an expression with a direction, which holds its sort key node. */
2693
+ var OrderingObject = class {
2694
+ [orderingNode];
2695
+ constructor(node) {
2696
+ this[orderingNode] = node;
2697
+ Object.freeze(this);
2473
2698
  }
2699
+ };
2700
+ /** Sorts by `value`, an expression or a query of one value, in ascending order, in the callback of `orderBy`. */
2701
+ function asc(value) {
2702
+ return orderBy(value, "asc");
2703
+ }
2704
+ /** Sorts by `value`, an expression or a query of one value, in descending order, in the callback of `orderBy`. */
2705
+ function desc(value) {
2706
+ return orderBy(value, "desc");
2707
+ }
2708
+ /** Sorts by `value`, an expression or a query of one value, in `direction`, such as `"desc nulls last"`, in the callback of `orderBy`. */
2709
+ function orderBy(value, direction) {
2710
+ if (!isOperandSource(value)) throw new TypeError("A sort key must be an expression or a query of one value");
2711
+ return new OrderingObject(createOrderNode(value[operandNode](), direction));
2474
2712
  }
2475
2713
  /**
2476
- * Creates the projection of the value of `get` or `pluck`, or of the argument of an aggregate method, such as `max`, over `source`.
2714
+ * Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
2477
2715
  *
2478
- * `arg` is the argument of `method`: a column key, or a callback that receives the columns and returns an expression.
2716
+ * `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.
2479
2717
  */
2480
- function projectValue(source, arg, method) {
2481
- if (typeof arg !== "function") return projectColumn(source, arg);
2482
- const value = arg(createColumnRefs(source));
2483
- if (!isExpression(value)) throw new TypeError(`The callback of ${method} must return an expression`);
2484
- const type = getType(value);
2485
- if (!type) throw new TypeError(`The expression of ${method} has no type to decode its values; build a sql fragment with a type, such as sql(t.integer)`);
2486
- return projectExpression("value", value, type);
2718
+ function addOrder(node, context, args) {
2719
+ const order = node.orderBy.slice();
2720
+ const first = args[0];
2721
+ if (typeof first === "function") {
2722
+ const terms = first(createSelectionRefs(node.from, context), createSelectedRefs(node));
2723
+ if (Array.isArray(terms)) for (const term of terms) order.push(readTerm(term));
2724
+ else order.push(readTerm(terms));
2725
+ return order;
2726
+ }
2727
+ for (const arg of args) if (typeof arg === "string") order.push(createOrderNode(createColumnNode(node.from, arg), "asc"));
2728
+ else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
2729
+ const columns = arg;
2730
+ for (const key of Object.keys(columns)) order.push(createOrderNode(createColumnNode(node.from, key), columns[key]));
2731
+ } else throw new TypeError("orderBy takes column keys and objects of columns with directions, or a callback");
2732
+ return order;
2487
2733
  }
2488
2734
  /**
2489
- * Creates the projection of the value of `field`, the only projection of a query of values, whose `NULL` `fallback` replaces: a value that the type of the field encodes, or an expression.
2735
+ * Creates the sort key node of a sort key returned by the callback of `orderBy`.
2490
2736
  *
2491
- * The projection of an aggregate keeps its call, which lowering builds, and adds the fallback after its earlier ones.
2737
+ * A query is converted by the method of `operandNode`, which rejects a query other than of one value.
2492
2738
  */
2493
- function coalesceValue(field, fallback) {
2494
- if (fallback === null || fallback === void 0) throw new TypeError(`coalesce takes a value other than ${String(fallback)}`);
2495
- const value = toValueNode(fallback, field.type);
2496
- if (isAggregateProjection(field)) return addFallback(field, value);
2739
+ function readTerm(term) {
2740
+ if (term instanceof OrderingObject) return term[orderingNode];
2741
+ if (isOperandSource(term)) return createOrderNode(term[operandNode](), "asc");
2742
+ throw new TypeError("The callback of orderBy must return expressions, queries of one value, or sort keys of asc, desc, and orderBy");
2743
+ }
2744
+ /** Creates the sort key node of `expression` in `direction`, an `OrderDirection` checked at runtime. */
2745
+ function createOrderNode(expression, direction) {
2746
+ const placement = typeof direction === "string" && Object.hasOwn(directions, direction) ? directions[direction] : void 0;
2747
+ if (placement === void 0) throw new TypeError(`Unknown sort direction ${JSON.stringify(direction)}`);
2497
2748
  return Object.freeze({
2498
- key: field.key,
2499
- expression: createList("coalesce(", [field.expression, value], ")"),
2500
- type: field.type
2749
+ expression,
2750
+ direction: placement.direction,
2751
+ nulls: placement.nulls
2501
2752
  });
2502
2753
  }
2503
- /** Creates the field of a value of a selection under `key`: an expression with a type, or a query whose rows become its value. */
2504
- function selectValue(key, value) {
2505
- if (value instanceof SelectQuery) return Object.freeze({
2506
- key,
2507
- query: value.node,
2508
- db: value.db
2754
+ /** Creates a reference to the column `key` of the table of `source`. */
2755
+ function createColumnNode(source, key) {
2756
+ requireColumn(source.table, key);
2757
+ return createNode({
2758
+ kind: "column",
2759
+ source,
2760
+ key
2509
2761
  });
2510
- if (value instanceof RecordQuery) throw new TypeError(`Selected field "${key}" is a query of $select, which cannot be nested; select its fields directly instead`);
2511
- if (value instanceof MutationQuery) throw new TypeError(`Selected field "${key}" is a query of insert, update, or delete, which cannot be nested`);
2512
- if (value instanceof SqlQuery) throw new TypeError(`Selected field "${key}" is a query of $query, which cannot be nested`);
2513
- if (!isExpression(value)) throw new TypeError(`Selected field "${key}" must be an expression or a query`);
2514
- const type = getType(value);
2515
- if (!type) throw new TypeError(`Selected field "${key}" has no type to decode its values; build a sql fragment with a type, such as sql(t.integer)`);
2516
- return projectExpression(key, value, type);
2517
2762
  }
2518
- /** Creates the projection of a column of the table of `source` under its key. */
2519
- function projectColumn(source, key) {
2520
- const { type } = requireColumn(source.table, key);
2521
- return Object.freeze({
2522
- key,
2523
- expression: createNode({
2524
- kind: "column",
2525
- source,
2526
- key
2527
- }),
2528
- type
2529
- });
2763
+ /**
2764
+ * Creates the references of the callback of `orderBy` to the fields selected by `node`.
2765
+ *
2766
+ * Each is an expression over the node of its projection, so that a sort key of it is the projection itself.
2767
+ * 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.
2768
+ */
2769
+ function createSelectedRefs(node) {
2770
+ const { projections } = node;
2771
+ if (node.value) return toSelectedExpression(projections[0]);
2772
+ const refs = {};
2773
+ for (const projection of projections) {
2774
+ const { key } = projection;
2775
+ if (isLiteralSelection(projection)) Object.defineProperty(refs, key, {
2776
+ get() {
2777
+ throw new Error(`Field "${key}" holds a literal and cannot be sorted by`);
2778
+ },
2779
+ enumerable: true
2780
+ });
2781
+ else if (isNestedSelection(projection)) {
2782
+ if (isSelectedAsValue(projection.query)) refs[key] = toQueryExpression(projection);
2783
+ else Object.defineProperty(refs, key, {
2784
+ get() {
2785
+ throw new Error(`Field "${key}" holds nested rows and cannot be sorted by`);
2786
+ },
2787
+ enumerable: true
2788
+ });
2789
+ } else refs[key] = toSelectedExpression(projection);
2790
+ }
2791
+ return refs;
2530
2792
  }
2531
- /** Creates the projection of an expression under `key`, decoded with `type`, the type of the expression. */
2532
- function projectExpression(key, value, type) {
2533
- return Object.freeze({
2534
- key,
2535
- expression: toNode(value),
2536
- type
2793
+ /** Creates an expression over the node of a projection of a query, which has a type: only lowering adds projections of nested queries. */
2794
+ function toSelectedExpression(projection) {
2795
+ return toExpression(projection.expression, projection.type);
2796
+ }
2797
+ /** Creates an expression of the value of `field`, a selected aggregate query or query of `exists`, with the type of its value. */
2798
+ function toQueryExpression(field) {
2799
+ const { query } = field;
2800
+ return toExpression(createNode({
2801
+ kind: "nestedQuery",
2802
+ query,
2803
+ db: field.db,
2804
+ field
2805
+ }), query.projections[0].type);
2806
+ }
2807
+ // src/query/pagination.ts
2808
+ /** 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. */
2809
+ function createPageCount(node, method, count) {
2810
+ if (node.rows !== "many") throw new Error(`${method} applies only to a query of many rows${createPaginationHint(node)}`);
2811
+ if (!Number.isSafeInteger(count) || count < 0) throw new RangeError(`${method} takes a non-negative integer, not ${count}`);
2812
+ return createNode({
2813
+ kind: "param",
2814
+ value: count,
2815
+ type: void 0
2537
2816
  });
2538
2817
  }
2539
- /** Adds or replaces a field by key, rejecting a key that a record cannot have. */
2540
- function setProjection(projections, projection) {
2541
- const { key } = projection;
2542
- if (!isRecordKey(key)) throw new Error(`Field "${key}" cannot be selected: it is not a valid record key`);
2543
- for (let index = 0; index < projections.length; index++) if (projections[index].key === key) {
2544
- projections[index] = projection;
2545
- return;
2546
- }
2547
- projections.push(projection);
2818
+ /** Creates the hint of the error of `limit` or `offset` of query `node` of one row: the method to call them before, if any. */
2819
+ function createPaginationHint(node) {
2820
+ if (node.relation?.kind === "one") return "";
2821
+ if (isAggregateQuery(node)) return "; call it before count or another aggregate to aggregate the rows of a page";
2822
+ if (isExistsQuery(node)) return "; call it before exists to check the rows of a page";
2823
+ return "; call it before take or get";
2548
2824
  }
2549
- // src/query/record.ts
2825
+ // src/query/select.ts
2550
2826
  /**
2551
- * Immutable query of `$select` of state `S`, unbound or bound to a `Db`: one record whose fields are the values of expressions and the results of independent queries, selected in one statement.
2827
+ * Immutable select query of state `S`, unbound or bound to a `Db`.
2552
2828
  *
2829
+ * Every method returns a query of the same kind and leaves this query unchanged.
2553
2830
  * An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
2554
2831
  * A bound query executes on its `Db` when awaited.
2555
2832
  */
2556
- var RecordQuery = class {
2833
+ var SelectQuery = class SelectQuery {
2834
+ table;
2557
2835
  node;
2836
+ context;
2558
2837
  /** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
2559
2838
  db;
2560
2839
  /** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
2561
2840
  then;
2562
- constructor(node, db) {
2841
+ constructor(table, node, context, db) {
2842
+ this.table = table;
2563
2843
  this.node = node;
2844
+ this.context = context;
2564
2845
  this.db = db;
2565
2846
  this.then = db === void 0 ? void 0 : executeOnAwait;
2566
2847
  Object.freeze(this);
2567
2848
  }
2849
+ where(filter) {
2850
+ return this.derive(this.addFilter(filter));
2851
+ }
2852
+ select(...args) {
2853
+ this.rejectValue("select");
2854
+ return this.derive({ projections: mergeProjections(this.node.projections, this.node.from, args, this.context) });
2855
+ }
2856
+ clearSelect() {
2857
+ this.rejectValue("clearSelect");
2858
+ return this.derive({ projections: [] });
2859
+ }
2860
+ take() {
2861
+ return this.deriveTaken({});
2862
+ }
2863
+ find(...values) {
2864
+ return this.takeWhere(toPrimaryKeyValues(this.table, values));
2865
+ }
2866
+ findBy(values) {
2867
+ return this.takeWhere(toKeyValues(this.table, values));
2868
+ }
2869
+ get(arg) {
2870
+ this.rejectAggregate("get");
2871
+ return this.deriveTaken({
2872
+ projections: [projectValue(this.node.from, arg, "get")],
2873
+ value: true,
2874
+ rows: toValueRows(this.node.rows)
2875
+ });
2876
+ }
2877
+ pluck(arg) {
2878
+ this.rejectAggregate("pluck");
2879
+ return this.derive({
2880
+ projections: [projectValue(this.node.from, arg, "pluck")],
2881
+ value: true,
2882
+ rows: toValueRows(this.node.rows)
2883
+ });
2884
+ }
2885
+ count(arg) {
2886
+ if (arg !== void 0) return this.aggregateBy("count", arg);
2887
+ this.rejectAggregate("count");
2888
+ return this.deriveAggregate(countProjection);
2889
+ }
2890
+ countDistinct(arg) {
2891
+ return this.aggregateBy("countDistinct", arg);
2892
+ }
2893
+ min(arg) {
2894
+ return this.aggregateBy("min", arg);
2895
+ }
2896
+ max(arg) {
2897
+ return this.aggregateBy("max", arg);
2898
+ }
2899
+ sum(arg) {
2900
+ return this.aggregateBy("sum", arg);
2901
+ }
2902
+ avg(arg) {
2903
+ return this.aggregateBy("avg", arg);
2904
+ }
2905
+ every(arg) {
2906
+ return this.aggregateBy("every", arg);
2907
+ }
2908
+ some(arg) {
2909
+ return this.aggregateBy("some", arg);
2910
+ }
2911
+ stringAgg(arg, delimiter) {
2912
+ return this.aggregateBy("stringAgg", arg, delimiter);
2913
+ }
2914
+ exists() {
2915
+ this.rejectAggregate("exists");
2916
+ return this.deriveAggregate(getExistsProjection());
2917
+ }
2918
+ coalesce(fallback) {
2919
+ const { node } = this;
2920
+ if (!node.value) throw new Error("coalesce applies only to a query of values, such as one made by get, pluck, or an aggregate");
2921
+ return this.derive({ projections: [coalesceValue(node.projections[0], fallback)] });
2922
+ }
2923
+ expr() {
2924
+ const value = this[operandNode]();
2925
+ return toExpression(value, this.node.projections[0].type);
2926
+ }
2927
+ orderBy(...args) {
2928
+ return this.derive({ orderBy: addOrder(this.node, this.context, args) });
2929
+ }
2930
+ limit(count) {
2931
+ return this.derive({ limit: createPageCount(this.node, "limit", count) });
2932
+ }
2933
+ offset(count) {
2934
+ return this.derive({ offset: createPageCount(this.node, "offset", count) });
2935
+ }
2936
+ optional() {
2937
+ return this.changeOneRow("optional", "optional");
2938
+ }
2939
+ nullable() {
2940
+ if (this.node.value) throw new Error("nullable applies only to a query of records; use optional after get, pluck, or an aggregate");
2941
+ return this.changeOneRow("nullable", "nullable");
2942
+ }
2943
+ required() {
2944
+ return this.changeOneRow("required", "one");
2945
+ }
2946
+ insert(values) {
2947
+ return createInsert(this, [values], "one", "insert");
2948
+ }
2949
+ insertMany(records) {
2950
+ if (!Array.isArray(records)) throw new TypeError("insertMany takes an array of records");
2951
+ return createInsert(this, records, "many", "insertMany");
2952
+ }
2953
+ update(set) {
2954
+ return createUpdate(this, set);
2955
+ }
2956
+ delete() {
2957
+ return createDelete(this);
2958
+ }
2959
+ all() {
2960
+ if (this.node.rows !== "many") throw new Error("all applies only to a query of many rows");
2961
+ return this.derive({ all: true });
2962
+ }
2963
+ use(callback, ...args) {
2964
+ return requireDerived(this, callback(this, ...args), "The callback of use");
2965
+ }
2568
2966
  toSQL(options) {
2569
2967
  return compileToSQL(this, options);
2570
2968
  }
2969
+ /**
2970
+ * Returns the node of the query as an operand of an expression, which lowering replaces with a scalar subquery of its value, or with `EXISTS` of its rows for a query of `exists`.
2971
+ *
2972
+ * It rejects a query of records or of rows `many`, which `ScalarQuery` rejects in types.
2973
+ */
2974
+ [operandNode]() {
2975
+ const { node } = this;
2976
+ 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");
2977
+ return createNode({
2978
+ kind: "nestedQuery",
2979
+ query: node,
2980
+ db: this.db
2981
+ });
2982
+ }
2983
+ /**
2984
+ * Returns the node of the query as the values of `in`, which lowering replaces with its statement.
2985
+ *
2986
+ * It rejects a query of records, which `ValuesQuery` rejects in types.
2987
+ */
2988
+ [valuesQueryNode]() {
2989
+ const { node } = this;
2990
+ if (!node.value) throw new TypeError("in takes a query of values, such as of pluck, not a query of records");
2991
+ return createNode({
2992
+ kind: "nestedQuery",
2993
+ query: node,
2994
+ db: this.db
2995
+ });
2996
+ }
2997
+ /** Creates a query of the same kind and `Db` with the node changes applied. */
2998
+ derive(changes) {
2999
+ return new SelectQuery(this.table, Object.freeze({
3000
+ ...this.node,
3001
+ ...changes
3002
+ }), this.context, this.db);
3003
+ }
3004
+ /**
3005
+ * Creates a query of the same kind and `Db` with the node changes applied and then those of `take`, in one derived query.
3006
+ *
3007
+ * It adds the changes of `take` to `changes`, which must leave the limit as it is.
3008
+ */
3009
+ deriveTaken(changes) {
3010
+ if ((changes.rows ?? this.node.rows) === "many") {
3011
+ changes.limit = limitToOne(this.node.limit);
3012
+ changes.rows = "one";
3013
+ }
3014
+ return this.derive(changes);
3015
+ }
3016
+ /** 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. */
3017
+ deriveAggregate(projection) {
3018
+ return this.derive({
3019
+ projections: [projection],
3020
+ rows: "one",
3021
+ value: true,
3022
+ guaranteed: false
3023
+ });
3024
+ }
3025
+ /** Creates the query of the aggregate of `method` over `arg`, a column key or a callback that receives the columns and returns an expression; `delimiter` is the delimiter of `stringAgg`. */
3026
+ aggregateBy(method, arg, delimiter) {
3027
+ this.rejectAggregate(method);
3028
+ return this.deriveAggregate(projectAggregate(method, projectValue(this.node.from, arg, method), delimiter));
3029
+ }
3030
+ /** Returns the changes of the node that add `filter` with `AND`, ending the guarantee of the row as `FilteredState` does in types. */
3031
+ addFilter(filter) {
3032
+ const { node } = this;
3033
+ return {
3034
+ filters: addUserFilter(node.filters, filter, node.from, this.context),
3035
+ rows: toFilteredRows(node.rows, node.value, node.guaranteed),
3036
+ guaranteed: false
3037
+ };
3038
+ }
3039
+ /** Creates the query of `find` and `findBy`: `where` of `values`, an object of column values, returning one row like `take`. */
3040
+ takeWhere(values) {
3041
+ const changes = this.addFilter(values);
3042
+ if (changes.rows === "many") changes.rows = "one";
3043
+ return this.derive(changes);
3044
+ }
3045
+ /** Creates the query of one row with rows mode `rows` that `method` returns, rejecting a query of many rows. */
3046
+ changeOneRow(method, rows) {
3047
+ if (this.node.rows === "many") throw new Error(`${method} applies only to a query of one row, such as one made by take`);
3048
+ return this.derive({
3049
+ rows,
3050
+ guaranteed: false
3051
+ });
3052
+ }
3053
+ /** Rejects a change of the selection by `method` of a query made by `get`, `pluck`, or an aggregate method. */
3054
+ rejectValue(method) {
3055
+ if (!this.node.value) return;
3056
+ this.rejectAggregate(method);
3057
+ throw new Error(`${method} cannot be used after get or pluck; call get or pluck to replace the value`);
3058
+ }
3059
+ /** Rejects `method`, which replaces the value or the selection, on a query made by an aggregate method or `exists`. */
3060
+ rejectAggregate(method) {
3061
+ const { node } = this;
3062
+ if (isAggregateQuery(node)) throw new Error(`${method} cannot be used after an aggregate`);
3063
+ if (isExistsQuery(node)) throw new Error(`${method} cannot be used after exists`);
3064
+ }
2571
3065
  };
2572
3066
  /**
2573
- * Starts the query of `$select` of `selection`, an object of expressions and queries by key, bound to `db` when given.
3067
+ * Returns `result`, which a callback named by `subject` returned for `query`, after checking that it is `query` or a query derived from it.
2574
3068
  *
2575
- * It reads the keys and values of `selection` when it is called.
3069
+ * An ancestor of `query` also passes, since only the source occurrence is compared.
2576
3070
  */
2577
- function startRecordQuery(selection, db) {
2578
- const projections = [];
2579
- addSelection(projections, selection);
2580
- return new RecordQuery(Object.freeze({
2581
- kind: "record",
2582
- projections
2583
- }), db);
3071
+ function requireDerived(query, result, subject) {
3072
+ 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`);
3073
+ return result;
3074
+ }
3075
+ /** Binds a query to `db`, so that awaiting it executes it there. */
3076
+ function bindQuery(query, db) {
3077
+ return new SelectQuery(query.table, query.node, query.context, db);
3078
+ }
3079
+ /** Starts a query over a new occurrence of the table. */
3080
+ function from(table) {
3081
+ return startQuery(table, noContext);
3082
+ }
3083
+ /** Starts a query over a new occurrence of the table with the relations of `context`, such as a query of a table of a schema. */
3084
+ function startQuery(table, context) {
3085
+ return new SelectQuery(table, createQueryNode(Object.freeze({
3086
+ kind: "table",
3087
+ table
3088
+ })), context);
3089
+ }
3090
+ // src/expression/functions.ts
3091
+ function coalesce(first, ...rest) {
3092
+ return callFunction("coalesce", first, rest);
3093
+ }
3094
+ function greatest(first, ...rest) {
3095
+ return callFunction("greatest", first, rest);
3096
+ }
3097
+ function least(first, ...rest) {
3098
+ return callFunction("least", first, rest);
3099
+ }
3100
+ function caseWhen(type, branches, otherwise) {
3101
+ if (!isType(type)) throw new TypeError("caseWhen() accepts a type, not a column");
3102
+ if (branches.length === 0) return toExpression(toCastValueNode(otherwise === void 0 ? null : otherwise, type), type);
3103
+ const fragments = [];
3104
+ const values = [];
3105
+ for (const branch of branches) {
3106
+ fragments.push(values.length === 0 ? "CASE WHEN " : " WHEN ", " THEN ");
3107
+ values.push(toCaseConditionNode(branch[0]), toCastValueNode(branch[1], type));
3108
+ }
3109
+ if (otherwise !== void 0) {
3110
+ fragments.push(" ELSE ");
3111
+ values.push(toCastValueNode(otherwise, type));
3112
+ }
3113
+ fragments.push(" END");
3114
+ return toExpression(createNode({
3115
+ kind: "raw",
3116
+ fragments,
3117
+ values
3118
+ }), type);
3119
+ }
3120
+ /** Converts a condition of `caseWhen` into a node, rejecting a value other than an expression or a query, such as an object of column values, which has no table here. */
3121
+ function toCaseConditionNode(condition) {
3122
+ if (!isOperandSource(condition)) throw new TypeError("A condition of caseWhen must be an expression or a query of one value");
3123
+ return condition[operandNode]();
3124
+ }
3125
+ /** Creates the expression of the call of the SQL function `name`, of the type of its first operand. */
3126
+ function callFunction(name, first, rest) {
3127
+ const expression = toTypedExpression(first, name);
3128
+ const type = getType(expression);
3129
+ const operands = [toNode(expression)];
3130
+ for (const value of rest) operands.push(toFunctionOperandNode(value, type, name));
3131
+ return toExpression(createList(`${name}(`, operands, ")"), type);
3132
+ }
3133
+ /** Converts the first operand of the function `name` into an expression with a type, rejecting any other value. */
3134
+ function toTypedExpression(value, name) {
3135
+ const expression = value instanceof SelectQuery ? value.expr() : value;
3136
+ if (!isExpression(expression) || getType(expression) === void 0) throw new TypeError(`The first operand of ${name} must be a query of one value of a table or an expression with a type to decode the result; build a sql fragment with a type, such as sql(t.integer)`);
3137
+ return expression;
2584
3138
  }
2585
3139
  // src/query/relations.ts
2586
3140
  /** Creates the contexts of the queries of the tables under `keys` in a schema of relations `relations`, by table key. */
@@ -2699,6 +3253,26 @@ function sql(first, ...values) {
2699
3253
  if (!isType(first)) throw new TypeError("sql() accepts a type, not a column");
2700
3254
  return (strings, ...values) => createFragment(strings, values, first);
2701
3255
  }
3256
+ /**
3257
+ * Creates a SQL fragment of a quoted name, such as of a table or a column known only at runtime, for the templates of `sql`, of `$query` and `$script` of a `Db`, and of `query` and `script` of a `Database`.
3258
+ *
3259
+ * Several names make a qualified name, such as `identifier(schema, table)` for `"schema"."table"`.
3260
+ * The fragment has no parameters, so a script takes it.
3261
+ * The name is not qualified by the PostgreSQL schema of the statement.
3262
+ */
3263
+ function identifier(...names) {
3264
+ let text = "";
3265
+ for (const name of names) {
3266
+ if (typeof name !== "string" || name === "") throw new TypeError("identifier() takes names as non-empty strings");
3267
+ text += (text === "" ? "" : ".") + quoteIdentifier(name);
3268
+ }
3269
+ if (text === "") throw new TypeError("identifier() takes at least one name");
3270
+ return toExpression(createNode({
3271
+ kind: "raw",
3272
+ fragments: [text],
3273
+ values: []
3274
+ }));
3275
+ }
2702
3276
  function param(type, value) {
2703
3277
  if (!isType(type) && !isExpression(type)) throw new TypeError("param() accepts a type or an expression, not a column");
2704
3278
  if (isExpression(type)) {
@@ -2706,12 +3280,7 @@ function param(type, value) {
2706
3280
  const valueType = getType(type);
2707
3281
  return toExpression(toValueNode(value, valueType), valueType);
2708
3282
  }
2709
- const node = toValueNode(value, type);
2710
- return toExpression(node.kind === "param" ? createNode({
2711
- kind: "cast",
2712
- operand: node,
2713
- type
2714
- }) : node, type);
3283
+ return toExpression(toCastValueNode(value, type), type);
2715
3284
  }
2716
3285
  function isTemplateStrings(value) {
2717
3286
  return Array.isArray(value);
@@ -2720,9 +3289,12 @@ function createFragment(strings, values, type) {
2720
3289
  return toExpression(toRawSqlNode(strings, values), type);
2721
3290
  }
2722
3291
  /**
2723
- * Creates the node of the SQL of a template: interpolated expressions keep their nodes, a query of a table without clauses becomes the name of the table, qualified by the PostgreSQL schema of the statement, and other values become query parameters.
3292
+ * Creates the node of the SQL of a template, whose interpolations stay structured nodes.
2724
3293
  *
2725
- * It rejects other queries, which a parameter would encode as text.
3294
+ * An interpolated expression keeps its node, and a query of a table without clauses becomes the name of the table, qualified by the PostgreSQL schema of the statement.
3295
+ * Another query of a table becomes a query in an expression, which lowering replaces with its SQL.
3296
+ * Other values become query parameters.
3297
+ * It rejects the other queries, which a parameter would encode as text.
2726
3298
  */
2727
3299
  function toRawSqlNode(strings, values) {
2728
3300
  const nodes = [];
@@ -2736,14 +3308,18 @@ function toRawSqlNode(strings, values) {
2736
3308
  /** Returns the node of a value interpolated into SQL, as `toRawSqlNode` takes it. */
2737
3309
  function toInterpolatedNode(value) {
2738
3310
  if (value instanceof SelectQuery) {
2739
- if (!isTableQuery(value.node)) throw new TypeError("sql takes a query of a table without clauses, such as db.form, as the name of the table, not a query with clauses");
2740
- return createNode({
3311
+ const { node } = value;
3312
+ return isTableQuery(node) ? createNode({
2741
3313
  kind: "tableName",
2742
3314
  table: value.table,
2743
3315
  db: value.db
3316
+ }) : createNode({
3317
+ kind: "nestedQuery",
3318
+ query: node,
3319
+ db: value.db
2744
3320
  });
2745
3321
  }
2746
- if (value instanceof RecordQuery || value instanceof MutationQuery || value instanceof SqlQuery) throw new TypeError("sql takes a query of a table without clauses, such as db.form, as the name of the table, not another query");
3322
+ 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");
2747
3323
  return toValueNode(value);
2748
3324
  }
2749
3325
  // src/execution/db.ts
@@ -2777,10 +3353,13 @@ function bindSchema(schema, database, options = {}) {
2777
3353
  async $script(script, ...values) {
2778
3354
  if (typeof script === "string") return database.script(script);
2779
3355
  const { text, parameters } = compile(startSqlQuery(toRawSqlNode(script, values), db));
2780
- if (parameters.length > 0) throw new Error("A script takes no values, since it has no parameters; interpolate only tables and sql fragments without values");
3356
+ if (parameters.length > 0) throw new Error("A script has no parameters, so it takes no values, also in the queries and sql fragments that it interpolates");
2781
3357
  return database.script(text);
2782
3358
  },
2783
- $transaction: (callback, options) => database.transaction(callback, options)
3359
+ $transaction: (callback, options) => database.transaction(callback, options),
3360
+ $ensureTransaction: (callback) => database.ensureTransaction(callback),
3361
+ $isInTransaction: () => database.isInTransaction(),
3362
+ $lock: (key) => database.lock(key)
2784
3363
  };
2785
3364
  const contexts = createQueryContexts(Object.keys(schema.$tables), relations);
2786
3365
  const db = defineEntries(methods, schema.$tables, (key) => bindQuery(startQuery(schema.$tables[key], contexts[key]), db));
@@ -2851,6 +3430,27 @@ var TransactionCoordinator = class {
2851
3430
  return this.test ? Promise.reject(new Error(describeOutsideTest("start a transaction"))) : this.#runRoot(callback, true, void 0, settings);
2852
3431
  }
2853
3432
  /**
3433
+ * Runs `callback` in the current transaction, or in a new transaction outside of one.
3434
+ *
3435
+ * Joining starts no savepoint, so a rejection of `callback` does not undo its changes.
3436
+ * It rejects without calling `callback` when the current level accepts no statements, as a nested transaction would.
3437
+ */
3438
+ async ensureTransaction(callback) {
3439
+ const level = this.#storage.getStore();
3440
+ if (!level?.inTransaction) return this.transaction(callback);
3441
+ const unavailable = createUnavailableError(level, "join the transaction");
3442
+ if (unavailable) throw unavailable;
3443
+ return callback();
3444
+ }
3445
+ /**
3446
+ * Tells whether the current async context runs in a transaction of `transaction`.
3447
+ *
3448
+ * 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.
3449
+ */
3450
+ isInTransaction() {
3451
+ return this.#storage.getStore()?.inTransaction ?? false;
3452
+ }
3453
+ /**
2854
3454
  * Runs `callback` in a transaction that always rolls back, on a coordinator created for tests.
2855
3455
  *
2856
3456
  * Inside a transaction, it runs `callback` in a savepoint nested in it that always rolls back.
@@ -3017,7 +3617,7 @@ var TransactionCoordinator = class {
3017
3617
  */
3018
3618
  async #seedFixture(fixture, seed) {
3019
3619
  try {
3020
- return await this.#run(await this.#startNested(fixture), seed, true);
3620
+ return await this.#run(await this.#startNested(fixture, false), seed, true);
3021
3621
  } catch (error) {
3022
3622
  return { rejection: { error } };
3023
3623
  }
@@ -3069,7 +3669,7 @@ var TransactionCoordinator = class {
3069
3669
  } catch (error) {
3070
3670
  throw (await releaseConnection(connection, { error }, { error })).error;
3071
3671
  }
3072
- return createLevel(connection, void 0);
3672
+ return createLevel(connection, void 0, false);
3073
3673
  }
3074
3674
  /**
3075
3675
  * Starts a nested level of a test fixture, preempting its active one.
@@ -3085,12 +3685,12 @@ var TransactionCoordinator = class {
3085
3685
  const unavailable = createUnavailableError(fixture, "start a level of the test fixture");
3086
3686
  if (unavailable) throw suppressAbortReason(unavailable, signal);
3087
3687
  signal?.throwIfAborted();
3088
- return this.#startNested(fixture, signal);
3688
+ return this.#startNested(fixture, false, signal);
3089
3689
  }
3090
3690
  async #runRoot(callback, commits, signal, settings) {
3091
3691
  const connection = await (signal ? acquireUnlessAborted(this.driver, signal) : this.driver.acquire());
3092
3692
  if (signal?.aborted) throw (await releaseConnection(connection, void 0, { error: signal.reason })).error;
3093
- const level = createLevel(connection, void 0);
3693
+ const level = createLevel(connection, void 0, commits);
3094
3694
  let outcome;
3095
3695
  try {
3096
3696
  await connection.execute(beginStatement);
@@ -3114,16 +3714,17 @@ var TransactionCoordinator = class {
3114
3714
  async #runNested(parent, callback, commits, signal, settings) {
3115
3715
  const unavailable = createUnavailableError(parent, "start a nested transaction");
3116
3716
  if (unavailable) throw unavailable;
3117
- return this.#runStarted(await this.#startNested(parent, signal, settings), callback, commits, signal);
3717
+ return this.#runStarted(await this.#startNested(parent, commits, signal, settings), callback, commits, signal);
3118
3718
  }
3119
3719
  /**
3120
3720
  * Reserves a nested level of `parent`, sends its `SAVEPOINT`, and applies its `settings`, rejecting when one of them fails.
3121
3721
  *
3722
+ * `transaction` tells whether the level is a transaction of `transaction` rather than a level of the test helpers.
3122
3723
  * A level whose settings fail rolls back to its savepoint first.
3123
3724
  * The failure suppresses the reason of `signal` when the signal aborted before it.
3124
3725
  */
3125
- async #startNested(parent, signal, settings) {
3126
- const level = createLevel(parent.connection, parent);
3726
+ async #startNested(parent, transaction, signal, settings) {
3727
+ const level = createLevel(parent.connection, parent, transaction);
3127
3728
  parent.child = level;
3128
3729
  try {
3129
3730
  await level.connection.execute(createStatement(`SAVEPOINT ${getSavepointName(level)}`));
@@ -3210,11 +3811,12 @@ var TransactionCoordinator = class {
3210
3811
  const beginStatement = createStatement("BEGIN");
3211
3812
  const commitStatement = createStatement("COMMIT");
3212
3813
  const rollbackStatement = createStatement("ROLLBACK");
3213
- function createLevel(connection, parent) {
3814
+ function createLevel(connection, parent, transaction) {
3214
3815
  return {
3215
3816
  connection,
3216
3817
  parent,
3217
3818
  depth: parent ? parent.depth + 1 : 0,
3819
+ inTransaction: transaction || (parent?.inTransaction ?? false),
3218
3820
  state: "starting",
3219
3821
  child: void 0,
3220
3822
  rollbackOnly: void 0,
@@ -3410,13 +4012,23 @@ function createDatabase({ driver, test }) {
3410
4012
  script: (script, ...values) => typeof script === "string" ? coordinator.executeScript(script) : getTablelessDb().$script(script, ...values),
3411
4013
  execute: (statement) => coordinator.execute(statement),
3412
4014
  transaction: (callback, options) => coordinator.transaction(callback, options),
4015
+ ensureTransaction: (callback) => coordinator.ensureTransaction(callback),
4016
+ isInTransaction: () => coordinator.isInTransaction(),
4017
+ async lock(key) {
4018
+ if (!coordinator.isInTransaction()) throw new Error("Cannot take an advisory lock outside a transaction, where it would end with its statement; a test transaction alone does not count");
4019
+ await coordinator.execute(createLockStatement(key));
4020
+ },
3413
4021
  close: () => coordinator.close(),
3414
4022
  [Symbol.asyncDispose]: () => coordinator.close()
3415
4023
  };
3416
4024
  return database;
3417
4025
  }
4026
+ /** Creates the statement that takes a transaction-level advisory lock on a 64-bit hash of `key`. */
4027
+ function createLockStatement(key) {
4028
+ return createStatement("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [key]);
4029
+ }
3418
4030
  // src/query/helper.ts
3419
4031
  function defineHelper(_table, helper) {
3420
4032
  return (query, ...args) => requireDerived(query, helper(query, ...args), "The callback of a helper");
3421
4033
  }
3422
- export { NotFoundError, RequiredRelationError, ResultDecodeError, and, asc, compare, createDatabase, createStatement, defineFilter, defineGenericOperator, defineHelper, defineSchema, defineTable, defineType, desc, equalityOperators, from, many, not, one, or, orderBy, orderingOperators, param, sql, textOperators };
4034
+ export { FALSE, NotFoundError, RequiredRelationError, ResultDecodeError, TRUE, and, asc, caseWhen, coalesce, compare, createDatabase, createStatement, defineFilter, defineGenericOperator, defineHelper, defineSchema, defineTable, defineType, desc, equalityOperators, from, greatest, identifier, least, literal, many, not, one, or, orderBy, orderingOperators, param, sql, textOperators };