qubu 0.0.0 → 0.3.3

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.
Files changed (109) hide show
  1. package/dist/canonical-BbnqavJm.mjs +972 -0
  2. package/dist/codegen.d.mts +107 -0
  3. package/dist/codegen.mjs +1199 -0
  4. package/dist/column-CXMxx8Hq.mjs +118 -0
  5. package/dist/complete-D5Djh-zo.mjs +1788 -0
  6. package/dist/complete-types-BdFqUfbb.d.mts +371 -0
  7. package/dist/core.d.mts +33 -0
  8. package/dist/core.mjs +36 -0
  9. package/dist/ddl.d.mts +118 -0
  10. package/dist/ddl.mjs +1120 -0
  11. package/dist/dialect-b2-Z6uBF.mjs +13 -0
  12. package/dist/diff.d.mts +2 -0
  13. package/dist/diff.mjs +883 -0
  14. package/dist/drizzle-mysql.d.mts +24 -0
  15. package/dist/drizzle-mysql.mjs +72 -0
  16. package/dist/drizzle-postgres.d.mts +24 -0
  17. package/dist/drizzle-postgres.mjs +73 -0
  18. package/dist/drizzle-sqlite.d.mts +24 -0
  19. package/dist/drizzle-sqlite.mjs +82 -0
  20. package/dist/drizzle.d.mts +13 -0
  21. package/dist/drizzle.mjs +2 -0
  22. package/dist/errors-BGCoLe_r.mjs +14 -0
  23. package/dist/errors-Dxv73YJu.mjs +26 -0
  24. package/dist/explain-CkIK13L_.mjs +98 -0
  25. package/dist/index-Ds7-mhJi.d.mts +146 -0
  26. package/dist/index-Dug5HnLB.d.mts +204 -0
  27. package/dist/index.d.mts +2 -0
  28. package/dist/index.mjs +1276 -0
  29. package/dist/introspection.d.mts +117 -0
  30. package/dist/introspection.mjs +4341 -0
  31. package/dist/json-CUZlv4HT.mjs +169 -0
  32. package/dist/migration.d.mts +215 -0
  33. package/dist/migration.mjs +1161 -0
  34. package/dist/mysql-DqkqXB6A.mjs +355 -0
  35. package/dist/mysql.d.mts +5 -0
  36. package/dist/mysql.mjs +50 -0
  37. package/dist/naming-QVCOnSj2.mjs +20 -0
  38. package/dist/omit-OxV58AwX.mjs +5 -0
  39. package/dist/on-conflict-BxnxubMb.mjs +143 -0
  40. package/dist/postgres-DEBBeh52.mjs +235 -0
  41. package/dist/postgres.d.mts +123 -0
  42. package/dist/postgres.mjs +52 -0
  43. package/dist/registry-BufIskVN.mjs +569 -0
  44. package/dist/relational-DCZrrNia.mjs +77 -0
  45. package/dist/runtime-BTr-MTlo.mjs +193 -0
  46. package/dist/schema.d.mts +2 -0
  47. package/dist/schema.mjs +5 -0
  48. package/dist/serialize-PF1cfH2P.mjs +556 -0
  49. package/dist/snapshot-CWPgzxNx.mjs +392 -0
  50. package/dist/snapshot.d.mts +4 -0
  51. package/dist/snapshot.mjs +8 -0
  52. package/dist/source-DUoJVXmL.mjs +68 -0
  53. package/dist/sqlite-BU6DBxef.mjs +320 -0
  54. package/dist/sqlite.d.mts +5 -0
  55. package/dist/sqlite.mjs +48 -0
  56. package/dist/standard-BTVYKh_F.mjs +12 -0
  57. package/dist/table-llv9tsZ8.mjs +384 -0
  58. package/dist/types-4Q076HKo.d.mts +3636 -0
  59. package/dist/types-BX0mckiU.d.mts +644 -0
  60. package/dist/types-CO1KaRNc.d.mts +46 -0
  61. package/dist/types-Cec0xzo4.mjs +116 -0
  62. package/dist/value-BvilP0oz.mjs +29 -0
  63. package/dist/vite/ambient.d.ts +449 -0
  64. package/dist/vite.d.mts +35 -0
  65. package/dist/vite.mjs +498 -0
  66. package/docs/config.json +21 -0
  67. package/docs/dialects-and-execution.md +427 -0
  68. package/docs/getting-started.md +96 -0
  69. package/docs/guides/compose-queries.md +225 -0
  70. package/docs/guides/drizzle.md +129 -0
  71. package/docs/guides/extensions/dialects.md +40 -0
  72. package/docs/guides/extensions/overview.md +36 -0
  73. package/docs/guides/extensions/sources-and-clauses.md +82 -0
  74. package/docs/guides/extensions/typed-expressions.md +94 -0
  75. package/docs/guides/extensions/unsafe-syntax.md +27 -0
  76. package/docs/guides/json.md +65 -0
  77. package/docs/guides/mutations.md +138 -0
  78. package/docs/guides/select/conditions.md +110 -0
  79. package/docs/guides/select/grouping-and-windows.md +82 -0
  80. package/docs/guides/select/ordering-and-pagination.md +78 -0
  81. package/docs/guides/select/overview.md +139 -0
  82. package/docs/guides/sql-templates.md +157 -0
  83. package/docs/guides/vite-plugin.md +92 -0
  84. package/docs/index.md +111 -0
  85. package/docs/query-model/fragments.md +109 -0
  86. package/docs/query-model/result-shapes.md +125 -0
  87. package/docs/query-model/source-scope.md +182 -0
  88. package/docs/reference/introspection-support.md +196 -0
  89. package/docs/reference/mysql-snapshot.md +55 -0
  90. package/docs/reference/postgres-snapshot.md +61 -0
  91. package/docs/reference/sqlite-snapshot.md +53 -0
  92. package/docs/reference/supported-surface.md +104 -0
  93. package/docs/schema/catalog-model.md +58 -0
  94. package/docs/schema/code-generation.md +139 -0
  95. package/docs/schema/columns-and-writes.md +130 -0
  96. package/docs/schema/constraints-and-indexes.md +180 -0
  97. package/docs/schema/ddl-emission.md +75 -0
  98. package/docs/schema/diff.md +75 -0
  99. package/docs/schema/introspection.md +214 -0
  100. package/docs/schema/migration-plans.md +76 -0
  101. package/docs/schema/snapshots.md +93 -0
  102. package/docs/schema/storage-and-schema-sql.md +104 -0
  103. package/docs/schema/tables-and-names.md +106 -0
  104. package/docs/sql-semantic-types.md +141 -0
  105. package/docs/troubleshooting.md +151 -0
  106. package/package.json +132 -10
  107. package/skills/qubu/SKILL.md +42 -0
  108. package/skills/qubu/agents/openai.yaml +6 -0
  109. package/readme.md +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,1276 @@
1
+ import { a as assertDialectCapability } from "./json-CUZlv4HT.mjs";
2
+ import { c as render, n as call, o as cast, r as schemaCall, t as createClause } from "./types-Cec0xzo4.mjs";
3
+ import { n as queryValidationError, t as QueryValidationError } from "./errors-Dxv73YJu.mjs";
4
+ import { c as makeExpression, h as parenthesize, i as identifier, l as makeSchemaExpression, m as isFragment, o as isExpression, p as fragment, t as createColumnReference, u as markExpressionCategory } from "./column-CXMxx8Hq.mjs";
5
+ import { i as value, t as asValue } from "./value-BvilP0oz.mjs";
6
+ import { a as isDistinctFrom, c as lt, d as notLike, f as expressionOperand, i as gte, l as lte, m as renderOperands, n as eq, o as isNotDistinctFrom, p as isNullOperand, r as gt, s as like, u as ne } from "./relational-DCZrrNia.mjs";
7
+ import { t as omit } from "./omit-OxV58AwX.mjs";
8
+ import { n as snakeCaseIdentifier, t as resolveSqlNames } from "./naming-QVCOnSj2.mjs";
9
+ import { r as exposeColumns, t as createSource } from "./source-DUoJVXmL.mjs";
10
+ import { S as identityColumn, T as lateral, a as column, b as externalGeneratedColumn, c as json, d as nullable, f as numeric, g as uuid, h as timestamp, i as boolean, l as nativeColumn, m as text, n as bigint, o as date, p as portableStorage, r as binary, s as integer, t as table, u as nativeStorage, w as alias, x as generatedColumn, y as externalDefault } from "./table-llv9tsZ8.mjs";
11
+ import { C as index, S as unsafeSchemaSql, c as check, d as references, f as unique, i as schema, l as foreignKey, o as catalogCheck, p as uniqueConstraint, s as catalogForeignKey, u as primaryKey } from "./registry-BufIskVN.mjs";
12
+ //#region src/execution.ts
13
+ function qubu(adapter) {
14
+ const client = createClient(adapter);
15
+ if (!isTransactionalQueryAdapter(adapter)) return client;
16
+ return Object.freeze({
17
+ ...client,
18
+ transaction(callback, options) {
19
+ return adapter.transaction(async (transactionAdapter) => callback(createClient(transactionAdapter)), options);
20
+ }
21
+ });
22
+ }
23
+ function createClient(adapter) {
24
+ const client = {
25
+ adapter,
26
+ execute(query, options) {
27
+ return execute(query, adapter, options);
28
+ },
29
+ rows(query, options) {
30
+ return executeRows(query, adapter, options);
31
+ }
32
+ };
33
+ const streaming = isStreamingQueryAdapter(adapter);
34
+ const explainable = isExplainableQueryAdapter(adapter);
35
+ if (!streaming && !explainable) return Object.freeze(client);
36
+ return Object.freeze({
37
+ ...client,
38
+ ...streaming ? { stream(query, options) {
39
+ return stream(query, adapter, options);
40
+ } } : {},
41
+ ...explainable ? { explain(query, options) {
42
+ return explain(query, adapter, options);
43
+ } } : {}
44
+ });
45
+ }
46
+ function isTransactionalQueryAdapter(adapter) {
47
+ return typeof adapter.transaction === "function";
48
+ }
49
+ function isStreamingQueryAdapter(adapter) {
50
+ return typeof adapter.stream === "function";
51
+ }
52
+ function isExplainableQueryAdapter(adapter) {
53
+ return typeof adapter.explain === "function";
54
+ }
55
+ function execute(first, second, options = {}) {
56
+ return executeInternal(first, second, options);
57
+ }
58
+ async function executeRows(first, second, options = {}) {
59
+ return (await executeInternal(first, second, options)).rows;
60
+ }
61
+ async function explain(first, second, options = {}) {
62
+ const query = isQuery(first) ? first : second;
63
+ const adapter = isQuery(first) ? second : first;
64
+ return adapter.explain(createExplainRequest(query, adapter, options));
65
+ }
66
+ function stream(first, second, options = {}) {
67
+ const query = isQuery(first) ? first : second;
68
+ const adapter = isQuery(first) ? second : first;
69
+ assertStreamableQuery(query);
70
+ return adapter.stream(createExecutionRequest(query, adapter, options));
71
+ }
72
+ async function executeInternal(first, second, options) {
73
+ const query = isQuery(first) ? first : second;
74
+ const adapter = isQuery(first) ? second : first;
75
+ const request = createExecutionRequest(query, adapter, options);
76
+ return adapter.execute(request);
77
+ }
78
+ function createExecutionRequest(query, adapter, options) {
79
+ const statement = render(query, { dialect: options.dialect ?? adapter.dialect });
80
+ return Object.freeze({
81
+ statement,
82
+ queryKind: query.queryKind,
83
+ ...options.signal === void 0 ? {} : { signal: options.signal }
84
+ });
85
+ }
86
+ function createExplainRequest(query, adapter, options) {
87
+ const request = createExecutionRequest(query, adapter, options);
88
+ if (options.analyze === true && query.queryKind !== "select" && query.queryKind !== "set") throw queryValidationError({
89
+ code: "invalid-explain-query",
90
+ context: "execution.explain",
91
+ path: ["analyze"],
92
+ message: `EXPLAIN ANALYZE is only available for read queries; received ${query.queryKind}`,
93
+ hint: "Remove analyze for a plan-only mutation EXPLAIN."
94
+ });
95
+ const dialect = options.dialect ?? adapter.dialect;
96
+ if (dialect.explain === void 0) throw queryValidationError({
97
+ code: "unsupported-explain-dialect",
98
+ context: "execution.explain",
99
+ path: ["dialect"],
100
+ message: `Dialect "${dialect.name}" does not provide an EXPLAIN rendering policy`,
101
+ hint: "Use PostgreSQL, SQLite, MySQL, or a custom dialect with an explain policy."
102
+ });
103
+ const statement = Object.freeze({
104
+ text: dialect.explain.render(request.statement.text, query.queryKind, options),
105
+ parameters: request.statement.parameters
106
+ });
107
+ return Object.freeze({
108
+ ...request,
109
+ statement
110
+ });
111
+ }
112
+ function assertStreamableQuery(query) {
113
+ if (query.queryKind === "select" || query.queryKind === "set") return;
114
+ throw queryValidationError({
115
+ code: "invalid-stream-query",
116
+ context: "execution.stream",
117
+ path: ["queryKind"],
118
+ message: `Only SELECT and set-operation queries can be streamed; received ${query.queryKind}`,
119
+ hint: "Use execute() or executeRows() for mutation queries."
120
+ });
121
+ }
122
+ function isQuery(value) {
123
+ return "render" in value && typeof value.render === "function";
124
+ }
125
+ //#endregion
126
+ //#region src/expressions/case.ts
127
+ function caseWhen(condition, thenValue, elseValue) {
128
+ const thenExpression = asValue(thenValue);
129
+ const elseExpression = asValue(elseValue);
130
+ return makeSchemaExpression("operator", (context) => {
131
+ context.append("CASE WHEN ");
132
+ context.render(condition);
133
+ context.append(" THEN ");
134
+ context.render(thenExpression);
135
+ context.append(" ELSE ");
136
+ context.render(elseExpression);
137
+ context.append(" END");
138
+ });
139
+ }
140
+ //#endregion
141
+ //#region src/expressions/sql.ts
142
+ function sqlTemplate(strings, values) {
143
+ return makeExpression("sql", (context) => {
144
+ strings.forEach((text, index) => {
145
+ context.append(text);
146
+ if (index >= values.length) return;
147
+ const value = values[index];
148
+ if (!isFragment(value)) {
149
+ context.parameter(value);
150
+ return;
151
+ }
152
+ if (isQueryFragment(value)) context.renderRelation(value);
153
+ else context.render(value);
154
+ });
155
+ });
156
+ }
157
+ function createSqlTag() {
158
+ return (strings, ...values) => sqlTemplate(strings, values);
159
+ }
160
+ const sqlTag = createSqlTag();
161
+ sqlTag.type = createSqlTag;
162
+ /**
163
+ * Build trusted SQL syntax while binding every ordinary substitution as a
164
+ * parameter. Qubu fragments remain composable substitutions.
165
+ *
166
+ * @remarks Template text is trusted and is not parsed. Use `identifier()` from
167
+ * `qubu/core` for runtime identifiers and `unsafeExpression()` for deliberately
168
+ * dynamic syntax. Use `sql.type<Output, SqlType>()` when the fragment's result
169
+ * is known.
170
+ */
171
+ const sql = Object.freeze(sqlTag);
172
+ function isQueryFragment(value) {
173
+ return "queryKind" in value && "row" in value;
174
+ }
175
+ //#endregion
176
+ //#region src/expressions/functions/aggregate.ts
177
+ function count(expression) {
178
+ return makeExpression("function", (context) => {
179
+ context.append("COUNT(");
180
+ if (expression) context.render(expression);
181
+ else context.append("*");
182
+ context.append(")");
183
+ }, "aggregate");
184
+ }
185
+ function countDistinct(expression) {
186
+ return makeExpression("function", (context) => {
187
+ context.append("COUNT(DISTINCT ");
188
+ context.render(expression);
189
+ context.append(")");
190
+ }, "aggregate");
191
+ }
192
+ function sum(expression) {
193
+ return markExpressionCategory(call("SUM", expression), "aggregate");
194
+ }
195
+ function avg(expression) {
196
+ return markExpressionCategory(call("AVG", expression), "aggregate");
197
+ }
198
+ function min(expression) {
199
+ return markExpressionCategory(call("MIN", expression), "aggregate");
200
+ }
201
+ function max(expression) {
202
+ return markExpressionCategory(call("MAX", expression), "aggregate");
203
+ }
204
+ //#endregion
205
+ //#region src/expressions/functions/string.ts
206
+ function lower(expression) {
207
+ return schemaCall("LOWER", expression);
208
+ }
209
+ function upper(expression) {
210
+ return schemaCall("UPPER", expression);
211
+ }
212
+ function coalesce(...expressions) {
213
+ return schemaCall("COALESCE", ...expressions);
214
+ }
215
+ function concat(...argumentsToConcat) {
216
+ return schemaCall("CONCAT", ...argumentsToConcat);
217
+ }
218
+ //#endregion
219
+ //#region src/expressions/functions/window.ts
220
+ function over(expression, window) {
221
+ return makeExpression("function", (context) => {
222
+ context.render(expression);
223
+ context.append(" OVER (");
224
+ let hasPart = false;
225
+ if (window?.partitionBy && window.partitionBy.length > 0) {
226
+ context.append("PARTITION BY ");
227
+ window.partitionBy.forEach((part, index) => {
228
+ if (index > 0) context.append(", ");
229
+ context.render(part);
230
+ });
231
+ hasPart = true;
232
+ }
233
+ if (window?.orderBy && window.orderBy.length > 0) {
234
+ if (hasPart) context.append(" ");
235
+ context.append("ORDER BY ");
236
+ window.orderBy.forEach((part, index) => {
237
+ if (index > 0) context.append(", ");
238
+ context.render(part);
239
+ });
240
+ }
241
+ context.append(")");
242
+ }, "window");
243
+ }
244
+ function rowNumber() {
245
+ return markExpressionCategory(call("ROW_NUMBER"), "window");
246
+ }
247
+ function rank() {
248
+ return markExpressionCategory(call("RANK"), "window");
249
+ }
250
+ function denseRank() {
251
+ return markExpressionCategory(call("DENSE_RANK"), "window");
252
+ }
253
+ //#endregion
254
+ //#region src/expressions/subquery.ts
255
+ function scalar(query) {
256
+ if (Object.keys(query.row).length !== 1) throw queryValidationError({
257
+ code: "invalid-subquery",
258
+ context: "expression.scalar.query",
259
+ path: [
260
+ "scalar",
261
+ "query",
262
+ "row"
263
+ ],
264
+ message: "scalar() requires a query with exactly one selected column",
265
+ hint: "Select exactly one named field before wrapping the query in scalar()."
266
+ });
267
+ return makeExpression("subquery", (context) => context.renderRelation(parenthesize(query)));
268
+ }
269
+ //#endregion
270
+ //#region src/expressions/json.ts
271
+ const jsonPathBrand = Symbol("json-path");
272
+ /**
273
+ * Build a root-relative JSON path from object keys and array indexes.
274
+ *
275
+ * @throws {TypeError} When an index is negative, fractional, or unsafe.
276
+ */
277
+ function jsonPath(...segments) {
278
+ for (const [index, segment] of segments.entries()) if (typeof segment === "number" && (!Number.isSafeInteger(segment) || segment < 0)) throw queryValidationError({
279
+ code: "invalid-json-path",
280
+ context: "expression.json-path",
281
+ path: ["jsonPath", index],
282
+ message: "JSON path indexes must be non-negative safe integers",
283
+ hint: "Use a non-negative safe integer for each JSON array index."
284
+ });
285
+ return Object.freeze({
286
+ [jsonPathBrand]: true,
287
+ pathKind: "json",
288
+ segments: Object.freeze([...segments])
289
+ });
290
+ }
291
+ function jsonScalar(document, path, kind) {
292
+ return makeSchemaExpression("function", (context) => {
293
+ assertDialectCapability(context.dialect, "json");
294
+ if (!context.dialect.json) throw new Error(`Dialect "${context.dialect.name}" advertises JSON support without a JSON renderer`);
295
+ context.dialect.json.renderScalar(context, document, path.segments, kind);
296
+ });
297
+ }
298
+ /** Extract a JSON string, returning SQL NULL for missing, null, or non-string values. */
299
+ function jsonText(document, path) {
300
+ return jsonScalar(document, path, "text");
301
+ }
302
+ /** Extract a JSON number, returning SQL NULL for missing, null, or non-number values. */
303
+ function jsonNumber(document, path) {
304
+ return jsonScalar(document, path, "number");
305
+ }
306
+ /** Extract a JSON boolean, returning SQL NULL for missing, null, or non-boolean values. */
307
+ function jsonBoolean(document, path) {
308
+ return jsonScalar(document, path, "boolean");
309
+ }
310
+ /** Test whether a path exists, including when its value is JSON null. */
311
+ function jsonExists(document, path) {
312
+ return makeSchemaExpression("function", (context) => {
313
+ assertDialectCapability(context.dialect, "json");
314
+ if (!context.dialect.json) throw new Error(`Dialect "${context.dialect.name}" advertises JSON support without a JSON renderer`);
315
+ context.dialect.json.renderExists(context, document, path.segments);
316
+ });
317
+ }
318
+ //#endregion
319
+ //#region src/expressions/operators/arithmetic.ts
320
+ function arithmetic(operator, left, right) {
321
+ const rightExpression = expressionOperand(right);
322
+ return makeSchemaExpression("operator", (context) => {
323
+ context.append("(");
324
+ context.render(left);
325
+ context.append(` ${operator} `);
326
+ context.render(rightExpression);
327
+ context.append(")");
328
+ });
329
+ }
330
+ const add = (left, right) => arithmetic("+", left, right);
331
+ const subtract = (left, right) => arithmetic("-", left, right);
332
+ const multiply = (left, right) => arithmetic("*", left, right);
333
+ const divide = (left, right) => arithmetic("/", left, right);
334
+ const modulo = (left, right) => arithmetic("%", left, right);
335
+ //#endregion
336
+ //#region src/expressions/operators/logic.ts
337
+ function composeConditions(conditions, separator, name) {
338
+ if (conditions.length === 0) throw queryValidationError({
339
+ code: "invalid-boolean-expression",
340
+ context: `expression.${name}`,
341
+ path: [name],
342
+ message: `${name}() requires at least one condition`,
343
+ hint: `Pass at least one condition to ${name}(), or use omit for a conditional predicate.`
344
+ });
345
+ const presentConditions = conditions.filter((condition) => condition !== omit);
346
+ if (presentConditions.length === 0) return omit;
347
+ return makeSchemaExpression("operator", (context) => {
348
+ context.append("(");
349
+ renderOperands(context, presentConditions, separator);
350
+ context.append(")");
351
+ });
352
+ }
353
+ function and(...conditions) {
354
+ return composeConditions(conditions, " AND ", "and");
355
+ }
356
+ function or(...conditions) {
357
+ return composeConditions(conditions, " OR ", "or");
358
+ }
359
+ function not(condition) {
360
+ return makeSchemaExpression("operator", (context) => {
361
+ context.append("(NOT ");
362
+ context.render(condition);
363
+ context.append(")");
364
+ });
365
+ }
366
+ //#endregion
367
+ //#region src/expressions/operators/comparison/range.ts
368
+ function between(expression, lower, upper) {
369
+ if (isNullOperand(lower) || isNullOperand(upper)) throw new TypeError("between() does not accept NULL bounds; use an explicit NULL predicate");
370
+ const lowerExpression = expressionOperand(lower);
371
+ const upperExpression = expressionOperand(upper);
372
+ return makeSchemaExpression("operator", (context) => {
373
+ context.append("(");
374
+ context.render(expression);
375
+ context.append(" BETWEEN ");
376
+ context.render(lowerExpression);
377
+ context.append(" AND ");
378
+ context.render(upperExpression);
379
+ context.append(")");
380
+ });
381
+ }
382
+ function inList(expression, values) {
383
+ const valueExpressions = values.map(expressionOperand);
384
+ return makeSchemaExpression("operator", (context) => {
385
+ if (values.length === 0) {
386
+ context.append("(1 = 0)");
387
+ return;
388
+ }
389
+ context.append("(");
390
+ context.render(expression);
391
+ context.append(" IN (");
392
+ valueExpressions.forEach((value, index) => {
393
+ if (index > 0) context.append(", ");
394
+ context.render(value);
395
+ });
396
+ context.append("))");
397
+ });
398
+ }
399
+ function notIn(expression, values) {
400
+ const valueExpressions = values.map(expressionOperand);
401
+ return makeSchemaExpression("operator", (context) => {
402
+ if (values.length === 0) {
403
+ context.append("(1 = 1)");
404
+ return;
405
+ }
406
+ context.append("(");
407
+ context.render(expression);
408
+ context.append(" NOT IN (");
409
+ valueExpressions.forEach((value, index) => {
410
+ if (index > 0) context.append(", ");
411
+ context.render(value);
412
+ });
413
+ context.append("))");
414
+ });
415
+ }
416
+ //#endregion
417
+ //#region src/expressions/operators/comparison/subquery.ts
418
+ function inQuery(expression, query) {
419
+ return makeExpression("subquery", (context) => {
420
+ context.append("(");
421
+ context.render(expression);
422
+ context.append(" IN ");
423
+ context.renderRelation(parenthesize(query));
424
+ context.append(")");
425
+ });
426
+ }
427
+ function exists(query) {
428
+ return makeExpression("subquery", (context) => {
429
+ context.append("EXISTS ");
430
+ context.renderRelation(parenthesize(query));
431
+ });
432
+ }
433
+ function notExists(query) {
434
+ return makeExpression("subquery", (context) => {
435
+ context.append("NOT EXISTS ");
436
+ context.renderRelation(parenthesize(query));
437
+ });
438
+ }
439
+ //#endregion
440
+ //#region src/expressions/operators/comparison/null.ts
441
+ function isNull(expression) {
442
+ return makeSchemaExpression("operator", (context) => {
443
+ context.append("(");
444
+ context.render(expression);
445
+ context.append(" IS NULL)");
446
+ });
447
+ }
448
+ function isNotNull(expression) {
449
+ return makeSchemaExpression("operator", (context) => {
450
+ context.append("(");
451
+ context.render(expression);
452
+ context.append(" IS NOT NULL)");
453
+ });
454
+ }
455
+ function isTrue(expression) {
456
+ return makeSchemaExpression("operator", (context) => {
457
+ context.render(expression);
458
+ context.append(" IS TRUE");
459
+ });
460
+ }
461
+ //#endregion
462
+ //#region src/query/selection.ts
463
+ /**
464
+ * Return every known source column as a named projection object.
465
+ *
466
+ * The result can be passed directly to `select()` or spread into a larger
467
+ * projection, such as `{ ...all(users), displayName: upper(users.name) }`.
468
+ */
469
+ function all(source) {
470
+ return Object.freeze({ ...source.columns });
471
+ }
472
+ //#endregion
473
+ //#region src/query/types.ts
474
+ function createQuery(queryKind, row, render) {
475
+ return Object.freeze({
476
+ queryKind,
477
+ row,
478
+ ...fragment(render)
479
+ });
480
+ }
481
+ //#endregion
482
+ //#region src/query/select/render.ts
483
+ function renderSelection(selection, context) {
484
+ assertNamedSelection(selection);
485
+ const entries = Object.entries(selection).filter(([, expression]) => expression !== omit);
486
+ if (entries.length === 0) throw queryValidationError({
487
+ code: "invalid-selection",
488
+ context: "select.projection",
489
+ path: ["selection"],
490
+ message: "select() requires at least one field",
491
+ hint: "Pass a named projection object with at least one expression."
492
+ });
493
+ entries.forEach(([name, expression], index) => {
494
+ if (index > 0) context.append(", ");
495
+ if (!isExpression(expression)) throw queryValidationError({
496
+ code: "invalid-selection",
497
+ context: "select.projection",
498
+ path: ["selection", name],
499
+ message: `Selection field "${name}" must be an expression`,
500
+ hint: "Use a Qubu expression such as table.column, value(), or a function."
501
+ });
502
+ const outputName = context.projectionMode === "result" ? name : snakeCaseIdentifier(name);
503
+ context.render(expression);
504
+ context.append(" AS ");
505
+ context.render(identifier(outputName));
506
+ });
507
+ }
508
+ function selectionRow(selection) {
509
+ assertNamedSelection(selection);
510
+ return Object.fromEntries(Object.entries(selection).filter(([, expression]) => expression !== omit).map(([name]) => [name, void 0]));
511
+ }
512
+ function assertNamedSelection(selection) {
513
+ if (Array.isArray(selection)) throw queryValidationError({
514
+ code: "invalid-selection",
515
+ context: "select.projection",
516
+ path: ["selection"],
517
+ message: "Selection must be a named object projection",
518
+ hint: "Pass { fieldName: expression } to select()."
519
+ });
520
+ }
521
+ //#endregion
522
+ //#region src/query/select/validate.ts
523
+ function validateClauses(clauses) {
524
+ const singletonKinds = /* @__PURE__ */ new Set([
525
+ "distinct",
526
+ "from",
527
+ "where",
528
+ "group-by",
529
+ "having",
530
+ "order-by",
531
+ "offset",
532
+ "fetch",
533
+ "row-lock"
534
+ ]);
535
+ const seen = /* @__PURE__ */ new Set();
536
+ for (const [index, clause] of clauses.entries()) {
537
+ if (!singletonKinds.has(clause.clauseKind)) continue;
538
+ if (seen.has(clause.clauseKind)) {
539
+ const hint = clause.clauseKind === "where" || clause.clauseKind === "having" ? "Compose repeated conditions with and() or or()." : clause.clauseKind === "order-by" ? "Combine terms in one orderBy() call." : clause.clauseKind === "row-lock" ? "Combine the mode and wait policy in one rowLock() call." : "Keep one clause of this kind.";
540
+ throw queryValidationError({
541
+ code: "duplicate-clause",
542
+ context: "select.clauses",
543
+ path: ["clauses", index],
544
+ message: `Only one ${clause.clauseKind} clause is allowed`,
545
+ hint
546
+ });
547
+ }
548
+ seen.add(clause.clauseKind);
549
+ }
550
+ }
551
+ //#endregion
552
+ //#region src/query/clauses/pagination.ts
553
+ function toPaginationPart(clause) {
554
+ return clause.clauseKind === "offset" ? {
555
+ kind: "offset",
556
+ rows: clause.rows
557
+ } : {
558
+ kind: "fetch",
559
+ rows: clause.rows,
560
+ direction: clause.direction
561
+ };
562
+ }
563
+ function renderPagination(context, clauses) {
564
+ const parts = clauses.map(toPaginationPart);
565
+ if (context.dialect.pagination) {
566
+ context.dialect.pagination.render(context, parts);
567
+ return;
568
+ }
569
+ parts.forEach((part, index) => {
570
+ if (index > 0) context.append(" ");
571
+ if (part.kind === "offset") {
572
+ context.append("OFFSET ");
573
+ context.parameter(part.rows);
574
+ context.append(" ROWS");
575
+ } else {
576
+ context.append(`FETCH ${part.direction} `);
577
+ context.parameter(part.rows);
578
+ context.append(" ROWS ONLY");
579
+ }
580
+ });
581
+ }
582
+ function offset(rows) {
583
+ if (!Number.isInteger(rows) || rows < 0) throw queryValidationError({
584
+ code: "invalid-pagination",
585
+ context: "select.pagination.offset",
586
+ path: ["offset"],
587
+ message: "offset() requires a non-negative integer",
588
+ hint: "Pass a non-negative integer row count."
589
+ });
590
+ return Object.assign(createClause("offset", "after-select", 90, (context) => renderPagination(context, [{
591
+ clauseKind: "offset",
592
+ rows
593
+ }])), {
594
+ clauseKind: "offset",
595
+ rows
596
+ });
597
+ }
598
+ function fetchRows(direction, rows) {
599
+ if (!Number.isInteger(rows) || rows < 0) throw queryValidationError({
600
+ code: "invalid-pagination",
601
+ context: `select.pagination.fetch${direction === "FIRST" ? "First" : "Next"}`,
602
+ path: ["fetch", direction],
603
+ message: "fetch rows require a non-negative integer",
604
+ hint: "Pass a non-negative integer row count."
605
+ });
606
+ return Object.assign(createClause("fetch", "after-select", 100, (context) => renderPagination(context, [{
607
+ clauseKind: "fetch",
608
+ direction,
609
+ rows
610
+ }])), {
611
+ clauseKind: "fetch",
612
+ direction,
613
+ rows
614
+ });
615
+ }
616
+ function fetchFirst(rows) {
617
+ return fetchRows("FIRST", rows);
618
+ }
619
+ function fetchNext(rows) {
620
+ return fetchRows("NEXT", rows);
621
+ }
622
+ //#endregion
623
+ //#region src/query/select.ts
624
+ function select(selection, ...parts) {
625
+ const normalizedClauses = parts.filter((part) => part !== omit);
626
+ validateClauses(normalizedClauses);
627
+ const orderedClauses = normalizedClauses.map((clause, index) => ({
628
+ clause,
629
+ index
630
+ })).sort((left, right) => left.clause.order - right.clause.order || left.index - right.index).map(({ clause }) => clause);
631
+ return createQuery("select", selectionRow(selection), (context) => {
632
+ const beforeSelect = orderedClauses.filter((clause) => clause.placement === "before-select");
633
+ const afterSelect = orderedClauses.filter((clause) => clause.placement === "after-select");
634
+ const paginationClauses = afterSelect.filter((clause) => clause.clauseKind === "offset" || clause.clauseKind === "fetch");
635
+ let renderedPagination = false;
636
+ for (const clause of beforeSelect) {
637
+ if (clause.clauseKind === "correlate") continue;
638
+ context.render(clause);
639
+ context.append(" ");
640
+ }
641
+ context.append("SELECT ");
642
+ const distinctClause = afterSelect.find((clause) => clause.clauseKind === "distinct");
643
+ if (distinctClause) {
644
+ context.render(distinctClause);
645
+ context.append(" ");
646
+ }
647
+ renderSelection(selection, context);
648
+ for (const clause of afterSelect) {
649
+ if (clause.clauseKind === "correlate") continue;
650
+ if (clause === distinctClause) continue;
651
+ if (clause.clauseKind === "offset" || clause.clauseKind === "fetch") {
652
+ if (renderedPagination) continue;
653
+ renderedPagination = true;
654
+ context.append(" ");
655
+ renderPagination(context, paginationClauses);
656
+ continue;
657
+ }
658
+ context.append(" ");
659
+ context.render(clause);
660
+ }
661
+ });
662
+ }
663
+ //#endregion
664
+ //#region src/query/set.ts
665
+ function setOperation(operator, left, right) {
666
+ return {
667
+ queryKind: "set",
668
+ row: left.row,
669
+ render: (context) => {
670
+ context.render(parenthesize(left));
671
+ context.append(` ${operator} `);
672
+ context.render(parenthesize(right));
673
+ }
674
+ };
675
+ }
676
+ function union(left, right) {
677
+ return setOperation("UNION", left, right);
678
+ }
679
+ function unionAll(left, right) {
680
+ return setOperation("UNION ALL", left, right);
681
+ }
682
+ function intersect(left, right) {
683
+ return setOperation("INTERSECT", left, right);
684
+ }
685
+ function except(left, right) {
686
+ return setOperation("EXCEPT", left, right);
687
+ }
688
+ //#endregion
689
+ //#region src/query/clauses/correlate.ts
690
+ function correlate(...sources) {
691
+ return Object.assign(createClause("correlate", "before-select", 0, (_context) => {}), {
692
+ clauseKind: "correlate",
693
+ sources
694
+ });
695
+ }
696
+ //#endregion
697
+ //#region src/query/clauses/distinct.ts
698
+ function distinct() {
699
+ return Object.assign(createClause("distinct", "after-select", 20, (context) => {
700
+ context.append("DISTINCT");
701
+ }), { clauseKind: "distinct" });
702
+ }
703
+ //#endregion
704
+ //#region src/query/clauses/from.ts
705
+ function from(...sources) {
706
+ return Object.assign(createClause("from", "after-select", 30, (context) => {
707
+ context.append("FROM ");
708
+ sources.forEach((source, index) => {
709
+ if (index > 0) context.append(", ");
710
+ context.render(source);
711
+ });
712
+ }), {
713
+ clauseKind: "from",
714
+ sources
715
+ });
716
+ }
717
+ //#endregion
718
+ //#region src/query/clauses/group-by.ts
719
+ function groupBy(...expressions) {
720
+ return Object.assign(createClause("group-by", "after-select", 60, (context) => {
721
+ context.append("GROUP BY ");
722
+ expressions.forEach((expression, index) => {
723
+ if (index > 0) context.append(", ");
724
+ context.render(expression);
725
+ });
726
+ }), {
727
+ clauseKind: "group-by",
728
+ expressions
729
+ });
730
+ }
731
+ //#endregion
732
+ //#region src/query/clauses/having.ts
733
+ function having(condition) {
734
+ if (condition === omit) return omit;
735
+ return Object.assign(createClause("having", "after-select", 70, (context) => {
736
+ context.append("HAVING ");
737
+ context.render(condition);
738
+ }), {
739
+ clauseKind: "having",
740
+ condition
741
+ });
742
+ }
743
+ //#endregion
744
+ //#region src/query/clauses/joins.ts
745
+ function join(joinType, source, condition) {
746
+ return Object.assign(createClause("join", "after-select", 40, (context) => {
747
+ context.append(`${joinType} JOIN `);
748
+ context.render(source);
749
+ if (condition) {
750
+ context.append(" ON ");
751
+ context.render(condition);
752
+ }
753
+ }), {
754
+ clauseKind: "join",
755
+ joinType,
756
+ source,
757
+ condition
758
+ });
759
+ }
760
+ function innerJoin(source, condition) {
761
+ return join("INNER", source, condition);
762
+ }
763
+ function leftJoin(source, condition) {
764
+ return join("LEFT", source, condition);
765
+ }
766
+ function rightJoin(source, condition) {
767
+ return join("RIGHT", source, condition);
768
+ }
769
+ function fullJoin(source, condition) {
770
+ return join("FULL", source, condition);
771
+ }
772
+ function crossJoin(source) {
773
+ return join("CROSS", source);
774
+ }
775
+ function naturalJoin(source) {
776
+ return join("NATURAL", source);
777
+ }
778
+ //#endregion
779
+ //#region src/query/clauses/order-by.ts
780
+ function orderTerm(expression, direction, nulls) {
781
+ const base = makeExpression("operator", (context) => {
782
+ context.render(expression);
783
+ if (direction) context.append(` ${direction}`);
784
+ if (nulls) context.append(` NULLS ${nulls}`);
785
+ });
786
+ return Object.freeze({
787
+ ...base,
788
+ orderKind: "term",
789
+ expression,
790
+ direction,
791
+ nulls
792
+ });
793
+ }
794
+ function order(expression, direction, nulls) {
795
+ return orderTerm(expression, direction, nulls);
796
+ }
797
+ function asc(expression, nulls) {
798
+ return orderTerm(expression, "ASC", nulls);
799
+ }
800
+ function desc(expression, nulls) {
801
+ return orderTerm(expression, "DESC", nulls);
802
+ }
803
+ function nullsFirst(expression) {
804
+ return orderTerm(expression, void 0, "FIRST");
805
+ }
806
+ function nullsLast(expression) {
807
+ return orderTerm(expression, void 0, "LAST");
808
+ }
809
+ function orderBy(...parts) {
810
+ const presentParts = parts.filter((part) => part !== omit);
811
+ if (presentParts.length === 0) return omit;
812
+ const terms = presentParts.map((part) => "orderKind" in part ? part : orderTerm(part));
813
+ return Object.assign(createClause("order-by", "after-select", 80, (context) => {
814
+ context.append("ORDER BY ");
815
+ terms.forEach((term, index) => {
816
+ if (index > 0) context.append(", ");
817
+ context.render(term);
818
+ });
819
+ }), {
820
+ clauseKind: "order-by",
821
+ terms
822
+ });
823
+ }
824
+ //#endregion
825
+ //#region src/query/clauses/row-lock.ts
826
+ const rowLockModes = [
827
+ "update",
828
+ "no-key-update",
829
+ "share",
830
+ "key-share"
831
+ ];
832
+ const rowLockWaitPolicies = [
833
+ "default",
834
+ "nowait",
835
+ "skip-locked"
836
+ ];
837
+ function rowLock(modeOrOptions = "update", wait = "default") {
838
+ const mode = typeof modeOrOptions === "string" ? modeOrOptions : modeOrOptions.mode ?? "update";
839
+ const waitPolicy = typeof modeOrOptions === "string" ? wait : modeOrOptions.wait ?? modeOrOptions.waitPolicy ?? "default";
840
+ validateRowLockMode(mode);
841
+ validateRowLockWaitPolicy(waitPolicy);
842
+ return Object.assign(createClause("row-lock", "after-select", 110, (context) => {
843
+ assertDialectCapability(context.dialect, "row-locking");
844
+ const policy = context.dialect.rowLocking;
845
+ if (!policy) throw new Error(`Dialect "${context.dialect.name}" advertises the "row-locking" capability without a row-locking policy`);
846
+ policy.render(context, mode, waitPolicy);
847
+ }), {
848
+ clauseKind: "row-lock",
849
+ mode,
850
+ wait: waitPolicy
851
+ });
852
+ }
853
+ function validateRowLockMode(mode) {
854
+ if (rowLockModes.includes(mode)) return;
855
+ throw queryValidationError({
856
+ code: "invalid-row-lock",
857
+ context: "select.row-lock",
858
+ path: ["rowLock", "mode"],
859
+ message: `rowLock() does not support the "${String(mode)}" lock mode`,
860
+ hint: "Use update, no-key-update, share, or key-share."
861
+ });
862
+ }
863
+ function validateRowLockWaitPolicy(wait) {
864
+ if (rowLockWaitPolicies.includes(wait)) return;
865
+ throw queryValidationError({
866
+ code: "invalid-row-lock",
867
+ context: "select.row-lock",
868
+ path: ["rowLock", "wait"],
869
+ message: `rowLock() does not support the "${String(wait)}" wait policy`,
870
+ hint: "Use default, nowait, or skip-locked."
871
+ });
872
+ }
873
+ //#endregion
874
+ //#region src/query/clauses/where.ts
875
+ function where(condition) {
876
+ if (condition === omit) return omit;
877
+ return Object.assign(createClause("where", "after-select", 50, (context) => {
878
+ context.append("WHERE ");
879
+ context.render(condition);
880
+ }), {
881
+ clauseKind: "where",
882
+ condition
883
+ });
884
+ }
885
+ //#endregion
886
+ //#region src/query/clauses/with.ts
887
+ function cte(name, query) {
888
+ const reference = identifier(name);
889
+ const source = createSource("cte", (context) => context.render(reference), reference);
890
+ const sqlNames = resolveSqlNames(Object.keys(query.row).map((fieldName) => ({ fieldName })));
891
+ const columns = Object.fromEntries(Object.keys(query.row).map((fieldName) => [fieldName, createColumnReference(sqlNames[fieldName], reference, fieldName)]));
892
+ Object.assign(source, {
893
+ cteName: name,
894
+ query,
895
+ columns
896
+ });
897
+ exposeColumns(source, columns);
898
+ return source;
899
+ }
900
+ /**
901
+ * Define a typed recursive `WITH` source from an anchor and one recursive
902
+ * member. The callback receives a forward reference that must be introduced
903
+ * through the member query's normal FROM or JOIN scope.
904
+ */
905
+ function recursiveCte(name, anchor, member) {
906
+ const reference = identifier(name);
907
+ const source = createSource("cte", (context) => context.render(reference), reference);
908
+ const sqlNames = resolveSqlNames(Object.keys(anchor.row).map((fieldName) => ({ fieldName })));
909
+ const columns = Object.fromEntries(Object.keys(anchor.row).map((fieldName) => [fieldName, createColumnReference(sqlNames[fieldName], reference, fieldName)]));
910
+ Object.assign(source, {
911
+ cteName: name,
912
+ query: anchor,
913
+ columns
914
+ });
915
+ exposeColumns(source, columns);
916
+ const memberQuery = member(source);
917
+ const query = Object.freeze({
918
+ ...createQuery("set", anchor.row, (context) => {
919
+ context.render(anchor);
920
+ context.append(" UNION ALL ");
921
+ context.render(memberQuery);
922
+ }),
923
+ recursive: true
924
+ });
925
+ Object.assign(source, { query });
926
+ return source;
927
+ }
928
+ function withCte(...ctes) {
929
+ return Object.assign(createClause("with", "before-select", 10, (context) => {
930
+ context.append(ctes.some((entry) => entry.query.recursive) ? "WITH RECURSIVE " : "WITH ");
931
+ ctes.forEach((entry, index) => {
932
+ if (index > 0) context.append(", ");
933
+ context.render(identifier(entry.cteName));
934
+ if (entry.query.recursive) {
935
+ const sqlNames = resolveSqlNames(Object.keys(entry.query.row).map((fieldName) => ({ fieldName })));
936
+ context.append(" (");
937
+ Object.keys(entry.query.row).forEach((fieldName, columnIndex) => {
938
+ if (columnIndex > 0) context.append(", ");
939
+ context.render(identifier(sqlNames[fieldName]));
940
+ });
941
+ context.append(")");
942
+ }
943
+ context.append(" AS ");
944
+ context.renderRelation(parenthesize(entry.query));
945
+ });
946
+ }), {
947
+ clauseKind: "with",
948
+ ctes
949
+ });
950
+ }
951
+ //#endregion
952
+ //#region src/query/mutation/types.ts
953
+ function createMutation(queryKind, row, render) {
954
+ return {
955
+ queryKind,
956
+ row,
957
+ render
958
+ };
959
+ }
960
+ function allowAll() {
961
+ return Object.freeze({
962
+ clauseKind: "allow-all",
963
+ render: () => void 0
964
+ });
965
+ }
966
+ function validateMutationClauses(kind, clauses) {
967
+ let whereSeen = false;
968
+ let returningSeen = false;
969
+ let allowAllSeen = false;
970
+ for (const clause of clauses) if (clause.clauseKind === "where") {
971
+ if (whereSeen) throw queryValidationError({
972
+ code: "duplicate-clause",
973
+ context: `mutation.${kind.toLowerCase()}.clauses`,
974
+ path: ["clauses", "where"],
975
+ message: `${kind} accepts only one WHERE clause`,
976
+ hint: "Compose repeated conditions with and() or or()."
977
+ });
978
+ whereSeen = true;
979
+ } else if (clause.clauseKind === "returning") {
980
+ if (returningSeen) throw queryValidationError({
981
+ code: "duplicate-clause",
982
+ context: `mutation.${kind.toLowerCase()}.clauses`,
983
+ path: ["clauses", "returning"],
984
+ message: `${kind} accepts only one RETURNING clause`,
985
+ hint: "Use one returning() clause containing the complete projection."
986
+ });
987
+ returningSeen = true;
988
+ } else if (clause.clauseKind === "allow-all") {
989
+ if (allowAllSeen) throw queryValidationError({
990
+ code: "duplicate-clause",
991
+ context: `mutation.${kind.toLowerCase()}.clauses`,
992
+ path: ["clauses", "allowAll"],
993
+ message: `${kind} accepts only one allowAll() marker`,
994
+ hint: "Keep one allowAll() marker when the mutation is intentionally unrestricted."
995
+ });
996
+ allowAllSeen = true;
997
+ }
998
+ if (!whereSeen && !allowAllSeen) throw queryValidationError({
999
+ code: "unsafe-mutation",
1000
+ context: `mutation.${kind.toLowerCase()}.safety`,
1001
+ path: ["clauses"],
1002
+ message: `${kind} requires a WHERE clause; use allowAll() to opt into an unrestricted mutation`,
1003
+ hint: "Add where(...) to target rows, or add allowAll() to make the unrestricted intent explicit."
1004
+ });
1005
+ }
1006
+ //#endregion
1007
+ //#region src/query/mutation/insert.ts
1008
+ function values(...rows) {
1009
+ return Object.freeze({
1010
+ insertKind: "values",
1011
+ rows
1012
+ });
1013
+ }
1014
+ function defaultValues() {
1015
+ return Object.freeze({ insertKind: "default-values" });
1016
+ }
1017
+ function insertSelect(query, columns) {
1018
+ return Object.freeze({
1019
+ insertKind: "select",
1020
+ query,
1021
+ columns
1022
+ });
1023
+ }
1024
+ function insertInto(table, source, ...clauses) {
1025
+ validateInsert(table, source);
1026
+ const insertClauses = clauses;
1027
+ return createMutation("insert", insertClauses.find((clause) => clause.clauseKind === "returning")?.row ?? {}, (context) => {
1028
+ context.append("INSERT INTO ");
1029
+ context.render(table.reference);
1030
+ if (source.insertKind === "values") {
1031
+ const rows = source.rows;
1032
+ const columns = Object.keys(rows[0] ?? {});
1033
+ if (columns.length === 0) context.append(" DEFAULT VALUES");
1034
+ else {
1035
+ renderTargetColumns(context, table, columns);
1036
+ context.append(" VALUES ");
1037
+ rows.forEach((row, rowIndex) => {
1038
+ if (rowIndex > 0) context.append(", ");
1039
+ context.append("(");
1040
+ columns.forEach((columnName, columnIndex) => {
1041
+ if (columnIndex > 0) context.append(", ");
1042
+ renderInsertValue(context, row[columnName]);
1043
+ });
1044
+ context.append(")");
1045
+ });
1046
+ }
1047
+ } else if (source.insertKind === "default-values") context.append(" DEFAULT VALUES");
1048
+ else {
1049
+ renderTargetColumns(context, table, source.columns);
1050
+ context.append(" ");
1051
+ context.renderRelation(source.query);
1052
+ }
1053
+ for (const clause of insertClauses) {
1054
+ context.append(" ");
1055
+ context.render(clause);
1056
+ }
1057
+ });
1058
+ }
1059
+ function renderTargetColumns(context, table, columns) {
1060
+ context.append(" (");
1061
+ columns.forEach((columnName, index) => {
1062
+ if (index > 0) context.append(", ");
1063
+ context.render(identifier(table.sqlNames[columnName] ?? columnName));
1064
+ });
1065
+ context.append(")");
1066
+ }
1067
+ function renderInsertValue(context, input) {
1068
+ if (isExpression(input)) context.render(input);
1069
+ else context.parameter(input);
1070
+ }
1071
+ function validateInsert(table, source) {
1072
+ const definitions = table.definitions;
1073
+ if (source.insertKind === "values") {
1074
+ const firstColumns = Object.keys(source.rows[0] ?? {});
1075
+ const firstSet = new Set(firstColumns);
1076
+ for (const columnName of firstColumns) {
1077
+ if (!definitions[columnName]) throw queryValidationError({
1078
+ code: "invalid-insert",
1079
+ context: "insert.values.columns",
1080
+ path: ["values", columnName],
1081
+ message: `Unknown insert column "${columnName}"`,
1082
+ hint: "Use only columns declared by the insert table."
1083
+ });
1084
+ if (definitions[columnName].generated) throw queryValidationError({
1085
+ code: "invalid-insert",
1086
+ context: "insert.values.columns",
1087
+ path: ["values", columnName],
1088
+ message: `Generated column "${columnName}" cannot be inserted`,
1089
+ hint: "Omit generated columns from the insert values."
1090
+ });
1091
+ }
1092
+ for (const row of source.rows) {
1093
+ const columns = Object.keys(row);
1094
+ if (columns.length !== firstColumns.length || columns.some((columnName) => !firstSet.has(columnName))) throw queryValidationError({
1095
+ code: "invalid-insert",
1096
+ context: "insert.values.rows",
1097
+ path: ["values", "rows"],
1098
+ message: "All INSERT values rows must use the same columns",
1099
+ hint: "Use one identical column set for every values row."
1100
+ });
1101
+ }
1102
+ for (const [columnName, definition] of Object.entries(definitions)) {
1103
+ if (definition.generated || definition.hasDefault) continue;
1104
+ if (!firstSet.has(columnName)) throw queryValidationError({
1105
+ code: "invalid-insert",
1106
+ context: "insert.values.columns",
1107
+ path: ["values", columnName],
1108
+ message: `Required insert column "${columnName}" is missing`,
1109
+ hint: "Provide the column value or define a default for it."
1110
+ });
1111
+ }
1112
+ } else if (source.insertKind === "default-values") {
1113
+ for (const [columnName, definition] of Object.entries(definitions)) if (!definition.generated && !definition.hasDefault) throw queryValidationError({
1114
+ code: "invalid-insert",
1115
+ context: "insert.default-values",
1116
+ path: ["defaultValues", columnName],
1117
+ message: `DEFAULT VALUES requires column "${columnName}" to have a default`,
1118
+ hint: "Provide values for required columns or define defaults for them."
1119
+ });
1120
+ } else {
1121
+ if (source.columns.length === 0) throw queryValidationError({
1122
+ code: "invalid-insert",
1123
+ context: "insert.select.columns",
1124
+ path: ["insertSelect", "columns"],
1125
+ message: "INSERT ... SELECT requires at least one target column",
1126
+ hint: "List the target columns receiving the SELECT result."
1127
+ });
1128
+ const seen = /* @__PURE__ */ new Set();
1129
+ for (const columnName of source.columns) {
1130
+ if (seen.has(columnName)) throw queryValidationError({
1131
+ code: "duplicate-clause",
1132
+ context: "insert.select.columns",
1133
+ path: [
1134
+ "insertSelect",
1135
+ "columns",
1136
+ columnName
1137
+ ],
1138
+ message: `Duplicate insert column "${columnName}"`,
1139
+ hint: "List each insert target column once."
1140
+ });
1141
+ seen.add(columnName);
1142
+ if (!definitions[columnName]) throw queryValidationError({
1143
+ code: "invalid-insert",
1144
+ context: "insert.select.columns",
1145
+ path: [
1146
+ "insertSelect",
1147
+ "columns",
1148
+ columnName
1149
+ ],
1150
+ message: `Unknown insert column "${columnName}"`,
1151
+ hint: "Use only columns declared by the insert table."
1152
+ });
1153
+ if (definitions[columnName].generated) throw queryValidationError({
1154
+ code: "invalid-insert",
1155
+ context: "insert.select.columns",
1156
+ path: [
1157
+ "insertSelect",
1158
+ "columns",
1159
+ columnName
1160
+ ],
1161
+ message: `Generated column "${columnName}" cannot be inserted`,
1162
+ hint: "Omit generated columns from the insert target list."
1163
+ });
1164
+ }
1165
+ for (const [columnName, definition] of Object.entries(definitions)) {
1166
+ if (definition.generated || definition.hasDefault) continue;
1167
+ if (!seen.has(columnName)) throw queryValidationError({
1168
+ code: "invalid-insert",
1169
+ context: "insert.select.columns",
1170
+ path: [
1171
+ "insertSelect",
1172
+ "columns",
1173
+ columnName
1174
+ ],
1175
+ message: `Required insert column "${columnName}" is missing`,
1176
+ hint: "Include every required insert column in the target list."
1177
+ });
1178
+ }
1179
+ }
1180
+ }
1181
+ //#endregion
1182
+ //#region src/query/mutation/update.ts
1183
+ function update(table, assignments, ...clauses) {
1184
+ const normalizedClauses = clauses;
1185
+ validateMutationClauses("UPDATE", normalizedClauses);
1186
+ const entries = validateUpdate(table, assignments);
1187
+ const whereClause = normalizedClauses.find((clause) => clause.clauseKind === "where");
1188
+ const returningClause = normalizedClauses.find((clause) => clause.clauseKind === "returning");
1189
+ return createMutation("update", returningClause?.row ?? {}, (context) => {
1190
+ context.append("UPDATE ");
1191
+ context.render(table.reference);
1192
+ context.append(" SET ");
1193
+ entries.forEach(([columnName, value], index) => {
1194
+ if (index > 0) context.append(", ");
1195
+ context.render(identifier(table.sqlNames[columnName] ?? columnName));
1196
+ context.append(" = ");
1197
+ renderAssignmentValue(context, value);
1198
+ });
1199
+ if (whereClause) {
1200
+ context.append(" ");
1201
+ context.render(whereClause);
1202
+ }
1203
+ if (returningClause) {
1204
+ context.append(" ");
1205
+ context.render(returningClause);
1206
+ }
1207
+ });
1208
+ }
1209
+ function renderAssignmentValue(context, value) {
1210
+ if (isExpression(value)) context.render(value);
1211
+ else context.parameter(value);
1212
+ }
1213
+ function validateUpdate(table, assignments) {
1214
+ const definitions = table.definitions;
1215
+ const entries = Object.entries(assignments).filter(([, value]) => value !== omit);
1216
+ if (entries.length === 0) throw queryValidationError({
1217
+ code: "invalid-update",
1218
+ context: "update.assignments",
1219
+ path: ["assignments"],
1220
+ message: "UPDATE requires at least one assignment",
1221
+ hint: "Provide at least one writable column, or remove the update."
1222
+ });
1223
+ for (const [columnName] of entries) {
1224
+ const definition = definitions[columnName];
1225
+ if (!definition) throw queryValidationError({
1226
+ code: "invalid-update",
1227
+ context: "update.assignments",
1228
+ path: ["assignments", columnName],
1229
+ message: `Unknown update column "${columnName}"`,
1230
+ hint: "Use a column declared by the update table."
1231
+ });
1232
+ if (definition.generated) throw queryValidationError({
1233
+ code: "invalid-update",
1234
+ context: "update.assignments",
1235
+ path: ["assignments", columnName],
1236
+ message: `Generated column "${columnName}" cannot be updated`,
1237
+ hint: "Remove the generated column from the assignments."
1238
+ });
1239
+ }
1240
+ return entries;
1241
+ }
1242
+ //#endregion
1243
+ //#region src/query/mutation/delete.ts
1244
+ function deleteFrom(table, ...clauses) {
1245
+ const normalizedClauses = clauses;
1246
+ validateMutationClauses("DELETE", normalizedClauses);
1247
+ const whereClause = normalizedClauses.find((clause) => clause.clauseKind === "where");
1248
+ const returningClause = normalizedClauses.find((clause) => clause.clauseKind === "returning");
1249
+ return createMutation("delete", returningClause?.row ?? {}, (context) => {
1250
+ context.append("DELETE FROM ");
1251
+ context.render(table.reference);
1252
+ if (whereClause) {
1253
+ context.append(" ");
1254
+ context.render(whereClause);
1255
+ }
1256
+ if (returningClause) {
1257
+ context.append(" ");
1258
+ context.render(returningClause);
1259
+ }
1260
+ });
1261
+ }
1262
+ //#endregion
1263
+ //#region src/query/mutation/returning.ts
1264
+ function returning(selection) {
1265
+ return Object.freeze({
1266
+ clauseKind: "returning",
1267
+ selection,
1268
+ row: selectionRow(selection),
1269
+ render(context) {
1270
+ context.append("RETURNING ");
1271
+ renderSelection(selection, context);
1272
+ }
1273
+ });
1274
+ }
1275
+ //#endregion
1276
+ export { QueryValidationError, add, alias, all, allowAll, and, asValue, asc, avg, between, bigint, binary, boolean, call, caseWhen, cast, catalogCheck, catalogForeignKey, check, coalesce, column, concat, correlate, count, countDistinct, crossJoin, cte, date, defaultValues, deleteFrom, denseRank, desc, distinct, divide, eq, except, execute, executeRows, exists, explain, externalDefault, externalGeneratedColumn, fetchFirst, fetchNext, foreignKey, from, fullJoin, generatedColumn, groupBy, gt, gte, having, identityColumn, inList, inQuery, index, innerJoin, insertInto, insertSelect, integer, intersect, isDistinctFrom, isNotDistinctFrom, isNotNull, isNull, isTrue, json, jsonBoolean, jsonExists, jsonNumber, jsonPath, jsonText, lateral, leftJoin, like, lower, lt, lte, max, min, modulo, multiply, nativeColumn, nativeStorage, naturalJoin, ne, not, notExists, notIn, notLike, nullable, nullsFirst, nullsLast, numeric, offset, omit, or, order, orderBy, over, portableStorage, primaryKey, qubu, rank, recursiveCte, references, render, returning, rightJoin, rowLock, rowNumber, scalar, schema, schemaCall, select, sql, stream, subtract, sum, table, text, timestamp, union, unionAll, unique, uniqueConstraint, unsafeSchemaSql, update, upper, uuid, value, values, where, withCte };