metal-orm 1.0.13 → 1.0.15

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 (115) hide show
  1. package/README.md +75 -82
  2. package/dist/decorators/index.cjs +1600 -27
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +6 -2
  5. package/dist/decorators/index.d.ts +6 -2
  6. package/dist/decorators/index.js +1599 -27
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +4608 -3429
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +511 -159
  11. package/dist/index.d.ts +511 -159
  12. package/dist/index.js +4526 -3415
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
  15. package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
  16. package/package.json +1 -1
  17. package/src/codegen/typescript.ts +38 -35
  18. package/src/core/ast/adapters.ts +21 -0
  19. package/src/core/ast/aggregate-functions.ts +13 -13
  20. package/src/core/ast/builders.ts +56 -43
  21. package/src/core/ast/expression-builders.ts +34 -34
  22. package/src/core/ast/expression-nodes.ts +18 -16
  23. package/src/core/ast/expression-visitor.ts +122 -69
  24. package/src/core/ast/expression.ts +6 -4
  25. package/src/core/ast/join-metadata.ts +15 -0
  26. package/src/core/ast/join-node.ts +22 -20
  27. package/src/core/ast/join.ts +5 -5
  28. package/src/core/ast/query.ts +52 -88
  29. package/src/core/ast/types.ts +20 -0
  30. package/src/core/ast/window-functions.ts +55 -55
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
  35. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
  36. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  37. package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
  38. package/src/core/ddl/introspect/context.ts +9 -0
  39. package/src/core/ddl/introspect/functions/postgres.ts +26 -0
  40. package/src/core/ddl/introspect/mssql.ts +149 -149
  41. package/src/core/ddl/introspect/mysql.ts +99 -99
  42. package/src/core/ddl/introspect/postgres.ts +245 -154
  43. package/src/core/ddl/introspect/registry.ts +26 -0
  44. package/src/core/ddl/introspect/run-select.ts +25 -0
  45. package/src/core/ddl/introspect/sqlite.ts +7 -7
  46. package/src/core/ddl/introspect/types.ts +23 -19
  47. package/src/core/ddl/introspect/utils.ts +1 -1
  48. package/src/core/ddl/naming-strategy.ts +10 -0
  49. package/src/core/ddl/schema-dialect.ts +41 -0
  50. package/src/core/ddl/schema-diff.ts +211 -179
  51. package/src/core/ddl/schema-generator.ts +16 -90
  52. package/src/core/ddl/schema-introspect.ts +25 -32
  53. package/src/core/ddl/schema-plan-executor.ts +17 -0
  54. package/src/core/ddl/schema-types.ts +46 -39
  55. package/src/core/ddl/sql-writing.ts +170 -0
  56. package/src/core/dialect/abstract.ts +144 -126
  57. package/src/core/dialect/base/cte-compiler.ts +33 -0
  58. package/src/core/dialect/base/function-table-formatter.ts +132 -0
  59. package/src/core/dialect/base/groupby-compiler.ts +21 -0
  60. package/src/core/dialect/base/join-compiler.ts +26 -0
  61. package/src/core/dialect/base/orderby-compiler.ts +21 -0
  62. package/src/core/dialect/base/pagination-strategy.ts +32 -0
  63. package/src/core/dialect/base/returning-strategy.ts +56 -0
  64. package/src/core/dialect/base/sql-dialect.ts +181 -204
  65. package/src/core/dialect/dialect-factory.ts +91 -0
  66. package/src/core/dialect/mssql/functions.ts +101 -0
  67. package/src/core/dialect/mssql/index.ts +128 -126
  68. package/src/core/dialect/mysql/functions.ts +101 -0
  69. package/src/core/dialect/mysql/index.ts +20 -18
  70. package/src/core/dialect/postgres/functions.ts +95 -0
  71. package/src/core/dialect/postgres/index.ts +30 -28
  72. package/src/core/dialect/sqlite/functions.ts +115 -0
  73. package/src/core/dialect/sqlite/index.ts +30 -28
  74. package/src/core/driver/database-driver.ts +11 -0
  75. package/src/core/driver/mssql-driver.ts +20 -0
  76. package/src/core/driver/mysql-driver.ts +20 -0
  77. package/src/core/driver/postgres-driver.ts +20 -0
  78. package/src/core/driver/sqlite-driver.ts +20 -0
  79. package/src/core/execution/db-executor.ts +63 -0
  80. package/src/core/execution/executors/mssql-executor.ts +39 -0
  81. package/src/core/execution/executors/mysql-executor.ts +47 -0
  82. package/src/core/execution/executors/postgres-executor.ts +32 -0
  83. package/src/core/execution/executors/sqlite-executor.ts +31 -0
  84. package/src/core/functions/datetime.ts +132 -0
  85. package/src/core/functions/numeric.ts +179 -0
  86. package/src/core/functions/standard-strategy.ts +47 -0
  87. package/src/core/functions/text.ts +147 -0
  88. package/src/core/functions/types.ts +18 -0
  89. package/src/core/hydration/types.ts +57 -0
  90. package/src/decorators/bootstrap.ts +10 -0
  91. package/src/decorators/relations.ts +15 -0
  92. package/src/index.ts +30 -19
  93. package/src/orm/entity-metadata.ts +7 -0
  94. package/src/orm/entity.ts +58 -27
  95. package/src/orm/hydration.ts +25 -17
  96. package/src/orm/lazy-batch.ts +46 -2
  97. package/src/orm/orm-context.ts +60 -60
  98. package/src/orm/query-logger.ts +1 -1
  99. package/src/orm/relation-change-processor.ts +43 -2
  100. package/src/orm/relations/has-one.ts +139 -0
  101. package/src/orm/transaction-runner.ts +1 -1
  102. package/src/orm/unit-of-work.ts +60 -60
  103. package/src/query-builder/delete.ts +22 -5
  104. package/src/query-builder/hydration-manager.ts +2 -1
  105. package/src/query-builder/hydration-planner.ts +8 -7
  106. package/src/query-builder/insert.ts +22 -5
  107. package/src/query-builder/relation-conditions.ts +9 -8
  108. package/src/query-builder/relation-service.ts +3 -2
  109. package/src/query-builder/select.ts +66 -61
  110. package/src/query-builder/update.ts +22 -5
  111. package/src/schema/column.ts +246 -246
  112. package/src/schema/relation.ts +35 -1
  113. package/src/schema/table.ts +28 -28
  114. package/src/schema/types.ts +41 -31
  115. package/src/orm/db-executor.ts +0 -11
@@ -24,6 +24,7 @@ __export(decorators_exports, {
24
24
  Column: () => Column,
25
25
  Entity: () => Entity,
26
26
  HasMany: () => HasMany,
27
+ HasOne: () => HasOne,
27
28
  PrimaryKey: () => PrimaryKey,
28
29
  bootstrapEntities: () => bootstrapEntities,
29
30
  getTableDefFromEntity: () => getTableDefFromEntity,
@@ -186,6 +187,8 @@ function PrimaryKey(definition) {
186
187
 
187
188
  // src/schema/relation.ts
188
189
  var RelationKinds = {
190
+ /** One-to-one relationship */
191
+ HasOne: "HAS_ONE",
189
192
  /** One-to-many relationship */
190
193
  HasMany: "HAS_MANY",
191
194
  /** Many-to-one relationship */
@@ -200,6 +203,13 @@ var hasMany = (target, foreignKey, localKey, cascade) => ({
200
203
  localKey,
201
204
  cascade
202
205
  });
206
+ var hasOne = (target, foreignKey, localKey, cascade) => ({
207
+ type: RelationKinds.HasOne,
208
+ target,
209
+ foreignKey,
210
+ localKey,
211
+ cascade
212
+ });
203
213
  var belongsTo = (target, foreignKey, localKey, cascade) => ({
204
214
  type: RelationKinds.BelongsTo,
205
215
  target,
@@ -260,6 +270,16 @@ function HasMany(options) {
260
270
  cascade: options.cascade
261
271
  }));
262
272
  }
273
+ function HasOne(options) {
274
+ return createFieldDecorator((propertyName) => ({
275
+ kind: RelationKinds.HasOne,
276
+ propertyKey: propertyName,
277
+ target: options.target,
278
+ foreignKey: options.foreignKey,
279
+ localKey: options.localKey,
280
+ cascade: options.cascade
281
+ }));
282
+ }
263
283
  function BelongsTo(options) {
264
284
  return createFieldDecorator((propertyName) => ({
265
285
  kind: RelationKinds.BelongsTo,
@@ -366,6 +386,1359 @@ var count = buildAggregate("COUNT");
366
386
  var sum = buildAggregate("SUM");
367
387
  var avg = buildAggregate("AVG");
368
388
 
389
+ // src/core/functions/standard-strategy.ts
390
+ var StandardFunctionStrategy = class {
391
+ constructor() {
392
+ this.renderers = /* @__PURE__ */ new Map();
393
+ this.registerStandard();
394
+ }
395
+ registerStandard() {
396
+ this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
397
+ this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
398
+ this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
399
+ this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
400
+ this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
401
+ this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
402
+ this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
403
+ this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
404
+ this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
405
+ this.add("NOW", () => `NOW()`);
406
+ this.add("CURRENT_DATE", () => `CURRENT_DATE`);
407
+ this.add("CURRENT_TIME", () => `CURRENT_TIME`);
408
+ this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
409
+ this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
410
+ this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
411
+ this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
412
+ this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
413
+ this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
414
+ this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
415
+ this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
416
+ this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
417
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
418
+ this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
419
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
420
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
421
+ this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
422
+ }
423
+ add(name, renderer) {
424
+ this.renderers.set(name, renderer);
425
+ }
426
+ getRenderer(name) {
427
+ return this.renderers.get(name);
428
+ }
429
+ };
430
+
431
+ // src/core/dialect/abstract.ts
432
+ var Dialect = class {
433
+ /**
434
+ * Compiles a SELECT query AST to SQL
435
+ * @param ast - Query AST to compile
436
+ * @returns Compiled query with SQL and parameters
437
+ */
438
+ compileSelect(ast) {
439
+ const ctx = this.createCompilerContext();
440
+ const normalized = this.normalizeSelectAst(ast);
441
+ const rawSql = this.compileSelectAst(normalized, ctx).trim();
442
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
443
+ return {
444
+ sql,
445
+ params: [...ctx.params]
446
+ };
447
+ }
448
+ compileInsert(ast) {
449
+ const ctx = this.createCompilerContext();
450
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
451
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
452
+ return {
453
+ sql,
454
+ params: [...ctx.params]
455
+ };
456
+ }
457
+ compileUpdate(ast) {
458
+ const ctx = this.createCompilerContext();
459
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
460
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
461
+ return {
462
+ sql,
463
+ params: [...ctx.params]
464
+ };
465
+ }
466
+ compileDelete(ast) {
467
+ const ctx = this.createCompilerContext();
468
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
469
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
470
+ return {
471
+ sql,
472
+ params: [...ctx.params]
473
+ };
474
+ }
475
+ supportsReturning() {
476
+ return false;
477
+ }
478
+ /**
479
+ * Compiles a WHERE clause
480
+ * @param where - WHERE expression
481
+ * @param ctx - Compiler context
482
+ * @returns SQL WHERE clause or empty string
483
+ */
484
+ compileWhere(where, ctx) {
485
+ if (!where) return "";
486
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
487
+ }
488
+ compileReturning(returning, ctx) {
489
+ if (!returning || returning.length === 0) return "";
490
+ throw new Error("RETURNING is not supported by this dialect.");
491
+ }
492
+ /**
493
+ * Generates subquery for EXISTS expressions
494
+ * Rule: Always forces SELECT 1, ignoring column list
495
+ * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
496
+ * Does not add ';' at the end
497
+ * @param ast - Query AST
498
+ * @param ctx - Compiler context
499
+ * @returns SQL for EXISTS subquery
500
+ */
501
+ compileSelectForExists(ast, ctx) {
502
+ const normalized = this.normalizeSelectAst(ast);
503
+ const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
504
+ if (normalized.setOps && normalized.setOps.length > 0) {
505
+ return `SELECT 1 FROM (${full}) AS _exists`;
506
+ }
507
+ const upper = full.toUpperCase();
508
+ const fromIndex = upper.indexOf(" FROM ");
509
+ if (fromIndex === -1) {
510
+ return full;
511
+ }
512
+ const tail = full.slice(fromIndex);
513
+ return `SELECT 1${tail}`;
514
+ }
515
+ /**
516
+ * Creates a new compiler context
517
+ * @returns Compiler context with parameter management
518
+ */
519
+ createCompilerContext() {
520
+ const params = [];
521
+ let counter = 0;
522
+ return {
523
+ params,
524
+ addParameter: (value) => {
525
+ counter += 1;
526
+ params.push(value);
527
+ return this.formatPlaceholder(counter);
528
+ }
529
+ };
530
+ }
531
+ /**
532
+ * Formats a parameter placeholder
533
+ * @param index - Parameter index
534
+ * @returns Formatted placeholder string
535
+ */
536
+ formatPlaceholder(index) {
537
+ return "?";
538
+ }
539
+ /**
540
+ * Whether the current dialect supports a given set operation.
541
+ * Override in concrete dialects to restrict support.
542
+ */
543
+ supportsSetOperation(kind) {
544
+ return true;
545
+ }
546
+ /**
547
+ * Validates set-operation semantics:
548
+ * - Ensures the dialect supports requested operators.
549
+ * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
550
+ * @param ast - Query to validate
551
+ * @param isOutermost - Whether this node is the outermost compound query
552
+ */
553
+ validateSetOperations(ast, isOutermost = true) {
554
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
555
+ if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
556
+ throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
557
+ }
558
+ if (hasSetOps) {
559
+ for (const op of ast.setOps) {
560
+ if (!this.supportsSetOperation(op.operator)) {
561
+ throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
562
+ }
563
+ this.validateSetOperations(op.query, false);
564
+ }
565
+ }
566
+ }
567
+ /**
568
+ * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
569
+ * @param ast - Query AST
570
+ * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
571
+ */
572
+ hoistCtes(ast) {
573
+ let hoisted = [];
574
+ const normalizedSetOps = ast.setOps?.map((op) => {
575
+ const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
576
+ const childCtes = child.ctes ?? [];
577
+ if (childCtes.length) {
578
+ hoisted = hoisted.concat(childCtes);
579
+ }
580
+ hoisted = hoisted.concat(childHoisted);
581
+ const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
582
+ return { ...op, query: queryWithoutCtes };
583
+ });
584
+ const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
585
+ return { normalized, hoistedCtes: hoisted };
586
+ }
587
+ /**
588
+ * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
589
+ * @param ast - Query AST
590
+ * @returns Normalized query AST
591
+ */
592
+ normalizeSelectAst(ast) {
593
+ this.validateSetOperations(ast, true);
594
+ const { normalized, hoistedCtes } = this.hoistCtes(ast);
595
+ const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
596
+ return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
597
+ }
598
+ constructor(functionStrategy) {
599
+ this.expressionCompilers = /* @__PURE__ */ new Map();
600
+ this.operandCompilers = /* @__PURE__ */ new Map();
601
+ this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
602
+ this.registerDefaultOperandCompilers();
603
+ this.registerDefaultExpressionCompilers();
604
+ }
605
+ /**
606
+ * Registers an expression compiler for a specific node type
607
+ * @param type - Expression node type
608
+ * @param compiler - Compiler function
609
+ */
610
+ registerExpressionCompiler(type, compiler) {
611
+ this.expressionCompilers.set(type, compiler);
612
+ }
613
+ /**
614
+ * Registers an operand compiler for a specific node type
615
+ * @param type - Operand node type
616
+ * @param compiler - Compiler function
617
+ */
618
+ registerOperandCompiler(type, compiler) {
619
+ this.operandCompilers.set(type, compiler);
620
+ }
621
+ /**
622
+ * Compiles an expression node
623
+ * @param node - Expression node to compile
624
+ * @param ctx - Compiler context
625
+ * @returns Compiled SQL expression
626
+ */
627
+ compileExpression(node, ctx) {
628
+ const compiler = this.expressionCompilers.get(node.type);
629
+ if (!compiler) {
630
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
631
+ }
632
+ return compiler(node, ctx);
633
+ }
634
+ /**
635
+ * Compiles an operand node
636
+ * @param node - Operand node to compile
637
+ * @param ctx - Compiler context
638
+ * @returns Compiled SQL operand
639
+ */
640
+ compileOperand(node, ctx) {
641
+ const compiler = this.operandCompilers.get(node.type);
642
+ if (!compiler) {
643
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
644
+ }
645
+ return compiler(node, ctx);
646
+ }
647
+ registerDefaultExpressionCompilers() {
648
+ this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
649
+ const left = this.compileOperand(binary.left, ctx);
650
+ const right = this.compileOperand(binary.right, ctx);
651
+ const base = `${left} ${binary.operator} ${right}`;
652
+ if (binary.escape) {
653
+ const escapeOperand = this.compileOperand(binary.escape, ctx);
654
+ return `${base} ESCAPE ${escapeOperand}`;
655
+ }
656
+ return base;
657
+ });
658
+ this.registerExpressionCompiler("LogicalExpression", (logical, ctx) => {
659
+ if (logical.operands.length === 0) return "";
660
+ const parts = logical.operands.map((op) => {
661
+ const compiled = this.compileExpression(op, ctx);
662
+ return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
663
+ });
664
+ return parts.join(` ${logical.operator} `);
665
+ });
666
+ this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
667
+ const left = this.compileOperand(nullExpr.left, ctx);
668
+ return `${left} ${nullExpr.operator}`;
669
+ });
670
+ this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
671
+ const left = this.compileOperand(inExpr.left, ctx);
672
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
673
+ return `${left} ${inExpr.operator} (${values})`;
674
+ });
675
+ this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
676
+ const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
677
+ return `${existsExpr.operator} (${subquerySql})`;
678
+ });
679
+ this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
680
+ const left = this.compileOperand(betweenExpr.left, ctx);
681
+ const lower = this.compileOperand(betweenExpr.lower, ctx);
682
+ const upper = this.compileOperand(betweenExpr.upper, ctx);
683
+ return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
684
+ });
685
+ }
686
+ registerDefaultOperandCompilers() {
687
+ this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
688
+ this.registerOperandCompiler("Column", (column, _ctx) => {
689
+ return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
690
+ });
691
+ this.registerOperandCompiler(
692
+ "Function",
693
+ (fnNode, ctx) => this.compileFunctionOperand(fnNode, ctx)
694
+ );
695
+ this.registerOperandCompiler("JsonPath", (path, _ctx) => this.compileJsonPath(path));
696
+ this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
697
+ const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
698
+ return `(${sql})`;
699
+ });
700
+ this.registerOperandCompiler("CaseExpression", (node, ctx) => {
701
+ const parts = ["CASE"];
702
+ for (const { when, then } of node.conditions) {
703
+ parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
704
+ }
705
+ if (node.else) {
706
+ parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
707
+ }
708
+ parts.push("END");
709
+ return parts.join(" ");
710
+ });
711
+ this.registerOperandCompiler("WindowFunction", (node, ctx) => {
712
+ let result = `${node.name}(`;
713
+ if (node.args.length > 0) {
714
+ result += node.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
715
+ }
716
+ result += ") OVER (";
717
+ const parts = [];
718
+ if (node.partitionBy && node.partitionBy.length > 0) {
719
+ const partitionClause = "PARTITION BY " + node.partitionBy.map(
720
+ (col) => `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
721
+ ).join(", ");
722
+ parts.push(partitionClause);
723
+ }
724
+ if (node.orderBy && node.orderBy.length > 0) {
725
+ const orderClause = "ORDER BY " + node.orderBy.map(
726
+ (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
727
+ ).join(", ");
728
+ parts.push(orderClause);
729
+ }
730
+ result += parts.join(" ");
731
+ result += ")";
732
+ return result;
733
+ });
734
+ }
735
+ // Default fallback, should be overridden by dialects if supported
736
+ compileJsonPath(node) {
737
+ throw new Error("JSON Path not supported by this dialect");
738
+ }
739
+ /**
740
+ * Compiles a function operand, using the dialect's function strategy.
741
+ */
742
+ compileFunctionOperand(fnNode, ctx) {
743
+ const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
744
+ const renderer = this.functionStrategy.getRenderer(fnNode.name);
745
+ if (renderer) {
746
+ return renderer({ node: fnNode, compiledArgs });
747
+ }
748
+ return `${fnNode.name}(${compiledArgs.join(", ")})`;
749
+ }
750
+ };
751
+
752
+ // src/core/dialect/base/function-table-formatter.ts
753
+ var FunctionTableFormatter = class {
754
+ /**
755
+ * Formats a function table node into SQL syntax.
756
+ * @param fn - The function table node containing schema, name, args, and aliases.
757
+ * @param ctx - Optional compiler context for operand compilation.
758
+ * @param dialect - The dialect instance for compiling operands.
759
+ * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
760
+ */
761
+ static format(fn, ctx, dialect) {
762
+ const schemaPart = this.formatSchema(fn, dialect);
763
+ const args = this.formatArgs(fn, ctx, dialect);
764
+ const base = this.formatBase(fn, schemaPart, args, dialect);
765
+ const lateral = this.formatLateral(fn);
766
+ const alias = this.formatAlias(fn, dialect);
767
+ const colAliases = this.formatColumnAliases(fn, dialect);
768
+ return `${lateral}${base}${alias}${colAliases}`;
769
+ }
770
+ /**
771
+ * Formats the schema prefix for the function name.
772
+ * @param fn - The function table node.
773
+ * @param dialect - The dialect instance for quoting identifiers.
774
+ * @returns Schema prefix (e.g., "schema.") or empty string.
775
+ * @internal
776
+ */
777
+ static formatSchema(fn, dialect) {
778
+ if (!fn.schema) return "";
779
+ const quoted = dialect ? dialect.quoteIdentifier(fn.schema) : fn.schema;
780
+ return `${quoted}.`;
781
+ }
782
+ /**
783
+ * Formats function arguments into SQL syntax.
784
+ * @param fn - The function table node containing arguments.
785
+ * @param ctx - Optional compiler context for operand compilation.
786
+ * @param dialect - The dialect instance for compiling operands.
787
+ * @returns Comma-separated function arguments.
788
+ * @internal
789
+ */
790
+ static formatArgs(fn, ctx, dialect) {
791
+ return (fn.args || []).map((a) => {
792
+ if (ctx && dialect) {
793
+ return dialect.compileOperand(a, ctx);
794
+ }
795
+ return String(a);
796
+ }).join(", ");
797
+ }
798
+ /**
799
+ * Formats the base function call with WITH ORDINALITY if present.
800
+ * @param fn - The function table node.
801
+ * @param schemaPart - Formatted schema prefix.
802
+ * @param args - Formatted function arguments.
803
+ * @param dialect - The dialect instance for quoting identifiers.
804
+ * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
805
+ * @internal
806
+ */
807
+ static formatBase(fn, schemaPart, args, dialect) {
808
+ const ordinality = fn.withOrdinality ? " WITH ORDINALITY" : "";
809
+ const quoted = dialect ? dialect.quoteIdentifier(fn.name) : fn.name;
810
+ return `${schemaPart}${quoted}(${args})${ordinality}`;
811
+ }
812
+ /**
813
+ * Formats the LATERAL keyword if present.
814
+ * @param fn - The function table node.
815
+ * @returns "LATERAL " or empty string.
816
+ * @internal
817
+ */
818
+ static formatLateral(fn) {
819
+ return fn.lateral ? "LATERAL " : "";
820
+ }
821
+ /**
822
+ * Formats the table alias for the function table.
823
+ * @param fn - The function table node.
824
+ * @param dialect - The dialect instance for quoting identifiers.
825
+ * @returns " AS alias" or empty string.
826
+ * @internal
827
+ */
828
+ static formatAlias(fn, dialect) {
829
+ if (!fn.alias) return "";
830
+ const quoted = dialect ? dialect.quoteIdentifier(fn.alias) : fn.alias;
831
+ return ` AS ${quoted}`;
832
+ }
833
+ /**
834
+ * Formats column aliases for the function table result columns.
835
+ * @param fn - The function table node containing column aliases.
836
+ * @param dialect - The dialect instance for quoting identifiers.
837
+ * @returns "(col1, col2, ...)" or empty string.
838
+ * @internal
839
+ */
840
+ static formatColumnAliases(fn, dialect) {
841
+ if (!fn.columnAliases || !fn.columnAliases.length) return "";
842
+ const aliases = fn.columnAliases.map((col) => dialect ? dialect.quoteIdentifier(col) : col).join(", ");
843
+ return `(${aliases})`;
844
+ }
845
+ };
846
+
847
+ // src/core/dialect/base/pagination-strategy.ts
848
+ var StandardLimitOffsetPagination = class {
849
+ /**
850
+ * Compiles LIMIT/OFFSET pagination clause.
851
+ * @param limit - The maximum number of rows to return.
852
+ * @param offset - The number of rows to skip.
853
+ * @returns SQL pagination clause with LIMIT and/or OFFSET.
854
+ */
855
+ compilePagination(limit, offset) {
856
+ const parts = [];
857
+ if (limit !== void 0) parts.push(`LIMIT ${limit}`);
858
+ if (offset !== void 0) parts.push(`OFFSET ${offset}`);
859
+ return parts.length ? ` ${parts.join(" ")}` : "";
860
+ }
861
+ };
862
+
863
+ // src/core/dialect/base/cte-compiler.ts
864
+ var CteCompiler = class {
865
+ /**
866
+ * Compiles CTEs (WITH clauses) including recursive CTEs.
867
+ * @param ast - The SELECT query AST containing CTE definitions.
868
+ * @param ctx - The compiler context for expression compilation.
869
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
870
+ * @param compileSelectAst - Function to recursively compile SELECT query ASTs.
871
+ * @param normalizeSelectAst - Function to normalize SELECT query ASTs before compilation.
872
+ * @param stripTrailingSemicolon - Function to remove trailing semicolons from SQL.
873
+ * @returns SQL WITH clause string (e.g., "WITH cte_name AS (...) ") or empty string if no CTEs.
874
+ */
875
+ static compileCtes(ast, ctx, quoteIdentifier, compileSelectAst, normalizeSelectAst, stripTrailingSemicolon) {
876
+ if (!ast.ctes || ast.ctes.length === 0) return "";
877
+ const hasRecursive = ast.ctes.some((cte) => cte.recursive);
878
+ const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
879
+ const cteDefs = ast.ctes.map((cte) => {
880
+ const name = quoteIdentifier(cte.name);
881
+ const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => quoteIdentifier(c)).join(", ")})` : "";
882
+ const query = stripTrailingSemicolon(compileSelectAst(normalizeSelectAst(cte.query), ctx));
883
+ return `${name}${cols} AS (${query})`;
884
+ }).join(", ");
885
+ return `${prefix}${cteDefs} `;
886
+ }
887
+ };
888
+
889
+ // src/core/dialect/base/returning-strategy.ts
890
+ var NoReturningStrategy = class {
891
+ /**
892
+ * Throws an error as RETURNING is not supported.
893
+ * @param returning - Columns to return (causes error if non-empty).
894
+ * @param _ctx - Compiler context (unused).
895
+ * @throws Error indicating RETURNING is not supported.
896
+ */
897
+ compileReturning(returning, _ctx) {
898
+ if (!returning || returning.length === 0) return "";
899
+ throw new Error("RETURNING is not supported by this dialect.");
900
+ }
901
+ /**
902
+ * Formats column names for RETURNING clause.
903
+ * @param returning - Columns to format.
904
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
905
+ * @returns Simple comma-separated column names.
906
+ */
907
+ formatReturningColumns(returning, quoteIdentifier) {
908
+ return returning.map((column) => {
909
+ const tablePart = column.table ? `${quoteIdentifier(column.table)}.` : "";
910
+ const aliasPart = column.alias ? ` AS ${quoteIdentifier(column.alias)}` : "";
911
+ return `${tablePart}${quoteIdentifier(column.name)}${aliasPart}`;
912
+ }).join(", ");
913
+ }
914
+ };
915
+
916
+ // src/core/dialect/base/join-compiler.ts
917
+ var JoinCompiler = class {
918
+ /**
919
+ * Compiles all JOIN clauses from a SELECT query AST.
920
+ * @param ast - The SELECT query AST containing join definitions.
921
+ * @param ctx - The compiler context for expression compilation.
922
+ * @param compileFrom - Function to compile table sources (tables or subqueries).
923
+ * @param compileExpression - Function to compile join condition expressions.
924
+ * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
925
+ */
926
+ static compileJoins(ast, ctx, compileFrom, compileExpression) {
927
+ if (!ast.joins || ast.joins.length === 0) return "";
928
+ const parts = ast.joins.map((j) => {
929
+ const table = compileFrom(j.table, ctx);
930
+ const cond = compileExpression(j.condition, ctx);
931
+ return `${j.kind} JOIN ${table} ON ${cond}`;
932
+ });
933
+ return ` ${parts.join(" ")}`;
934
+ }
935
+ };
936
+
937
+ // src/core/dialect/base/groupby-compiler.ts
938
+ var GroupByCompiler = class {
939
+ /**
940
+ * Compiles GROUP BY clause from a SELECT query AST.
941
+ * @param ast - The SELECT query AST containing grouping columns.
942
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
943
+ * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
944
+ */
945
+ static compileGroupBy(ast, quoteIdentifier) {
946
+ if (!ast.groupBy || ast.groupBy.length === 0) return "";
947
+ const cols = ast.groupBy.map((c) => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`).join(", ");
948
+ return ` GROUP BY ${cols}`;
949
+ }
950
+ };
951
+
952
+ // src/core/dialect/base/orderby-compiler.ts
953
+ var OrderByCompiler = class {
954
+ /**
955
+ * Compiles ORDER BY clause from a SELECT query AST.
956
+ * @param ast - The SELECT query AST containing sort specifications.
957
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
958
+ * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
959
+ */
960
+ static compileOrderBy(ast, quoteIdentifier) {
961
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
962
+ const parts = ast.orderBy.map((o) => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
963
+ return ` ORDER BY ${parts}`;
964
+ }
965
+ };
966
+
967
+ // src/core/dialect/base/sql-dialect.ts
968
+ var SqlDialectBase = class extends Dialect {
969
+ constructor() {
970
+ super(...arguments);
971
+ this.paginationStrategy = new StandardLimitOffsetPagination();
972
+ this.returningStrategy = new NoReturningStrategy();
973
+ }
974
+ compileSelectAst(ast, ctx) {
975
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
976
+ const ctes = CteCompiler.compileCtes(
977
+ ast,
978
+ ctx,
979
+ this.quoteIdentifier.bind(this),
980
+ this.compileSelectAst.bind(this),
981
+ this.normalizeSelectAst?.bind(this) ?? ((a) => a),
982
+ this.stripTrailingSemicolon.bind(this)
983
+ );
984
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
985
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
986
+ if (!hasSetOps) {
987
+ return `${ctes}${baseSelect}`;
988
+ }
989
+ return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
990
+ }
991
+ compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
992
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
993
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
994
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
995
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
996
+ return `${ctes}${combined}${orderBy}${pagination}`;
997
+ }
998
+ compileInsertAst(ast, ctx) {
999
+ const table = this.compileTableName(ast.into);
1000
+ const columnList = this.compileInsertColumnList(ast.columns);
1001
+ const values = this.compileInsertValues(ast.values, ctx);
1002
+ const returning = this.compileReturning(ast.returning, ctx);
1003
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
1004
+ }
1005
+ compileReturning(returning, ctx) {
1006
+ return this.returningStrategy.compileReturning(returning, ctx);
1007
+ }
1008
+ compileInsertColumnList(columns) {
1009
+ return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1010
+ }
1011
+ compileInsertValues(values, ctx) {
1012
+ return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1013
+ }
1014
+ compileSelectCore(ast, ctx) {
1015
+ const columns = this.compileSelectColumns(ast, ctx);
1016
+ const from = this.compileFrom(ast.from, ctx);
1017
+ const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
1018
+ const whereClause = this.compileWhere(ast.where, ctx);
1019
+ const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
1020
+ const having = this.compileHaving(ast, ctx);
1021
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1022
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1023
+ return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
1024
+ }
1025
+ compileUpdateAst(ast, ctx) {
1026
+ const table = this.compileTableName(ast.table);
1027
+ const assignments = this.compileUpdateAssignments(ast.set, ctx);
1028
+ const whereClause = this.compileWhere(ast.where, ctx);
1029
+ const returning = this.compileReturning(ast.returning, ctx);
1030
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
1031
+ }
1032
+ compileUpdateAssignments(assignments, ctx) {
1033
+ return assignments.map((assignment) => {
1034
+ const col = assignment.column;
1035
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1036
+ const value = this.compileOperand(assignment.value, ctx);
1037
+ return `${target} = ${value}`;
1038
+ }).join(", ");
1039
+ }
1040
+ compileDeleteAst(ast, ctx) {
1041
+ const table = this.compileTableName(ast.from);
1042
+ const whereClause = this.compileWhere(ast.where, ctx);
1043
+ const returning = this.compileReturning(ast.returning, ctx);
1044
+ return `DELETE FROM ${table}${whereClause}${returning}`;
1045
+ }
1046
+ formatReturningColumns(returning) {
1047
+ return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
1048
+ }
1049
+ compileDistinct(ast) {
1050
+ return ast.distinct ? "DISTINCT " : "";
1051
+ }
1052
+ compileSelectColumns(ast, ctx) {
1053
+ return ast.columns.map((c) => {
1054
+ const expr = this.compileOperand(c, ctx);
1055
+ if (c.alias) {
1056
+ if (c.alias.includes("(")) return c.alias;
1057
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1058
+ }
1059
+ return expr;
1060
+ }).join(", ");
1061
+ }
1062
+ compileFrom(ast, ctx) {
1063
+ const tableSource = ast;
1064
+ if (tableSource.type === "FunctionTable") {
1065
+ return this.compileFunctionTable(tableSource, ctx);
1066
+ }
1067
+ return this.compileTableSource(tableSource);
1068
+ }
1069
+ compileFunctionTable(fn, ctx) {
1070
+ return FunctionTableFormatter.format(fn, ctx, this);
1071
+ }
1072
+ compileTableSource(table) {
1073
+ const base = this.compileTableName(table);
1074
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1075
+ }
1076
+ compileTableName(table) {
1077
+ if (table.schema) {
1078
+ return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
1079
+ }
1080
+ return this.quoteIdentifier(table.name);
1081
+ }
1082
+ compileHaving(ast, ctx) {
1083
+ if (!ast.having) return "";
1084
+ return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
1085
+ }
1086
+ stripTrailingSemicolon(sql) {
1087
+ return sql.trim().replace(/;$/, "");
1088
+ }
1089
+ wrapSetOperand(sql) {
1090
+ const trimmed = this.stripTrailingSemicolon(sql);
1091
+ return `(${trimmed})`;
1092
+ }
1093
+ };
1094
+
1095
+ // src/core/dialect/postgres/functions.ts
1096
+ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1097
+ constructor() {
1098
+ super();
1099
+ this.registerOverrides();
1100
+ }
1101
+ registerOverrides() {
1102
+ this.add("UTC_NOW", () => `(NOW() AT TIME ZONE 'UTC')`);
1103
+ this.add("UNIX_TIMESTAMP", () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
1104
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1105
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1106
+ return `to_timestamp(${compiledArgs[0]})`;
1107
+ });
1108
+ this.add("EXTRACT", ({ compiledArgs }) => {
1109
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1110
+ const [part, date] = compiledArgs;
1111
+ const partClean = part.replace(/['"]/g, "");
1112
+ return `EXTRACT(${partClean} FROM ${date})`;
1113
+ });
1114
+ this.add("YEAR", ({ compiledArgs }) => {
1115
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1116
+ return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
1117
+ });
1118
+ this.add("MONTH", ({ compiledArgs }) => {
1119
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1120
+ return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
1121
+ });
1122
+ this.add("DAY", ({ compiledArgs }) => {
1123
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1124
+ return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
1125
+ });
1126
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1127
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1128
+ const [date, interval] = compiledArgs;
1129
+ const unitArg = node.args[2];
1130
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1131
+ return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
1132
+ });
1133
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1134
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1135
+ const [date, interval] = compiledArgs;
1136
+ const unitArg = node.args[2];
1137
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1138
+ return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
1139
+ });
1140
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1141
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1142
+ const [date1, date2] = compiledArgs;
1143
+ return `(${date1}::DATE - ${date2}::DATE)`;
1144
+ });
1145
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1146
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1147
+ const [date, format] = compiledArgs;
1148
+ return `TO_CHAR(${date}, ${format})`;
1149
+ });
1150
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1151
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1152
+ return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
1153
+ });
1154
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1155
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1156
+ return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
1157
+ });
1158
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1159
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1160
+ return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
1161
+ });
1162
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1163
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1164
+ const [, date] = compiledArgs;
1165
+ const partArg = node.args[0];
1166
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1167
+ return `DATE_TRUNC('${partClean}', ${date})`;
1168
+ });
1169
+ }
1170
+ };
1171
+
1172
+ // src/core/dialect/postgres/index.ts
1173
+ var PostgresDialect = class extends SqlDialectBase {
1174
+ /**
1175
+ * Creates a new PostgresDialect instance
1176
+ */
1177
+ constructor() {
1178
+ super(new PostgresFunctionStrategy());
1179
+ this.dialect = "postgres";
1180
+ }
1181
+ /**
1182
+ * Quotes an identifier using PostgreSQL double-quote syntax
1183
+ * @param id - Identifier to quote
1184
+ * @returns Quoted identifier
1185
+ */
1186
+ quoteIdentifier(id) {
1187
+ return `"${id}"`;
1188
+ }
1189
+ /**
1190
+ * Compiles JSON path expression using PostgreSQL syntax
1191
+ * @param node - JSON path node
1192
+ * @returns PostgreSQL JSON path expression
1193
+ */
1194
+ compileJsonPath(node) {
1195
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1196
+ return `${col}->>'${node.path}'`;
1197
+ }
1198
+ compileReturning(returning, ctx) {
1199
+ if (!returning || returning.length === 0) return "";
1200
+ const columns = this.formatReturningColumns(returning);
1201
+ return ` RETURNING ${columns}`;
1202
+ }
1203
+ supportsReturning() {
1204
+ return true;
1205
+ }
1206
+ };
1207
+
1208
+ // src/core/dialect/mysql/functions.ts
1209
+ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
1210
+ constructor() {
1211
+ super();
1212
+ this.registerOverrides();
1213
+ }
1214
+ registerOverrides() {
1215
+ this.add("NOW", () => `NOW()`);
1216
+ this.add("CURRENT_DATE", () => `CURDATE()`);
1217
+ this.add("CURRENT_TIME", () => `CURTIME()`);
1218
+ this.add("UTC_NOW", () => `UTC_TIMESTAMP()`);
1219
+ this.add("EXTRACT", ({ compiledArgs }) => {
1220
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1221
+ const [part, date] = compiledArgs;
1222
+ const partClean = part.replace(/['"]/g, "");
1223
+ return `EXTRACT(${partClean} FROM ${date})`;
1224
+ });
1225
+ this.add("YEAR", ({ compiledArgs }) => {
1226
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1227
+ return `YEAR(${compiledArgs[0]})`;
1228
+ });
1229
+ this.add("MONTH", ({ compiledArgs }) => {
1230
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1231
+ return `MONTH(${compiledArgs[0]})`;
1232
+ });
1233
+ this.add("DAY", ({ compiledArgs }) => {
1234
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1235
+ return `DAY(${compiledArgs[0]})`;
1236
+ });
1237
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1238
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1239
+ const [date, interval] = compiledArgs;
1240
+ const unitArg = node.args[2];
1241
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1242
+ return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
1243
+ });
1244
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1245
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1246
+ const [date, interval] = compiledArgs;
1247
+ const unitArg = node.args[2];
1248
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1249
+ return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
1250
+ });
1251
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1252
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1253
+ const [date1, date2] = compiledArgs;
1254
+ return `DATEDIFF(${date1}, ${date2})`;
1255
+ });
1256
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1257
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1258
+ const [date, format] = compiledArgs;
1259
+ return `DATE_FORMAT(${date}, ${format})`;
1260
+ });
1261
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1262
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1263
+ return `LAST_DAY(${compiledArgs[0]})`;
1264
+ });
1265
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1266
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1267
+ return `DAYOFWEEK(${compiledArgs[0]})`;
1268
+ });
1269
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1270
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1271
+ return `WEEKOFYEAR(${compiledArgs[0]})`;
1272
+ });
1273
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1274
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1275
+ const [, date] = compiledArgs;
1276
+ const partArg = node.args[0];
1277
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1278
+ if (partClean === "year") {
1279
+ return `DATE_FORMAT(${date}, '%Y-01-01')`;
1280
+ } else if (partClean === "month") {
1281
+ return `DATE_FORMAT(${date}, '%Y-%m-01')`;
1282
+ } else if (partClean === "day") {
1283
+ return `DATE(${date})`;
1284
+ }
1285
+ return `DATE(${date})`;
1286
+ });
1287
+ }
1288
+ };
1289
+
1290
+ // src/core/dialect/mysql/index.ts
1291
+ var MySqlDialect = class extends SqlDialectBase {
1292
+ /**
1293
+ * Creates a new MySqlDialect instance
1294
+ */
1295
+ constructor() {
1296
+ super(new MysqlFunctionStrategy());
1297
+ this.dialect = "mysql";
1298
+ }
1299
+ /**
1300
+ * Quotes an identifier using MySQL backtick syntax
1301
+ * @param id - Identifier to quote
1302
+ * @returns Quoted identifier
1303
+ */
1304
+ quoteIdentifier(id) {
1305
+ return `\`${id}\``;
1306
+ }
1307
+ /**
1308
+ * Compiles JSON path expression using MySQL syntax
1309
+ * @param node - JSON path node
1310
+ * @returns MySQL JSON path expression
1311
+ */
1312
+ compileJsonPath(node) {
1313
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1314
+ return `${col}->'${node.path}'`;
1315
+ }
1316
+ };
1317
+
1318
+ // src/core/dialect/sqlite/functions.ts
1319
+ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1320
+ constructor() {
1321
+ super();
1322
+ this.registerOverrides();
1323
+ }
1324
+ registerOverrides() {
1325
+ this.add("NOW", () => `datetime('now', 'localtime')`);
1326
+ this.add("CURRENT_DATE", () => `date('now', 'localtime')`);
1327
+ this.add("CURRENT_TIME", () => `time('now', 'localtime')`);
1328
+ this.add("UTC_NOW", () => `datetime('now')`);
1329
+ this.add("EXTRACT", ({ compiledArgs }) => {
1330
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1331
+ const [part, date] = compiledArgs;
1332
+ const partUpper = part.replace(/['"]/g, "").toUpperCase();
1333
+ const formatMap = {
1334
+ "YEAR": "%Y",
1335
+ "MONTH": "%m",
1336
+ "DAY": "%d",
1337
+ "HOUR": "%H",
1338
+ "MINUTE": "%M",
1339
+ "SECOND": "%S",
1340
+ "DOW": "%w",
1341
+ "WEEK": "%W"
1342
+ };
1343
+ const format = formatMap[partUpper] || "%Y";
1344
+ return `CAST(strftime('${format}', ${date}) AS INTEGER)`;
1345
+ });
1346
+ this.add("YEAR", ({ compiledArgs }) => {
1347
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1348
+ return `CAST(strftime('%Y', ${compiledArgs[0]}) AS INTEGER)`;
1349
+ });
1350
+ this.add("MONTH", ({ compiledArgs }) => {
1351
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1352
+ return `CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER)`;
1353
+ });
1354
+ this.add("DAY", ({ compiledArgs }) => {
1355
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1356
+ return `CAST(strftime('%d', ${compiledArgs[0]}) AS INTEGER)`;
1357
+ });
1358
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1359
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1360
+ const [date, interval] = compiledArgs;
1361
+ const unitArg = node.args[2];
1362
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1363
+ return `datetime(${date}, '+' || ${interval} || ' ${unitClean}')`;
1364
+ });
1365
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1366
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1367
+ const [date, interval] = compiledArgs;
1368
+ const unitArg = node.args[2];
1369
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1370
+ return `datetime(${date}, '-' || ${interval} || ' ${unitClean}')`;
1371
+ });
1372
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1373
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1374
+ const [date1, date2] = compiledArgs;
1375
+ return `CAST(julianday(${date1}) - julianday(${date2}) AS INTEGER)`;
1376
+ });
1377
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1378
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1379
+ const [date, format] = compiledArgs;
1380
+ return `strftime(${format}, ${date})`;
1381
+ });
1382
+ this.add("UNIX_TIMESTAMP", () => `CAST(strftime('%s', 'now') AS INTEGER)`);
1383
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1384
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1385
+ return `datetime(${compiledArgs[0]}, 'unixepoch')`;
1386
+ });
1387
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1388
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1389
+ return `date(${compiledArgs[0]}, 'start of month', '+1 month', '-1 day')`;
1390
+ });
1391
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1392
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1393
+ return `CAST(strftime('%w', ${compiledArgs[0]}) AS INTEGER)`;
1394
+ });
1395
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1396
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1397
+ return `CAST(strftime('%W', ${compiledArgs[0]}) AS INTEGER)`;
1398
+ });
1399
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1400
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1401
+ const [, date] = compiledArgs;
1402
+ const partArg = node.args[0];
1403
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1404
+ if (partClean === "year") {
1405
+ return `date(${date}, 'start of year')`;
1406
+ } else if (partClean === "month") {
1407
+ return `date(${date}, 'start of month')`;
1408
+ } else if (partClean === "day") {
1409
+ return `date(${date})`;
1410
+ }
1411
+ return `date(${date}, 'start of ${partClean}')`;
1412
+ });
1413
+ }
1414
+ };
1415
+
1416
+ // src/core/dialect/sqlite/index.ts
1417
+ var SqliteDialect = class extends SqlDialectBase {
1418
+ /**
1419
+ * Creates a new SqliteDialect instance
1420
+ */
1421
+ constructor() {
1422
+ super(new SqliteFunctionStrategy());
1423
+ this.dialect = "sqlite";
1424
+ }
1425
+ /**
1426
+ * Quotes an identifier using SQLite double-quote syntax
1427
+ * @param id - Identifier to quote
1428
+ * @returns Quoted identifier
1429
+ */
1430
+ quoteIdentifier(id) {
1431
+ return `"${id}"`;
1432
+ }
1433
+ /**
1434
+ * Compiles JSON path expression using SQLite syntax
1435
+ * @param node - JSON path node
1436
+ * @returns SQLite JSON path expression
1437
+ */
1438
+ compileJsonPath(node) {
1439
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1440
+ return `json_extract(${col}, '${node.path}')`;
1441
+ }
1442
+ compileReturning(returning, ctx) {
1443
+ if (!returning || returning.length === 0) return "";
1444
+ const columns = this.formatReturningColumns(returning);
1445
+ return ` RETURNING ${columns}`;
1446
+ }
1447
+ supportsReturning() {
1448
+ return true;
1449
+ }
1450
+ };
1451
+
1452
+ // src/core/dialect/mssql/functions.ts
1453
+ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1454
+ constructor() {
1455
+ super();
1456
+ this.registerOverrides();
1457
+ }
1458
+ registerOverrides() {
1459
+ this.add("NOW", () => `GETDATE()`);
1460
+ this.add("CURRENT_DATE", () => `CAST(GETDATE() AS DATE)`);
1461
+ this.add("CURRENT_TIME", () => `CAST(GETDATE() AS TIME)`);
1462
+ this.add("UTC_NOW", () => `GETUTCDATE()`);
1463
+ this.add("EXTRACT", ({ compiledArgs }) => {
1464
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1465
+ const [part, date] = compiledArgs;
1466
+ const partClean = part.replace(/['"]/g, "").toLowerCase();
1467
+ return `DATEPART(${partClean}, ${date})`;
1468
+ });
1469
+ this.add("YEAR", ({ compiledArgs }) => {
1470
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1471
+ return `YEAR(${compiledArgs[0]})`;
1472
+ });
1473
+ this.add("MONTH", ({ compiledArgs }) => {
1474
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1475
+ return `MONTH(${compiledArgs[0]})`;
1476
+ });
1477
+ this.add("DAY", ({ compiledArgs }) => {
1478
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1479
+ return `DAY(${compiledArgs[0]})`;
1480
+ });
1481
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1482
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1483
+ const [date, interval] = compiledArgs;
1484
+ const unitArg = node.args[2];
1485
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1486
+ return `DATEADD(${unitClean}, ${interval}, ${date})`;
1487
+ });
1488
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1489
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1490
+ const [date, interval] = compiledArgs;
1491
+ const unitArg = node.args[2];
1492
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1493
+ return `DATEADD(${unitClean}, -${interval}, ${date})`;
1494
+ });
1495
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1496
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1497
+ const [date1, date2] = compiledArgs;
1498
+ return `DATEDIFF(day, ${date2}, ${date1})`;
1499
+ });
1500
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1501
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1502
+ const [date, format] = compiledArgs;
1503
+ return `FORMAT(${date}, ${format})`;
1504
+ });
1505
+ this.add("UNIX_TIMESTAMP", () => `DATEDIFF(SECOND, '1970-01-01', GETUTCDATE())`);
1506
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1507
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1508
+ return `DATEADD(SECOND, ${compiledArgs[0]}, '1970-01-01')`;
1509
+ });
1510
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1511
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1512
+ return `EOMONTH(${compiledArgs[0]})`;
1513
+ });
1514
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1515
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1516
+ return `DATEPART(dw, ${compiledArgs[0]})`;
1517
+ });
1518
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1519
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1520
+ return `DATEPART(wk, ${compiledArgs[0]})`;
1521
+ });
1522
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1523
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1524
+ const [, date] = compiledArgs;
1525
+ const partArg = node.args[0];
1526
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1527
+ return `DATETRUNC(${partClean}, ${date})`;
1528
+ });
1529
+ }
1530
+ };
1531
+
1532
+ // src/core/dialect/mssql/index.ts
1533
+ var SqlServerDialect = class extends Dialect {
1534
+ /**
1535
+ * Creates a new SqlServerDialect instance
1536
+ */
1537
+ constructor() {
1538
+ super(new MssqlFunctionStrategy());
1539
+ this.dialect = "mssql";
1540
+ }
1541
+ /**
1542
+ * Quotes an identifier using SQL Server bracket syntax
1543
+ * @param id - Identifier to quote
1544
+ * @returns Quoted identifier
1545
+ */
1546
+ quoteIdentifier(id) {
1547
+ return `[${id}]`;
1548
+ }
1549
+ /**
1550
+ * Compiles JSON path expression using SQL Server syntax
1551
+ * @param node - JSON path node
1552
+ * @returns SQL Server JSON path expression
1553
+ */
1554
+ compileJsonPath(node) {
1555
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1556
+ return `JSON_VALUE(${col}, '${node.path}')`;
1557
+ }
1558
+ /**
1559
+ * Formats parameter placeholders using SQL Server named parameter syntax
1560
+ * @param index - Parameter index
1561
+ * @returns Named parameter placeholder
1562
+ */
1563
+ formatPlaceholder(index) {
1564
+ return `@p${index}`;
1565
+ }
1566
+ /**
1567
+ * Compiles SELECT query AST to SQL Server SQL
1568
+ * @param ast - Query AST
1569
+ * @param ctx - Compiler context
1570
+ * @returns SQL Server SQL string
1571
+ */
1572
+ compileSelectAst(ast, ctx) {
1573
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
1574
+ const ctes = this.compileCtes(ast, ctx);
1575
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
1576
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
1577
+ if (!hasSetOps) {
1578
+ return `${ctes}${baseSelect}`;
1579
+ }
1580
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1581
+ const orderBy = this.compileOrderBy(ast);
1582
+ const pagination = this.compilePagination(ast, orderBy);
1583
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1584
+ const tail = pagination || orderBy;
1585
+ return `${ctes}${combined}${tail}`;
1586
+ }
1587
+ compileInsertAst(ast, ctx) {
1588
+ const table = this.quoteIdentifier(ast.into.name);
1589
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1590
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1591
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
1592
+ }
1593
+ compileUpdateAst(ast, ctx) {
1594
+ const table = this.quoteIdentifier(ast.table.name);
1595
+ const assignments = ast.set.map((assignment) => {
1596
+ const col = assignment.column;
1597
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1598
+ const value = this.compileOperand(assignment.value, ctx);
1599
+ return `${target} = ${value}`;
1600
+ }).join(", ");
1601
+ const whereClause = this.compileWhere(ast.where, ctx);
1602
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
1603
+ }
1604
+ compileDeleteAst(ast, ctx) {
1605
+ const table = this.quoteIdentifier(ast.from.name);
1606
+ const whereClause = this.compileWhere(ast.where, ctx);
1607
+ return `DELETE FROM ${table}${whereClause};`;
1608
+ }
1609
+ compileSelectCore(ast, ctx) {
1610
+ const columns = ast.columns.map((c) => {
1611
+ let expr = "";
1612
+ if (c.type === "Function") {
1613
+ expr = this.compileOperand(c, ctx);
1614
+ } else if (c.type === "Column") {
1615
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
1616
+ } else if (c.type === "ScalarSubquery") {
1617
+ expr = this.compileOperand(c, ctx);
1618
+ } else if (c.type === "WindowFunction") {
1619
+ expr = this.compileOperand(c, ctx);
1620
+ }
1621
+ if (c.alias) {
1622
+ if (c.alias.includes("(")) return c.alias;
1623
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1624
+ }
1625
+ return expr;
1626
+ }).join(", ");
1627
+ const distinct = ast.distinct ? "DISTINCT " : "";
1628
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
1629
+ const joins = ast.joins.map((j) => {
1630
+ const table = this.quoteIdentifier(j.table.name);
1631
+ const cond = this.compileExpression(j.condition, ctx);
1632
+ return `${j.kind} JOIN ${table} ON ${cond}`;
1633
+ }).join(" ");
1634
+ const whereClause = this.compileWhere(ast.where, ctx);
1635
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
1636
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
1637
+ const orderBy = this.compileOrderBy(ast);
1638
+ const pagination = this.compilePagination(ast, orderBy);
1639
+ if (pagination) {
1640
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
1641
+ }
1642
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
1643
+ }
1644
+ compileOrderBy(ast) {
1645
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
1646
+ return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1647
+ }
1648
+ compilePagination(ast, orderBy) {
1649
+ const hasLimit = ast.limit !== void 0;
1650
+ const hasOffset = ast.offset !== void 0;
1651
+ if (!hasLimit && !hasOffset) return "";
1652
+ const off = ast.offset ?? 0;
1653
+ const orderClause = orderBy || " ORDER BY (SELECT NULL)";
1654
+ let pagination = `${orderClause} OFFSET ${off} ROWS`;
1655
+ if (hasLimit) {
1656
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
1657
+ }
1658
+ return pagination;
1659
+ }
1660
+ compileCtes(ast, ctx) {
1661
+ if (!ast.ctes || ast.ctes.length === 0) return "";
1662
+ const defs = ast.ctes.map((cte) => {
1663
+ const name = this.quoteIdentifier(cte.name);
1664
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1665
+ const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
1666
+ return `${name}${cols} AS (${query})`;
1667
+ }).join(", ");
1668
+ return `WITH ${defs} `;
1669
+ }
1670
+ wrapSetOperand(sql) {
1671
+ const trimmed = sql.trim().replace(/;$/, "");
1672
+ return `(${trimmed})`;
1673
+ }
1674
+ };
1675
+
1676
+ // src/core/dialect/dialect-factory.ts
1677
+ var DialectFactory = class {
1678
+ static {
1679
+ this.registry = /* @__PURE__ */ new Map();
1680
+ }
1681
+ static {
1682
+ this.defaultsInitialized = false;
1683
+ }
1684
+ static ensureDefaults() {
1685
+ if (this.defaultsInitialized) return;
1686
+ this.defaultsInitialized = true;
1687
+ if (!this.registry.has("postgres")) {
1688
+ this.registry.set("postgres", () => new PostgresDialect());
1689
+ }
1690
+ if (!this.registry.has("mysql")) {
1691
+ this.registry.set("mysql", () => new MySqlDialect());
1692
+ }
1693
+ if (!this.registry.has("sqlite")) {
1694
+ this.registry.set("sqlite", () => new SqliteDialect());
1695
+ }
1696
+ if (!this.registry.has("mssql")) {
1697
+ this.registry.set("mssql", () => new SqlServerDialect());
1698
+ }
1699
+ }
1700
+ /**
1701
+ * Register (or override) a dialect factory for a key.
1702
+ *
1703
+ * Examples:
1704
+ * DialectFactory.register('sqlite', () => new SqliteDialect());
1705
+ * DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
1706
+ */
1707
+ static register(key, factory) {
1708
+ this.registry.set(key, factory);
1709
+ }
1710
+ /**
1711
+ * Resolve a key into a Dialect instance.
1712
+ * Throws if the key is not registered.
1713
+ */
1714
+ static create(key) {
1715
+ this.ensureDefaults();
1716
+ const factory = this.registry.get(key);
1717
+ if (!factory) {
1718
+ throw new Error(
1719
+ `Dialect "${String(
1720
+ key
1721
+ )}" is not registered. Use DialectFactory.register(...) to register it.`
1722
+ );
1723
+ }
1724
+ return factory();
1725
+ }
1726
+ /**
1727
+ * Clear all registrations (mainly for tests).
1728
+ * Built-ins will be re-registered lazily on the next create().
1729
+ */
1730
+ static clear() {
1731
+ this.registry.clear();
1732
+ this.defaultsInitialized = false;
1733
+ }
1734
+ };
1735
+ var resolveDialectInput = (dialect) => {
1736
+ if (typeof dialect === "string") {
1737
+ return DialectFactory.create(dialect);
1738
+ }
1739
+ return dialect;
1740
+ };
1741
+
369
1742
  // src/query-builder/select-query-state.ts
370
1743
  var SelectQueryState = class _SelectQueryState {
371
1744
  /**
@@ -517,9 +1890,9 @@ var SelectQueryState = class _SelectQueryState {
517
1890
  var createJoinNode = (kind, tableName, condition, relationName) => ({
518
1891
  type: "Join",
519
1892
  kind,
520
- table: { type: "Table", name: tableName },
1893
+ table: typeof tableName === "string" ? { type: "Table", name: tableName } : tableName,
521
1894
  condition,
522
- relationName
1895
+ meta: relationName ? { relationName } : void 0
523
1896
  });
524
1897
 
525
1898
  // src/core/sql/sql.ts
@@ -861,7 +2234,8 @@ var HydrationPlanner = class _HydrationPlanner {
861
2234
  */
862
2235
  buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
863
2236
  switch (rel.type) {
864
- case RelationKinds.HasMany: {
2237
+ case RelationKinds.HasMany:
2238
+ case RelationKinds.HasOne: {
865
2239
  const localKey = rel.localKey || findPrimaryKey(this.table);
866
2240
  return {
867
2241
  name: relationName,
@@ -1190,10 +2564,11 @@ var assertNever = (value) => {
1190
2564
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
1191
2565
  };
1192
2566
  var baseRelationCondition = (root, relation) => {
1193
- const defaultLocalKey = relation.type === RelationKinds.HasMany ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2567
+ const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
1194
2568
  const localKey = relation.localKey || defaultLocalKey;
1195
2569
  switch (relation.type) {
1196
2570
  case RelationKinds.HasMany:
2571
+ case RelationKinds.HasOne:
1197
2572
  return eq(
1198
2573
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
1199
2574
  { type: "Column", table: root.name, name: localKey }
@@ -1240,6 +2615,9 @@ var buildRelationCorrelation = (root, relation) => {
1240
2615
  return baseRelationCondition(root, relation);
1241
2616
  };
1242
2617
 
2618
+ // src/core/ast/join-metadata.ts
2619
+ var getJoinRelationName = (join) => join.meta?.relationName;
2620
+
1243
2621
  // src/query-builder/relation-service.ts
1244
2622
  var RelationService = class {
1245
2623
  /**
@@ -1294,7 +2672,7 @@ var RelationService = class {
1294
2672
  let hydration = this.hydration;
1295
2673
  const relation = this.getRelation(relationName);
1296
2674
  const aliasPrefix = options?.aliasPrefix ?? relationName;
1297
- const alreadyJoined = state.ast.joins.some((j) => j.relationName === relationName);
2675
+ const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
1298
2676
  if (!alreadyJoined) {
1299
2677
  const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
1300
2678
  state = joined.state;
@@ -1615,6 +2993,12 @@ var hydrateRows = (rows, plan) => {
1615
2993
  const seen = getRelationSeenSet(rootId, rel.name);
1616
2994
  if (seen.has(childPk)) continue;
1617
2995
  seen.add(childPk);
2996
+ if (rel.type === RelationKinds.HasOne) {
2997
+ if (!parent[rel.name]) {
2998
+ parent[rel.name] = buildChild(row, rel);
2999
+ }
3000
+ continue;
3001
+ }
1618
3002
  const bucket = parent[rel.name];
1619
3003
  bucket.push(buildChild(row, rel));
1620
3004
  }
@@ -1628,7 +3012,7 @@ var createBaseRow = (row, plan) => {
1628
3012
  base[key] = row[key];
1629
3013
  }
1630
3014
  for (const rel of plan.relations) {
1631
- base[rel.name] = [];
3015
+ base[rel.name] = rel.type === RelationKinds.HasOne ? null : [];
1632
3016
  }
1633
3017
  return base;
1634
3018
  };
@@ -1791,7 +3175,7 @@ var DefaultHasManyCollection = class {
1791
3175
  }
1792
3176
  };
1793
3177
 
1794
- // src/orm/relations/belongs-to.ts
3178
+ // src/orm/relations/has-one.ts
1795
3179
  var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
1796
3180
  var hideInternal2 = (obj, keys) => {
1797
3181
  for (const key of keys) {
@@ -1803,6 +3187,123 @@ var hideInternal2 = (obj, keys) => {
1803
3187
  });
1804
3188
  }
1805
3189
  };
3190
+ var DefaultHasOneReference = class {
3191
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3192
+ this.ctx = ctx;
3193
+ this.meta = meta;
3194
+ this.root = root;
3195
+ this.relationName = relationName;
3196
+ this.relation = relation;
3197
+ this.rootTable = rootTable;
3198
+ this.loader = loader;
3199
+ this.createEntity = createEntity;
3200
+ this.localKey = localKey;
3201
+ this.loaded = false;
3202
+ this.current = null;
3203
+ hideInternal2(this, [
3204
+ "ctx",
3205
+ "meta",
3206
+ "root",
3207
+ "relationName",
3208
+ "relation",
3209
+ "rootTable",
3210
+ "loader",
3211
+ "createEntity",
3212
+ "localKey"
3213
+ ]);
3214
+ this.populateFromHydrationCache();
3215
+ }
3216
+ async load() {
3217
+ if (this.loaded) return this.current;
3218
+ const map = await this.loader();
3219
+ const keyValue = this.root[this.localKey];
3220
+ if (keyValue === void 0 || keyValue === null) {
3221
+ this.loaded = true;
3222
+ return this.current;
3223
+ }
3224
+ const row = map.get(toKey3(keyValue));
3225
+ this.current = row ? this.createEntity(row) : null;
3226
+ this.loaded = true;
3227
+ return this.current;
3228
+ }
3229
+ get() {
3230
+ return this.current;
3231
+ }
3232
+ set(data) {
3233
+ if (data === null) {
3234
+ return this.detachCurrent();
3235
+ }
3236
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
3237
+ if (this.current && this.current !== entity) {
3238
+ this.ctx.registerRelationChange(
3239
+ this.root,
3240
+ this.relationKey,
3241
+ this.rootTable,
3242
+ this.relationName,
3243
+ this.relation,
3244
+ { kind: "remove", entity: this.current }
3245
+ );
3246
+ }
3247
+ this.assignForeignKey(entity);
3248
+ this.current = entity;
3249
+ this.loaded = true;
3250
+ this.ctx.registerRelationChange(
3251
+ this.root,
3252
+ this.relationKey,
3253
+ this.rootTable,
3254
+ this.relationName,
3255
+ this.relation,
3256
+ { kind: "attach", entity }
3257
+ );
3258
+ return entity;
3259
+ }
3260
+ toJSON() {
3261
+ return this.current;
3262
+ }
3263
+ detachCurrent() {
3264
+ const previous = this.current;
3265
+ if (!previous) return null;
3266
+ this.current = null;
3267
+ this.loaded = true;
3268
+ this.ctx.registerRelationChange(
3269
+ this.root,
3270
+ this.relationKey,
3271
+ this.rootTable,
3272
+ this.relationName,
3273
+ this.relation,
3274
+ { kind: "remove", entity: previous }
3275
+ );
3276
+ return null;
3277
+ }
3278
+ assignForeignKey(entity) {
3279
+ const keyValue = this.root[this.localKey];
3280
+ entity[this.relation.foreignKey] = keyValue;
3281
+ }
3282
+ get relationKey() {
3283
+ return `${this.rootTable.name}.${this.relationName}`;
3284
+ }
3285
+ populateFromHydrationCache() {
3286
+ const keyValue = this.root[this.localKey];
3287
+ if (keyValue === void 0 || keyValue === null) return;
3288
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
3289
+ if (!row) return;
3290
+ this.current = this.createEntity(row);
3291
+ this.loaded = true;
3292
+ }
3293
+ };
3294
+
3295
+ // src/orm/relations/belongs-to.ts
3296
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
3297
+ var hideInternal3 = (obj, keys) => {
3298
+ for (const key of keys) {
3299
+ Object.defineProperty(obj, key, {
3300
+ value: obj[key],
3301
+ writable: false,
3302
+ configurable: false,
3303
+ enumerable: false
3304
+ });
3305
+ }
3306
+ };
1806
3307
  var DefaultBelongsToReference = class {
1807
3308
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
1808
3309
  this.ctx = ctx;
@@ -1816,7 +3317,7 @@ var DefaultBelongsToReference = class {
1816
3317
  this.targetKey = targetKey;
1817
3318
  this.loaded = false;
1818
3319
  this.current = null;
1819
- hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
3320
+ hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
1820
3321
  this.populateFromHydrationCache();
1821
3322
  }
1822
3323
  async load() {
@@ -1826,7 +3327,7 @@ var DefaultBelongsToReference = class {
1826
3327
  if (fkValue === null || fkValue === void 0) {
1827
3328
  this.current = null;
1828
3329
  } else {
1829
- const row = map.get(toKey3(fkValue));
3330
+ const row = map.get(toKey4(fkValue));
1830
3331
  this.current = row ? this.createEntity(row) : null;
1831
3332
  }
1832
3333
  this.loaded = true;
@@ -1883,8 +3384,8 @@ var DefaultBelongsToReference = class {
1883
3384
  };
1884
3385
 
1885
3386
  // src/orm/relations/many-to-many.ts
1886
- var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
1887
- var hideInternal3 = (obj, keys) => {
3387
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3388
+ var hideInternal4 = (obj, keys) => {
1888
3389
  for (const key of keys) {
1889
3390
  Object.defineProperty(obj, key, {
1890
3391
  value: obj[key],
@@ -1907,13 +3408,13 @@ var DefaultManyToManyCollection = class {
1907
3408
  this.localKey = localKey;
1908
3409
  this.loaded = false;
1909
3410
  this.items = [];
1910
- hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
3411
+ hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
1911
3412
  this.hydrateFromCache();
1912
3413
  }
1913
3414
  async load() {
1914
3415
  if (this.loaded) return this.items;
1915
3416
  const map = await this.loader();
1916
- const key = toKey4(this.root[this.localKey]);
3417
+ const key = toKey5(this.root[this.localKey]);
1917
3418
  const rows = map.get(key) ?? [];
1918
3419
  this.items = rows.map((row) => {
1919
3420
  const entity = this.createEntity(row);
@@ -1963,15 +3464,15 @@ var DefaultManyToManyCollection = class {
1963
3464
  async syncByIds(ids) {
1964
3465
  await this.load();
1965
3466
  const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
1966
- const normalized = new Set(ids.map((id) => toKey4(id)));
1967
- const currentIds = new Set(this.items.map((item) => toKey4(this.extractId(item))));
3467
+ const normalized = new Set(ids.map((id) => toKey5(id)));
3468
+ const currentIds = new Set(this.items.map((item) => toKey5(this.extractId(item))));
1968
3469
  for (const id of normalized) {
1969
3470
  if (!currentIds.has(id)) {
1970
3471
  this.attach(id);
1971
3472
  }
1972
3473
  }
1973
3474
  for (const item of [...this.items]) {
1974
- const itemId = toKey4(this.extractId(item));
3475
+ const itemId = toKey5(this.extractId(item));
1975
3476
  if (!normalized.has(itemId)) {
1976
3477
  this.detach(item);
1977
3478
  }
@@ -2042,7 +3543,7 @@ var executeQuery = async (ctx, qb) => {
2042
3543
  const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
2043
3544
  return rowsFromResults(results);
2044
3545
  };
2045
- var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3546
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
2046
3547
  var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2047
3548
  const localKey = relation.localKey || findPrimaryKey(rootTable);
2048
3549
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -2066,13 +3567,43 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2066
3567
  for (const row of rows) {
2067
3568
  const fkValue = row[relation.foreignKey];
2068
3569
  if (fkValue === null || fkValue === void 0) continue;
2069
- const key = toKey5(fkValue);
3570
+ const key = toKey6(fkValue);
2070
3571
  const bucket = grouped.get(key) ?? [];
2071
3572
  bucket.push(row);
2072
3573
  grouped.set(key, bucket);
2073
3574
  }
2074
3575
  return grouped;
2075
3576
  };
3577
+ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
3578
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
3579
+ const roots = ctx.getEntitiesForTable(rootTable);
3580
+ const keys = /* @__PURE__ */ new Set();
3581
+ for (const tracked of roots) {
3582
+ const value = tracked.entity[localKey];
3583
+ if (value !== null && value !== void 0) {
3584
+ keys.add(value);
3585
+ }
3586
+ }
3587
+ if (!keys.size) {
3588
+ return /* @__PURE__ */ new Map();
3589
+ }
3590
+ const selectMap = selectAllColumns(relation.target);
3591
+ const qb = new SelectQueryBuilder(relation.target).select(selectMap);
3592
+ const fkColumn = relation.target.columns[relation.foreignKey];
3593
+ if (!fkColumn) return /* @__PURE__ */ new Map();
3594
+ qb.where(inList(fkColumn, Array.from(keys)));
3595
+ const rows = await executeQuery(ctx, qb);
3596
+ const lookup = /* @__PURE__ */ new Map();
3597
+ for (const row of rows) {
3598
+ const fkValue = row[relation.foreignKey];
3599
+ if (fkValue === null || fkValue === void 0) continue;
3600
+ const key = toKey6(fkValue);
3601
+ if (!lookup.has(key)) {
3602
+ lookup.set(key, row);
3603
+ }
3604
+ }
3605
+ return lookup;
3606
+ };
2076
3607
  var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2077
3608
  const roots = ctx.getEntitiesForTable(rootTable);
2078
3609
  const foreignKeys = /* @__PURE__ */ new Set();
@@ -2096,7 +3627,7 @@ var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2096
3627
  for (const row of rows) {
2097
3628
  const keyValue = row[targetKey];
2098
3629
  if (keyValue === null || keyValue === void 0) continue;
2099
- map.set(toKey5(keyValue), row);
3630
+ map.set(toKey6(keyValue), row);
2100
3631
  }
2101
3632
  return map;
2102
3633
  };
@@ -2127,12 +3658,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2127
3658
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
2128
3659
  continue;
2129
3660
  }
2130
- const bucket = rootLookup.get(toKey5(rootValue)) ?? [];
3661
+ const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
2131
3662
  bucket.push({
2132
3663
  targetId: targetValue,
2133
3664
  pivot: { ...pivot }
2134
3665
  });
2135
- rootLookup.set(toKey5(rootValue), bucket);
3666
+ rootLookup.set(toKey6(rootValue), bucket);
2136
3667
  targetIds.add(targetValue);
2137
3668
  }
2138
3669
  if (!targetIds.size) {
@@ -2149,13 +3680,13 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2149
3680
  for (const row of targetRows) {
2150
3681
  const pkValue = row[targetKey];
2151
3682
  if (pkValue === null || pkValue === void 0) continue;
2152
- targetMap.set(toKey5(pkValue), row);
3683
+ targetMap.set(toKey6(pkValue), row);
2153
3684
  }
2154
3685
  const result = /* @__PURE__ */ new Map();
2155
3686
  for (const [rootId, entries] of rootLookup.entries()) {
2156
3687
  const bucket = [];
2157
3688
  for (const entry of entries) {
2158
- const targetRow = targetMap.get(toKey5(entry.targetId));
3689
+ const targetRow = targetMap.get(toKey6(entry.targetId));
2159
3690
  if (!targetRow) continue;
2160
3691
  bucket.push({
2161
3692
  ...targetRow,
@@ -2250,18 +3781,29 @@ var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
2250
3781
  }
2251
3782
  return entity;
2252
3783
  };
2253
- var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
3784
+ var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
2254
3785
  var populateHydrationCache = (entity, row, meta) => {
2255
3786
  for (const relationName of Object.keys(meta.table.relations)) {
2256
3787
  const relation = meta.table.relations[relationName];
2257
3788
  const data = row[relationName];
3789
+ if (relation.type === RelationKinds.HasOne) {
3790
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
3791
+ const rootValue = entity[localKey];
3792
+ if (rootValue === void 0 || rootValue === null) continue;
3793
+ if (!data || typeof data !== "object") continue;
3794
+ const cache = /* @__PURE__ */ new Map();
3795
+ cache.set(toKey7(rootValue), data);
3796
+ meta.relationHydration.set(relationName, cache);
3797
+ meta.relationCache.set(relationName, Promise.resolve(cache));
3798
+ continue;
3799
+ }
2258
3800
  if (!Array.isArray(data)) continue;
2259
3801
  if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
2260
3802
  const localKey = relation.localKey || findPrimaryKey(meta.table);
2261
3803
  const rootValue = entity[localKey];
2262
3804
  if (rootValue === void 0 || rootValue === null) continue;
2263
3805
  const cache = /* @__PURE__ */ new Map();
2264
- cache.set(toKey6(rootValue), data);
3806
+ cache.set(toKey7(rootValue), data);
2265
3807
  meta.relationHydration.set(relationName, cache);
2266
3808
  meta.relationCache.set(relationName, Promise.resolve(cache));
2267
3809
  continue;
@@ -2272,7 +3814,7 @@ var populateHydrationCache = (entity, row, meta) => {
2272
3814
  for (const item of data) {
2273
3815
  const pkValue = item[targetKey];
2274
3816
  if (pkValue === void 0 || pkValue === null) continue;
2275
- cache.set(toKey6(pkValue), item);
3817
+ cache.set(toKey7(pkValue), item);
2276
3818
  }
2277
3819
  if (cache.size) {
2278
3820
  meta.relationHydration.set(relationName, cache);
@@ -2295,6 +3837,26 @@ var getRelationWrapper = (meta, relationName, owner) => {
2295
3837
  };
2296
3838
  var instantiateWrapper = (meta, relationName, relation, owner) => {
2297
3839
  switch (relation.type) {
3840
+ case RelationKinds.HasOne: {
3841
+ const hasOne2 = relation;
3842
+ const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
3843
+ const loader = () => relationLoaderCache(
3844
+ meta,
3845
+ relationName,
3846
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
3847
+ );
3848
+ return new DefaultHasOneReference(
3849
+ meta.ctx,
3850
+ meta,
3851
+ owner,
3852
+ relationName,
3853
+ hasOne2,
3854
+ meta.table,
3855
+ loader,
3856
+ (row) => createEntityFromRow(meta.ctx, hasOne2.target, row),
3857
+ localKey
3858
+ );
3859
+ }
2298
3860
  case RelationKinds.HasMany: {
2299
3861
  const hasMany2 = relation;
2300
3862
  const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
@@ -2717,7 +4279,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2717
4279
  * @returns Compiled query with SQL and parameters
2718
4280
  */
2719
4281
  compile(dialect) {
2720
- return dialect.compileSelect(this.context.state.ast);
4282
+ const resolved = resolveDialectInput(dialect);
4283
+ return resolved.compileSelect(this.context.state.ast);
2721
4284
  }
2722
4285
  /**
2723
4286
  * Converts the query to SQL string for a specific dialect
@@ -2768,6 +4331,15 @@ var buildRelationDefinitions = (meta, tableMap) => {
2768
4331
  const relations = {};
2769
4332
  for (const [name, relation] of Object.entries(meta.relations)) {
2770
4333
  switch (relation.kind) {
4334
+ case RelationKinds.HasOne: {
4335
+ relations[name] = hasOne(
4336
+ resolveTableTarget(relation.target, tableMap),
4337
+ relation.foreignKey,
4338
+ relation.localKey,
4339
+ relation.cascade
4340
+ );
4341
+ break;
4342
+ }
2771
4343
  case RelationKinds.HasMany: {
2772
4344
  relations[name] = hasMany(
2773
4345
  resolveTableTarget(relation.target, tableMap),
@@ -2839,6 +4411,7 @@ var selectFromEntity = (ctor) => {
2839
4411
  Column,
2840
4412
  Entity,
2841
4413
  HasMany,
4414
+ HasOne,
2842
4415
  PrimaryKey,
2843
4416
  bootstrapEntities,
2844
4417
  getTableDefFromEntity,