@vibeorm/sql 3.0.0 → 4.1.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.
@@ -0,0 +1,1080 @@
1
+ // @bun
2
+ // packages/sql/src/query/select.ts
3
+ import { assertSqlParamBuilding as assertSqlParamBuilding2, createSqlQuery, isSqlParam as isSqlParam2, recordSqlBinding as recordSqlBinding2, sameResultDecoder, sqlParamContract as sqlParamContract2 } from "@vibeorm/sql";
4
+
5
+ // packages/sql/src/query/state.ts
6
+ import { VibeError } from "@vibeorm/schema";
7
+ import { registerSqlHandleCheck } from "@vibeorm/sql";
8
+ var expressionStates = new WeakMap;
9
+ var sourceStates = new WeakMap;
10
+ var readStates = new WeakMap;
11
+ var builderTerminals = new WeakSet;
12
+ registerSqlHandleCheck({ check: (value) => expressionStates.has(value) || sourceStates.has(value) || builderTerminals.has(value) });
13
+ function columnContract(definition) {
14
+ return JSON.stringify({ scalar: definition.scalar, nullable: definition.nullable === true, list: definition.list === true, enumType: definition.enumType, enumValues: definition.enumValues, nativeType: definition.nativeType, transport: definition.transport });
15
+ }
16
+ function invalid(message) {
17
+ throw new VibeError({ code: "VIBE_VALIDATION", message });
18
+ }
19
+ function invalidData(message) {
20
+ throw new VibeError({ code: "VIBE_DATA_INVALID", message });
21
+ }
22
+ function unsupported(message) {
23
+ throw new VibeError({ code: "VIBE_UNSUPPORTED_CAPABILITY", message });
24
+ }
25
+ function identifier(name) {
26
+ if (typeof name !== "string" || name.length === 0 || name.includes("\x00"))
27
+ invalid("SQL names must be non-empty and NUL-free");
28
+ }
29
+ function expressionState(expression) {
30
+ if (typeof expression !== "object" || expression === null)
31
+ invalid("Expected an authentic SQL expression");
32
+ return expressionStates.get(expression) ?? invalid("Expected an authentic SQL expression");
33
+ }
34
+ function sourceState(source) {
35
+ if (typeof source !== "object" || source === null)
36
+ invalid("Expected an authentic SQL source");
37
+ return sourceStates.get(source) ?? invalid("Expected an authentic SQL source");
38
+ }
39
+ function readState(query) {
40
+ if (typeof query !== "object" || query === null)
41
+ invalid("Expected a builder read query");
42
+ return readStates.get(query) ?? invalid("Derived sources and unions require builder SELECT queries, not raw or write plans");
43
+ }
44
+ function makeExpression(state) {
45
+ const expression = Object.freeze(Object.create(null));
46
+ expressionStates.set(expression, Object.freeze(state));
47
+ return expression;
48
+ }
49
+ function checkScope(params) {
50
+ const state = expressionState(params.expression);
51
+ if (state.default === true)
52
+ invalid("SQL DEFAULT is only valid in write values");
53
+ const available = new Set(params.bindings.map((binding) => binding.token));
54
+ for (const token of state.references)
55
+ if (!available.has(token))
56
+ invalid("SQL expression belongs to a different source scope");
57
+ if (params.nullableContracts === true && state.column !== undefined && state.definition?.nullable !== true && params.bindings.some((binding) => binding.token === state.column.binding && binding.nullable))
58
+ invalid("A captured pre-join column cannot bypass outer-join nullability; use the output scope");
59
+ if (params.aggregates === false && state.aggregate)
60
+ invalid("Aggregate expressions are not valid in this clause");
61
+ }
62
+
63
+ // packages/sql/src/query/sources.ts
64
+ import { assertSqlNamespace } from "@vibeorm/schema";
65
+
66
+ // packages/sql/src/query/expressions.ts
67
+ import { assertSqlParamBuilding, decode, float32Codec, isSqlBindingCollector, isSqlFragment, isSqlParam, jsonListParameter, nativeEnumArrayParameter, parsePgArrayLiteral, recordSqlBinding, renderSqlFragment, resultScalarContract, snapshotSqlValue, sqlParamContract, timeOfDayCodec, toPgArrayLiteral } from "@vibeorm/sql";
68
+ var nullableFloat = decode.nullable({ decoder: decode.float });
69
+ var countDefinition = Object.freeze({ name: "", scalar: "BigInt", decoder: decode.bigInt });
70
+ var floatDefinition = Object.freeze({ name: "", scalar: "Float", nullable: true, decoder: nullableFloat });
71
+ var nullableDecoders = new WeakMap;
72
+ function nullableDecoder(decoder) {
73
+ let lifted = nullableDecoders.get(decoder);
74
+ if (lifted === undefined) {
75
+ lifted = Object.freeze({ decode: (params) => params.value === null ? null : decoder.decode(params) });
76
+ nullableDecoders.set(decoder, lifted);
77
+ }
78
+ return lifted;
79
+ }
80
+ function assertDecoder(decoder) {
81
+ if (typeof decoder?.decode !== "function")
82
+ invalid("SQL expressions require an explicit result decoder");
83
+ }
84
+ function inputDecoder(definition) {
85
+ const decoder = definition.inputDecoder ?? definition.decoder;
86
+ if (!("encode" in decoder) || typeof decoder.encode !== "function")
87
+ invalid("A decode-only projection requires a separate inputDecoder for parameters and writes");
88
+ return decoder;
89
+ }
90
+ function nativeColumnDefinition(definition) {
91
+ const contract = resultScalarContract({ decoder: definition.decoder });
92
+ if (contract === undefined || contract.scalar !== definition.scalar)
93
+ return definition;
94
+ const codec = definition.scalar === "DateTime" ? timeOfDayCodec({ nativeType: definition.nativeType }) : definition.scalar === "Float" && /^(real|float4)$/i.test(definition.nativeType ?? "") ? float32Codec : undefined;
95
+ if (codec === undefined)
96
+ return definition;
97
+ const original = definition.decoder;
98
+ const list = definition.list === true;
99
+ let decoders = nativeDecoders.get(original);
100
+ if (decoders === undefined) {
101
+ decoders = new Map;
102
+ nativeDecoders.set(original, decoders);
103
+ }
104
+ const key = `${definition.nativeType}|${list}`;
105
+ let decoder = decoders.get(key);
106
+ if (decoder === undefined) {
107
+ decoder = Object.freeze({
108
+ decode: (params) => {
109
+ const raw = params.value;
110
+ const convert = (value) => value === null ? null : codec.decode(value);
111
+ const elements = typeof raw === "string" && list && params.dialect === "postgres" ? parsePgArrayLiteral({ literal: raw }) : ArrayBuffer.isView(raw) && ("length" in raw) ? Array.from(raw) : raw;
112
+ const value = raw === null ? null : list && Array.isArray(elements) ? elements.map(convert) : convert(raw);
113
+ return original.decode({ ...params, value });
114
+ },
115
+ encode: (params) => {
116
+ const encoded = original.encode(params);
117
+ const convert = (value) => value === null ? null : codec.encode(value);
118
+ return list && Array.isArray(encoded) ? encoded.map(convert) : convert(encoded);
119
+ }
120
+ });
121
+ decoders.set(key, decoder);
122
+ }
123
+ return { ...definition, decoder };
124
+ }
125
+ var nativeDecoders = new WeakMap;
126
+ function transportProjection(params) {
127
+ const { context, expression, definition } = params;
128
+ if (definition.transport === "text" || definition.scalar === "Json" || definition.scalar === "BigInt" || definition.list === true && definition.scalar === "Float") {
129
+ return `CAST(${expression} AS ${context.dialect.name === "mysql" ? "CHAR" : "TEXT"})`;
130
+ }
131
+ return expression;
132
+ }
133
+ function columnInputContract(definition) {
134
+ const encoder = inputDecoder(definition);
135
+ return {
136
+ encoder,
137
+ scalar: definition.scalar,
138
+ nullable: definition.nullable === true,
139
+ list: definition.list === true,
140
+ nativeType: definition.nativeType,
141
+ enumValues: definition.enumValues,
142
+ enumType: definition.enumType,
143
+ jsonStorage: definition.jsonStorage,
144
+ ...definition.inputDecoder !== undefined && definition.inputDecoder !== definition.decoder ? { columnIdentity: encoder } : {}
145
+ };
146
+ }
147
+ function physicalInput(contract) {
148
+ return JSON.stringify({ scalar: contract.scalar, list: contract.list, nativeType: contract.nativeType, enumValues: contract.enumValues, enumType: contract.enumType === undefined ? undefined : [contract.enumType.namespace, contract.enumType.name], jsonStorage: contract.jsonStorage });
149
+ }
150
+ function parameterDefinition(params) {
151
+ const declared = sqlParamContract({ param: params.param });
152
+ const target = columnInputContract(params.definition);
153
+ if (declared.columnIdentity !== undefined || target.columnIdentity !== undefined) {
154
+ if (declared.encoder !== target.encoder || physicalInput(declared) !== physicalInput(target))
155
+ invalid("SQL input token does not match this column's write encoder and physical contract");
156
+ return { ...params.definition, nullable: declared.nullable && target.nullable, inputDecoder: declared.encoder };
157
+ }
158
+ if (declared.scalar !== "Unknown" && (declared.scalar !== target.scalar || declared.list !== target.list))
159
+ invalid("SQL input token has an incompatible scalar or list contract");
160
+ if (declared.scalar === "Unknown" && (target.nativeType !== undefined || target.scalar === "Enum" || target.scalar === "Json"))
161
+ invalid("Opaque SQL input decoders need qb.input for native, enum and JSON columns");
162
+ if (declared.nativeType !== undefined && physicalInput(declared) !== physicalInput(target))
163
+ invalid("SQL input token has an incompatible native column contract");
164
+ if (declared.scalar === "Enum" && physicalInput(declared) !== physicalInput(target))
165
+ invalid("SQL input token has an incompatible enum column contract");
166
+ if ("decode" in declared.encoder && typeof declared.encoder.decode === "function") {
167
+ return nativeColumnDefinition({ ...params.definition, nullable: declared.nullable && target.nullable, decoder: declared.encoder, inputDecoder: undefined });
168
+ }
169
+ if (declared.encoder !== target.encoder)
170
+ invalid("A write-only SQL input token must match its column encoder");
171
+ return { ...params.definition, nullable: declared.nullable && target.nullable, inputDecoder: declared.encoder };
172
+ }
173
+ function columnTransport(definition) {
174
+ if (definition.list === true && definition.scalar === "Enum" && definition.enumType !== undefined)
175
+ return { kind: "native-enum-array", type: definition.enumType };
176
+ if (definition.list === true && definition.scalar === "Json")
177
+ return { kind: "json-list", storage: definition.jsonStorage ?? "jsonb", whole: true };
178
+ return { kind: "plain" };
179
+ }
180
+ function encodeColumnValue(params) {
181
+ const { dialect, definition } = params;
182
+ if (params.value === null) {
183
+ if (definition.nullable !== true)
184
+ invalid("A non-null SQL column cannot receive SQL NULL");
185
+ return null;
186
+ }
187
+ const encoded = snapshotSqlValue(inputDecoder(definition).encode({ value: snapshotSqlValue(params.value), dialect: dialect.name }));
188
+ if (encoded === null) {
189
+ if (definition.nullable !== true)
190
+ invalid("A non-null SQL column cannot receive SQL NULL from its encoder");
191
+ return null;
192
+ }
193
+ if (definition.scalar === "BigInt" && definition.list !== true && dialect.name === "sqlite") {
194
+ const integer = decode.bigInt.decode({ value: encoded, dialect: "sqlite" });
195
+ if (integer < -9223372036854775808n || integer > 9223372036854775807n)
196
+ invalid("SQLite BigInt parameters must fit signed int64");
197
+ }
198
+ if (definition.list === true && definition.scalar === "Enum" && definition.enumType !== undefined) {
199
+ if (!Array.isArray(encoded))
200
+ invalid("Native enum list encoder must return an array");
201
+ return nativeEnumArrayParameter({ value: toPgArrayLiteral({ values: encoded }), type: definition.enumType });
202
+ }
203
+ if (definition.list === true && definition.scalar === "Json") {
204
+ if (!Array.isArray(encoded) || encoded.some((entry) => typeof entry !== "string"))
205
+ invalid("JSON list encoder must return encoded JSON text elements");
206
+ return jsonListParameter({ value: encoded, storage: definition.jsonStorage ?? "jsonb" });
207
+ }
208
+ return encoded;
209
+ }
210
+ function bindColumnValue(params) {
211
+ const { context, definition } = params;
212
+ if (definition.list === true && !context.dialect.capabilities.scalarArrays)
213
+ unsupported("Scalar array parameters require a dialect with native scalar arrays");
214
+ const token = isSqlParam(params.value) ? params.value : undefined;
215
+ const input = token !== undefined ? parameterDefinition({ definition, param: token }) : params.encoder === undefined ? definition : nativeColumnDefinition({ ...definition, decoder: params.encoder, inputDecoder: undefined });
216
+ let placeholder;
217
+ if (isSqlBindingCollector({ collector: context.collector })) {
218
+ placeholder = recordSqlBinding({
219
+ collector: context.collector,
220
+ source: token === undefined ? { kind: "literal", value: params.value } : { kind: "input", param: token },
221
+ contract: columnInputContract(input),
222
+ transport: columnTransport(definition),
223
+ rejectNull: params.rejectNull,
224
+ encode: ({ value, dialect }) => encodeColumnValue({ value, dialect, definition: input })
225
+ });
226
+ } else {
227
+ if (token !== undefined)
228
+ invalid("SQL input tokens require execution through their reusable template");
229
+ const encoded = encodeColumnValue({ dialect: context.dialect, definition: input, value: params.value });
230
+ placeholder = context.collector.add(encoded);
231
+ if (encoded === null)
232
+ return placeholder;
233
+ }
234
+ if (definition.scalar === "BigInt" && definition.list !== true && context.dialect.name === "sqlite")
235
+ return `CAST(${placeholder} AS INTEGER)`;
236
+ if (definition.scalar === "Json" && definition.list !== true)
237
+ return placeholder + (definition.jsonStorage === "json" && context.dialect.name === "postgres" ? "::text::json" : context.dialect.jsonParamCast);
238
+ return placeholder;
239
+ }
240
+ function predicateDefinition(expression) {
241
+ const state = expressionState(expression);
242
+ if (state.definition?.inputDecoder !== undefined && (!("encode" in state.definition.decoder) || typeof state.definition.decoder.encode !== "function"))
243
+ unsupported("Input tokens cannot infer predicates over read-transformed columns");
244
+ if (state.definition !== undefined)
245
+ return state.definition;
246
+ if (!("encode" in state.decoder) || typeof state.decoder.encode !== "function")
247
+ invalid("A decode-only expression cannot infer a parameter encoder");
248
+ const physical = resultScalarContract({ decoder: state.decoder });
249
+ return { name: "", scalar: physical?.scalar ?? "Unknown", nullable: physical?.nullable ?? true, list: physical?.list ?? false, decoder: state.decoder };
250
+ }
251
+ function bindPredicateParam(params) {
252
+ return bindColumnValue({ context: params.context, definition: predicateDefinition(params.expression), value: params.param, rejectNull: true });
253
+ }
254
+ function childrenState(params) {
255
+ const states = params.children.map(expressionState);
256
+ if (states.some((state) => state.default === true))
257
+ invalid("SQL DEFAULT cannot be used in expressions");
258
+ return {
259
+ decoder: params.decoder,
260
+ render: params.render,
261
+ contract: params.contract ?? params.decoder,
262
+ references: new Set(states.flatMap((state) => [...state.references])),
263
+ ungrouped: new Set(states.flatMap((state) => [...state.ungrouped])),
264
+ aggregate: params.aggregate === true || states.some((state) => state.aggregate)
265
+ };
266
+ }
267
+ function literal(params) {
268
+ assertDecoder(params.decoder);
269
+ if (typeof params.decoder.encode !== "function")
270
+ invalid("SQL values require a bidirectional decoder");
271
+ const decoder = params.decoder;
272
+ const physical = resultScalarContract({ decoder });
273
+ if (physical === undefined)
274
+ invalid("qb.value requires a built-in scalar decoder with a physical SQL type; use qb.raw with explicit SQL for custom decoders");
275
+ const value = snapshotSqlValue(params.value);
276
+ const definition = Object.freeze({ ...physical, name: "", decoder, inputDecoder: decoder });
277
+ const readDecoder = physical.scalar === "Json" ? Object.freeze({ decode: (result) => decoder.decode({ ...result, wire: { ...result.wire, json: "text" } }) }) : decoder;
278
+ const render = (context) => {
279
+ const operand = bindColumnValue({ context, definition, value });
280
+ if (physical.scalar === "Json" && context.dialect.name === "postgres")
281
+ return operand;
282
+ const type = context.dialect.valueCastTypes[physical.scalar];
283
+ return `CAST(${operand} AS ${type}${physical.list ? "[]" : ""})`;
284
+ };
285
+ return makeExpression({
286
+ decoder: readDecoder,
287
+ contract: decoder,
288
+ references: new Set,
289
+ ungrouped: new Set,
290
+ aggregate: false,
291
+ parameter: Object.freeze({ value, decoder }),
292
+ definition,
293
+ render,
294
+ projection: (context) => transportProjection({ context, expression: render(context), definition })
295
+ });
296
+ }
297
+ function comparedLiteral(params) {
298
+ const state = expressionState(params.expression);
299
+ const definition = state.definition;
300
+ if (definition?.inputDecoder !== undefined && (!("encode" in definition.decoder) || typeof definition.decoder.encode !== "function")) {
301
+ unsupported("Implicit literal predicates are unavailable for read-transformed columns; use an explicit trusted SQL input contract");
302
+ }
303
+ const value = snapshotSqlValue(params.value);
304
+ return makeExpression({
305
+ decoder: state.decoder,
306
+ contract: state.contract,
307
+ references: new Set,
308
+ ungrouped: new Set,
309
+ aggregate: false,
310
+ render: (context) => {
311
+ if (state.definition !== undefined)
312
+ return bindColumnValue({ context, definition: state.definition, value });
313
+ if (!("encode" in state.decoder) || typeof state.decoder.encode !== "function")
314
+ invalid("A decode-only expression cannot infer a parameter encoder; use qb.value with an explicit write decoder");
315
+ if (isSqlBindingCollector({ collector: context.collector }))
316
+ return recordSqlBinding({ collector: context.collector, source: { kind: "literal", value }, transport: { kind: "plain" }, encode: (input) => state.decoder.encode({ value: input.value, dialect: input.dialect.name }) });
317
+ return context.collector.add(snapshotSqlValue(state.decoder.encode({ value: snapshotSqlValue(value), dialect: context.dialect.name })));
318
+ }
319
+ });
320
+ }
321
+ function comparison(params) {
322
+ const initialLeft = expressionState(params.left);
323
+ if (isSqlParam(params.right)) {
324
+ const param = params.right;
325
+ assertSqlParamBuilding({ param });
326
+ predicateDefinition(params.left);
327
+ return makeExpression(childrenState({ children: [params.left], decoder: nullableDecoder(decode.boolean), render: (context) => {
328
+ if (context.dialect.name === "sqlite" && initialLeft.definition?.scalar === "Decimal")
329
+ unsupported("SQLite Decimal comparisons are not exact; use an explicit raw numeric contract");
330
+ return `(${initialLeft.render(context)} ${params.operator} ${bindPredicateParam({ context, expression: params.left, param })})`;
331
+ } }));
332
+ }
333
+ if (params.right === null)
334
+ invalid("Use isNull for SQL NULL predicates");
335
+ const right = typeof params.right === "object" && params.right !== null && expressionStates.has(params.right) ? params.right : comparedLiteral({ expression: params.left, value: params.right });
336
+ const leftState = expressionState(params.left);
337
+ const rightState = expressionState(right);
338
+ const unsafeDecimal = (state) => state.definition?.scalar === "Decimal";
339
+ return makeExpression(childrenState({
340
+ children: [params.left, right],
341
+ decoder: nullableDecoder(decode.boolean),
342
+ render: (context) => {
343
+ if (context.dialect.name === "sqlite" && (unsafeDecimal(leftState) || unsafeDecimal(rightState)))
344
+ unsupported("SQLite Decimal comparisons are not exact; use an explicit raw numeric contract");
345
+ const left = leftState.render(context);
346
+ const operand = leftState.parameter === undefined && leftState.definition !== undefined && rightState.parameter !== undefined ? bindColumnValue({ context, definition: leftState.definition, encoder: rightState.parameter.decoder, value: rightState.parameter.value }) : rightState.render(context);
347
+ return `(${left} ${params.operator} ${operand})`;
348
+ }
349
+ }));
350
+ }
351
+ function logic(params) {
352
+ const predicates = [...params.predicates];
353
+ return makeExpression(childrenState({
354
+ children: predicates,
355
+ decoder: nullableDecoder(decode.boolean),
356
+ render: (context) => predicates.length === 0 ? params.operator === "AND" ? "(1 = 1)" : "(1 = 0)" : `(${predicates.map((predicate) => expressionState(predicate).render(context)).join(` ${params.operator} `)})`
357
+ }));
358
+ }
359
+ function aggregate(params) {
360
+ const state = expressionState(params.expression);
361
+ const definition = state.definition;
362
+ if (state.aggregate)
363
+ invalid("Nested aggregate expressions are unsupported");
364
+ if (definition === undefined || definition.list === true || !["String", "Int", "BigInt", "Float", "DateTime"].includes(definition.scalar))
365
+ unsupported("MIN/MAX require a qualified ordered scalar column; use an explicit raw decoder otherwise");
366
+ if (definition.scalar === "String" && definition.nativeType !== undefined && !/^(text|varchar|char|bpchar|tinytext|mediumtext|longtext)(?:\s*\(.*\))?$/i.test(definition.nativeType))
367
+ unsupported("MIN/MAX do not support this native String type; UUID and other non-text types need explicit SQL");
368
+ return makeExpression({
369
+ ...state,
370
+ parameter: undefined,
371
+ column: undefined,
372
+ definition: { ...definition, nullable: true },
373
+ decoder: nullableDecoder(state.decoder),
374
+ contract: columnContract({ ...definition, nullable: true }),
375
+ ungrouped: new Set,
376
+ aggregate: true,
377
+ render: (context) => `${params.fn}(${state.render(context)})`,
378
+ projection: definition.scalar === "BigInt" ? (context) => `CAST(${params.fn}(${state.render(context)}) AS ${context.dialect.name === "mysql" ? "CHAR" : "TEXT"})` : undefined
379
+ });
380
+ }
381
+ function floatAggregate(params) {
382
+ const state = expressionState(params.expression);
383
+ const definition = state.definition;
384
+ if (state.aggregate)
385
+ invalid("Nested aggregate expressions are unsupported");
386
+ if (definition?.scalar !== "Float" || definition.list === true || definition.nativeType !== undefined && !["doubleprecision", "double", "float8"].includes(definition.nativeType.toLowerCase()))
387
+ unsupported("SUM/AVG support only qualified double-precision Float columns; Int, BigInt, Decimal and real/float4 require an explicit raw result contract");
388
+ return makeExpression({
389
+ ...state,
390
+ parameter: undefined,
391
+ column: undefined,
392
+ definition: floatDefinition,
393
+ projection: undefined,
394
+ decoder: nullableDecoder(decode.float),
395
+ contract: columnContract(floatDefinition),
396
+ ungrouped: new Set,
397
+ aggregate: true,
398
+ render: (context) => `${params.fn}(${state.render(context)})`
399
+ });
400
+ }
401
+ function rawExpression(params) {
402
+ if (!isSqlFragment(params.sql))
403
+ invalid("Raw expressions require an authentic core SQL fragment");
404
+ assertDecoder(params.decoder);
405
+ const fragment = params.sql;
406
+ return makeExpression(childrenState({ children: [...params.references ?? []], decoder: params.decoder, render: (context) => renderSqlFragment({ ...context, fragment }) }));
407
+ }
408
+ function nullTest(params) {
409
+ const { expression, not } = params;
410
+ return makeExpression(childrenState({ children: [expression], decoder: decode.boolean, render: (context) => `(${expressionState(expression).render(context)} IS ${not ? "NOT " : ""}NULL)` }));
411
+ }
412
+ function membership(params) {
413
+ if (!Array.isArray(params.values))
414
+ invalid("inList/notInList require an expression and a literal value array");
415
+ const { not } = params;
416
+ const values = [...params.values].map((value) => {
417
+ if (!isSqlParam(value))
418
+ return comparedLiteral({ expression: params.expression, value });
419
+ assertSqlParamBuilding({ param: value });
420
+ predicateDefinition(params.expression);
421
+ return value;
422
+ });
423
+ const renderValue = (value, context) => isSqlParam(value) ? bindPredicateParam({ context, expression: params.expression, param: value }) : expressionState(value).render(context);
424
+ const state = expressionState(params.expression);
425
+ return makeExpression(childrenState({
426
+ children: [params.expression, ...values.filter((value) => !isSqlParam(value))],
427
+ decoder: nullableDecoder(decode.boolean),
428
+ render: (context) => {
429
+ if (state.definition?.scalar === "Decimal" && context.dialect.name === "sqlite")
430
+ unsupported("SQLite Decimal membership is not exact");
431
+ if (values.length === 0)
432
+ return not ? "(1 = 1)" : "(1 = 0)";
433
+ if (state.definition?.scalar === "DateTime" && timeOfDayCodec({ nativeType: state.definition.nativeType }) !== undefined && context.dialect.timeOfDayInStrategy === "equalities") {
434
+ const equalities = values.map((value) => `(${state.render(context)} = ${renderValue(value, context)})`).join(" OR ");
435
+ return not ? `(NOT (${equalities}))` : `(${equalities})`;
436
+ }
437
+ return `(${state.render(context)} ${not ? "NOT " : ""}IN (${values.map((value) => renderValue(value, context)).join(", ")}))`;
438
+ }
439
+ }));
440
+ }
441
+ var eq = (left, right) => comparison({ left, right, operator: "=" });
442
+ var ne = (left, right) => comparison({ left, right, operator: "<>" });
443
+ var gt = (left, right) => comparison({ left, right, operator: ">" });
444
+ var gte = (left, right) => comparison({ left, right, operator: ">=" });
445
+ var lt = (left, right) => comparison({ left, right, operator: "<" });
446
+ var lte = (left, right) => comparison({ left, right, operator: "<=" });
447
+ var isNull = (expression) => nullTest({ expression, not: false });
448
+ var isNotNull = (expression) => nullTest({ expression, not: true });
449
+ var and = (...predicates) => logic({ predicates, operator: "AND" });
450
+ var or = (...predicates) => logic({ predicates, operator: "OR" });
451
+ var not = (predicate) => makeExpression(childrenState({ children: [predicate], decoder: nullableDecoder(decode.boolean), render: (context) => `(NOT ${expressionState(predicate).render(context)})` }));
452
+ var inList = (expression, values) => membership({ expression, values, not: false });
453
+ var notInList = (expression, values) => membership({ expression, values, not: true });
454
+ var count = (expression, options) => {
455
+ const distinct = options?.distinct === true;
456
+ if (distinct && expression === undefined)
457
+ invalid("COUNT DISTINCT requires an expression");
458
+ const operand = expression === undefined ? undefined : expressionState(expression);
459
+ if (operand?.default === true)
460
+ invalid("COUNT cannot consume SQL DEFAULT");
461
+ if (operand?.aggregate === true)
462
+ invalid("Nested aggregate expressions are unsupported");
463
+ return makeExpression({
464
+ decoder: decode.bigInt,
465
+ contract: columnContract(countDefinition),
466
+ references: operand?.references ?? new Set,
467
+ ungrouped: new Set,
468
+ aggregate: true,
469
+ definition: countDefinition,
470
+ render: (context) => `COUNT(${distinct ? "DISTINCT " : ""}${operand === undefined ? "*" : operand.render(context)})`,
471
+ projection: (context) => {
472
+ const count = `COUNT(${distinct ? "DISTINCT " : ""}${operand === undefined ? "*" : operand.render(context)})`;
473
+ return `CAST(${count} AS ${context.dialect.name === "mysql" ? "CHAR" : "TEXT"})`;
474
+ }
475
+ });
476
+ };
477
+ var min = (expression) => aggregate({ expression, fn: "MIN" });
478
+ var max = (expression) => aggregate({ expression, fn: "MAX" });
479
+ var sum = (expression) => floatAggregate({ expression, fn: "SUM" });
480
+ var avg = (expression) => floatAggregate({ expression, fn: "AVG" });
481
+
482
+ // packages/sql/src/query/sources.ts
483
+ function makeTable(params) {
484
+ identifier(params.name);
485
+ if (params.namespace !== undefined)
486
+ identifier(params.namespace);
487
+ const columns = Object.fromEntries(Object.entries(params.columns).map(([key, definition]) => {
488
+ identifier(key);
489
+ identifier(definition.name);
490
+ assertDecoder(definition.decoder);
491
+ if (definition.inputDecoder !== undefined && typeof definition.inputDecoder.encode !== "function")
492
+ invalid("SQL input decoders must have an encoder");
493
+ if (definition.enumType !== undefined) {
494
+ identifier(definition.enumType.name);
495
+ identifier(definition.enumType.namespace);
496
+ }
497
+ return [key, Object.freeze({ ...nativeColumnDefinition(definition), ...definition.enumValues === undefined ? {} : { enumValues: Object.freeze([...definition.enumValues]) }, ...definition.enumType === undefined ? {} : { enumType: Object.freeze({ ...definition.enumType }) } })];
498
+ }));
499
+ if (Object.keys(columns).length === 0)
500
+ invalid("SQL sources require at least one column");
501
+ const physical = Object.values(columns).map((column) => column.name);
502
+ if (new Set(physical).size !== physical.length)
503
+ invalid("Physical column names must be unique within a source");
504
+ const required = [...params.required ?? Object.entries(columns).filter(([, definition]) => definition.nullable !== true && definition.dbDefault !== true).map(([name]) => name)];
505
+ if (required.some((key) => !Object.hasOwn(columns, key)))
506
+ invalid("Required insert columns must belong to the table");
507
+ const source = Object.freeze(Object.create(null));
508
+ sourceStates.set(source, Object.freeze({ name: params.name, namespace: params.namespace, columns: Object.freeze(columns), required: Object.freeze(required), readOnly: params.readOnly === true }));
509
+ return source;
510
+ }
511
+ function makeDerived(params) {
512
+ const query = readState(params.query);
513
+ if (params.name !== undefined) {
514
+ identifier(params.name);
515
+ if (query.physicalNames.includes(params.name))
516
+ invalid("A read CTE cannot shadow a physical source table");
517
+ }
518
+ const columns = Object.fromEntries(query.entries.map(({ alias, expression }) => {
519
+ const state = expressionState(expression);
520
+ const decoder = state.definition?.inputDecoder ?? state.definition?.decoder ?? state.decoder;
521
+ const inputDecoder = "encode" in decoder && typeof decoder.encode === "function" ? decoder : undefined;
522
+ return [alias, Object.freeze({ ...state.definition, name: alias, decoder: state.decoder, inputDecoder, scalar: state.definition?.scalar ?? "Unknown", dbDefault: false })];
523
+ }));
524
+ const source = Object.freeze(Object.create(null));
525
+ sourceStates.set(source, Object.freeze({ columns: Object.freeze(columns), required: Object.freeze([]), query, cte: params.name, readOnly: true }));
526
+ return source;
527
+ }
528
+ function makeBinding(params) {
529
+ const state = sourceState(params.source);
530
+ identifier(params.alias);
531
+ const token = Object.freeze({});
532
+ const keys = Object.fromEntries(Object.keys(state.columns).map((name) => [name, Object.freeze({ binding: token, name })]));
533
+ return Object.freeze({ source: params.source, alias: params.alias, token, keys: Object.freeze(keys), nullable: params.nullable === true });
534
+ }
535
+ function bindingColumns(binding, qualified = true) {
536
+ const source = sourceState(binding.source);
537
+ return Object.freeze(Object.fromEntries(Object.entries(source.columns).map(([name, original]) => {
538
+ const definition = binding.nullable ? Object.freeze({ ...original, nullable: true }) : original;
539
+ const decoder = definition.nullable === true ? nullableDecoder(definition.decoder) : definition.decoder;
540
+ const readDecoder = definition.transport === "text" || definition.scalar === "Json" ? Object.freeze({ decode: (params) => decoder.decode({ ...params, wire: { ...params.wire, json: "text" } }) }) : decoder;
541
+ const column = binding.keys[name];
542
+ const render = (context) => {
543
+ if (definition.list === true && !context.dialect.capabilities.scalarArrays)
544
+ unsupported("Scalar array columns require native array support");
545
+ const name = context.dialect.quoteIdent({ name: definition.name });
546
+ return qualified ? `${context.dialect.quoteIdent({ name: binding.alias })}.${name}` : name;
547
+ };
548
+ return [name, makeExpression({
549
+ decoder: readDecoder,
550
+ render,
551
+ projection: (context) => transportProjection({ context, expression: render(context), definition }),
552
+ references: new Set([binding.token]),
553
+ ungrouped: new Set([column]),
554
+ aggregate: false,
555
+ column,
556
+ definition,
557
+ contract: columnContract(definition)
558
+ })];
559
+ })));
560
+ }
561
+ function scope(bindings) {
562
+ return Object.freeze(Object.fromEntries(bindings.map((binding) => [binding.alias, bindingColumns(binding)])));
563
+ }
564
+ function renderSource(params) {
565
+ const { context, binding } = params;
566
+ const state = sourceState(binding.source);
567
+ const alias = context.dialect.quoteIdent({ name: binding.alias });
568
+ if (state.cte !== undefined)
569
+ return `${context.dialect.quoteIdent({ name: state.cte })} AS ${alias}`;
570
+ if (state.query !== undefined)
571
+ return `(${state.query.render({ ...context, transport: false })}) AS ${alias}`;
572
+ if (state.namespace !== undefined)
573
+ assertSqlNamespace({ dialect: context.dialect.name, namespace: state.namespace });
574
+ const name = context.dialect.quoteIdent({ name: state.name });
575
+ return `${state.namespace === undefined ? "" : `${context.dialect.quoteIdent({ name: state.namespace })}.`}${name} AS ${alias}`;
576
+ }
577
+ function projectionEntries(params) {
578
+ const entries = Object.entries(params.projection);
579
+ if (entries.length === 0)
580
+ invalid("SELECT/RETURNING requires a non-empty explicit projection");
581
+ return Object.freeze(entries.map(([alias, expression]) => {
582
+ identifier(alias);
583
+ checkScope({ expression, bindings: params.bindings, aggregates: params.aggregates, nullableContracts: true });
584
+ return Object.freeze({ alias, expression });
585
+ }));
586
+ }
587
+ function renderEntries(params) {
588
+ return params.entries.map(({ alias, expression }) => {
589
+ const state = expressionState(expression);
590
+ const rendered = params.context.transport !== false && state.projection !== undefined ? state.projection(params.context) : state.render(params.context);
591
+ return `${rendered} AS ${params.context.dialect.quoteIdent({ name: alias })}`;
592
+ }).join(", ");
593
+ }
594
+ function projectionDecoder(entries) {
595
+ return Object.freeze({
596
+ decode: (params) => {
597
+ if (typeof params.value !== "object" || params.value === null || Array.isArray(params.value))
598
+ invalidData("SQL query decoder requires a row object");
599
+ const row = params.value;
600
+ return Object.fromEntries(entries.map(({ alias, expression }) => {
601
+ if (!Object.hasOwn(row, alias))
602
+ invalidData(`Missing SQL result alias "${alias}"`);
603
+ return [alias, expressionState(expression).decoder.decode({ ...params, value: row[alias] })];
604
+ }));
605
+ }
606
+ });
607
+ }
608
+ function groupKeys(expressions) {
609
+ return new Set(expressions.flatMap((expression) => {
610
+ const state = expressionState(expression);
611
+ return state.column === undefined ? [] : [state.column];
612
+ }));
613
+ }
614
+
615
+ // packages/sql/src/query/select.ts
616
+ function count2(value) {
617
+ if (!Number.isSafeInteger(value) || value < 0)
618
+ invalid("SQL LIMIT/OFFSET must be non-negative safe integers");
619
+ return value;
620
+ }
621
+ function pageValue(value) {
622
+ if (isSqlParam2(value)) {
623
+ assertSqlParamBuilding2({ param: value });
624
+ return value;
625
+ }
626
+ return count2(value);
627
+ }
628
+ function renderPage(params) {
629
+ if (!isSqlParam2(params.value))
630
+ return String(params.value);
631
+ const contract = sqlParamContract2({ param: params.value });
632
+ if (contract.scalar !== "Int" || contract.list || contract.nullable || contract.nativeType !== undefined)
633
+ invalid("SQL pagination inputs require a non-null Int contract");
634
+ return recordSqlBinding2({
635
+ collector: params.context.collector,
636
+ source: { kind: "input", param: params.value },
637
+ contract,
638
+ transport: { kind: "plain" },
639
+ rejectNull: true,
640
+ validate: (value) => {
641
+ if (typeof value !== "number")
642
+ invalid("SQL LIMIT/OFFSET must be non-negative safe integers");
643
+ count2(value);
644
+ },
645
+ encode: ({ value, dialect }) => contract.encoder.encode({ value, dialect: dialect.name })
646
+ });
647
+ }
648
+ function predicate(expression, bindings, aggregates) {
649
+ checkScope({ expression, bindings, aggregates });
650
+ return expression;
651
+ }
652
+ function collectCtes(bindings) {
653
+ const ctes = [];
654
+ for (const binding of bindings) {
655
+ const state = sourceState(binding.source);
656
+ if (state.query !== undefined)
657
+ ctes.push(...state.query.ctes);
658
+ if (state.cte !== undefined)
659
+ ctes.push(binding.source);
660
+ }
661
+ const unique = [...new Set(ctes)];
662
+ const names = unique.map((source) => sourceState(source).cte);
663
+ if (new Set(names).size !== names.length)
664
+ invalid("Read CTE names must be unique across composed queries");
665
+ for (const binding of bindings) {
666
+ const source = sourceState(binding.source);
667
+ if (source.name !== undefined && source.namespace === undefined && names.includes(source.name))
668
+ invalid("A read CTE cannot shadow a physical source table");
669
+ }
670
+ return Object.freeze(unique);
671
+ }
672
+ function withCtes(params) {
673
+ const { state, context } = params;
674
+ if (state.ctes.length === 0)
675
+ return state.render(context);
676
+ const definitions = state.ctes.map((source) => {
677
+ const cte = sourceState(source);
678
+ return `${context.dialect.quoteIdent({ name: cte.cte })} AS (${cte.query.render({ ...context, transport: false })})`;
679
+ });
680
+ return `WITH ${definitions.join(", ")} ${state.render(context)}`;
681
+ }
682
+ function finish(state) {
683
+ const query = createSqlQuery({ decoder: projectionDecoder(state.entries), render: (context) => withCtes({ state, context }) });
684
+ readStates.set(query, Object.freeze(state));
685
+ builderTerminals.add(query);
686
+ return query;
687
+ }
688
+ function renderOrder(params) {
689
+ const { context } = params;
690
+ const terms = params.terms.flatMap((term) => {
691
+ const state = expressionState(term.expression);
692
+ if (state.definition?.scalar === "Decimal" && context.dialect.name === "sqlite")
693
+ unsupported("SQLite Decimal ordering is not exact; use an explicit raw numeric contract");
694
+ const expression = () => state.render(context);
695
+ const direction = term.direction === "desc" ? "DESC" : "ASC";
696
+ if (term.nulls === undefined)
697
+ return [`${expression()} ${direction}`];
698
+ if (context.dialect.orderByNullsMode === "native")
699
+ return [`${expression()} ${direction} NULLS ${term.nulls.toUpperCase()}`];
700
+ return [`(${expression()} IS NULL) ${term.nulls === "first" ? "DESC" : "ASC"}`, `${expression()} ${direction}`];
701
+ });
702
+ return terms.length === 0 ? "" : ` ORDER BY ${terms.join(", ")}`;
703
+ }
704
+ function select(params) {
705
+ const { state } = params;
706
+ const entries = projectionEntries({ projection: params.projection, bindings: state.bindings });
707
+ const groupedExpressions = [...entries.map((entry) => entry.expression), ...state.having, ...state.order.map((term) => term.expression)];
708
+ const aggregate = groupedExpressions.some((expression) => expressionState(expression).aggregate);
709
+ if (state.having.length > 0 && state.group === undefined && !aggregate)
710
+ invalid("HAVING requires a grouped or aggregate query");
711
+ if (state.group !== undefined || aggregate) {
712
+ const keys = groupKeys(state.group ?? []);
713
+ for (const expression of groupedExpressions) {
714
+ if (state.group?.includes(expression))
715
+ continue;
716
+ for (const key of expressionState(expression).ungrouped)
717
+ if (!keys.has(key))
718
+ invalid("Every non-aggregate projection, HAVING and ORDER expression must be grouped");
719
+ }
720
+ }
721
+ const ctes = collectCtes(state.bindings);
722
+ const physicalNames = Object.freeze([...new Set(state.bindings.flatMap((binding) => {
723
+ const source = sourceState(binding.source);
724
+ return source.query?.physicalNames ?? (source.name !== undefined && source.namespace === undefined ? [source.name] : []);
725
+ }))]);
726
+ if (ctes.some((source) => physicalNames.includes(sourceState(source).cte)))
727
+ invalid("A read CTE cannot shadow a physical source table");
728
+ const distinct = params.distinct === true;
729
+ const distinctEntries = distinct ? entries.map(({ alias, expression }) => {
730
+ const original = expressionState(expression);
731
+ const render = (context) => `${context.dialect.quoteIdent({ name: "__vibe_distinct" })}.${context.dialect.quoteIdent({ name: alias })}`;
732
+ return { alias, expression: makeExpression({ ...original, render, projection: original.definition === undefined ? undefined : (context) => transportProjection({ context, expression: render(context), definition: original.definition }) }) };
733
+ }) : entries;
734
+ const distinctOrder = distinct ? state.order.map((term) => {
735
+ const ordered = expressionState(term.expression);
736
+ const index = entries.findIndex((entry) => entry.expression === term.expression || ordered.column !== undefined && expressionState(entry.expression).column === ordered.column);
737
+ if (index < 0)
738
+ invalid("DISTINCT ordering must use a selected column or the same selected expression");
739
+ return { ...term, expression: distinctEntries[index].expression };
740
+ }) : state.order;
741
+ return finish({ entries, ctes, physicalNames, render: (context) => {
742
+ const columns = renderEntries({ entries, context: distinct ? { ...context, transport: false } : context });
743
+ const from = renderSource({ context, binding: state.bindings[0] });
744
+ const joins = state.joins.map((join) => ` ${join.kind} JOIN ${renderSource({ context, binding: join.binding })}${join.on === undefined ? "" : ` ON ${expressionState(join.on).render(context)}`}`).join("");
745
+ const where = state.where.length === 0 ? "" : ` WHERE ${state.where.map((expression) => expressionState(expression).render(context)).join(" AND ")}`;
746
+ const group = state.group === undefined || state.group.length === 0 ? "" : ` GROUP BY ${state.group.map((expression) => expressionState(expression).render(context)).join(", ")}`;
747
+ const having = state.having.length === 0 ? "" : ` HAVING ${state.having.map((expression) => expressionState(expression).render(context)).join(" AND ")}`;
748
+ const order = renderOrder({ terms: distinctOrder, context });
749
+ const limit = state.limit === undefined ? state.offset === undefined || context.dialect.offsetRequiresLimit === null ? "" : ` LIMIT ${context.dialect.offsetRequiresLimit.limitText}` : ` LIMIT ${renderPage({ value: state.limit, context })}`;
750
+ const offset = state.offset === undefined ? "" : ` OFFSET ${renderPage({ value: state.offset, context })}`;
751
+ const native = `SELECT ${distinct ? "DISTINCT " : ""}${columns} FROM ${from}${joins}${where}${group}${having}`;
752
+ const query = distinct ? `SELECT ${renderEntries({ entries: distinctEntries, context })} FROM (${native}) AS ${context.dialect.quoteIdent({ name: "__vibe_distinct" })}` : native;
753
+ return `${query}${order}${limit}${offset}`;
754
+ } });
755
+ }
756
+ function builder(state) {
757
+ const addJoin = (params) => {
758
+ if (state.bindings.some((binding) => binding.alias === params.as))
759
+ invalid("SQL source aliases must be unique within a SELECT");
760
+ const joined = makeBinding({ source: params.source, alias: params.as });
761
+ const onBindings = [...state.bindings, joined];
762
+ const on = params.kind === "CROSS" ? undefined : predicate(params.on(scope(onBindings)), onBindings, false);
763
+ const output = params.kind === "LEFT" ? Object.freeze({ ...joined, nullable: true }) : joined;
764
+ return builder({ ...state, bindings: Object.freeze([...state.bindings, output]), joins: Object.freeze([...state.joins, Object.freeze({ binding: output, kind: params.kind, on })]) });
765
+ };
766
+ return Object.freeze({
767
+ innerJoin: (params) => addJoin({ ...params, kind: "INNER" }),
768
+ leftJoin: (params) => addJoin({ ...params, kind: "LEFT" }),
769
+ crossJoin: (params) => addJoin({ ...params, kind: "CROSS" }),
770
+ where: (callback) => {
771
+ if (typeof callback !== "function")
772
+ invalid("SQL WHERE requires a callback");
773
+ return builder({ ...state, where: Object.freeze([...state.where, predicate(callback(scope(state.bindings)), state.bindings, false)]) });
774
+ },
775
+ groupBy: (callback) => {
776
+ if (typeof callback !== "function")
777
+ invalid("SQL GROUP BY requires a callback");
778
+ const expressions = [...callback(scope(state.bindings))];
779
+ for (const expression of expressions)
780
+ checkScope({ expression, bindings: state.bindings, aggregates: false });
781
+ return builder({ ...state, group: Object.freeze(expressions) });
782
+ },
783
+ having: (callback) => {
784
+ if (typeof callback !== "function")
785
+ invalid("SQL HAVING requires a callback");
786
+ return builder({ ...state, having: Object.freeze([...state.having, predicate(callback(scope(state.bindings)), state.bindings, true)]) });
787
+ },
788
+ orderBy: (callback) => {
789
+ if (typeof callback !== "function")
790
+ invalid("SQL ORDER BY requires a callback");
791
+ const terms = callback(scope(state.bindings)).map((term) => {
792
+ checkScope({ expression: term.expression, bindings: state.bindings });
793
+ if (term.direction !== undefined && term.direction !== "asc" && term.direction !== "desc")
794
+ invalid("SQL order direction must be asc or desc");
795
+ if (term.nulls !== undefined && term.nulls !== "first" && term.nulls !== "last")
796
+ invalid("SQL null ordering must be first or last");
797
+ return Object.freeze({ ...term });
798
+ });
799
+ return builder({ ...state, order: Object.freeze([...state.order, ...terms]) });
800
+ },
801
+ limit: (value) => builder({ ...state, limit: pageValue(value) }),
802
+ offset: (value) => builder({ ...state, offset: pageValue(value) }),
803
+ select: (callback, options) => {
804
+ if (typeof callback !== "function")
805
+ invalid("SQL SELECT requires a callback");
806
+ return select({ state, projection: callback(scope(state.bindings)), distinct: options?.distinct });
807
+ }
808
+ });
809
+ }
810
+ function from(params) {
811
+ const binding = makeBinding({ source: params.source, alias: params.as });
812
+ return builder({ bindings: Object.freeze([binding]), joins: Object.freeze([]), where: Object.freeze([]), having: Object.freeze([]), order: Object.freeze([]) });
813
+ }
814
+ function union(params) {
815
+ if (!Array.isArray(params.queries) || params.queries.length < 2)
816
+ invalid("UNION requires at least two builder read queries");
817
+ const all = params.all === true;
818
+ const branches = params.queries.map(readState);
819
+ const first = branches[0];
820
+ for (const branch of branches.slice(1)) {
821
+ if (branch.entries.length !== first.entries.length)
822
+ invalid("UNION branches must expose the same aliases and result contracts");
823
+ for (const expected of first.entries) {
824
+ const other = branch.entries.find((entry) => entry.alias === expected.alias);
825
+ if (other === undefined)
826
+ invalid("UNION branches must expose the same aliases");
827
+ const left = expressionState(expected.expression);
828
+ const right = expressionState(other.expression);
829
+ const leftDecoder = left.definition?.decoder ?? left.decoder;
830
+ const rightDecoder = right.definition?.decoder ?? right.decoder;
831
+ if (left.contract !== right.contract || !sameResultDecoder({ left: leftDecoder, right: rightDecoder }))
832
+ invalid("UNION output decoders, physical types and nullability must match by alias");
833
+ }
834
+ }
835
+ const ctes = [...new Set(branches.flatMap((branch) => [...branch.ctes]))];
836
+ const names = ctes.map((source) => sourceState(source).cte);
837
+ if (new Set(names).size !== names.length)
838
+ invalid("UNION read CTE names must be unique");
839
+ if (names.some((name) => branches.some((branch) => branch.physicalNames.includes(name))))
840
+ invalid("A UNION read CTE cannot shadow a physical source table");
841
+ const entries = first.entries.map(({ alias, expression }) => {
842
+ identifier(alias);
843
+ const original = expressionState(expression);
844
+ return { alias, expression: makeExpression({ ...original, references: new Set, ungrouped: new Set, aggregate: false, column: undefined, render: (context) => `${context.dialect.quoteIdent({ name: "__vibe_union" })}.${context.dialect.quoteIdent({ name: alias })}`, projection: original.definition === undefined ? undefined : (context) => transportProjection({ context, expression: `${context.dialect.quoteIdent({ name: "__vibe_union" })}.${context.dialect.quoteIdent({ name: alias })}`, definition: original.definition }) }) };
845
+ });
846
+ const state = {
847
+ entries,
848
+ ctes: Object.freeze(ctes),
849
+ physicalNames: Object.freeze([...new Set(branches.flatMap((branch) => [...branch.physicalNames]))]),
850
+ render: (context) => {
851
+ const rendered = branches.map((branch, index) => {
852
+ const alias = context.dialect.quoteIdent({ name: `__vibe_branch_${index}` });
853
+ const columns = first.entries.map((entry) => `${alias}.${context.dialect.quoteIdent({ name: entry.alias })}`).join(", ");
854
+ return `SELECT ${columns} FROM (${branch.render({ ...context, transport: false })}) AS ${alias}`;
855
+ }).join(all ? " UNION ALL " : " UNION ");
856
+ return `SELECT ${renderEntries({ entries, context })} FROM (${rendered}) AS ${context.dialect.quoteIdent({ name: "__vibe_union" })}`;
857
+ }
858
+ };
859
+ return finish(state);
860
+ }
861
+
862
+ // packages/sql/src/query/mutations.ts
863
+ import { assertSqlNamespace as assertSqlNamespace2 } from "@vibeorm/schema";
864
+ import { assertSqlParamBuilding as assertSqlParamBuilding3, createSqlCommand, createSqlQuery as createSqlQuery2, decode as decode2, isSqlParam as isSqlParam3, snapshotSqlValue as snapshotSqlValue2 } from "@vibeorm/sql";
865
+ var sqlDefault = makeExpression({ decoder: decode2.json, contract: "sql-default", references: new Set, ungrouped: new Set, aggregate: false, default: true, render: () => "DEFAULT" });
866
+ function tableBinding(table) {
867
+ const state = sourceState(table);
868
+ if (state.readOnly === true || state.query !== undefined || state.name === undefined)
869
+ invalid("SQL writes require a writable physical table; views and read sources cannot be mutated");
870
+ return makeBinding({ source: table, alias: "__vibe_write" });
871
+ }
872
+ function cells(params) {
873
+ if (typeof params.value !== "object" || params.value === null || Array.isArray(params.value))
874
+ invalid("SQL write rows must be field objects");
875
+ const table = sourceState(params.binding.source);
876
+ const row = params.value;
877
+ for (const name of Object.keys(row))
878
+ if (!Object.hasOwn(table.columns, name))
879
+ invalid(`Unknown SQL write field "${name}"`);
880
+ return Object.freeze(Object.keys(table.columns).filter((name) => Object.hasOwn(row, name)).map((name) => {
881
+ const value = row[name];
882
+ if (isSqlParam3(value)) {
883
+ assertSqlParamBuilding3({ param: value });
884
+ return Object.freeze({ name, value });
885
+ }
886
+ if (typeof value === "object" && value !== null && expressionStates.has(value)) {
887
+ const expression = value;
888
+ const state = expressionState(expression);
889
+ if (state.default === true) {
890
+ if (table.columns[name].dbDefault !== true || table.required.includes(name))
891
+ invalid(`SQL DEFAULT requires a physical database default on "${name}"`);
892
+ } else
893
+ checkScope({ expression, bindings: [params.binding], aggregates: false });
894
+ return Object.freeze({ name, value: undefined, expression });
895
+ }
896
+ return Object.freeze({ name, value: snapshotSqlValue2(value) });
897
+ }));
898
+ }
899
+ function physicalTable(context, binding) {
900
+ const table = sourceState(binding.source);
901
+ if (table.namespace !== undefined)
902
+ assertSqlNamespace2({ namespace: table.namespace, dialect: context.dialect.name });
903
+ return `${table.namespace === undefined ? "" : `${context.dialect.quoteIdent({ name: table.namespace })}.`}${context.dialect.quoteIdent({ name: table.name })}`;
904
+ }
905
+ function renderCell(params) {
906
+ const definition = sourceState(params.state.binding.source).columns[params.cell.name];
907
+ if (params.cell.expression !== undefined) {
908
+ const expression = expressionState(params.cell.expression);
909
+ if (expression.default === true && params.state.kind === "insert" && !params.context.dialect.rowDefaultKeyword)
910
+ unsupported("This dialect cannot render SQL DEFAULT in VALUES cells; omit the defaulted column");
911
+ if (expression.default === true && params.state.kind === "update" && !params.context.dialect.rowDefaultKeyword)
912
+ unsupported("This dialect cannot render SQL DEFAULT in UPDATE assignments");
913
+ if (expression.parameter !== undefined)
914
+ return bindColumnValue({ context: params.context, definition, encoder: expression.parameter.decoder, value: expression.parameter.value });
915
+ return expression.render(params.context);
916
+ }
917
+ return bindColumnValue({ context: params.context, definition, value: params.cell.value });
918
+ }
919
+ function renderWrite(params) {
920
+ const { state, context } = params;
921
+ const table = physicalTable(context, state.binding);
922
+ const alias = context.dialect.quoteIdent({ name: state.binding.alias });
923
+ if (state.kind === "insert") {
924
+ const rows = state.rows;
925
+ const fields = rows[0];
926
+ if (fields.length === 0) {
927
+ if (rows.length > 1)
928
+ unsupported("Multiple all-default insert rows cannot preserve one portable statement");
929
+ return context.dialect.name === "mysql" ? `INSERT INTO ${table} () VALUES ()` : `INSERT INTO ${table} DEFAULT VALUES`;
930
+ }
931
+ const definitions = sourceState(state.binding.source).columns;
932
+ const columns = fields.map((cell) => context.dialect.quoteIdent({ name: definitions[cell.name].name })).join(", ");
933
+ const values = rows.map((row) => `(${row.map((cell) => renderCell({ cell, state, context })).join(", ")})`).join(", ");
934
+ return `INSERT INTO ${table} (${columns}) VALUES ${values}`;
935
+ }
936
+ const where = () => state.where.length === 0 ? "" : ` WHERE ${state.where.map((expression) => expressionState(expression).render(context)).join(" AND ")}`;
937
+ if (state.kind === "delete")
938
+ return `DELETE FROM ${table} AS ${alias}${where()}`;
939
+ const definitions = sourceState(state.binding.source).columns;
940
+ const set = state.set.map((cell) => `${context.dialect.quoteIdent({ name: definitions[cell.name].name })} = ${renderCell({ cell, state, context })}`).join(", ");
941
+ return `UPDATE ${table} AS ${alias} SET ${set}${where()}`;
942
+ }
943
+ function mutation(state) {
944
+ return Object.freeze({
945
+ where: (callback) => {
946
+ if (state.kind === "insert")
947
+ invalid("INSERT does not accept WHERE");
948
+ if (typeof callback !== "function")
949
+ invalid("SQL WHERE requires a callback");
950
+ const expression = callback(bindingColumns(state.binding));
951
+ checkScope({ expression, bindings: [state.binding], aggregates: false });
952
+ return mutation({ ...state, where: Object.freeze([...state.where, expression]) });
953
+ },
954
+ command: () => {
955
+ const query = createSqlCommand({ render: (context) => renderWrite({ state, context }) });
956
+ builderTerminals.add(query);
957
+ return query;
958
+ },
959
+ returning: (callback) => {
960
+ if (typeof callback !== "function")
961
+ invalid("SQL RETURNING requires a callback");
962
+ const entries = projectionEntries({ projection: callback(bindingColumns(state.binding, false)), bindings: [state.binding], aggregates: false });
963
+ const query = createSqlQuery2({ decoder: projectionDecoder(entries), render: (context) => {
964
+ if (!context.dialect.capabilities.returning)
965
+ unsupported("This dialect does not support native RETURNING; use a command instead");
966
+ return `${renderWrite({ state, context })} RETURNING ${renderEntries({ entries, context })}`;
967
+ } });
968
+ builderTerminals.add(query);
969
+ return query;
970
+ }
971
+ });
972
+ }
973
+ function insert(params) {
974
+ const binding = tableBinding(params.into);
975
+ const table = sourceState(params.into);
976
+ const values = Array.isArray(params.values) ? params.values : [params.values];
977
+ if (values.length === 0)
978
+ invalid("INSERT requires at least one row");
979
+ const rows = values.map((value) => cells({ value, binding }));
980
+ for (const row of rows) {
981
+ const names = row.map((cell) => cell.name);
982
+ if (table.required.some((name) => !names.includes(name)))
983
+ invalid("INSERT is missing a required physical column; application defaults are not generated");
984
+ if (names.length !== rows[0].length || names.some((name, index) => name !== rows[0][index].name))
985
+ invalid("Batch INSERT rows must have one homogeneous field set; statements are never split implicitly");
986
+ for (const cell of row)
987
+ if (cell.expression !== undefined && expressionState(cell.expression).references.size > 0)
988
+ invalid("INSERT values cannot reference the target table");
989
+ }
990
+ return mutation({ binding, kind: "insert", rows: Object.freeze(rows), where: Object.freeze([]) });
991
+ }
992
+ function update(params) {
993
+ const binding = tableBinding(params.table);
994
+ const set = cells({ value: params.set, binding });
995
+ if (set.length === 0)
996
+ invalid("UPDATE requires at least one assignment");
997
+ return mutation({ binding, kind: "update", set, where: Object.freeze([]) });
998
+ }
999
+ function deleteRows(params) {
1000
+ return mutation({ binding: tableBinding(params.table), kind: "delete", where: Object.freeze([]) });
1001
+ }
1002
+
1003
+ // packages/sql/src/query/reusable.ts
1004
+ import { createReusableSqlCommand, createReusableSqlQuery, createSqlInput, createSqlInputScope, isSqlCommand, isSqlQuery, sealSqlInputScope } from "@vibeorm/sql";
1005
+ var columnInputs = new WeakMap;
1006
+ function input(params) {
1007
+ const table = sourceState(params.table);
1008
+ if (table.readOnly || table.query !== undefined || table.name === undefined)
1009
+ invalid("qb.input requires a writable physical table");
1010
+ if (!Object.hasOwn(table.columns, params.column))
1011
+ invalid("SQL input column does not belong to its table");
1012
+ const definition = table.columns[params.column];
1013
+ let descriptor = columnInputs.get(definition);
1014
+ if (descriptor === undefined) {
1015
+ descriptor = createSqlInput({ contract: { ...columnInputContract(definition), columnIdentity: definition } });
1016
+ columnInputs.set(definition, descriptor);
1017
+ }
1018
+ return descriptor;
1019
+ }
1020
+ function builderHandle(value) {
1021
+ return expressionStates.has(value) || sourceStates.has(value);
1022
+ }
1023
+ function reusable(params) {
1024
+ if (typeof params.build !== "function")
1025
+ invalid("Reusable SQL requires a synchronous builder callback");
1026
+ const environment = createSqlInputScope({ inputs: params.params, isHandle: builderHandle });
1027
+ let query;
1028
+ try {
1029
+ query = params.build(environment.params);
1030
+ } finally {
1031
+ sealSqlInputScope({ scope: environment.scope });
1032
+ }
1033
+ if (typeof query !== "object" || query === null || !builderTerminals.has(query))
1034
+ invalid("Reusable SQL requires an ordinary builder query or command terminal");
1035
+ if (isSqlQuery(query))
1036
+ return createReusableSqlQuery({ query, scope: environment.scope });
1037
+ if (isSqlCommand(query))
1038
+ return createReusableSqlCommand({ query, scope: environment.scope });
1039
+ return invalid("Reusable SQL requires an ordinary builder query or command terminal");
1040
+ }
1041
+
1042
+ // packages/sql/src/query/index.ts
1043
+ var qb = Object.freeze({
1044
+ input,
1045
+ reusable,
1046
+ table: makeTable,
1047
+ from,
1048
+ derived: (params) => makeDerived(params),
1049
+ cte: (params) => makeDerived(params),
1050
+ union,
1051
+ insert,
1052
+ update,
1053
+ delete: deleteRows,
1054
+ default: sqlDefault,
1055
+ value: literal,
1056
+ raw: rawExpression
1057
+ });
1058
+ export {
1059
+ and,
1060
+ avg,
1061
+ count,
1062
+ eq,
1063
+ gt,
1064
+ gte,
1065
+ inList,
1066
+ isNotNull,
1067
+ isNull,
1068
+ lt,
1069
+ lte,
1070
+ max,
1071
+ min,
1072
+ ne,
1073
+ not,
1074
+ notInList,
1075
+ or,
1076
+ qb,
1077
+ sum
1078
+ };
1079
+
1080
+ //# debugId=B60163A02807B75F64756E2164756E21