metal-orm 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4968 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
-
19
- // src/decorators/index.ts
20
- var decorators_exports = {};
21
- __export(decorators_exports, {
22
- BelongsTo: () => BelongsTo,
23
- BelongsToMany: () => BelongsToMany,
24
- Column: () => Column,
25
- Entity: () => Entity,
26
- HasMany: () => HasMany,
27
- HasOne: () => HasOne,
28
- PrimaryKey: () => PrimaryKey,
29
- bootstrapEntities: () => bootstrapEntities,
30
- getTableDefFromEntity: () => getTableDefFromEntity,
31
- selectFromEntity: () => selectFromEntity
32
- });
33
- module.exports = __toCommonJS(decorators_exports);
34
-
35
- // src/schema/table.ts
36
- var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
37
- const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
38
- acc[key] = { ...def, name: key, table: name };
39
- return acc;
40
- }, {});
41
- return {
42
- name,
43
- schema: options.schema,
44
- columns: colsWithNames,
45
- relations,
46
- hooks,
47
- primaryKey: options.primaryKey,
48
- indexes: options.indexes,
49
- checks: options.checks,
50
- comment: options.comment,
51
- engine: options.engine,
52
- charset: options.charset,
53
- collation: options.collation
54
- };
55
- };
56
-
57
- // src/orm/entity-metadata.ts
58
- var metadataMap = /* @__PURE__ */ new Map();
59
- var ensureEntityMetadata = (target) => {
60
- let meta = metadataMap.get(target);
61
- if (!meta) {
62
- meta = {
63
- target,
64
- tableName: target.name || "unknown",
65
- columns: {},
66
- relations: {}
67
- };
68
- metadataMap.set(target, meta);
69
- }
70
- return meta;
71
- };
72
- var getEntityMetadata = (target) => {
73
- return metadataMap.get(target);
74
- };
75
- var getAllEntityMetadata = () => {
76
- return Array.from(metadataMap.values());
77
- };
78
- var addColumnMetadata = (target, propertyKey, column) => {
79
- const meta = ensureEntityMetadata(target);
80
- meta.columns[propertyKey] = { ...column };
81
- };
82
- var addRelationMetadata = (target, propertyKey, relation) => {
83
- const meta = ensureEntityMetadata(target);
84
- meta.relations[propertyKey] = relation;
85
- };
86
- var setEntityTableName = (target, tableName, hooks) => {
87
- const meta = ensureEntityMetadata(target);
88
- if (tableName && tableName.length > 0) {
89
- meta.tableName = tableName;
90
- }
91
- if (hooks) {
92
- meta.hooks = hooks;
93
- }
94
- };
95
- var buildTableDef = (meta) => {
96
- if (meta.table) {
97
- return meta.table;
98
- }
99
- const columns = Object.entries(meta.columns).reduce((acc, [key, def]) => {
100
- acc[key] = {
101
- ...def,
102
- name: key,
103
- table: meta.tableName
104
- };
105
- return acc;
106
- }, {});
107
- const table = defineTable(meta.tableName, columns, {}, meta.hooks);
108
- meta.table = table;
109
- return table;
110
- };
111
-
112
- // src/decorators/decorator-metadata.ts
113
- var METADATA_KEY = "metal-orm:decorators";
114
- var isStandardDecoratorContext = (value) => {
115
- return typeof value === "object" && value !== null && "kind" in value;
116
- };
117
- var getOrCreateMetadataBag = (context) => {
118
- const metadata = context.metadata || (context.metadata = {});
119
- const existing = metadata[METADATA_KEY];
120
- if (existing) {
121
- return existing;
122
- }
123
- const bag = { columns: [], relations: [] };
124
- metadata[METADATA_KEY] = bag;
125
- return bag;
126
- };
127
- var readMetadataBag = (context) => {
128
- return context.metadata?.[METADATA_KEY];
129
- };
130
- var registerInitializer = (context, initializer) => {
131
- context.addInitializer?.(initializer);
132
- };
133
-
134
- // src/decorators/entity.ts
135
- var toSnakeCase = (value) => {
136
- return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
137
- };
138
- var deriveTableNameFromConstructor = (ctor) => {
139
- const fallback = "unknown";
140
- const rawName = ctor.name || fallback;
141
- const strippedName = rawName.replace(/Entity$/i, "");
142
- const normalized = toSnakeCase(strippedName || rawName);
143
- if (!normalized) {
144
- return fallback;
145
- }
146
- return normalized.endsWith("s") ? normalized : `${normalized}s`;
147
- };
148
- function Entity(options = {}) {
149
- const decorator = (value) => {
150
- const tableName = options.tableName ?? deriveTableNameFromConstructor(value);
151
- setEntityTableName(value, tableName, options.hooks);
152
- return value;
153
- };
154
- const decoratorWithContext = (value, context) => {
155
- const ctor = value;
156
- decorator(ctor);
157
- if (context && isStandardDecoratorContext(context)) {
158
- const bag = readMetadataBag(context);
159
- if (bag) {
160
- const meta = ensureEntityMetadata(ctor);
161
- for (const entry of bag.columns) {
162
- if (!meta.columns[entry.propertyName]) {
163
- addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
164
- }
165
- }
166
- for (const entry of bag.relations) {
167
- if (!meta.relations[entry.propertyName]) {
168
- addRelationMetadata(ctor, entry.propertyName, entry.relation);
169
- }
170
- }
171
- }
172
- }
173
- return ctor;
174
- };
175
- return decoratorWithContext;
176
- }
177
-
178
- // src/decorators/column.ts
179
- var normalizeColumnInput = (input) => {
180
- const asOptions = input;
181
- const asDefinition = input;
182
- const column = {
183
- type: asOptions.type ?? asDefinition.type,
184
- args: asOptions.args ?? asDefinition.args,
185
- notNull: asOptions.notNull ?? asDefinition.notNull,
186
- primary: asOptions.primary ?? asDefinition.primary,
187
- unique: asDefinition.unique,
188
- default: asDefinition.default,
189
- autoIncrement: asDefinition.autoIncrement,
190
- generated: asDefinition.generated,
191
- check: asDefinition.check,
192
- references: asDefinition.references,
193
- comment: asDefinition.comment
194
- };
195
- if (!column.type) {
196
- throw new Error("Column decorator requires a column type");
197
- }
198
- return column;
199
- };
200
- var normalizePropertyName = (name) => {
201
- if (typeof name === "symbol") {
202
- return name.description ?? name.toString();
203
- }
204
- return name;
205
- };
206
- var resolveConstructor = (target) => {
207
- if (typeof target === "function") {
208
- return target;
209
- }
210
- if (target && typeof target.constructor === "function") {
211
- return target.constructor;
212
- }
213
- return void 0;
214
- };
215
- var registerColumn = (ctor, propertyName, column) => {
216
- const meta = ensureEntityMetadata(ctor);
217
- if (meta.columns[propertyName]) {
218
- return;
219
- }
220
- addColumnMetadata(ctor, propertyName, column);
221
- };
222
- var registerColumnFromContext = (context, column) => {
223
- if (!context.name) {
224
- throw new Error("Column decorator requires a property name");
225
- }
226
- const propertyName = normalizePropertyName(context.name);
227
- const bag = getOrCreateMetadataBag(context);
228
- if (!bag.columns.some((entry) => entry.propertyName === propertyName)) {
229
- bag.columns.push({ propertyName, column: { ...column } });
230
- }
231
- registerInitializer(context, function() {
232
- const ctor = resolveConstructor(this);
233
- if (!ctor) {
234
- return;
235
- }
236
- registerColumn(ctor, propertyName, column);
237
- });
238
- };
239
- function Column(definition) {
240
- const normalized = normalizeColumnInput(definition);
241
- const decorator = (targetOrValue, propertyKeyOrContext) => {
242
- if (isStandardDecoratorContext(propertyKeyOrContext)) {
243
- registerColumnFromContext(propertyKeyOrContext, normalized);
244
- return;
245
- }
246
- const propertyName = normalizePropertyName(propertyKeyOrContext);
247
- const ctor = resolveConstructor(targetOrValue);
248
- if (!ctor) {
249
- throw new Error("Unable to resolve constructor when registering column metadata");
250
- }
251
- registerColumn(ctor, propertyName, { ...normalized });
252
- };
253
- return decorator;
254
- }
255
- function PrimaryKey(definition) {
256
- const normalized = normalizeColumnInput(definition);
257
- normalized.primary = true;
258
- return Column(normalized);
259
- }
260
-
261
- // src/schema/relation.ts
262
- var RelationKinds = {
263
- /** One-to-one relationship */
264
- HasOne: "HAS_ONE",
265
- /** One-to-many relationship */
266
- HasMany: "HAS_MANY",
267
- /** Many-to-one relationship */
268
- BelongsTo: "BELONGS_TO",
269
- /** Many-to-many relationship with pivot metadata */
270
- BelongsToMany: "BELONGS_TO_MANY"
271
- };
272
- var hasMany = (target, foreignKey, localKey, cascade) => ({
273
- type: RelationKinds.HasMany,
274
- target,
275
- foreignKey,
276
- localKey,
277
- cascade
278
- });
279
- var hasOne = (target, foreignKey, localKey, cascade) => ({
280
- type: RelationKinds.HasOne,
281
- target,
282
- foreignKey,
283
- localKey,
284
- cascade
285
- });
286
- var belongsTo = (target, foreignKey, localKey, cascade) => ({
287
- type: RelationKinds.BelongsTo,
288
- target,
289
- foreignKey,
290
- localKey,
291
- cascade
292
- });
293
- var belongsToMany = (target, pivotTable, options) => ({
294
- type: RelationKinds.BelongsToMany,
295
- target,
296
- pivotTable,
297
- pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
298
- pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
299
- localKey: options.localKey,
300
- targetKey: options.targetKey,
301
- pivotPrimaryKey: options.pivotPrimaryKey,
302
- defaultPivotColumns: options.defaultPivotColumns,
303
- cascade: options.cascade
304
- });
305
-
306
- // src/decorators/relations.ts
307
- var normalizePropertyName2 = (name) => {
308
- if (typeof name === "symbol") {
309
- return name.description ?? name.toString();
310
- }
311
- return name;
312
- };
313
- var resolveConstructor2 = (instanceOrCtor) => {
314
- if (typeof instanceOrCtor === "function") {
315
- return instanceOrCtor;
316
- }
317
- if (instanceOrCtor && typeof instanceOrCtor.constructor === "function") {
318
- return instanceOrCtor.constructor;
319
- }
320
- return void 0;
321
- };
322
- var registerRelation = (ctor, propertyName, metadata) => {
323
- addRelationMetadata(ctor, propertyName, metadata);
324
- };
325
- var createFieldDecorator = (metadataFactory) => {
326
- const decorator = (targetOrValue, propertyKeyOrContext) => {
327
- if (isStandardDecoratorContext(propertyKeyOrContext)) {
328
- const ctx = propertyKeyOrContext;
329
- if (!ctx.name) {
330
- throw new Error("Relation decorator requires a property name");
331
- }
332
- const propertyName2 = normalizePropertyName2(ctx.name);
333
- const bag = getOrCreateMetadataBag(ctx);
334
- const relationMetadata = metadataFactory(propertyName2);
335
- if (!bag.relations.some((entry) => entry.propertyName === propertyName2)) {
336
- bag.relations.push({ propertyName: propertyName2, relation: relationMetadata });
337
- }
338
- registerInitializer(ctx, function() {
339
- const ctor2 = resolveConstructor2(this);
340
- if (!ctor2) {
341
- return;
342
- }
343
- registerRelation(ctor2, propertyName2, relationMetadata);
344
- });
345
- return;
346
- }
347
- const propertyName = normalizePropertyName2(propertyKeyOrContext);
348
- const ctor = resolveConstructor2(targetOrValue);
349
- if (!ctor) {
350
- throw new Error("Unable to resolve constructor when registering relation metadata");
351
- }
352
- registerRelation(ctor, propertyName, metadataFactory(propertyName));
353
- };
354
- return decorator;
355
- };
356
- function HasMany(options) {
357
- return createFieldDecorator((propertyName) => ({
358
- kind: RelationKinds.HasMany,
359
- propertyKey: propertyName,
360
- target: options.target,
361
- foreignKey: options.foreignKey,
362
- localKey: options.localKey,
363
- cascade: options.cascade
364
- }));
365
- }
366
- function HasOne(options) {
367
- return createFieldDecorator((propertyName) => ({
368
- kind: RelationKinds.HasOne,
369
- propertyKey: propertyName,
370
- target: options.target,
371
- foreignKey: options.foreignKey,
372
- localKey: options.localKey,
373
- cascade: options.cascade
374
- }));
375
- }
376
- function BelongsTo(options) {
377
- return createFieldDecorator((propertyName) => ({
378
- kind: RelationKinds.BelongsTo,
379
- propertyKey: propertyName,
380
- target: options.target,
381
- foreignKey: options.foreignKey,
382
- localKey: options.localKey,
383
- cascade: options.cascade
384
- }));
385
- }
386
- function BelongsToMany(options) {
387
- return createFieldDecorator((propertyName) => ({
388
- kind: RelationKinds.BelongsToMany,
389
- propertyKey: propertyName,
390
- target: options.target,
391
- pivotTable: options.pivotTable,
392
- pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
393
- pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
394
- localKey: options.localKey,
395
- targetKey: options.targetKey,
396
- pivotPrimaryKey: options.pivotPrimaryKey,
397
- defaultPivotColumns: options.defaultPivotColumns,
398
- cascade: options.cascade
399
- }));
400
- }
401
-
402
- // src/core/ast/expression-nodes.ts
403
- var operandTypes = /* @__PURE__ */ new Set([
404
- "Column",
405
- "Literal",
406
- "Function",
407
- "JsonPath",
408
- "ScalarSubquery",
409
- "CaseExpression",
410
- "WindowFunction"
411
- ]);
412
- var isOperandNode = (node) => node && operandTypes.has(node.type);
413
- var isFunctionNode = (node) => node?.type === "Function";
414
- var isCaseExpressionNode = (node) => node?.type === "CaseExpression";
415
- var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
416
- var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
417
-
418
- // src/core/ast/expression-builders.ts
419
- var valueToOperand = (value) => {
420
- if (isOperandNode(value)) {
421
- return value;
422
- }
423
- return {
424
- type: "Literal",
425
- value
426
- };
427
- };
428
- var toNode = (col) => {
429
- if (isOperandNode(col)) return col;
430
- const def = col;
431
- return { type: "Column", table: def.table || "unknown", name: def.name };
432
- };
433
- var toLiteralNode = (value) => ({
434
- type: "Literal",
435
- value
436
- });
437
- var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
438
- var toOperand = (val) => {
439
- if (isLiteralValue(val)) {
440
- return valueToOperand(val);
441
- }
442
- return toNode(val);
443
- };
444
- var columnOperand = (col) => toNode(col);
445
- var createBinaryExpression = (operator, left, right, escape) => {
446
- const node = {
447
- type: "BinaryExpression",
448
- left: toNode(left),
449
- operator,
450
- right: toOperand(right)
451
- };
452
- if (escape !== void 0) {
453
- node.escape = toLiteralNode(escape);
454
- }
455
- return node;
456
- };
457
- var eq = (left, right) => createBinaryExpression("=", left, right);
458
- var and = (...operands) => ({
459
- type: "LogicalExpression",
460
- operator: "AND",
461
- operands
462
- });
463
- var createInExpression = (operator, left, values) => ({
464
- type: "InExpression",
465
- left: toNode(left),
466
- operator,
467
- right: values.map((v) => toOperand(v))
468
- });
469
- var inList = (left, values) => createInExpression("IN", left, values);
470
- var exists = (subquery) => ({
471
- type: "ExistsExpression",
472
- operator: "EXISTS",
473
- subquery
474
- });
475
- var notExists = (subquery) => ({
476
- type: "ExistsExpression",
477
- operator: "NOT EXISTS",
478
- subquery
479
- });
480
-
481
- // src/core/sql/sql.ts
482
- var JOIN_KINDS = {
483
- /** INNER JOIN type */
484
- INNER: "INNER",
485
- /** LEFT JOIN type */
486
- LEFT: "LEFT",
487
- /** RIGHT JOIN type */
488
- RIGHT: "RIGHT",
489
- /** CROSS JOIN type */
490
- CROSS: "CROSS"
491
- };
492
- var ORDER_DIRECTIONS = {
493
- /** Ascending order */
494
- ASC: "ASC",
495
- /** Descending order */
496
- DESC: "DESC"
497
- };
498
-
499
- // src/core/ast/aggregate-functions.ts
500
- var buildAggregate = (name) => (col) => ({
501
- type: "Function",
502
- name,
503
- args: [columnOperand(col)]
504
- });
505
- var count = buildAggregate("COUNT");
506
- var sum = buildAggregate("SUM");
507
- var avg = buildAggregate("AVG");
508
- var min = buildAggregate("MIN");
509
- var max = buildAggregate("MAX");
510
-
511
- // src/core/ast/builders.ts
512
- var buildColumnNode = (table, column) => {
513
- if (column.type === "Column") {
514
- return column;
515
- }
516
- const def = column;
517
- const baseTable = def.table ? table.alias && def.table === table.name ? table.alias : def.table : table.alias || table.name;
518
- return {
519
- type: "Column",
520
- table: baseTable,
521
- name: def.name
522
- };
523
- };
524
- var derivedTable = (query, alias, columnAliases) => ({
525
- type: "DerivedTable",
526
- query,
527
- alias,
528
- columnAliases
529
- });
530
-
531
- // src/core/functions/standard-strategy.ts
532
- var StandardFunctionStrategy = class _StandardFunctionStrategy {
533
- constructor() {
534
- this.renderers = /* @__PURE__ */ new Map();
535
- this.registerStandard();
536
- }
537
- registerStandard() {
538
- this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
539
- this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
540
- this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
541
- this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
542
- this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
543
- this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
544
- this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
545
- this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
546
- this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
547
- this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
548
- this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
549
- this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
550
- this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
551
- this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
552
- this.add("NOW", () => `NOW()`);
553
- this.add("CURRENT_DATE", () => `CURRENT_DATE`);
554
- this.add("CURRENT_TIME", () => `CURRENT_TIME`);
555
- this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
556
- this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
557
- this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
558
- this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
559
- this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
560
- this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
561
- this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
562
- this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
563
- this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
564
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
565
- this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
566
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
567
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
568
- this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
569
- this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
570
- }
571
- add(name, renderer) {
572
- this.renderers.set(name, renderer);
573
- }
574
- getRenderer(name) {
575
- return this.renderers.get(name);
576
- }
577
- renderGroupConcat(ctx) {
578
- const arg = ctx.compiledArgs[0];
579
- const orderClause = this.buildOrderByExpression(ctx);
580
- const orderSegment = orderClause ? ` ${orderClause}` : "";
581
- const separatorClause = this.formatGroupConcatSeparator(ctx);
582
- return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
583
- }
584
- buildOrderByExpression(ctx) {
585
- const orderBy = ctx.node.orderBy;
586
- if (!orderBy || orderBy.length === 0) {
587
- return "";
588
- }
589
- const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
590
- return `ORDER BY ${parts.join(", ")}`;
591
- }
592
- formatGroupConcatSeparator(ctx) {
593
- if (!ctx.node.separator) {
594
- return "";
595
- }
596
- return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
597
- }
598
- getGroupConcatSeparatorOperand(ctx) {
599
- return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
600
- }
601
- static {
602
- this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
603
- type: "Literal",
604
- value: ","
605
- };
606
- }
607
- };
608
-
609
- // src/core/dialect/abstract.ts
610
- var Dialect = class _Dialect {
611
- /**
612
- * Compiles a SELECT query AST to SQL
613
- * @param ast - Query AST to compile
614
- * @returns Compiled query with SQL and parameters
615
- */
616
- compileSelect(ast) {
617
- const ctx = this.createCompilerContext();
618
- const normalized = this.normalizeSelectAst(ast);
619
- const rawSql = this.compileSelectAst(normalized, ctx).trim();
620
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
621
- return {
622
- sql,
623
- params: [...ctx.params]
624
- };
625
- }
626
- compileInsert(ast) {
627
- const ctx = this.createCompilerContext();
628
- const rawSql = this.compileInsertAst(ast, ctx).trim();
629
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
630
- return {
631
- sql,
632
- params: [...ctx.params]
633
- };
634
- }
635
- compileUpdate(ast) {
636
- const ctx = this.createCompilerContext();
637
- const rawSql = this.compileUpdateAst(ast, ctx).trim();
638
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
639
- return {
640
- sql,
641
- params: [...ctx.params]
642
- };
643
- }
644
- compileDelete(ast) {
645
- const ctx = this.createCompilerContext();
646
- const rawSql = this.compileDeleteAst(ast, ctx).trim();
647
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
648
- return {
649
- sql,
650
- params: [...ctx.params]
651
- };
652
- }
653
- supportsReturning() {
654
- return false;
655
- }
656
- /**
657
- * Compiles a WHERE clause
658
- * @param where - WHERE expression
659
- * @param ctx - Compiler context
660
- * @returns SQL WHERE clause or empty string
661
- */
662
- compileWhere(where, ctx) {
663
- if (!where) return "";
664
- return ` WHERE ${this.compileExpression(where, ctx)}`;
665
- }
666
- compileReturning(returning, ctx) {
667
- if (!returning || returning.length === 0) return "";
668
- throw new Error("RETURNING is not supported by this dialect.");
669
- }
670
- /**
671
- * Generates subquery for EXISTS expressions
672
- * Rule: Always forces SELECT 1, ignoring column list
673
- * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
674
- * Does not add ';' at the end
675
- * @param ast - Query AST
676
- * @param ctx - Compiler context
677
- * @returns SQL for EXISTS subquery
678
- */
679
- compileSelectForExists(ast, ctx) {
680
- const normalized = this.normalizeSelectAst(ast);
681
- const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
682
- if (normalized.setOps && normalized.setOps.length > 0) {
683
- return `SELECT 1 FROM (${full}) AS _exists`;
684
- }
685
- const upper = full.toUpperCase();
686
- const fromIndex = upper.indexOf(" FROM ");
687
- if (fromIndex === -1) {
688
- return full;
689
- }
690
- const tail = full.slice(fromIndex);
691
- return `SELECT 1${tail}`;
692
- }
693
- /**
694
- * Creates a new compiler context
695
- * @returns Compiler context with parameter management
696
- */
697
- createCompilerContext() {
698
- const params = [];
699
- let counter = 0;
700
- return {
701
- params,
702
- addParameter: (value) => {
703
- counter += 1;
704
- params.push(value);
705
- return this.formatPlaceholder(counter);
706
- }
707
- };
708
- }
709
- /**
710
- * Formats a parameter placeholder
711
- * @param index - Parameter index
712
- * @returns Formatted placeholder string
713
- */
714
- formatPlaceholder(index) {
715
- return "?";
716
- }
717
- /**
718
- * Whether the current dialect supports a given set operation.
719
- * Override in concrete dialects to restrict support.
720
- */
721
- supportsSetOperation(kind) {
722
- return true;
723
- }
724
- /**
725
- * Validates set-operation semantics:
726
- * - Ensures the dialect supports requested operators.
727
- * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
728
- * @param ast - Query to validate
729
- * @param isOutermost - Whether this node is the outermost compound query
730
- */
731
- validateSetOperations(ast, isOutermost = true) {
732
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
733
- if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
734
- throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
735
- }
736
- if (hasSetOps) {
737
- for (const op of ast.setOps) {
738
- if (!this.supportsSetOperation(op.operator)) {
739
- throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
740
- }
741
- this.validateSetOperations(op.query, false);
742
- }
743
- }
744
- }
745
- /**
746
- * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
747
- * @param ast - Query AST
748
- * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
749
- */
750
- hoistCtes(ast) {
751
- let hoisted = [];
752
- const normalizedSetOps = ast.setOps?.map((op) => {
753
- const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
754
- const childCtes = child.ctes ?? [];
755
- if (childCtes.length) {
756
- hoisted = hoisted.concat(childCtes);
757
- }
758
- hoisted = hoisted.concat(childHoisted);
759
- const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
760
- return { ...op, query: queryWithoutCtes };
761
- });
762
- const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
763
- return { normalized, hoistedCtes: hoisted };
764
- }
765
- /**
766
- * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
767
- * @param ast - Query AST
768
- * @returns Normalized query AST
769
- */
770
- normalizeSelectAst(ast) {
771
- this.validateSetOperations(ast, true);
772
- const { normalized, hoistedCtes } = this.hoistCtes(ast);
773
- const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
774
- return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
775
- }
776
- constructor(functionStrategy) {
777
- this.expressionCompilers = /* @__PURE__ */ new Map();
778
- this.operandCompilers = /* @__PURE__ */ new Map();
779
- this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
780
- this.registerDefaultOperandCompilers();
781
- this.registerDefaultExpressionCompilers();
782
- }
783
- /**
784
- * Creates a new Dialect instance (for testing purposes)
785
- * @param functionStrategy - Optional function strategy
786
- * @returns New Dialect instance
787
- */
788
- static create(functionStrategy) {
789
- class TestDialect extends _Dialect {
790
- constructor() {
791
- super(...arguments);
792
- this.dialect = "sqlite";
793
- }
794
- quoteIdentifier(id) {
795
- return `"${id}"`;
796
- }
797
- compileSelectAst() {
798
- throw new Error("Not implemented");
799
- }
800
- compileInsertAst() {
801
- throw new Error("Not implemented");
802
- }
803
- compileUpdateAst() {
804
- throw new Error("Not implemented");
805
- }
806
- compileDeleteAst() {
807
- throw new Error("Not implemented");
808
- }
809
- }
810
- return new TestDialect(functionStrategy);
811
- }
812
- /**
813
- * Registers an expression compiler for a specific node type
814
- * @param type - Expression node type
815
- * @param compiler - Compiler function
816
- */
817
- registerExpressionCompiler(type, compiler) {
818
- this.expressionCompilers.set(type, compiler);
819
- }
820
- /**
821
- * Registers an operand compiler for a specific node type
822
- * @param type - Operand node type
823
- * @param compiler - Compiler function
824
- */
825
- registerOperandCompiler(type, compiler) {
826
- this.operandCompilers.set(type, compiler);
827
- }
828
- /**
829
- * Compiles an expression node
830
- * @param node - Expression node to compile
831
- * @param ctx - Compiler context
832
- * @returns Compiled SQL expression
833
- */
834
- compileExpression(node, ctx) {
835
- const compiler = this.expressionCompilers.get(node.type);
836
- if (!compiler) {
837
- throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
838
- }
839
- return compiler(node, ctx);
840
- }
841
- /**
842
- * Compiles an operand node
843
- * @param node - Operand node to compile
844
- * @param ctx - Compiler context
845
- * @returns Compiled SQL operand
846
- */
847
- compileOperand(node, ctx) {
848
- const compiler = this.operandCompilers.get(node.type);
849
- if (!compiler) {
850
- throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
851
- }
852
- return compiler(node, ctx);
853
- }
854
- registerDefaultExpressionCompilers() {
855
- this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
856
- const left = this.compileOperand(binary.left, ctx);
857
- const right = this.compileOperand(binary.right, ctx);
858
- const base = `${left} ${binary.operator} ${right}`;
859
- if (binary.escape) {
860
- const escapeOperand = this.compileOperand(binary.escape, ctx);
861
- return `${base} ESCAPE ${escapeOperand}`;
862
- }
863
- return base;
864
- });
865
- this.registerExpressionCompiler("LogicalExpression", (logical, ctx) => {
866
- if (logical.operands.length === 0) return "";
867
- const parts = logical.operands.map((op) => {
868
- const compiled = this.compileExpression(op, ctx);
869
- return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
870
- });
871
- return parts.join(` ${logical.operator} `);
872
- });
873
- this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
874
- const left = this.compileOperand(nullExpr.left, ctx);
875
- return `${left} ${nullExpr.operator}`;
876
- });
877
- this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
878
- const left = this.compileOperand(inExpr.left, ctx);
879
- const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
880
- return `${left} ${inExpr.operator} (${values})`;
881
- });
882
- this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
883
- const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
884
- return `${existsExpr.operator} (${subquerySql})`;
885
- });
886
- this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
887
- const left = this.compileOperand(betweenExpr.left, ctx);
888
- const lower = this.compileOperand(betweenExpr.lower, ctx);
889
- const upper = this.compileOperand(betweenExpr.upper, ctx);
890
- return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
891
- });
892
- }
893
- registerDefaultOperandCompilers() {
894
- this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
895
- this.registerOperandCompiler("Column", (column, _ctx) => {
896
- return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
897
- });
898
- this.registerOperandCompiler(
899
- "Function",
900
- (fnNode, ctx) => this.compileFunctionOperand(fnNode, ctx)
901
- );
902
- this.registerOperandCompiler("JsonPath", (path, _ctx) => this.compileJsonPath(path));
903
- this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
904
- const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
905
- return `(${sql})`;
906
- });
907
- this.registerOperandCompiler("CaseExpression", (node, ctx) => {
908
- const parts = ["CASE"];
909
- for (const { when, then } of node.conditions) {
910
- parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
911
- }
912
- if (node.else) {
913
- parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
914
- }
915
- parts.push("END");
916
- return parts.join(" ");
917
- });
918
- this.registerOperandCompiler("WindowFunction", (node, ctx) => {
919
- let result = `${node.name}(`;
920
- if (node.args.length > 0) {
921
- result += node.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
922
- }
923
- result += ") OVER (";
924
- const parts = [];
925
- if (node.partitionBy && node.partitionBy.length > 0) {
926
- const partitionClause = "PARTITION BY " + node.partitionBy.map(
927
- (col) => `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
928
- ).join(", ");
929
- parts.push(partitionClause);
930
- }
931
- if (node.orderBy && node.orderBy.length > 0) {
932
- const orderClause = "ORDER BY " + node.orderBy.map(
933
- (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
934
- ).join(", ");
935
- parts.push(orderClause);
936
- }
937
- result += parts.join(" ");
938
- result += ")";
939
- return result;
940
- });
941
- }
942
- // Default fallback, should be overridden by dialects if supported
943
- compileJsonPath(node) {
944
- throw new Error("JSON Path not supported by this dialect");
945
- }
946
- /**
947
- * Compiles a function operand, using the dialect's function strategy.
948
- */
949
- compileFunctionOperand(fnNode, ctx) {
950
- const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
951
- const renderer = this.functionStrategy.getRenderer(fnNode.name);
952
- if (renderer) {
953
- return renderer({
954
- node: fnNode,
955
- compiledArgs,
956
- compileOperand: (operand) => this.compileOperand(operand, ctx)
957
- });
958
- }
959
- return `${fnNode.name}(${compiledArgs.join(", ")})`;
960
- }
961
- };
962
-
963
- // src/core/dialect/base/function-table-formatter.ts
964
- var FunctionTableFormatter = class {
965
- /**
966
- * Formats a function table node into SQL syntax.
967
- * @param fn - The function table node containing schema, name, args, and aliases.
968
- * @param ctx - Optional compiler context for operand compilation.
969
- * @param dialect - The dialect instance for compiling operands.
970
- * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
971
- */
972
- static format(fn, ctx, dialect) {
973
- const schemaPart = this.formatSchema(fn, dialect);
974
- const args = this.formatArgs(fn, ctx, dialect);
975
- const base = this.formatBase(fn, schemaPart, args, dialect);
976
- const lateral = this.formatLateral(fn);
977
- const alias = this.formatAlias(fn, dialect);
978
- const colAliases = this.formatColumnAliases(fn, dialect);
979
- return `${lateral}${base}${alias}${colAliases}`;
980
- }
981
- /**
982
- * Formats the schema prefix for the function name.
983
- * @param fn - The function table node.
984
- * @param dialect - The dialect instance for quoting identifiers.
985
- * @returns Schema prefix (e.g., "schema.") or empty string.
986
- * @internal
987
- */
988
- static formatSchema(fn, dialect) {
989
- if (!fn.schema) return "";
990
- const quoted = dialect ? dialect.quoteIdentifier(fn.schema) : fn.schema;
991
- return `${quoted}.`;
992
- }
993
- /**
994
- * Formats function arguments into SQL syntax.
995
- * @param fn - The function table node containing arguments.
996
- * @param ctx - Optional compiler context for operand compilation.
997
- * @param dialect - The dialect instance for compiling operands.
998
- * @returns Comma-separated function arguments.
999
- * @internal
1000
- */
1001
- static formatArgs(fn, ctx, dialect) {
1002
- return (fn.args || []).map((a) => {
1003
- if (ctx && dialect) {
1004
- return dialect.compileOperand(a, ctx);
1005
- }
1006
- return String(a);
1007
- }).join(", ");
1008
- }
1009
- /**
1010
- * Formats the base function call with WITH ORDINALITY if present.
1011
- * @param fn - The function table node.
1012
- * @param schemaPart - Formatted schema prefix.
1013
- * @param args - Formatted function arguments.
1014
- * @param dialect - The dialect instance for quoting identifiers.
1015
- * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
1016
- * @internal
1017
- */
1018
- static formatBase(fn, schemaPart, args, dialect) {
1019
- const ordinality = fn.withOrdinality ? " WITH ORDINALITY" : "";
1020
- const quoted = dialect ? dialect.quoteIdentifier(fn.name) : fn.name;
1021
- return `${schemaPart}${quoted}(${args})${ordinality}`;
1022
- }
1023
- /**
1024
- * Formats the LATERAL keyword if present.
1025
- * @param fn - The function table node.
1026
- * @returns "LATERAL " or empty string.
1027
- * @internal
1028
- */
1029
- static formatLateral(fn) {
1030
- return fn.lateral ? "LATERAL " : "";
1031
- }
1032
- /**
1033
- * Formats the table alias for the function table.
1034
- * @param fn - The function table node.
1035
- * @param dialect - The dialect instance for quoting identifiers.
1036
- * @returns " AS alias" or empty string.
1037
- * @internal
1038
- */
1039
- static formatAlias(fn, dialect) {
1040
- if (!fn.alias) return "";
1041
- const quoted = dialect ? dialect.quoteIdentifier(fn.alias) : fn.alias;
1042
- return ` AS ${quoted}`;
1043
- }
1044
- /**
1045
- * Formats column aliases for the function table result columns.
1046
- * @param fn - The function table node containing column aliases.
1047
- * @param dialect - The dialect instance for quoting identifiers.
1048
- * @returns "(col1, col2, ...)" or empty string.
1049
- * @internal
1050
- */
1051
- static formatColumnAliases(fn, dialect) {
1052
- if (!fn.columnAliases || !fn.columnAliases.length) return "";
1053
- const aliases = fn.columnAliases.map((col) => dialect ? dialect.quoteIdentifier(col) : col).join(", ");
1054
- return `(${aliases})`;
1055
- }
1056
- };
1057
-
1058
- // src/core/dialect/base/pagination-strategy.ts
1059
- var StandardLimitOffsetPagination = class {
1060
- /**
1061
- * Compiles LIMIT/OFFSET pagination clause.
1062
- * @param limit - The maximum number of rows to return.
1063
- * @param offset - The number of rows to skip.
1064
- * @returns SQL pagination clause with LIMIT and/or OFFSET.
1065
- */
1066
- compilePagination(limit, offset) {
1067
- const parts = [];
1068
- if (limit !== void 0) parts.push(`LIMIT ${limit}`);
1069
- if (offset !== void 0) parts.push(`OFFSET ${offset}`);
1070
- return parts.length ? ` ${parts.join(" ")}` : "";
1071
- }
1072
- };
1073
-
1074
- // src/core/dialect/base/cte-compiler.ts
1075
- var CteCompiler = class {
1076
- /**
1077
- * Compiles CTEs (WITH clauses) including recursive CTEs.
1078
- * @param ast - The SELECT query AST containing CTE definitions.
1079
- * @param ctx - The compiler context for expression compilation.
1080
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1081
- * @param compileSelectAst - Function to recursively compile SELECT query ASTs.
1082
- * @param normalizeSelectAst - Function to normalize SELECT query ASTs before compilation.
1083
- * @param stripTrailingSemicolon - Function to remove trailing semicolons from SQL.
1084
- * @returns SQL WITH clause string (e.g., "WITH cte_name AS (...) ") or empty string if no CTEs.
1085
- */
1086
- static compileCtes(ast, ctx, quoteIdentifier, compileSelectAst, normalizeSelectAst, stripTrailingSemicolon) {
1087
- if (!ast.ctes || ast.ctes.length === 0) return "";
1088
- const hasRecursive = ast.ctes.some((cte) => cte.recursive);
1089
- const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
1090
- const cteDefs = ast.ctes.map((cte) => {
1091
- const name = quoteIdentifier(cte.name);
1092
- const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => quoteIdentifier(c)).join(", ")})` : "";
1093
- const query = stripTrailingSemicolon(compileSelectAst(normalizeSelectAst(cte.query), ctx));
1094
- return `${name}${cols} AS (${query})`;
1095
- }).join(", ");
1096
- return `${prefix}${cteDefs} `;
1097
- }
1098
- };
1099
-
1100
- // src/core/dialect/base/returning-strategy.ts
1101
- var NoReturningStrategy = class {
1102
- /**
1103
- * Throws an error as RETURNING is not supported.
1104
- * @param returning - Columns to return (causes error if non-empty).
1105
- * @param _ctx - Compiler context (unused).
1106
- * @throws Error indicating RETURNING is not supported.
1107
- */
1108
- compileReturning(returning, _ctx) {
1109
- if (!returning || returning.length === 0) return "";
1110
- throw new Error("RETURNING is not supported by this dialect.");
1111
- }
1112
- /**
1113
- * Formats column names for RETURNING clause.
1114
- * @param returning - Columns to format.
1115
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1116
- * @returns Simple comma-separated column names.
1117
- */
1118
- formatReturningColumns(returning, quoteIdentifier) {
1119
- return returning.map((column) => {
1120
- const tablePart = column.table ? `${quoteIdentifier(column.table)}.` : "";
1121
- const aliasPart = column.alias ? ` AS ${quoteIdentifier(column.alias)}` : "";
1122
- return `${tablePart}${quoteIdentifier(column.name)}${aliasPart}`;
1123
- }).join(", ");
1124
- }
1125
- };
1126
-
1127
- // src/core/dialect/base/join-compiler.ts
1128
- var JoinCompiler = class {
1129
- /**
1130
- * Compiles all JOIN clauses from a SELECT query AST.
1131
- * @param ast - The SELECT query AST containing join definitions.
1132
- * @param ctx - The compiler context for expression compilation.
1133
- * @param compileFrom - Function to compile table sources (tables or subqueries).
1134
- * @param compileExpression - Function to compile join condition expressions.
1135
- * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
1136
- */
1137
- static compileJoins(ast, ctx, compileFrom, compileExpression) {
1138
- if (!ast.joins || ast.joins.length === 0) return "";
1139
- const parts = ast.joins.map((j) => {
1140
- const table = compileFrom(j.table, ctx);
1141
- const cond = compileExpression(j.condition, ctx);
1142
- return `${j.kind} JOIN ${table} ON ${cond}`;
1143
- });
1144
- return ` ${parts.join(" ")}`;
1145
- }
1146
- };
1147
-
1148
- // src/core/dialect/base/groupby-compiler.ts
1149
- var GroupByCompiler = class {
1150
- /**
1151
- * Compiles GROUP BY clause from a SELECT query AST.
1152
- * @param ast - The SELECT query AST containing grouping columns.
1153
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1154
- * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
1155
- */
1156
- static compileGroupBy(ast, quoteIdentifier) {
1157
- if (!ast.groupBy || ast.groupBy.length === 0) return "";
1158
- const cols = ast.groupBy.map((c) => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`).join(", ");
1159
- return ` GROUP BY ${cols}`;
1160
- }
1161
- };
1162
-
1163
- // src/core/dialect/base/orderby-compiler.ts
1164
- var OrderByCompiler = class {
1165
- /**
1166
- * Compiles ORDER BY clause from a SELECT query AST.
1167
- * @param ast - The SELECT query AST containing sort specifications.
1168
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1169
- * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
1170
- */
1171
- static compileOrderBy(ast, quoteIdentifier) {
1172
- if (!ast.orderBy || ast.orderBy.length === 0) return "";
1173
- const parts = ast.orderBy.map((o) => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1174
- return ` ORDER BY ${parts}`;
1175
- }
1176
- };
1177
-
1178
- // src/core/dialect/base/sql-dialect.ts
1179
- var SqlDialectBase = class extends Dialect {
1180
- constructor() {
1181
- super(...arguments);
1182
- this.paginationStrategy = new StandardLimitOffsetPagination();
1183
- this.returningStrategy = new NoReturningStrategy();
1184
- }
1185
- compileSelectAst(ast, ctx) {
1186
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
1187
- const ctes = CteCompiler.compileCtes(
1188
- ast,
1189
- ctx,
1190
- this.quoteIdentifier.bind(this),
1191
- this.compileSelectAst.bind(this),
1192
- this.normalizeSelectAst?.bind(this) ?? ((a) => a),
1193
- this.stripTrailingSemicolon.bind(this)
1194
- );
1195
- const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
1196
- const baseSelect = this.compileSelectCore(baseAst, ctx);
1197
- if (!hasSetOps) {
1198
- return `${ctes}${baseSelect}`;
1199
- }
1200
- return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
1201
- }
1202
- compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
1203
- const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1204
- const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1205
- const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1206
- const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1207
- return `${ctes}${combined}${orderBy}${pagination}`;
1208
- }
1209
- compileInsertAst(ast, ctx) {
1210
- const table = this.compileTableName(ast.into);
1211
- const columnList = this.compileInsertColumnList(ast.columns);
1212
- const values = this.compileInsertValues(ast.values, ctx);
1213
- const returning = this.compileReturning(ast.returning, ctx);
1214
- return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
1215
- }
1216
- compileReturning(returning, ctx) {
1217
- return this.returningStrategy.compileReturning(returning, ctx);
1218
- }
1219
- compileInsertColumnList(columns) {
1220
- return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1221
- }
1222
- compileInsertValues(values, ctx) {
1223
- return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1224
- }
1225
- compileSelectCore(ast, ctx) {
1226
- const columns = this.compileSelectColumns(ast, ctx);
1227
- const from = this.compileFrom(ast.from, ctx);
1228
- const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
1229
- const whereClause = this.compileWhere(ast.where, ctx);
1230
- const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
1231
- const having = this.compileHaving(ast, ctx);
1232
- const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1233
- const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1234
- return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
1235
- }
1236
- compileUpdateAst(ast, ctx) {
1237
- const table = this.compileTableName(ast.table);
1238
- const assignments = this.compileUpdateAssignments(ast.set, ctx);
1239
- const whereClause = this.compileWhere(ast.where, ctx);
1240
- const returning = this.compileReturning(ast.returning, ctx);
1241
- return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
1242
- }
1243
- compileUpdateAssignments(assignments, ctx) {
1244
- return assignments.map((assignment) => {
1245
- const col = assignment.column;
1246
- const target = this.quoteIdentifier(col.name);
1247
- const value = this.compileOperand(assignment.value, ctx);
1248
- return `${target} = ${value}`;
1249
- }).join(", ");
1250
- }
1251
- compileDeleteAst(ast, ctx) {
1252
- const table = this.compileTableName(ast.from);
1253
- const whereClause = this.compileWhere(ast.where, ctx);
1254
- const returning = this.compileReturning(ast.returning, ctx);
1255
- return `DELETE FROM ${table}${whereClause}${returning}`;
1256
- }
1257
- formatReturningColumns(returning) {
1258
- return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
1259
- }
1260
- compileDistinct(ast) {
1261
- return ast.distinct ? "DISTINCT " : "";
1262
- }
1263
- compileSelectColumns(ast, ctx) {
1264
- return ast.columns.map((c) => {
1265
- const expr = this.compileOperand(c, ctx);
1266
- if (c.alias) {
1267
- if (c.alias.includes("(")) return c.alias;
1268
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1269
- }
1270
- return expr;
1271
- }).join(", ");
1272
- }
1273
- compileFrom(ast, ctx) {
1274
- const tableSource = ast;
1275
- if (tableSource.type === "FunctionTable") {
1276
- return this.compileFunctionTable(tableSource, ctx);
1277
- }
1278
- if (tableSource.type === "DerivedTable") {
1279
- return this.compileDerivedTable(tableSource, ctx);
1280
- }
1281
- return this.compileTableSource(tableSource);
1282
- }
1283
- compileFunctionTable(fn, ctx) {
1284
- return FunctionTableFormatter.format(fn, ctx, this);
1285
- }
1286
- compileDerivedTable(table, ctx) {
1287
- if (!table.alias) {
1288
- throw new Error("Derived tables must have an alias.");
1289
- }
1290
- const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1291
- const columns = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1292
- return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
1293
- }
1294
- compileTableSource(table) {
1295
- if (table.type === "FunctionTable") {
1296
- return this.compileFunctionTable(table);
1297
- }
1298
- if (table.type === "DerivedTable") {
1299
- return this.compileDerivedTable(table);
1300
- }
1301
- const base = this.compileTableName(table);
1302
- return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1303
- }
1304
- compileTableName(table) {
1305
- if (table.schema) {
1306
- return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
1307
- }
1308
- return this.quoteIdentifier(table.name);
1309
- }
1310
- compileHaving(ast, ctx) {
1311
- if (!ast.having) return "";
1312
- return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
1313
- }
1314
- stripTrailingSemicolon(sql) {
1315
- return sql.trim().replace(/;$/, "");
1316
- }
1317
- wrapSetOperand(sql) {
1318
- const trimmed = this.stripTrailingSemicolon(sql);
1319
- return `(${trimmed})`;
1320
- }
1321
- };
1322
-
1323
- // src/core/dialect/postgres/functions.ts
1324
- var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1325
- constructor() {
1326
- super();
1327
- this.registerOverrides();
1328
- }
1329
- registerOverrides() {
1330
- this.add("UTC_NOW", () => `(NOW() AT TIME ZONE 'UTC')`);
1331
- this.add("UNIX_TIMESTAMP", () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
1332
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1333
- if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1334
- return `to_timestamp(${compiledArgs[0]})`;
1335
- });
1336
- this.add("EXTRACT", ({ compiledArgs }) => {
1337
- if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1338
- const [part, date] = compiledArgs;
1339
- const partClean = part.replace(/['"]/g, "");
1340
- return `EXTRACT(${partClean} FROM ${date})`;
1341
- });
1342
- this.add("YEAR", ({ compiledArgs }) => {
1343
- if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1344
- return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
1345
- });
1346
- this.add("MONTH", ({ compiledArgs }) => {
1347
- if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1348
- return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
1349
- });
1350
- this.add("DAY", ({ compiledArgs }) => {
1351
- if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1352
- return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
1353
- });
1354
- this.add("DATE_ADD", ({ node, compiledArgs }) => {
1355
- if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1356
- const [date, interval] = compiledArgs;
1357
- const unitArg = node.args[2];
1358
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1359
- return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
1360
- });
1361
- this.add("DATE_SUB", ({ node, compiledArgs }) => {
1362
- if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1363
- const [date, interval] = compiledArgs;
1364
- const unitArg = node.args[2];
1365
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1366
- return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
1367
- });
1368
- this.add("DATE_DIFF", ({ compiledArgs }) => {
1369
- if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1370
- const [date1, date2] = compiledArgs;
1371
- return `(${date1}::DATE - ${date2}::DATE)`;
1372
- });
1373
- this.add("DATE_FORMAT", ({ compiledArgs }) => {
1374
- if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1375
- const [date, format] = compiledArgs;
1376
- return `TO_CHAR(${date}, ${format})`;
1377
- });
1378
- this.add("END_OF_MONTH", ({ compiledArgs }) => {
1379
- if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1380
- return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
1381
- });
1382
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1383
- if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1384
- return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
1385
- });
1386
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1387
- if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1388
- return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
1389
- });
1390
- this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1391
- if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1392
- const [, date] = compiledArgs;
1393
- const partArg = node.args[0];
1394
- const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1395
- return `DATE_TRUNC('${partClean}', ${date})`;
1396
- });
1397
- this.add("GROUP_CONCAT", (ctx) => {
1398
- const arg = ctx.compiledArgs[0];
1399
- const orderClause = this.buildOrderByExpression(ctx);
1400
- const orderSegment = orderClause ? ` ${orderClause}` : "";
1401
- const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1402
- const separator = ctx.compileOperand(separatorOperand);
1403
- return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1404
- });
1405
- }
1406
- };
1407
-
1408
- // src/core/dialect/postgres/index.ts
1409
- var PostgresDialect = class extends SqlDialectBase {
1410
- /**
1411
- * Creates a new PostgresDialect instance
1412
- */
1413
- constructor() {
1414
- super(new PostgresFunctionStrategy());
1415
- this.dialect = "postgres";
1416
- }
1417
- /**
1418
- * Quotes an identifier using PostgreSQL double-quote syntax
1419
- * @param id - Identifier to quote
1420
- * @returns Quoted identifier
1421
- */
1422
- quoteIdentifier(id) {
1423
- return `"${id}"`;
1424
- }
1425
- /**
1426
- * Compiles JSON path expression using PostgreSQL syntax
1427
- * @param node - JSON path node
1428
- * @returns PostgreSQL JSON path expression
1429
- */
1430
- compileJsonPath(node) {
1431
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1432
- return `${col}->>'${node.path}'`;
1433
- }
1434
- compileReturning(returning, ctx) {
1435
- if (!returning || returning.length === 0) return "";
1436
- const columns = this.formatReturningColumns(returning);
1437
- return ` RETURNING ${columns}`;
1438
- }
1439
- supportsReturning() {
1440
- return true;
1441
- }
1442
- };
1443
-
1444
- // src/core/dialect/mysql/functions.ts
1445
- var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
1446
- constructor() {
1447
- super();
1448
- this.registerOverrides();
1449
- }
1450
- registerOverrides() {
1451
- this.add("NOW", () => `NOW()`);
1452
- this.add("CURRENT_DATE", () => `CURDATE()`);
1453
- this.add("CURRENT_TIME", () => `CURTIME()`);
1454
- this.add("UTC_NOW", () => `UTC_TIMESTAMP()`);
1455
- this.add("EXTRACT", ({ compiledArgs }) => {
1456
- if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1457
- const [part, date] = compiledArgs;
1458
- const partClean = part.replace(/['"]/g, "");
1459
- return `EXTRACT(${partClean} FROM ${date})`;
1460
- });
1461
- this.add("YEAR", ({ compiledArgs }) => {
1462
- if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1463
- return `YEAR(${compiledArgs[0]})`;
1464
- });
1465
- this.add("MONTH", ({ compiledArgs }) => {
1466
- if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1467
- return `MONTH(${compiledArgs[0]})`;
1468
- });
1469
- this.add("DAY", ({ compiledArgs }) => {
1470
- if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1471
- return `DAY(${compiledArgs[0]})`;
1472
- });
1473
- this.add("DATE_ADD", ({ node, compiledArgs }) => {
1474
- if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1475
- const [date, interval] = compiledArgs;
1476
- const unitArg = node.args[2];
1477
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1478
- return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
1479
- });
1480
- this.add("DATE_SUB", ({ node, compiledArgs }) => {
1481
- if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1482
- const [date, interval] = compiledArgs;
1483
- const unitArg = node.args[2];
1484
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1485
- return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
1486
- });
1487
- this.add("DATE_DIFF", ({ compiledArgs }) => {
1488
- if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1489
- const [date1, date2] = compiledArgs;
1490
- return `DATEDIFF(${date1}, ${date2})`;
1491
- });
1492
- this.add("DATE_FORMAT", ({ compiledArgs }) => {
1493
- if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1494
- const [date, format] = compiledArgs;
1495
- return `DATE_FORMAT(${date}, ${format})`;
1496
- });
1497
- this.add("END_OF_MONTH", ({ compiledArgs }) => {
1498
- if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1499
- return `LAST_DAY(${compiledArgs[0]})`;
1500
- });
1501
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1502
- if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1503
- return `DAYOFWEEK(${compiledArgs[0]})`;
1504
- });
1505
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1506
- if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1507
- return `WEEKOFYEAR(${compiledArgs[0]})`;
1508
- });
1509
- this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1510
- if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1511
- const [, date] = compiledArgs;
1512
- const partArg = node.args[0];
1513
- const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1514
- if (partClean === "year") {
1515
- return `DATE_FORMAT(${date}, '%Y-01-01')`;
1516
- } else if (partClean === "month") {
1517
- return `DATE_FORMAT(${date}, '%Y-%m-01')`;
1518
- } else if (partClean === "day") {
1519
- return `DATE(${date})`;
1520
- }
1521
- return `DATE(${date})`;
1522
- });
1523
- }
1524
- };
1525
-
1526
- // src/core/dialect/mysql/index.ts
1527
- var MySqlDialect = class extends SqlDialectBase {
1528
- /**
1529
- * Creates a new MySqlDialect instance
1530
- */
1531
- constructor() {
1532
- super(new MysqlFunctionStrategy());
1533
- this.dialect = "mysql";
1534
- }
1535
- /**
1536
- * Quotes an identifier using MySQL backtick syntax
1537
- * @param id - Identifier to quote
1538
- * @returns Quoted identifier
1539
- */
1540
- quoteIdentifier(id) {
1541
- return `\`${id}\``;
1542
- }
1543
- /**
1544
- * Compiles JSON path expression using MySQL syntax
1545
- * @param node - JSON path node
1546
- * @returns MySQL JSON path expression
1547
- */
1548
- compileJsonPath(node) {
1549
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1550
- return `${col}->'${node.path}'`;
1551
- }
1552
- };
1553
-
1554
- // src/core/dialect/sqlite/functions.ts
1555
- var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1556
- constructor() {
1557
- super();
1558
- this.registerOverrides();
1559
- }
1560
- registerOverrides() {
1561
- this.add("NOW", () => `datetime('now', 'localtime')`);
1562
- this.add("CURRENT_DATE", () => `date('now', 'localtime')`);
1563
- this.add("CURRENT_TIME", () => `time('now', 'localtime')`);
1564
- this.add("UTC_NOW", () => `datetime('now')`);
1565
- this.add("EXTRACT", ({ compiledArgs }) => {
1566
- if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1567
- const [part, date] = compiledArgs;
1568
- const partUpper = part.replace(/['"]/g, "").toUpperCase();
1569
- const formatMap = {
1570
- "YEAR": "%Y",
1571
- "MONTH": "%m",
1572
- "DAY": "%d",
1573
- "HOUR": "%H",
1574
- "MINUTE": "%M",
1575
- "SECOND": "%S",
1576
- "DOW": "%w",
1577
- "WEEK": "%W"
1578
- };
1579
- const format = formatMap[partUpper] || "%Y";
1580
- return `CAST(strftime('${format}', ${date}) AS INTEGER)`;
1581
- });
1582
- this.add("YEAR", ({ compiledArgs }) => {
1583
- if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1584
- return `CAST(strftime('%Y', ${compiledArgs[0]}) AS INTEGER)`;
1585
- });
1586
- this.add("MONTH", ({ compiledArgs }) => {
1587
- if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1588
- return `CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER)`;
1589
- });
1590
- this.add("DAY", ({ compiledArgs }) => {
1591
- if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1592
- return `CAST(strftime('%d', ${compiledArgs[0]}) AS INTEGER)`;
1593
- });
1594
- this.add("DATE_ADD", ({ node, compiledArgs }) => {
1595
- if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1596
- const [date, interval] = compiledArgs;
1597
- const unitArg = node.args[2];
1598
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1599
- return `datetime(${date}, '+' || ${interval} || ' ${unitClean}')`;
1600
- });
1601
- this.add("DATE_SUB", ({ node, compiledArgs }) => {
1602
- if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1603
- const [date, interval] = compiledArgs;
1604
- const unitArg = node.args[2];
1605
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1606
- return `datetime(${date}, '-' || ${interval} || ' ${unitClean}')`;
1607
- });
1608
- this.add("DATE_DIFF", ({ compiledArgs }) => {
1609
- if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1610
- const [date1, date2] = compiledArgs;
1611
- return `CAST(julianday(${date1}) - julianday(${date2}) AS INTEGER)`;
1612
- });
1613
- this.add("DATE_FORMAT", ({ compiledArgs }) => {
1614
- if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1615
- const [date, format] = compiledArgs;
1616
- return `strftime(${format}, ${date})`;
1617
- });
1618
- this.add("UNIX_TIMESTAMP", () => `CAST(strftime('%s', 'now') AS INTEGER)`);
1619
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1620
- if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1621
- return `datetime(${compiledArgs[0]}, 'unixepoch')`;
1622
- });
1623
- this.add("END_OF_MONTH", ({ compiledArgs }) => {
1624
- if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1625
- return `date(${compiledArgs[0]}, 'start of month', '+1 month', '-1 day')`;
1626
- });
1627
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1628
- if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1629
- return `CAST(strftime('%w', ${compiledArgs[0]}) AS INTEGER)`;
1630
- });
1631
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1632
- if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1633
- return `CAST(strftime('%W', ${compiledArgs[0]}) AS INTEGER)`;
1634
- });
1635
- this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1636
- if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1637
- const [, date] = compiledArgs;
1638
- const partArg = node.args[0];
1639
- const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1640
- if (partClean === "year") {
1641
- return `date(${date}, 'start of year')`;
1642
- } else if (partClean === "month") {
1643
- return `date(${date}, 'start of month')`;
1644
- } else if (partClean === "day") {
1645
- return `date(${date})`;
1646
- }
1647
- return `date(${date}, 'start of ${partClean}')`;
1648
- });
1649
- this.add("GROUP_CONCAT", (ctx) => {
1650
- const arg = ctx.compiledArgs[0];
1651
- const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1652
- const separator = ctx.compileOperand(separatorOperand);
1653
- return `GROUP_CONCAT(${arg}, ${separator})`;
1654
- });
1655
- }
1656
- };
1657
-
1658
- // src/core/dialect/sqlite/index.ts
1659
- var SqliteDialect = class extends SqlDialectBase {
1660
- /**
1661
- * Creates a new SqliteDialect instance
1662
- */
1663
- constructor() {
1664
- super(new SqliteFunctionStrategy());
1665
- this.dialect = "sqlite";
1666
- }
1667
- /**
1668
- * Quotes an identifier using SQLite double-quote syntax
1669
- * @param id - Identifier to quote
1670
- * @returns Quoted identifier
1671
- */
1672
- quoteIdentifier(id) {
1673
- return `"${id}"`;
1674
- }
1675
- /**
1676
- * Compiles JSON path expression using SQLite syntax
1677
- * @param node - JSON path node
1678
- * @returns SQLite JSON path expression
1679
- */
1680
- compileJsonPath(node) {
1681
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1682
- return `json_extract(${col}, '${node.path}')`;
1683
- }
1684
- compileReturning(returning, ctx) {
1685
- if (!returning || returning.length === 0) return "";
1686
- const columns = this.formatReturningColumns(returning);
1687
- return ` RETURNING ${columns}`;
1688
- }
1689
- formatReturningColumns(returning) {
1690
- return returning.map((column) => {
1691
- const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
1692
- return `${this.quoteIdentifier(column.name)}${alias}`;
1693
- }).join(", ");
1694
- }
1695
- supportsReturning() {
1696
- return true;
1697
- }
1698
- };
1699
-
1700
- // src/core/dialect/mssql/functions.ts
1701
- var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1702
- constructor() {
1703
- super();
1704
- this.registerOverrides();
1705
- }
1706
- registerOverrides() {
1707
- this.add("NOW", () => `GETDATE()`);
1708
- this.add("CURRENT_DATE", () => `CAST(GETDATE() AS DATE)`);
1709
- this.add("CURRENT_TIME", () => `CAST(GETDATE() AS TIME)`);
1710
- this.add("UTC_NOW", () => `GETUTCDATE()`);
1711
- this.add("EXTRACT", ({ compiledArgs }) => {
1712
- if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1713
- const [part, date] = compiledArgs;
1714
- const partClean = part.replace(/['"]/g, "").toLowerCase();
1715
- return `DATEPART(${partClean}, ${date})`;
1716
- });
1717
- this.add("YEAR", ({ compiledArgs }) => {
1718
- if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1719
- return `YEAR(${compiledArgs[0]})`;
1720
- });
1721
- this.add("MONTH", ({ compiledArgs }) => {
1722
- if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1723
- return `MONTH(${compiledArgs[0]})`;
1724
- });
1725
- this.add("DAY", ({ compiledArgs }) => {
1726
- if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1727
- return `DAY(${compiledArgs[0]})`;
1728
- });
1729
- this.add("DATE_ADD", ({ node, compiledArgs }) => {
1730
- if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1731
- const [date, interval] = compiledArgs;
1732
- const unitArg = node.args[2];
1733
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1734
- return `DATEADD(${unitClean}, ${interval}, ${date})`;
1735
- });
1736
- this.add("DATE_SUB", ({ node, compiledArgs }) => {
1737
- if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1738
- const [date, interval] = compiledArgs;
1739
- const unitArg = node.args[2];
1740
- const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1741
- return `DATEADD(${unitClean}, -${interval}, ${date})`;
1742
- });
1743
- this.add("DATE_DIFF", ({ compiledArgs }) => {
1744
- if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1745
- const [date1, date2] = compiledArgs;
1746
- return `DATEDIFF(day, ${date2}, ${date1})`;
1747
- });
1748
- this.add("DATE_FORMAT", ({ compiledArgs }) => {
1749
- if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1750
- const [date, format] = compiledArgs;
1751
- return `FORMAT(${date}, ${format})`;
1752
- });
1753
- this.add("UNIX_TIMESTAMP", () => `DATEDIFF(SECOND, '1970-01-01', GETUTCDATE())`);
1754
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1755
- if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1756
- return `DATEADD(SECOND, ${compiledArgs[0]}, '1970-01-01')`;
1757
- });
1758
- this.add("END_OF_MONTH", ({ compiledArgs }) => {
1759
- if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1760
- return `EOMONTH(${compiledArgs[0]})`;
1761
- });
1762
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1763
- if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1764
- return `DATEPART(dw, ${compiledArgs[0]})`;
1765
- });
1766
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1767
- if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1768
- return `DATEPART(wk, ${compiledArgs[0]})`;
1769
- });
1770
- this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1771
- if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1772
- const [, date] = compiledArgs;
1773
- const partArg = node.args[0];
1774
- const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1775
- return `DATETRUNC(${partClean}, ${date})`;
1776
- });
1777
- this.add("GROUP_CONCAT", (ctx) => {
1778
- const arg = ctx.compiledArgs[0];
1779
- const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1780
- const separator = ctx.compileOperand(separatorOperand);
1781
- const orderClause = this.buildOrderByExpression(ctx);
1782
- const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1783
- return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1784
- });
1785
- }
1786
- };
1787
-
1788
- // src/core/dialect/mssql/index.ts
1789
- var SqlServerDialect = class extends Dialect {
1790
- /**
1791
- * Creates a new SqlServerDialect instance
1792
- */
1793
- constructor() {
1794
- super(new MssqlFunctionStrategy());
1795
- this.dialect = "mssql";
1796
- }
1797
- /**
1798
- * Quotes an identifier using SQL Server bracket syntax
1799
- * @param id - Identifier to quote
1800
- * @returns Quoted identifier
1801
- */
1802
- quoteIdentifier(id) {
1803
- return `[${id}]`;
1804
- }
1805
- /**
1806
- * Compiles JSON path expression using SQL Server syntax
1807
- * @param node - JSON path node
1808
- * @returns SQL Server JSON path expression
1809
- */
1810
- compileJsonPath(node) {
1811
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1812
- return `JSON_VALUE(${col}, '${node.path}')`;
1813
- }
1814
- /**
1815
- * Formats parameter placeholders using SQL Server named parameter syntax
1816
- * @param index - Parameter index
1817
- * @returns Named parameter placeholder
1818
- */
1819
- formatPlaceholder(index) {
1820
- return `@p${index}`;
1821
- }
1822
- /**
1823
- * Compiles SELECT query AST to SQL Server SQL
1824
- * @param ast - Query AST
1825
- * @param ctx - Compiler context
1826
- * @returns SQL Server SQL string
1827
- */
1828
- compileSelectAst(ast, ctx) {
1829
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
1830
- const ctes = this.compileCtes(ast, ctx);
1831
- const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
1832
- const baseSelect = this.compileSelectCore(baseAst, ctx);
1833
- if (!hasSetOps) {
1834
- return `${ctes}${baseSelect}`;
1835
- }
1836
- const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1837
- const orderBy = this.compileOrderBy(ast);
1838
- const pagination = this.compilePagination(ast, orderBy);
1839
- const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1840
- const tail = pagination || orderBy;
1841
- return `${ctes}${combined}${tail}`;
1842
- }
1843
- compileInsertAst(ast, ctx) {
1844
- const table = this.quoteIdentifier(ast.into.name);
1845
- const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1846
- const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1847
- return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
1848
- }
1849
- compileUpdateAst(ast, ctx) {
1850
- const table = this.quoteIdentifier(ast.table.name);
1851
- const assignments = ast.set.map((assignment) => {
1852
- const col = assignment.column;
1853
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1854
- const value = this.compileOperand(assignment.value, ctx);
1855
- return `${target} = ${value}`;
1856
- }).join(", ");
1857
- const whereClause = this.compileWhere(ast.where, ctx);
1858
- return `UPDATE ${table} SET ${assignments}${whereClause};`;
1859
- }
1860
- compileDeleteAst(ast, ctx) {
1861
- if (ast.from.type !== "Table") {
1862
- throw new Error("DELETE only supports base tables in the MSSQL dialect.");
1863
- }
1864
- const table = this.quoteIdentifier(ast.from.name);
1865
- const whereClause = this.compileWhere(ast.where, ctx);
1866
- return `DELETE FROM ${table}${whereClause};`;
1867
- }
1868
- compileSelectCore(ast, ctx) {
1869
- const columns = ast.columns.map((c) => {
1870
- let expr = "";
1871
- if (c.type === "Function") {
1872
- expr = this.compileOperand(c, ctx);
1873
- } else if (c.type === "Column") {
1874
- expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
1875
- } else if (c.type === "ScalarSubquery") {
1876
- expr = this.compileOperand(c, ctx);
1877
- } else if (c.type === "WindowFunction") {
1878
- expr = this.compileOperand(c, ctx);
1879
- }
1880
- if (c.alias) {
1881
- if (c.alias.includes("(")) return c.alias;
1882
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1883
- }
1884
- return expr;
1885
- }).join(", ");
1886
- const distinct = ast.distinct ? "DISTINCT " : "";
1887
- const from = this.compileTableSource(ast.from, ctx);
1888
- const joins = ast.joins.map((j) => {
1889
- const table = this.compileTableSource(j.table, ctx);
1890
- const cond = this.compileExpression(j.condition, ctx);
1891
- return `${j.kind} JOIN ${table} ON ${cond}`;
1892
- }).join(" ");
1893
- const whereClause = this.compileWhere(ast.where, ctx);
1894
- const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
1895
- const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
1896
- const orderBy = this.compileOrderBy(ast);
1897
- const pagination = this.compilePagination(ast, orderBy);
1898
- if (pagination) {
1899
- return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
1900
- }
1901
- return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
1902
- }
1903
- compileOrderBy(ast) {
1904
- if (!ast.orderBy || ast.orderBy.length === 0) return "";
1905
- return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1906
- }
1907
- compilePagination(ast, orderBy) {
1908
- const hasLimit = ast.limit !== void 0;
1909
- const hasOffset = ast.offset !== void 0;
1910
- if (!hasLimit && !hasOffset) return "";
1911
- const off = ast.offset ?? 0;
1912
- const orderClause = orderBy || " ORDER BY (SELECT NULL)";
1913
- let pagination = `${orderClause} OFFSET ${off} ROWS`;
1914
- if (hasLimit) {
1915
- pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
1916
- }
1917
- return pagination;
1918
- }
1919
- compileTableSource(table, ctx) {
1920
- if (table.type === "FunctionTable") {
1921
- return FunctionTableFormatter.format(table, ctx, this);
1922
- }
1923
- if (table.type === "DerivedTable") {
1924
- return this.compileDerivedTable(table, ctx);
1925
- }
1926
- const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
1927
- return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1928
- }
1929
- compileDerivedTable(table, ctx) {
1930
- const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1931
- const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1932
- return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
1933
- }
1934
- compileCtes(ast, ctx) {
1935
- if (!ast.ctes || ast.ctes.length === 0) return "";
1936
- const defs = ast.ctes.map((cte) => {
1937
- const name = this.quoteIdentifier(cte.name);
1938
- const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1939
- const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
1940
- return `${name}${cols} AS (${query})`;
1941
- }).join(", ");
1942
- return `WITH ${defs} `;
1943
- }
1944
- wrapSetOperand(sql) {
1945
- const trimmed = sql.trim().replace(/;$/, "");
1946
- return `(${trimmed})`;
1947
- }
1948
- };
1949
-
1950
- // src/core/dialect/dialect-factory.ts
1951
- var DialectFactory = class {
1952
- static {
1953
- this.registry = /* @__PURE__ */ new Map();
1954
- }
1955
- static {
1956
- this.defaultsInitialized = false;
1957
- }
1958
- static ensureDefaults() {
1959
- if (this.defaultsInitialized) return;
1960
- this.defaultsInitialized = true;
1961
- if (!this.registry.has("postgres")) {
1962
- this.registry.set("postgres", () => new PostgresDialect());
1963
- }
1964
- if (!this.registry.has("mysql")) {
1965
- this.registry.set("mysql", () => new MySqlDialect());
1966
- }
1967
- if (!this.registry.has("sqlite")) {
1968
- this.registry.set("sqlite", () => new SqliteDialect());
1969
- }
1970
- if (!this.registry.has("mssql")) {
1971
- this.registry.set("mssql", () => new SqlServerDialect());
1972
- }
1973
- }
1974
- /**
1975
- * Register (or override) a dialect factory for a key.
1976
- *
1977
- * Examples:
1978
- * DialectFactory.register('sqlite', () => new SqliteDialect());
1979
- * DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
1980
- */
1981
- static register(key, factory) {
1982
- this.registry.set(key, factory);
1983
- }
1984
- /**
1985
- * Resolve a key into a Dialect instance.
1986
- * Throws if the key is not registered.
1987
- */
1988
- static create(key) {
1989
- this.ensureDefaults();
1990
- const factory = this.registry.get(key);
1991
- if (!factory) {
1992
- throw new Error(
1993
- `Dialect "${String(
1994
- key
1995
- )}" is not registered. Use DialectFactory.register(...) to register it.`
1996
- );
1997
- }
1998
- return factory();
1999
- }
2000
- /**
2001
- * Clear all registrations (mainly for tests).
2002
- * Built-ins will be re-registered lazily on the next create().
2003
- */
2004
- static clear() {
2005
- this.registry.clear();
2006
- this.defaultsInitialized = false;
2007
- }
2008
- };
2009
- var resolveDialectInput = (dialect) => {
2010
- if (typeof dialect === "string") {
2011
- return DialectFactory.create(dialect);
2012
- }
2013
- return dialect;
2014
- };
2015
-
2016
- // src/query-builder/select-query-state.ts
2017
- var SelectQueryState = class _SelectQueryState {
2018
- /**
2019
- * Creates a new SelectQueryState instance
2020
- * @param table - Table definition
2021
- * @param ast - Optional existing AST
2022
- */
2023
- constructor(table, ast) {
2024
- this.table = table;
2025
- this.ast = ast ?? {
2026
- type: "SelectQuery",
2027
- from: { type: "Table", name: table.name },
2028
- columns: [],
2029
- joins: []
2030
- };
2031
- }
2032
- /**
2033
- * Creates a new SelectQueryState with updated AST
2034
- * @param nextAst - Updated AST
2035
- * @returns New SelectQueryState instance
2036
- */
2037
- clone(nextAst) {
2038
- return new _SelectQueryState(this.table, nextAst);
2039
- }
2040
- /**
2041
- * Adds columns to the query
2042
- * @param newCols - Columns to add
2043
- * @returns New SelectQueryState with added columns
2044
- */
2045
- withColumns(newCols) {
2046
- return this.clone({
2047
- ...this.ast,
2048
- columns: [...this.ast.columns ?? [], ...newCols]
2049
- });
2050
- }
2051
- /**
2052
- * Adds a join to the query
2053
- * @param join - Join node to add
2054
- * @returns New SelectQueryState with added join
2055
- */
2056
- withJoin(join) {
2057
- return this.clone({
2058
- ...this.ast,
2059
- joins: [...this.ast.joins ?? [], join]
2060
- });
2061
- }
2062
- /**
2063
- * Replaces the FROM clause.
2064
- * @param from - Table source for the FROM clause
2065
- * @returns New SelectQueryState with updated FROM
2066
- */
2067
- withFrom(from) {
2068
- return this.clone({
2069
- ...this.ast,
2070
- from
2071
- });
2072
- }
2073
- /**
2074
- * Adds a WHERE clause to the query
2075
- * @param predicate - WHERE predicate expression
2076
- * @returns New SelectQueryState with WHERE clause
2077
- */
2078
- withWhere(predicate) {
2079
- return this.clone({
2080
- ...this.ast,
2081
- where: predicate
2082
- });
2083
- }
2084
- /**
2085
- * Adds a HAVING clause to the query
2086
- * @param predicate - HAVING predicate expression
2087
- * @returns New SelectQueryState with HAVING clause
2088
- */
2089
- withHaving(predicate) {
2090
- return this.clone({
2091
- ...this.ast,
2092
- having: predicate
2093
- });
2094
- }
2095
- /**
2096
- * Adds GROUP BY columns to the query
2097
- * @param columns - Columns to group by
2098
- * @returns New SelectQueryState with GROUP BY clause
2099
- */
2100
- withGroupBy(columns) {
2101
- return this.clone({
2102
- ...this.ast,
2103
- groupBy: [...this.ast.groupBy ?? [], ...columns]
2104
- });
2105
- }
2106
- /**
2107
- * Adds ORDER BY clauses to the query
2108
- * @param orderBy - ORDER BY nodes
2109
- * @returns New SelectQueryState with ORDER BY clause
2110
- */
2111
- withOrderBy(orderBy) {
2112
- return this.clone({
2113
- ...this.ast,
2114
- orderBy: [...this.ast.orderBy ?? [], ...orderBy]
2115
- });
2116
- }
2117
- /**
2118
- * Adds DISTINCT columns to the query
2119
- * @param columns - Columns to make distinct
2120
- * @returns New SelectQueryState with DISTINCT clause
2121
- */
2122
- withDistinct(columns) {
2123
- return this.clone({
2124
- ...this.ast,
2125
- distinct: [...this.ast.distinct ?? [], ...columns]
2126
- });
2127
- }
2128
- /**
2129
- * Adds a LIMIT clause to the query
2130
- * @param limit - Maximum number of rows to return
2131
- * @returns New SelectQueryState with LIMIT clause
2132
- */
2133
- withLimit(limit) {
2134
- return this.clone({
2135
- ...this.ast,
2136
- limit
2137
- });
2138
- }
2139
- /**
2140
- * Adds an OFFSET clause to the query
2141
- * @param offset - Number of rows to skip
2142
- * @returns New SelectQueryState with OFFSET clause
2143
- */
2144
- withOffset(offset) {
2145
- return this.clone({
2146
- ...this.ast,
2147
- offset
2148
- });
2149
- }
2150
- /**
2151
- * Adds a Common Table Expression (CTE) to the query
2152
- * @param cte - CTE node to add
2153
- * @returns New SelectQueryState with CTE
2154
- */
2155
- withCte(cte) {
2156
- return this.clone({
2157
- ...this.ast,
2158
- ctes: [...this.ast.ctes ?? [], cte]
2159
- });
2160
- }
2161
- /**
2162
- * Adds a set operation (UNION/INTERSECT/EXCEPT) to the query
2163
- * @param op - Set operation node to add
2164
- * @returns New SelectQueryState with set operation
2165
- */
2166
- withSetOperation(op) {
2167
- return this.clone({
2168
- ...this.ast,
2169
- setOps: [...this.ast.setOps ?? [], op]
2170
- });
2171
- }
2172
- };
2173
-
2174
- // src/core/ast/join-node.ts
2175
- var createJoinNode = (kind, tableName, condition, relationName) => ({
2176
- type: "Join",
2177
- kind,
2178
- table: typeof tableName === "string" ? { type: "Table", name: tableName } : tableName,
2179
- condition,
2180
- meta: relationName ? { relationName } : void 0
2181
- });
2182
-
2183
- // src/query-builder/hydration-manager.ts
2184
- var HydrationManager = class _HydrationManager {
2185
- /**
2186
- * Creates a new HydrationManager instance
2187
- * @param table - Table definition
2188
- * @param planner - Hydration planner
2189
- */
2190
- constructor(table, planner) {
2191
- this.table = table;
2192
- this.planner = planner;
2193
- }
2194
- /**
2195
- * Creates a new HydrationManager with updated planner
2196
- * @param nextPlanner - Updated hydration planner
2197
- * @returns New HydrationManager instance
2198
- */
2199
- clone(nextPlanner) {
2200
- return new _HydrationManager(this.table, nextPlanner);
2201
- }
2202
- /**
2203
- * Handles column selection for hydration planning
2204
- * @param state - Current query state
2205
- * @param newColumns - Newly selected columns
2206
- * @returns Updated HydrationManager with captured columns
2207
- */
2208
- onColumnsSelected(state, newColumns) {
2209
- const updated = this.planner.captureRootColumns(newColumns);
2210
- return this.clone(updated);
2211
- }
2212
- /**
2213
- * Handles relation inclusion for hydration planning
2214
- * @param state - Current query state
2215
- * @param relation - Relation definition
2216
- * @param relationName - Name of the relation
2217
- * @param aliasPrefix - Alias prefix for the relation
2218
- * @param targetColumns - Target columns to include
2219
- * @returns Updated HydrationManager with included relation
2220
- */
2221
- onRelationIncluded(state, relation, relationName, aliasPrefix, targetColumns, pivot) {
2222
- const withRoots = this.planner.captureRootColumns(state.ast.columns);
2223
- const next = withRoots.includeRelation(relation, relationName, aliasPrefix, targetColumns, pivot);
2224
- return this.clone(next);
2225
- }
2226
- /**
2227
- * Applies hydration plan to the AST
2228
- * @param ast - Query AST to modify
2229
- * @returns AST with hydration metadata
2230
- */
2231
- applyToAst(ast) {
2232
- if (ast.setOps && ast.setOps.length > 0) {
2233
- return ast;
2234
- }
2235
- const plan = this.planner.getPlan();
2236
- if (!plan) return ast;
2237
- const needsPaginationGuard = this.requiresParentPagination(ast, plan);
2238
- const rewritten = needsPaginationGuard ? this.wrapForParentPagination(ast, plan) : ast;
2239
- return this.attachHydrationMeta(rewritten, plan);
2240
- }
2241
- /**
2242
- * Gets the current hydration plan
2243
- * @returns Hydration plan or undefined if none exists
2244
- */
2245
- getPlan() {
2246
- return this.planner.getPlan();
2247
- }
2248
- /**
2249
- * Attaches hydration metadata to a query AST node.
2250
- */
2251
- attachHydrationMeta(ast, plan) {
2252
- return {
2253
- ...ast,
2254
- meta: {
2255
- ...ast.meta || {},
2256
- hydration: plan
2257
- }
2258
- };
2259
- }
2260
- /**
2261
- * Determines whether the query needs pagination rewriting to keep LIMIT/OFFSET
2262
- * applied to parent rows when eager-loading multiplicative relations.
2263
- */
2264
- requiresParentPagination(ast, plan) {
2265
- const hasPagination = ast.limit !== void 0 || ast.offset !== void 0;
2266
- return hasPagination && this.hasMultiplyingRelations(plan);
2267
- }
2268
- hasMultiplyingRelations(plan) {
2269
- return plan.relations.some(
2270
- (rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
2271
- );
2272
- }
2273
- /**
2274
- * Rewrites the query using CTEs so LIMIT/OFFSET target distinct parent rows
2275
- * instead of the joined result set.
2276
- *
2277
- * The strategy:
2278
- * - Hoist the original query (minus limit/offset) into a base CTE.
2279
- * - Select distinct parent ids from that base CTE with the original ordering and pagination.
2280
- * - Join the base CTE against the paged ids to retrieve the joined rows for just that page.
2281
- */
2282
- wrapForParentPagination(ast, plan) {
2283
- const projectionNames = this.getProjectionNames(ast.columns);
2284
- if (!projectionNames) {
2285
- return ast;
2286
- }
2287
- const projectionAliases = this.buildProjectionAliasMap(ast.columns);
2288
- const projectionSet = new Set(projectionNames);
2289
- const rootPkAlias = projectionAliases.get(`${plan.rootTable}.${plan.rootPrimaryKey}`) ?? plan.rootPrimaryKey;
2290
- const baseCteName = this.nextCteName(ast.ctes, "__metal_pagination_base");
2291
- const baseQuery = {
2292
- ...ast,
2293
- ctes: void 0,
2294
- limit: void 0,
2295
- offset: void 0,
2296
- orderBy: void 0,
2297
- meta: void 0
2298
- };
2299
- const baseCte = {
2300
- type: "CommonTableExpression",
2301
- name: baseCteName,
2302
- query: baseQuery,
2303
- recursive: false
2304
- };
2305
- const orderBy = this.mapOrderBy(ast.orderBy, plan, projectionAliases, baseCteName, projectionSet);
2306
- if (orderBy === null) {
2307
- return ast;
2308
- }
2309
- const pageCteName = this.nextCteName([...ast.ctes ?? [], baseCte], "__metal_pagination_page");
2310
- const pagingColumns = this.buildPagingColumns(rootPkAlias, orderBy, baseCteName);
2311
- const pageCte = {
2312
- type: "CommonTableExpression",
2313
- name: pageCteName,
2314
- query: {
2315
- type: "SelectQuery",
2316
- from: { type: "Table", name: baseCteName },
2317
- columns: pagingColumns,
2318
- joins: [],
2319
- distinct: [{ type: "Column", table: baseCteName, name: rootPkAlias }],
2320
- orderBy,
2321
- limit: ast.limit,
2322
- offset: ast.offset
2323
- },
2324
- recursive: false
2325
- };
2326
- const joinCondition = eq(
2327
- { type: "Column", table: baseCteName, name: rootPkAlias },
2328
- { type: "Column", table: pageCteName, name: rootPkAlias }
2329
- );
2330
- const outerColumns = projectionNames.map((name) => ({
2331
- type: "Column",
2332
- table: baseCteName,
2333
- name,
2334
- alias: name
2335
- }));
2336
- return {
2337
- type: "SelectQuery",
2338
- from: { type: "Table", name: baseCteName },
2339
- columns: outerColumns,
2340
- joins: [createJoinNode(JOIN_KINDS.INNER, pageCteName, joinCondition)],
2341
- orderBy,
2342
- ctes: [...ast.ctes ?? [], baseCte, pageCte]
2343
- };
2344
- }
2345
- nextCteName(existing, baseName) {
2346
- const names = new Set((existing ?? []).map((cte) => cte.name));
2347
- let candidate = baseName;
2348
- let suffix = 1;
2349
- while (names.has(candidate)) {
2350
- suffix += 1;
2351
- candidate = `${baseName}_${suffix}`;
2352
- }
2353
- return candidate;
2354
- }
2355
- getProjectionNames(columns) {
2356
- const names = [];
2357
- for (const col of columns) {
2358
- const alias = col.alias ?? col.name;
2359
- if (!alias) return void 0;
2360
- names.push(alias);
2361
- }
2362
- return names;
2363
- }
2364
- buildProjectionAliasMap(columns) {
2365
- const map = /* @__PURE__ */ new Map();
2366
- for (const col of columns) {
2367
- if (col.type !== "Column") continue;
2368
- const node = col;
2369
- const key = `${node.table}.${node.name}`;
2370
- map.set(key, node.alias ?? node.name);
2371
- }
2372
- return map;
2373
- }
2374
- mapOrderBy(orderBy, plan, projectionAliases, baseAlias, availableColumns) {
2375
- if (!orderBy || orderBy.length === 0) {
2376
- return void 0;
2377
- }
2378
- const mapped = [];
2379
- for (const ob of orderBy) {
2380
- if (ob.column.table !== plan.rootTable) {
2381
- return null;
2382
- }
2383
- const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
2384
- if (!availableColumns.has(alias)) {
2385
- return null;
2386
- }
2387
- mapped.push({
2388
- type: "OrderBy",
2389
- column: { type: "Column", table: baseAlias, name: alias },
2390
- direction: ob.direction
2391
- });
2392
- }
2393
- return mapped;
2394
- }
2395
- buildPagingColumns(primaryKey, orderBy, tableAlias) {
2396
- const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
2397
- if (!orderBy) return columns;
2398
- for (const ob of orderBy) {
2399
- if (!columns.some((col) => col.name === ob.column.name)) {
2400
- columns.push({
2401
- type: "Column",
2402
- table: tableAlias,
2403
- name: ob.column.name,
2404
- alias: ob.column.name
2405
- });
2406
- }
2407
- }
2408
- return columns;
2409
- }
2410
- };
2411
-
2412
- // src/query-builder/relation-alias.ts
2413
- var RELATION_SEPARATOR = "__";
2414
- var makeRelationAlias = (relationName, columnName) => `${relationName}${RELATION_SEPARATOR}${columnName}`;
2415
- var isRelationAlias = (alias) => !!alias && alias.includes(RELATION_SEPARATOR);
2416
-
2417
- // src/query-builder/relation-utils.ts
2418
- var buildDefaultPivotColumns = (rel, pivotPk) => {
2419
- const excluded = /* @__PURE__ */ new Set([pivotPk, rel.pivotForeignKeyToRoot, rel.pivotForeignKeyToTarget]);
2420
- return Object.keys(rel.pivotTable.columns).filter((col) => !excluded.has(col));
2421
- };
2422
-
2423
- // src/query-builder/hydration-planner.ts
2424
- var findPrimaryKey = (table) => {
2425
- const pk = Object.values(table.columns).find((c) => c.primary);
2426
- return pk?.name || "id";
2427
- };
2428
- var HydrationPlanner = class _HydrationPlanner {
2429
- /**
2430
- * Creates a new HydrationPlanner instance
2431
- * @param table - Table definition
2432
- * @param plan - Optional existing hydration plan
2433
- */
2434
- constructor(table, plan) {
2435
- this.table = table;
2436
- this.plan = plan;
2437
- }
2438
- /**
2439
- * Captures root table columns for hydration planning
2440
- * @param columns - Columns to capture
2441
- * @returns Updated HydrationPlanner with captured columns
2442
- */
2443
- captureRootColumns(columns) {
2444
- const currentPlan = this.getPlanOrDefault();
2445
- const rootCols = new Set(currentPlan.rootColumns);
2446
- let changed = false;
2447
- columns.forEach((node) => {
2448
- if (node.type !== "Column") return;
2449
- if (node.table !== this.table.name) return;
2450
- const alias = node.alias || node.name;
2451
- if (isRelationAlias(alias)) return;
2452
- if (!rootCols.has(alias)) {
2453
- rootCols.add(alias);
2454
- changed = true;
2455
- }
2456
- });
2457
- if (!changed) return this;
2458
- return new _HydrationPlanner(this.table, {
2459
- ...currentPlan,
2460
- rootColumns: Array.from(rootCols)
2461
- });
2462
- }
2463
- /**
2464
- * Includes a relation in the hydration plan
2465
- * @param rel - Relation definition
2466
- * @param relationName - Name of the relation
2467
- * @param aliasPrefix - Alias prefix for relation columns
2468
- * @param columns - Columns to include from the relation
2469
- * @returns Updated HydrationPlanner with included relation
2470
- */
2471
- includeRelation(rel, relationName, aliasPrefix, columns, pivot) {
2472
- const currentPlan = this.getPlanOrDefault();
2473
- const relations = currentPlan.relations.filter((r) => r.name !== relationName);
2474
- relations.push(this.buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot));
2475
- return new _HydrationPlanner(this.table, {
2476
- ...currentPlan,
2477
- relations
2478
- });
2479
- }
2480
- /**
2481
- * Gets the current hydration plan
2482
- * @returns Current hydration plan or undefined
2483
- */
2484
- getPlan() {
2485
- return this.plan;
2486
- }
2487
- /**
2488
- * Gets the current hydration plan or creates a default one
2489
- * @returns Current hydration plan or default plan
2490
- */
2491
- getPlanOrDefault() {
2492
- return this.plan ?? buildDefaultHydrationPlan(this.table);
2493
- }
2494
- /**
2495
- * Builds a relation plan for hydration
2496
- * @param rel - Relation definition
2497
- * @param relationName - Name of the relation
2498
- * @param aliasPrefix - Alias prefix for relation columns
2499
- * @param columns - Columns to include from the relation
2500
- * @returns Hydration relation plan
2501
- */
2502
- buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
2503
- switch (rel.type) {
2504
- case RelationKinds.HasMany:
2505
- case RelationKinds.HasOne: {
2506
- const localKey = rel.localKey || findPrimaryKey(this.table);
2507
- return {
2508
- name: relationName,
2509
- aliasPrefix,
2510
- type: rel.type,
2511
- targetTable: rel.target.name,
2512
- targetPrimaryKey: findPrimaryKey(rel.target),
2513
- foreignKey: rel.foreignKey,
2514
- localKey,
2515
- columns
2516
- };
2517
- }
2518
- case RelationKinds.BelongsTo: {
2519
- const localKey = rel.localKey || findPrimaryKey(rel.target);
2520
- return {
2521
- name: relationName,
2522
- aliasPrefix,
2523
- type: rel.type,
2524
- targetTable: rel.target.name,
2525
- targetPrimaryKey: findPrimaryKey(rel.target),
2526
- foreignKey: rel.foreignKey,
2527
- localKey,
2528
- columns
2529
- };
2530
- }
2531
- case RelationKinds.BelongsToMany: {
2532
- const many = rel;
2533
- const localKey = many.localKey || findPrimaryKey(this.table);
2534
- const targetPk = many.targetKey || findPrimaryKey(many.target);
2535
- const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
2536
- const pivotAliasPrefix = pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
2537
- const pivotColumns = pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
2538
- return {
2539
- name: relationName,
2540
- aliasPrefix,
2541
- type: rel.type,
2542
- targetTable: many.target.name,
2543
- targetPrimaryKey: targetPk,
2544
- foreignKey: many.pivotForeignKeyToRoot,
2545
- localKey,
2546
- columns,
2547
- pivot: {
2548
- table: many.pivotTable.name,
2549
- primaryKey: pivotPk,
2550
- aliasPrefix: pivotAliasPrefix,
2551
- columns: pivotColumns
2552
- }
2553
- };
2554
- }
2555
- }
2556
- }
2557
- };
2558
- var buildDefaultHydrationPlan = (table) => ({
2559
- rootTable: table.name,
2560
- rootPrimaryKey: findPrimaryKey(table),
2561
- rootColumns: [],
2562
- relations: []
2563
- });
2564
-
2565
- // src/query-builder/raw-column-parser.ts
2566
- var parseRawColumn = (col, tableName, ctes) => {
2567
- if (col.includes("(")) {
2568
- const [fn, rest] = col.split("(");
2569
- const colName = rest.replace(")", "");
2570
- const [table, name] = colName.includes(".") ? colName.split(".") : [tableName, colName];
2571
- return { type: "Column", table, name, alias: col };
2572
- }
2573
- if (col.includes(".")) {
2574
- const [potentialCteName, columnName] = col.split(".");
2575
- const hasCte = ctes?.some((cte) => cte.name === potentialCteName);
2576
- if (hasCte) {
2577
- return { type: "Column", table: tableName, name: col };
2578
- }
2579
- return { type: "Column", table: potentialCteName, name: columnName };
2580
- }
2581
- return { type: "Column", table: tableName, name: col };
2582
- };
2583
-
2584
- // src/query-builder/query-ast-service.ts
2585
- var QueryAstService = class {
2586
- /**
2587
- * Creates a new QueryAstService instance
2588
- * @param table - Table definition
2589
- * @param state - Current query state
2590
- */
2591
- constructor(table, state) {
2592
- this.table = table;
2593
- this.state = state;
2594
- }
2595
- /**
2596
- * Selects columns for the query
2597
- * @param columns - Columns to select (key: alias, value: column definition or expression)
2598
- * @returns Column selection result with updated state and added columns
2599
- */
2600
- select(columns) {
2601
- const existingAliases = new Set(
2602
- this.state.ast.columns.map((c) => c.alias || c.name)
2603
- );
2604
- const from = this.state.ast.from;
2605
- const rootTableName = from.type === "Table" && from.alias ? from.alias : this.table.name;
2606
- const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
2607
- if (existingAliases.has(alias)) return acc;
2608
- if (isExpressionSelectionNode(val)) {
2609
- acc.push({ ...val, alias });
2610
- return acc;
2611
- }
2612
- const colDef = val;
2613
- const resolvedTable = colDef.table && colDef.table === this.table.name && from.type === "Table" && from.alias ? from.alias : colDef.table || rootTableName;
2614
- acc.push({
2615
- type: "Column",
2616
- table: resolvedTable,
2617
- name: colDef.name,
2618
- alias
2619
- });
2620
- return acc;
2621
- }, []);
2622
- const nextState = this.state.withColumns(newCols);
2623
- return { state: nextState, addedColumns: newCols };
2624
- }
2625
- /**
2626
- * Selects raw column expressions (best-effort parser for simple references/functions)
2627
- * @param cols - Raw column expressions
2628
- * @returns Column selection result with updated state and added columns
2629
- */
2630
- selectRaw(cols) {
2631
- const from = this.state.ast.from;
2632
- const defaultTable = from.type === "Table" && from.alias ? from.alias : this.table.name;
2633
- const newCols = cols.map((col) => parseRawColumn(col, defaultTable, this.state.ast.ctes));
2634
- const nextState = this.state.withColumns(newCols);
2635
- return { state: nextState, addedColumns: newCols };
2636
- }
2637
- /**
2638
- * Adds a Common Table Expression (CTE) to the query
2639
- * @param name - Name of the CTE
2640
- * @param query - Query for the CTE
2641
- * @param columns - Optional column names for the CTE
2642
- * @param recursive - Whether the CTE is recursive
2643
- * @returns Updated query state with CTE
2644
- */
2645
- withCte(name, query, columns, recursive = false) {
2646
- const cte = {
2647
- type: "CommonTableExpression",
2648
- name,
2649
- query,
2650
- columns,
2651
- recursive
2652
- };
2653
- return this.state.withCte(cte);
2654
- }
2655
- /**
2656
- * Adds a set operation (UNION/UNION ALL/INTERSECT/EXCEPT) to the query
2657
- * @param operator - Set operator
2658
- * @param query - Right-hand side query
2659
- * @returns Updated query state with set operation
2660
- */
2661
- withSetOperation(operator, query) {
2662
- const op = {
2663
- type: "SetOperation",
2664
- operator,
2665
- query
2666
- };
2667
- return this.state.withSetOperation(op);
2668
- }
2669
- /**
2670
- * Replaces the FROM clause for the current query.
2671
- * @param from - Table source to use in the FROM clause
2672
- * @returns Updated query state with new FROM
2673
- */
2674
- withFrom(from) {
2675
- return this.state.withFrom(from);
2676
- }
2677
- /**
2678
- * Selects a subquery as a column
2679
- * @param alias - Alias for the subquery
2680
- * @param query - Subquery to select
2681
- * @returns Updated query state with subquery selection
2682
- */
2683
- selectSubquery(alias, query) {
2684
- const node = { type: "ScalarSubquery", query, alias };
2685
- return this.state.withColumns([node]);
2686
- }
2687
- /**
2688
- * Adds a JOIN clause to the query
2689
- * @param join - Join node to add
2690
- * @returns Updated query state with JOIN
2691
- */
2692
- withJoin(join) {
2693
- return this.state.withJoin(join);
2694
- }
2695
- /**
2696
- * Adds a WHERE clause to the query
2697
- * @param expr - Expression for the WHERE clause
2698
- * @returns Updated query state with WHERE clause
2699
- */
2700
- withWhere(expr) {
2701
- const combined = this.combineExpressions(this.state.ast.where, expr);
2702
- return this.state.withWhere(combined);
2703
- }
2704
- /**
2705
- * Adds a GROUP BY clause to the query
2706
- * @param col - Column to group by
2707
- * @returns Updated query state with GROUP BY clause
2708
- */
2709
- withGroupBy(col) {
2710
- const from = this.state.ast.from;
2711
- const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2712
- const node = buildColumnNode(tableRef, col);
2713
- return this.state.withGroupBy([node]);
2714
- }
2715
- /**
2716
- * Adds a HAVING clause to the query
2717
- * @param expr - Expression for the HAVING clause
2718
- * @returns Updated query state with HAVING clause
2719
- */
2720
- withHaving(expr) {
2721
- const combined = this.combineExpressions(this.state.ast.having, expr);
2722
- return this.state.withHaving(combined);
2723
- }
2724
- /**
2725
- * Adds an ORDER BY clause to the query
2726
- * @param col - Column to order by
2727
- * @param direction - Order direction (ASC/DESC)
2728
- * @returns Updated query state with ORDER BY clause
2729
- */
2730
- withOrderBy(col, direction) {
2731
- const from = this.state.ast.from;
2732
- const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2733
- const node = buildColumnNode(tableRef, col);
2734
- return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
2735
- }
2736
- /**
2737
- * Adds a DISTINCT clause to the query
2738
- * @param cols - Columns to make distinct
2739
- * @returns Updated query state with DISTINCT clause
2740
- */
2741
- withDistinct(cols) {
2742
- return this.state.withDistinct(cols);
2743
- }
2744
- /**
2745
- * Adds a LIMIT clause to the query
2746
- * @param limit - Maximum number of rows to return
2747
- * @returns Updated query state with LIMIT clause
2748
- */
2749
- withLimit(limit) {
2750
- return this.state.withLimit(limit);
2751
- }
2752
- /**
2753
- * Adds an OFFSET clause to the query
2754
- * @param offset - Number of rows to skip
2755
- * @returns Updated query state with OFFSET clause
2756
- */
2757
- withOffset(offset) {
2758
- return this.state.withOffset(offset);
2759
- }
2760
- /**
2761
- * Combines expressions with AND operator
2762
- * @param existing - Existing expression
2763
- * @param next - New expression to combine
2764
- * @returns Combined expression
2765
- */
2766
- combineExpressions(existing, next) {
2767
- return existing ? and(existing, next) : next;
2768
- }
2769
- };
2770
-
2771
- // src/query-builder/relation-projection-helper.ts
2772
- var RelationProjectionHelper = class {
2773
- /**
2774
- * Creates a new RelationProjectionHelper instance
2775
- * @param table - Table definition
2776
- * @param selectColumns - Callback for selecting columns
2777
- */
2778
- constructor(table, selectColumns) {
2779
- this.table = table;
2780
- this.selectColumns = selectColumns;
2781
- }
2782
- /**
2783
- * Ensures base projection is included in the query
2784
- * @param state - Current query state
2785
- * @param hydration - Hydration manager
2786
- * @returns Relation result with updated state and hydration
2787
- */
2788
- ensureBaseProjection(state, hydration) {
2789
- const primaryKey = findPrimaryKey(this.table);
2790
- if (!this.hasBaseProjection(state)) {
2791
- return this.selectColumns(state, hydration, this.getBaseColumns());
2792
- }
2793
- if (primaryKey && !this.hasPrimarySelected(state, primaryKey) && this.table.columns[primaryKey]) {
2794
- return this.selectColumns(state, hydration, {
2795
- [primaryKey]: this.table.columns[primaryKey]
2796
- });
2797
- }
2798
- return { state, hydration };
2799
- }
2800
- /**
2801
- * Checks if base projection exists in the query
2802
- * @param state - Current query state
2803
- * @returns True if base projection exists
2804
- */
2805
- hasBaseProjection(state) {
2806
- return state.ast.columns.some((col) => !isRelationAlias(col.alias));
2807
- }
2808
- /**
2809
- * Checks if primary key is selected in the query
2810
- * @param state - Current query state
2811
- * @param primaryKey - Primary key name
2812
- * @returns True if primary key is selected
2813
- */
2814
- hasPrimarySelected(state, primaryKey) {
2815
- return state.ast.columns.some((col) => {
2816
- const alias = col.alias;
2817
- const name = alias || col.name;
2818
- return !isRelationAlias(alias) && name === primaryKey;
2819
- });
2820
- }
2821
- /**
2822
- * Gets all base columns for the table
2823
- * @returns Record of all table columns
2824
- */
2825
- getBaseColumns() {
2826
- return Object.keys(this.table.columns).reduce((acc, key) => {
2827
- acc[key] = this.table.columns[key];
2828
- return acc;
2829
- }, {});
2830
- }
2831
- };
2832
-
2833
- // src/query-builder/relation-conditions.ts
2834
- var assertNever = (value) => {
2835
- throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
2836
- };
2837
- var baseRelationCondition = (root, relation, rootAlias) => {
2838
- const rootTable = rootAlias || root.name;
2839
- const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2840
- const localKey = relation.localKey || defaultLocalKey;
2841
- switch (relation.type) {
2842
- case RelationKinds.HasMany:
2843
- case RelationKinds.HasOne:
2844
- return eq(
2845
- { type: "Column", table: relation.target.name, name: relation.foreignKey },
2846
- { type: "Column", table: rootTable, name: localKey }
2847
- );
2848
- case RelationKinds.BelongsTo:
2849
- return eq(
2850
- { type: "Column", table: relation.target.name, name: localKey },
2851
- { type: "Column", table: rootTable, name: relation.foreignKey }
2852
- );
2853
- case RelationKinds.BelongsToMany:
2854
- throw new Error("BelongsToMany relations do not support the standard join condition builder");
2855
- default:
2856
- return assertNever(relation);
2857
- }
2858
- };
2859
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
2860
- const rootKey = relation.localKey || findPrimaryKey(root);
2861
- const targetKey = relation.targetKey || findPrimaryKey(relation.target);
2862
- const rootTable = rootAlias || root.name;
2863
- const pivotCondition = eq(
2864
- { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
2865
- { type: "Column", table: rootTable, name: rootKey }
2866
- );
2867
- const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
2868
- let targetCondition = eq(
2869
- { type: "Column", table: relation.target.name, name: targetKey },
2870
- { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
2871
- );
2872
- if (extra) {
2873
- targetCondition = and(targetCondition, extra);
2874
- }
2875
- const targetJoin = createJoinNode(
2876
- joinKind,
2877
- relation.target.name,
2878
- targetCondition,
2879
- relationName
2880
- );
2881
- return [pivotJoin, targetJoin];
2882
- };
2883
- var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
2884
- const base = baseRelationCondition(root, relation, rootAlias);
2885
- return extra ? and(base, extra) : base;
2886
- };
2887
- var buildRelationCorrelation = (root, relation, rootAlias) => {
2888
- return baseRelationCondition(root, relation, rootAlias);
2889
- };
2890
-
2891
- // src/core/ast/join-metadata.ts
2892
- var getJoinRelationName = (join) => join.meta?.relationName;
2893
-
2894
- // src/query-builder/relation-service.ts
2895
- var RelationService = class {
2896
- /**
2897
- * Creates a new RelationService instance
2898
- * @param table - Table definition
2899
- * @param state - Current query state
2900
- * @param hydration - Hydration manager
2901
- */
2902
- constructor(table, state, hydration, createQueryAstService) {
2903
- this.table = table;
2904
- this.state = state;
2905
- this.hydration = hydration;
2906
- this.createQueryAstService = createQueryAstService;
2907
- this.projectionHelper = new RelationProjectionHelper(
2908
- table,
2909
- (state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
2910
- );
2911
- }
2912
- /**
2913
- * Joins a relation to the query
2914
- * @param relationName - Name of the relation to join
2915
- * @param joinKind - Type of join to use
2916
- * @param extraCondition - Additional join condition
2917
- * @returns Relation result with updated state and hydration
2918
- */
2919
- joinRelation(relationName, joinKind, extraCondition) {
2920
- const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
2921
- return { state: nextState, hydration: this.hydration };
2922
- }
2923
- /**
2924
- * Matches records based on a relation with an optional predicate
2925
- * @param relationName - Name of the relation to match
2926
- * @param predicate - Optional predicate expression
2927
- * @returns Relation result with updated state and hydration
2928
- */
2929
- match(relationName, predicate) {
2930
- const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
2931
- const pk = findPrimaryKey(this.table);
2932
- const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
2933
- const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
2934
- const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
2935
- return { state: nextState, hydration: joined.hydration };
2936
- }
2937
- /**
2938
- * Includes a relation in the query result
2939
- * @param relationName - Name of the relation to include
2940
- * @param options - Options for relation inclusion
2941
- * @returns Relation result with updated state and hydration
2942
- */
2943
- include(relationName, options) {
2944
- let state = this.state;
2945
- let hydration = this.hydration;
2946
- const relation = this.getRelation(relationName);
2947
- const aliasPrefix = options?.aliasPrefix ?? relationName;
2948
- const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
2949
- if (!alreadyJoined) {
2950
- const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
2951
- state = joined.state;
2952
- }
2953
- const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
2954
- state = projectionResult.state;
2955
- hydration = projectionResult.hydration;
2956
- const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
2957
- const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
2958
- return keys.reduce((acc, key) => {
2959
- const def = columns[key];
2960
- if (!def) {
2961
- throw new Error(missingMsg(key));
2962
- }
2963
- acc[makeRelationAlias(prefix, key)] = def;
2964
- return acc;
2965
- }, {});
2966
- };
2967
- const targetSelection = buildTypedSelection(
2968
- relation.target.columns,
2969
- aliasPrefix,
2970
- targetColumns,
2971
- (key) => `Column '${key}' not found on relation '${relationName}'`
2972
- );
2973
- if (relation.type !== RelationKinds.BelongsToMany) {
2974
- const relationSelectionResult2 = this.selectColumns(state, hydration, targetSelection);
2975
- state = relationSelectionResult2.state;
2976
- hydration = relationSelectionResult2.hydration;
2977
- hydration = hydration.onRelationIncluded(
2978
- state,
2979
- relation,
2980
- relationName,
2981
- aliasPrefix,
2982
- targetColumns
2983
- );
2984
- return { state, hydration };
2985
- }
2986
- const many = relation;
2987
- const pivotAliasPrefix = options?.pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
2988
- const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
2989
- const pivotColumns = options?.pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
2990
- const pivotSelection = buildTypedSelection(
2991
- many.pivotTable.columns,
2992
- pivotAliasPrefix,
2993
- pivotColumns,
2994
- (key) => `Column '${key}' not found on pivot table '${many.pivotTable.name}'`
2995
- );
2996
- const combinedSelection = {
2997
- ...targetSelection,
2998
- ...pivotSelection
2999
- };
3000
- const relationSelectionResult = this.selectColumns(state, hydration, combinedSelection);
3001
- state = relationSelectionResult.state;
3002
- hydration = relationSelectionResult.hydration;
3003
- hydration = hydration.onRelationIncluded(
3004
- state,
3005
- relation,
3006
- relationName,
3007
- aliasPrefix,
3008
- targetColumns,
3009
- { aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
3010
- );
3011
- return { state, hydration };
3012
- }
3013
- /**
3014
- * Applies relation correlation to a query AST
3015
- * @param relationName - Name of the relation
3016
- * @param ast - Query AST to modify
3017
- * @returns Modified query AST with relation correlation
3018
- */
3019
- applyRelationCorrelation(relationName, ast, additionalCorrelation) {
3020
- const relation = this.getRelation(relationName);
3021
- const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
3022
- let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
3023
- if (additionalCorrelation) {
3024
- correlation = and(correlation, additionalCorrelation);
3025
- }
3026
- const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
3027
- return {
3028
- ...ast,
3029
- where: whereInSubquery
3030
- };
3031
- }
3032
- /**
3033
- * Creates a join node for a relation
3034
- * @param state - Current query state
3035
- * @param relationName - Name of the relation
3036
- * @param joinKind - Type of join to use
3037
- * @param extraCondition - Additional join condition
3038
- * @returns Updated query state with join
3039
- */
3040
- withJoin(state, relationName, joinKind, extraCondition) {
3041
- const relation = this.getRelation(relationName);
3042
- const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
3043
- if (relation.type === RelationKinds.BelongsToMany) {
3044
- const joins = buildBelongsToManyJoins(
3045
- this.table,
3046
- relationName,
3047
- relation,
3048
- joinKind,
3049
- extraCondition,
3050
- rootAlias
3051
- );
3052
- return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
3053
- }
3054
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
3055
- const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
3056
- return this.astService(state).withJoin(joinNode);
3057
- }
3058
- /**
3059
- * Selects columns for a relation
3060
- * @param state - Current query state
3061
- * @param hydration - Hydration manager
3062
- * @param columns - Columns to select
3063
- * @returns Relation result with updated state and hydration
3064
- */
3065
- selectColumns(state, hydration, columns) {
3066
- const { state: nextState, addedColumns } = this.astService(state).select(columns);
3067
- return {
3068
- state: nextState,
3069
- hydration: hydration.onColumnsSelected(nextState, addedColumns)
3070
- };
3071
- }
3072
- /**
3073
- * Gets a relation definition by name
3074
- * @param relationName - Name of the relation
3075
- * @returns Relation definition
3076
- * @throws Error if relation is not found
3077
- */
3078
- getRelation(relationName) {
3079
- const relation = this.table.relations[relationName];
3080
- if (!relation) {
3081
- throw new Error(`Relation '${relationName}' not found on table '${this.table.name}'`);
3082
- }
3083
- return relation;
3084
- }
3085
- /**
3086
- * Creates a QueryAstService instance
3087
- * @param state - Current query state
3088
- * @returns QueryAstService instance
3089
- */
3090
- astService(state = this.state) {
3091
- return this.createQueryAstService(this.table, state);
3092
- }
3093
- rootTableName() {
3094
- const from = this.state.ast.from;
3095
- if (from.type === "Table" && from.alias) return from.alias;
3096
- return this.table.name;
3097
- }
3098
- };
3099
-
3100
- // src/query-builder/select-query-builder-deps.ts
3101
- var defaultCreateQueryAstService = (table, state) => new QueryAstService(table, state);
3102
- var defaultCreateHydrationPlanner = (table) => new HydrationPlanner(table);
3103
- var defaultCreateHydration = (table, plannerFactory) => new HydrationManager(table, plannerFactory(table));
3104
- var resolveSelectQueryBuilderDependencies = (overrides = {}) => {
3105
- const createQueryAstService = overrides.createQueryAstService ?? defaultCreateQueryAstService;
3106
- const createHydrationPlanner = overrides.createHydrationPlanner ?? defaultCreateHydrationPlanner;
3107
- const createHydration = overrides.createHydration ?? ((table) => defaultCreateHydration(table, createHydrationPlanner));
3108
- const createRelationService = overrides.createRelationService ?? ((table, state, hydration) => new RelationService(table, state, hydration, createQueryAstService));
3109
- return {
3110
- createState: overrides.createState ?? ((table) => new SelectQueryState(table)),
3111
- createHydration,
3112
- createHydrationPlanner,
3113
- createQueryAstService,
3114
- createRelationService
3115
- };
3116
- };
3117
- var defaultSelectQueryBuilderDependencies = resolveSelectQueryBuilderDependencies();
3118
-
3119
- // src/query-builder/column-selector.ts
3120
- var ColumnSelector = class {
3121
- /**
3122
- * Creates a new ColumnSelector instance
3123
- * @param env - Query builder environment
3124
- */
3125
- constructor(env) {
3126
- this.env = env;
3127
- }
3128
- /**
3129
- * Selects columns for the query
3130
- * @param context - Current query context
3131
- * @param columns - Columns to select
3132
- * @returns Updated query context with selected columns
3133
- */
3134
- select(context, columns) {
3135
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3136
- const { state: nextState, addedColumns } = astService.select(columns);
3137
- return {
3138
- state: nextState,
3139
- hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
3140
- };
3141
- }
3142
- /**
3143
- * Selects raw column expressions
3144
- * @param context - Current query context
3145
- * @param columns - Raw column expressions
3146
- * @returns Updated query context with raw column selections
3147
- */
3148
- selectRaw(context, columns) {
3149
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3150
- const nextState = astService.selectRaw(columns).state;
3151
- return { state: nextState, hydration: context.hydration };
3152
- }
3153
- /**
3154
- * Selects a subquery as a column
3155
- * @param context - Current query context
3156
- * @param alias - Alias for the subquery
3157
- * @param query - Subquery to select
3158
- * @returns Updated query context with subquery selection
3159
- */
3160
- selectSubquery(context, alias, query) {
3161
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3162
- const nextState = astService.selectSubquery(alias, query);
3163
- return { state: nextState, hydration: context.hydration };
3164
- }
3165
- /**
3166
- * Adds DISTINCT clause to the query
3167
- * @param context - Current query context
3168
- * @param columns - Columns to make distinct
3169
- * @returns Updated query context with DISTINCT clause
3170
- */
3171
- distinct(context, columns) {
3172
- const from = context.state.ast.from;
3173
- const tableRef = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
3174
- const nodes = columns.map((col) => buildColumnNode(tableRef, col));
3175
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3176
- const nextState = astService.withDistinct(nodes);
3177
- return { state: nextState, hydration: context.hydration };
3178
- }
3179
- };
3180
-
3181
- // src/query-builder/relation-manager.ts
3182
- var RelationManager = class {
3183
- /**
3184
- * Creates a new RelationManager instance
3185
- * @param env - Query builder environment
3186
- */
3187
- constructor(env) {
3188
- this.env = env;
3189
- }
3190
- /**
3191
- * Matches records based on a relation with an optional predicate
3192
- * @param context - Current query context
3193
- * @param relationName - Name of the relation to match
3194
- * @param predicate - Optional predicate expression
3195
- * @returns Updated query context with relation match
3196
- */
3197
- match(context, relationName, predicate) {
3198
- const result = this.createService(context).match(relationName, predicate);
3199
- return { state: result.state, hydration: result.hydration };
3200
- }
3201
- /**
3202
- * Joins a relation to the query
3203
- * @param context - Current query context
3204
- * @param relationName - Name of the relation to join
3205
- * @param joinKind - Type of join to use
3206
- * @param extraCondition - Additional join condition
3207
- * @returns Updated query context with relation join
3208
- */
3209
- joinRelation(context, relationName, joinKind, extraCondition) {
3210
- const result = this.createService(context).joinRelation(relationName, joinKind, extraCondition);
3211
- return { state: result.state, hydration: result.hydration };
3212
- }
3213
- /**
3214
- * Includes a relation in the query result
3215
- * @param context - Current query context
3216
- * @param relationName - Name of the relation to include
3217
- * @param options - Options for relation inclusion
3218
- * @returns Updated query context with included relation
3219
- */
3220
- include(context, relationName, options) {
3221
- const result = this.createService(context).include(relationName, options);
3222
- return { state: result.state, hydration: result.hydration };
3223
- }
3224
- /**
3225
- * Applies relation correlation to a query AST
3226
- * @param context - Current query context
3227
- * @param relationName - Name of the relation
3228
- * @param ast - Query AST to modify
3229
- * @returns Modified query AST with relation correlation
3230
- */
3231
- applyRelationCorrelation(context, relationName, ast, additionalCorrelation) {
3232
- return this.createService(context).applyRelationCorrelation(relationName, ast, additionalCorrelation);
3233
- }
3234
- /**
3235
- * Creates a relation service instance
3236
- * @param context - Current query context
3237
- * @returns Relation service instance
3238
- */
3239
- createService(context) {
3240
- return this.env.deps.createRelationService(this.env.table, context.state, context.hydration);
3241
- }
3242
- };
3243
-
3244
- // src/orm/hydration.ts
3245
- var hydrateRows = (rows, plan) => {
3246
- if (!plan || !rows.length) return rows;
3247
- const rootMap = /* @__PURE__ */ new Map();
3248
- const relationIndex = /* @__PURE__ */ new Map();
3249
- const getOrCreateParent = (row) => {
3250
- const rootId = row[plan.rootPrimaryKey];
3251
- if (rootId === void 0) return void 0;
3252
- if (!rootMap.has(rootId)) {
3253
- rootMap.set(rootId, createBaseRow(row, plan));
3254
- }
3255
- return rootMap.get(rootId);
3256
- };
3257
- const getRelationSeenSet = (rootId, relationName) => {
3258
- let byRelation = relationIndex.get(rootId);
3259
- if (!byRelation) {
3260
- byRelation = {};
3261
- relationIndex.set(rootId, byRelation);
3262
- }
3263
- let seen = byRelation[relationName];
3264
- if (!seen) {
3265
- seen = /* @__PURE__ */ new Set();
3266
- byRelation[relationName] = seen;
3267
- }
3268
- return seen;
3269
- };
3270
- for (const row of rows) {
3271
- const rootId = row[plan.rootPrimaryKey];
3272
- if (rootId === void 0) continue;
3273
- const parent = getOrCreateParent(row);
3274
- if (!parent) continue;
3275
- for (const rel of plan.relations) {
3276
- const childPkKey = makeRelationAlias(rel.aliasPrefix, rel.targetPrimaryKey);
3277
- const childPk = row[childPkKey];
3278
- if (childPk === null || childPk === void 0) continue;
3279
- const seen = getRelationSeenSet(rootId, rel.name);
3280
- if (seen.has(childPk)) continue;
3281
- seen.add(childPk);
3282
- if (rel.type === RelationKinds.HasOne) {
3283
- if (!parent[rel.name]) {
3284
- parent[rel.name] = buildChild(row, rel);
3285
- }
3286
- continue;
3287
- }
3288
- const bucket = parent[rel.name];
3289
- bucket.push(buildChild(row, rel));
3290
- }
3291
- }
3292
- return Array.from(rootMap.values());
3293
- };
3294
- var createBaseRow = (row, plan) => {
3295
- const base = {};
3296
- const baseKeys = plan.rootColumns.length ? plan.rootColumns : Object.keys(row).filter((k) => !isRelationAlias(k));
3297
- for (const key of baseKeys) {
3298
- base[key] = row[key];
3299
- }
3300
- for (const rel of plan.relations) {
3301
- base[rel.name] = rel.type === RelationKinds.HasOne ? null : [];
3302
- }
3303
- return base;
3304
- };
3305
- var buildChild = (row, rel) => {
3306
- const child = {};
3307
- for (const col of rel.columns) {
3308
- const key = makeRelationAlias(rel.aliasPrefix, col);
3309
- child[col] = row[key];
3310
- }
3311
- const pivot = buildPivot(row, rel);
3312
- if (pivot) {
3313
- child._pivot = pivot;
3314
- }
3315
- return child;
3316
- };
3317
- var buildPivot = (row, rel) => {
3318
- if (!rel.pivot) return void 0;
3319
- const pivot = {};
3320
- for (const col of rel.pivot.columns) {
3321
- const key = makeRelationAlias(rel.pivot.aliasPrefix, col);
3322
- pivot[col] = row[key];
3323
- }
3324
- const hasValue = Object.values(pivot).some((v) => v !== null && v !== void 0);
3325
- return hasValue ? pivot : void 0;
3326
- };
3327
-
3328
- // src/orm/entity-meta.ts
3329
- var ENTITY_META = Symbol("EntityMeta");
3330
- var toKey = (value) => value === null || value === void 0 ? "" : String(value);
3331
- var getHydrationRows = (meta, relationName, key) => {
3332
- const map = meta.relationHydration.get(relationName);
3333
- if (!map) return void 0;
3334
- const rows = map.get(toKey(key));
3335
- if (!rows) return void 0;
3336
- return Array.isArray(rows) ? rows : void 0;
3337
- };
3338
- var getHydrationRecord = (meta, relationName, key) => {
3339
- const map = meta.relationHydration.get(relationName);
3340
- if (!map) return void 0;
3341
- const value = map.get(toKey(key));
3342
- if (!value) return void 0;
3343
- if (Array.isArray(value)) {
3344
- return value[0];
3345
- }
3346
- return value;
3347
- };
3348
- var getEntityMeta = (entity) => {
3349
- if (!entity || typeof entity !== "object") return void 0;
3350
- return entity[ENTITY_META];
3351
- };
3352
- var hasEntityMeta = (entity) => {
3353
- return Boolean(getEntityMeta(entity));
3354
- };
3355
-
3356
- // src/orm/relations/has-many.ts
3357
- var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
3358
- var hideInternal = (obj, keys) => {
3359
- for (const key of keys) {
3360
- Object.defineProperty(obj, key, {
3361
- value: obj[key],
3362
- writable: false,
3363
- configurable: false,
3364
- enumerable: false
3365
- });
3366
- }
3367
- };
3368
- var DefaultHasManyCollection = class {
3369
- constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3370
- this.ctx = ctx;
3371
- this.meta = meta;
3372
- this.root = root;
3373
- this.relationName = relationName;
3374
- this.relation = relation;
3375
- this.rootTable = rootTable;
3376
- this.loader = loader;
3377
- this.createEntity = createEntity;
3378
- this.localKey = localKey;
3379
- this.loaded = false;
3380
- this.items = [];
3381
- this.added = /* @__PURE__ */ new Set();
3382
- this.removed = /* @__PURE__ */ new Set();
3383
- hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
3384
- this.hydrateFromCache();
3385
- }
3386
- async load() {
3387
- if (this.loaded) return this.items;
3388
- const map = await this.loader();
3389
- const key = toKey2(this.root[this.localKey]);
3390
- const rows = map.get(key) ?? [];
3391
- this.items = rows.map((row) => this.createEntity(row));
3392
- this.loaded = true;
3393
- return this.items;
3394
- }
3395
- getItems() {
3396
- return this.items;
3397
- }
3398
- add(data) {
3399
- const keyValue = this.root[this.localKey];
3400
- const childRow = {
3401
- ...data,
3402
- [this.relation.foreignKey]: keyValue
3403
- };
3404
- const entity = this.createEntity(childRow);
3405
- this.added.add(entity);
3406
- this.items.push(entity);
3407
- this.ctx.registerRelationChange(
3408
- this.root,
3409
- this.relationKey,
3410
- this.rootTable,
3411
- this.relationName,
3412
- this.relation,
3413
- { kind: "add", entity }
3414
- );
3415
- return entity;
3416
- }
3417
- attach(entity) {
3418
- const keyValue = this.root[this.localKey];
3419
- entity[this.relation.foreignKey] = keyValue;
3420
- this.ctx.markDirty(entity);
3421
- this.items.push(entity);
3422
- this.ctx.registerRelationChange(
3423
- this.root,
3424
- this.relationKey,
3425
- this.rootTable,
3426
- this.relationName,
3427
- this.relation,
3428
- { kind: "attach", entity }
3429
- );
3430
- }
3431
- remove(entity) {
3432
- this.items = this.items.filter((item) => item !== entity);
3433
- this.removed.add(entity);
3434
- this.ctx.registerRelationChange(
3435
- this.root,
3436
- this.relationKey,
3437
- this.rootTable,
3438
- this.relationName,
3439
- this.relation,
3440
- { kind: "remove", entity }
3441
- );
3442
- }
3443
- clear() {
3444
- for (const entity of [...this.items]) {
3445
- this.remove(entity);
3446
- }
3447
- }
3448
- get relationKey() {
3449
- return `${this.rootTable.name}.${this.relationName}`;
3450
- }
3451
- hydrateFromCache() {
3452
- const keyValue = this.root[this.localKey];
3453
- if (keyValue === void 0 || keyValue === null) return;
3454
- const rows = getHydrationRows(this.meta, this.relationName, keyValue);
3455
- if (!rows?.length) return;
3456
- this.items = rows.map((row) => this.createEntity(row));
3457
- this.loaded = true;
3458
- }
3459
- toJSON() {
3460
- return this.items;
3461
- }
3462
- };
3463
-
3464
- // src/orm/relations/has-one.ts
3465
- var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
3466
- var hideInternal2 = (obj, keys) => {
3467
- for (const key of keys) {
3468
- Object.defineProperty(obj, key, {
3469
- value: obj[key],
3470
- writable: false,
3471
- configurable: false,
3472
- enumerable: false
3473
- });
3474
- }
3475
- };
3476
- var DefaultHasOneReference = class {
3477
- constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3478
- this.ctx = ctx;
3479
- this.meta = meta;
3480
- this.root = root;
3481
- this.relationName = relationName;
3482
- this.relation = relation;
3483
- this.rootTable = rootTable;
3484
- this.loader = loader;
3485
- this.createEntity = createEntity;
3486
- this.localKey = localKey;
3487
- this.loaded = false;
3488
- this.current = null;
3489
- hideInternal2(this, [
3490
- "ctx",
3491
- "meta",
3492
- "root",
3493
- "relationName",
3494
- "relation",
3495
- "rootTable",
3496
- "loader",
3497
- "createEntity",
3498
- "localKey"
3499
- ]);
3500
- this.populateFromHydrationCache();
3501
- }
3502
- async load() {
3503
- if (this.loaded) return this.current;
3504
- const map = await this.loader();
3505
- const keyValue = this.root[this.localKey];
3506
- if (keyValue === void 0 || keyValue === null) {
3507
- this.loaded = true;
3508
- return this.current;
3509
- }
3510
- const row = map.get(toKey3(keyValue));
3511
- this.current = row ? this.createEntity(row) : null;
3512
- this.loaded = true;
3513
- return this.current;
3514
- }
3515
- get() {
3516
- return this.current;
3517
- }
3518
- set(data) {
3519
- if (data === null) {
3520
- return this.detachCurrent();
3521
- }
3522
- const entity = hasEntityMeta(data) ? data : this.createEntity(data);
3523
- if (this.current && this.current !== entity) {
3524
- this.ctx.registerRelationChange(
3525
- this.root,
3526
- this.relationKey,
3527
- this.rootTable,
3528
- this.relationName,
3529
- this.relation,
3530
- { kind: "remove", entity: this.current }
3531
- );
3532
- }
3533
- this.assignForeignKey(entity);
3534
- this.current = entity;
3535
- this.loaded = true;
3536
- this.ctx.registerRelationChange(
3537
- this.root,
3538
- this.relationKey,
3539
- this.rootTable,
3540
- this.relationName,
3541
- this.relation,
3542
- { kind: "attach", entity }
3543
- );
3544
- return entity;
3545
- }
3546
- toJSON() {
3547
- return this.current;
3548
- }
3549
- detachCurrent() {
3550
- const previous = this.current;
3551
- if (!previous) return null;
3552
- this.current = null;
3553
- this.loaded = true;
3554
- this.ctx.registerRelationChange(
3555
- this.root,
3556
- this.relationKey,
3557
- this.rootTable,
3558
- this.relationName,
3559
- this.relation,
3560
- { kind: "remove", entity: previous }
3561
- );
3562
- return null;
3563
- }
3564
- assignForeignKey(entity) {
3565
- const keyValue = this.root[this.localKey];
3566
- entity[this.relation.foreignKey] = keyValue;
3567
- }
3568
- get relationKey() {
3569
- return `${this.rootTable.name}.${this.relationName}`;
3570
- }
3571
- populateFromHydrationCache() {
3572
- const keyValue = this.root[this.localKey];
3573
- if (keyValue === void 0 || keyValue === null) return;
3574
- const row = getHydrationRecord(this.meta, this.relationName, keyValue);
3575
- if (!row) return;
3576
- this.current = this.createEntity(row);
3577
- this.loaded = true;
3578
- }
3579
- };
3580
-
3581
- // src/orm/relations/belongs-to.ts
3582
- var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
3583
- var hideInternal3 = (obj, keys) => {
3584
- for (const key of keys) {
3585
- Object.defineProperty(obj, key, {
3586
- value: obj[key],
3587
- writable: false,
3588
- configurable: false,
3589
- enumerable: false
3590
- });
3591
- }
3592
- };
3593
- var DefaultBelongsToReference = class {
3594
- constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
3595
- this.ctx = ctx;
3596
- this.meta = meta;
3597
- this.root = root;
3598
- this.relationName = relationName;
3599
- this.relation = relation;
3600
- this.rootTable = rootTable;
3601
- this.loader = loader;
3602
- this.createEntity = createEntity;
3603
- this.targetKey = targetKey;
3604
- this.loaded = false;
3605
- this.current = null;
3606
- hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
3607
- this.populateFromHydrationCache();
3608
- }
3609
- async load() {
3610
- if (this.loaded) return this.current;
3611
- const map = await this.loader();
3612
- const fkValue = this.root[this.relation.foreignKey];
3613
- if (fkValue === null || fkValue === void 0) {
3614
- this.current = null;
3615
- } else {
3616
- const row = map.get(toKey4(fkValue));
3617
- this.current = row ? this.createEntity(row) : null;
3618
- }
3619
- this.loaded = true;
3620
- return this.current;
3621
- }
3622
- get() {
3623
- return this.current;
3624
- }
3625
- set(data) {
3626
- if (data === null) {
3627
- const previous = this.current;
3628
- this.root[this.relation.foreignKey] = null;
3629
- this.current = null;
3630
- this.ctx.registerRelationChange(
3631
- this.root,
3632
- this.relationKey,
3633
- this.rootTable,
3634
- this.relationName,
3635
- this.relation,
3636
- { kind: "remove", entity: previous }
3637
- );
3638
- return null;
3639
- }
3640
- const entity = hasEntityMeta(data) ? data : this.createEntity(data);
3641
- const pkValue = entity[this.targetKey];
3642
- if (pkValue !== void 0) {
3643
- this.root[this.relation.foreignKey] = pkValue;
3644
- }
3645
- this.current = entity;
3646
- this.ctx.registerRelationChange(
3647
- this.root,
3648
- this.relationKey,
3649
- this.rootTable,
3650
- this.relationName,
3651
- this.relation,
3652
- { kind: "attach", entity }
3653
- );
3654
- return entity;
3655
- }
3656
- get relationKey() {
3657
- return `${this.rootTable.name}.${this.relationName}`;
3658
- }
3659
- populateFromHydrationCache() {
3660
- const fkValue = this.root[this.relation.foreignKey];
3661
- if (fkValue === void 0 || fkValue === null) return;
3662
- const row = getHydrationRecord(this.meta, this.relationName, fkValue);
3663
- if (!row) return;
3664
- this.current = this.createEntity(row);
3665
- this.loaded = true;
3666
- }
3667
- toJSON() {
3668
- return this.current;
3669
- }
3670
- };
3671
-
3672
- // src/orm/relations/many-to-many.ts
3673
- var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3674
- var hideInternal4 = (obj, keys) => {
3675
- for (const key of keys) {
3676
- Object.defineProperty(obj, key, {
3677
- value: obj[key],
3678
- writable: false,
3679
- configurable: false,
3680
- enumerable: false
3681
- });
3682
- }
3683
- };
3684
- var DefaultManyToManyCollection = class {
3685
- constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3686
- this.ctx = ctx;
3687
- this.meta = meta;
3688
- this.root = root;
3689
- this.relationName = relationName;
3690
- this.relation = relation;
3691
- this.rootTable = rootTable;
3692
- this.loader = loader;
3693
- this.createEntity = createEntity;
3694
- this.localKey = localKey;
3695
- this.loaded = false;
3696
- this.items = [];
3697
- hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
3698
- this.hydrateFromCache();
3699
- }
3700
- async load() {
3701
- if (this.loaded) return this.items;
3702
- const map = await this.loader();
3703
- const key = toKey5(this.root[this.localKey]);
3704
- const rows = map.get(key) ?? [];
3705
- this.items = rows.map((row) => {
3706
- const entity = this.createEntity(row);
3707
- if (row._pivot) {
3708
- entity._pivot = row._pivot;
3709
- }
3710
- return entity;
3711
- });
3712
- this.loaded = true;
3713
- return this.items;
3714
- }
3715
- getItems() {
3716
- return this.items;
3717
- }
3718
- attach(target) {
3719
- const entity = this.ensureEntity(target);
3720
- const id = this.extractId(entity);
3721
- if (id == null) return;
3722
- if (this.items.some((item) => this.extractId(item) === id)) {
3723
- return;
3724
- }
3725
- this.items.push(entity);
3726
- this.ctx.registerRelationChange(
3727
- this.root,
3728
- this.relationKey,
3729
- this.rootTable,
3730
- this.relationName,
3731
- this.relation,
3732
- { kind: "attach", entity }
3733
- );
3734
- }
3735
- detach(target) {
3736
- const id = typeof target === "number" || typeof target === "string" ? target : this.extractId(target);
3737
- if (id == null) return;
3738
- const existing = this.items.find((item) => this.extractId(item) === id);
3739
- if (!existing) return;
3740
- this.items = this.items.filter((item) => this.extractId(item) !== id);
3741
- this.ctx.registerRelationChange(
3742
- this.root,
3743
- this.relationKey,
3744
- this.rootTable,
3745
- this.relationName,
3746
- this.relation,
3747
- { kind: "detach", entity: existing }
3748
- );
3749
- }
3750
- async syncByIds(ids) {
3751
- await this.load();
3752
- const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
3753
- const normalized = new Set(ids.map((id) => toKey5(id)));
3754
- const currentIds = new Set(this.items.map((item) => toKey5(this.extractId(item))));
3755
- for (const id of normalized) {
3756
- if (!currentIds.has(id)) {
3757
- this.attach(id);
3758
- }
3759
- }
3760
- for (const item of [...this.items]) {
3761
- const itemId = toKey5(this.extractId(item));
3762
- if (!normalized.has(itemId)) {
3763
- this.detach(item);
3764
- }
3765
- }
3766
- }
3767
- ensureEntity(target) {
3768
- if (typeof target === "number" || typeof target === "string") {
3769
- const stub = {
3770
- [this.targetKey]: target
3771
- };
3772
- return this.createEntity(stub);
3773
- }
3774
- return target;
3775
- }
3776
- extractId(entity) {
3777
- if (entity === null || entity === void 0) return null;
3778
- if (typeof entity === "number" || typeof entity === "string") {
3779
- return entity;
3780
- }
3781
- return entity[this.targetKey] ?? null;
3782
- }
3783
- get relationKey() {
3784
- return `${this.rootTable.name}.${this.relationName}`;
3785
- }
3786
- get targetKey() {
3787
- return this.relation.targetKey || findPrimaryKey(this.relation.target);
3788
- }
3789
- hydrateFromCache() {
3790
- const keyValue = this.root[this.localKey];
3791
- if (keyValue === void 0 || keyValue === null) return;
3792
- const rows = getHydrationRows(this.meta, this.relationName, keyValue);
3793
- if (!rows?.length) return;
3794
- this.items = rows.map((row) => {
3795
- const entity = this.createEntity(row);
3796
- if (row._pivot) {
3797
- entity._pivot = row._pivot;
3798
- }
3799
- return entity;
3800
- });
3801
- this.loaded = true;
3802
- }
3803
- toJSON() {
3804
- return this.items;
3805
- }
3806
- };
3807
-
3808
- // src/orm/lazy-batch.ts
3809
- var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
3810
- acc[name] = def;
3811
- return acc;
3812
- }, {});
3813
- var rowsFromResults = (results) => {
3814
- const rows = [];
3815
- for (const result of results) {
3816
- const { columns, values } = result;
3817
- for (const valueRow of values) {
3818
- const row = {};
3819
- columns.forEach((column, idx) => {
3820
- row[column] = valueRow[idx];
3821
- });
3822
- rows.push(row);
3823
- }
3824
- }
3825
- return rows;
3826
- };
3827
- var executeQuery = async (ctx, qb) => {
3828
- const compiled = ctx.dialect.compileSelect(qb.getAST());
3829
- const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
3830
- return rowsFromResults(results);
3831
- };
3832
- var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
3833
- var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
3834
- const localKey = relation.localKey || findPrimaryKey(rootTable);
3835
- const roots = ctx.getEntitiesForTable(rootTable);
3836
- const keys = /* @__PURE__ */ new Set();
3837
- for (const tracked of roots) {
3838
- const value = tracked.entity[localKey];
3839
- if (value !== null && value !== void 0) {
3840
- keys.add(value);
3841
- }
3842
- }
3843
- if (!keys.size) {
3844
- return /* @__PURE__ */ new Map();
3845
- }
3846
- const selectMap = selectAllColumns(relation.target);
3847
- const fb = new SelectQueryBuilder(relation.target).select(selectMap);
3848
- const fkColumn = relation.target.columns[relation.foreignKey];
3849
- if (!fkColumn) return /* @__PURE__ */ new Map();
3850
- fb.where(inList(fkColumn, Array.from(keys)));
3851
- const rows = await executeQuery(ctx, fb);
3852
- const grouped = /* @__PURE__ */ new Map();
3853
- for (const row of rows) {
3854
- const fkValue = row[relation.foreignKey];
3855
- if (fkValue === null || fkValue === void 0) continue;
3856
- const key = toKey6(fkValue);
3857
- const bucket = grouped.get(key) ?? [];
3858
- bucket.push(row);
3859
- grouped.set(key, bucket);
3860
- }
3861
- return grouped;
3862
- };
3863
- var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
3864
- const localKey = relation.localKey || findPrimaryKey(rootTable);
3865
- const roots = ctx.getEntitiesForTable(rootTable);
3866
- const keys = /* @__PURE__ */ new Set();
3867
- for (const tracked of roots) {
3868
- const value = tracked.entity[localKey];
3869
- if (value !== null && value !== void 0) {
3870
- keys.add(value);
3871
- }
3872
- }
3873
- if (!keys.size) {
3874
- return /* @__PURE__ */ new Map();
3875
- }
3876
- const selectMap = selectAllColumns(relation.target);
3877
- const qb = new SelectQueryBuilder(relation.target).select(selectMap);
3878
- const fkColumn = relation.target.columns[relation.foreignKey];
3879
- if (!fkColumn) return /* @__PURE__ */ new Map();
3880
- qb.where(inList(fkColumn, Array.from(keys)));
3881
- const rows = await executeQuery(ctx, qb);
3882
- const lookup = /* @__PURE__ */ new Map();
3883
- for (const row of rows) {
3884
- const fkValue = row[relation.foreignKey];
3885
- if (fkValue === null || fkValue === void 0) continue;
3886
- const key = toKey6(fkValue);
3887
- if (!lookup.has(key)) {
3888
- lookup.set(key, row);
3889
- }
3890
- }
3891
- return lookup;
3892
- };
3893
- var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
3894
- const roots = ctx.getEntitiesForTable(rootTable);
3895
- const foreignKeys = /* @__PURE__ */ new Set();
3896
- for (const tracked of roots) {
3897
- const value = tracked.entity[relation.foreignKey];
3898
- if (value !== null && value !== void 0) {
3899
- foreignKeys.add(value);
3900
- }
3901
- }
3902
- if (!foreignKeys.size) {
3903
- return /* @__PURE__ */ new Map();
3904
- }
3905
- const selectMap = selectAllColumns(relation.target);
3906
- const qb = new SelectQueryBuilder(relation.target).select(selectMap);
3907
- const targetKey = relation.localKey || findPrimaryKey(relation.target);
3908
- const pkColumn = relation.target.columns[targetKey];
3909
- if (!pkColumn) return /* @__PURE__ */ new Map();
3910
- qb.where(inList(pkColumn, Array.from(foreignKeys)));
3911
- const rows = await executeQuery(ctx, qb);
3912
- const map = /* @__PURE__ */ new Map();
3913
- for (const row of rows) {
3914
- const keyValue = row[targetKey];
3915
- if (keyValue === null || keyValue === void 0) continue;
3916
- map.set(toKey6(keyValue), row);
3917
- }
3918
- return map;
3919
- };
3920
- var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
3921
- const rootKey = relation.localKey || findPrimaryKey(rootTable);
3922
- const roots = ctx.getEntitiesForTable(rootTable);
3923
- const rootIds = /* @__PURE__ */ new Set();
3924
- for (const tracked of roots) {
3925
- const value = tracked.entity[rootKey];
3926
- if (value !== null && value !== void 0) {
3927
- rootIds.add(value);
3928
- }
3929
- }
3930
- if (!rootIds.size) {
3931
- return /* @__PURE__ */ new Map();
3932
- }
3933
- const pivotSelect = selectAllColumns(relation.pivotTable);
3934
- const pivotQb = new SelectQueryBuilder(relation.pivotTable).select(pivotSelect);
3935
- const pivotFkCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
3936
- if (!pivotFkCol) return /* @__PURE__ */ new Map();
3937
- pivotQb.where(inList(pivotFkCol, Array.from(rootIds)));
3938
- const pivotRows = await executeQuery(ctx, pivotQb);
3939
- const rootLookup = /* @__PURE__ */ new Map();
3940
- const targetIds = /* @__PURE__ */ new Set();
3941
- for (const pivot of pivotRows) {
3942
- const rootValue = pivot[relation.pivotForeignKeyToRoot];
3943
- const targetValue = pivot[relation.pivotForeignKeyToTarget];
3944
- if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
3945
- continue;
3946
- }
3947
- const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
3948
- bucket.push({
3949
- targetId: targetValue,
3950
- pivot: { ...pivot }
3951
- });
3952
- rootLookup.set(toKey6(rootValue), bucket);
3953
- targetIds.add(targetValue);
3954
- }
3955
- if (!targetIds.size) {
3956
- return /* @__PURE__ */ new Map();
3957
- }
3958
- const targetSelect = selectAllColumns(relation.target);
3959
- const targetKey = relation.targetKey || findPrimaryKey(relation.target);
3960
- const targetPkColumn = relation.target.columns[targetKey];
3961
- if (!targetPkColumn) return /* @__PURE__ */ new Map();
3962
- const targetQb = new SelectQueryBuilder(relation.target).select(targetSelect);
3963
- targetQb.where(inList(targetPkColumn, Array.from(targetIds)));
3964
- const targetRows = await executeQuery(ctx, targetQb);
3965
- const targetMap = /* @__PURE__ */ new Map();
3966
- for (const row of targetRows) {
3967
- const pkValue = row[targetKey];
3968
- if (pkValue === null || pkValue === void 0) continue;
3969
- targetMap.set(toKey6(pkValue), row);
3970
- }
3971
- const result = /* @__PURE__ */ new Map();
3972
- for (const [rootId, entries] of rootLookup.entries()) {
3973
- const bucket = [];
3974
- for (const entry of entries) {
3975
- const targetRow = targetMap.get(toKey6(entry.targetId));
3976
- if (!targetRow) continue;
3977
- bucket.push({
3978
- ...targetRow,
3979
- _pivot: entry.pivot
3980
- });
3981
- }
3982
- result.set(rootId, bucket);
3983
- }
3984
- return result;
3985
- };
3986
-
3987
- // src/orm/entity.ts
3988
- var relationLoaderCache = (meta, relationName, factory) => {
3989
- if (meta.relationCache.has(relationName)) {
3990
- return meta.relationCache.get(relationName);
3991
- }
3992
- const promise = factory().then((value) => {
3993
- for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
3994
- const otherMeta = getEntityMeta(tracked.entity);
3995
- if (!otherMeta) continue;
3996
- otherMeta.relationHydration.set(relationName, value);
3997
- }
3998
- return value;
3999
- });
4000
- meta.relationCache.set(relationName, promise);
4001
- for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
4002
- const otherMeta = getEntityMeta(tracked.entity);
4003
- if (!otherMeta) continue;
4004
- otherMeta.relationCache.set(relationName, promise);
4005
- }
4006
- return promise;
4007
- };
4008
- var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
4009
- const target = { ...row };
4010
- const meta = {
4011
- ctx,
4012
- table,
4013
- lazyRelations: [...lazyRelations],
4014
- relationCache: /* @__PURE__ */ new Map(),
4015
- relationHydration: /* @__PURE__ */ new Map(),
4016
- relationWrappers: /* @__PURE__ */ new Map()
4017
- };
4018
- Object.defineProperty(target, ENTITY_META, {
4019
- value: meta,
4020
- enumerable: false,
4021
- writable: false
4022
- });
4023
- let proxy;
4024
- const handler = {
4025
- get(targetObj, prop, receiver) {
4026
- if (prop === ENTITY_META) {
4027
- return meta;
4028
- }
4029
- if (prop === "$load") {
4030
- return async (relationName) => {
4031
- const wrapper = getRelationWrapper(meta, relationName, proxy);
4032
- if (wrapper && typeof wrapper.load === "function") {
4033
- return wrapper.load();
4034
- }
4035
- return void 0;
4036
- };
4037
- }
4038
- if (typeof prop === "string" && table.relations[prop]) {
4039
- return getRelationWrapper(meta, prop, proxy);
4040
- }
4041
- return Reflect.get(targetObj, prop, receiver);
4042
- },
4043
- set(targetObj, prop, value, receiver) {
4044
- const result = Reflect.set(targetObj, prop, value, receiver);
4045
- if (typeof prop === "string" && table.columns[prop]) {
4046
- ctx.markDirty(proxy);
4047
- }
4048
- return result;
4049
- }
4050
- };
4051
- proxy = new Proxy(target, handler);
4052
- populateHydrationCache(proxy, row, meta);
4053
- return proxy;
4054
- };
4055
- var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
4056
- const pkName = findPrimaryKey(table);
4057
- const pkValue = row[pkName];
4058
- if (pkValue !== void 0 && pkValue !== null) {
4059
- const tracked = ctx.getEntity(table, pkValue);
4060
- if (tracked) return tracked;
4061
- }
4062
- const entity = createEntityProxy(ctx, table, row, lazyRelations);
4063
- if (pkValue !== void 0 && pkValue !== null) {
4064
- ctx.trackManaged(table, pkValue, entity);
4065
- } else {
4066
- ctx.trackNew(table, entity);
4067
- }
4068
- return entity;
4069
- };
4070
- var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
4071
- var populateHydrationCache = (entity, row, meta) => {
4072
- for (const relationName of Object.keys(meta.table.relations)) {
4073
- const relation = meta.table.relations[relationName];
4074
- const data = row[relationName];
4075
- if (relation.type === RelationKinds.HasOne) {
4076
- const localKey = relation.localKey || findPrimaryKey(meta.table);
4077
- const rootValue = entity[localKey];
4078
- if (rootValue === void 0 || rootValue === null) continue;
4079
- if (!data || typeof data !== "object") continue;
4080
- const cache = /* @__PURE__ */ new Map();
4081
- cache.set(toKey7(rootValue), data);
4082
- meta.relationHydration.set(relationName, cache);
4083
- meta.relationCache.set(relationName, Promise.resolve(cache));
4084
- continue;
4085
- }
4086
- if (!Array.isArray(data)) continue;
4087
- if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
4088
- const localKey = relation.localKey || findPrimaryKey(meta.table);
4089
- const rootValue = entity[localKey];
4090
- if (rootValue === void 0 || rootValue === null) continue;
4091
- const cache = /* @__PURE__ */ new Map();
4092
- cache.set(toKey7(rootValue), data);
4093
- meta.relationHydration.set(relationName, cache);
4094
- meta.relationCache.set(relationName, Promise.resolve(cache));
4095
- continue;
4096
- }
4097
- if (relation.type === RelationKinds.BelongsTo) {
4098
- const targetKey = relation.localKey || findPrimaryKey(relation.target);
4099
- const cache = /* @__PURE__ */ new Map();
4100
- for (const item of data) {
4101
- const pkValue = item[targetKey];
4102
- if (pkValue === void 0 || pkValue === null) continue;
4103
- cache.set(toKey7(pkValue), item);
4104
- }
4105
- if (cache.size) {
4106
- meta.relationHydration.set(relationName, cache);
4107
- meta.relationCache.set(relationName, Promise.resolve(cache));
4108
- }
4109
- }
4110
- }
4111
- };
4112
- var getRelationWrapper = (meta, relationName, owner) => {
4113
- if (meta.relationWrappers.has(relationName)) {
4114
- return meta.relationWrappers.get(relationName);
4115
- }
4116
- const relation = meta.table.relations[relationName];
4117
- if (!relation) return void 0;
4118
- const wrapper = instantiateWrapper(meta, relationName, relation, owner);
4119
- if (wrapper) {
4120
- meta.relationWrappers.set(relationName, wrapper);
4121
- }
4122
- return wrapper;
4123
- };
4124
- var instantiateWrapper = (meta, relationName, relation, owner) => {
4125
- switch (relation.type) {
4126
- case RelationKinds.HasOne: {
4127
- const hasOne2 = relation;
4128
- const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
4129
- const loader = () => relationLoaderCache(
4130
- meta,
4131
- relationName,
4132
- () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
4133
- );
4134
- return new DefaultHasOneReference(
4135
- meta.ctx,
4136
- meta,
4137
- owner,
4138
- relationName,
4139
- hasOne2,
4140
- meta.table,
4141
- loader,
4142
- (row) => createEntityFromRow(meta.ctx, hasOne2.target, row),
4143
- localKey
4144
- );
4145
- }
4146
- case RelationKinds.HasMany: {
4147
- const hasMany2 = relation;
4148
- const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
4149
- const loader = () => relationLoaderCache(
4150
- meta,
4151
- relationName,
4152
- () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
4153
- );
4154
- return new DefaultHasManyCollection(
4155
- meta.ctx,
4156
- meta,
4157
- owner,
4158
- relationName,
4159
- hasMany2,
4160
- meta.table,
4161
- loader,
4162
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
4163
- localKey
4164
- );
4165
- }
4166
- case RelationKinds.BelongsTo: {
4167
- const belongsTo2 = relation;
4168
- const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
4169
- const loader = () => relationLoaderCache(
4170
- meta,
4171
- relationName,
4172
- () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
4173
- );
4174
- return new DefaultBelongsToReference(
4175
- meta.ctx,
4176
- meta,
4177
- owner,
4178
- relationName,
4179
- belongsTo2,
4180
- meta.table,
4181
- loader,
4182
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
4183
- targetKey
4184
- );
4185
- }
4186
- case RelationKinds.BelongsToMany: {
4187
- const many = relation;
4188
- const localKey = many.localKey || findPrimaryKey(meta.table);
4189
- const loader = () => relationLoaderCache(
4190
- meta,
4191
- relationName,
4192
- () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
4193
- );
4194
- return new DefaultManyToManyCollection(
4195
- meta.ctx,
4196
- meta,
4197
- owner,
4198
- relationName,
4199
- many,
4200
- meta.table,
4201
- loader,
4202
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
4203
- localKey
4204
- );
4205
- }
4206
- default:
4207
- return void 0;
4208
- }
4209
- };
4210
-
4211
- // src/orm/execute.ts
4212
- var flattenResults = (results) => {
4213
- const rows = [];
4214
- for (const result of results) {
4215
- const { columns, values } = result;
4216
- for (const valueRow of values) {
4217
- const row = {};
4218
- columns.forEach((column, idx) => {
4219
- row[column] = valueRow[idx];
4220
- });
4221
- rows.push(row);
4222
- }
4223
- }
4224
- return rows;
4225
- };
4226
- var executeWithEntityContext = async (entityCtx, qb) => {
4227
- const ast = qb.getAST();
4228
- const compiled = entityCtx.dialect.compileSelect(ast);
4229
- const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
4230
- const rows = flattenResults(executed);
4231
- if (ast.setOps && ast.setOps.length > 0) {
4232
- return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4233
- }
4234
- const hydrated = hydrateRows(rows, qb.getHydrationPlan());
4235
- return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4236
- };
4237
- async function executeHydrated(session, qb) {
4238
- return executeWithEntityContext(session, qb);
4239
- }
4240
- async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
4241
- const entityCtx = hydCtx.entityContext;
4242
- if (!entityCtx) {
4243
- throw new Error("Hydration context is missing an EntityContext");
4244
- }
4245
- return executeWithEntityContext(entityCtx, qb);
4246
- }
4247
-
4248
- // src/query-builder/select.ts
4249
- var SelectQueryBuilder = class _SelectQueryBuilder {
4250
- /**
4251
-
4252
- * Creates a new SelectQueryBuilder instance
4253
-
4254
- * @param table - Table definition to query
4255
-
4256
- * @param state - Optional initial query state
4257
-
4258
- * @param hydration - Optional hydration manager
4259
-
4260
- * @param dependencies - Optional query builder dependencies
4261
-
4262
- */
4263
- constructor(table, state, hydration, dependencies, lazyRelations) {
4264
- const deps = resolveSelectQueryBuilderDependencies(dependencies);
4265
- this.env = { table, deps };
4266
- const initialState = state ?? deps.createState(table);
4267
- const initialHydration = hydration ?? deps.createHydration(table);
4268
- this.context = {
4269
- state: initialState,
4270
- hydration: initialHydration
4271
- };
4272
- this.lazyRelations = new Set(lazyRelations ?? []);
4273
- this.columnSelector = new ColumnSelector(this.env);
4274
- this.relationManager = new RelationManager(this.env);
4275
- }
4276
- clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
4277
- return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
4278
- }
4279
- /**
4280
- * Applies an alias to the root FROM table.
4281
- * @param alias - Alias to apply
4282
- */
4283
- as(alias) {
4284
- const from = this.context.state.ast.from;
4285
- if (from.type !== "Table") {
4286
- throw new Error("Cannot alias non-table FROM sources");
4287
- }
4288
- const nextFrom = { ...from, alias };
4289
- const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
4290
- return this.clone(nextContext);
4291
- }
4292
- resolveQueryNode(query) {
4293
- return typeof query.getAST === "function" ? query.getAST() : query;
4294
- }
4295
- applyCorrelation(ast, correlation) {
4296
- if (!correlation) return ast;
4297
- const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
4298
- return {
4299
- ...ast,
4300
- where: combinedWhere
4301
- };
4302
- }
4303
- createChildBuilder(table) {
4304
- return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
4305
- }
4306
- applyAst(context, mutator) {
4307
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
4308
- const nextState = mutator(astService);
4309
- return { state: nextState, hydration: context.hydration };
4310
- }
4311
- applyJoin(context, table, condition, kind) {
4312
- const joinNode = createJoinNode(kind, table.name, condition);
4313
- return this.applyAst(context, (service) => service.withJoin(joinNode));
4314
- }
4315
- applySetOperation(operator, query) {
4316
- const subAst = this.resolveQueryNode(query);
4317
- return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
4318
- }
4319
- /**
4320
-
4321
- * Selects specific columns for the query
4322
-
4323
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4324
-
4325
- * @returns New query builder instance with selected columns
4326
-
4327
- */
4328
- select(columns) {
4329
- return this.clone(this.columnSelector.select(this.context, columns));
4330
- }
4331
- /**
4332
- * Selects columns from the root table by name (typed).
4333
- * @param cols - Column names on the root table
4334
- */
4335
- selectColumns(...cols) {
4336
- const selection = {};
4337
- for (const key of cols) {
4338
- const col = this.env.table.columns[key];
4339
- if (!col) {
4340
- throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4341
- }
4342
- selection[key] = col;
4343
- }
4344
- return this.select(selection);
4345
- }
4346
- /**
4347
-
4348
- * Selects raw column expressions
4349
-
4350
- * @param cols - Column expressions as strings
4351
-
4352
- * @returns New query builder instance with raw column selections
4353
-
4354
- */
4355
- selectRaw(...cols) {
4356
- return this.clone(this.columnSelector.selectRaw(this.context, cols));
4357
- }
4358
- /**
4359
-
4360
- * Adds a Common Table Expression (CTE) to the query
4361
-
4362
- * @param name - Name of the CTE
4363
-
4364
- * @param query - Query builder or query node for the CTE
4365
-
4366
- * @param columns - Optional column names for the CTE
4367
-
4368
- * @returns New query builder instance with the CTE
4369
-
4370
- */
4371
- with(name, query, columns) {
4372
- const subAst = this.resolveQueryNode(query);
4373
- const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
4374
- return this.clone(nextContext);
4375
- }
4376
- /**
4377
-
4378
- * Adds a recursive Common Table Expression (CTE) to the query
4379
-
4380
- * @param name - Name of the CTE
4381
-
4382
- * @param query - Query builder or query node for the CTE
4383
-
4384
- * @param columns - Optional column names for the CTE
4385
-
4386
- * @returns New query builder instance with the recursive CTE
4387
-
4388
- */
4389
- withRecursive(name, query, columns) {
4390
- const subAst = this.resolveQueryNode(query);
4391
- const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4392
- return this.clone(nextContext);
4393
- }
4394
- /**
4395
- * Replaces the FROM clause with a derived table (subquery with alias)
4396
- * @param subquery - Subquery to use as the FROM source
4397
- * @param alias - Alias for the derived table
4398
- * @param columnAliases - Optional column alias list
4399
- * @returns New query builder instance with updated FROM
4400
- */
4401
- fromSubquery(subquery, alias, columnAliases) {
4402
- const subAst = this.resolveQueryNode(subquery);
4403
- const fromNode = derivedTable(subAst, alias, columnAliases);
4404
- const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
4405
- return this.clone(nextContext);
4406
- }
4407
- /**
4408
-
4409
- * Selects a subquery as a column
4410
-
4411
- * @param alias - Alias for the subquery column
4412
-
4413
- * @param sub - Query builder or query node for the subquery
4414
-
4415
- * @returns New query builder instance with the subquery selection
4416
-
4417
- */
4418
- selectSubquery(alias, sub) {
4419
- const query = this.resolveQueryNode(sub);
4420
- return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4421
- }
4422
- /**
4423
- * Adds a JOIN against a derived table (subquery with alias)
4424
- * @param subquery - Subquery to join
4425
- * @param alias - Alias for the derived table
4426
- * @param condition - Join condition expression
4427
- * @param joinKind - Join kind (defaults to INNER)
4428
- * @param columnAliases - Optional column alias list for the derived table
4429
- * @returns New query builder instance with the derived-table join
4430
- */
4431
- joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
4432
- const subAst = this.resolveQueryNode(subquery);
4433
- const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
4434
- const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
4435
- return this.clone(nextContext);
4436
- }
4437
- /**
4438
-
4439
- * Adds an INNER JOIN to the query
4440
-
4441
- * @param table - Table to join
4442
-
4443
- * @param condition - Join condition expression
4444
-
4445
- * @returns New query builder instance with the INNER JOIN
4446
-
4447
- */
4448
- innerJoin(table, condition) {
4449
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
4450
- return this.clone(nextContext);
4451
- }
4452
- /**
4453
-
4454
- * Adds a LEFT JOIN to the query
4455
-
4456
- * @param table - Table to join
4457
-
4458
- * @param condition - Join condition expression
4459
-
4460
- * @returns New query builder instance with the LEFT JOIN
4461
-
4462
- */
4463
- leftJoin(table, condition) {
4464
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
4465
- return this.clone(nextContext);
4466
- }
4467
- /**
4468
-
4469
- * Adds a RIGHT JOIN to the query
4470
-
4471
- * @param table - Table to join
4472
-
4473
- * @param condition - Join condition expression
4474
-
4475
- * @returns New query builder instance with the RIGHT JOIN
4476
-
4477
- */
4478
- rightJoin(table, condition) {
4479
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
4480
- return this.clone(nextContext);
4481
- }
4482
- /**
4483
-
4484
- * Matches records based on a relationship
4485
-
4486
- * @param relationName - Name of the relationship to match
4487
-
4488
- * @param predicate - Optional predicate expression
4489
-
4490
- * @returns New query builder instance with the relationship match
4491
-
4492
- */
4493
- match(relationName, predicate) {
4494
- const nextContext = this.relationManager.match(this.context, relationName, predicate);
4495
- return this.clone(nextContext);
4496
- }
4497
- /**
4498
-
4499
- * Joins a related table
4500
-
4501
- * @param relationName - Name of the relationship to join
4502
-
4503
- * @param joinKind - Type of join (defaults to INNER)
4504
-
4505
- * @param extraCondition - Optional additional join condition
4506
-
4507
- * @returns New query builder instance with the relationship join
4508
-
4509
- */
4510
- joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
4511
- const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
4512
- return this.clone(nextContext);
4513
- }
4514
- /**
4515
-
4516
- * Includes related data in the query results
4517
-
4518
- * @param relationName - Name of the relationship to include
4519
-
4520
- * @param options - Optional include options
4521
-
4522
- * @returns New query builder instance with the relationship inclusion
4523
-
4524
- */
4525
- include(relationName, options) {
4526
- const nextContext = this.relationManager.include(this.context, relationName, options);
4527
- return this.clone(nextContext);
4528
- }
4529
- includeLazy(relationName) {
4530
- const nextLazy = new Set(this.lazyRelations);
4531
- nextLazy.add(relationName);
4532
- return this.clone(this.context, nextLazy);
4533
- }
4534
- /**
4535
- * Selects columns for a related table in a single hop.
4536
- */
4537
- selectRelationColumns(relationName, ...cols) {
4538
- const relation = this.env.table.relations[relationName];
4539
- if (!relation) {
4540
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4541
- }
4542
- const target = relation.target;
4543
- for (const col of cols) {
4544
- if (!target.columns[col]) {
4545
- throw new Error(
4546
- `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
4547
- );
4548
- }
4549
- }
4550
- return this.include(relationName, { columns: cols });
4551
- }
4552
- /**
4553
- * Convenience alias for selecting specific columns from a relation.
4554
- */
4555
- includePick(relationName, cols) {
4556
- return this.selectRelationColumns(relationName, ...cols);
4557
- }
4558
- /**
4559
- * Selects columns for the root table and relations from a single config object.
4560
- */
4561
- selectColumnsDeep(config) {
4562
- let qb = this;
4563
- if (config.root?.length) {
4564
- qb = qb.selectColumns(...config.root);
4565
- }
4566
- for (const key of Object.keys(config)) {
4567
- if (key === "root") continue;
4568
- const relName = key;
4569
- const cols = config[relName];
4570
- if (!cols || !cols.length) continue;
4571
- qb = qb.selectRelationColumns(relName, ...cols);
4572
- }
4573
- return qb;
4574
- }
4575
- getLazyRelations() {
4576
- return Array.from(this.lazyRelations);
4577
- }
4578
- getTable() {
4579
- return this.env.table;
4580
- }
4581
- async execute(ctx) {
4582
- return executeHydrated(ctx, this);
4583
- }
4584
- async executeWithContexts(execCtx, hydCtx) {
4585
- return executeHydratedWithContexts(execCtx, hydCtx, this);
4586
- }
4587
- /**
4588
-
4589
- * Adds a WHERE condition to the query
4590
-
4591
- * @param expr - Expression for the WHERE clause
4592
-
4593
- * @returns New query builder instance with the WHERE condition
4594
-
4595
- */
4596
- where(expr) {
4597
- const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
4598
- return this.clone(nextContext);
4599
- }
4600
- /**
4601
-
4602
- * Adds a GROUP BY clause to the query
4603
-
4604
- * @param col - Column definition or column node to group by
4605
-
4606
- * @returns New query builder instance with the GROUP BY clause
4607
-
4608
- */
4609
- groupBy(col) {
4610
- const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col));
4611
- return this.clone(nextContext);
4612
- }
4613
- /**
4614
-
4615
- * Adds a HAVING condition to the query
4616
-
4617
- * @param expr - Expression for the HAVING clause
4618
-
4619
- * @returns New query builder instance with the HAVING condition
4620
-
4621
- */
4622
- having(expr) {
4623
- const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
4624
- return this.clone(nextContext);
4625
- }
4626
- /**
4627
-
4628
- * Adds an ORDER BY clause to the query
4629
-
4630
- * @param col - Column definition or column node to order by
4631
-
4632
- * @param direction - Order direction (defaults to ASC)
4633
-
4634
- * @returns New query builder instance with the ORDER BY clause
4635
-
4636
- */
4637
- orderBy(col, direction = ORDER_DIRECTIONS.ASC) {
4638
- const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col, direction));
4639
- return this.clone(nextContext);
4640
- }
4641
- /**
4642
-
4643
- * Adds a DISTINCT clause to the query
4644
-
4645
- * @param cols - Columns to make distinct
4646
-
4647
- * @returns New query builder instance with the DISTINCT clause
4648
-
4649
- */
4650
- distinct(...cols) {
4651
- return this.clone(this.columnSelector.distinct(this.context, cols));
4652
- }
4653
- /**
4654
-
4655
- * Adds a LIMIT clause to the query
4656
-
4657
- * @param n - Maximum number of rows to return
4658
-
4659
- * @returns New query builder instance with the LIMIT clause
4660
-
4661
- */
4662
- limit(n) {
4663
- const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
4664
- return this.clone(nextContext);
4665
- }
4666
- /**
4667
-
4668
- * Adds an OFFSET clause to the query
4669
-
4670
- * @param n - Number of rows to skip
4671
-
4672
- * @returns New query builder instance with the OFFSET clause
4673
-
4674
- */
4675
- offset(n) {
4676
- const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
4677
- return this.clone(nextContext);
4678
- }
4679
- /**
4680
-
4681
- * Combines this query with another using UNION
4682
-
4683
- * @param query - Query to union with
4684
-
4685
- * @returns New query builder instance with the set operation
4686
-
4687
- */
4688
- union(query) {
4689
- return this.clone(this.applySetOperation("UNION", query));
4690
- }
4691
- /**
4692
-
4693
- * Combines this query with another using UNION ALL
4694
-
4695
- * @param query - Query to union with
4696
-
4697
- * @returns New query builder instance with the set operation
4698
-
4699
- */
4700
- unionAll(query) {
4701
- return this.clone(this.applySetOperation("UNION ALL", query));
4702
- }
4703
- /**
4704
-
4705
- * Combines this query with another using INTERSECT
4706
-
4707
- * @param query - Query to intersect with
4708
-
4709
- * @returns New query builder instance with the set operation
4710
-
4711
- */
4712
- intersect(query) {
4713
- return this.clone(this.applySetOperation("INTERSECT", query));
4714
- }
4715
- /**
4716
-
4717
- * Combines this query with another using EXCEPT
4718
-
4719
- * @param query - Query to subtract
4720
-
4721
- * @returns New query builder instance with the set operation
4722
-
4723
- */
4724
- except(query) {
4725
- return this.clone(this.applySetOperation("EXCEPT", query));
4726
- }
4727
- /**
4728
-
4729
- * Adds a WHERE EXISTS condition to the query
4730
-
4731
- * @param subquery - Subquery to check for existence
4732
-
4733
- * @returns New query builder instance with the WHERE EXISTS condition
4734
-
4735
- */
4736
- whereExists(subquery, correlate) {
4737
- const subAst = this.resolveQueryNode(subquery);
4738
- const correlated = this.applyCorrelation(subAst, correlate);
4739
- return this.where(exists(correlated));
4740
- }
4741
- /**
4742
-
4743
- * Adds a WHERE NOT EXISTS condition to the query
4744
-
4745
- * @param subquery - Subquery to check for non-existence
4746
-
4747
- * @returns New query builder instance with the WHERE NOT EXISTS condition
4748
-
4749
- */
4750
- whereNotExists(subquery, correlate) {
4751
- const subAst = this.resolveQueryNode(subquery);
4752
- const correlated = this.applyCorrelation(subAst, correlate);
4753
- return this.where(notExists(correlated));
4754
- }
4755
- /**
4756
-
4757
- * Adds a WHERE EXISTS condition based on a relationship
4758
-
4759
- * @param relationName - Name of the relationship to check
4760
-
4761
- * @param callback - Optional callback to modify the relationship query
4762
-
4763
- * @returns New query builder instance with the relationship existence check
4764
-
4765
- */
4766
- whereHas(relationName, callbackOrOptions, maybeOptions) {
4767
- const relation = this.env.table.relations[relationName];
4768
- if (!relation) {
4769
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4770
- }
4771
- const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4772
- const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4773
- let subQb = this.createChildBuilder(relation.target);
4774
- if (callback) {
4775
- subQb = callback(subQb);
4776
- }
4777
- const subAst = subQb.getAST();
4778
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4779
- return this.where(exists(finalSubAst));
4780
- }
4781
- /**
4782
-
4783
- * Adds a WHERE NOT EXISTS condition based on a relationship
4784
-
4785
- * @param relationName - Name of the relationship to check
4786
-
4787
- * @param callback - Optional callback to modify the relationship query
4788
-
4789
- * @returns New query builder instance with the relationship non-existence check
4790
-
4791
- */
4792
- whereHasNot(relationName, callbackOrOptions, maybeOptions) {
4793
- const relation = this.env.table.relations[relationName];
4794
- if (!relation) {
4795
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4796
- }
4797
- const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4798
- const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4799
- let subQb = this.createChildBuilder(relation.target);
4800
- if (callback) {
4801
- subQb = callback(subQb);
4802
- }
4803
- const subAst = subQb.getAST();
4804
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4805
- return this.where(notExists(finalSubAst));
4806
- }
4807
- /**
4808
-
4809
- * Compiles the query to SQL for a specific dialect
4810
-
4811
- * @param dialect - Database dialect to compile for
4812
-
4813
- * @returns Compiled query with SQL and parameters
4814
-
4815
- */
4816
- compile(dialect) {
4817
- const resolved = resolveDialectInput(dialect);
4818
- return resolved.compileSelect(this.context.state.ast);
4819
- }
4820
- /**
4821
-
4822
- * Converts the query to SQL string for a specific dialect
4823
-
4824
- * @param dialect - Database dialect to generate SQL for
4825
-
4826
- * @returns SQL string representation of the query
4827
-
4828
- */
4829
- toSql(dialect) {
4830
- return this.compile(dialect).sql;
4831
- }
4832
- /**
4833
-
4834
- * Gets the hydration plan for the query
4835
-
4836
- * @returns Hydration plan or undefined if none exists
4837
-
4838
- */
4839
- getHydrationPlan() {
4840
- return this.context.hydration.getPlan();
4841
- }
4842
- /**
4843
-
4844
- * Gets the Abstract Syntax Tree (AST) representation of the query
4845
-
4846
- * @returns Query AST with hydration applied
4847
-
4848
- */
4849
- getAST() {
4850
- return this.context.hydration.applyToAst(this.context.state.ast);
4851
- }
4852
- };
4853
-
4854
- // src/decorators/bootstrap.ts
4855
- var isTableDef = (value) => {
4856
- return typeof value === "object" && value !== null && "columns" in value;
4857
- };
4858
- var unwrapTarget = (target) => {
4859
- if (typeof target === "function" && target.prototype === void 0) {
4860
- return target();
4861
- }
4862
- return target;
4863
- };
4864
- var resolveTableTarget = (target, tableMap) => {
4865
- const resolved = unwrapTarget(target);
4866
- if (isTableDef(resolved)) {
4867
- return resolved;
4868
- }
4869
- const table = tableMap.get(resolved);
4870
- if (!table) {
4871
- throw new Error(`Entity '${resolved.name}' is not registered with decorators`);
4872
- }
4873
- return table;
4874
- };
4875
- var buildRelationDefinitions = (meta, tableMap) => {
4876
- const relations = {};
4877
- for (const [name, relation] of Object.entries(meta.relations)) {
4878
- switch (relation.kind) {
4879
- case RelationKinds.HasOne: {
4880
- relations[name] = hasOne(
4881
- resolveTableTarget(relation.target, tableMap),
4882
- relation.foreignKey,
4883
- relation.localKey,
4884
- relation.cascade
4885
- );
4886
- break;
4887
- }
4888
- case RelationKinds.HasMany: {
4889
- relations[name] = hasMany(
4890
- resolveTableTarget(relation.target, tableMap),
4891
- relation.foreignKey,
4892
- relation.localKey,
4893
- relation.cascade
4894
- );
4895
- break;
4896
- }
4897
- case RelationKinds.BelongsTo: {
4898
- relations[name] = belongsTo(
4899
- resolveTableTarget(relation.target, tableMap),
4900
- relation.foreignKey,
4901
- relation.localKey,
4902
- relation.cascade
4903
- );
4904
- break;
4905
- }
4906
- case RelationKinds.BelongsToMany: {
4907
- relations[name] = belongsToMany(
4908
- resolveTableTarget(relation.target, tableMap),
4909
- resolveTableTarget(relation.pivotTable, tableMap),
4910
- {
4911
- pivotForeignKeyToRoot: relation.pivotForeignKeyToRoot,
4912
- pivotForeignKeyToTarget: relation.pivotForeignKeyToTarget,
4913
- localKey: relation.localKey,
4914
- targetKey: relation.targetKey,
4915
- pivotPrimaryKey: relation.pivotPrimaryKey,
4916
- defaultPivotColumns: relation.defaultPivotColumns,
4917
- cascade: relation.cascade
4918
- }
4919
- );
4920
- break;
4921
- }
4922
- }
4923
- }
4924
- return relations;
4925
- };
4926
- var bootstrapEntities = () => {
4927
- const metas = getAllEntityMetadata();
4928
- const tableMap = /* @__PURE__ */ new Map();
4929
- for (const meta of metas) {
4930
- const table = buildTableDef(meta);
4931
- tableMap.set(meta.target, table);
4932
- }
4933
- for (const meta of metas) {
4934
- const table = meta.table;
4935
- const relations = buildRelationDefinitions(meta, tableMap);
4936
- table.relations = relations;
4937
- }
4938
- return metas.map((meta) => meta.table);
4939
- };
4940
- var getTableDefFromEntity = (ctor) => {
4941
- const meta = getEntityMetadata(ctor);
4942
- if (!meta) return void 0;
4943
- if (!meta.table) {
4944
- bootstrapEntities();
4945
- }
4946
- return meta.table;
4947
- };
4948
- var selectFromEntity = (ctor) => {
4949
- const table = getTableDefFromEntity(ctor);
4950
- if (!table) {
4951
- throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
4952
- }
4953
- return new SelectQueryBuilder(table);
4954
- };
4955
- // Annotate the CommonJS export names for ESM import in node:
4956
- 0 && (module.exports = {
4957
- BelongsTo,
4958
- BelongsToMany,
4959
- Column,
4960
- Entity,
4961
- HasMany,
4962
- HasOne,
4963
- PrimaryKey,
4964
- bootstrapEntities,
4965
- getTableDefFromEntity,
4966
- selectFromEntity
4967
- });
4968
- //# sourceMappingURL=index.cjs.map