metal-orm 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +69 -67
  2. package/dist/decorators/index.cjs +1983 -224
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +6 -6
  5. package/dist/decorators/index.d.ts +6 -6
  6. package/dist/decorators/index.js +1982 -224
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +5284 -3751
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +524 -169
  11. package/dist/index.d.ts +524 -169
  12. package/dist/index.js +5197 -3736
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
  15. package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +19 -21
  19. package/src/core/ast/adapters.ts +21 -0
  20. package/src/core/ast/aggregate-functions.ts +13 -13
  21. package/src/core/ast/builders.ts +56 -43
  22. package/src/core/ast/expression-builders.ts +34 -34
  23. package/src/core/ast/expression-nodes.ts +18 -16
  24. package/src/core/ast/expression-visitor.ts +122 -69
  25. package/src/core/ast/expression.ts +6 -4
  26. package/src/core/ast/join-metadata.ts +15 -0
  27. package/src/core/ast/join-node.ts +22 -20
  28. package/src/core/ast/join.ts +5 -5
  29. package/src/core/ast/query.ts +52 -88
  30. package/src/core/ast/types.ts +20 -0
  31. package/src/core/ast/window-functions.ts +55 -55
  32. package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
  33. package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
  34. package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
  35. package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
  37. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
  39. package/src/core/ddl/introspect/context.ts +9 -0
  40. package/src/core/ddl/introspect/functions/postgres.ts +26 -0
  41. package/src/core/ddl/introspect/mssql.ts +149 -149
  42. package/src/core/ddl/introspect/mysql.ts +99 -99
  43. package/src/core/ddl/introspect/postgres.ts +245 -154
  44. package/src/core/ddl/introspect/registry.ts +26 -0
  45. package/src/core/ddl/introspect/run-select.ts +25 -0
  46. package/src/core/ddl/introspect/sqlite.ts +7 -7
  47. package/src/core/ddl/introspect/types.ts +23 -19
  48. package/src/core/ddl/introspect/utils.ts +1 -1
  49. package/src/core/ddl/naming-strategy.ts +10 -0
  50. package/src/core/ddl/schema-dialect.ts +41 -0
  51. package/src/core/ddl/schema-diff.ts +211 -179
  52. package/src/core/ddl/schema-generator.ts +17 -90
  53. package/src/core/ddl/schema-introspect.ts +25 -32
  54. package/src/core/ddl/schema-plan-executor.ts +17 -0
  55. package/src/core/ddl/schema-types.ts +46 -39
  56. package/src/core/ddl/sql-writing.ts +170 -0
  57. package/src/core/dialect/abstract.ts +172 -126
  58. package/src/core/dialect/base/cte-compiler.ts +33 -0
  59. package/src/core/dialect/base/function-table-formatter.ts +132 -0
  60. package/src/core/dialect/base/groupby-compiler.ts +21 -0
  61. package/src/core/dialect/base/join-compiler.ts +26 -0
  62. package/src/core/dialect/base/orderby-compiler.ts +21 -0
  63. package/src/core/dialect/base/pagination-strategy.ts +32 -0
  64. package/src/core/dialect/base/returning-strategy.ts +56 -0
  65. package/src/core/dialect/base/sql-dialect.ts +181 -204
  66. package/src/core/dialect/dialect-factory.ts +91 -0
  67. package/src/core/dialect/mssql/functions.ts +101 -0
  68. package/src/core/dialect/mssql/index.ts +128 -126
  69. package/src/core/dialect/mysql/functions.ts +101 -0
  70. package/src/core/dialect/mysql/index.ts +20 -18
  71. package/src/core/dialect/postgres/functions.ts +95 -0
  72. package/src/core/dialect/postgres/index.ts +30 -28
  73. package/src/core/dialect/sqlite/functions.ts +115 -0
  74. package/src/core/dialect/sqlite/index.ts +30 -28
  75. package/src/core/driver/database-driver.ts +11 -0
  76. package/src/core/driver/mssql-driver.ts +20 -0
  77. package/src/core/driver/mysql-driver.ts +20 -0
  78. package/src/core/driver/postgres-driver.ts +20 -0
  79. package/src/core/driver/sqlite-driver.ts +20 -0
  80. package/src/core/execution/db-executor.ts +63 -0
  81. package/src/core/execution/executors/mssql-executor.ts +39 -0
  82. package/src/core/execution/executors/mysql-executor.ts +47 -0
  83. package/src/core/execution/executors/postgres-executor.ts +32 -0
  84. package/src/core/execution/executors/sqlite-executor.ts +31 -0
  85. package/src/core/functions/datetime.ts +132 -0
  86. package/src/core/functions/numeric.ts +179 -0
  87. package/src/core/functions/standard-strategy.ts +47 -0
  88. package/src/core/functions/text.ts +147 -0
  89. package/src/core/functions/types.ts +18 -0
  90. package/src/core/hydration/types.ts +57 -0
  91. package/src/decorators/bootstrap.ts +10 -0
  92. package/src/decorators/column.ts +13 -4
  93. package/src/decorators/relations.ts +15 -0
  94. package/src/index.ts +37 -19
  95. package/src/orm/entity-context.ts +30 -0
  96. package/src/orm/entity-meta.ts +2 -2
  97. package/src/orm/entity-metadata.ts +8 -6
  98. package/src/orm/entity.ts +72 -41
  99. package/src/orm/execute.ts +42 -25
  100. package/src/orm/execution-context.ts +12 -0
  101. package/src/orm/hydration-context.ts +14 -0
  102. package/src/orm/hydration.ts +25 -17
  103. package/src/orm/identity-map.ts +4 -0
  104. package/src/orm/interceptor-pipeline.ts +29 -0
  105. package/src/orm/lazy-batch.ts +50 -6
  106. package/src/orm/orm-session.ts +234 -0
  107. package/src/orm/orm.ts +58 -0
  108. package/src/orm/query-logger.ts +1 -1
  109. package/src/orm/relation-change-processor.ts +48 -3
  110. package/src/orm/relations/belongs-to.ts +45 -44
  111. package/src/orm/relations/has-many.ts +44 -43
  112. package/src/orm/relations/has-one.ts +140 -0
  113. package/src/orm/relations/many-to-many.ts +46 -45
  114. package/src/orm/transaction-runner.ts +1 -1
  115. package/src/orm/unit-of-work.ts +66 -61
  116. package/src/query-builder/delete.ts +22 -5
  117. package/src/query-builder/hydration-manager.ts +2 -1
  118. package/src/query-builder/hydration-planner.ts +8 -7
  119. package/src/query-builder/insert.ts +22 -5
  120. package/src/query-builder/relation-conditions.ts +9 -8
  121. package/src/query-builder/relation-service.ts +3 -2
  122. package/src/query-builder/select.ts +575 -64
  123. package/src/query-builder/update.ts +22 -5
  124. package/src/schema/column.ts +246 -246
  125. package/src/schema/relation.ts +35 -1
  126. package/src/schema/table.ts +28 -28
  127. package/src/schema/types.ts +41 -31
  128. package/src/orm/db-executor.ts +0 -11
  129. package/src/orm/orm-context.ts +0 -159
@@ -100,11 +100,20 @@ function Entity(options = {}) {
100
100
 
101
101
  // src/decorators/column.ts
102
102
  var normalizeColumnInput = (input) => {
103
+ const asOptions = input;
104
+ const asDefinition = input;
103
105
  const column = {
104
- type: input.type ?? input.type,
105
- args: input.args ?? input.args,
106
- notNull: input.notNull ?? input.notNull,
107
- primary: input.primary ?? input.primary
106
+ type: asOptions.type ?? asDefinition.type,
107
+ args: asOptions.args ?? asDefinition.args,
108
+ notNull: asOptions.notNull ?? asDefinition.notNull,
109
+ primary: asOptions.primary ?? asDefinition.primary,
110
+ unique: asDefinition.unique,
111
+ default: asDefinition.default,
112
+ autoIncrement: asDefinition.autoIncrement,
113
+ generated: asDefinition.generated,
114
+ check: asDefinition.check,
115
+ references: asDefinition.references,
116
+ comment: asDefinition.comment
108
117
  };
109
118
  if (!column.type) {
110
119
  throw new Error("Column decorator requires a column type");
@@ -153,6 +162,8 @@ function PrimaryKey(definition) {
153
162
 
154
163
  // src/schema/relation.ts
155
164
  var RelationKinds = {
165
+ /** One-to-one relationship */
166
+ HasOne: "HAS_ONE",
156
167
  /** One-to-many relationship */
157
168
  HasMany: "HAS_MANY",
158
169
  /** Many-to-one relationship */
@@ -167,6 +178,13 @@ var hasMany = (target, foreignKey, localKey, cascade) => ({
167
178
  localKey,
168
179
  cascade
169
180
  });
181
+ var hasOne = (target, foreignKey, localKey, cascade) => ({
182
+ type: RelationKinds.HasOne,
183
+ target,
184
+ foreignKey,
185
+ localKey,
186
+ cascade
187
+ });
170
188
  var belongsTo = (target, foreignKey, localKey, cascade) => ({
171
189
  type: RelationKinds.BelongsTo,
172
190
  target,
@@ -227,6 +245,16 @@ function HasMany(options) {
227
245
  cascade: options.cascade
228
246
  }));
229
247
  }
248
+ function HasOne(options) {
249
+ return createFieldDecorator((propertyName) => ({
250
+ kind: RelationKinds.HasOne,
251
+ propertyKey: propertyName,
252
+ target: options.target,
253
+ foreignKey: options.foreignKey,
254
+ localKey: options.localKey,
255
+ cascade: options.cascade
256
+ }));
257
+ }
230
258
  function BelongsTo(options) {
231
259
  return createFieldDecorator((propertyName) => ({
232
260
  kind: RelationKinds.BelongsTo,
@@ -284,54 +312,1436 @@ var toOperand = (val) => {
284
312
  if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
285
313
  return { type: "Literal", value: val };
286
314
  }
287
- return toNode(val);
315
+ return toNode(val);
316
+ };
317
+ var columnOperand = (col) => toNode(col);
318
+ var createBinaryExpression = (operator, left, right, escape) => {
319
+ const node = {
320
+ type: "BinaryExpression",
321
+ left: toNode(left),
322
+ operator,
323
+ right: toOperand(right)
324
+ };
325
+ if (escape !== void 0) {
326
+ node.escape = toLiteralNode(escape);
327
+ }
328
+ return node;
329
+ };
330
+ var eq = (left, right) => createBinaryExpression("=", left, right);
331
+ var and = (...operands) => ({
332
+ type: "LogicalExpression",
333
+ operator: "AND",
334
+ operands
335
+ });
336
+ var createInExpression = (operator, left, values) => ({
337
+ type: "InExpression",
338
+ left: toNode(left),
339
+ operator,
340
+ right: values.map((v) => toOperand(v))
341
+ });
342
+ var inList = (left, values) => createInExpression("IN", left, values);
343
+ var exists = (subquery) => ({
344
+ type: "ExistsExpression",
345
+ operator: "EXISTS",
346
+ subquery
347
+ });
348
+ var notExists = (subquery) => ({
349
+ type: "ExistsExpression",
350
+ operator: "NOT EXISTS",
351
+ subquery
352
+ });
353
+
354
+ // src/core/ast/aggregate-functions.ts
355
+ var buildAggregate = (name) => (col) => ({
356
+ type: "Function",
357
+ name,
358
+ args: [columnOperand(col)]
359
+ });
360
+ var count = buildAggregate("COUNT");
361
+ var sum = buildAggregate("SUM");
362
+ var avg = buildAggregate("AVG");
363
+
364
+ // src/core/functions/standard-strategy.ts
365
+ var StandardFunctionStrategy = class {
366
+ constructor() {
367
+ this.renderers = /* @__PURE__ */ new Map();
368
+ this.registerStandard();
369
+ }
370
+ registerStandard() {
371
+ this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
372
+ this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
373
+ this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
374
+ this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
375
+ this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
376
+ this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
377
+ this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
378
+ this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
379
+ this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
380
+ this.add("NOW", () => `NOW()`);
381
+ this.add("CURRENT_DATE", () => `CURRENT_DATE`);
382
+ this.add("CURRENT_TIME", () => `CURRENT_TIME`);
383
+ this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
384
+ this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
385
+ this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
386
+ this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
387
+ this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
388
+ this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
389
+ this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
390
+ this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
391
+ this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
392
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
393
+ this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
394
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
395
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
396
+ this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
397
+ }
398
+ add(name, renderer) {
399
+ this.renderers.set(name, renderer);
400
+ }
401
+ getRenderer(name) {
402
+ return this.renderers.get(name);
403
+ }
404
+ };
405
+
406
+ // src/core/dialect/abstract.ts
407
+ var Dialect = class _Dialect {
408
+ /**
409
+ * Compiles a SELECT query AST to SQL
410
+ * @param ast - Query AST to compile
411
+ * @returns Compiled query with SQL and parameters
412
+ */
413
+ compileSelect(ast) {
414
+ const ctx = this.createCompilerContext();
415
+ const normalized = this.normalizeSelectAst(ast);
416
+ const rawSql = this.compileSelectAst(normalized, ctx).trim();
417
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
418
+ return {
419
+ sql,
420
+ params: [...ctx.params]
421
+ };
422
+ }
423
+ compileInsert(ast) {
424
+ const ctx = this.createCompilerContext();
425
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
426
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
427
+ return {
428
+ sql,
429
+ params: [...ctx.params]
430
+ };
431
+ }
432
+ compileUpdate(ast) {
433
+ const ctx = this.createCompilerContext();
434
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
435
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
436
+ return {
437
+ sql,
438
+ params: [...ctx.params]
439
+ };
440
+ }
441
+ compileDelete(ast) {
442
+ const ctx = this.createCompilerContext();
443
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
444
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
445
+ return {
446
+ sql,
447
+ params: [...ctx.params]
448
+ };
449
+ }
450
+ supportsReturning() {
451
+ return false;
452
+ }
453
+ /**
454
+ * Compiles a WHERE clause
455
+ * @param where - WHERE expression
456
+ * @param ctx - Compiler context
457
+ * @returns SQL WHERE clause or empty string
458
+ */
459
+ compileWhere(where, ctx) {
460
+ if (!where) return "";
461
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
462
+ }
463
+ compileReturning(returning, ctx) {
464
+ if (!returning || returning.length === 0) return "";
465
+ throw new Error("RETURNING is not supported by this dialect.");
466
+ }
467
+ /**
468
+ * Generates subquery for EXISTS expressions
469
+ * Rule: Always forces SELECT 1, ignoring column list
470
+ * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
471
+ * Does not add ';' at the end
472
+ * @param ast - Query AST
473
+ * @param ctx - Compiler context
474
+ * @returns SQL for EXISTS subquery
475
+ */
476
+ compileSelectForExists(ast, ctx) {
477
+ const normalized = this.normalizeSelectAst(ast);
478
+ const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
479
+ if (normalized.setOps && normalized.setOps.length > 0) {
480
+ return `SELECT 1 FROM (${full}) AS _exists`;
481
+ }
482
+ const upper = full.toUpperCase();
483
+ const fromIndex = upper.indexOf(" FROM ");
484
+ if (fromIndex === -1) {
485
+ return full;
486
+ }
487
+ const tail = full.slice(fromIndex);
488
+ return `SELECT 1${tail}`;
489
+ }
490
+ /**
491
+ * Creates a new compiler context
492
+ * @returns Compiler context with parameter management
493
+ */
494
+ createCompilerContext() {
495
+ const params = [];
496
+ let counter = 0;
497
+ return {
498
+ params,
499
+ addParameter: (value) => {
500
+ counter += 1;
501
+ params.push(value);
502
+ return this.formatPlaceholder(counter);
503
+ }
504
+ };
505
+ }
506
+ /**
507
+ * Formats a parameter placeholder
508
+ * @param index - Parameter index
509
+ * @returns Formatted placeholder string
510
+ */
511
+ formatPlaceholder(index) {
512
+ return "?";
513
+ }
514
+ /**
515
+ * Whether the current dialect supports a given set operation.
516
+ * Override in concrete dialects to restrict support.
517
+ */
518
+ supportsSetOperation(kind) {
519
+ return true;
520
+ }
521
+ /**
522
+ * Validates set-operation semantics:
523
+ * - Ensures the dialect supports requested operators.
524
+ * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
525
+ * @param ast - Query to validate
526
+ * @param isOutermost - Whether this node is the outermost compound query
527
+ */
528
+ validateSetOperations(ast, isOutermost = true) {
529
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
530
+ if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
531
+ throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
532
+ }
533
+ if (hasSetOps) {
534
+ for (const op of ast.setOps) {
535
+ if (!this.supportsSetOperation(op.operator)) {
536
+ throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
537
+ }
538
+ this.validateSetOperations(op.query, false);
539
+ }
540
+ }
541
+ }
542
+ /**
543
+ * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
544
+ * @param ast - Query AST
545
+ * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
546
+ */
547
+ hoistCtes(ast) {
548
+ let hoisted = [];
549
+ const normalizedSetOps = ast.setOps?.map((op) => {
550
+ const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
551
+ const childCtes = child.ctes ?? [];
552
+ if (childCtes.length) {
553
+ hoisted = hoisted.concat(childCtes);
554
+ }
555
+ hoisted = hoisted.concat(childHoisted);
556
+ const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
557
+ return { ...op, query: queryWithoutCtes };
558
+ });
559
+ const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
560
+ return { normalized, hoistedCtes: hoisted };
561
+ }
562
+ /**
563
+ * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
564
+ * @param ast - Query AST
565
+ * @returns Normalized query AST
566
+ */
567
+ normalizeSelectAst(ast) {
568
+ this.validateSetOperations(ast, true);
569
+ const { normalized, hoistedCtes } = this.hoistCtes(ast);
570
+ const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
571
+ return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
572
+ }
573
+ constructor(functionStrategy) {
574
+ this.expressionCompilers = /* @__PURE__ */ new Map();
575
+ this.operandCompilers = /* @__PURE__ */ new Map();
576
+ this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
577
+ this.registerDefaultOperandCompilers();
578
+ this.registerDefaultExpressionCompilers();
579
+ }
580
+ /**
581
+ * Creates a new Dialect instance (for testing purposes)
582
+ * @param functionStrategy - Optional function strategy
583
+ * @returns New Dialect instance
584
+ */
585
+ static create(functionStrategy) {
586
+ class TestDialect extends _Dialect {
587
+ constructor() {
588
+ super(...arguments);
589
+ this.dialect = "sqlite";
590
+ }
591
+ quoteIdentifier(id) {
592
+ return `"${id}"`;
593
+ }
594
+ compileSelectAst() {
595
+ throw new Error("Not implemented");
596
+ }
597
+ compileInsertAst() {
598
+ throw new Error("Not implemented");
599
+ }
600
+ compileUpdateAst() {
601
+ throw new Error("Not implemented");
602
+ }
603
+ compileDeleteAst() {
604
+ throw new Error("Not implemented");
605
+ }
606
+ }
607
+ return new TestDialect(functionStrategy);
608
+ }
609
+ /**
610
+ * Registers an expression compiler for a specific node type
611
+ * @param type - Expression node type
612
+ * @param compiler - Compiler function
613
+ */
614
+ registerExpressionCompiler(type, compiler) {
615
+ this.expressionCompilers.set(type, compiler);
616
+ }
617
+ /**
618
+ * Registers an operand compiler for a specific node type
619
+ * @param type - Operand node type
620
+ * @param compiler - Compiler function
621
+ */
622
+ registerOperandCompiler(type, compiler) {
623
+ this.operandCompilers.set(type, compiler);
624
+ }
625
+ /**
626
+ * Compiles an expression node
627
+ * @param node - Expression node to compile
628
+ * @param ctx - Compiler context
629
+ * @returns Compiled SQL expression
630
+ */
631
+ compileExpression(node, ctx) {
632
+ const compiler = this.expressionCompilers.get(node.type);
633
+ if (!compiler) {
634
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
635
+ }
636
+ return compiler(node, ctx);
637
+ }
638
+ /**
639
+ * Compiles an operand node
640
+ * @param node - Operand node to compile
641
+ * @param ctx - Compiler context
642
+ * @returns Compiled SQL operand
643
+ */
644
+ compileOperand(node, ctx) {
645
+ const compiler = this.operandCompilers.get(node.type);
646
+ if (!compiler) {
647
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
648
+ }
649
+ return compiler(node, ctx);
650
+ }
651
+ registerDefaultExpressionCompilers() {
652
+ this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
653
+ const left = this.compileOperand(binary.left, ctx);
654
+ const right = this.compileOperand(binary.right, ctx);
655
+ const base = `${left} ${binary.operator} ${right}`;
656
+ if (binary.escape) {
657
+ const escapeOperand = this.compileOperand(binary.escape, ctx);
658
+ return `${base} ESCAPE ${escapeOperand}`;
659
+ }
660
+ return base;
661
+ });
662
+ this.registerExpressionCompiler("LogicalExpression", (logical, ctx) => {
663
+ if (logical.operands.length === 0) return "";
664
+ const parts = logical.operands.map((op) => {
665
+ const compiled = this.compileExpression(op, ctx);
666
+ return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
667
+ });
668
+ return parts.join(` ${logical.operator} `);
669
+ });
670
+ this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
671
+ const left = this.compileOperand(nullExpr.left, ctx);
672
+ return `${left} ${nullExpr.operator}`;
673
+ });
674
+ this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
675
+ const left = this.compileOperand(inExpr.left, ctx);
676
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
677
+ return `${left} ${inExpr.operator} (${values})`;
678
+ });
679
+ this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
680
+ const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
681
+ return `${existsExpr.operator} (${subquerySql})`;
682
+ });
683
+ this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
684
+ const left = this.compileOperand(betweenExpr.left, ctx);
685
+ const lower = this.compileOperand(betweenExpr.lower, ctx);
686
+ const upper = this.compileOperand(betweenExpr.upper, ctx);
687
+ return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
688
+ });
689
+ }
690
+ registerDefaultOperandCompilers() {
691
+ this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
692
+ this.registerOperandCompiler("Column", (column, _ctx) => {
693
+ return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
694
+ });
695
+ this.registerOperandCompiler(
696
+ "Function",
697
+ (fnNode, ctx) => this.compileFunctionOperand(fnNode, ctx)
698
+ );
699
+ this.registerOperandCompiler("JsonPath", (path, _ctx) => this.compileJsonPath(path));
700
+ this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
701
+ const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
702
+ return `(${sql})`;
703
+ });
704
+ this.registerOperandCompiler("CaseExpression", (node, ctx) => {
705
+ const parts = ["CASE"];
706
+ for (const { when, then } of node.conditions) {
707
+ parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
708
+ }
709
+ if (node.else) {
710
+ parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
711
+ }
712
+ parts.push("END");
713
+ return parts.join(" ");
714
+ });
715
+ this.registerOperandCompiler("WindowFunction", (node, ctx) => {
716
+ let result = `${node.name}(`;
717
+ if (node.args.length > 0) {
718
+ result += node.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
719
+ }
720
+ result += ") OVER (";
721
+ const parts = [];
722
+ if (node.partitionBy && node.partitionBy.length > 0) {
723
+ const partitionClause = "PARTITION BY " + node.partitionBy.map(
724
+ (col) => `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
725
+ ).join(", ");
726
+ parts.push(partitionClause);
727
+ }
728
+ if (node.orderBy && node.orderBy.length > 0) {
729
+ const orderClause = "ORDER BY " + node.orderBy.map(
730
+ (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
731
+ ).join(", ");
732
+ parts.push(orderClause);
733
+ }
734
+ result += parts.join(" ");
735
+ result += ")";
736
+ return result;
737
+ });
738
+ }
739
+ // Default fallback, should be overridden by dialects if supported
740
+ compileJsonPath(node) {
741
+ throw new Error("JSON Path not supported by this dialect");
742
+ }
743
+ /**
744
+ * Compiles a function operand, using the dialect's function strategy.
745
+ */
746
+ compileFunctionOperand(fnNode, ctx) {
747
+ const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
748
+ const renderer = this.functionStrategy.getRenderer(fnNode.name);
749
+ if (renderer) {
750
+ return renderer({ node: fnNode, compiledArgs });
751
+ }
752
+ return `${fnNode.name}(${compiledArgs.join(", ")})`;
753
+ }
754
+ };
755
+
756
+ // src/core/dialect/base/function-table-formatter.ts
757
+ var FunctionTableFormatter = class {
758
+ /**
759
+ * Formats a function table node into SQL syntax.
760
+ * @param fn - The function table node containing schema, name, args, and aliases.
761
+ * @param ctx - Optional compiler context for operand compilation.
762
+ * @param dialect - The dialect instance for compiling operands.
763
+ * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
764
+ */
765
+ static format(fn, ctx, dialect) {
766
+ const schemaPart = this.formatSchema(fn, dialect);
767
+ const args = this.formatArgs(fn, ctx, dialect);
768
+ const base = this.formatBase(fn, schemaPart, args, dialect);
769
+ const lateral = this.formatLateral(fn);
770
+ const alias = this.formatAlias(fn, dialect);
771
+ const colAliases = this.formatColumnAliases(fn, dialect);
772
+ return `${lateral}${base}${alias}${colAliases}`;
773
+ }
774
+ /**
775
+ * Formats the schema prefix for the function name.
776
+ * @param fn - The function table node.
777
+ * @param dialect - The dialect instance for quoting identifiers.
778
+ * @returns Schema prefix (e.g., "schema.") or empty string.
779
+ * @internal
780
+ */
781
+ static formatSchema(fn, dialect) {
782
+ if (!fn.schema) return "";
783
+ const quoted = dialect ? dialect.quoteIdentifier(fn.schema) : fn.schema;
784
+ return `${quoted}.`;
785
+ }
786
+ /**
787
+ * Formats function arguments into SQL syntax.
788
+ * @param fn - The function table node containing arguments.
789
+ * @param ctx - Optional compiler context for operand compilation.
790
+ * @param dialect - The dialect instance for compiling operands.
791
+ * @returns Comma-separated function arguments.
792
+ * @internal
793
+ */
794
+ static formatArgs(fn, ctx, dialect) {
795
+ return (fn.args || []).map((a) => {
796
+ if (ctx && dialect) {
797
+ return dialect.compileOperand(a, ctx);
798
+ }
799
+ return String(a);
800
+ }).join(", ");
801
+ }
802
+ /**
803
+ * Formats the base function call with WITH ORDINALITY if present.
804
+ * @param fn - The function table node.
805
+ * @param schemaPart - Formatted schema prefix.
806
+ * @param args - Formatted function arguments.
807
+ * @param dialect - The dialect instance for quoting identifiers.
808
+ * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
809
+ * @internal
810
+ */
811
+ static formatBase(fn, schemaPart, args, dialect) {
812
+ const ordinality = fn.withOrdinality ? " WITH ORDINALITY" : "";
813
+ const quoted = dialect ? dialect.quoteIdentifier(fn.name) : fn.name;
814
+ return `${schemaPart}${quoted}(${args})${ordinality}`;
815
+ }
816
+ /**
817
+ * Formats the LATERAL keyword if present.
818
+ * @param fn - The function table node.
819
+ * @returns "LATERAL " or empty string.
820
+ * @internal
821
+ */
822
+ static formatLateral(fn) {
823
+ return fn.lateral ? "LATERAL " : "";
824
+ }
825
+ /**
826
+ * Formats the table alias for the function table.
827
+ * @param fn - The function table node.
828
+ * @param dialect - The dialect instance for quoting identifiers.
829
+ * @returns " AS alias" or empty string.
830
+ * @internal
831
+ */
832
+ static formatAlias(fn, dialect) {
833
+ if (!fn.alias) return "";
834
+ const quoted = dialect ? dialect.quoteIdentifier(fn.alias) : fn.alias;
835
+ return ` AS ${quoted}`;
836
+ }
837
+ /**
838
+ * Formats column aliases for the function table result columns.
839
+ * @param fn - The function table node containing column aliases.
840
+ * @param dialect - The dialect instance for quoting identifiers.
841
+ * @returns "(col1, col2, ...)" or empty string.
842
+ * @internal
843
+ */
844
+ static formatColumnAliases(fn, dialect) {
845
+ if (!fn.columnAliases || !fn.columnAliases.length) return "";
846
+ const aliases = fn.columnAliases.map((col) => dialect ? dialect.quoteIdentifier(col) : col).join(", ");
847
+ return `(${aliases})`;
848
+ }
849
+ };
850
+
851
+ // src/core/dialect/base/pagination-strategy.ts
852
+ var StandardLimitOffsetPagination = class {
853
+ /**
854
+ * Compiles LIMIT/OFFSET pagination clause.
855
+ * @param limit - The maximum number of rows to return.
856
+ * @param offset - The number of rows to skip.
857
+ * @returns SQL pagination clause with LIMIT and/or OFFSET.
858
+ */
859
+ compilePagination(limit, offset) {
860
+ const parts = [];
861
+ if (limit !== void 0) parts.push(`LIMIT ${limit}`);
862
+ if (offset !== void 0) parts.push(`OFFSET ${offset}`);
863
+ return parts.length ? ` ${parts.join(" ")}` : "";
864
+ }
865
+ };
866
+
867
+ // src/core/dialect/base/cte-compiler.ts
868
+ var CteCompiler = class {
869
+ /**
870
+ * Compiles CTEs (WITH clauses) including recursive CTEs.
871
+ * @param ast - The SELECT query AST containing CTE definitions.
872
+ * @param ctx - The compiler context for expression compilation.
873
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
874
+ * @param compileSelectAst - Function to recursively compile SELECT query ASTs.
875
+ * @param normalizeSelectAst - Function to normalize SELECT query ASTs before compilation.
876
+ * @param stripTrailingSemicolon - Function to remove trailing semicolons from SQL.
877
+ * @returns SQL WITH clause string (e.g., "WITH cte_name AS (...) ") or empty string if no CTEs.
878
+ */
879
+ static compileCtes(ast, ctx, quoteIdentifier, compileSelectAst, normalizeSelectAst, stripTrailingSemicolon) {
880
+ if (!ast.ctes || ast.ctes.length === 0) return "";
881
+ const hasRecursive = ast.ctes.some((cte) => cte.recursive);
882
+ const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
883
+ const cteDefs = ast.ctes.map((cte) => {
884
+ const name = quoteIdentifier(cte.name);
885
+ const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => quoteIdentifier(c)).join(", ")})` : "";
886
+ const query = stripTrailingSemicolon(compileSelectAst(normalizeSelectAst(cte.query), ctx));
887
+ return `${name}${cols} AS (${query})`;
888
+ }).join(", ");
889
+ return `${prefix}${cteDefs} `;
890
+ }
891
+ };
892
+
893
+ // src/core/dialect/base/returning-strategy.ts
894
+ var NoReturningStrategy = class {
895
+ /**
896
+ * Throws an error as RETURNING is not supported.
897
+ * @param returning - Columns to return (causes error if non-empty).
898
+ * @param _ctx - Compiler context (unused).
899
+ * @throws Error indicating RETURNING is not supported.
900
+ */
901
+ compileReturning(returning, _ctx) {
902
+ if (!returning || returning.length === 0) return "";
903
+ throw new Error("RETURNING is not supported by this dialect.");
904
+ }
905
+ /**
906
+ * Formats column names for RETURNING clause.
907
+ * @param returning - Columns to format.
908
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
909
+ * @returns Simple comma-separated column names.
910
+ */
911
+ formatReturningColumns(returning, quoteIdentifier) {
912
+ return returning.map((column) => {
913
+ const tablePart = column.table ? `${quoteIdentifier(column.table)}.` : "";
914
+ const aliasPart = column.alias ? ` AS ${quoteIdentifier(column.alias)}` : "";
915
+ return `${tablePart}${quoteIdentifier(column.name)}${aliasPart}`;
916
+ }).join(", ");
917
+ }
918
+ };
919
+
920
+ // src/core/dialect/base/join-compiler.ts
921
+ var JoinCompiler = class {
922
+ /**
923
+ * Compiles all JOIN clauses from a SELECT query AST.
924
+ * @param ast - The SELECT query AST containing join definitions.
925
+ * @param ctx - The compiler context for expression compilation.
926
+ * @param compileFrom - Function to compile table sources (tables or subqueries).
927
+ * @param compileExpression - Function to compile join condition expressions.
928
+ * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
929
+ */
930
+ static compileJoins(ast, ctx, compileFrom, compileExpression) {
931
+ if (!ast.joins || ast.joins.length === 0) return "";
932
+ const parts = ast.joins.map((j) => {
933
+ const table = compileFrom(j.table, ctx);
934
+ const cond = compileExpression(j.condition, ctx);
935
+ return `${j.kind} JOIN ${table} ON ${cond}`;
936
+ });
937
+ return ` ${parts.join(" ")}`;
938
+ }
939
+ };
940
+
941
+ // src/core/dialect/base/groupby-compiler.ts
942
+ var GroupByCompiler = class {
943
+ /**
944
+ * Compiles GROUP BY clause from a SELECT query AST.
945
+ * @param ast - The SELECT query AST containing grouping columns.
946
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
947
+ * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
948
+ */
949
+ static compileGroupBy(ast, quoteIdentifier) {
950
+ if (!ast.groupBy || ast.groupBy.length === 0) return "";
951
+ const cols = ast.groupBy.map((c) => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`).join(", ");
952
+ return ` GROUP BY ${cols}`;
953
+ }
954
+ };
955
+
956
+ // src/core/dialect/base/orderby-compiler.ts
957
+ var OrderByCompiler = class {
958
+ /**
959
+ * Compiles ORDER BY clause from a SELECT query AST.
960
+ * @param ast - The SELECT query AST containing sort specifications.
961
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
962
+ * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
963
+ */
964
+ static compileOrderBy(ast, quoteIdentifier) {
965
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
966
+ const parts = ast.orderBy.map((o) => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
967
+ return ` ORDER BY ${parts}`;
968
+ }
969
+ };
970
+
971
+ // src/core/dialect/base/sql-dialect.ts
972
+ var SqlDialectBase = class extends Dialect {
973
+ constructor() {
974
+ super(...arguments);
975
+ this.paginationStrategy = new StandardLimitOffsetPagination();
976
+ this.returningStrategy = new NoReturningStrategy();
977
+ }
978
+ compileSelectAst(ast, ctx) {
979
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
980
+ const ctes = CteCompiler.compileCtes(
981
+ ast,
982
+ ctx,
983
+ this.quoteIdentifier.bind(this),
984
+ this.compileSelectAst.bind(this),
985
+ this.normalizeSelectAst?.bind(this) ?? ((a) => a),
986
+ this.stripTrailingSemicolon.bind(this)
987
+ );
988
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
989
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
990
+ if (!hasSetOps) {
991
+ return `${ctes}${baseSelect}`;
992
+ }
993
+ return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
994
+ }
995
+ compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
996
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
997
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
998
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
999
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1000
+ return `${ctes}${combined}${orderBy}${pagination}`;
1001
+ }
1002
+ compileInsertAst(ast, ctx) {
1003
+ const table = this.compileTableName(ast.into);
1004
+ const columnList = this.compileInsertColumnList(ast.columns);
1005
+ const values = this.compileInsertValues(ast.values, ctx);
1006
+ const returning = this.compileReturning(ast.returning, ctx);
1007
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
1008
+ }
1009
+ compileReturning(returning, ctx) {
1010
+ return this.returningStrategy.compileReturning(returning, ctx);
1011
+ }
1012
+ compileInsertColumnList(columns) {
1013
+ return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1014
+ }
1015
+ compileInsertValues(values, ctx) {
1016
+ return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1017
+ }
1018
+ compileSelectCore(ast, ctx) {
1019
+ const columns = this.compileSelectColumns(ast, ctx);
1020
+ const from = this.compileFrom(ast.from, ctx);
1021
+ const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
1022
+ const whereClause = this.compileWhere(ast.where, ctx);
1023
+ const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
1024
+ const having = this.compileHaving(ast, ctx);
1025
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1026
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1027
+ return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
1028
+ }
1029
+ compileUpdateAst(ast, ctx) {
1030
+ const table = this.compileTableName(ast.table);
1031
+ const assignments = this.compileUpdateAssignments(ast.set, ctx);
1032
+ const whereClause = this.compileWhere(ast.where, ctx);
1033
+ const returning = this.compileReturning(ast.returning, ctx);
1034
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
1035
+ }
1036
+ compileUpdateAssignments(assignments, ctx) {
1037
+ return assignments.map((assignment) => {
1038
+ const col = assignment.column;
1039
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1040
+ const value = this.compileOperand(assignment.value, ctx);
1041
+ return `${target} = ${value}`;
1042
+ }).join(", ");
1043
+ }
1044
+ compileDeleteAst(ast, ctx) {
1045
+ const table = this.compileTableName(ast.from);
1046
+ const whereClause = this.compileWhere(ast.where, ctx);
1047
+ const returning = this.compileReturning(ast.returning, ctx);
1048
+ return `DELETE FROM ${table}${whereClause}${returning}`;
1049
+ }
1050
+ formatReturningColumns(returning) {
1051
+ return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
1052
+ }
1053
+ compileDistinct(ast) {
1054
+ return ast.distinct ? "DISTINCT " : "";
1055
+ }
1056
+ compileSelectColumns(ast, ctx) {
1057
+ return ast.columns.map((c) => {
1058
+ const expr = this.compileOperand(c, ctx);
1059
+ if (c.alias) {
1060
+ if (c.alias.includes("(")) return c.alias;
1061
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1062
+ }
1063
+ return expr;
1064
+ }).join(", ");
1065
+ }
1066
+ compileFrom(ast, ctx) {
1067
+ const tableSource = ast;
1068
+ if (tableSource.type === "FunctionTable") {
1069
+ return this.compileFunctionTable(tableSource, ctx);
1070
+ }
1071
+ return this.compileTableSource(tableSource);
1072
+ }
1073
+ compileFunctionTable(fn, ctx) {
1074
+ return FunctionTableFormatter.format(fn, ctx, this);
1075
+ }
1076
+ compileTableSource(table) {
1077
+ const base = this.compileTableName(table);
1078
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1079
+ }
1080
+ compileTableName(table) {
1081
+ if (table.schema) {
1082
+ return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
1083
+ }
1084
+ return this.quoteIdentifier(table.name);
1085
+ }
1086
+ compileHaving(ast, ctx) {
1087
+ if (!ast.having) return "";
1088
+ return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
1089
+ }
1090
+ stripTrailingSemicolon(sql) {
1091
+ return sql.trim().replace(/;$/, "");
1092
+ }
1093
+ wrapSetOperand(sql) {
1094
+ const trimmed = this.stripTrailingSemicolon(sql);
1095
+ return `(${trimmed})`;
1096
+ }
1097
+ };
1098
+
1099
+ // src/core/dialect/postgres/functions.ts
1100
+ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1101
+ constructor() {
1102
+ super();
1103
+ this.registerOverrides();
1104
+ }
1105
+ registerOverrides() {
1106
+ this.add("UTC_NOW", () => `(NOW() AT TIME ZONE 'UTC')`);
1107
+ this.add("UNIX_TIMESTAMP", () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
1108
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1109
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1110
+ return `to_timestamp(${compiledArgs[0]})`;
1111
+ });
1112
+ this.add("EXTRACT", ({ compiledArgs }) => {
1113
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1114
+ const [part, date] = compiledArgs;
1115
+ const partClean = part.replace(/['"]/g, "");
1116
+ return `EXTRACT(${partClean} FROM ${date})`;
1117
+ });
1118
+ this.add("YEAR", ({ compiledArgs }) => {
1119
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1120
+ return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
1121
+ });
1122
+ this.add("MONTH", ({ compiledArgs }) => {
1123
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1124
+ return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
1125
+ });
1126
+ this.add("DAY", ({ compiledArgs }) => {
1127
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1128
+ return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
1129
+ });
1130
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1131
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1132
+ const [date, interval] = compiledArgs;
1133
+ const unitArg = node.args[2];
1134
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1135
+ return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
1136
+ });
1137
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1138
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1139
+ const [date, interval] = compiledArgs;
1140
+ const unitArg = node.args[2];
1141
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1142
+ return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
1143
+ });
1144
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1145
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1146
+ const [date1, date2] = compiledArgs;
1147
+ return `(${date1}::DATE - ${date2}::DATE)`;
1148
+ });
1149
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1150
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1151
+ const [date, format] = compiledArgs;
1152
+ return `TO_CHAR(${date}, ${format})`;
1153
+ });
1154
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1155
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1156
+ return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
1157
+ });
1158
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1159
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1160
+ return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
1161
+ });
1162
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1163
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1164
+ return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
1165
+ });
1166
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1167
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1168
+ const [, date] = compiledArgs;
1169
+ const partArg = node.args[0];
1170
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1171
+ return `DATE_TRUNC('${partClean}', ${date})`;
1172
+ });
1173
+ }
1174
+ };
1175
+
1176
+ // src/core/dialect/postgres/index.ts
1177
+ var PostgresDialect = class extends SqlDialectBase {
1178
+ /**
1179
+ * Creates a new PostgresDialect instance
1180
+ */
1181
+ constructor() {
1182
+ super(new PostgresFunctionStrategy());
1183
+ this.dialect = "postgres";
1184
+ }
1185
+ /**
1186
+ * Quotes an identifier using PostgreSQL double-quote syntax
1187
+ * @param id - Identifier to quote
1188
+ * @returns Quoted identifier
1189
+ */
1190
+ quoteIdentifier(id) {
1191
+ return `"${id}"`;
1192
+ }
1193
+ /**
1194
+ * Compiles JSON path expression using PostgreSQL syntax
1195
+ * @param node - JSON path node
1196
+ * @returns PostgreSQL JSON path expression
1197
+ */
1198
+ compileJsonPath(node) {
1199
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1200
+ return `${col}->>'${node.path}'`;
1201
+ }
1202
+ compileReturning(returning, ctx) {
1203
+ if (!returning || returning.length === 0) return "";
1204
+ const columns = this.formatReturningColumns(returning);
1205
+ return ` RETURNING ${columns}`;
1206
+ }
1207
+ supportsReturning() {
1208
+ return true;
1209
+ }
1210
+ };
1211
+
1212
+ // src/core/dialect/mysql/functions.ts
1213
+ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
1214
+ constructor() {
1215
+ super();
1216
+ this.registerOverrides();
1217
+ }
1218
+ registerOverrides() {
1219
+ this.add("NOW", () => `NOW()`);
1220
+ this.add("CURRENT_DATE", () => `CURDATE()`);
1221
+ this.add("CURRENT_TIME", () => `CURTIME()`);
1222
+ this.add("UTC_NOW", () => `UTC_TIMESTAMP()`);
1223
+ this.add("EXTRACT", ({ compiledArgs }) => {
1224
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1225
+ const [part, date] = compiledArgs;
1226
+ const partClean = part.replace(/['"]/g, "");
1227
+ return `EXTRACT(${partClean} FROM ${date})`;
1228
+ });
1229
+ this.add("YEAR", ({ compiledArgs }) => {
1230
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1231
+ return `YEAR(${compiledArgs[0]})`;
1232
+ });
1233
+ this.add("MONTH", ({ compiledArgs }) => {
1234
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1235
+ return `MONTH(${compiledArgs[0]})`;
1236
+ });
1237
+ this.add("DAY", ({ compiledArgs }) => {
1238
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1239
+ return `DAY(${compiledArgs[0]})`;
1240
+ });
1241
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1242
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1243
+ const [date, interval] = compiledArgs;
1244
+ const unitArg = node.args[2];
1245
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1246
+ return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
1247
+ });
1248
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1249
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1250
+ const [date, interval] = compiledArgs;
1251
+ const unitArg = node.args[2];
1252
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1253
+ return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
1254
+ });
1255
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1256
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1257
+ const [date1, date2] = compiledArgs;
1258
+ return `DATEDIFF(${date1}, ${date2})`;
1259
+ });
1260
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1261
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1262
+ const [date, format] = compiledArgs;
1263
+ return `DATE_FORMAT(${date}, ${format})`;
1264
+ });
1265
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1266
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1267
+ return `LAST_DAY(${compiledArgs[0]})`;
1268
+ });
1269
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1270
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1271
+ return `DAYOFWEEK(${compiledArgs[0]})`;
1272
+ });
1273
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1274
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1275
+ return `WEEKOFYEAR(${compiledArgs[0]})`;
1276
+ });
1277
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1278
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1279
+ const [, date] = compiledArgs;
1280
+ const partArg = node.args[0];
1281
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1282
+ if (partClean === "year") {
1283
+ return `DATE_FORMAT(${date}, '%Y-01-01')`;
1284
+ } else if (partClean === "month") {
1285
+ return `DATE_FORMAT(${date}, '%Y-%m-01')`;
1286
+ } else if (partClean === "day") {
1287
+ return `DATE(${date})`;
1288
+ }
1289
+ return `DATE(${date})`;
1290
+ });
1291
+ }
1292
+ };
1293
+
1294
+ // src/core/dialect/mysql/index.ts
1295
+ var MySqlDialect = class extends SqlDialectBase {
1296
+ /**
1297
+ * Creates a new MySqlDialect instance
1298
+ */
1299
+ constructor() {
1300
+ super(new MysqlFunctionStrategy());
1301
+ this.dialect = "mysql";
1302
+ }
1303
+ /**
1304
+ * Quotes an identifier using MySQL backtick syntax
1305
+ * @param id - Identifier to quote
1306
+ * @returns Quoted identifier
1307
+ */
1308
+ quoteIdentifier(id) {
1309
+ return `\`${id}\``;
1310
+ }
1311
+ /**
1312
+ * Compiles JSON path expression using MySQL syntax
1313
+ * @param node - JSON path node
1314
+ * @returns MySQL JSON path expression
1315
+ */
1316
+ compileJsonPath(node) {
1317
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1318
+ return `${col}->'${node.path}'`;
1319
+ }
1320
+ };
1321
+
1322
+ // src/core/dialect/sqlite/functions.ts
1323
+ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1324
+ constructor() {
1325
+ super();
1326
+ this.registerOverrides();
1327
+ }
1328
+ registerOverrides() {
1329
+ this.add("NOW", () => `datetime('now', 'localtime')`);
1330
+ this.add("CURRENT_DATE", () => `date('now', 'localtime')`);
1331
+ this.add("CURRENT_TIME", () => `time('now', 'localtime')`);
1332
+ this.add("UTC_NOW", () => `datetime('now')`);
1333
+ this.add("EXTRACT", ({ compiledArgs }) => {
1334
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1335
+ const [part, date] = compiledArgs;
1336
+ const partUpper = part.replace(/['"]/g, "").toUpperCase();
1337
+ const formatMap = {
1338
+ "YEAR": "%Y",
1339
+ "MONTH": "%m",
1340
+ "DAY": "%d",
1341
+ "HOUR": "%H",
1342
+ "MINUTE": "%M",
1343
+ "SECOND": "%S",
1344
+ "DOW": "%w",
1345
+ "WEEK": "%W"
1346
+ };
1347
+ const format = formatMap[partUpper] || "%Y";
1348
+ return `CAST(strftime('${format}', ${date}) AS INTEGER)`;
1349
+ });
1350
+ this.add("YEAR", ({ compiledArgs }) => {
1351
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1352
+ return `CAST(strftime('%Y', ${compiledArgs[0]}) AS INTEGER)`;
1353
+ });
1354
+ this.add("MONTH", ({ compiledArgs }) => {
1355
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1356
+ return `CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER)`;
1357
+ });
1358
+ this.add("DAY", ({ compiledArgs }) => {
1359
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1360
+ return `CAST(strftime('%d', ${compiledArgs[0]}) AS INTEGER)`;
1361
+ });
1362
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1363
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1364
+ const [date, interval] = compiledArgs;
1365
+ const unitArg = node.args[2];
1366
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1367
+ return `datetime(${date}, '+' || ${interval} || ' ${unitClean}')`;
1368
+ });
1369
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1370
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1371
+ const [date, interval] = compiledArgs;
1372
+ const unitArg = node.args[2];
1373
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1374
+ return `datetime(${date}, '-' || ${interval} || ' ${unitClean}')`;
1375
+ });
1376
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1377
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1378
+ const [date1, date2] = compiledArgs;
1379
+ return `CAST(julianday(${date1}) - julianday(${date2}) AS INTEGER)`;
1380
+ });
1381
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1382
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1383
+ const [date, format] = compiledArgs;
1384
+ return `strftime(${format}, ${date})`;
1385
+ });
1386
+ this.add("UNIX_TIMESTAMP", () => `CAST(strftime('%s', 'now') AS INTEGER)`);
1387
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1388
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1389
+ return `datetime(${compiledArgs[0]}, 'unixepoch')`;
1390
+ });
1391
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1392
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1393
+ return `date(${compiledArgs[0]}, 'start of month', '+1 month', '-1 day')`;
1394
+ });
1395
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1396
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1397
+ return `CAST(strftime('%w', ${compiledArgs[0]}) AS INTEGER)`;
1398
+ });
1399
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1400
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1401
+ return `CAST(strftime('%W', ${compiledArgs[0]}) AS INTEGER)`;
1402
+ });
1403
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1404
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1405
+ const [, date] = compiledArgs;
1406
+ const partArg = node.args[0];
1407
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1408
+ if (partClean === "year") {
1409
+ return `date(${date}, 'start of year')`;
1410
+ } else if (partClean === "month") {
1411
+ return `date(${date}, 'start of month')`;
1412
+ } else if (partClean === "day") {
1413
+ return `date(${date})`;
1414
+ }
1415
+ return `date(${date}, 'start of ${partClean}')`;
1416
+ });
1417
+ }
1418
+ };
1419
+
1420
+ // src/core/dialect/sqlite/index.ts
1421
+ var SqliteDialect = class extends SqlDialectBase {
1422
+ /**
1423
+ * Creates a new SqliteDialect instance
1424
+ */
1425
+ constructor() {
1426
+ super(new SqliteFunctionStrategy());
1427
+ this.dialect = "sqlite";
1428
+ }
1429
+ /**
1430
+ * Quotes an identifier using SQLite double-quote syntax
1431
+ * @param id - Identifier to quote
1432
+ * @returns Quoted identifier
1433
+ */
1434
+ quoteIdentifier(id) {
1435
+ return `"${id}"`;
1436
+ }
1437
+ /**
1438
+ * Compiles JSON path expression using SQLite syntax
1439
+ * @param node - JSON path node
1440
+ * @returns SQLite JSON path expression
1441
+ */
1442
+ compileJsonPath(node) {
1443
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1444
+ return `json_extract(${col}, '${node.path}')`;
1445
+ }
1446
+ compileReturning(returning, ctx) {
1447
+ if (!returning || returning.length === 0) return "";
1448
+ const columns = this.formatReturningColumns(returning);
1449
+ return ` RETURNING ${columns}`;
1450
+ }
1451
+ supportsReturning() {
1452
+ return true;
1453
+ }
1454
+ };
1455
+
1456
+ // src/core/dialect/mssql/functions.ts
1457
+ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1458
+ constructor() {
1459
+ super();
1460
+ this.registerOverrides();
1461
+ }
1462
+ registerOverrides() {
1463
+ this.add("NOW", () => `GETDATE()`);
1464
+ this.add("CURRENT_DATE", () => `CAST(GETDATE() AS DATE)`);
1465
+ this.add("CURRENT_TIME", () => `CAST(GETDATE() AS TIME)`);
1466
+ this.add("UTC_NOW", () => `GETUTCDATE()`);
1467
+ this.add("EXTRACT", ({ compiledArgs }) => {
1468
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1469
+ const [part, date] = compiledArgs;
1470
+ const partClean = part.replace(/['"]/g, "").toLowerCase();
1471
+ return `DATEPART(${partClean}, ${date})`;
1472
+ });
1473
+ this.add("YEAR", ({ compiledArgs }) => {
1474
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1475
+ return `YEAR(${compiledArgs[0]})`;
1476
+ });
1477
+ this.add("MONTH", ({ compiledArgs }) => {
1478
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1479
+ return `MONTH(${compiledArgs[0]})`;
1480
+ });
1481
+ this.add("DAY", ({ compiledArgs }) => {
1482
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1483
+ return `DAY(${compiledArgs[0]})`;
1484
+ });
1485
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1486
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1487
+ const [date, interval] = compiledArgs;
1488
+ const unitArg = node.args[2];
1489
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1490
+ return `DATEADD(${unitClean}, ${interval}, ${date})`;
1491
+ });
1492
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1493
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1494
+ const [date, interval] = compiledArgs;
1495
+ const unitArg = node.args[2];
1496
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1497
+ return `DATEADD(${unitClean}, -${interval}, ${date})`;
1498
+ });
1499
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1500
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1501
+ const [date1, date2] = compiledArgs;
1502
+ return `DATEDIFF(day, ${date2}, ${date1})`;
1503
+ });
1504
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1505
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1506
+ const [date, format] = compiledArgs;
1507
+ return `FORMAT(${date}, ${format})`;
1508
+ });
1509
+ this.add("UNIX_TIMESTAMP", () => `DATEDIFF(SECOND, '1970-01-01', GETUTCDATE())`);
1510
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1511
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1512
+ return `DATEADD(SECOND, ${compiledArgs[0]}, '1970-01-01')`;
1513
+ });
1514
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1515
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1516
+ return `EOMONTH(${compiledArgs[0]})`;
1517
+ });
1518
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1519
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1520
+ return `DATEPART(dw, ${compiledArgs[0]})`;
1521
+ });
1522
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1523
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1524
+ return `DATEPART(wk, ${compiledArgs[0]})`;
1525
+ });
1526
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1527
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1528
+ const [, date] = compiledArgs;
1529
+ const partArg = node.args[0];
1530
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1531
+ return `DATETRUNC(${partClean}, ${date})`;
1532
+ });
1533
+ }
1534
+ };
1535
+
1536
+ // src/core/dialect/mssql/index.ts
1537
+ var SqlServerDialect = class extends Dialect {
1538
+ /**
1539
+ * Creates a new SqlServerDialect instance
1540
+ */
1541
+ constructor() {
1542
+ super(new MssqlFunctionStrategy());
1543
+ this.dialect = "mssql";
1544
+ }
1545
+ /**
1546
+ * Quotes an identifier using SQL Server bracket syntax
1547
+ * @param id - Identifier to quote
1548
+ * @returns Quoted identifier
1549
+ */
1550
+ quoteIdentifier(id) {
1551
+ return `[${id}]`;
1552
+ }
1553
+ /**
1554
+ * Compiles JSON path expression using SQL Server syntax
1555
+ * @param node - JSON path node
1556
+ * @returns SQL Server JSON path expression
1557
+ */
1558
+ compileJsonPath(node) {
1559
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1560
+ return `JSON_VALUE(${col}, '${node.path}')`;
1561
+ }
1562
+ /**
1563
+ * Formats parameter placeholders using SQL Server named parameter syntax
1564
+ * @param index - Parameter index
1565
+ * @returns Named parameter placeholder
1566
+ */
1567
+ formatPlaceholder(index) {
1568
+ return `@p${index}`;
1569
+ }
1570
+ /**
1571
+ * Compiles SELECT query AST to SQL Server SQL
1572
+ * @param ast - Query AST
1573
+ * @param ctx - Compiler context
1574
+ * @returns SQL Server SQL string
1575
+ */
1576
+ compileSelectAst(ast, ctx) {
1577
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
1578
+ const ctes = this.compileCtes(ast, ctx);
1579
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
1580
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
1581
+ if (!hasSetOps) {
1582
+ return `${ctes}${baseSelect}`;
1583
+ }
1584
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1585
+ const orderBy = this.compileOrderBy(ast);
1586
+ const pagination = this.compilePagination(ast, orderBy);
1587
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1588
+ const tail = pagination || orderBy;
1589
+ return `${ctes}${combined}${tail}`;
1590
+ }
1591
+ compileInsertAst(ast, ctx) {
1592
+ const table = this.quoteIdentifier(ast.into.name);
1593
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1594
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1595
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
1596
+ }
1597
+ compileUpdateAst(ast, ctx) {
1598
+ const table = this.quoteIdentifier(ast.table.name);
1599
+ const assignments = ast.set.map((assignment) => {
1600
+ const col = assignment.column;
1601
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1602
+ const value = this.compileOperand(assignment.value, ctx);
1603
+ return `${target} = ${value}`;
1604
+ }).join(", ");
1605
+ const whereClause = this.compileWhere(ast.where, ctx);
1606
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
1607
+ }
1608
+ compileDeleteAst(ast, ctx) {
1609
+ const table = this.quoteIdentifier(ast.from.name);
1610
+ const whereClause = this.compileWhere(ast.where, ctx);
1611
+ return `DELETE FROM ${table}${whereClause};`;
1612
+ }
1613
+ compileSelectCore(ast, ctx) {
1614
+ const columns = ast.columns.map((c) => {
1615
+ let expr = "";
1616
+ if (c.type === "Function") {
1617
+ expr = this.compileOperand(c, ctx);
1618
+ } else if (c.type === "Column") {
1619
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
1620
+ } else if (c.type === "ScalarSubquery") {
1621
+ expr = this.compileOperand(c, ctx);
1622
+ } else if (c.type === "WindowFunction") {
1623
+ expr = this.compileOperand(c, ctx);
1624
+ }
1625
+ if (c.alias) {
1626
+ if (c.alias.includes("(")) return c.alias;
1627
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1628
+ }
1629
+ return expr;
1630
+ }).join(", ");
1631
+ const distinct = ast.distinct ? "DISTINCT " : "";
1632
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
1633
+ const joins = ast.joins.map((j) => {
1634
+ const table = this.quoteIdentifier(j.table.name);
1635
+ const cond = this.compileExpression(j.condition, ctx);
1636
+ return `${j.kind} JOIN ${table} ON ${cond}`;
1637
+ }).join(" ");
1638
+ const whereClause = this.compileWhere(ast.where, ctx);
1639
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
1640
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
1641
+ const orderBy = this.compileOrderBy(ast);
1642
+ const pagination = this.compilePagination(ast, orderBy);
1643
+ if (pagination) {
1644
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
1645
+ }
1646
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
1647
+ }
1648
+ compileOrderBy(ast) {
1649
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
1650
+ return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1651
+ }
1652
+ compilePagination(ast, orderBy) {
1653
+ const hasLimit = ast.limit !== void 0;
1654
+ const hasOffset = ast.offset !== void 0;
1655
+ if (!hasLimit && !hasOffset) return "";
1656
+ const off = ast.offset ?? 0;
1657
+ const orderClause = orderBy || " ORDER BY (SELECT NULL)";
1658
+ let pagination = `${orderClause} OFFSET ${off} ROWS`;
1659
+ if (hasLimit) {
1660
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
1661
+ }
1662
+ return pagination;
1663
+ }
1664
+ compileCtes(ast, ctx) {
1665
+ if (!ast.ctes || ast.ctes.length === 0) return "";
1666
+ const defs = ast.ctes.map((cte) => {
1667
+ const name = this.quoteIdentifier(cte.name);
1668
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1669
+ const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
1670
+ return `${name}${cols} AS (${query})`;
1671
+ }).join(", ");
1672
+ return `WITH ${defs} `;
1673
+ }
1674
+ wrapSetOperand(sql) {
1675
+ const trimmed = sql.trim().replace(/;$/, "");
1676
+ return `(${trimmed})`;
1677
+ }
1678
+ };
1679
+
1680
+ // src/core/dialect/dialect-factory.ts
1681
+ var DialectFactory = class {
1682
+ static {
1683
+ this.registry = /* @__PURE__ */ new Map();
1684
+ }
1685
+ static {
1686
+ this.defaultsInitialized = false;
1687
+ }
1688
+ static ensureDefaults() {
1689
+ if (this.defaultsInitialized) return;
1690
+ this.defaultsInitialized = true;
1691
+ if (!this.registry.has("postgres")) {
1692
+ this.registry.set("postgres", () => new PostgresDialect());
1693
+ }
1694
+ if (!this.registry.has("mysql")) {
1695
+ this.registry.set("mysql", () => new MySqlDialect());
1696
+ }
1697
+ if (!this.registry.has("sqlite")) {
1698
+ this.registry.set("sqlite", () => new SqliteDialect());
1699
+ }
1700
+ if (!this.registry.has("mssql")) {
1701
+ this.registry.set("mssql", () => new SqlServerDialect());
1702
+ }
1703
+ }
1704
+ /**
1705
+ * Register (or override) a dialect factory for a key.
1706
+ *
1707
+ * Examples:
1708
+ * DialectFactory.register('sqlite', () => new SqliteDialect());
1709
+ * DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
1710
+ */
1711
+ static register(key, factory) {
1712
+ this.registry.set(key, factory);
1713
+ }
1714
+ /**
1715
+ * Resolve a key into a Dialect instance.
1716
+ * Throws if the key is not registered.
1717
+ */
1718
+ static create(key) {
1719
+ this.ensureDefaults();
1720
+ const factory = this.registry.get(key);
1721
+ if (!factory) {
1722
+ throw new Error(
1723
+ `Dialect "${String(
1724
+ key
1725
+ )}" is not registered. Use DialectFactory.register(...) to register it.`
1726
+ );
1727
+ }
1728
+ return factory();
1729
+ }
1730
+ /**
1731
+ * Clear all registrations (mainly for tests).
1732
+ * Built-ins will be re-registered lazily on the next create().
1733
+ */
1734
+ static clear() {
1735
+ this.registry.clear();
1736
+ this.defaultsInitialized = false;
1737
+ }
288
1738
  };
289
- var columnOperand = (col) => toNode(col);
290
- var createBinaryExpression = (operator, left, right, escape) => {
291
- const node = {
292
- type: "BinaryExpression",
293
- left: toNode(left),
294
- operator,
295
- right: toOperand(right)
296
- };
297
- if (escape !== void 0) {
298
- node.escape = toLiteralNode(escape);
1739
+ var resolveDialectInput = (dialect) => {
1740
+ if (typeof dialect === "string") {
1741
+ return DialectFactory.create(dialect);
299
1742
  }
300
- return node;
1743
+ return dialect;
301
1744
  };
302
- var eq = (left, right) => createBinaryExpression("=", left, right);
303
- var and = (...operands) => ({
304
- type: "LogicalExpression",
305
- operator: "AND",
306
- operands
307
- });
308
- var createInExpression = (operator, left, values) => ({
309
- type: "InExpression",
310
- left: toNode(left),
311
- operator,
312
- right: values.map((v) => toOperand(v))
313
- });
314
- var inList = (left, values) => createInExpression("IN", left, values);
315
- var exists = (subquery) => ({
316
- type: "ExistsExpression",
317
- operator: "EXISTS",
318
- subquery
319
- });
320
- var notExists = (subquery) => ({
321
- type: "ExistsExpression",
322
- operator: "NOT EXISTS",
323
- subquery
324
- });
325
-
326
- // src/core/ast/aggregate-functions.ts
327
- var buildAggregate = (name) => (col) => ({
328
- type: "Function",
329
- name,
330
- args: [columnOperand(col)]
331
- });
332
- var count = buildAggregate("COUNT");
333
- var sum = buildAggregate("SUM");
334
- var avg = buildAggregate("AVG");
335
1745
 
336
1746
  // src/query-builder/select-query-state.ts
337
1747
  var SelectQueryState = class _SelectQueryState {
@@ -484,9 +1894,9 @@ var SelectQueryState = class _SelectQueryState {
484
1894
  var createJoinNode = (kind, tableName, condition, relationName) => ({
485
1895
  type: "Join",
486
1896
  kind,
487
- table: { type: "Table", name: tableName },
1897
+ table: typeof tableName === "string" ? { type: "Table", name: tableName } : tableName,
488
1898
  condition,
489
- relationName
1899
+ meta: relationName ? { relationName } : void 0
490
1900
  });
491
1901
 
492
1902
  // src/core/sql/sql.ts
@@ -828,7 +2238,8 @@ var HydrationPlanner = class _HydrationPlanner {
828
2238
  */
829
2239
  buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
830
2240
  switch (rel.type) {
831
- case RelationKinds.HasMany: {
2241
+ case RelationKinds.HasMany:
2242
+ case RelationKinds.HasOne: {
832
2243
  const localKey = rel.localKey || findPrimaryKey(this.table);
833
2244
  return {
834
2245
  name: relationName,
@@ -1157,10 +2568,11 @@ var assertNever = (value) => {
1157
2568
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
1158
2569
  };
1159
2570
  var baseRelationCondition = (root, relation) => {
1160
- const defaultLocalKey = relation.type === RelationKinds.HasMany ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2571
+ const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
1161
2572
  const localKey = relation.localKey || defaultLocalKey;
1162
2573
  switch (relation.type) {
1163
2574
  case RelationKinds.HasMany:
2575
+ case RelationKinds.HasOne:
1164
2576
  return eq(
1165
2577
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
1166
2578
  { type: "Column", table: root.name, name: localKey }
@@ -1207,6 +2619,9 @@ var buildRelationCorrelation = (root, relation) => {
1207
2619
  return baseRelationCondition(root, relation);
1208
2620
  };
1209
2621
 
2622
+ // src/core/ast/join-metadata.ts
2623
+ var getJoinRelationName = (join) => join.meta?.relationName;
2624
+
1210
2625
  // src/query-builder/relation-service.ts
1211
2626
  var RelationService = class {
1212
2627
  /**
@@ -1261,7 +2676,7 @@ var RelationService = class {
1261
2676
  let hydration = this.hydration;
1262
2677
  const relation = this.getRelation(relationName);
1263
2678
  const aliasPrefix = options?.aliasPrefix ?? relationName;
1264
- const alreadyJoined = state.ast.joins.some((j) => j.relationName === relationName);
2679
+ const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
1265
2680
  if (!alreadyJoined) {
1266
2681
  const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
1267
2682
  state = joined.state;
@@ -1582,6 +2997,12 @@ var hydrateRows = (rows, plan) => {
1582
2997
  const seen = getRelationSeenSet(rootId, rel.name);
1583
2998
  if (seen.has(childPk)) continue;
1584
2999
  seen.add(childPk);
3000
+ if (rel.type === RelationKinds.HasOne) {
3001
+ if (!parent[rel.name]) {
3002
+ parent[rel.name] = buildChild(row, rel);
3003
+ }
3004
+ continue;
3005
+ }
1585
3006
  const bucket = parent[rel.name];
1586
3007
  bucket.push(buildChild(row, rel));
1587
3008
  }
@@ -1595,7 +3016,7 @@ var createBaseRow = (row, plan) => {
1595
3016
  base[key] = row[key];
1596
3017
  }
1597
3018
  for (const rel of plan.relations) {
1598
- base[rel.name] = [];
3019
+ base[rel.name] = rel.type === RelationKinds.HasOne ? null : [];
1599
3020
  }
1600
3021
  return base;
1601
3022
  };
@@ -1758,7 +3179,7 @@ var DefaultHasManyCollection = class {
1758
3179
  }
1759
3180
  };
1760
3181
 
1761
- // src/orm/relations/belongs-to.ts
3182
+ // src/orm/relations/has-one.ts
1762
3183
  var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
1763
3184
  var hideInternal2 = (obj, keys) => {
1764
3185
  for (const key of keys) {
@@ -1770,6 +3191,123 @@ var hideInternal2 = (obj, keys) => {
1770
3191
  });
1771
3192
  }
1772
3193
  };
3194
+ var DefaultHasOneReference = class {
3195
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3196
+ this.ctx = ctx;
3197
+ this.meta = meta;
3198
+ this.root = root;
3199
+ this.relationName = relationName;
3200
+ this.relation = relation;
3201
+ this.rootTable = rootTable;
3202
+ this.loader = loader;
3203
+ this.createEntity = createEntity;
3204
+ this.localKey = localKey;
3205
+ this.loaded = false;
3206
+ this.current = null;
3207
+ hideInternal2(this, [
3208
+ "ctx",
3209
+ "meta",
3210
+ "root",
3211
+ "relationName",
3212
+ "relation",
3213
+ "rootTable",
3214
+ "loader",
3215
+ "createEntity",
3216
+ "localKey"
3217
+ ]);
3218
+ this.populateFromHydrationCache();
3219
+ }
3220
+ async load() {
3221
+ if (this.loaded) return this.current;
3222
+ const map = await this.loader();
3223
+ const keyValue = this.root[this.localKey];
3224
+ if (keyValue === void 0 || keyValue === null) {
3225
+ this.loaded = true;
3226
+ return this.current;
3227
+ }
3228
+ const row = map.get(toKey3(keyValue));
3229
+ this.current = row ? this.createEntity(row) : null;
3230
+ this.loaded = true;
3231
+ return this.current;
3232
+ }
3233
+ get() {
3234
+ return this.current;
3235
+ }
3236
+ set(data) {
3237
+ if (data === null) {
3238
+ return this.detachCurrent();
3239
+ }
3240
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
3241
+ if (this.current && this.current !== entity) {
3242
+ this.ctx.registerRelationChange(
3243
+ this.root,
3244
+ this.relationKey,
3245
+ this.rootTable,
3246
+ this.relationName,
3247
+ this.relation,
3248
+ { kind: "remove", entity: this.current }
3249
+ );
3250
+ }
3251
+ this.assignForeignKey(entity);
3252
+ this.current = entity;
3253
+ this.loaded = true;
3254
+ this.ctx.registerRelationChange(
3255
+ this.root,
3256
+ this.relationKey,
3257
+ this.rootTable,
3258
+ this.relationName,
3259
+ this.relation,
3260
+ { kind: "attach", entity }
3261
+ );
3262
+ return entity;
3263
+ }
3264
+ toJSON() {
3265
+ return this.current;
3266
+ }
3267
+ detachCurrent() {
3268
+ const previous = this.current;
3269
+ if (!previous) return null;
3270
+ this.current = null;
3271
+ this.loaded = true;
3272
+ this.ctx.registerRelationChange(
3273
+ this.root,
3274
+ this.relationKey,
3275
+ this.rootTable,
3276
+ this.relationName,
3277
+ this.relation,
3278
+ { kind: "remove", entity: previous }
3279
+ );
3280
+ return null;
3281
+ }
3282
+ assignForeignKey(entity) {
3283
+ const keyValue = this.root[this.localKey];
3284
+ entity[this.relation.foreignKey] = keyValue;
3285
+ }
3286
+ get relationKey() {
3287
+ return `${this.rootTable.name}.${this.relationName}`;
3288
+ }
3289
+ populateFromHydrationCache() {
3290
+ const keyValue = this.root[this.localKey];
3291
+ if (keyValue === void 0 || keyValue === null) return;
3292
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
3293
+ if (!row) return;
3294
+ this.current = this.createEntity(row);
3295
+ this.loaded = true;
3296
+ }
3297
+ };
3298
+
3299
+ // src/orm/relations/belongs-to.ts
3300
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
3301
+ var hideInternal3 = (obj, keys) => {
3302
+ for (const key of keys) {
3303
+ Object.defineProperty(obj, key, {
3304
+ value: obj[key],
3305
+ writable: false,
3306
+ configurable: false,
3307
+ enumerable: false
3308
+ });
3309
+ }
3310
+ };
1773
3311
  var DefaultBelongsToReference = class {
1774
3312
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
1775
3313
  this.ctx = ctx;
@@ -1783,7 +3321,7 @@ var DefaultBelongsToReference = class {
1783
3321
  this.targetKey = targetKey;
1784
3322
  this.loaded = false;
1785
3323
  this.current = null;
1786
- hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
3324
+ hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
1787
3325
  this.populateFromHydrationCache();
1788
3326
  }
1789
3327
  async load() {
@@ -1793,7 +3331,7 @@ var DefaultBelongsToReference = class {
1793
3331
  if (fkValue === null || fkValue === void 0) {
1794
3332
  this.current = null;
1795
3333
  } else {
1796
- const row = map.get(toKey3(fkValue));
3334
+ const row = map.get(toKey4(fkValue));
1797
3335
  this.current = row ? this.createEntity(row) : null;
1798
3336
  }
1799
3337
  this.loaded = true;
@@ -1850,8 +3388,8 @@ var DefaultBelongsToReference = class {
1850
3388
  };
1851
3389
 
1852
3390
  // src/orm/relations/many-to-many.ts
1853
- var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
1854
- var hideInternal3 = (obj, keys) => {
3391
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3392
+ var hideInternal4 = (obj, keys) => {
1855
3393
  for (const key of keys) {
1856
3394
  Object.defineProperty(obj, key, {
1857
3395
  value: obj[key],
@@ -1874,13 +3412,13 @@ var DefaultManyToManyCollection = class {
1874
3412
  this.localKey = localKey;
1875
3413
  this.loaded = false;
1876
3414
  this.items = [];
1877
- hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
3415
+ hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
1878
3416
  this.hydrateFromCache();
1879
3417
  }
1880
3418
  async load() {
1881
3419
  if (this.loaded) return this.items;
1882
3420
  const map = await this.loader();
1883
- const key = toKey4(this.root[this.localKey]);
3421
+ const key = toKey5(this.root[this.localKey]);
1884
3422
  const rows = map.get(key) ?? [];
1885
3423
  this.items = rows.map((row) => {
1886
3424
  const entity = this.createEntity(row);
@@ -1930,15 +3468,15 @@ var DefaultManyToManyCollection = class {
1930
3468
  async syncByIds(ids) {
1931
3469
  await this.load();
1932
3470
  const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
1933
- const normalized = new Set(ids.map((id) => toKey4(id)));
1934
- const currentIds = new Set(this.items.map((item) => toKey4(this.extractId(item))));
3471
+ const normalized = new Set(ids.map((id) => toKey5(id)));
3472
+ const currentIds = new Set(this.items.map((item) => toKey5(this.extractId(item))));
1935
3473
  for (const id of normalized) {
1936
3474
  if (!currentIds.has(id)) {
1937
3475
  this.attach(id);
1938
3476
  }
1939
3477
  }
1940
3478
  for (const item of [...this.items]) {
1941
- const itemId = toKey4(this.extractId(item));
3479
+ const itemId = toKey5(this.extractId(item));
1942
3480
  if (!normalized.has(itemId)) {
1943
3481
  this.detach(item);
1944
3482
  }
@@ -2009,7 +3547,7 @@ var executeQuery = async (ctx, qb) => {
2009
3547
  const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
2010
3548
  return rowsFromResults(results);
2011
3549
  };
2012
- var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3550
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
2013
3551
  var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2014
3552
  const localKey = relation.localKey || findPrimaryKey(rootTable);
2015
3553
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -2033,13 +3571,43 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2033
3571
  for (const row of rows) {
2034
3572
  const fkValue = row[relation.foreignKey];
2035
3573
  if (fkValue === null || fkValue === void 0) continue;
2036
- const key = toKey5(fkValue);
3574
+ const key = toKey6(fkValue);
2037
3575
  const bucket = grouped.get(key) ?? [];
2038
3576
  bucket.push(row);
2039
3577
  grouped.set(key, bucket);
2040
3578
  }
2041
3579
  return grouped;
2042
3580
  };
3581
+ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
3582
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
3583
+ const roots = ctx.getEntitiesForTable(rootTable);
3584
+ const keys = /* @__PURE__ */ new Set();
3585
+ for (const tracked of roots) {
3586
+ const value = tracked.entity[localKey];
3587
+ if (value !== null && value !== void 0) {
3588
+ keys.add(value);
3589
+ }
3590
+ }
3591
+ if (!keys.size) {
3592
+ return /* @__PURE__ */ new Map();
3593
+ }
3594
+ const selectMap = selectAllColumns(relation.target);
3595
+ const qb = new SelectQueryBuilder(relation.target).select(selectMap);
3596
+ const fkColumn = relation.target.columns[relation.foreignKey];
3597
+ if (!fkColumn) return /* @__PURE__ */ new Map();
3598
+ qb.where(inList(fkColumn, Array.from(keys)));
3599
+ const rows = await executeQuery(ctx, qb);
3600
+ const lookup = /* @__PURE__ */ new Map();
3601
+ for (const row of rows) {
3602
+ const fkValue = row[relation.foreignKey];
3603
+ if (fkValue === null || fkValue === void 0) continue;
3604
+ const key = toKey6(fkValue);
3605
+ if (!lookup.has(key)) {
3606
+ lookup.set(key, row);
3607
+ }
3608
+ }
3609
+ return lookup;
3610
+ };
2043
3611
  var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2044
3612
  const roots = ctx.getEntitiesForTable(rootTable);
2045
3613
  const foreignKeys = /* @__PURE__ */ new Set();
@@ -2063,7 +3631,7 @@ var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2063
3631
  for (const row of rows) {
2064
3632
  const keyValue = row[targetKey];
2065
3633
  if (keyValue === null || keyValue === void 0) continue;
2066
- map.set(toKey5(keyValue), row);
3634
+ map.set(toKey6(keyValue), row);
2067
3635
  }
2068
3636
  return map;
2069
3637
  };
@@ -2094,12 +3662,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2094
3662
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
2095
3663
  continue;
2096
3664
  }
2097
- const bucket = rootLookup.get(toKey5(rootValue)) ?? [];
3665
+ const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
2098
3666
  bucket.push({
2099
3667
  targetId: targetValue,
2100
3668
  pivot: { ...pivot }
2101
3669
  });
2102
- rootLookup.set(toKey5(rootValue), bucket);
3670
+ rootLookup.set(toKey6(rootValue), bucket);
2103
3671
  targetIds.add(targetValue);
2104
3672
  }
2105
3673
  if (!targetIds.size) {
@@ -2116,13 +3684,13 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2116
3684
  for (const row of targetRows) {
2117
3685
  const pkValue = row[targetKey];
2118
3686
  if (pkValue === null || pkValue === void 0) continue;
2119
- targetMap.set(toKey5(pkValue), row);
3687
+ targetMap.set(toKey6(pkValue), row);
2120
3688
  }
2121
3689
  const result = /* @__PURE__ */ new Map();
2122
3690
  for (const [rootId, entries] of rootLookup.entries()) {
2123
3691
  const bucket = [];
2124
3692
  for (const entry of entries) {
2125
- const targetRow = targetMap.get(toKey5(entry.targetId));
3693
+ const targetRow = targetMap.get(toKey6(entry.targetId));
2126
3694
  if (!targetRow) continue;
2127
3695
  bucket.push({
2128
3696
  ...targetRow,
@@ -2217,18 +3785,29 @@ var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
2217
3785
  }
2218
3786
  return entity;
2219
3787
  };
2220
- var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
3788
+ var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
2221
3789
  var populateHydrationCache = (entity, row, meta) => {
2222
3790
  for (const relationName of Object.keys(meta.table.relations)) {
2223
3791
  const relation = meta.table.relations[relationName];
2224
3792
  const data = row[relationName];
3793
+ if (relation.type === RelationKinds.HasOne) {
3794
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
3795
+ const rootValue = entity[localKey];
3796
+ if (rootValue === void 0 || rootValue === null) continue;
3797
+ if (!data || typeof data !== "object") continue;
3798
+ const cache = /* @__PURE__ */ new Map();
3799
+ cache.set(toKey7(rootValue), data);
3800
+ meta.relationHydration.set(relationName, cache);
3801
+ meta.relationCache.set(relationName, Promise.resolve(cache));
3802
+ continue;
3803
+ }
2225
3804
  if (!Array.isArray(data)) continue;
2226
3805
  if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
2227
3806
  const localKey = relation.localKey || findPrimaryKey(meta.table);
2228
3807
  const rootValue = entity[localKey];
2229
3808
  if (rootValue === void 0 || rootValue === null) continue;
2230
3809
  const cache = /* @__PURE__ */ new Map();
2231
- cache.set(toKey6(rootValue), data);
3810
+ cache.set(toKey7(rootValue), data);
2232
3811
  meta.relationHydration.set(relationName, cache);
2233
3812
  meta.relationCache.set(relationName, Promise.resolve(cache));
2234
3813
  continue;
@@ -2239,7 +3818,7 @@ var populateHydrationCache = (entity, row, meta) => {
2239
3818
  for (const item of data) {
2240
3819
  const pkValue = item[targetKey];
2241
3820
  if (pkValue === void 0 || pkValue === null) continue;
2242
- cache.set(toKey6(pkValue), item);
3821
+ cache.set(toKey7(pkValue), item);
2243
3822
  }
2244
3823
  if (cache.size) {
2245
3824
  meta.relationHydration.set(relationName, cache);
@@ -2262,6 +3841,26 @@ var getRelationWrapper = (meta, relationName, owner) => {
2262
3841
  };
2263
3842
  var instantiateWrapper = (meta, relationName, relation, owner) => {
2264
3843
  switch (relation.type) {
3844
+ case RelationKinds.HasOne: {
3845
+ const hasOne2 = relation;
3846
+ const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
3847
+ const loader = () => relationLoaderCache(
3848
+ meta,
3849
+ relationName,
3850
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
3851
+ );
3852
+ return new DefaultHasOneReference(
3853
+ meta.ctx,
3854
+ meta,
3855
+ owner,
3856
+ relationName,
3857
+ hasOne2,
3858
+ meta.table,
3859
+ loader,
3860
+ (row) => createEntityFromRow(meta.ctx, hasOne2.target, row),
3861
+ localKey
3862
+ );
3863
+ }
2265
3864
  case RelationKinds.HasMany: {
2266
3865
  const hasMany2 = relation;
2267
3866
  const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
@@ -2342,31 +3941,43 @@ var flattenResults = (results) => {
2342
3941
  }
2343
3942
  return rows;
2344
3943
  };
2345
- async function executeHydrated(ctx, qb) {
3944
+ var executeWithEntityContext = async (entityCtx, qb) => {
2346
3945
  const ast = qb.getAST();
2347
- const compiled = ctx.dialect.compileSelect(ast);
2348
- const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
3946
+ const compiled = entityCtx.dialect.compileSelect(ast);
3947
+ const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
2349
3948
  const rows = flattenResults(executed);
2350
3949
  if (ast.setOps && ast.setOps.length > 0) {
2351
- return rows.map(
2352
- (row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
2353
- );
3950
+ return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
2354
3951
  }
2355
3952
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
2356
- return hydrated.map(
2357
- (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
2358
- );
3953
+ return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
3954
+ };
3955
+ async function executeHydrated(session, qb) {
3956
+ return executeWithEntityContext(session, qb);
3957
+ }
3958
+ async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
3959
+ const entityCtx = hydCtx.entityContext;
3960
+ if (!entityCtx) {
3961
+ throw new Error("Hydration context is missing an EntityContext");
3962
+ }
3963
+ return executeWithEntityContext(entityCtx, qb);
2359
3964
  }
2360
3965
 
2361
3966
  // src/query-builder/select.ts
2362
3967
  var SelectQueryBuilder = class _SelectQueryBuilder {
2363
3968
  /**
2364
- * Creates a new SelectQueryBuilder instance
2365
- * @param table - Table definition to query
2366
- * @param state - Optional initial query state
2367
- * @param hydration - Optional hydration manager
2368
- * @param dependencies - Optional query builder dependencies
2369
- */
3969
+
3970
+ * Creates a new SelectQueryBuilder instance
3971
+
3972
+ * @param table - Table definition to query
3973
+
3974
+ * @param state - Optional initial query state
3975
+
3976
+ * @param hydration - Optional hydration manager
3977
+
3978
+ * @param dependencies - Optional query builder dependencies
3979
+
3980
+ */
2370
3981
  constructor(table, state, hydration, dependencies, lazyRelations) {
2371
3982
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
2372
3983
  this.env = { table, deps };
@@ -2403,112 +4014,168 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2403
4014
  return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
2404
4015
  }
2405
4016
  /**
2406
- * Selects specific columns for the query
2407
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
2408
- * @returns New query builder instance with selected columns
2409
- */
4017
+
4018
+ * Selects specific columns for the query
4019
+
4020
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4021
+
4022
+ * @returns New query builder instance with selected columns
4023
+
4024
+ */
2410
4025
  select(columns) {
2411
4026
  return this.clone(this.columnSelector.select(this.context, columns));
2412
4027
  }
2413
4028
  /**
2414
- * Selects raw column expressions
2415
- * @param cols - Column expressions as strings
2416
- * @returns New query builder instance with raw column selections
2417
- */
4029
+
4030
+ * Selects raw column expressions
4031
+
4032
+ * @param cols - Column expressions as strings
4033
+
4034
+ * @returns New query builder instance with raw column selections
4035
+
4036
+ */
2418
4037
  selectRaw(...cols) {
2419
4038
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
2420
4039
  }
2421
4040
  /**
2422
- * Adds a Common Table Expression (CTE) to the query
2423
- * @param name - Name of the CTE
2424
- * @param query - Query builder or query node for the CTE
2425
- * @param columns - Optional column names for the CTE
2426
- * @returns New query builder instance with the CTE
2427
- */
4041
+
4042
+ * Adds a Common Table Expression (CTE) to the query
4043
+
4044
+ * @param name - Name of the CTE
4045
+
4046
+ * @param query - Query builder or query node for the CTE
4047
+
4048
+ * @param columns - Optional column names for the CTE
4049
+
4050
+ * @returns New query builder instance with the CTE
4051
+
4052
+ */
2428
4053
  with(name, query, columns) {
2429
4054
  const subAst = this.resolveQueryNode(query);
2430
4055
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
2431
4056
  return this.clone(nextContext);
2432
4057
  }
2433
4058
  /**
2434
- * Adds a recursive Common Table Expression (CTE) to the query
2435
- * @param name - Name of the CTE
2436
- * @param query - Query builder or query node for the CTE
2437
- * @param columns - Optional column names for the CTE
2438
- * @returns New query builder instance with the recursive CTE
2439
- */
4059
+
4060
+ * Adds a recursive Common Table Expression (CTE) to the query
4061
+
4062
+ * @param name - Name of the CTE
4063
+
4064
+ * @param query - Query builder or query node for the CTE
4065
+
4066
+ * @param columns - Optional column names for the CTE
4067
+
4068
+ * @returns New query builder instance with the recursive CTE
4069
+
4070
+ */
2440
4071
  withRecursive(name, query, columns) {
2441
4072
  const subAst = this.resolveQueryNode(query);
2442
4073
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
2443
4074
  return this.clone(nextContext);
2444
4075
  }
2445
4076
  /**
2446
- * Selects a subquery as a column
2447
- * @param alias - Alias for the subquery column
2448
- * @param sub - Query builder or query node for the subquery
2449
- * @returns New query builder instance with the subquery selection
2450
- */
4077
+
4078
+ * Selects a subquery as a column
4079
+
4080
+ * @param alias - Alias for the subquery column
4081
+
4082
+ * @param sub - Query builder or query node for the subquery
4083
+
4084
+ * @returns New query builder instance with the subquery selection
4085
+
4086
+ */
2451
4087
  selectSubquery(alias, sub) {
2452
4088
  const query = this.resolveQueryNode(sub);
2453
4089
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
2454
4090
  }
2455
4091
  /**
2456
- * Adds an INNER JOIN to the query
2457
- * @param table - Table to join
2458
- * @param condition - Join condition expression
2459
- * @returns New query builder instance with the INNER JOIN
2460
- */
4092
+
4093
+ * Adds an INNER JOIN to the query
4094
+
4095
+ * @param table - Table to join
4096
+
4097
+ * @param condition - Join condition expression
4098
+
4099
+ * @returns New query builder instance with the INNER JOIN
4100
+
4101
+ */
2461
4102
  innerJoin(table, condition) {
2462
4103
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
2463
4104
  return this.clone(nextContext);
2464
4105
  }
2465
4106
  /**
2466
- * Adds a LEFT JOIN to the query
2467
- * @param table - Table to join
2468
- * @param condition - Join condition expression
2469
- * @returns New query builder instance with the LEFT JOIN
2470
- */
4107
+
4108
+ * Adds a LEFT JOIN to the query
4109
+
4110
+ * @param table - Table to join
4111
+
4112
+ * @param condition - Join condition expression
4113
+
4114
+ * @returns New query builder instance with the LEFT JOIN
4115
+
4116
+ */
2471
4117
  leftJoin(table, condition) {
2472
4118
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
2473
4119
  return this.clone(nextContext);
2474
4120
  }
2475
4121
  /**
2476
- * Adds a RIGHT JOIN to the query
2477
- * @param table - Table to join
2478
- * @param condition - Join condition expression
2479
- * @returns New query builder instance with the RIGHT JOIN
2480
- */
4122
+
4123
+ * Adds a RIGHT JOIN to the query
4124
+
4125
+ * @param table - Table to join
4126
+
4127
+ * @param condition - Join condition expression
4128
+
4129
+ * @returns New query builder instance with the RIGHT JOIN
4130
+
4131
+ */
2481
4132
  rightJoin(table, condition) {
2482
4133
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
2483
4134
  return this.clone(nextContext);
2484
4135
  }
2485
4136
  /**
2486
- * Matches records based on a relationship
2487
- * @param relationName - Name of the relationship to match
2488
- * @param predicate - Optional predicate expression
2489
- * @returns New query builder instance with the relationship match
2490
- */
4137
+
4138
+ * Matches records based on a relationship
4139
+
4140
+ * @param relationName - Name of the relationship to match
4141
+
4142
+ * @param predicate - Optional predicate expression
4143
+
4144
+ * @returns New query builder instance with the relationship match
4145
+
4146
+ */
2491
4147
  match(relationName, predicate) {
2492
4148
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
2493
4149
  return this.clone(nextContext);
2494
4150
  }
2495
4151
  /**
2496
- * Joins a related table
2497
- * @param relationName - Name of the relationship to join
2498
- * @param joinKind - Type of join (defaults to INNER)
2499
- * @param extraCondition - Optional additional join condition
2500
- * @returns New query builder instance with the relationship join
2501
- */
4152
+
4153
+ * Joins a related table
4154
+
4155
+ * @param relationName - Name of the relationship to join
4156
+
4157
+ * @param joinKind - Type of join (defaults to INNER)
4158
+
4159
+ * @param extraCondition - Optional additional join condition
4160
+
4161
+ * @returns New query builder instance with the relationship join
4162
+
4163
+ */
2502
4164
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
2503
4165
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
2504
4166
  return this.clone(nextContext);
2505
4167
  }
2506
4168
  /**
2507
- * Includes related data in the query results
2508
- * @param relationName - Name of the relationship to include
2509
- * @param options - Optional include options
2510
- * @returns New query builder instance with the relationship inclusion
2511
- */
4169
+
4170
+ * Includes related data in the query results
4171
+
4172
+ * @param relationName - Name of the relationship to include
4173
+
4174
+ * @param options - Optional include options
4175
+
4176
+ * @returns New query builder instance with the relationship inclusion
4177
+
4178
+ */
2512
4179
  include(relationName, options) {
2513
4180
  const nextContext = this.relationManager.include(this.context, relationName, options);
2514
4181
  return this.clone(nextContext);
@@ -2527,125 +4194,186 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2527
4194
  async execute(ctx) {
2528
4195
  return executeHydrated(ctx, this);
2529
4196
  }
4197
+ async executeWithContexts(execCtx, hydCtx) {
4198
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
4199
+ }
2530
4200
  /**
2531
- * Adds a WHERE condition to the query
2532
- * @param expr - Expression for the WHERE clause
2533
- * @returns New query builder instance with the WHERE condition
2534
- */
4201
+
4202
+ * Adds a WHERE condition to the query
4203
+
4204
+ * @param expr - Expression for the WHERE clause
4205
+
4206
+ * @returns New query builder instance with the WHERE condition
4207
+
4208
+ */
2535
4209
  where(expr) {
2536
4210
  const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
2537
4211
  return this.clone(nextContext);
2538
4212
  }
2539
4213
  /**
2540
- * Adds a GROUP BY clause to the query
2541
- * @param col - Column definition or column node to group by
2542
- * @returns New query builder instance with the GROUP BY clause
2543
- */
4214
+
4215
+ * Adds a GROUP BY clause to the query
4216
+
4217
+ * @param col - Column definition or column node to group by
4218
+
4219
+ * @returns New query builder instance with the GROUP BY clause
4220
+
4221
+ */
2544
4222
  groupBy(col) {
2545
4223
  const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col));
2546
4224
  return this.clone(nextContext);
2547
4225
  }
2548
4226
  /**
2549
- * Adds a HAVING condition to the query
2550
- * @param expr - Expression for the HAVING clause
2551
- * @returns New query builder instance with the HAVING condition
2552
- */
4227
+
4228
+ * Adds a HAVING condition to the query
4229
+
4230
+ * @param expr - Expression for the HAVING clause
4231
+
4232
+ * @returns New query builder instance with the HAVING condition
4233
+
4234
+ */
2553
4235
  having(expr) {
2554
4236
  const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
2555
4237
  return this.clone(nextContext);
2556
4238
  }
2557
4239
  /**
2558
- * Adds an ORDER BY clause to the query
2559
- * @param col - Column definition or column node to order by
2560
- * @param direction - Order direction (defaults to ASC)
2561
- * @returns New query builder instance with the ORDER BY clause
2562
- */
4240
+
4241
+ * Adds an ORDER BY clause to the query
4242
+
4243
+ * @param col - Column definition or column node to order by
4244
+
4245
+ * @param direction - Order direction (defaults to ASC)
4246
+
4247
+ * @returns New query builder instance with the ORDER BY clause
4248
+
4249
+ */
2563
4250
  orderBy(col, direction = ORDER_DIRECTIONS.ASC) {
2564
4251
  const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col, direction));
2565
4252
  return this.clone(nextContext);
2566
4253
  }
2567
4254
  /**
2568
- * Adds a DISTINCT clause to the query
2569
- * @param cols - Columns to make distinct
2570
- * @returns New query builder instance with the DISTINCT clause
2571
- */
4255
+
4256
+ * Adds a DISTINCT clause to the query
4257
+
4258
+ * @param cols - Columns to make distinct
4259
+
4260
+ * @returns New query builder instance with the DISTINCT clause
4261
+
4262
+ */
2572
4263
  distinct(...cols) {
2573
4264
  return this.clone(this.columnSelector.distinct(this.context, cols));
2574
4265
  }
2575
4266
  /**
2576
- * Adds a LIMIT clause to the query
2577
- * @param n - Maximum number of rows to return
2578
- * @returns New query builder instance with the LIMIT clause
2579
- */
4267
+
4268
+ * Adds a LIMIT clause to the query
4269
+
4270
+ * @param n - Maximum number of rows to return
4271
+
4272
+ * @returns New query builder instance with the LIMIT clause
4273
+
4274
+ */
2580
4275
  limit(n) {
2581
4276
  const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
2582
4277
  return this.clone(nextContext);
2583
4278
  }
2584
4279
  /**
2585
- * Adds an OFFSET clause to the query
2586
- * @param n - Number of rows to skip
2587
- * @returns New query builder instance with the OFFSET clause
2588
- */
4280
+
4281
+ * Adds an OFFSET clause to the query
4282
+
4283
+ * @param n - Number of rows to skip
4284
+
4285
+ * @returns New query builder instance with the OFFSET clause
4286
+
4287
+ */
2589
4288
  offset(n) {
2590
4289
  const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
2591
4290
  return this.clone(nextContext);
2592
4291
  }
2593
4292
  /**
2594
- * Combines this query with another using UNION
2595
- * @param query - Query to union with
2596
- * @returns New query builder instance with the set operation
2597
- */
4293
+
4294
+ * Combines this query with another using UNION
4295
+
4296
+ * @param query - Query to union with
4297
+
4298
+ * @returns New query builder instance with the set operation
4299
+
4300
+ */
2598
4301
  union(query) {
2599
4302
  return this.clone(this.applySetOperation("UNION", query));
2600
4303
  }
2601
4304
  /**
2602
- * Combines this query with another using UNION ALL
2603
- * @param query - Query to union with
2604
- * @returns New query builder instance with the set operation
2605
- */
4305
+
4306
+ * Combines this query with another using UNION ALL
4307
+
4308
+ * @param query - Query to union with
4309
+
4310
+ * @returns New query builder instance with the set operation
4311
+
4312
+ */
2606
4313
  unionAll(query) {
2607
4314
  return this.clone(this.applySetOperation("UNION ALL", query));
2608
4315
  }
2609
4316
  /**
2610
- * Combines this query with another using INTERSECT
2611
- * @param query - Query to intersect with
2612
- * @returns New query builder instance with the set operation
2613
- */
4317
+
4318
+ * Combines this query with another using INTERSECT
4319
+
4320
+ * @param query - Query to intersect with
4321
+
4322
+ * @returns New query builder instance with the set operation
4323
+
4324
+ */
2614
4325
  intersect(query) {
2615
4326
  return this.clone(this.applySetOperation("INTERSECT", query));
2616
4327
  }
2617
4328
  /**
2618
- * Combines this query with another using EXCEPT
2619
- * @param query - Query to subtract
2620
- * @returns New query builder instance with the set operation
2621
- */
4329
+
4330
+ * Combines this query with another using EXCEPT
4331
+
4332
+ * @param query - Query to subtract
4333
+
4334
+ * @returns New query builder instance with the set operation
4335
+
4336
+ */
2622
4337
  except(query) {
2623
4338
  return this.clone(this.applySetOperation("EXCEPT", query));
2624
4339
  }
2625
4340
  /**
2626
- * Adds a WHERE EXISTS condition to the query
2627
- * @param subquery - Subquery to check for existence
2628
- * @returns New query builder instance with the WHERE EXISTS condition
2629
- */
4341
+
4342
+ * Adds a WHERE EXISTS condition to the query
4343
+
4344
+ * @param subquery - Subquery to check for existence
4345
+
4346
+ * @returns New query builder instance with the WHERE EXISTS condition
4347
+
4348
+ */
2630
4349
  whereExists(subquery) {
2631
4350
  const subAst = this.resolveQueryNode(subquery);
2632
4351
  return this.where(exists(subAst));
2633
4352
  }
2634
4353
  /**
2635
- * Adds a WHERE NOT EXISTS condition to the query
2636
- * @param subquery - Subquery to check for non-existence
2637
- * @returns New query builder instance with the WHERE NOT EXISTS condition
2638
- */
4354
+
4355
+ * Adds a WHERE NOT EXISTS condition to the query
4356
+
4357
+ * @param subquery - Subquery to check for non-existence
4358
+
4359
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
4360
+
4361
+ */
2639
4362
  whereNotExists(subquery) {
2640
4363
  const subAst = this.resolveQueryNode(subquery);
2641
4364
  return this.where(notExists(subAst));
2642
4365
  }
2643
4366
  /**
2644
- * Adds a WHERE EXISTS condition based on a relationship
2645
- * @param relationName - Name of the relationship to check
2646
- * @param callback - Optional callback to modify the relationship query
2647
- * @returns New query builder instance with the relationship existence check
2648
- */
4367
+
4368
+ * Adds a WHERE EXISTS condition based on a relationship
4369
+
4370
+ * @param relationName - Name of the relationship to check
4371
+
4372
+ * @param callback - Optional callback to modify the relationship query
4373
+
4374
+ * @returns New query builder instance with the relationship existence check
4375
+
4376
+ */
2649
4377
  whereHas(relationName, callback) {
2650
4378
  const relation = this.env.table.relations[relationName];
2651
4379
  if (!relation) {
@@ -2660,11 +4388,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2660
4388
  return this.where(exists(finalSubAst));
2661
4389
  }
2662
4390
  /**
2663
- * Adds a WHERE NOT EXISTS condition based on a relationship
2664
- * @param relationName - Name of the relationship to check
2665
- * @param callback - Optional callback to modify the relationship query
2666
- * @returns New query builder instance with the relationship non-existence check
2667
- */
4391
+
4392
+ * Adds a WHERE NOT EXISTS condition based on a relationship
4393
+
4394
+ * @param relationName - Name of the relationship to check
4395
+
4396
+ * @param callback - Optional callback to modify the relationship query
4397
+
4398
+ * @returns New query builder instance with the relationship non-existence check
4399
+
4400
+ */
2668
4401
  whereHasNot(relationName, callback) {
2669
4402
  const relation = this.env.table.relations[relationName];
2670
4403
  if (!relation) {
@@ -2679,32 +4412,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2679
4412
  return this.where(notExists(finalSubAst));
2680
4413
  }
2681
4414
  /**
2682
- * Compiles the query to SQL for a specific dialect
2683
- * @param dialect - Database dialect to compile for
2684
- * @returns Compiled query with SQL and parameters
2685
- */
4415
+
4416
+ * Compiles the query to SQL for a specific dialect
4417
+
4418
+ * @param dialect - Database dialect to compile for
4419
+
4420
+ * @returns Compiled query with SQL and parameters
4421
+
4422
+ */
2686
4423
  compile(dialect) {
2687
- return dialect.compileSelect(this.context.state.ast);
4424
+ const resolved = resolveDialectInput(dialect);
4425
+ return resolved.compileSelect(this.context.state.ast);
2688
4426
  }
2689
4427
  /**
2690
- * Converts the query to SQL string for a specific dialect
2691
- * @param dialect - Database dialect to generate SQL for
2692
- * @returns SQL string representation of the query
2693
- */
4428
+
4429
+ * Converts the query to SQL string for a specific dialect
4430
+
4431
+ * @param dialect - Database dialect to generate SQL for
4432
+
4433
+ * @returns SQL string representation of the query
4434
+
4435
+ */
2694
4436
  toSql(dialect) {
2695
4437
  return this.compile(dialect).sql;
2696
4438
  }
2697
4439
  /**
2698
- * Gets the hydration plan for the query
2699
- * @returns Hydration plan or undefined if none exists
2700
- */
4440
+
4441
+ * Gets the hydration plan for the query
4442
+
4443
+ * @returns Hydration plan or undefined if none exists
4444
+
4445
+ */
2701
4446
  getHydrationPlan() {
2702
4447
  return this.context.hydration.getPlan();
2703
4448
  }
2704
4449
  /**
2705
- * Gets the Abstract Syntax Tree (AST) representation of the query
2706
- * @returns Query AST with hydration applied
2707
- */
4450
+
4451
+ * Gets the Abstract Syntax Tree (AST) representation of the query
4452
+
4453
+ * @returns Query AST with hydration applied
4454
+
4455
+ */
2708
4456
  getAST() {
2709
4457
  return this.context.hydration.applyToAst(this.context.state.ast);
2710
4458
  }
@@ -2735,6 +4483,15 @@ var buildRelationDefinitions = (meta, tableMap) => {
2735
4483
  const relations = {};
2736
4484
  for (const [name, relation] of Object.entries(meta.relations)) {
2737
4485
  switch (relation.kind) {
4486
+ case RelationKinds.HasOne: {
4487
+ relations[name] = hasOne(
4488
+ resolveTableTarget(relation.target, tableMap),
4489
+ relation.foreignKey,
4490
+ relation.localKey,
4491
+ relation.cascade
4492
+ );
4493
+ break;
4494
+ }
2738
4495
  case RelationKinds.HasMany: {
2739
4496
  relations[name] = hasMany(
2740
4497
  resolveTableTarget(relation.target, tableMap),
@@ -2805,6 +4562,7 @@ export {
2805
4562
  Column,
2806
4563
  Entity,
2807
4564
  HasMany,
4565
+ HasOne,
2808
4566
  PrimaryKey,
2809
4567
  bootstrapEntities,
2810
4568
  getTableDefFromEntity,