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
@@ -153,6 +153,8 @@ function PrimaryKey(definition) {
153
153
 
154
154
  // src/schema/relation.ts
155
155
  var RelationKinds = {
156
+ /** One-to-one relationship */
157
+ HasOne: "HAS_ONE",
156
158
  /** One-to-many relationship */
157
159
  HasMany: "HAS_MANY",
158
160
  /** Many-to-one relationship */
@@ -167,6 +169,13 @@ var hasMany = (target, foreignKey, localKey, cascade) => ({
167
169
  localKey,
168
170
  cascade
169
171
  });
172
+ var hasOne = (target, foreignKey, localKey, cascade) => ({
173
+ type: RelationKinds.HasOne,
174
+ target,
175
+ foreignKey,
176
+ localKey,
177
+ cascade
178
+ });
170
179
  var belongsTo = (target, foreignKey, localKey, cascade) => ({
171
180
  type: RelationKinds.BelongsTo,
172
181
  target,
@@ -227,6 +236,16 @@ function HasMany(options) {
227
236
  cascade: options.cascade
228
237
  }));
229
238
  }
239
+ function HasOne(options) {
240
+ return createFieldDecorator((propertyName) => ({
241
+ kind: RelationKinds.HasOne,
242
+ propertyKey: propertyName,
243
+ target: options.target,
244
+ foreignKey: options.foreignKey,
245
+ localKey: options.localKey,
246
+ cascade: options.cascade
247
+ }));
248
+ }
230
249
  function BelongsTo(options) {
231
250
  return createFieldDecorator((propertyName) => ({
232
251
  kind: RelationKinds.BelongsTo,
@@ -333,6 +352,1359 @@ var count = buildAggregate("COUNT");
333
352
  var sum = buildAggregate("SUM");
334
353
  var avg = buildAggregate("AVG");
335
354
 
355
+ // src/core/functions/standard-strategy.ts
356
+ var StandardFunctionStrategy = class {
357
+ constructor() {
358
+ this.renderers = /* @__PURE__ */ new Map();
359
+ this.registerStandard();
360
+ }
361
+ registerStandard() {
362
+ this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
363
+ this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
364
+ this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
365
+ this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
366
+ this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
367
+ this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
368
+ this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
369
+ this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
370
+ this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
371
+ this.add("NOW", () => `NOW()`);
372
+ this.add("CURRENT_DATE", () => `CURRENT_DATE`);
373
+ this.add("CURRENT_TIME", () => `CURRENT_TIME`);
374
+ this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
375
+ this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
376
+ this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
377
+ this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
378
+ this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
379
+ this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
380
+ this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
381
+ this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
382
+ this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
383
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
384
+ this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
385
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
386
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
387
+ this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
388
+ }
389
+ add(name, renderer) {
390
+ this.renderers.set(name, renderer);
391
+ }
392
+ getRenderer(name) {
393
+ return this.renderers.get(name);
394
+ }
395
+ };
396
+
397
+ // src/core/dialect/abstract.ts
398
+ var Dialect = class {
399
+ /**
400
+ * Compiles a SELECT query AST to SQL
401
+ * @param ast - Query AST to compile
402
+ * @returns Compiled query with SQL and parameters
403
+ */
404
+ compileSelect(ast) {
405
+ const ctx = this.createCompilerContext();
406
+ const normalized = this.normalizeSelectAst(ast);
407
+ const rawSql = this.compileSelectAst(normalized, ctx).trim();
408
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
409
+ return {
410
+ sql,
411
+ params: [...ctx.params]
412
+ };
413
+ }
414
+ compileInsert(ast) {
415
+ const ctx = this.createCompilerContext();
416
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
417
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
418
+ return {
419
+ sql,
420
+ params: [...ctx.params]
421
+ };
422
+ }
423
+ compileUpdate(ast) {
424
+ const ctx = this.createCompilerContext();
425
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
426
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
427
+ return {
428
+ sql,
429
+ params: [...ctx.params]
430
+ };
431
+ }
432
+ compileDelete(ast) {
433
+ const ctx = this.createCompilerContext();
434
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
435
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
436
+ return {
437
+ sql,
438
+ params: [...ctx.params]
439
+ };
440
+ }
441
+ supportsReturning() {
442
+ return false;
443
+ }
444
+ /**
445
+ * Compiles a WHERE clause
446
+ * @param where - WHERE expression
447
+ * @param ctx - Compiler context
448
+ * @returns SQL WHERE clause or empty string
449
+ */
450
+ compileWhere(where, ctx) {
451
+ if (!where) return "";
452
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
453
+ }
454
+ compileReturning(returning, ctx) {
455
+ if (!returning || returning.length === 0) return "";
456
+ throw new Error("RETURNING is not supported by this dialect.");
457
+ }
458
+ /**
459
+ * Generates subquery for EXISTS expressions
460
+ * Rule: Always forces SELECT 1, ignoring column list
461
+ * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
462
+ * Does not add ';' at the end
463
+ * @param ast - Query AST
464
+ * @param ctx - Compiler context
465
+ * @returns SQL for EXISTS subquery
466
+ */
467
+ compileSelectForExists(ast, ctx) {
468
+ const normalized = this.normalizeSelectAst(ast);
469
+ const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
470
+ if (normalized.setOps && normalized.setOps.length > 0) {
471
+ return `SELECT 1 FROM (${full}) AS _exists`;
472
+ }
473
+ const upper = full.toUpperCase();
474
+ const fromIndex = upper.indexOf(" FROM ");
475
+ if (fromIndex === -1) {
476
+ return full;
477
+ }
478
+ const tail = full.slice(fromIndex);
479
+ return `SELECT 1${tail}`;
480
+ }
481
+ /**
482
+ * Creates a new compiler context
483
+ * @returns Compiler context with parameter management
484
+ */
485
+ createCompilerContext() {
486
+ const params = [];
487
+ let counter = 0;
488
+ return {
489
+ params,
490
+ addParameter: (value) => {
491
+ counter += 1;
492
+ params.push(value);
493
+ return this.formatPlaceholder(counter);
494
+ }
495
+ };
496
+ }
497
+ /**
498
+ * Formats a parameter placeholder
499
+ * @param index - Parameter index
500
+ * @returns Formatted placeholder string
501
+ */
502
+ formatPlaceholder(index) {
503
+ return "?";
504
+ }
505
+ /**
506
+ * Whether the current dialect supports a given set operation.
507
+ * Override in concrete dialects to restrict support.
508
+ */
509
+ supportsSetOperation(kind) {
510
+ return true;
511
+ }
512
+ /**
513
+ * Validates set-operation semantics:
514
+ * - Ensures the dialect supports requested operators.
515
+ * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
516
+ * @param ast - Query to validate
517
+ * @param isOutermost - Whether this node is the outermost compound query
518
+ */
519
+ validateSetOperations(ast, isOutermost = true) {
520
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
521
+ if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
522
+ throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
523
+ }
524
+ if (hasSetOps) {
525
+ for (const op of ast.setOps) {
526
+ if (!this.supportsSetOperation(op.operator)) {
527
+ throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
528
+ }
529
+ this.validateSetOperations(op.query, false);
530
+ }
531
+ }
532
+ }
533
+ /**
534
+ * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
535
+ * @param ast - Query AST
536
+ * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
537
+ */
538
+ hoistCtes(ast) {
539
+ let hoisted = [];
540
+ const normalizedSetOps = ast.setOps?.map((op) => {
541
+ const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
542
+ const childCtes = child.ctes ?? [];
543
+ if (childCtes.length) {
544
+ hoisted = hoisted.concat(childCtes);
545
+ }
546
+ hoisted = hoisted.concat(childHoisted);
547
+ const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
548
+ return { ...op, query: queryWithoutCtes };
549
+ });
550
+ const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
551
+ return { normalized, hoistedCtes: hoisted };
552
+ }
553
+ /**
554
+ * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
555
+ * @param ast - Query AST
556
+ * @returns Normalized query AST
557
+ */
558
+ normalizeSelectAst(ast) {
559
+ this.validateSetOperations(ast, true);
560
+ const { normalized, hoistedCtes } = this.hoistCtes(ast);
561
+ const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
562
+ return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
563
+ }
564
+ constructor(functionStrategy) {
565
+ this.expressionCompilers = /* @__PURE__ */ new Map();
566
+ this.operandCompilers = /* @__PURE__ */ new Map();
567
+ this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
568
+ this.registerDefaultOperandCompilers();
569
+ this.registerDefaultExpressionCompilers();
570
+ }
571
+ /**
572
+ * Registers an expression compiler for a specific node type
573
+ * @param type - Expression node type
574
+ * @param compiler - Compiler function
575
+ */
576
+ registerExpressionCompiler(type, compiler) {
577
+ this.expressionCompilers.set(type, compiler);
578
+ }
579
+ /**
580
+ * Registers an operand compiler for a specific node type
581
+ * @param type - Operand node type
582
+ * @param compiler - Compiler function
583
+ */
584
+ registerOperandCompiler(type, compiler) {
585
+ this.operandCompilers.set(type, compiler);
586
+ }
587
+ /**
588
+ * Compiles an expression node
589
+ * @param node - Expression node to compile
590
+ * @param ctx - Compiler context
591
+ * @returns Compiled SQL expression
592
+ */
593
+ compileExpression(node, ctx) {
594
+ const compiler = this.expressionCompilers.get(node.type);
595
+ if (!compiler) {
596
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
597
+ }
598
+ return compiler(node, ctx);
599
+ }
600
+ /**
601
+ * Compiles an operand node
602
+ * @param node - Operand node to compile
603
+ * @param ctx - Compiler context
604
+ * @returns Compiled SQL operand
605
+ */
606
+ compileOperand(node, ctx) {
607
+ const compiler = this.operandCompilers.get(node.type);
608
+ if (!compiler) {
609
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
610
+ }
611
+ return compiler(node, ctx);
612
+ }
613
+ registerDefaultExpressionCompilers() {
614
+ this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
615
+ const left = this.compileOperand(binary.left, ctx);
616
+ const right = this.compileOperand(binary.right, ctx);
617
+ const base = `${left} ${binary.operator} ${right}`;
618
+ if (binary.escape) {
619
+ const escapeOperand = this.compileOperand(binary.escape, ctx);
620
+ return `${base} ESCAPE ${escapeOperand}`;
621
+ }
622
+ return base;
623
+ });
624
+ this.registerExpressionCompiler("LogicalExpression", (logical, ctx) => {
625
+ if (logical.operands.length === 0) return "";
626
+ const parts = logical.operands.map((op) => {
627
+ const compiled = this.compileExpression(op, ctx);
628
+ return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
629
+ });
630
+ return parts.join(` ${logical.operator} `);
631
+ });
632
+ this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
633
+ const left = this.compileOperand(nullExpr.left, ctx);
634
+ return `${left} ${nullExpr.operator}`;
635
+ });
636
+ this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
637
+ const left = this.compileOperand(inExpr.left, ctx);
638
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
639
+ return `${left} ${inExpr.operator} (${values})`;
640
+ });
641
+ this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
642
+ const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
643
+ return `${existsExpr.operator} (${subquerySql})`;
644
+ });
645
+ this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
646
+ const left = this.compileOperand(betweenExpr.left, ctx);
647
+ const lower = this.compileOperand(betweenExpr.lower, ctx);
648
+ const upper = this.compileOperand(betweenExpr.upper, ctx);
649
+ return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
650
+ });
651
+ }
652
+ registerDefaultOperandCompilers() {
653
+ this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
654
+ this.registerOperandCompiler("Column", (column, _ctx) => {
655
+ return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
656
+ });
657
+ this.registerOperandCompiler(
658
+ "Function",
659
+ (fnNode, ctx) => this.compileFunctionOperand(fnNode, ctx)
660
+ );
661
+ this.registerOperandCompiler("JsonPath", (path, _ctx) => this.compileJsonPath(path));
662
+ this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
663
+ const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
664
+ return `(${sql})`;
665
+ });
666
+ this.registerOperandCompiler("CaseExpression", (node, ctx) => {
667
+ const parts = ["CASE"];
668
+ for (const { when, then } of node.conditions) {
669
+ parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
670
+ }
671
+ if (node.else) {
672
+ parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
673
+ }
674
+ parts.push("END");
675
+ return parts.join(" ");
676
+ });
677
+ this.registerOperandCompiler("WindowFunction", (node, ctx) => {
678
+ let result = `${node.name}(`;
679
+ if (node.args.length > 0) {
680
+ result += node.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
681
+ }
682
+ result += ") OVER (";
683
+ const parts = [];
684
+ if (node.partitionBy && node.partitionBy.length > 0) {
685
+ const partitionClause = "PARTITION BY " + node.partitionBy.map(
686
+ (col) => `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`
687
+ ).join(", ");
688
+ parts.push(partitionClause);
689
+ }
690
+ if (node.orderBy && node.orderBy.length > 0) {
691
+ const orderClause = "ORDER BY " + node.orderBy.map(
692
+ (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
693
+ ).join(", ");
694
+ parts.push(orderClause);
695
+ }
696
+ result += parts.join(" ");
697
+ result += ")";
698
+ return result;
699
+ });
700
+ }
701
+ // Default fallback, should be overridden by dialects if supported
702
+ compileJsonPath(node) {
703
+ throw new Error("JSON Path not supported by this dialect");
704
+ }
705
+ /**
706
+ * Compiles a function operand, using the dialect's function strategy.
707
+ */
708
+ compileFunctionOperand(fnNode, ctx) {
709
+ const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
710
+ const renderer = this.functionStrategy.getRenderer(fnNode.name);
711
+ if (renderer) {
712
+ return renderer({ node: fnNode, compiledArgs });
713
+ }
714
+ return `${fnNode.name}(${compiledArgs.join(", ")})`;
715
+ }
716
+ };
717
+
718
+ // src/core/dialect/base/function-table-formatter.ts
719
+ var FunctionTableFormatter = class {
720
+ /**
721
+ * Formats a function table node into SQL syntax.
722
+ * @param fn - The function table node containing schema, name, args, and aliases.
723
+ * @param ctx - Optional compiler context for operand compilation.
724
+ * @param dialect - The dialect instance for compiling operands.
725
+ * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
726
+ */
727
+ static format(fn, ctx, dialect) {
728
+ const schemaPart = this.formatSchema(fn, dialect);
729
+ const args = this.formatArgs(fn, ctx, dialect);
730
+ const base = this.formatBase(fn, schemaPart, args, dialect);
731
+ const lateral = this.formatLateral(fn);
732
+ const alias = this.formatAlias(fn, dialect);
733
+ const colAliases = this.formatColumnAliases(fn, dialect);
734
+ return `${lateral}${base}${alias}${colAliases}`;
735
+ }
736
+ /**
737
+ * Formats the schema prefix for the function name.
738
+ * @param fn - The function table node.
739
+ * @param dialect - The dialect instance for quoting identifiers.
740
+ * @returns Schema prefix (e.g., "schema.") or empty string.
741
+ * @internal
742
+ */
743
+ static formatSchema(fn, dialect) {
744
+ if (!fn.schema) return "";
745
+ const quoted = dialect ? dialect.quoteIdentifier(fn.schema) : fn.schema;
746
+ return `${quoted}.`;
747
+ }
748
+ /**
749
+ * Formats function arguments into SQL syntax.
750
+ * @param fn - The function table node containing arguments.
751
+ * @param ctx - Optional compiler context for operand compilation.
752
+ * @param dialect - The dialect instance for compiling operands.
753
+ * @returns Comma-separated function arguments.
754
+ * @internal
755
+ */
756
+ static formatArgs(fn, ctx, dialect) {
757
+ return (fn.args || []).map((a) => {
758
+ if (ctx && dialect) {
759
+ return dialect.compileOperand(a, ctx);
760
+ }
761
+ return String(a);
762
+ }).join(", ");
763
+ }
764
+ /**
765
+ * Formats the base function call with WITH ORDINALITY if present.
766
+ * @param fn - The function table node.
767
+ * @param schemaPart - Formatted schema prefix.
768
+ * @param args - Formatted function arguments.
769
+ * @param dialect - The dialect instance for quoting identifiers.
770
+ * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
771
+ * @internal
772
+ */
773
+ static formatBase(fn, schemaPart, args, dialect) {
774
+ const ordinality = fn.withOrdinality ? " WITH ORDINALITY" : "";
775
+ const quoted = dialect ? dialect.quoteIdentifier(fn.name) : fn.name;
776
+ return `${schemaPart}${quoted}(${args})${ordinality}`;
777
+ }
778
+ /**
779
+ * Formats the LATERAL keyword if present.
780
+ * @param fn - The function table node.
781
+ * @returns "LATERAL " or empty string.
782
+ * @internal
783
+ */
784
+ static formatLateral(fn) {
785
+ return fn.lateral ? "LATERAL " : "";
786
+ }
787
+ /**
788
+ * Formats the table alias for the function table.
789
+ * @param fn - The function table node.
790
+ * @param dialect - The dialect instance for quoting identifiers.
791
+ * @returns " AS alias" or empty string.
792
+ * @internal
793
+ */
794
+ static formatAlias(fn, dialect) {
795
+ if (!fn.alias) return "";
796
+ const quoted = dialect ? dialect.quoteIdentifier(fn.alias) : fn.alias;
797
+ return ` AS ${quoted}`;
798
+ }
799
+ /**
800
+ * Formats column aliases for the function table result columns.
801
+ * @param fn - The function table node containing column aliases.
802
+ * @param dialect - The dialect instance for quoting identifiers.
803
+ * @returns "(col1, col2, ...)" or empty string.
804
+ * @internal
805
+ */
806
+ static formatColumnAliases(fn, dialect) {
807
+ if (!fn.columnAliases || !fn.columnAliases.length) return "";
808
+ const aliases = fn.columnAliases.map((col) => dialect ? dialect.quoteIdentifier(col) : col).join(", ");
809
+ return `(${aliases})`;
810
+ }
811
+ };
812
+
813
+ // src/core/dialect/base/pagination-strategy.ts
814
+ var StandardLimitOffsetPagination = class {
815
+ /**
816
+ * Compiles LIMIT/OFFSET pagination clause.
817
+ * @param limit - The maximum number of rows to return.
818
+ * @param offset - The number of rows to skip.
819
+ * @returns SQL pagination clause with LIMIT and/or OFFSET.
820
+ */
821
+ compilePagination(limit, offset) {
822
+ const parts = [];
823
+ if (limit !== void 0) parts.push(`LIMIT ${limit}`);
824
+ if (offset !== void 0) parts.push(`OFFSET ${offset}`);
825
+ return parts.length ? ` ${parts.join(" ")}` : "";
826
+ }
827
+ };
828
+
829
+ // src/core/dialect/base/cte-compiler.ts
830
+ var CteCompiler = class {
831
+ /**
832
+ * Compiles CTEs (WITH clauses) including recursive CTEs.
833
+ * @param ast - The SELECT query AST containing CTE definitions.
834
+ * @param ctx - The compiler context for expression compilation.
835
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
836
+ * @param compileSelectAst - Function to recursively compile SELECT query ASTs.
837
+ * @param normalizeSelectAst - Function to normalize SELECT query ASTs before compilation.
838
+ * @param stripTrailingSemicolon - Function to remove trailing semicolons from SQL.
839
+ * @returns SQL WITH clause string (e.g., "WITH cte_name AS (...) ") or empty string if no CTEs.
840
+ */
841
+ static compileCtes(ast, ctx, quoteIdentifier, compileSelectAst, normalizeSelectAst, stripTrailingSemicolon) {
842
+ if (!ast.ctes || ast.ctes.length === 0) return "";
843
+ const hasRecursive = ast.ctes.some((cte) => cte.recursive);
844
+ const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
845
+ const cteDefs = ast.ctes.map((cte) => {
846
+ const name = quoteIdentifier(cte.name);
847
+ const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => quoteIdentifier(c)).join(", ")})` : "";
848
+ const query = stripTrailingSemicolon(compileSelectAst(normalizeSelectAst(cte.query), ctx));
849
+ return `${name}${cols} AS (${query})`;
850
+ }).join(", ");
851
+ return `${prefix}${cteDefs} `;
852
+ }
853
+ };
854
+
855
+ // src/core/dialect/base/returning-strategy.ts
856
+ var NoReturningStrategy = class {
857
+ /**
858
+ * Throws an error as RETURNING is not supported.
859
+ * @param returning - Columns to return (causes error if non-empty).
860
+ * @param _ctx - Compiler context (unused).
861
+ * @throws Error indicating RETURNING is not supported.
862
+ */
863
+ compileReturning(returning, _ctx) {
864
+ if (!returning || returning.length === 0) return "";
865
+ throw new Error("RETURNING is not supported by this dialect.");
866
+ }
867
+ /**
868
+ * Formats column names for RETURNING clause.
869
+ * @param returning - Columns to format.
870
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
871
+ * @returns Simple comma-separated column names.
872
+ */
873
+ formatReturningColumns(returning, quoteIdentifier) {
874
+ return returning.map((column) => {
875
+ const tablePart = column.table ? `${quoteIdentifier(column.table)}.` : "";
876
+ const aliasPart = column.alias ? ` AS ${quoteIdentifier(column.alias)}` : "";
877
+ return `${tablePart}${quoteIdentifier(column.name)}${aliasPart}`;
878
+ }).join(", ");
879
+ }
880
+ };
881
+
882
+ // src/core/dialect/base/join-compiler.ts
883
+ var JoinCompiler = class {
884
+ /**
885
+ * Compiles all JOIN clauses from a SELECT query AST.
886
+ * @param ast - The SELECT query AST containing join definitions.
887
+ * @param ctx - The compiler context for expression compilation.
888
+ * @param compileFrom - Function to compile table sources (tables or subqueries).
889
+ * @param compileExpression - Function to compile join condition expressions.
890
+ * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
891
+ */
892
+ static compileJoins(ast, ctx, compileFrom, compileExpression) {
893
+ if (!ast.joins || ast.joins.length === 0) return "";
894
+ const parts = ast.joins.map((j) => {
895
+ const table = compileFrom(j.table, ctx);
896
+ const cond = compileExpression(j.condition, ctx);
897
+ return `${j.kind} JOIN ${table} ON ${cond}`;
898
+ });
899
+ return ` ${parts.join(" ")}`;
900
+ }
901
+ };
902
+
903
+ // src/core/dialect/base/groupby-compiler.ts
904
+ var GroupByCompiler = class {
905
+ /**
906
+ * Compiles GROUP BY clause from a SELECT query AST.
907
+ * @param ast - The SELECT query AST containing grouping columns.
908
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
909
+ * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
910
+ */
911
+ static compileGroupBy(ast, quoteIdentifier) {
912
+ if (!ast.groupBy || ast.groupBy.length === 0) return "";
913
+ const cols = ast.groupBy.map((c) => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`).join(", ");
914
+ return ` GROUP BY ${cols}`;
915
+ }
916
+ };
917
+
918
+ // src/core/dialect/base/orderby-compiler.ts
919
+ var OrderByCompiler = class {
920
+ /**
921
+ * Compiles ORDER BY clause from a SELECT query AST.
922
+ * @param ast - The SELECT query AST containing sort specifications.
923
+ * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
924
+ * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
925
+ */
926
+ static compileOrderBy(ast, quoteIdentifier) {
927
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
928
+ const parts = ast.orderBy.map((o) => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
929
+ return ` ORDER BY ${parts}`;
930
+ }
931
+ };
932
+
933
+ // src/core/dialect/base/sql-dialect.ts
934
+ var SqlDialectBase = class extends Dialect {
935
+ constructor() {
936
+ super(...arguments);
937
+ this.paginationStrategy = new StandardLimitOffsetPagination();
938
+ this.returningStrategy = new NoReturningStrategy();
939
+ }
940
+ compileSelectAst(ast, ctx) {
941
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
942
+ const ctes = CteCompiler.compileCtes(
943
+ ast,
944
+ ctx,
945
+ this.quoteIdentifier.bind(this),
946
+ this.compileSelectAst.bind(this),
947
+ this.normalizeSelectAst?.bind(this) ?? ((a) => a),
948
+ this.stripTrailingSemicolon.bind(this)
949
+ );
950
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
951
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
952
+ if (!hasSetOps) {
953
+ return `${ctes}${baseSelect}`;
954
+ }
955
+ return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
956
+ }
957
+ compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
958
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
959
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
960
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
961
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
962
+ return `${ctes}${combined}${orderBy}${pagination}`;
963
+ }
964
+ compileInsertAst(ast, ctx) {
965
+ const table = this.compileTableName(ast.into);
966
+ const columnList = this.compileInsertColumnList(ast.columns);
967
+ const values = this.compileInsertValues(ast.values, ctx);
968
+ const returning = this.compileReturning(ast.returning, ctx);
969
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
970
+ }
971
+ compileReturning(returning, ctx) {
972
+ return this.returningStrategy.compileReturning(returning, ctx);
973
+ }
974
+ compileInsertColumnList(columns) {
975
+ return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
976
+ }
977
+ compileInsertValues(values, ctx) {
978
+ return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
979
+ }
980
+ compileSelectCore(ast, ctx) {
981
+ const columns = this.compileSelectColumns(ast, ctx);
982
+ const from = this.compileFrom(ast.from, ctx);
983
+ const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
984
+ const whereClause = this.compileWhere(ast.where, ctx);
985
+ const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
986
+ const having = this.compileHaving(ast, ctx);
987
+ const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
988
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
989
+ return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
990
+ }
991
+ compileUpdateAst(ast, ctx) {
992
+ const table = this.compileTableName(ast.table);
993
+ const assignments = this.compileUpdateAssignments(ast.set, ctx);
994
+ const whereClause = this.compileWhere(ast.where, ctx);
995
+ const returning = this.compileReturning(ast.returning, ctx);
996
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
997
+ }
998
+ compileUpdateAssignments(assignments, ctx) {
999
+ return assignments.map((assignment) => {
1000
+ const col = assignment.column;
1001
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1002
+ const value = this.compileOperand(assignment.value, ctx);
1003
+ return `${target} = ${value}`;
1004
+ }).join(", ");
1005
+ }
1006
+ compileDeleteAst(ast, ctx) {
1007
+ const table = this.compileTableName(ast.from);
1008
+ const whereClause = this.compileWhere(ast.where, ctx);
1009
+ const returning = this.compileReturning(ast.returning, ctx);
1010
+ return `DELETE FROM ${table}${whereClause}${returning}`;
1011
+ }
1012
+ formatReturningColumns(returning) {
1013
+ return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
1014
+ }
1015
+ compileDistinct(ast) {
1016
+ return ast.distinct ? "DISTINCT " : "";
1017
+ }
1018
+ compileSelectColumns(ast, ctx) {
1019
+ return ast.columns.map((c) => {
1020
+ const expr = this.compileOperand(c, ctx);
1021
+ if (c.alias) {
1022
+ if (c.alias.includes("(")) return c.alias;
1023
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1024
+ }
1025
+ return expr;
1026
+ }).join(", ");
1027
+ }
1028
+ compileFrom(ast, ctx) {
1029
+ const tableSource = ast;
1030
+ if (tableSource.type === "FunctionTable") {
1031
+ return this.compileFunctionTable(tableSource, ctx);
1032
+ }
1033
+ return this.compileTableSource(tableSource);
1034
+ }
1035
+ compileFunctionTable(fn, ctx) {
1036
+ return FunctionTableFormatter.format(fn, ctx, this);
1037
+ }
1038
+ compileTableSource(table) {
1039
+ const base = this.compileTableName(table);
1040
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1041
+ }
1042
+ compileTableName(table) {
1043
+ if (table.schema) {
1044
+ return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
1045
+ }
1046
+ return this.quoteIdentifier(table.name);
1047
+ }
1048
+ compileHaving(ast, ctx) {
1049
+ if (!ast.having) return "";
1050
+ return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
1051
+ }
1052
+ stripTrailingSemicolon(sql) {
1053
+ return sql.trim().replace(/;$/, "");
1054
+ }
1055
+ wrapSetOperand(sql) {
1056
+ const trimmed = this.stripTrailingSemicolon(sql);
1057
+ return `(${trimmed})`;
1058
+ }
1059
+ };
1060
+
1061
+ // src/core/dialect/postgres/functions.ts
1062
+ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1063
+ constructor() {
1064
+ super();
1065
+ this.registerOverrides();
1066
+ }
1067
+ registerOverrides() {
1068
+ this.add("UTC_NOW", () => `(NOW() AT TIME ZONE 'UTC')`);
1069
+ this.add("UNIX_TIMESTAMP", () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
1070
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1071
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1072
+ return `to_timestamp(${compiledArgs[0]})`;
1073
+ });
1074
+ this.add("EXTRACT", ({ compiledArgs }) => {
1075
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1076
+ const [part, date] = compiledArgs;
1077
+ const partClean = part.replace(/['"]/g, "");
1078
+ return `EXTRACT(${partClean} FROM ${date})`;
1079
+ });
1080
+ this.add("YEAR", ({ compiledArgs }) => {
1081
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1082
+ return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
1083
+ });
1084
+ this.add("MONTH", ({ compiledArgs }) => {
1085
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1086
+ return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
1087
+ });
1088
+ this.add("DAY", ({ compiledArgs }) => {
1089
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1090
+ return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
1091
+ });
1092
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1093
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1094
+ const [date, interval] = compiledArgs;
1095
+ const unitArg = node.args[2];
1096
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1097
+ return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
1098
+ });
1099
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1100
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1101
+ const [date, interval] = compiledArgs;
1102
+ const unitArg = node.args[2];
1103
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1104
+ return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
1105
+ });
1106
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1107
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1108
+ const [date1, date2] = compiledArgs;
1109
+ return `(${date1}::DATE - ${date2}::DATE)`;
1110
+ });
1111
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1112
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1113
+ const [date, format] = compiledArgs;
1114
+ return `TO_CHAR(${date}, ${format})`;
1115
+ });
1116
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1117
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1118
+ return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
1119
+ });
1120
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1121
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1122
+ return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
1123
+ });
1124
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1125
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1126
+ return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
1127
+ });
1128
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1129
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1130
+ const [, date] = compiledArgs;
1131
+ const partArg = node.args[0];
1132
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1133
+ return `DATE_TRUNC('${partClean}', ${date})`;
1134
+ });
1135
+ }
1136
+ };
1137
+
1138
+ // src/core/dialect/postgres/index.ts
1139
+ var PostgresDialect = class extends SqlDialectBase {
1140
+ /**
1141
+ * Creates a new PostgresDialect instance
1142
+ */
1143
+ constructor() {
1144
+ super(new PostgresFunctionStrategy());
1145
+ this.dialect = "postgres";
1146
+ }
1147
+ /**
1148
+ * Quotes an identifier using PostgreSQL double-quote syntax
1149
+ * @param id - Identifier to quote
1150
+ * @returns Quoted identifier
1151
+ */
1152
+ quoteIdentifier(id) {
1153
+ return `"${id}"`;
1154
+ }
1155
+ /**
1156
+ * Compiles JSON path expression using PostgreSQL syntax
1157
+ * @param node - JSON path node
1158
+ * @returns PostgreSQL JSON path expression
1159
+ */
1160
+ compileJsonPath(node) {
1161
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1162
+ return `${col}->>'${node.path}'`;
1163
+ }
1164
+ compileReturning(returning, ctx) {
1165
+ if (!returning || returning.length === 0) return "";
1166
+ const columns = this.formatReturningColumns(returning);
1167
+ return ` RETURNING ${columns}`;
1168
+ }
1169
+ supportsReturning() {
1170
+ return true;
1171
+ }
1172
+ };
1173
+
1174
+ // src/core/dialect/mysql/functions.ts
1175
+ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
1176
+ constructor() {
1177
+ super();
1178
+ this.registerOverrides();
1179
+ }
1180
+ registerOverrides() {
1181
+ this.add("NOW", () => `NOW()`);
1182
+ this.add("CURRENT_DATE", () => `CURDATE()`);
1183
+ this.add("CURRENT_TIME", () => `CURTIME()`);
1184
+ this.add("UTC_NOW", () => `UTC_TIMESTAMP()`);
1185
+ this.add("EXTRACT", ({ compiledArgs }) => {
1186
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1187
+ const [part, date] = compiledArgs;
1188
+ const partClean = part.replace(/['"]/g, "");
1189
+ return `EXTRACT(${partClean} FROM ${date})`;
1190
+ });
1191
+ this.add("YEAR", ({ compiledArgs }) => {
1192
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1193
+ return `YEAR(${compiledArgs[0]})`;
1194
+ });
1195
+ this.add("MONTH", ({ compiledArgs }) => {
1196
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1197
+ return `MONTH(${compiledArgs[0]})`;
1198
+ });
1199
+ this.add("DAY", ({ compiledArgs }) => {
1200
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1201
+ return `DAY(${compiledArgs[0]})`;
1202
+ });
1203
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1204
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1205
+ const [date, interval] = compiledArgs;
1206
+ const unitArg = node.args[2];
1207
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1208
+ return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
1209
+ });
1210
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1211
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1212
+ const [date, interval] = compiledArgs;
1213
+ const unitArg = node.args[2];
1214
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toUpperCase();
1215
+ return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
1216
+ });
1217
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1218
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1219
+ const [date1, date2] = compiledArgs;
1220
+ return `DATEDIFF(${date1}, ${date2})`;
1221
+ });
1222
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1223
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1224
+ const [date, format] = compiledArgs;
1225
+ return `DATE_FORMAT(${date}, ${format})`;
1226
+ });
1227
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1228
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1229
+ return `LAST_DAY(${compiledArgs[0]})`;
1230
+ });
1231
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1232
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1233
+ return `DAYOFWEEK(${compiledArgs[0]})`;
1234
+ });
1235
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1236
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1237
+ return `WEEKOFYEAR(${compiledArgs[0]})`;
1238
+ });
1239
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1240
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1241
+ const [, date] = compiledArgs;
1242
+ const partArg = node.args[0];
1243
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1244
+ if (partClean === "year") {
1245
+ return `DATE_FORMAT(${date}, '%Y-01-01')`;
1246
+ } else if (partClean === "month") {
1247
+ return `DATE_FORMAT(${date}, '%Y-%m-01')`;
1248
+ } else if (partClean === "day") {
1249
+ return `DATE(${date})`;
1250
+ }
1251
+ return `DATE(${date})`;
1252
+ });
1253
+ }
1254
+ };
1255
+
1256
+ // src/core/dialect/mysql/index.ts
1257
+ var MySqlDialect = class extends SqlDialectBase {
1258
+ /**
1259
+ * Creates a new MySqlDialect instance
1260
+ */
1261
+ constructor() {
1262
+ super(new MysqlFunctionStrategy());
1263
+ this.dialect = "mysql";
1264
+ }
1265
+ /**
1266
+ * Quotes an identifier using MySQL backtick syntax
1267
+ * @param id - Identifier to quote
1268
+ * @returns Quoted identifier
1269
+ */
1270
+ quoteIdentifier(id) {
1271
+ return `\`${id}\``;
1272
+ }
1273
+ /**
1274
+ * Compiles JSON path expression using MySQL syntax
1275
+ * @param node - JSON path node
1276
+ * @returns MySQL JSON path expression
1277
+ */
1278
+ compileJsonPath(node) {
1279
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1280
+ return `${col}->'${node.path}'`;
1281
+ }
1282
+ };
1283
+
1284
+ // src/core/dialect/sqlite/functions.ts
1285
+ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1286
+ constructor() {
1287
+ super();
1288
+ this.registerOverrides();
1289
+ }
1290
+ registerOverrides() {
1291
+ this.add("NOW", () => `datetime('now', 'localtime')`);
1292
+ this.add("CURRENT_DATE", () => `date('now', 'localtime')`);
1293
+ this.add("CURRENT_TIME", () => `time('now', 'localtime')`);
1294
+ this.add("UTC_NOW", () => `datetime('now')`);
1295
+ this.add("EXTRACT", ({ compiledArgs }) => {
1296
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1297
+ const [part, date] = compiledArgs;
1298
+ const partUpper = part.replace(/['"]/g, "").toUpperCase();
1299
+ const formatMap = {
1300
+ "YEAR": "%Y",
1301
+ "MONTH": "%m",
1302
+ "DAY": "%d",
1303
+ "HOUR": "%H",
1304
+ "MINUTE": "%M",
1305
+ "SECOND": "%S",
1306
+ "DOW": "%w",
1307
+ "WEEK": "%W"
1308
+ };
1309
+ const format = formatMap[partUpper] || "%Y";
1310
+ return `CAST(strftime('${format}', ${date}) AS INTEGER)`;
1311
+ });
1312
+ this.add("YEAR", ({ compiledArgs }) => {
1313
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1314
+ return `CAST(strftime('%Y', ${compiledArgs[0]}) AS INTEGER)`;
1315
+ });
1316
+ this.add("MONTH", ({ compiledArgs }) => {
1317
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1318
+ return `CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER)`;
1319
+ });
1320
+ this.add("DAY", ({ compiledArgs }) => {
1321
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1322
+ return `CAST(strftime('%d', ${compiledArgs[0]}) AS INTEGER)`;
1323
+ });
1324
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1325
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1326
+ const [date, interval] = compiledArgs;
1327
+ const unitArg = node.args[2];
1328
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1329
+ return `datetime(${date}, '+' || ${interval} || ' ${unitClean}')`;
1330
+ });
1331
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1332
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1333
+ const [date, interval] = compiledArgs;
1334
+ const unitArg = node.args[2];
1335
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1336
+ return `datetime(${date}, '-' || ${interval} || ' ${unitClean}')`;
1337
+ });
1338
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1339
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1340
+ const [date1, date2] = compiledArgs;
1341
+ return `CAST(julianday(${date1}) - julianday(${date2}) AS INTEGER)`;
1342
+ });
1343
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1344
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1345
+ const [date, format] = compiledArgs;
1346
+ return `strftime(${format}, ${date})`;
1347
+ });
1348
+ this.add("UNIX_TIMESTAMP", () => `CAST(strftime('%s', 'now') AS INTEGER)`);
1349
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1350
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1351
+ return `datetime(${compiledArgs[0]}, 'unixepoch')`;
1352
+ });
1353
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1354
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1355
+ return `date(${compiledArgs[0]}, 'start of month', '+1 month', '-1 day')`;
1356
+ });
1357
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1358
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1359
+ return `CAST(strftime('%w', ${compiledArgs[0]}) AS INTEGER)`;
1360
+ });
1361
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1362
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1363
+ return `CAST(strftime('%W', ${compiledArgs[0]}) AS INTEGER)`;
1364
+ });
1365
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1366
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1367
+ const [, date] = compiledArgs;
1368
+ const partArg = node.args[0];
1369
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1370
+ if (partClean === "year") {
1371
+ return `date(${date}, 'start of year')`;
1372
+ } else if (partClean === "month") {
1373
+ return `date(${date}, 'start of month')`;
1374
+ } else if (partClean === "day") {
1375
+ return `date(${date})`;
1376
+ }
1377
+ return `date(${date}, 'start of ${partClean}')`;
1378
+ });
1379
+ }
1380
+ };
1381
+
1382
+ // src/core/dialect/sqlite/index.ts
1383
+ var SqliteDialect = class extends SqlDialectBase {
1384
+ /**
1385
+ * Creates a new SqliteDialect instance
1386
+ */
1387
+ constructor() {
1388
+ super(new SqliteFunctionStrategy());
1389
+ this.dialect = "sqlite";
1390
+ }
1391
+ /**
1392
+ * Quotes an identifier using SQLite double-quote syntax
1393
+ * @param id - Identifier to quote
1394
+ * @returns Quoted identifier
1395
+ */
1396
+ quoteIdentifier(id) {
1397
+ return `"${id}"`;
1398
+ }
1399
+ /**
1400
+ * Compiles JSON path expression using SQLite syntax
1401
+ * @param node - JSON path node
1402
+ * @returns SQLite JSON path expression
1403
+ */
1404
+ compileJsonPath(node) {
1405
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1406
+ return `json_extract(${col}, '${node.path}')`;
1407
+ }
1408
+ compileReturning(returning, ctx) {
1409
+ if (!returning || returning.length === 0) return "";
1410
+ const columns = this.formatReturningColumns(returning);
1411
+ return ` RETURNING ${columns}`;
1412
+ }
1413
+ supportsReturning() {
1414
+ return true;
1415
+ }
1416
+ };
1417
+
1418
+ // src/core/dialect/mssql/functions.ts
1419
+ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1420
+ constructor() {
1421
+ super();
1422
+ this.registerOverrides();
1423
+ }
1424
+ registerOverrides() {
1425
+ this.add("NOW", () => `GETDATE()`);
1426
+ this.add("CURRENT_DATE", () => `CAST(GETDATE() AS DATE)`);
1427
+ this.add("CURRENT_TIME", () => `CAST(GETDATE() AS TIME)`);
1428
+ this.add("UTC_NOW", () => `GETUTCDATE()`);
1429
+ this.add("EXTRACT", ({ compiledArgs }) => {
1430
+ if (compiledArgs.length !== 2) throw new Error("EXTRACT expects 2 arguments (part, date)");
1431
+ const [part, date] = compiledArgs;
1432
+ const partClean = part.replace(/['"]/g, "").toLowerCase();
1433
+ return `DATEPART(${partClean}, ${date})`;
1434
+ });
1435
+ this.add("YEAR", ({ compiledArgs }) => {
1436
+ if (compiledArgs.length !== 1) throw new Error("YEAR expects 1 argument");
1437
+ return `YEAR(${compiledArgs[0]})`;
1438
+ });
1439
+ this.add("MONTH", ({ compiledArgs }) => {
1440
+ if (compiledArgs.length !== 1) throw new Error("MONTH expects 1 argument");
1441
+ return `MONTH(${compiledArgs[0]})`;
1442
+ });
1443
+ this.add("DAY", ({ compiledArgs }) => {
1444
+ if (compiledArgs.length !== 1) throw new Error("DAY expects 1 argument");
1445
+ return `DAY(${compiledArgs[0]})`;
1446
+ });
1447
+ this.add("DATE_ADD", ({ node, compiledArgs }) => {
1448
+ if (compiledArgs.length !== 3) throw new Error("DATE_ADD expects 3 arguments (date, interval, unit)");
1449
+ const [date, interval] = compiledArgs;
1450
+ const unitArg = node.args[2];
1451
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1452
+ return `DATEADD(${unitClean}, ${interval}, ${date})`;
1453
+ });
1454
+ this.add("DATE_SUB", ({ node, compiledArgs }) => {
1455
+ if (compiledArgs.length !== 3) throw new Error("DATE_SUB expects 3 arguments (date, interval, unit)");
1456
+ const [date, interval] = compiledArgs;
1457
+ const unitArg = node.args[2];
1458
+ const unitClean = String(unitArg.value).replace(/['"]/g, "").toLowerCase();
1459
+ return `DATEADD(${unitClean}, -${interval}, ${date})`;
1460
+ });
1461
+ this.add("DATE_DIFF", ({ compiledArgs }) => {
1462
+ if (compiledArgs.length !== 2) throw new Error("DATE_DIFF expects 2 arguments");
1463
+ const [date1, date2] = compiledArgs;
1464
+ return `DATEDIFF(day, ${date2}, ${date1})`;
1465
+ });
1466
+ this.add("DATE_FORMAT", ({ compiledArgs }) => {
1467
+ if (compiledArgs.length !== 2) throw new Error("DATE_FORMAT expects 2 arguments");
1468
+ const [date, format] = compiledArgs;
1469
+ return `FORMAT(${date}, ${format})`;
1470
+ });
1471
+ this.add("UNIX_TIMESTAMP", () => `DATEDIFF(SECOND, '1970-01-01', GETUTCDATE())`);
1472
+ this.add("FROM_UNIXTIME", ({ compiledArgs }) => {
1473
+ if (compiledArgs.length !== 1) throw new Error("FROM_UNIXTIME expects 1 argument");
1474
+ return `DATEADD(SECOND, ${compiledArgs[0]}, '1970-01-01')`;
1475
+ });
1476
+ this.add("END_OF_MONTH", ({ compiledArgs }) => {
1477
+ if (compiledArgs.length !== 1) throw new Error("END_OF_MONTH expects 1 argument");
1478
+ return `EOMONTH(${compiledArgs[0]})`;
1479
+ });
1480
+ this.add("DAY_OF_WEEK", ({ compiledArgs }) => {
1481
+ if (compiledArgs.length !== 1) throw new Error("DAY_OF_WEEK expects 1 argument");
1482
+ return `DATEPART(dw, ${compiledArgs[0]})`;
1483
+ });
1484
+ this.add("WEEK_OF_YEAR", ({ compiledArgs }) => {
1485
+ if (compiledArgs.length !== 1) throw new Error("WEEK_OF_YEAR expects 1 argument");
1486
+ return `DATEPART(wk, ${compiledArgs[0]})`;
1487
+ });
1488
+ this.add("DATE_TRUNC", ({ node, compiledArgs }) => {
1489
+ if (compiledArgs.length !== 2) throw new Error("DATE_TRUNC expects 2 arguments (part, date)");
1490
+ const [, date] = compiledArgs;
1491
+ const partArg = node.args[0];
1492
+ const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1493
+ return `DATETRUNC(${partClean}, ${date})`;
1494
+ });
1495
+ }
1496
+ };
1497
+
1498
+ // src/core/dialect/mssql/index.ts
1499
+ var SqlServerDialect = class extends Dialect {
1500
+ /**
1501
+ * Creates a new SqlServerDialect instance
1502
+ */
1503
+ constructor() {
1504
+ super(new MssqlFunctionStrategy());
1505
+ this.dialect = "mssql";
1506
+ }
1507
+ /**
1508
+ * Quotes an identifier using SQL Server bracket syntax
1509
+ * @param id - Identifier to quote
1510
+ * @returns Quoted identifier
1511
+ */
1512
+ quoteIdentifier(id) {
1513
+ return `[${id}]`;
1514
+ }
1515
+ /**
1516
+ * Compiles JSON path expression using SQL Server syntax
1517
+ * @param node - JSON path node
1518
+ * @returns SQL Server JSON path expression
1519
+ */
1520
+ compileJsonPath(node) {
1521
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
1522
+ return `JSON_VALUE(${col}, '${node.path}')`;
1523
+ }
1524
+ /**
1525
+ * Formats parameter placeholders using SQL Server named parameter syntax
1526
+ * @param index - Parameter index
1527
+ * @returns Named parameter placeholder
1528
+ */
1529
+ formatPlaceholder(index) {
1530
+ return `@p${index}`;
1531
+ }
1532
+ /**
1533
+ * Compiles SELECT query AST to SQL Server SQL
1534
+ * @param ast - Query AST
1535
+ * @param ctx - Compiler context
1536
+ * @returns SQL Server SQL string
1537
+ */
1538
+ compileSelectAst(ast, ctx) {
1539
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
1540
+ const ctes = this.compileCtes(ast, ctx);
1541
+ const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
1542
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
1543
+ if (!hasSetOps) {
1544
+ return `${ctes}${baseSelect}`;
1545
+ }
1546
+ const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1547
+ const orderBy = this.compileOrderBy(ast);
1548
+ const pagination = this.compilePagination(ast, orderBy);
1549
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1550
+ const tail = pagination || orderBy;
1551
+ return `${ctes}${combined}${tail}`;
1552
+ }
1553
+ compileInsertAst(ast, ctx) {
1554
+ const table = this.quoteIdentifier(ast.into.name);
1555
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1556
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1557
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
1558
+ }
1559
+ compileUpdateAst(ast, ctx) {
1560
+ const table = this.quoteIdentifier(ast.table.name);
1561
+ const assignments = ast.set.map((assignment) => {
1562
+ const col = assignment.column;
1563
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1564
+ const value = this.compileOperand(assignment.value, ctx);
1565
+ return `${target} = ${value}`;
1566
+ }).join(", ");
1567
+ const whereClause = this.compileWhere(ast.where, ctx);
1568
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
1569
+ }
1570
+ compileDeleteAst(ast, ctx) {
1571
+ const table = this.quoteIdentifier(ast.from.name);
1572
+ const whereClause = this.compileWhere(ast.where, ctx);
1573
+ return `DELETE FROM ${table}${whereClause};`;
1574
+ }
1575
+ compileSelectCore(ast, ctx) {
1576
+ const columns = ast.columns.map((c) => {
1577
+ let expr = "";
1578
+ if (c.type === "Function") {
1579
+ expr = this.compileOperand(c, ctx);
1580
+ } else if (c.type === "Column") {
1581
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
1582
+ } else if (c.type === "ScalarSubquery") {
1583
+ expr = this.compileOperand(c, ctx);
1584
+ } else if (c.type === "WindowFunction") {
1585
+ expr = this.compileOperand(c, ctx);
1586
+ }
1587
+ if (c.alias) {
1588
+ if (c.alias.includes("(")) return c.alias;
1589
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
1590
+ }
1591
+ return expr;
1592
+ }).join(", ");
1593
+ const distinct = ast.distinct ? "DISTINCT " : "";
1594
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
1595
+ const joins = ast.joins.map((j) => {
1596
+ const table = this.quoteIdentifier(j.table.name);
1597
+ const cond = this.compileExpression(j.condition, ctx);
1598
+ return `${j.kind} JOIN ${table} ON ${cond}`;
1599
+ }).join(" ");
1600
+ const whereClause = this.compileWhere(ast.where, ctx);
1601
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
1602
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
1603
+ const orderBy = this.compileOrderBy(ast);
1604
+ const pagination = this.compilePagination(ast, orderBy);
1605
+ if (pagination) {
1606
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
1607
+ }
1608
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
1609
+ }
1610
+ compileOrderBy(ast) {
1611
+ if (!ast.orderBy || ast.orderBy.length === 0) return "";
1612
+ return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1613
+ }
1614
+ compilePagination(ast, orderBy) {
1615
+ const hasLimit = ast.limit !== void 0;
1616
+ const hasOffset = ast.offset !== void 0;
1617
+ if (!hasLimit && !hasOffset) return "";
1618
+ const off = ast.offset ?? 0;
1619
+ const orderClause = orderBy || " ORDER BY (SELECT NULL)";
1620
+ let pagination = `${orderClause} OFFSET ${off} ROWS`;
1621
+ if (hasLimit) {
1622
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
1623
+ }
1624
+ return pagination;
1625
+ }
1626
+ compileCtes(ast, ctx) {
1627
+ if (!ast.ctes || ast.ctes.length === 0) return "";
1628
+ const defs = ast.ctes.map((cte) => {
1629
+ const name = this.quoteIdentifier(cte.name);
1630
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1631
+ const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
1632
+ return `${name}${cols} AS (${query})`;
1633
+ }).join(", ");
1634
+ return `WITH ${defs} `;
1635
+ }
1636
+ wrapSetOperand(sql) {
1637
+ const trimmed = sql.trim().replace(/;$/, "");
1638
+ return `(${trimmed})`;
1639
+ }
1640
+ };
1641
+
1642
+ // src/core/dialect/dialect-factory.ts
1643
+ var DialectFactory = class {
1644
+ static {
1645
+ this.registry = /* @__PURE__ */ new Map();
1646
+ }
1647
+ static {
1648
+ this.defaultsInitialized = false;
1649
+ }
1650
+ static ensureDefaults() {
1651
+ if (this.defaultsInitialized) return;
1652
+ this.defaultsInitialized = true;
1653
+ if (!this.registry.has("postgres")) {
1654
+ this.registry.set("postgres", () => new PostgresDialect());
1655
+ }
1656
+ if (!this.registry.has("mysql")) {
1657
+ this.registry.set("mysql", () => new MySqlDialect());
1658
+ }
1659
+ if (!this.registry.has("sqlite")) {
1660
+ this.registry.set("sqlite", () => new SqliteDialect());
1661
+ }
1662
+ if (!this.registry.has("mssql")) {
1663
+ this.registry.set("mssql", () => new SqlServerDialect());
1664
+ }
1665
+ }
1666
+ /**
1667
+ * Register (or override) a dialect factory for a key.
1668
+ *
1669
+ * Examples:
1670
+ * DialectFactory.register('sqlite', () => new SqliteDialect());
1671
+ * DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
1672
+ */
1673
+ static register(key, factory) {
1674
+ this.registry.set(key, factory);
1675
+ }
1676
+ /**
1677
+ * Resolve a key into a Dialect instance.
1678
+ * Throws if the key is not registered.
1679
+ */
1680
+ static create(key) {
1681
+ this.ensureDefaults();
1682
+ const factory = this.registry.get(key);
1683
+ if (!factory) {
1684
+ throw new Error(
1685
+ `Dialect "${String(
1686
+ key
1687
+ )}" is not registered. Use DialectFactory.register(...) to register it.`
1688
+ );
1689
+ }
1690
+ return factory();
1691
+ }
1692
+ /**
1693
+ * Clear all registrations (mainly for tests).
1694
+ * Built-ins will be re-registered lazily on the next create().
1695
+ */
1696
+ static clear() {
1697
+ this.registry.clear();
1698
+ this.defaultsInitialized = false;
1699
+ }
1700
+ };
1701
+ var resolveDialectInput = (dialect) => {
1702
+ if (typeof dialect === "string") {
1703
+ return DialectFactory.create(dialect);
1704
+ }
1705
+ return dialect;
1706
+ };
1707
+
336
1708
  // src/query-builder/select-query-state.ts
337
1709
  var SelectQueryState = class _SelectQueryState {
338
1710
  /**
@@ -484,9 +1856,9 @@ var SelectQueryState = class _SelectQueryState {
484
1856
  var createJoinNode = (kind, tableName, condition, relationName) => ({
485
1857
  type: "Join",
486
1858
  kind,
487
- table: { type: "Table", name: tableName },
1859
+ table: typeof tableName === "string" ? { type: "Table", name: tableName } : tableName,
488
1860
  condition,
489
- relationName
1861
+ meta: relationName ? { relationName } : void 0
490
1862
  });
491
1863
 
492
1864
  // src/core/sql/sql.ts
@@ -828,7 +2200,8 @@ var HydrationPlanner = class _HydrationPlanner {
828
2200
  */
829
2201
  buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
830
2202
  switch (rel.type) {
831
- case RelationKinds.HasMany: {
2203
+ case RelationKinds.HasMany:
2204
+ case RelationKinds.HasOne: {
832
2205
  const localKey = rel.localKey || findPrimaryKey(this.table);
833
2206
  return {
834
2207
  name: relationName,
@@ -1157,10 +2530,11 @@ var assertNever = (value) => {
1157
2530
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
1158
2531
  };
1159
2532
  var baseRelationCondition = (root, relation) => {
1160
- const defaultLocalKey = relation.type === RelationKinds.HasMany ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2533
+ const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
1161
2534
  const localKey = relation.localKey || defaultLocalKey;
1162
2535
  switch (relation.type) {
1163
2536
  case RelationKinds.HasMany:
2537
+ case RelationKinds.HasOne:
1164
2538
  return eq(
1165
2539
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
1166
2540
  { type: "Column", table: root.name, name: localKey }
@@ -1207,6 +2581,9 @@ var buildRelationCorrelation = (root, relation) => {
1207
2581
  return baseRelationCondition(root, relation);
1208
2582
  };
1209
2583
 
2584
+ // src/core/ast/join-metadata.ts
2585
+ var getJoinRelationName = (join) => join.meta?.relationName;
2586
+
1210
2587
  // src/query-builder/relation-service.ts
1211
2588
  var RelationService = class {
1212
2589
  /**
@@ -1261,7 +2638,7 @@ var RelationService = class {
1261
2638
  let hydration = this.hydration;
1262
2639
  const relation = this.getRelation(relationName);
1263
2640
  const aliasPrefix = options?.aliasPrefix ?? relationName;
1264
- const alreadyJoined = state.ast.joins.some((j) => j.relationName === relationName);
2641
+ const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
1265
2642
  if (!alreadyJoined) {
1266
2643
  const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
1267
2644
  state = joined.state;
@@ -1582,6 +2959,12 @@ var hydrateRows = (rows, plan) => {
1582
2959
  const seen = getRelationSeenSet(rootId, rel.name);
1583
2960
  if (seen.has(childPk)) continue;
1584
2961
  seen.add(childPk);
2962
+ if (rel.type === RelationKinds.HasOne) {
2963
+ if (!parent[rel.name]) {
2964
+ parent[rel.name] = buildChild(row, rel);
2965
+ }
2966
+ continue;
2967
+ }
1585
2968
  const bucket = parent[rel.name];
1586
2969
  bucket.push(buildChild(row, rel));
1587
2970
  }
@@ -1595,7 +2978,7 @@ var createBaseRow = (row, plan) => {
1595
2978
  base[key] = row[key];
1596
2979
  }
1597
2980
  for (const rel of plan.relations) {
1598
- base[rel.name] = [];
2981
+ base[rel.name] = rel.type === RelationKinds.HasOne ? null : [];
1599
2982
  }
1600
2983
  return base;
1601
2984
  };
@@ -1758,7 +3141,7 @@ var DefaultHasManyCollection = class {
1758
3141
  }
1759
3142
  };
1760
3143
 
1761
- // src/orm/relations/belongs-to.ts
3144
+ // src/orm/relations/has-one.ts
1762
3145
  var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
1763
3146
  var hideInternal2 = (obj, keys) => {
1764
3147
  for (const key of keys) {
@@ -1770,6 +3153,123 @@ var hideInternal2 = (obj, keys) => {
1770
3153
  });
1771
3154
  }
1772
3155
  };
3156
+ var DefaultHasOneReference = class {
3157
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
3158
+ this.ctx = ctx;
3159
+ this.meta = meta;
3160
+ this.root = root;
3161
+ this.relationName = relationName;
3162
+ this.relation = relation;
3163
+ this.rootTable = rootTable;
3164
+ this.loader = loader;
3165
+ this.createEntity = createEntity;
3166
+ this.localKey = localKey;
3167
+ this.loaded = false;
3168
+ this.current = null;
3169
+ hideInternal2(this, [
3170
+ "ctx",
3171
+ "meta",
3172
+ "root",
3173
+ "relationName",
3174
+ "relation",
3175
+ "rootTable",
3176
+ "loader",
3177
+ "createEntity",
3178
+ "localKey"
3179
+ ]);
3180
+ this.populateFromHydrationCache();
3181
+ }
3182
+ async load() {
3183
+ if (this.loaded) return this.current;
3184
+ const map = await this.loader();
3185
+ const keyValue = this.root[this.localKey];
3186
+ if (keyValue === void 0 || keyValue === null) {
3187
+ this.loaded = true;
3188
+ return this.current;
3189
+ }
3190
+ const row = map.get(toKey3(keyValue));
3191
+ this.current = row ? this.createEntity(row) : null;
3192
+ this.loaded = true;
3193
+ return this.current;
3194
+ }
3195
+ get() {
3196
+ return this.current;
3197
+ }
3198
+ set(data) {
3199
+ if (data === null) {
3200
+ return this.detachCurrent();
3201
+ }
3202
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
3203
+ if (this.current && this.current !== entity) {
3204
+ this.ctx.registerRelationChange(
3205
+ this.root,
3206
+ this.relationKey,
3207
+ this.rootTable,
3208
+ this.relationName,
3209
+ this.relation,
3210
+ { kind: "remove", entity: this.current }
3211
+ );
3212
+ }
3213
+ this.assignForeignKey(entity);
3214
+ this.current = entity;
3215
+ this.loaded = true;
3216
+ this.ctx.registerRelationChange(
3217
+ this.root,
3218
+ this.relationKey,
3219
+ this.rootTable,
3220
+ this.relationName,
3221
+ this.relation,
3222
+ { kind: "attach", entity }
3223
+ );
3224
+ return entity;
3225
+ }
3226
+ toJSON() {
3227
+ return this.current;
3228
+ }
3229
+ detachCurrent() {
3230
+ const previous = this.current;
3231
+ if (!previous) return null;
3232
+ this.current = null;
3233
+ this.loaded = true;
3234
+ this.ctx.registerRelationChange(
3235
+ this.root,
3236
+ this.relationKey,
3237
+ this.rootTable,
3238
+ this.relationName,
3239
+ this.relation,
3240
+ { kind: "remove", entity: previous }
3241
+ );
3242
+ return null;
3243
+ }
3244
+ assignForeignKey(entity) {
3245
+ const keyValue = this.root[this.localKey];
3246
+ entity[this.relation.foreignKey] = keyValue;
3247
+ }
3248
+ get relationKey() {
3249
+ return `${this.rootTable.name}.${this.relationName}`;
3250
+ }
3251
+ populateFromHydrationCache() {
3252
+ const keyValue = this.root[this.localKey];
3253
+ if (keyValue === void 0 || keyValue === null) return;
3254
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
3255
+ if (!row) return;
3256
+ this.current = this.createEntity(row);
3257
+ this.loaded = true;
3258
+ }
3259
+ };
3260
+
3261
+ // src/orm/relations/belongs-to.ts
3262
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
3263
+ var hideInternal3 = (obj, keys) => {
3264
+ for (const key of keys) {
3265
+ Object.defineProperty(obj, key, {
3266
+ value: obj[key],
3267
+ writable: false,
3268
+ configurable: false,
3269
+ enumerable: false
3270
+ });
3271
+ }
3272
+ };
1773
3273
  var DefaultBelongsToReference = class {
1774
3274
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
1775
3275
  this.ctx = ctx;
@@ -1783,7 +3283,7 @@ var DefaultBelongsToReference = class {
1783
3283
  this.targetKey = targetKey;
1784
3284
  this.loaded = false;
1785
3285
  this.current = null;
1786
- hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
3286
+ hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
1787
3287
  this.populateFromHydrationCache();
1788
3288
  }
1789
3289
  async load() {
@@ -1793,7 +3293,7 @@ var DefaultBelongsToReference = class {
1793
3293
  if (fkValue === null || fkValue === void 0) {
1794
3294
  this.current = null;
1795
3295
  } else {
1796
- const row = map.get(toKey3(fkValue));
3296
+ const row = map.get(toKey4(fkValue));
1797
3297
  this.current = row ? this.createEntity(row) : null;
1798
3298
  }
1799
3299
  this.loaded = true;
@@ -1850,8 +3350,8 @@ var DefaultBelongsToReference = class {
1850
3350
  };
1851
3351
 
1852
3352
  // src/orm/relations/many-to-many.ts
1853
- var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
1854
- var hideInternal3 = (obj, keys) => {
3353
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3354
+ var hideInternal4 = (obj, keys) => {
1855
3355
  for (const key of keys) {
1856
3356
  Object.defineProperty(obj, key, {
1857
3357
  value: obj[key],
@@ -1874,13 +3374,13 @@ var DefaultManyToManyCollection = class {
1874
3374
  this.localKey = localKey;
1875
3375
  this.loaded = false;
1876
3376
  this.items = [];
1877
- hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
3377
+ hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
1878
3378
  this.hydrateFromCache();
1879
3379
  }
1880
3380
  async load() {
1881
3381
  if (this.loaded) return this.items;
1882
3382
  const map = await this.loader();
1883
- const key = toKey4(this.root[this.localKey]);
3383
+ const key = toKey5(this.root[this.localKey]);
1884
3384
  const rows = map.get(key) ?? [];
1885
3385
  this.items = rows.map((row) => {
1886
3386
  const entity = this.createEntity(row);
@@ -1930,15 +3430,15 @@ var DefaultManyToManyCollection = class {
1930
3430
  async syncByIds(ids) {
1931
3431
  await this.load();
1932
3432
  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))));
3433
+ const normalized = new Set(ids.map((id) => toKey5(id)));
3434
+ const currentIds = new Set(this.items.map((item) => toKey5(this.extractId(item))));
1935
3435
  for (const id of normalized) {
1936
3436
  if (!currentIds.has(id)) {
1937
3437
  this.attach(id);
1938
3438
  }
1939
3439
  }
1940
3440
  for (const item of [...this.items]) {
1941
- const itemId = toKey4(this.extractId(item));
3441
+ const itemId = toKey5(this.extractId(item));
1942
3442
  if (!normalized.has(itemId)) {
1943
3443
  this.detach(item);
1944
3444
  }
@@ -2009,7 +3509,7 @@ var executeQuery = async (ctx, qb) => {
2009
3509
  const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
2010
3510
  return rowsFromResults(results);
2011
3511
  };
2012
- var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
3512
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
2013
3513
  var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2014
3514
  const localKey = relation.localKey || findPrimaryKey(rootTable);
2015
3515
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -2033,13 +3533,43 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
2033
3533
  for (const row of rows) {
2034
3534
  const fkValue = row[relation.foreignKey];
2035
3535
  if (fkValue === null || fkValue === void 0) continue;
2036
- const key = toKey5(fkValue);
3536
+ const key = toKey6(fkValue);
2037
3537
  const bucket = grouped.get(key) ?? [];
2038
3538
  bucket.push(row);
2039
3539
  grouped.set(key, bucket);
2040
3540
  }
2041
3541
  return grouped;
2042
3542
  };
3543
+ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
3544
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
3545
+ const roots = ctx.getEntitiesForTable(rootTable);
3546
+ const keys = /* @__PURE__ */ new Set();
3547
+ for (const tracked of roots) {
3548
+ const value = tracked.entity[localKey];
3549
+ if (value !== null && value !== void 0) {
3550
+ keys.add(value);
3551
+ }
3552
+ }
3553
+ if (!keys.size) {
3554
+ return /* @__PURE__ */ new Map();
3555
+ }
3556
+ const selectMap = selectAllColumns(relation.target);
3557
+ const qb = new SelectQueryBuilder(relation.target).select(selectMap);
3558
+ const fkColumn = relation.target.columns[relation.foreignKey];
3559
+ if (!fkColumn) return /* @__PURE__ */ new Map();
3560
+ qb.where(inList(fkColumn, Array.from(keys)));
3561
+ const rows = await executeQuery(ctx, qb);
3562
+ const lookup = /* @__PURE__ */ new Map();
3563
+ for (const row of rows) {
3564
+ const fkValue = row[relation.foreignKey];
3565
+ if (fkValue === null || fkValue === void 0) continue;
3566
+ const key = toKey6(fkValue);
3567
+ if (!lookup.has(key)) {
3568
+ lookup.set(key, row);
3569
+ }
3570
+ }
3571
+ return lookup;
3572
+ };
2043
3573
  var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2044
3574
  const roots = ctx.getEntitiesForTable(rootTable);
2045
3575
  const foreignKeys = /* @__PURE__ */ new Set();
@@ -2063,7 +3593,7 @@ var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
2063
3593
  for (const row of rows) {
2064
3594
  const keyValue = row[targetKey];
2065
3595
  if (keyValue === null || keyValue === void 0) continue;
2066
- map.set(toKey5(keyValue), row);
3596
+ map.set(toKey6(keyValue), row);
2067
3597
  }
2068
3598
  return map;
2069
3599
  };
@@ -2094,12 +3624,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2094
3624
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
2095
3625
  continue;
2096
3626
  }
2097
- const bucket = rootLookup.get(toKey5(rootValue)) ?? [];
3627
+ const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
2098
3628
  bucket.push({
2099
3629
  targetId: targetValue,
2100
3630
  pivot: { ...pivot }
2101
3631
  });
2102
- rootLookup.set(toKey5(rootValue), bucket);
3632
+ rootLookup.set(toKey6(rootValue), bucket);
2103
3633
  targetIds.add(targetValue);
2104
3634
  }
2105
3635
  if (!targetIds.size) {
@@ -2116,13 +3646,13 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
2116
3646
  for (const row of targetRows) {
2117
3647
  const pkValue = row[targetKey];
2118
3648
  if (pkValue === null || pkValue === void 0) continue;
2119
- targetMap.set(toKey5(pkValue), row);
3649
+ targetMap.set(toKey6(pkValue), row);
2120
3650
  }
2121
3651
  const result = /* @__PURE__ */ new Map();
2122
3652
  for (const [rootId, entries] of rootLookup.entries()) {
2123
3653
  const bucket = [];
2124
3654
  for (const entry of entries) {
2125
- const targetRow = targetMap.get(toKey5(entry.targetId));
3655
+ const targetRow = targetMap.get(toKey6(entry.targetId));
2126
3656
  if (!targetRow) continue;
2127
3657
  bucket.push({
2128
3658
  ...targetRow,
@@ -2217,18 +3747,29 @@ var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
2217
3747
  }
2218
3748
  return entity;
2219
3749
  };
2220
- var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
3750
+ var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
2221
3751
  var populateHydrationCache = (entity, row, meta) => {
2222
3752
  for (const relationName of Object.keys(meta.table.relations)) {
2223
3753
  const relation = meta.table.relations[relationName];
2224
3754
  const data = row[relationName];
3755
+ if (relation.type === RelationKinds.HasOne) {
3756
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
3757
+ const rootValue = entity[localKey];
3758
+ if (rootValue === void 0 || rootValue === null) continue;
3759
+ if (!data || typeof data !== "object") continue;
3760
+ const cache = /* @__PURE__ */ new Map();
3761
+ cache.set(toKey7(rootValue), data);
3762
+ meta.relationHydration.set(relationName, cache);
3763
+ meta.relationCache.set(relationName, Promise.resolve(cache));
3764
+ continue;
3765
+ }
2225
3766
  if (!Array.isArray(data)) continue;
2226
3767
  if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
2227
3768
  const localKey = relation.localKey || findPrimaryKey(meta.table);
2228
3769
  const rootValue = entity[localKey];
2229
3770
  if (rootValue === void 0 || rootValue === null) continue;
2230
3771
  const cache = /* @__PURE__ */ new Map();
2231
- cache.set(toKey6(rootValue), data);
3772
+ cache.set(toKey7(rootValue), data);
2232
3773
  meta.relationHydration.set(relationName, cache);
2233
3774
  meta.relationCache.set(relationName, Promise.resolve(cache));
2234
3775
  continue;
@@ -2239,7 +3780,7 @@ var populateHydrationCache = (entity, row, meta) => {
2239
3780
  for (const item of data) {
2240
3781
  const pkValue = item[targetKey];
2241
3782
  if (pkValue === void 0 || pkValue === null) continue;
2242
- cache.set(toKey6(pkValue), item);
3783
+ cache.set(toKey7(pkValue), item);
2243
3784
  }
2244
3785
  if (cache.size) {
2245
3786
  meta.relationHydration.set(relationName, cache);
@@ -2262,6 +3803,26 @@ var getRelationWrapper = (meta, relationName, owner) => {
2262
3803
  };
2263
3804
  var instantiateWrapper = (meta, relationName, relation, owner) => {
2264
3805
  switch (relation.type) {
3806
+ case RelationKinds.HasOne: {
3807
+ const hasOne2 = relation;
3808
+ const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
3809
+ const loader = () => relationLoaderCache(
3810
+ meta,
3811
+ relationName,
3812
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
3813
+ );
3814
+ return new DefaultHasOneReference(
3815
+ meta.ctx,
3816
+ meta,
3817
+ owner,
3818
+ relationName,
3819
+ hasOne2,
3820
+ meta.table,
3821
+ loader,
3822
+ (row) => createEntityFromRow(meta.ctx, hasOne2.target, row),
3823
+ localKey
3824
+ );
3825
+ }
2265
3826
  case RelationKinds.HasMany: {
2266
3827
  const hasMany2 = relation;
2267
3828
  const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
@@ -2684,7 +4245,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
2684
4245
  * @returns Compiled query with SQL and parameters
2685
4246
  */
2686
4247
  compile(dialect) {
2687
- return dialect.compileSelect(this.context.state.ast);
4248
+ const resolved = resolveDialectInput(dialect);
4249
+ return resolved.compileSelect(this.context.state.ast);
2688
4250
  }
2689
4251
  /**
2690
4252
  * Converts the query to SQL string for a specific dialect
@@ -2735,6 +4297,15 @@ var buildRelationDefinitions = (meta, tableMap) => {
2735
4297
  const relations = {};
2736
4298
  for (const [name, relation] of Object.entries(meta.relations)) {
2737
4299
  switch (relation.kind) {
4300
+ case RelationKinds.HasOne: {
4301
+ relations[name] = hasOne(
4302
+ resolveTableTarget(relation.target, tableMap),
4303
+ relation.foreignKey,
4304
+ relation.localKey,
4305
+ relation.cascade
4306
+ );
4307
+ break;
4308
+ }
2738
4309
  case RelationKinds.HasMany: {
2739
4310
  relations[name] = hasMany(
2740
4311
  resolveTableTarget(relation.target, tableMap),
@@ -2805,6 +4376,7 @@ export {
2805
4376
  Column,
2806
4377
  Entity,
2807
4378
  HasMany,
4379
+ HasOne,
2808
4380
  PrimaryKey,
2809
4381
  bootstrapEntities,
2810
4382
  getTableDefFromEntity,