kern-orm 0.3.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-CNqYlONX.d.mts → index--0tyG7sU.d.mts} +139 -19
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1186 -1052
- package/dist/testing.d.mts +1 -1
- package/dist/{text-BUNl8wpl.mjs → text-BTFErHIi.mjs} +57 -10
- package/dist/types.d.mts +5 -30
- package/dist/types.mjs +2 -8
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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,14 +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: a query nests in it, and 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
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");
|
|
39
40
|
return createNode({
|
|
40
41
|
kind: "values",
|
|
41
42
|
values: operand
|
|
42
43
|
});
|
|
43
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
|
+
}
|
|
44
51
|
function and(...operands) {
|
|
45
52
|
return combineConditions("and", operands);
|
|
46
53
|
}
|
|
@@ -55,6 +62,244 @@ function combineConditions(kind, operands) {
|
|
|
55
62
|
for (const operand of operands) nodes.push(toConditionNode(operand));
|
|
56
63
|
return toBooleanExpression(combineNodes(kind, nodes));
|
|
57
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
|
+
}
|
|
58
303
|
// src/result/result.ts
|
|
59
304
|
/** Error thrown when a query of one row finds no row, executed or nested, unless a required relation guarantees the row. */
|
|
60
305
|
var NotFoundError = class extends Error {
|
|
@@ -111,9 +356,9 @@ function decodeStatement(plan, result) {
|
|
|
111
356
|
}
|
|
112
357
|
}
|
|
113
358
|
if (counted === "number") return count;
|
|
114
|
-
if (plan.rows !== "many") return
|
|
359
|
+
if (plan.rows !== "many") return createRecord(plan);
|
|
115
360
|
const records = [];
|
|
116
|
-
for (let index = 0; index < count; index++) records.push(
|
|
361
|
+
for (let index = 0; index < count; index++) records.push(createRecord(plan));
|
|
117
362
|
return records;
|
|
118
363
|
}
|
|
119
364
|
/**
|
|
@@ -232,7 +477,7 @@ function decodeRow(plan, row, packed) {
|
|
|
232
477
|
let index = 0;
|
|
233
478
|
try {
|
|
234
479
|
if (plan.value) return packed ? decodePackedValue(fields[0], row[0]) : decodeDriverValue(fields[0], row[0]);
|
|
235
|
-
const record =
|
|
480
|
+
const record = createRecord(plan);
|
|
236
481
|
for (; index < fields.length; index++) {
|
|
237
482
|
const field = fields[index];
|
|
238
483
|
record[field.key] = packed ? decodePackedValue(field, row[index]) : decodeDriverValue(field, row[index]);
|
|
@@ -243,6 +488,16 @@ function decodeRow(plan, row, packed) {
|
|
|
243
488
|
throw prefixPath(packed ? `.${key}` : key, error);
|
|
244
489
|
}
|
|
245
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
|
+
}
|
|
246
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. */
|
|
247
502
|
function decodeDriverValue(field, value) {
|
|
248
503
|
if (value === void 0) throw new Error("Driver returned no value");
|
|
@@ -387,40 +642,144 @@ function toColumn(definition) {
|
|
|
387
642
|
hasDefault: false
|
|
388
643
|
}) : void 0;
|
|
389
644
|
}
|
|
390
|
-
// src/
|
|
391
|
-
/** Encodes a parameter without a type, such as a value interpolated into `sql`, by its JavaScript type. */
|
|
392
|
-
function encodeUntyped(value) {
|
|
393
|
-
if (typeof value === "string") return value;
|
|
394
|
-
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
395
|
-
if (typeof value === "boolean") return value ? "t" : "f";
|
|
396
|
-
if (value instanceof Date) return value.toISOString();
|
|
397
|
-
if (value instanceof Uint8Array) return encodeBytea(value);
|
|
398
|
-
if (Array.isArray(value)) throw new TypeError("Pass an array with param(type, value), so that its elements are encoded by type");
|
|
399
|
-
return stringifyJson(value);
|
|
400
|
-
}
|
|
401
|
-
// src/compiler/postgres/identifier.ts
|
|
402
|
-
/** 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)). */
|
|
403
|
-
const maxCharacterBytes = 4;
|
|
404
|
-
function quoteIdentifier(name) {
|
|
405
|
-
return `"${name.replaceAll("\"", "\"\"")}"`;
|
|
406
|
-
}
|
|
645
|
+
// src/query/resolve-values.ts
|
|
407
646
|
/**
|
|
408
|
-
*
|
|
647
|
+
* Replaces objects of column values in a predicate with conditions on the columns of `source`.
|
|
409
648
|
*
|
|
410
|
-
*
|
|
649
|
+
* Groups are rebuilt, so constants that appear after resolution, such as an empty object, are simplified.
|
|
411
650
|
*/
|
|
412
|
-
function
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
/**
|
|
424
783
|
* Returns the SQL name of the projection with `key` at `index` of its query.
|
|
425
784
|
*
|
|
426
785
|
* A key of up to 50 bytes is the name itself.
|
|
@@ -538,6 +897,7 @@ function compileStatement(node, options = {}, tables, db) {
|
|
|
538
897
|
text: compileQuery(context, void 0, node),
|
|
539
898
|
parameters: context.parameters,
|
|
540
899
|
fields: node.projections,
|
|
900
|
+
recordTemplate: node.recordTemplate,
|
|
541
901
|
rows: node.rows,
|
|
542
902
|
value: node.value,
|
|
543
903
|
counted: void 0,
|
|
@@ -561,6 +921,7 @@ function compileStatement(node, options = {}, tables, db) {
|
|
|
561
921
|
text: compileMutation(context, node),
|
|
562
922
|
parameters: context.parameters,
|
|
563
923
|
fields: returning ?? noFields,
|
|
924
|
+
recordTemplate: node.recordTemplate,
|
|
564
925
|
rows: node.rows,
|
|
565
926
|
value: node.value,
|
|
566
927
|
counted: returning === void 0 ? "number" : returning.length === 0 ? "records" : void 0,
|
|
@@ -853,237 +1214,54 @@ function visitQuery(query, visit) {
|
|
|
853
1214
|
if (query.limit !== void 0) visitNodes(query.limit, true, visit);
|
|
854
1215
|
if (query.offset !== void 0) visitNodes(query.offset, true, visit);
|
|
855
1216
|
}
|
|
856
|
-
// src/query/
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1217
|
+
// src/query/rows.ts
|
|
1218
|
+
/** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
|
|
1219
|
+
function getRelationRows(relation) {
|
|
1220
|
+
if (relation.kind === "many") return "many";
|
|
1221
|
+
return relation.required ? "one" : "nullable";
|
|
1222
|
+
}
|
|
1223
|
+
/** Returns the rows mode after a filter of a query of rows mode `rows`, of values when `value`, whose row is `guaranteed`, as `FilteredStateRows` gives it in types. */
|
|
1224
|
+
function toFilteredRows(rows, value, guaranteed) {
|
|
1225
|
+
if (!guaranteed || rows !== "one") return rows;
|
|
1226
|
+
return value ? "optional" : "nullable";
|
|
1227
|
+
}
|
|
1228
|
+
/** Returns the limit of a query of many rows that becomes a query of one row, given its limit `limit`: one row, or none under a limit of zero. */
|
|
1229
|
+
function limitToOne(limit) {
|
|
1230
|
+
return limit?.kind === "param" && limit.value === 0 ? limit : oneNode;
|
|
1231
|
+
}
|
|
1232
|
+
/** Returns the rows mode after `pluck` of a query of rows mode `rows`, as `ValueRows` gives it in types. */
|
|
1233
|
+
function toValueRows(rows) {
|
|
1234
|
+
return rows === "nullable" ? "optional" : rows;
|
|
1235
|
+
}
|
|
1236
|
+
// src/query/node.ts
|
|
1237
|
+
/** Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation. */
|
|
1238
|
+
function createQueryNode(from, relation, filters = noFilters) {
|
|
1239
|
+
return Object.freeze({
|
|
1240
|
+
kind: "query",
|
|
1241
|
+
from,
|
|
1242
|
+
relation,
|
|
1243
|
+
projections: [],
|
|
1244
|
+
filters,
|
|
1245
|
+
orderBy: [],
|
|
1246
|
+
limit: void 0,
|
|
1247
|
+
offset: void 0,
|
|
1248
|
+
rows: relation === void 0 ? "many" : getRelationRows(relation),
|
|
1249
|
+
value: false,
|
|
1250
|
+
guaranteed: relation?.required ?? false,
|
|
1251
|
+
all: false
|
|
864
1252
|
});
|
|
865
|
-
return existsProjection;
|
|
866
1253
|
}
|
|
867
|
-
/** Tells whether `node` is a query of
|
|
868
|
-
function
|
|
869
|
-
return node.
|
|
1254
|
+
/** Tells whether `node` is a query of a table without clauses, such as a table of a `Db`: no relation, filters, selected fields, order, limit, offset, rows other than `many`, or `all`. */
|
|
1255
|
+
function isTableQuery(node) {
|
|
1256
|
+
return node.relation === void 0 && node.projections.length === 0 && node.filters.relation === void 0 && node.filters.scope === void 0 && node.filters.user === void 0 && node.orderBy.length === 0 && node.limit === void 0 && node.offset === void 0 && node.rows === "many" && !node.all;
|
|
870
1257
|
}
|
|
871
|
-
/** Tells whether a selected field is the
|
|
872
|
-
function
|
|
873
|
-
return
|
|
1258
|
+
/** Tells whether a selected field is the rows of a nested query. */
|
|
1259
|
+
function isNestedSelection(node) {
|
|
1260
|
+
return "query" in node;
|
|
874
1261
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
decode: (text) => decodeSafeInteger(text, "Count"),
|
|
879
|
-
encode: (value) => String(value)
|
|
880
|
-
}).withOperators(orderingOperators);
|
|
881
|
-
/** Types of the sums of the values of `smallint` and `integer` types, by the type of the values, created once for each. */
|
|
882
|
-
const sumTypes = /* @__PURE__ */ new WeakMap();
|
|
883
|
-
const noFallbacks = /* @__PURE__ */ Object.freeze([]);
|
|
884
|
-
/** Definitions of the aggregate methods that take a column or an expression. */
|
|
885
|
-
const aggregateDefinitions = /* @__PURE__ */ Object.freeze({
|
|
886
|
-
count: {
|
|
887
|
-
name: "count",
|
|
888
|
-
distinct: false,
|
|
889
|
-
ordered: false,
|
|
890
|
-
type: toCountType
|
|
891
|
-
},
|
|
892
|
-
countDistinct: {
|
|
893
|
-
name: "count",
|
|
894
|
-
distinct: true,
|
|
895
|
-
ordered: false,
|
|
896
|
-
type: toCountType
|
|
897
|
-
},
|
|
898
|
-
min: {
|
|
899
|
-
name: "min",
|
|
900
|
-
distinct: false,
|
|
901
|
-
ordered: false,
|
|
902
|
-
type: toArgumentType
|
|
903
|
-
},
|
|
904
|
-
max: {
|
|
905
|
-
name: "max",
|
|
906
|
-
distinct: false,
|
|
907
|
-
ordered: false,
|
|
908
|
-
type: toArgumentType
|
|
909
|
-
},
|
|
910
|
-
sum: {
|
|
911
|
-
name: "sum",
|
|
912
|
-
distinct: false,
|
|
913
|
-
ordered: false,
|
|
914
|
-
type: toSumType
|
|
915
|
-
},
|
|
916
|
-
avg: {
|
|
917
|
-
name: "avg",
|
|
918
|
-
distinct: false,
|
|
919
|
-
ordered: false,
|
|
920
|
-
type: toAverageType
|
|
921
|
-
},
|
|
922
|
-
every: {
|
|
923
|
-
name: "bool_and",
|
|
924
|
-
distinct: false,
|
|
925
|
-
ordered: false,
|
|
926
|
-
type: toArgumentType
|
|
927
|
-
},
|
|
928
|
-
some: {
|
|
929
|
-
name: "bool_or",
|
|
930
|
-
distinct: false,
|
|
931
|
-
ordered: false,
|
|
932
|
-
type: toArgumentType
|
|
933
|
-
},
|
|
934
|
-
stringAgg: {
|
|
935
|
-
name: "string_agg",
|
|
936
|
-
distinct: false,
|
|
937
|
-
ordered: true,
|
|
938
|
-
type: toArgumentType
|
|
939
|
-
}
|
|
940
|
-
});
|
|
941
|
-
/** Projection of `count(*)`, which `count` without an argument selects; its value does not depend on the values or the order of the rows. */
|
|
942
|
-
const countProjection = /* @__PURE__ */ Object.freeze({
|
|
943
|
-
key: "count",
|
|
944
|
-
expression: /* @__PURE__ */ createNode({
|
|
945
|
-
kind: "aggregate",
|
|
946
|
-
name: "count",
|
|
947
|
-
distinct: false,
|
|
948
|
-
arguments: [/* @__PURE__ */ createNode({
|
|
949
|
-
kind: "raw",
|
|
950
|
-
fragments: ["*"],
|
|
951
|
-
values: []
|
|
952
|
-
})],
|
|
953
|
-
orderBy: []
|
|
954
|
-
}),
|
|
955
|
-
type: countType,
|
|
956
|
-
ordered: false,
|
|
957
|
-
fallbacks: noFallbacks
|
|
958
|
-
});
|
|
959
|
-
/**
|
|
960
|
-
* Creates the projection of the aggregate of `method` over the values of `argument`, the projection of a column or an expression of each row.
|
|
961
|
-
*
|
|
962
|
-
* `delimiter` is the delimiter of `stringAgg`, sent as a parameter.
|
|
963
|
-
*/
|
|
964
|
-
function projectAggregate(method, argument, delimiter) {
|
|
965
|
-
const definition = aggregateDefinitions[method];
|
|
966
|
-
const args = [argument.expression];
|
|
967
|
-
if (delimiter !== void 0) args.push(createNode({
|
|
968
|
-
kind: "param",
|
|
969
|
-
value: delimiter,
|
|
970
|
-
type: void 0
|
|
971
|
-
}));
|
|
972
|
-
return Object.freeze({
|
|
973
|
-
key: method,
|
|
974
|
-
expression: createNode({
|
|
975
|
-
kind: "aggregate",
|
|
976
|
-
name: definition.name,
|
|
977
|
-
distinct: definition.distinct,
|
|
978
|
-
arguments: args,
|
|
979
|
-
orderBy: []
|
|
980
|
-
}),
|
|
981
|
-
type: definition.type(argument.type),
|
|
982
|
-
ordered: definition.ordered,
|
|
983
|
-
fallbacks: noFallbacks
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
/** Creates the projection of the aggregate of `field` whose `NULL` `fallback` replaces after its earlier fallbacks. */
|
|
987
|
-
function addFallback(field, fallback) {
|
|
988
|
-
const fallbacks = field.fallbacks.slice();
|
|
989
|
-
fallbacks.push(fallback);
|
|
990
|
-
return Object.freeze({
|
|
991
|
-
key: field.key,
|
|
992
|
-
expression: field.expression,
|
|
993
|
-
type: field.type,
|
|
994
|
-
ordered: field.ordered,
|
|
995
|
-
fallbacks
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
/** Tells whether `node` is an aggregate query, whose value is the aggregate over the rows that its filters, limit, and offset choose. */
|
|
999
|
-
function isAggregateQuery(node) {
|
|
1000
|
-
return node.value && isAggregateProjection(node.projections[0]);
|
|
1001
|
-
}
|
|
1002
|
-
/** 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. */
|
|
1003
|
-
function isSelectedAsValue(node) {
|
|
1004
|
-
return isAggregateQuery(node) || isExistsQuery(node);
|
|
1005
|
-
}
|
|
1006
|
-
/** Tells whether a selected field is the projection of an aggregate, which only the aggregate methods select. */
|
|
1007
|
-
function isAggregateProjection(field) {
|
|
1008
|
-
return "ordered" in field;
|
|
1009
|
-
}
|
|
1010
|
-
function toCountType() {
|
|
1011
|
-
return countType;
|
|
1012
|
-
}
|
|
1013
|
-
function toArgumentType(argument) {
|
|
1014
|
-
return argument;
|
|
1015
|
-
}
|
|
1016
|
-
/** 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. */
|
|
1017
|
-
function toSumType(argument) {
|
|
1018
|
-
if (argument.castType !== "smallint" && argument.castType !== "integer") return argument;
|
|
1019
|
-
let type = sumTypes.get(argument);
|
|
1020
|
-
if (type === void 0) {
|
|
1021
|
-
type = defineType({ sqlType: "bigint" }).transform({
|
|
1022
|
-
decode: (text) => {
|
|
1023
|
-
decodeSafeInteger(text, "Sum");
|
|
1024
|
-
return argument.decode(text);
|
|
1025
|
-
},
|
|
1026
|
-
encode: (value) => argument.encode(value)
|
|
1027
|
-
}).withOperators(argument.operators);
|
|
1028
|
-
sumTypes.set(argument, type);
|
|
1029
|
-
}
|
|
1030
|
-
return type;
|
|
1031
|
-
}
|
|
1032
|
-
/** 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`. */
|
|
1033
|
-
function toAverageType(argument) {
|
|
1034
|
-
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");
|
|
1035
|
-
return argument;
|
|
1036
|
-
}
|
|
1037
|
-
/** Decodes `text` of a `bigint` as a number, rejecting one that a number cannot hold exactly; `subject` names it in the error. */
|
|
1038
|
-
function decodeSafeInteger(text, subject) {
|
|
1039
|
-
const number = Number(text);
|
|
1040
|
-
if (!Number.isSafeInteger(number)) throw new RangeError(`${subject} ${text} is outside the safe integer range of numbers`);
|
|
1041
|
-
return number;
|
|
1042
|
-
}
|
|
1043
|
-
// src/query/rows.ts
|
|
1044
|
-
/** Returns the rows mode of a query of the records of `relation`, as `RelationRows` gives it in types. */
|
|
1045
|
-
function getRelationRows(relation) {
|
|
1046
|
-
if (relation.kind === "many") return "many";
|
|
1047
|
-
return relation.required ? "one" : "nullable";
|
|
1048
|
-
}
|
|
1049
|
-
/** Returns the rows mode after a filter of a query of rows mode `rows`, of values when `value`, whose row is `guaranteed`, as `FilteredStateRows` gives it in types. */
|
|
1050
|
-
function toFilteredRows(rows, value, guaranteed) {
|
|
1051
|
-
if (!guaranteed || rows !== "one") return rows;
|
|
1052
|
-
return value ? "optional" : "nullable";
|
|
1053
|
-
}
|
|
1054
|
-
/** Returns the limit of a query of many rows that becomes a query of one row, given its limit `limit`: one row, or none under a limit of zero. */
|
|
1055
|
-
function limitToOne(limit) {
|
|
1056
|
-
return limit?.kind === "param" && limit.value === 0 ? limit : oneNode;
|
|
1057
|
-
}
|
|
1058
|
-
/** Returns the rows mode after `pluck` of a query of rows mode `rows`, as `ValueRows` gives it in types. */
|
|
1059
|
-
function toValueRows(rows) {
|
|
1060
|
-
return rows === "nullable" ? "optional" : rows;
|
|
1061
|
-
}
|
|
1062
|
-
// src/query/node.ts
|
|
1063
|
-
/** Creates a query of all rows of `from`, or of the records of `relation` with `filters` in the rows mode of the relation. */
|
|
1064
|
-
function createQueryNode(from, relation, filters = noFilters) {
|
|
1065
|
-
return Object.freeze({
|
|
1066
|
-
kind: "query",
|
|
1067
|
-
from,
|
|
1068
|
-
relation,
|
|
1069
|
-
projections: [],
|
|
1070
|
-
filters,
|
|
1071
|
-
orderBy: [],
|
|
1072
|
-
limit: void 0,
|
|
1073
|
-
offset: void 0,
|
|
1074
|
-
rows: relation === void 0 ? "many" : getRelationRows(relation),
|
|
1075
|
-
value: false,
|
|
1076
|
-
guaranteed: relation?.required ?? false,
|
|
1077
|
-
all: false
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
/** Tells whether `node` is a query of a table without clauses, such as a table of a `Db`: no relation, filters, selected fields, order, limit, offset, rows other than `many`, or `all`. */
|
|
1081
|
-
function isTableQuery(node) {
|
|
1082
|
-
return node.relation === void 0 && node.projections.length === 0 && node.filters.relation === void 0 && node.filters.scope === void 0 && node.filters.user === void 0 && node.orderBy.length === 0 && node.limit === void 0 && node.offset === void 0 && node.rows === "many" && !node.all;
|
|
1083
|
-
}
|
|
1084
|
-
/** Tells whether a selected field is the rows of a nested query. */
|
|
1085
|
-
function isNestedSelection(node) {
|
|
1086
|
-
return "query" in node;
|
|
1262
|
+
/** Tells whether a selected field is a literal. */
|
|
1263
|
+
function isLiteralSelection(node) {
|
|
1264
|
+
return "literal" in node;
|
|
1087
1265
|
}
|
|
1088
1266
|
// src/schema/relation.ts
|
|
1089
1267
|
/** Runtime mark of the declarations of `many` and `one`. */
|
|
@@ -1373,7 +1551,8 @@ function packRows(query) {
|
|
|
1373
1551
|
fields: projections,
|
|
1374
1552
|
rows: query.rows,
|
|
1375
1553
|
value: query.value,
|
|
1376
|
-
guaranteedBy: query.guaranteedBy
|
|
1554
|
+
guaranteedBy: query.guaranteedBy,
|
|
1555
|
+
recordTemplate: query.recordTemplate
|
|
1377
1556
|
});
|
|
1378
1557
|
const projection = Object.freeze({
|
|
1379
1558
|
key: "rows",
|
|
@@ -1440,7 +1619,8 @@ function lowerText(node, context) {
|
|
|
1440
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. */
|
|
1441
1620
|
function lowerMutation(node, context) {
|
|
1442
1621
|
const { target, returning, rows, value } = node;
|
|
1443
|
-
const
|
|
1622
|
+
const recordTemplate = returning === void 0 ? void 0 : createRecordTemplate(returning);
|
|
1623
|
+
const lowered = returning === void 0 ? void 0 : lowerProjections(dropLiterals(returning), context);
|
|
1444
1624
|
switch (node.kind) {
|
|
1445
1625
|
case "insert": {
|
|
1446
1626
|
const values = lowerEach(node.values, context, lowerRow);
|
|
@@ -1451,6 +1631,7 @@ function lowerMutation(node, context) {
|
|
|
1451
1631
|
columns: node.columns,
|
|
1452
1632
|
values,
|
|
1453
1633
|
returning: lowered,
|
|
1634
|
+
recordTemplate,
|
|
1454
1635
|
rows,
|
|
1455
1636
|
value
|
|
1456
1637
|
});
|
|
@@ -1465,6 +1646,7 @@ function lowerMutation(node, context) {
|
|
|
1465
1646
|
set,
|
|
1466
1647
|
filters,
|
|
1467
1648
|
returning: lowered,
|
|
1649
|
+
recordTemplate,
|
|
1468
1650
|
rows,
|
|
1469
1651
|
value
|
|
1470
1652
|
});
|
|
@@ -1477,6 +1659,7 @@ function lowerMutation(node, context) {
|
|
|
1477
1659
|
target,
|
|
1478
1660
|
filters,
|
|
1479
1661
|
returning: lowered,
|
|
1662
|
+
recordTemplate,
|
|
1480
1663
|
rows,
|
|
1481
1664
|
value
|
|
1482
1665
|
});
|
|
@@ -1503,13 +1686,14 @@ function lowerSelect(node, context, positions) {
|
|
|
1503
1686
|
if (isExistsQuery(node)) return selectExists(node, context);
|
|
1504
1687
|
checkRelation(node, context);
|
|
1505
1688
|
if (isAggregateQuery(node)) return lowerAggregate(node, context);
|
|
1506
|
-
const
|
|
1689
|
+
const recordTemplate = createRecordTemplate(node.projections);
|
|
1690
|
+
const selected = dropLiterals(node.projections);
|
|
1507
1691
|
const projections = lowerProjections(selected, context);
|
|
1508
1692
|
const filters = lowerFilters(node.filters, context);
|
|
1509
1693
|
const orderBy = lowerOrder(node.orderBy, context, selected, projections);
|
|
1510
1694
|
const { relation } = node;
|
|
1511
1695
|
const guaranteedBy = node.guaranteed && relation !== void 0 ? formatRelationName(relation.key, relation.name) : void 0;
|
|
1512
|
-
const page = node.offset !== void 0 && selected.some((field, index) => isPagedField(field, projections[index], orderBy)) ? lowerPage(node, projections, filters, orderBy, 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;
|
|
1513
1697
|
if (page !== void 0) return page;
|
|
1514
1698
|
return Object.freeze({
|
|
1515
1699
|
kind: "select",
|
|
@@ -1522,9 +1706,27 @@ function lowerSelect(node, context, positions) {
|
|
|
1522
1706
|
offset: node.offset,
|
|
1523
1707
|
rows: node.rows,
|
|
1524
1708
|
value: node.value,
|
|
1525
|
-
guaranteedBy
|
|
1709
|
+
guaranteedBy,
|
|
1710
|
+
recordTemplate
|
|
1526
1711
|
});
|
|
1527
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
|
+
}
|
|
1528
1730
|
/**
|
|
1529
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.
|
|
1530
1732
|
*
|
|
@@ -1547,6 +1749,7 @@ function checkDb(db, context, subject) {
|
|
|
1547
1749
|
}
|
|
1548
1750
|
/**
|
|
1549
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.
|
|
1550
1753
|
* The derived source applies the filters, the order, the limit, and the offset, and replaces the occurrence of the table.
|
|
1551
1754
|
*
|
|
1552
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.
|
|
@@ -1557,7 +1760,7 @@ function checkDb(db, context, subject) {
|
|
|
1557
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(*)`.
|
|
1558
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.
|
|
1559
1762
|
*/
|
|
1560
|
-
function lowerPage(node, projections, filters, orderBy, positions, guaranteedBy) {
|
|
1763
|
+
function lowerPage(node, queryFields, projections, filters, orderBy, positions, guaranteedBy, recordTemplate) {
|
|
1561
1764
|
const { from } = node;
|
|
1562
1765
|
const columnKeys = /* @__PURE__ */ new Set();
|
|
1563
1766
|
const fields = [];
|
|
@@ -1565,7 +1768,7 @@ function lowerPage(node, projections, filters, orderBy, positions, guaranteedBy)
|
|
|
1565
1768
|
for (let index = 0; index < projections.length; index++) {
|
|
1566
1769
|
const { expression } = projections[index];
|
|
1567
1770
|
let key;
|
|
1568
|
-
if (isPagedField(
|
|
1771
|
+
if (isPagedField(queryFields[index], projections[index], orderBy)) {
|
|
1569
1772
|
if (holdsFragmentOfSource(expression, from)) return;
|
|
1570
1773
|
collectColumnKeys(expression, from, columnKeys);
|
|
1571
1774
|
} else if (expression.kind === "column" && expression.source === from) columnKeys.add(expression.key);
|
|
@@ -1628,7 +1831,8 @@ function lowerPage(node, projections, filters, orderBy, positions, guaranteedBy)
|
|
|
1628
1831
|
offset: void 0,
|
|
1629
1832
|
rows: node.rows,
|
|
1630
1833
|
value: node.value,
|
|
1631
|
-
guaranteedBy
|
|
1834
|
+
guaranteedBy,
|
|
1835
|
+
recordTemplate
|
|
1632
1836
|
});
|
|
1633
1837
|
}
|
|
1634
1838
|
/** Lowers an aggregate query to a statement of the value of its aggregate over the rows that its filters, limit, and offset choose. */
|
|
@@ -1785,14 +1989,15 @@ function lowerRecord(node, context) {
|
|
|
1785
1989
|
kind: "select",
|
|
1786
1990
|
from: void 0,
|
|
1787
1991
|
joins: noJoins,
|
|
1788
|
-
projections: lowerProjections(node.projections, context),
|
|
1992
|
+
projections: lowerProjections(dropLiterals(node.projections), context),
|
|
1789
1993
|
filters: noFilters,
|
|
1790
1994
|
orderBy: noOrder,
|
|
1791
1995
|
limit: void 0,
|
|
1792
1996
|
offset: void 0,
|
|
1793
1997
|
rows: "one",
|
|
1794
1998
|
value: false,
|
|
1795
|
-
guaranteedBy: void 0
|
|
1999
|
+
guaranteedBy: void 0,
|
|
2000
|
+
recordTemplate: createRecordTemplate(node.projections)
|
|
1796
2001
|
});
|
|
1797
2002
|
}
|
|
1798
2003
|
/**
|
|
@@ -2026,550 +2231,45 @@ function compileToSQL(query, options) {
|
|
|
2026
2231
|
if (options !== void 0) throw new Error("A query bound to a Db compiles with the options of its Db");
|
|
2027
2232
|
return db.$compile(query);
|
|
2028
2233
|
}
|
|
2029
|
-
// src/query/
|
|
2030
|
-
/** Context of a query whose table has no relations, such as one of `from(table)`. */
|
|
2031
|
-
const noContext = /* @__PURE__ */ Object.freeze({ relations: /* @__PURE__ */ Object.freeze({}) });
|
|
2032
|
-
/** Creates expressions of the columns of `source`, each with the type of its column. */
|
|
2033
|
-
function createColumnRefs(source) {
|
|
2034
|
-
return addColumns({}, source);
|
|
2035
|
-
}
|
|
2036
|
-
/** Creates the references of the callbacks of `select`, `where`, and `orderBy` over `source`: its columns, and the relations of `context`. */
|
|
2037
|
-
function createSelectionRefs(source, context) {
|
|
2038
|
-
return Object.defineProperties(addColumns({}, source), context.relations);
|
|
2039
|
-
}
|
|
2040
|
-
function addColumns(refs, source) {
|
|
2041
|
-
const { columns } = source.table;
|
|
2042
|
-
for (const key of Object.keys(columns)) refs[key] = toExpression(createNode({
|
|
2043
|
-
kind: "column",
|
|
2044
|
-
source,
|
|
2045
|
-
key
|
|
2046
|
-
}), columns[key].type);
|
|
2047
|
-
return refs;
|
|
2048
|
-
}
|
|
2049
|
-
// src/query/resolve-values.ts
|
|
2234
|
+
// src/query/record.ts
|
|
2050
2235
|
/**
|
|
2051
|
-
*
|
|
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.
|
|
2052
2237
|
*
|
|
2053
|
-
*
|
|
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`.
|
|
2239
|
+
* A bound query executes on its `Db` when awaited.
|
|
2054
2240
|
*/
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
});
|
|
2067
|
-
if (value === null) conditions.push(createIsNullNode(column));
|
|
2068
|
-
else if (type) conditions.push(matchValue(source, column, type, value));
|
|
2069
|
-
else conditions.push(compareNodes("=", column, value, void 0));
|
|
2070
|
-
}
|
|
2071
|
-
return combineNodes("and", conditions);
|
|
2072
|
-
}
|
|
2073
|
-
case "and":
|
|
2074
|
-
case "or": {
|
|
2075
|
-
const operands = [];
|
|
2076
|
-
for (const operand of node.operands) operands.push(resolveValues(operand, source));
|
|
2077
|
-
return combineNodes(node.kind, operands);
|
|
2078
|
-
}
|
|
2079
|
-
case "not": return negateNode(resolveValues(node.operand, source));
|
|
2080
|
-
case "raw": return createNode({
|
|
2081
|
-
...node,
|
|
2082
|
-
values: node.values.map((value) => resolveValues(value, source))
|
|
2083
|
-
});
|
|
2084
|
-
default: return node;
|
|
2241
|
+
var RecordQuery = class {
|
|
2242
|
+
node;
|
|
2243
|
+
/** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
|
|
2244
|
+
db;
|
|
2245
|
+
/** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
|
|
2246
|
+
then;
|
|
2247
|
+
constructor(node, db) {
|
|
2248
|
+
this.node = node;
|
|
2249
|
+
this.db = db;
|
|
2250
|
+
this.then = db === void 0 ? void 0 : executeOnAwait;
|
|
2251
|
+
Object.freeze(this);
|
|
2085
2252
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
}
|
|
2095
|
-
// src/query/filters.ts
|
|
2096
|
-
/** Creates query filters with the filter of `where` over `source` with the relations of `context` combined with `AND` with the user filters of `filters`. */
|
|
2097
|
-
function addUserFilter(filters, filter, source, context) {
|
|
2098
|
-
const predicate = resolveValues(toConditionNode(typeof filter === "function" ? filter(createSelectionRefs(source, context)) : filter), source);
|
|
2099
|
-
const { user } = filters;
|
|
2100
|
-
return Object.freeze({
|
|
2101
|
-
...filters,
|
|
2102
|
-
user: user ? combineNodes("and", [user, predicate]) : predicate
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
2105
|
-
function defineFilter(_table, filter) {
|
|
2106
|
-
return filter;
|
|
2107
|
-
}
|
|
2108
|
-
// src/query/find.ts
|
|
2253
|
+
toSQL(options) {
|
|
2254
|
+
return compileToSQL(this, options);
|
|
2255
|
+
}
|
|
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");
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2109
2261
|
/**
|
|
2110
|
-
*
|
|
2262
|
+
* Starts the query of `$select` of `selection`, an object of expressions, queries, and literals by key, bound to `db` when given.
|
|
2111
2263
|
*
|
|
2112
|
-
* It
|
|
2264
|
+
* It reads the keys and values of `selection` when it is called.
|
|
2113
2265
|
*/
|
|
2114
|
-
function
|
|
2115
|
-
const
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
filter[column] = requireKeyValue(table, column, values[index], "find");
|
|
2122
|
-
}
|
|
2123
|
-
return filter;
|
|
2124
|
-
}
|
|
2125
|
-
/**
|
|
2126
|
-
* Returns the object of column values that `findBy` filters a query of `table` by: a copy of `values`, read once.
|
|
2127
|
-
*
|
|
2128
|
-
* It rejects columns that are not exactly a key of `table`, primary or unique, as `isKey` tells, and `null` and `undefined`.
|
|
2129
|
-
*/
|
|
2130
|
-
function toKeyValues(table, values) {
|
|
2131
|
-
if (typeof values !== "object" || values === null) throw new TypeError(`findBy takes an object of the columns of a key of table "${table.name}"`);
|
|
2132
|
-
const columns = Object.keys(values);
|
|
2133
|
-
if (!isKey(table, columns)) {
|
|
2134
|
-
const listed = columns.length === 0 ? "an object without columns" : `columns ${quoteColumns(columns)}`;
|
|
2135
|
-
throw new Error(`findBy takes the columns of one key of table "${table.name}", primary or unique, not ${listed}`);
|
|
2136
|
-
}
|
|
2137
|
-
const filter = {};
|
|
2138
|
-
for (const column of columns) {
|
|
2139
|
-
const value = values[column];
|
|
2140
|
-
filter[column] = requireKeyValue(table, column, value, "findBy");
|
|
2141
|
-
}
|
|
2142
|
-
return filter;
|
|
2143
|
-
}
|
|
2144
|
-
/** Returns `value` of column `column` of a key of `table` that `method` takes, rejecting `null` and `undefined`. */
|
|
2145
|
-
function requireKeyValue(table, column, value, method) {
|
|
2146
|
-
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`);
|
|
2147
|
-
return value;
|
|
2148
|
-
}
|
|
2149
|
-
/** Lists `columns` in quotes, separated by commas. */
|
|
2150
|
-
function quoteColumns(columns) {
|
|
2151
|
-
return columns.map((column) => `"${column}"`).join(", ");
|
|
2152
|
-
}
|
|
2153
|
-
// src/query/order.ts
|
|
2154
|
-
/** Direction and placement of `NULL` of a sort key node, by `OrderDirection`. */
|
|
2155
|
-
const directions = /* @__PURE__ */ Object.freeze({
|
|
2156
|
-
asc: {
|
|
2157
|
-
direction: "asc",
|
|
2158
|
-
nulls: void 0
|
|
2159
|
-
},
|
|
2160
|
-
desc: {
|
|
2161
|
-
direction: "desc",
|
|
2162
|
-
nulls: void 0
|
|
2163
|
-
},
|
|
2164
|
-
"asc nulls first": {
|
|
2165
|
-
direction: "asc",
|
|
2166
|
-
nulls: "first"
|
|
2167
|
-
},
|
|
2168
|
-
"asc nulls last": {
|
|
2169
|
-
direction: "asc",
|
|
2170
|
-
nulls: "last"
|
|
2171
|
-
},
|
|
2172
|
-
"desc nulls first": {
|
|
2173
|
-
direction: "desc",
|
|
2174
|
-
nulls: "first"
|
|
2175
|
-
},
|
|
2176
|
-
"desc nulls last": {
|
|
2177
|
-
direction: "desc",
|
|
2178
|
-
nulls: "last"
|
|
2179
|
-
}
|
|
2180
|
-
});
|
|
2181
|
-
const orderingNode = Symbol("kern-orm.ordering.node");
|
|
2182
|
-
/** Runtime class of an expression with a direction, which holds its sort key node. */
|
|
2183
|
-
var OrderingObject = class {
|
|
2184
|
-
[orderingNode];
|
|
2185
|
-
constructor(node) {
|
|
2186
|
-
this[orderingNode] = node;
|
|
2187
|
-
Object.freeze(this);
|
|
2188
|
-
}
|
|
2189
|
-
};
|
|
2190
|
-
/** Sorts by `value`, an expression or a query of one value, in ascending order, in the callback of `orderBy`. */
|
|
2191
|
-
function asc(value) {
|
|
2192
|
-
return orderBy(value, "asc");
|
|
2193
|
-
}
|
|
2194
|
-
/** Sorts by `value`, an expression or a query of one value, in descending order, in the callback of `orderBy`. */
|
|
2195
|
-
function desc(value) {
|
|
2196
|
-
return orderBy(value, "desc");
|
|
2197
|
-
}
|
|
2198
|
-
/** Sorts by `value`, an expression or a query of one value, in `direction`, such as `"desc nulls last"`, in the callback of `orderBy`. */
|
|
2199
|
-
function orderBy(value, direction) {
|
|
2200
|
-
if (!isOperandSource(value)) throw new TypeError("A sort key must be an expression or a query of one value");
|
|
2201
|
-
return new OrderingObject(createOrderNode(value[operandNode](), direction));
|
|
2202
|
-
}
|
|
2203
|
-
/**
|
|
2204
|
-
* Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
|
|
2205
|
-
*
|
|
2206
|
-
* `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.
|
|
2207
|
-
*/
|
|
2208
|
-
function addOrder(node, context, args) {
|
|
2209
|
-
const order = node.orderBy.slice();
|
|
2210
|
-
const first = args[0];
|
|
2211
|
-
if (typeof first === "function") {
|
|
2212
|
-
const terms = first(createSelectionRefs(node.from, context), createSelectedRefs(node));
|
|
2213
|
-
if (Array.isArray(terms)) for (const term of terms) order.push(readTerm(term));
|
|
2214
|
-
else order.push(readTerm(terms));
|
|
2215
|
-
return order;
|
|
2216
|
-
}
|
|
2217
|
-
for (const arg of args) if (typeof arg === "string") order.push(createOrderNode(createColumnNode(node.from, arg), "asc"));
|
|
2218
|
-
else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
|
|
2219
|
-
const columns = arg;
|
|
2220
|
-
for (const key of Object.keys(columns)) order.push(createOrderNode(createColumnNode(node.from, key), columns[key]));
|
|
2221
|
-
} else throw new TypeError("orderBy takes column keys and objects of columns with directions, or a callback");
|
|
2222
|
-
return order;
|
|
2223
|
-
}
|
|
2224
|
-
/**
|
|
2225
|
-
* Creates the sort key node of a sort key returned by the callback of `orderBy`.
|
|
2226
|
-
*
|
|
2227
|
-
* A query is converted by the method of `operandNode`, which rejects a query other than of one value.
|
|
2228
|
-
*/
|
|
2229
|
-
function readTerm(term) {
|
|
2230
|
-
if (term instanceof OrderingObject) return term[orderingNode];
|
|
2231
|
-
if (isOperandSource(term)) return createOrderNode(term[operandNode](), "asc");
|
|
2232
|
-
throw new TypeError("The callback of orderBy must return expressions, queries of one value, or sort keys of asc, desc, and orderBy");
|
|
2233
|
-
}
|
|
2234
|
-
/** Creates the sort key node of `expression` in `direction`, an `OrderDirection` checked at runtime. */
|
|
2235
|
-
function createOrderNode(expression, direction) {
|
|
2236
|
-
const placement = typeof direction === "string" && Object.hasOwn(directions, direction) ? directions[direction] : void 0;
|
|
2237
|
-
if (placement === void 0) throw new TypeError(`Unknown sort direction ${JSON.stringify(direction)}`);
|
|
2238
|
-
return Object.freeze({
|
|
2239
|
-
expression,
|
|
2240
|
-
direction: placement.direction,
|
|
2241
|
-
nulls: placement.nulls
|
|
2242
|
-
});
|
|
2243
|
-
}
|
|
2244
|
-
/** Creates a reference to the column `key` of the table of `source`. */
|
|
2245
|
-
function createColumnNode(source, key) {
|
|
2246
|
-
requireColumn(source.table, key);
|
|
2247
|
-
return createNode({
|
|
2248
|
-
kind: "column",
|
|
2249
|
-
source,
|
|
2250
|
-
key
|
|
2251
|
-
});
|
|
2252
|
-
}
|
|
2253
|
-
/**
|
|
2254
|
-
* Creates the references of the callback of `orderBy` to the fields selected by `node`.
|
|
2255
|
-
*
|
|
2256
|
-
* Each is an expression over the node of its projection, so that a sort key of it is the projection itself.
|
|
2257
|
-
* 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.
|
|
2258
|
-
*/
|
|
2259
|
-
function createSelectedRefs(node) {
|
|
2260
|
-
const { projections } = node;
|
|
2261
|
-
if (node.value) return toSelectedExpression(projections[0]);
|
|
2262
|
-
const refs = {};
|
|
2263
|
-
for (const projection of projections) {
|
|
2264
|
-
const { key } = projection;
|
|
2265
|
-
if (isNestedSelection(projection)) {
|
|
2266
|
-
if (isSelectedAsValue(projection.query)) refs[key] = toQueryExpression(projection);
|
|
2267
|
-
else Object.defineProperty(refs, key, {
|
|
2268
|
-
get() {
|
|
2269
|
-
throw new Error(`Field "${key}" holds nested rows and cannot be sorted by`);
|
|
2270
|
-
},
|
|
2271
|
-
enumerable: true
|
|
2272
|
-
});
|
|
2273
|
-
} else refs[key] = toSelectedExpression(projection);
|
|
2274
|
-
}
|
|
2275
|
-
return refs;
|
|
2276
|
-
}
|
|
2277
|
-
/** Creates an expression over the node of a projection of a query, which has a type: only lowering adds projections of nested queries. */
|
|
2278
|
-
function toSelectedExpression(projection) {
|
|
2279
|
-
return toExpression(projection.expression, projection.type);
|
|
2280
|
-
}
|
|
2281
|
-
/** Creates an expression of the value of `field`, a selected aggregate query or query of `exists`, with the type of its value. */
|
|
2282
|
-
function toQueryExpression(field) {
|
|
2283
|
-
const { query } = field;
|
|
2284
|
-
return toExpression(createNode({
|
|
2285
|
-
kind: "nestedQuery",
|
|
2286
|
-
query,
|
|
2287
|
-
db: field.db,
|
|
2288
|
-
field
|
|
2289
|
-
}), query.projections[0].type);
|
|
2290
|
-
}
|
|
2291
|
-
// src/query/pagination.ts
|
|
2292
|
-
/** 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. */
|
|
2293
|
-
function createPageCount(node, method, count) {
|
|
2294
|
-
if (node.rows !== "many") throw new Error(`${method} applies only to a query of many rows${createPaginationHint(node)}`);
|
|
2295
|
-
if (!Number.isSafeInteger(count) || count < 0) throw new RangeError(`${method} takes a non-negative integer, not ${count}`);
|
|
2296
|
-
return createNode({
|
|
2297
|
-
kind: "param",
|
|
2298
|
-
value: count,
|
|
2299
|
-
type: void 0
|
|
2300
|
-
});
|
|
2301
|
-
}
|
|
2302
|
-
/** Creates the hint of the error of `limit` or `offset` of query `node` of one row: the method to call them before, if any. */
|
|
2303
|
-
function createPaginationHint(node) {
|
|
2304
|
-
if (node.relation?.kind === "one") return "";
|
|
2305
|
-
if (isAggregateQuery(node)) return "; call it before count or another aggregate to aggregate the rows of a page";
|
|
2306
|
-
if (isExistsQuery(node)) return "; call it before exists to check the rows of a page";
|
|
2307
|
-
return "; call it before take or get";
|
|
2308
|
-
}
|
|
2309
|
-
// src/query/select.ts
|
|
2310
|
-
/**
|
|
2311
|
-
* Immutable select query of state `S`, unbound or bound to a `Db`.
|
|
2312
|
-
*
|
|
2313
|
-
* Every method returns a query of the same kind and leaves this query unchanged.
|
|
2314
|
-
* An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
|
|
2315
|
-
* A bound query executes on its `Db` when awaited.
|
|
2316
|
-
*/
|
|
2317
|
-
var SelectQuery = class SelectQuery {
|
|
2318
|
-
table;
|
|
2319
|
-
node;
|
|
2320
|
-
context;
|
|
2321
|
-
/** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
|
|
2322
|
-
db;
|
|
2323
|
-
/** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
|
|
2324
|
-
then;
|
|
2325
|
-
constructor(table, node, context, db) {
|
|
2326
|
-
this.table = table;
|
|
2327
|
-
this.node = node;
|
|
2328
|
-
this.context = context;
|
|
2329
|
-
this.db = db;
|
|
2330
|
-
this.then = db === void 0 ? void 0 : executeOnAwait;
|
|
2331
|
-
Object.freeze(this);
|
|
2332
|
-
}
|
|
2333
|
-
where(filter) {
|
|
2334
|
-
return this.derive(this.addFilter(filter));
|
|
2335
|
-
}
|
|
2336
|
-
select(...args) {
|
|
2337
|
-
this.rejectValue("select");
|
|
2338
|
-
return this.derive({ projections: mergeProjections(this.node.projections, this.node.from, args, this.context) });
|
|
2339
|
-
}
|
|
2340
|
-
clearSelect() {
|
|
2341
|
-
this.rejectValue("clearSelect");
|
|
2342
|
-
return this.derive({ projections: [] });
|
|
2343
|
-
}
|
|
2344
|
-
take() {
|
|
2345
|
-
return this.deriveTaken({});
|
|
2346
|
-
}
|
|
2347
|
-
find(...values) {
|
|
2348
|
-
return this.takeWhere(toPrimaryKeyValues(this.table, values));
|
|
2349
|
-
}
|
|
2350
|
-
findBy(values) {
|
|
2351
|
-
return this.takeWhere(toKeyValues(this.table, values));
|
|
2352
|
-
}
|
|
2353
|
-
get(arg) {
|
|
2354
|
-
this.rejectAggregate("get");
|
|
2355
|
-
return this.deriveTaken({
|
|
2356
|
-
projections: [projectValue(this.node.from, arg, "get")],
|
|
2357
|
-
value: true,
|
|
2358
|
-
rows: toValueRows(this.node.rows)
|
|
2359
|
-
});
|
|
2360
|
-
}
|
|
2361
|
-
pluck(arg) {
|
|
2362
|
-
this.rejectAggregate("pluck");
|
|
2363
|
-
return this.derive({
|
|
2364
|
-
projections: [projectValue(this.node.from, arg, "pluck")],
|
|
2365
|
-
value: true,
|
|
2366
|
-
rows: toValueRows(this.node.rows)
|
|
2367
|
-
});
|
|
2368
|
-
}
|
|
2369
|
-
count(arg) {
|
|
2370
|
-
if (arg !== void 0) return this.aggregateBy("count", arg);
|
|
2371
|
-
this.rejectAggregate("count");
|
|
2372
|
-
return this.deriveAggregate(countProjection);
|
|
2373
|
-
}
|
|
2374
|
-
countDistinct(arg) {
|
|
2375
|
-
return this.aggregateBy("countDistinct", arg);
|
|
2376
|
-
}
|
|
2377
|
-
min(arg) {
|
|
2378
|
-
return this.aggregateBy("min", arg);
|
|
2379
|
-
}
|
|
2380
|
-
max(arg) {
|
|
2381
|
-
return this.aggregateBy("max", arg);
|
|
2382
|
-
}
|
|
2383
|
-
sum(arg) {
|
|
2384
|
-
return this.aggregateBy("sum", arg);
|
|
2385
|
-
}
|
|
2386
|
-
avg(arg) {
|
|
2387
|
-
return this.aggregateBy("avg", arg);
|
|
2388
|
-
}
|
|
2389
|
-
every(arg) {
|
|
2390
|
-
return this.aggregateBy("every", arg);
|
|
2391
|
-
}
|
|
2392
|
-
some(arg) {
|
|
2393
|
-
return this.aggregateBy("some", arg);
|
|
2394
|
-
}
|
|
2395
|
-
stringAgg(arg, delimiter) {
|
|
2396
|
-
return this.aggregateBy("stringAgg", arg, delimiter);
|
|
2397
|
-
}
|
|
2398
|
-
exists() {
|
|
2399
|
-
this.rejectAggregate("exists");
|
|
2400
|
-
return this.deriveAggregate(getExistsProjection());
|
|
2401
|
-
}
|
|
2402
|
-
coalesce(fallback) {
|
|
2403
|
-
const { node } = this;
|
|
2404
|
-
if (!node.value) throw new Error("coalesce applies only to a query of values, such as one made by get, pluck, or an aggregate");
|
|
2405
|
-
return this.derive({ projections: [coalesceValue(node.projections[0], fallback)] });
|
|
2406
|
-
}
|
|
2407
|
-
expr() {
|
|
2408
|
-
const value = this[operandNode]();
|
|
2409
|
-
return toExpression(value, this.node.projections[0].type);
|
|
2410
|
-
}
|
|
2411
|
-
orderBy(...args) {
|
|
2412
|
-
return this.derive({ orderBy: addOrder(this.node, this.context, args) });
|
|
2413
|
-
}
|
|
2414
|
-
limit(count) {
|
|
2415
|
-
return this.derive({ limit: createPageCount(this.node, "limit", count) });
|
|
2416
|
-
}
|
|
2417
|
-
offset(count) {
|
|
2418
|
-
return this.derive({ offset: createPageCount(this.node, "offset", count) });
|
|
2419
|
-
}
|
|
2420
|
-
optional() {
|
|
2421
|
-
return this.changeOneRow("optional", "optional");
|
|
2422
|
-
}
|
|
2423
|
-
nullable() {
|
|
2424
|
-
if (this.node.value) throw new Error("nullable applies only to a query of records; use optional after get, pluck, or an aggregate");
|
|
2425
|
-
return this.changeOneRow("nullable", "nullable");
|
|
2426
|
-
}
|
|
2427
|
-
required() {
|
|
2428
|
-
return this.changeOneRow("required", "one");
|
|
2429
|
-
}
|
|
2430
|
-
insert(values) {
|
|
2431
|
-
return createInsert(this, [values], "one", "insert");
|
|
2432
|
-
}
|
|
2433
|
-
insertMany(records) {
|
|
2434
|
-
if (!Array.isArray(records)) throw new TypeError("insertMany takes an array of records");
|
|
2435
|
-
return createInsert(this, records, "many", "insertMany");
|
|
2436
|
-
}
|
|
2437
|
-
update(set) {
|
|
2438
|
-
return createUpdate(this, set);
|
|
2439
|
-
}
|
|
2440
|
-
delete() {
|
|
2441
|
-
return createDelete(this);
|
|
2442
|
-
}
|
|
2443
|
-
all() {
|
|
2444
|
-
if (this.node.rows !== "many") throw new Error("all applies only to a query of many rows");
|
|
2445
|
-
return this.derive({ all: true });
|
|
2446
|
-
}
|
|
2447
|
-
use(callback, ...args) {
|
|
2448
|
-
return requireDerived(this, callback(this, ...args), "The callback of use");
|
|
2449
|
-
}
|
|
2450
|
-
toSQL(options) {
|
|
2451
|
-
return compileToSQL(this, options);
|
|
2452
|
-
}
|
|
2453
|
-
/**
|
|
2454
|
-
* 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`.
|
|
2455
|
-
*
|
|
2456
|
-
* It rejects a query of records or of rows `many`, which `ScalarQuery` rejects in types.
|
|
2457
|
-
*/
|
|
2458
|
-
[operandNode]() {
|
|
2459
|
-
const { node } = this;
|
|
2460
|
-
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");
|
|
2461
|
-
return createNode({
|
|
2462
|
-
kind: "nestedQuery",
|
|
2463
|
-
query: node,
|
|
2464
|
-
db: this.db
|
|
2465
|
-
});
|
|
2466
|
-
}
|
|
2467
|
-
/**
|
|
2468
|
-
* Returns the node of the query as the values of `in`, which lowering replaces with its statement.
|
|
2469
|
-
*
|
|
2470
|
-
* It rejects a query of records, which `ValuesQuery` rejects in types.
|
|
2471
|
-
*/
|
|
2472
|
-
[valuesQueryNode]() {
|
|
2473
|
-
const { node } = this;
|
|
2474
|
-
if (!node.value) throw new TypeError("in takes a query of values, such as of pluck, not a query of records");
|
|
2475
|
-
return createNode({
|
|
2476
|
-
kind: "nestedQuery",
|
|
2477
|
-
query: node,
|
|
2478
|
-
db: this.db
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
/** Creates a query of the same kind and `Db` with the node changes applied. */
|
|
2482
|
-
derive(changes) {
|
|
2483
|
-
return new SelectQuery(this.table, Object.freeze({
|
|
2484
|
-
...this.node,
|
|
2485
|
-
...changes
|
|
2486
|
-
}), this.context, this.db);
|
|
2487
|
-
}
|
|
2488
|
-
/**
|
|
2489
|
-
* Creates a query of the same kind and `Db` with the node changes applied and then those of `take`, in one derived query.
|
|
2490
|
-
*
|
|
2491
|
-
* It adds the changes of `take` to `changes`, which must leave the limit as it is.
|
|
2492
|
-
*/
|
|
2493
|
-
deriveTaken(changes) {
|
|
2494
|
-
if ((changes.rows ?? this.node.rows) === "many") {
|
|
2495
|
-
changes.limit = limitToOne(this.node.limit);
|
|
2496
|
-
changes.rows = "one";
|
|
2497
|
-
}
|
|
2498
|
-
return this.derive(changes);
|
|
2499
|
-
}
|
|
2500
|
-
/** 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. */
|
|
2501
|
-
deriveAggregate(projection) {
|
|
2502
|
-
return this.derive({
|
|
2503
|
-
projections: [projection],
|
|
2504
|
-
rows: "one",
|
|
2505
|
-
value: true,
|
|
2506
|
-
guaranteed: false
|
|
2507
|
-
});
|
|
2508
|
-
}
|
|
2509
|
-
/** 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`. */
|
|
2510
|
-
aggregateBy(method, arg, delimiter) {
|
|
2511
|
-
this.rejectAggregate(method);
|
|
2512
|
-
return this.deriveAggregate(projectAggregate(method, projectValue(this.node.from, arg, method), delimiter));
|
|
2513
|
-
}
|
|
2514
|
-
/** Returns the changes of the node that add `filter` with `AND`, ending the guarantee of the row as `FilteredState` does in types. */
|
|
2515
|
-
addFilter(filter) {
|
|
2516
|
-
const { node } = this;
|
|
2517
|
-
return {
|
|
2518
|
-
filters: addUserFilter(node.filters, filter, node.from, this.context),
|
|
2519
|
-
rows: toFilteredRows(node.rows, node.value, node.guaranteed),
|
|
2520
|
-
guaranteed: false
|
|
2521
|
-
};
|
|
2522
|
-
}
|
|
2523
|
-
/** Creates the query of `find` and `findBy`: `where` of `values`, an object of column values, returning one row like `take`. */
|
|
2524
|
-
takeWhere(values) {
|
|
2525
|
-
const changes = this.addFilter(values);
|
|
2526
|
-
if (changes.rows === "many") changes.rows = "one";
|
|
2527
|
-
return this.derive(changes);
|
|
2528
|
-
}
|
|
2529
|
-
/** Creates the query of one row with rows mode `rows` that `method` returns, rejecting a query of many rows. */
|
|
2530
|
-
changeOneRow(method, rows) {
|
|
2531
|
-
if (this.node.rows === "many") throw new Error(`${method} applies only to a query of one row, such as one made by take`);
|
|
2532
|
-
return this.derive({
|
|
2533
|
-
rows,
|
|
2534
|
-
guaranteed: false
|
|
2535
|
-
});
|
|
2536
|
-
}
|
|
2537
|
-
/** Rejects a change of the selection by `method` of a query made by `get`, `pluck`, or an aggregate method. */
|
|
2538
|
-
rejectValue(method) {
|
|
2539
|
-
if (!this.node.value) return;
|
|
2540
|
-
this.rejectAggregate(method);
|
|
2541
|
-
throw new Error(`${method} cannot be used after get or pluck; call get or pluck to replace the value`);
|
|
2542
|
-
}
|
|
2543
|
-
/** Rejects `method`, which replaces the value or the selection, on a query made by an aggregate method or `exists`. */
|
|
2544
|
-
rejectAggregate(method) {
|
|
2545
|
-
const { node } = this;
|
|
2546
|
-
if (isAggregateQuery(node)) throw new Error(`${method} cannot be used after an aggregate`);
|
|
2547
|
-
if (isExistsQuery(node)) throw new Error(`${method} cannot be used after exists`);
|
|
2548
|
-
}
|
|
2549
|
-
};
|
|
2550
|
-
/**
|
|
2551
|
-
* Returns `result`, which a callback named by `subject` returned for `query`, after checking that it is `query` or a query derived from it.
|
|
2552
|
-
*
|
|
2553
|
-
* An ancestor of `query` also passes, since only the source occurrence is compared.
|
|
2554
|
-
*/
|
|
2555
|
-
function requireDerived(query, result, subject) {
|
|
2556
|
-
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`);
|
|
2557
|
-
return result;
|
|
2558
|
-
}
|
|
2559
|
-
/** Binds a query to `db`, so that awaiting it executes it there. */
|
|
2560
|
-
function bindQuery(query, db) {
|
|
2561
|
-
return new SelectQuery(query.table, query.node, query.context, db);
|
|
2562
|
-
}
|
|
2563
|
-
/** Starts a query over a new occurrence of the table. */
|
|
2564
|
-
function from(table) {
|
|
2565
|
-
return startQuery(table, noContext);
|
|
2566
|
-
}
|
|
2567
|
-
/** 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. */
|
|
2568
|
-
function startQuery(table, context) {
|
|
2569
|
-
return new SelectQuery(table, createQueryNode(Object.freeze({
|
|
2570
|
-
kind: "table",
|
|
2571
|
-
table
|
|
2572
|
-
})), 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);
|
|
2573
2273
|
}
|
|
2574
2274
|
// src/query/sql-query.ts
|
|
2575
2275
|
/**
|
|
@@ -2675,6 +2375,108 @@ function requireType(value, what) {
|
|
|
2675
2375
|
if (!isType(value)) throw new TypeError(`${what} takes a type, such as t.integer, not a column or another value`);
|
|
2676
2376
|
return value;
|
|
2677
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);
|
|
2479
|
+
}
|
|
2678
2480
|
// src/query/values.ts
|
|
2679
2481
|
/**
|
|
2680
2482
|
* Returns the columns and the rows of `VALUES` of an insert of `records`, at least one, into `table`, reading the keys and values of each record when it is called.
|
|
@@ -2710,41 +2512,325 @@ function toInsertRows(table, records, method) {
|
|
|
2710
2512
|
};
|
|
2711
2513
|
}
|
|
2712
2514
|
/**
|
|
2713
|
-
* Returns the assignments of an update of the rows of `source` that `set` gives: the values of its columns, or a callback that receives the columns and returns them, read when it is called.
|
|
2515
|
+
* Returns the assignments of an update of the rows of `source` that `set` gives: the values of its columns, or a callback that receives the columns and returns them, read when it is called.
|
|
2516
|
+
*
|
|
2517
|
+
* A value of `undefined` keeps its column, and an update must set at least one column.
|
|
2518
|
+
* It rejects a key that is not a column of the table of `source`, and a query as a value.
|
|
2519
|
+
*/
|
|
2520
|
+
function toAssignments(source, set) {
|
|
2521
|
+
const { table } = source;
|
|
2522
|
+
const values = typeof set === "function" ? set(createColumnRefs(source)) : set;
|
|
2523
|
+
if (typeof values !== "object" || values === null) throw new TypeError(`update takes the values of the columns of table "${table.name}" or a callback that returns them`);
|
|
2524
|
+
const assignments = [];
|
|
2525
|
+
for (const key of Object.keys(values)) {
|
|
2526
|
+
const value = values[key];
|
|
2527
|
+
if (value !== void 0) assignments.push(Object.freeze({
|
|
2528
|
+
key,
|
|
2529
|
+
value: resolveValue(table, key, value)
|
|
2530
|
+
}));
|
|
2531
|
+
}
|
|
2532
|
+
if (assignments.length === 0) throw new Error(`update takes the value of at least one column of table "${table.name}"; a column given as undefined keeps its value`);
|
|
2533
|
+
return assignments;
|
|
2534
|
+
}
|
|
2535
|
+
/** Returns the node of `value` of the column of `table` under `key`: an expression, or a parameter encoded with the type of the column. */
|
|
2536
|
+
function resolveValue(table, key, value) {
|
|
2537
|
+
const column = requireColumn(table, key);
|
|
2538
|
+
if (value instanceof SelectQuery || value instanceof RecordQuery || value instanceof MutationQuery || value instanceof SqlQuery) throw new TypeError(`Column "${key}" of table "${table.name}" takes a value or an expression, not a query`);
|
|
2539
|
+
return toValueNode(value, column.type);
|
|
2540
|
+
}
|
|
2541
|
+
// src/query/mutation.ts
|
|
2542
|
+
/**
|
|
2543
|
+
* Immutable query of state `S` that inserts, updates, or deletes rows, unbound or bound to a `Db`.
|
|
2544
|
+
*
|
|
2545
|
+
* Without a selection, it resolves to the number of the rows it changes; `select`, `get`, and `pluck` return the fields of those rows instead.
|
|
2546
|
+
* An unbound query is not thenable, so it can be returned from async functions; it is compiled with `toSQL` or executed by a `Db`.
|
|
2547
|
+
* A bound query executes on its `Db` when awaited.
|
|
2548
|
+
*/
|
|
2549
|
+
var MutationQuery = class MutationQuery {
|
|
2550
|
+
table;
|
|
2551
|
+
node;
|
|
2552
|
+
context;
|
|
2553
|
+
/** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
|
|
2554
|
+
db;
|
|
2555
|
+
/** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
|
|
2556
|
+
then;
|
|
2557
|
+
constructor(table, node, context, db) {
|
|
2558
|
+
this.table = table;
|
|
2559
|
+
this.node = node;
|
|
2560
|
+
this.context = context;
|
|
2561
|
+
this.db = db;
|
|
2562
|
+
this.then = db === void 0 ? void 0 : executeOnAwait;
|
|
2563
|
+
Object.freeze(this);
|
|
2564
|
+
}
|
|
2565
|
+
select(...args) {
|
|
2566
|
+
const { node } = this;
|
|
2567
|
+
const { returning } = node;
|
|
2568
|
+
if (returning !== void 0 && node.value) throw new Error("select cannot be used after get or pluck; call get or pluck to replace the value");
|
|
2569
|
+
return this.derive({
|
|
2570
|
+
returning: mergeProjections(returning ?? [], node.target, args, this.context),
|
|
2571
|
+
value: false
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
get(arg) {
|
|
2575
|
+
if (this.node.rows === "many") throw new Error("get applies only to a query that changes one row, such as of insert or find; use pluck to return the values of several rows");
|
|
2576
|
+
return this.deriveValue(arg, "get");
|
|
2577
|
+
}
|
|
2578
|
+
pluck(arg) {
|
|
2579
|
+
return this.deriveValue(arg, "pluck");
|
|
2580
|
+
}
|
|
2581
|
+
toSQL(options) {
|
|
2582
|
+
return compileToSQL(this, options);
|
|
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
|
+
}
|
|
2588
|
+
/** Creates a mutation of the same kind and `Db` with the node changes applied. */
|
|
2589
|
+
derive(changes) {
|
|
2590
|
+
return new MutationQuery(this.table, Object.freeze({
|
|
2591
|
+
...this.node,
|
|
2592
|
+
...changes
|
|
2593
|
+
}), this.context, this.db);
|
|
2594
|
+
}
|
|
2595
|
+
/** Creates the mutation that returns the value of `arg` of `method`, a column key or a callback that receives the columns and returns an expression, of each changed row. */
|
|
2596
|
+
deriveValue(arg, method) {
|
|
2597
|
+
const { node } = this;
|
|
2598
|
+
return this.derive({
|
|
2599
|
+
returning: [projectValue(node.target, arg, method)],
|
|
2600
|
+
value: true,
|
|
2601
|
+
rows: toValueRows(node.rows)
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
/**
|
|
2606
|
+
* Creates the insert of `records` into the table of `query`, which must have no clauses, in rows mode `rows`: `one` for `insert` and `many` for `insertMany`, named by `method`.
|
|
2607
|
+
*
|
|
2608
|
+
* It returns the number of the inserted rows, until a selection returns their fields.
|
|
2609
|
+
*/
|
|
2610
|
+
function createInsert(query, records, rows, method) {
|
|
2611
|
+
const { node } = query;
|
|
2612
|
+
if (!isTableQuery(node)) throw new Error(`${method} applies only to a query of a table without clauses, such as filters or selected fields; select the fields that it returns after it`);
|
|
2613
|
+
const { columns, values } = toInsertRows(node.from.table, records, method);
|
|
2614
|
+
return new MutationQuery(query.table, Object.freeze({
|
|
2615
|
+
kind: "insert",
|
|
2616
|
+
target: node.from,
|
|
2617
|
+
columns,
|
|
2618
|
+
values,
|
|
2619
|
+
returning: void 0,
|
|
2620
|
+
rows,
|
|
2621
|
+
value: false
|
|
2622
|
+
}), query.context, query.db);
|
|
2623
|
+
}
|
|
2624
|
+
/** Creates the update of the rows of `query` that `set` gives, as `update` takes it, in the rows mode of the query. */
|
|
2625
|
+
function createUpdate(query, set) {
|
|
2626
|
+
const { node } = query;
|
|
2627
|
+
checkChangedRows(node, "update");
|
|
2628
|
+
return new MutationQuery(query.table, Object.freeze({
|
|
2629
|
+
kind: "update",
|
|
2630
|
+
target: node.from,
|
|
2631
|
+
set: toAssignments(node.from, set),
|
|
2632
|
+
filters: node.filters,
|
|
2633
|
+
returning: void 0,
|
|
2634
|
+
rows: node.rows,
|
|
2635
|
+
value: false
|
|
2636
|
+
}), query.context, query.db);
|
|
2637
|
+
}
|
|
2638
|
+
/** Creates the delete of the rows of `query`, in the rows mode of the query. */
|
|
2639
|
+
function createDelete(query) {
|
|
2640
|
+
const { node } = query;
|
|
2641
|
+
checkChangedRows(node, "delete");
|
|
2642
|
+
return new MutationQuery(query.table, Object.freeze({
|
|
2643
|
+
kind: "delete",
|
|
2644
|
+
target: node.from,
|
|
2645
|
+
filters: node.filters,
|
|
2646
|
+
returning: void 0,
|
|
2647
|
+
rows: node.rows,
|
|
2648
|
+
value: false
|
|
2649
|
+
}), query.context, query.db);
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Rejects a query whose rows `method`, `update` or `delete`, cannot change: one of a relation, with selected fields, which a mutation selects after it, with an order, a limit, or an offset, or without a filter that restricts its rows unless `all` allows it.
|
|
2653
|
+
*
|
|
2654
|
+
* A filter simplified to `TRUE`, such as `where({})`, restricts no row.
|
|
2655
|
+
*/
|
|
2656
|
+
function checkChangedRows(node, method) {
|
|
2657
|
+
if (node.relation !== void 0) throw new Error(`${method} applies only to a query of a table, not of a relation`);
|
|
2658
|
+
if (node.projections.length > 0) throw new Error(`${method} returns the fields selected after it, not before; call select, get, or pluck after ${method}`);
|
|
2659
|
+
if (node.orderBy.length > 0 || node.limit !== void 0 || node.offset !== void 0) throw new Error(`${method} cannot sort or limit the rows it changes; choose them with where, find, or findBy instead of take, limit, offset, or orderBy`);
|
|
2660
|
+
const predicate = combineFilters(node.filters);
|
|
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`);
|
|
2662
|
+
}
|
|
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"
|
|
2689
|
+
}
|
|
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);
|
|
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));
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Creates the order of `node` with the sort keys of the arguments of `orderBy` after its own.
|
|
2715
|
+
*
|
|
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.
|
|
2717
|
+
*/
|
|
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;
|
|
2733
|
+
}
|
|
2734
|
+
/**
|
|
2735
|
+
* Creates the sort key node of a sort key returned by the callback of `orderBy`.
|
|
2736
|
+
*
|
|
2737
|
+
* A query is converted by the method of `operandNode`, which rejects a query other than of one value.
|
|
2738
|
+
*/
|
|
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)}`);
|
|
2748
|
+
return Object.freeze({
|
|
2749
|
+
expression,
|
|
2750
|
+
direction: placement.direction,
|
|
2751
|
+
nulls: placement.nulls
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
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
|
|
2761
|
+
});
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Creates the references of the callback of `orderBy` to the fields selected by `node`.
|
|
2714
2765
|
*
|
|
2715
|
-
*
|
|
2716
|
-
*
|
|
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.
|
|
2717
2768
|
*/
|
|
2718
|
-
function
|
|
2719
|
-
const {
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
const
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
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);
|
|
2729
2790
|
}
|
|
2730
|
-
|
|
2731
|
-
return assignments;
|
|
2791
|
+
return refs;
|
|
2732
2792
|
}
|
|
2733
|
-
/**
|
|
2734
|
-
function
|
|
2735
|
-
|
|
2736
|
-
if (value instanceof SelectQuery || value instanceof RecordQuery || value instanceof MutationQuery || value instanceof SqlQuery) throw new TypeError(`Column "${key}" of table "${table.name}" takes a value or an expression, not a query`);
|
|
2737
|
-
return toValueNode(value, column.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);
|
|
2738
2796
|
}
|
|
2739
|
-
|
|
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
|
|
2816
|
+
});
|
|
2817
|
+
}
|
|
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";
|
|
2824
|
+
}
|
|
2825
|
+
// src/query/select.ts
|
|
2740
2826
|
/**
|
|
2741
|
-
* Immutable query of state `S
|
|
2827
|
+
* Immutable select query of state `S`, unbound or bound to a `Db`.
|
|
2742
2828
|
*
|
|
2743
|
-
*
|
|
2829
|
+
* Every method returns a query of the same kind and leaves this query unchanged.
|
|
2744
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`.
|
|
2745
2831
|
* A bound query executes on its `Db` when awaited.
|
|
2746
2832
|
*/
|
|
2747
|
-
var
|
|
2833
|
+
var SelectQuery = class SelectQuery {
|
|
2748
2834
|
table;
|
|
2749
2835
|
node;
|
|
2750
2836
|
context;
|
|
@@ -2760,242 +2846,295 @@ var MutationQuery = class MutationQuery {
|
|
|
2760
2846
|
this.then = db === void 0 ? void 0 : executeOnAwait;
|
|
2761
2847
|
Object.freeze(this);
|
|
2762
2848
|
}
|
|
2849
|
+
where(filter) {
|
|
2850
|
+
return this.derive(this.addFilter(filter));
|
|
2851
|
+
}
|
|
2763
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) {
|
|
2764
2919
|
const { node } = this;
|
|
2765
|
-
|
|
2766
|
-
|
|
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
|
+
}
|
|
2966
|
+
toSQL(options) {
|
|
2967
|
+
return compileToSQL(this, options);
|
|
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) {
|
|
2767
3018
|
return this.derive({
|
|
2768
|
-
|
|
2769
|
-
|
|
3019
|
+
projections: [projection],
|
|
3020
|
+
rows: "one",
|
|
3021
|
+
value: true,
|
|
3022
|
+
guaranteed: false
|
|
2770
3023
|
});
|
|
2771
3024
|
}
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
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));
|
|
2775
3029
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
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
|
+
};
|
|
2778
3038
|
}
|
|
2779
|
-
|
|
2780
|
-
|
|
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);
|
|
2781
3044
|
}
|
|
2782
|
-
/**
|
|
2783
|
-
|
|
2784
|
-
throw new
|
|
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
|
+
});
|
|
2785
3052
|
}
|
|
2786
|
-
/**
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
}), this.context, this.db);
|
|
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`);
|
|
2792
3058
|
}
|
|
2793
|
-
/**
|
|
2794
|
-
|
|
3059
|
+
/** Rejects `method`, which replaces the value or the selection, on a query made by an aggregate method or `exists`. */
|
|
3060
|
+
rejectAggregate(method) {
|
|
2795
3061
|
const { node } = this;
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
value: true,
|
|
2799
|
-
rows: toValueRows(node.rows)
|
|
2800
|
-
});
|
|
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`);
|
|
2801
3064
|
}
|
|
2802
3065
|
};
|
|
2803
3066
|
/**
|
|
2804
|
-
*
|
|
2805
|
-
*
|
|
2806
|
-
* It returns the number of the inserted rows, until a selection returns their fields.
|
|
2807
|
-
*/
|
|
2808
|
-
function createInsert(query, records, rows, method) {
|
|
2809
|
-
const { node } = query;
|
|
2810
|
-
if (!isTableQuery(node)) throw new Error(`${method} applies only to a query of a table without clauses, such as filters or selected fields; select the fields that it returns after it`);
|
|
2811
|
-
const { columns, values } = toInsertRows(node.from.table, records, method);
|
|
2812
|
-
return new MutationQuery(query.table, Object.freeze({
|
|
2813
|
-
kind: "insert",
|
|
2814
|
-
target: node.from,
|
|
2815
|
-
columns,
|
|
2816
|
-
values,
|
|
2817
|
-
returning: void 0,
|
|
2818
|
-
rows,
|
|
2819
|
-
value: false
|
|
2820
|
-
}), query.context, query.db);
|
|
2821
|
-
}
|
|
2822
|
-
/** Creates the update of the rows of `query` that `set` gives, as `update` takes it, in the rows mode of the query. */
|
|
2823
|
-
function createUpdate(query, set) {
|
|
2824
|
-
const { node } = query;
|
|
2825
|
-
checkChangedRows(node, "update");
|
|
2826
|
-
return new MutationQuery(query.table, Object.freeze({
|
|
2827
|
-
kind: "update",
|
|
2828
|
-
target: node.from,
|
|
2829
|
-
set: toAssignments(node.from, set),
|
|
2830
|
-
filters: node.filters,
|
|
2831
|
-
returning: void 0,
|
|
2832
|
-
rows: node.rows,
|
|
2833
|
-
value: false
|
|
2834
|
-
}), query.context, query.db);
|
|
2835
|
-
}
|
|
2836
|
-
/** Creates the delete of the rows of `query`, in the rows mode of the query. */
|
|
2837
|
-
function createDelete(query) {
|
|
2838
|
-
const { node } = query;
|
|
2839
|
-
checkChangedRows(node, "delete");
|
|
2840
|
-
return new MutationQuery(query.table, Object.freeze({
|
|
2841
|
-
kind: "delete",
|
|
2842
|
-
target: node.from,
|
|
2843
|
-
filters: node.filters,
|
|
2844
|
-
returning: void 0,
|
|
2845
|
-
rows: node.rows,
|
|
2846
|
-
value: false
|
|
2847
|
-
}), query.context, query.db);
|
|
2848
|
-
}
|
|
2849
|
-
/**
|
|
2850
|
-
* Rejects a query whose rows `method`, `update` or `delete`, cannot change: one of a relation, with selected fields, which a mutation selects after it, with an order, a limit, or an offset, or without a filter that restricts its rows unless `all` allows it.
|
|
2851
|
-
*
|
|
2852
|
-
* A filter simplified to `TRUE`, such as `where({})`, restricts no row.
|
|
2853
|
-
*/
|
|
2854
|
-
function checkChangedRows(node, method) {
|
|
2855
|
-
if (node.relation !== void 0) throw new Error(`${method} applies only to a query of a table, not of a relation`);
|
|
2856
|
-
if (node.projections.length > 0) throw new Error(`${method} returns the fields selected after it, not before; call select, get, or pluck after ${method}`);
|
|
2857
|
-
if (node.orderBy.length > 0 || node.limit !== void 0 || node.offset !== void 0) throw new Error(`${method} cannot sort or limit the rows it changes; choose them with where, find, or findBy instead of take, limit, offset, or orderBy`);
|
|
2858
|
-
const predicate = combineFilters(node.filters);
|
|
2859
|
-
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`);
|
|
2860
|
-
}
|
|
2861
|
-
// src/query/projections.ts
|
|
2862
|
-
/**
|
|
2863
|
-
* Creates the fields of `select` over `source`, appending new keys and replacing existing ones in place.
|
|
3067
|
+
* Returns `result`, which a callback named by `subject` returned for `query`, after checking that it is `query` or a query derived from it.
|
|
2864
3068
|
*
|
|
2865
|
-
*
|
|
3069
|
+
* An ancestor of `query` also passes, since only the source occurrence is compared.
|
|
2866
3070
|
*/
|
|
2867
|
-
function
|
|
2868
|
-
|
|
2869
|
-
const first = args[0];
|
|
2870
|
-
if (typeof first === "function") addSelection(result, first(createSelectionRefs(source, context)));
|
|
2871
|
-
else {
|
|
2872
|
-
const keys = first === "*" ? Object.keys(source.table.columns) : args;
|
|
2873
|
-
for (const key of keys) setProjection(result, projectColumn(source, key));
|
|
2874
|
-
}
|
|
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`);
|
|
2875
3073
|
return result;
|
|
2876
3074
|
}
|
|
2877
|
-
/**
|
|
2878
|
-
function
|
|
2879
|
-
|
|
2880
|
-
if (typeof key === "symbol") throw new TypeError("A selected field must have a string key");
|
|
2881
|
-
setProjection(projections, selectValue(key, selection[key]));
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
/**
|
|
2885
|
-
* Creates the projection of the value of `get` or `pluck`, or of the argument of an aggregate method, such as `max`, over `source`.
|
|
2886
|
-
*
|
|
2887
|
-
* `arg` is the argument of `method`: a column key, or a callback that receives the columns and returns an expression.
|
|
2888
|
-
*/
|
|
2889
|
-
function projectValue(source, arg, method) {
|
|
2890
|
-
if (typeof arg !== "function") return projectColumn(source, arg);
|
|
2891
|
-
const value = arg(createColumnRefs(source));
|
|
2892
|
-
if (!isExpression(value)) throw new TypeError(`The callback of ${method} must return an expression`);
|
|
2893
|
-
const type = getType(value);
|
|
2894
|
-
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)`);
|
|
2895
|
-
return projectExpression("value", value, type);
|
|
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);
|
|
2896
3078
|
}
|
|
2897
|
-
/**
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
* The projection of an aggregate keeps its call, which lowering builds, and adds the fallback after its earlier ones.
|
|
2901
|
-
* The projection of `exists` stays as it is, since `EXISTS` is never `NULL`.
|
|
2902
|
-
*/
|
|
2903
|
-
function coalesceValue(field, fallback) {
|
|
2904
|
-
if (fallback === null || fallback === void 0) throw new TypeError(`coalesce takes a value other than ${String(fallback)}`);
|
|
2905
|
-
if (isExistsProjection(field)) return field;
|
|
2906
|
-
const value = toValueNode(fallback, field.type);
|
|
2907
|
-
if (isAggregateProjection(field)) return addFallback(field, value);
|
|
2908
|
-
return Object.freeze({
|
|
2909
|
-
key: field.key,
|
|
2910
|
-
expression: createList("coalesce(", [field.expression, value], ")"),
|
|
2911
|
-
type: field.type
|
|
2912
|
-
});
|
|
3079
|
+
/** Starts a query over a new occurrence of the table. */
|
|
3080
|
+
function from(table) {
|
|
3081
|
+
return startQuery(table, noContext);
|
|
2913
3082
|
}
|
|
2914
|
-
/**
|
|
2915
|
-
function
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
});
|
|
2921
|
-
if (value instanceof RecordQuery) throw new TypeError(`Selected field "${key}" is a query of $select, which cannot be nested; select its fields directly instead`);
|
|
2922
|
-
if (value instanceof MutationQuery) throw new TypeError(`Selected field "${key}" is a query of insert, update, or delete, which cannot be nested`);
|
|
2923
|
-
if (value instanceof SqlQuery) throw new TypeError(`Selected field "${key}" is a query of $query, which cannot be nested`);
|
|
2924
|
-
if (!isExpression(value)) throw new TypeError(`Selected field "${key}" must be an expression or a query`);
|
|
2925
|
-
const type = getType(value);
|
|
2926
|
-
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)`);
|
|
2927
|
-
return projectExpression(key, value, type);
|
|
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);
|
|
2928
3089
|
}
|
|
2929
|
-
|
|
2930
|
-
function
|
|
2931
|
-
|
|
2932
|
-
return Object.freeze({
|
|
2933
|
-
key,
|
|
2934
|
-
expression: createNode({
|
|
2935
|
-
kind: "column",
|
|
2936
|
-
source,
|
|
2937
|
-
key
|
|
2938
|
-
}),
|
|
2939
|
-
type
|
|
2940
|
-
});
|
|
3090
|
+
// src/expression/functions.ts
|
|
3091
|
+
function coalesce(first, ...rest) {
|
|
3092
|
+
return callFunction("coalesce", first, rest);
|
|
2941
3093
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
return Object.freeze({
|
|
2945
|
-
key,
|
|
2946
|
-
expression: toNode(value),
|
|
2947
|
-
type
|
|
2948
|
-
});
|
|
3094
|
+
function greatest(first, ...rest) {
|
|
3095
|
+
return callFunction("greatest", first, rest);
|
|
2949
3096
|
}
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
const { key } = projection;
|
|
2953
|
-
if (!isRecordKey(key)) throw new Error(`Field "${key}" cannot be selected: it is not a valid record key`);
|
|
2954
|
-
for (let index = 0; index < projections.length; index++) if (projections[index].key === key) {
|
|
2955
|
-
projections[index] = projection;
|
|
2956
|
-
return;
|
|
2957
|
-
}
|
|
2958
|
-
projections.push(projection);
|
|
3097
|
+
function least(first, ...rest) {
|
|
3098
|
+
return callFunction("least", first, rest);
|
|
2959
3099
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
node;
|
|
2969
|
-
/** `Db` that executes the query when it is awaited; `undefined` for an unbound query. */
|
|
2970
|
-
db;
|
|
2971
|
-
/** Executes a bound query on its `Db`; `undefined` for an unbound query, so that it is not thenable. */
|
|
2972
|
-
then;
|
|
2973
|
-
constructor(node, db) {
|
|
2974
|
-
this.node = node;
|
|
2975
|
-
this.db = db;
|
|
2976
|
-
this.then = db === void 0 ? void 0 : executeOnAwait;
|
|
2977
|
-
Object.freeze(this);
|
|
2978
|
-
}
|
|
2979
|
-
toSQL(options) {
|
|
2980
|
-
return compileToSQL(this, options);
|
|
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));
|
|
2981
3108
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
3109
|
+
if (otherwise !== void 0) {
|
|
3110
|
+
fragments.push(" ELSE ");
|
|
3111
|
+
values.push(toCastValueNode(otherwise, type));
|
|
2985
3112
|
}
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
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;
|
|
2999
3138
|
}
|
|
3000
3139
|
// src/query/relations.ts
|
|
3001
3140
|
/** Creates the contexts of the queries of the tables under `keys` in a schema of relations `relations`, by table key. */
|
|
@@ -3141,12 +3280,7 @@ function param(type, value) {
|
|
|
3141
3280
|
const valueType = getType(type);
|
|
3142
3281
|
return toExpression(toValueNode(value, valueType), valueType);
|
|
3143
3282
|
}
|
|
3144
|
-
|
|
3145
|
-
return toExpression(node.kind === "param" ? createNode({
|
|
3146
|
-
kind: "cast",
|
|
3147
|
-
operand: node,
|
|
3148
|
-
type
|
|
3149
|
-
}) : node, type);
|
|
3283
|
+
return toExpression(toCastValueNode(value, type), type);
|
|
3150
3284
|
}
|
|
3151
3285
|
function isTemplateStrings(value) {
|
|
3152
3286
|
return Array.isArray(value);
|
|
@@ -3897,4 +4031,4 @@ function createLockStatement(key) {
|
|
|
3897
4031
|
function defineHelper(_table, helper) {
|
|
3898
4032
|
return (query, ...args) => requireDerived(query, helper(query, ...args), "The callback of a helper");
|
|
3899
4033
|
}
|
|
3900
|
-
export { NotFoundError, RequiredRelationError, ResultDecodeError, and, asc, compare, createDatabase, createStatement, defineFilter, defineGenericOperator, defineHelper, defineSchema, defineTable, defineType, desc, equalityOperators, from, identifier, 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 };
|