metal-orm 1.0.56 → 1.0.58

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 (82) hide show
  1. package/README.md +41 -33
  2. package/dist/index.cjs +1461 -195
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +541 -114
  5. package/dist/index.d.ts +541 -114
  6. package/dist/index.js +1424 -195
  7. package/dist/index.js.map +1 -1
  8. package/package.json +69 -69
  9. package/src/codegen/naming-strategy.ts +3 -1
  10. package/src/codegen/typescript.ts +20 -10
  11. package/src/core/ast/aggregate-functions.ts +14 -0
  12. package/src/core/ast/builders.ts +38 -20
  13. package/src/core/ast/expression-builders.ts +70 -2
  14. package/src/core/ast/expression-nodes.ts +305 -274
  15. package/src/core/ast/expression-visitor.ts +11 -1
  16. package/src/core/ast/expression.ts +4 -0
  17. package/src/core/ast/query.ts +3 -0
  18. package/src/core/ddl/introspect/catalogs/mysql.ts +5 -0
  19. package/src/core/ddl/introspect/catalogs/sqlite.ts +3 -0
  20. package/src/core/ddl/introspect/functions/mssql.ts +13 -0
  21. package/src/core/ddl/introspect/mssql.ts +4 -0
  22. package/src/core/ddl/introspect/mysql.ts +4 -0
  23. package/src/core/ddl/introspect/sqlite.ts +4 -0
  24. package/src/core/dialect/abstract.ts +552 -531
  25. package/src/core/dialect/base/function-table-formatter.ts +9 -30
  26. package/src/core/dialect/base/sql-dialect.ts +24 -0
  27. package/src/core/dialect/mssql/functions.ts +40 -2
  28. package/src/core/dialect/mysql/functions.ts +16 -2
  29. package/src/core/dialect/postgres/functions.ts +66 -2
  30. package/src/core/dialect/postgres/index.ts +17 -4
  31. package/src/core/dialect/postgres/table-functions.ts +27 -0
  32. package/src/core/dialect/sqlite/functions.ts +34 -0
  33. package/src/core/dialect/sqlite/index.ts +17 -1
  34. package/src/core/driver/database-driver.ts +9 -1
  35. package/src/core/driver/mssql-driver.ts +3 -0
  36. package/src/core/driver/mysql-driver.ts +3 -0
  37. package/src/core/driver/postgres-driver.ts +3 -0
  38. package/src/core/driver/sqlite-driver.ts +3 -0
  39. package/src/core/execution/executors/mssql-executor.ts +5 -0
  40. package/src/core/execution/executors/mysql-executor.ts +5 -0
  41. package/src/core/execution/executors/postgres-executor.ts +5 -0
  42. package/src/core/execution/executors/sqlite-executor.ts +5 -0
  43. package/src/core/functions/array.ts +26 -0
  44. package/src/core/functions/control-flow.ts +69 -0
  45. package/src/core/functions/datetime.ts +50 -0
  46. package/src/core/functions/definitions/aggregate.ts +16 -0
  47. package/src/core/functions/definitions/control-flow.ts +24 -0
  48. package/src/core/functions/definitions/datetime.ts +36 -0
  49. package/src/core/functions/definitions/helpers.ts +29 -0
  50. package/src/core/functions/definitions/json.ts +49 -0
  51. package/src/core/functions/definitions/numeric.ts +55 -0
  52. package/src/core/functions/definitions/string.ts +43 -0
  53. package/src/core/functions/function-registry.ts +48 -0
  54. package/src/core/functions/group-concat-helpers.ts +57 -0
  55. package/src/core/functions/json.ts +38 -0
  56. package/src/core/functions/numeric.ts +14 -0
  57. package/src/core/functions/standard-strategy.ts +86 -115
  58. package/src/core/functions/standard-table-strategy.ts +13 -0
  59. package/src/core/functions/table-types.ts +15 -0
  60. package/src/core/functions/text.ts +57 -0
  61. package/src/core/sql/sql.ts +59 -38
  62. package/src/decorators/bootstrap.ts +41 -4
  63. package/src/index.ts +18 -11
  64. package/src/orm/entity-meta.ts +6 -3
  65. package/src/orm/entity.ts +81 -14
  66. package/src/orm/execute.ts +87 -20
  67. package/src/orm/hydration-context.ts +10 -0
  68. package/src/orm/identity-map.ts +19 -0
  69. package/src/orm/interceptor-pipeline.ts +4 -0
  70. package/src/orm/lazy-batch.ts +237 -54
  71. package/src/orm/relations/belongs-to.ts +19 -2
  72. package/src/orm/relations/has-many.ts +23 -9
  73. package/src/orm/relations/has-one.ts +19 -2
  74. package/src/orm/relations/many-to-many.ts +59 -4
  75. package/src/orm/save-graph-types.ts +2 -2
  76. package/src/orm/save-graph.ts +18 -18
  77. package/src/query-builder/relation-conditions.ts +80 -59
  78. package/src/query-builder/relation-service.ts +399 -95
  79. package/src/query-builder/relation-types.ts +2 -2
  80. package/src/query-builder/select.ts +124 -106
  81. package/src/schema/table-guards.ts +6 -0
  82. package/src/schema/types.ts +109 -85
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn5, res) => function __init() {
4
- return fn5 && (res = (0, fn5[__getOwnPropNames(fn5)[0]])(fn5 = 0)), res;
3
+ var __esm = (fn8, res) => function __init() {
4
+ return fn8 && (res = (0, fn8[__getOwnPropNames(fn8)[0]])(fn8 = 0)), res;
5
5
  };
6
6
  var __export = (target, all) => {
7
7
  for (var name in all)
@@ -363,7 +363,9 @@ var operandTypes = /* @__PURE__ */ new Set([
363
363
  "CaseExpression",
364
364
  "Cast",
365
365
  "WindowFunction",
366
- "ArithmeticExpression"
366
+ "ArithmeticExpression",
367
+ "BitwiseExpression",
368
+ "Collate"
367
369
  ]);
368
370
  var hasTypeProperty = (value) => typeof value === "object" && value !== null && "type" in value;
369
371
  var isOperandNode = (node) => {
@@ -373,6 +375,7 @@ var isOperandNode = (node) => {
373
375
  var isFunctionNode = (node) => isOperandNode(node) && node.type === "Function";
374
376
  var isCaseExpressionNode = (node) => isOperandNode(node) && node.type === "CaseExpression";
375
377
  var isCastExpressionNode = (node) => isOperandNode(node) && node.type === "Cast";
378
+ var isCollateExpressionNode = (node) => isOperandNode(node) && node.type === "Collate";
376
379
  var isWindowFunctionNode = (node) => isOperandNode(node) && node.type === "WindowFunction";
377
380
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isCastExpressionNode(node) || isWindowFunctionNode(node);
378
381
 
@@ -497,6 +500,17 @@ var add = (left2, right2) => createArithmeticExpression("+", left2, right2);
497
500
  var sub = (left2, right2) => createArithmeticExpression("-", left2, right2);
498
501
  var mul = (left2, right2) => createArithmeticExpression("*", left2, right2);
499
502
  var div = (left2, right2) => createArithmeticExpression("/", left2, right2);
503
+ var createBitwiseExpression = (operator, left2, right2) => ({
504
+ type: "BitwiseExpression",
505
+ left: toOperand(left2),
506
+ operator,
507
+ right: toOperand(right2)
508
+ });
509
+ var bitAnd = (left2, right2) => createBitwiseExpression("&", left2, right2);
510
+ var bitOr = (left2, right2) => createBitwiseExpression("|", left2, right2);
511
+ var bitXor = (left2, right2) => createBitwiseExpression("^", left2, right2);
512
+ var shiftLeft = (left2, right2) => createBitwiseExpression("<<", left2, right2);
513
+ var shiftRight = (left2, right2) => createBitwiseExpression(">>", left2, right2);
500
514
  var jsonPath = (col2, path) => ({
501
515
  type: "JsonPath",
502
516
  column: columnOperand(col2),
@@ -525,6 +539,11 @@ var notExists = (subquery) => ({
525
539
  operator: "NOT EXISTS",
526
540
  subquery
527
541
  });
542
+ var collate = (expression, collation) => ({
543
+ type: "Collate",
544
+ expression: toOperand(expression),
545
+ collation
546
+ });
528
547
 
529
548
  // src/core/ast/window-functions.ts
530
549
  var buildWindowFunction = (name, args = [], partitionBy, orderBy) => {
@@ -670,6 +689,8 @@ var groupConcat = (col2, options) => ({
670
689
  orderBy: options?.orderBy?.map(toOrderByNode),
671
690
  separator: options?.separator !== void 0 ? valueToOperand(options.separator) : void 0
672
691
  });
692
+ var stddev = buildAggregate("STDDEV");
693
+ var variance = buildAggregate("VARIANCE");
673
694
 
674
695
  // src/core/ast/expression-visitor.ts
675
696
  var DispatcherRegistry = class _DispatcherRegistry {
@@ -743,6 +764,9 @@ var visitExpression = (node, visitor) => {
743
764
  case "ArithmeticExpression":
744
765
  if (visitor.visitArithmeticExpression) return visitor.visitArithmeticExpression(node);
745
766
  break;
767
+ case "BitwiseExpression":
768
+ if (visitor.visitBitwiseExpression) return visitor.visitBitwiseExpression(node);
769
+ break;
746
770
  default:
747
771
  break;
748
772
  }
@@ -780,6 +804,9 @@ var visitOperand = (node, visitor) => {
780
804
  case "Cast":
781
805
  if (visitor.visitCast) return visitor.visitCast(node);
782
806
  break;
807
+ case "Collate":
808
+ if (visitor.visitCollate) return visitor.visitCollate(node);
809
+ break;
783
810
  default:
784
811
  break;
785
812
  }
@@ -850,62 +877,337 @@ var derivedTable = (query, alias, columnAliases) => ({
850
877
  columnAliases
851
878
  });
852
879
 
880
+ // src/core/functions/function-registry.ts
881
+ var FunctionRegistry = class {
882
+ constructor() {
883
+ this.renderers = /* @__PURE__ */ new Map();
884
+ }
885
+ /**
886
+ * Registers or overrides a renderer for the given function name.
887
+ */
888
+ add(name, renderer) {
889
+ this.renderers.set(name, renderer);
890
+ }
891
+ /**
892
+ * Registers a batch of definitions.
893
+ */
894
+ register(definitions) {
895
+ for (const definition of definitions) {
896
+ this.add(definition.name, definition.renderer);
897
+ }
898
+ }
899
+ /**
900
+ * Merges another registry into this one, allowing overrides from the other source.
901
+ */
902
+ merge(other) {
903
+ for (const [name, renderer] of other.renderers.entries()) {
904
+ this.renderers.set(name, renderer);
905
+ }
906
+ }
907
+ /**
908
+ * Retrieves a renderer by function name.
909
+ */
910
+ get(name) {
911
+ return this.renderers.get(name);
912
+ }
913
+ };
914
+
915
+ // src/core/functions/definitions/helpers.ts
916
+ function unaryRenderer(name) {
917
+ return ({ compiledArgs }) => `${name}(${compiledArgs[0]})`;
918
+ }
919
+ function binaryRenderer(name) {
920
+ return ({ compiledArgs }) => `${name}(${compiledArgs[0]}, ${compiledArgs[1]})`;
921
+ }
922
+ function variadicRenderer(name) {
923
+ return ({ compiledArgs }) => `${name}(${compiledArgs.join(", ")})`;
924
+ }
925
+ function noArgsRenderer(name) {
926
+ return () => `${name}()`;
927
+ }
928
+
929
+ // src/core/functions/definitions/aggregate.ts
930
+ var aggregateFunctionDefinitions = [
931
+ {
932
+ name: "COUNT",
933
+ renderer: ({ compiledArgs }) => compiledArgs.length ? `COUNT(${compiledArgs.join(", ")})` : "COUNT(*)"
934
+ },
935
+ { name: "SUM", renderer: unaryRenderer("SUM") },
936
+ { name: "AVG", renderer: unaryRenderer("AVG") },
937
+ { name: "MIN", renderer: unaryRenderer("MIN") },
938
+ { name: "MAX", renderer: unaryRenderer("MAX") },
939
+ { name: "STDDEV", renderer: unaryRenderer("STDDEV") },
940
+ { name: "VARIANCE", renderer: unaryRenderer("VARIANCE") }
941
+ ];
942
+
943
+ // src/core/functions/definitions/string.ts
944
+ var stringFunctionDefinitions = [
945
+ { name: "UPPER", renderer: unaryRenderer("UPPER") },
946
+ { name: "LOWER", renderer: unaryRenderer("LOWER") },
947
+ { name: "LENGTH", renderer: unaryRenderer("LENGTH") },
948
+ { name: "CHAR_LENGTH", renderer: unaryRenderer("CHAR_LENGTH") },
949
+ { name: "CHARACTER_LENGTH", renderer: unaryRenderer("CHARACTER_LENGTH") },
950
+ { name: "TRIM", renderer: unaryRenderer("TRIM") },
951
+ { name: "LTRIM", renderer: unaryRenderer("LTRIM") },
952
+ { name: "RTRIM", renderer: unaryRenderer("RTRIM") },
953
+ { name: "SUBSTRING", renderer: variadicRenderer("SUBSTRING") },
954
+ { name: "SUBSTR", renderer: variadicRenderer("SUBSTR") },
955
+ { name: "CONCAT", renderer: variadicRenderer("CONCAT") },
956
+ { name: "CONCAT_WS", renderer: variadicRenderer("CONCAT_WS") },
957
+ { name: "ASCII", renderer: unaryRenderer("ASCII") },
958
+ { name: "CHAR", renderer: variadicRenderer("CHAR") },
959
+ {
960
+ name: "POSITION",
961
+ renderer: ({ compiledArgs }) => `POSITION(${compiledArgs[0]} IN ${compiledArgs[1]})`
962
+ },
963
+ { name: "REPLACE", renderer: ({ compiledArgs }) => `REPLACE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
964
+ { name: "REPEAT", renderer: binaryRenderer("REPEAT") },
965
+ { name: "LPAD", renderer: ({ compiledArgs }) => `LPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
966
+ { name: "RPAD", renderer: ({ compiledArgs }) => `RPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
967
+ { name: "LEFT", renderer: binaryRenderer("LEFT") },
968
+ { name: "RIGHT", renderer: binaryRenderer("RIGHT") },
969
+ { name: "INSTR", renderer: binaryRenderer("INSTR") },
970
+ {
971
+ name: "LOCATE",
972
+ renderer: ({ compiledArgs }) => compiledArgs.length === 3 ? `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` : `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
973
+ },
974
+ { name: "SPACE", renderer: unaryRenderer("SPACE") },
975
+ { name: "REVERSE", renderer: unaryRenderer("REVERSE") },
976
+ { name: "INITCAP", renderer: unaryRenderer("INITCAP") },
977
+ { name: "MD5", renderer: unaryRenderer("MD5") },
978
+ { name: "SHA1", renderer: unaryRenderer("SHA1") },
979
+ { name: "SHA2", renderer: ({ compiledArgs }) => `SHA2(${compiledArgs[0]}, ${compiledArgs[1]})` }
980
+ ];
981
+
982
+ // src/core/functions/definitions/datetime.ts
983
+ var dateTimeFunctionDefinitions = [
984
+ { name: "NOW", renderer: noArgsRenderer("NOW") },
985
+ { name: "CURRENT_DATE", renderer: () => "CURRENT_DATE" },
986
+ { name: "CURRENT_TIME", renderer: () => "CURRENT_TIME" },
987
+ {
988
+ name: "EXTRACT",
989
+ renderer: ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`
990
+ },
991
+ { name: "YEAR", renderer: ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})` },
992
+ { name: "MONTH", renderer: ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})` },
993
+ { name: "DAY", renderer: ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})` },
994
+ { name: "HOUR", renderer: ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})` },
995
+ { name: "MINUTE", renderer: ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})` },
996
+ { name: "SECOND", renderer: ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})` },
997
+ { name: "QUARTER", renderer: ({ compiledArgs }) => `EXTRACT(QUARTER FROM ${compiledArgs[0]})` },
998
+ { name: "DATE_ADD", renderer: ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
999
+ { name: "DATE_SUB", renderer: ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
1000
+ { name: "DATE_DIFF", renderer: ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})` },
1001
+ { name: "DATE_FORMAT", renderer: ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})` },
1002
+ { name: "UNIX_TIMESTAMP", renderer: noArgsRenderer("UNIX_TIMESTAMP") },
1003
+ { name: "FROM_UNIXTIME", renderer: ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})` },
1004
+ { name: "END_OF_MONTH", renderer: ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})` },
1005
+ { name: "DAY_OF_WEEK", renderer: ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})` },
1006
+ { name: "WEEK_OF_YEAR", renderer: ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})` },
1007
+ { name: "DATE_TRUNC", renderer: ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` },
1008
+ {
1009
+ name: "AGE",
1010
+ renderer: ({ compiledArgs }) => compiledArgs.length === 1 ? `AGE(${compiledArgs[0]})` : `AGE(${compiledArgs[0]}, ${compiledArgs[1]})`
1011
+ },
1012
+ { name: "LOCALTIME", renderer: () => "LOCALTIME" },
1013
+ { name: "LOCALTIMESTAMP", renderer: () => "LOCALTIMESTAMP" }
1014
+ ];
1015
+
1016
+ // src/core/functions/definitions/numeric.ts
1017
+ var numericFunctionDefinitions = [
1018
+ { name: "ABS", renderer: unaryRenderer("ABS") },
1019
+ { name: "BIT_LENGTH", renderer: unaryRenderer("BIT_LENGTH") },
1020
+ { name: "OCTET_LENGTH", renderer: unaryRenderer("OCTET_LENGTH") },
1021
+ { name: "CHR", renderer: unaryRenderer("CHR") },
1022
+ { name: "LOG2", renderer: unaryRenderer("LOG2") },
1023
+ { name: "CBRT", renderer: unaryRenderer("CBRT") },
1024
+ { name: "ACOS", renderer: unaryRenderer("ACOS") },
1025
+ { name: "ASIN", renderer: unaryRenderer("ASIN") },
1026
+ { name: "ATAN", renderer: unaryRenderer("ATAN") },
1027
+ { name: "ATAN2", renderer: binaryRenderer("ATAN2") },
1028
+ { name: "CEIL", renderer: unaryRenderer("CEIL") },
1029
+ { name: "CEILING", renderer: unaryRenderer("CEILING") },
1030
+ { name: "COS", renderer: unaryRenderer("COS") },
1031
+ { name: "COT", renderer: unaryRenderer("COT") },
1032
+ { name: "DEGREES", renderer: unaryRenderer("DEGREES") },
1033
+ { name: "EXP", renderer: unaryRenderer("EXP") },
1034
+ { name: "FLOOR", renderer: unaryRenderer("FLOOR") },
1035
+ { name: "LN", renderer: unaryRenderer("LN") },
1036
+ {
1037
+ name: "LOG",
1038
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `LOG(${compiledArgs[0]}, ${compiledArgs[1]})` : `LOG(${compiledArgs[0]})`
1039
+ },
1040
+ { name: "LOG10", renderer: unaryRenderer("LOG10") },
1041
+ { name: "LOG_BASE", renderer: binaryRenderer("LOG") },
1042
+ { name: "MOD", renderer: binaryRenderer("MOD") },
1043
+ { name: "PI", renderer: noArgsRenderer("PI") },
1044
+ { name: "POWER", renderer: binaryRenderer("POWER") },
1045
+ { name: "POW", renderer: binaryRenderer("POW") },
1046
+ { name: "RADIANS", renderer: unaryRenderer("RADIANS") },
1047
+ { name: "RANDOM", renderer: noArgsRenderer("RANDOM") },
1048
+ { name: "RAND", renderer: noArgsRenderer("RAND") },
1049
+ {
1050
+ name: "ROUND",
1051
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `ROUND(${compiledArgs[0]}, ${compiledArgs[1]})` : `ROUND(${compiledArgs[0]})`
1052
+ },
1053
+ { name: "SIGN", renderer: unaryRenderer("SIGN") },
1054
+ { name: "SIN", renderer: unaryRenderer("SIN") },
1055
+ { name: "SQRT", renderer: unaryRenderer("SQRT") },
1056
+ { name: "TAN", renderer: unaryRenderer("TAN") },
1057
+ {
1058
+ name: "TRUNC",
1059
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` : `TRUNC(${compiledArgs[0]})`
1060
+ },
1061
+ {
1062
+ name: "TRUNCATE",
1063
+ renderer: ({ compiledArgs }) => `TRUNCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
1064
+ }
1065
+ ];
1066
+
1067
+ // src/core/functions/definitions/control-flow.ts
1068
+ var controlFlowFunctionDefinitions = [
1069
+ {
1070
+ name: "COALESCE",
1071
+ renderer: ({ compiledArgs }) => `COALESCE(${compiledArgs.join(", ")})`
1072
+ },
1073
+ {
1074
+ name: "NULLIF",
1075
+ renderer: ({ compiledArgs }) => `NULLIF(${compiledArgs[0]}, ${compiledArgs[1]})`
1076
+ },
1077
+ {
1078
+ name: "GREATEST",
1079
+ renderer: ({ compiledArgs }) => `GREATEST(${compiledArgs.join(", ")})`
1080
+ },
1081
+ {
1082
+ name: "LEAST",
1083
+ renderer: ({ compiledArgs }) => `LEAST(${compiledArgs.join(", ")})`
1084
+ },
1085
+ {
1086
+ name: "IFNULL",
1087
+ renderer: ({ compiledArgs }) => `IFNULL(${compiledArgs[0]}, ${compiledArgs[1]})`
1088
+ }
1089
+ ];
1090
+
1091
+ // src/core/functions/definitions/json.ts
1092
+ var jsonFunctionDefinitions = [
1093
+ {
1094
+ name: "JSON_LENGTH",
1095
+ renderer: ({ compiledArgs }) => {
1096
+ if (compiledArgs.length === 0 || compiledArgs.length > 2) {
1097
+ throw new Error("JSON_LENGTH expects 1 or 2 arguments");
1098
+ }
1099
+ return `JSON_LENGTH(${compiledArgs.join(", ")})`;
1100
+ }
1101
+ },
1102
+ {
1103
+ name: "JSON_SET",
1104
+ renderer: ({ compiledArgs }) => {
1105
+ if (compiledArgs.length < 3 || (compiledArgs.length - 1) % 2 !== 0) {
1106
+ throw new Error("JSON_SET expects a JSON document followed by one or more path/value pairs");
1107
+ }
1108
+ return `JSON_SET(${compiledArgs.join(", ")})`;
1109
+ }
1110
+ },
1111
+ {
1112
+ name: "JSON_ARRAYAGG",
1113
+ renderer: ({ compiledArgs }) => {
1114
+ if (compiledArgs.length !== 1) {
1115
+ throw new Error("JSON_ARRAYAGG expects exactly one argument");
1116
+ }
1117
+ return `JSON_ARRAYAGG(${compiledArgs[0]})`;
1118
+ }
1119
+ },
1120
+ {
1121
+ name: "JSON_CONTAINS",
1122
+ renderer: ({ compiledArgs }) => {
1123
+ if (compiledArgs.length < 2 || compiledArgs.length > 3) {
1124
+ throw new Error("JSON_CONTAINS expects two or three arguments");
1125
+ }
1126
+ return `JSON_CONTAINS(${compiledArgs.join(", ")})`;
1127
+ }
1128
+ },
1129
+ {
1130
+ name: "ARRAY_APPEND",
1131
+ renderer: ({ compiledArgs }) => {
1132
+ if (compiledArgs.length !== 2) {
1133
+ throw new Error("ARRAY_APPEND expects exactly two arguments");
1134
+ }
1135
+ return `ARRAY_APPEND(${compiledArgs[0]}, ${compiledArgs[1]})`;
1136
+ }
1137
+ }
1138
+ ];
1139
+
1140
+ // src/core/functions/group-concat-helpers.ts
1141
+ var DEFAULT_GROUP_CONCAT_SEPARATOR = {
1142
+ type: "Literal",
1143
+ value: ","
1144
+ };
1145
+ function buildGroupConcatOrderBy(ctx) {
1146
+ const orderBy = ctx.node.orderBy;
1147
+ if (!orderBy || orderBy.length === 0) {
1148
+ return "";
1149
+ }
1150
+ const parts = orderBy.map((order) => {
1151
+ const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
1152
+ throw new Error("ORDER BY expressions inside functions must be operands");
1153
+ })();
1154
+ const collation = order.collation ? ` COLLATE ${order.collation}` : "";
1155
+ const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
1156
+ return `${term} ${order.direction}${collation}${nulls}`;
1157
+ });
1158
+ return `ORDER BY ${parts.join(", ")}`;
1159
+ }
1160
+ function formatGroupConcatSeparator(ctx) {
1161
+ if (!ctx.node.separator) {
1162
+ return "";
1163
+ }
1164
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
1165
+ }
1166
+ function getGroupConcatSeparatorOperand(ctx) {
1167
+ return ctx.node.separator ?? DEFAULT_GROUP_CONCAT_SEPARATOR;
1168
+ }
1169
+ function renderStandardGroupConcat(ctx) {
1170
+ const arg = ctx.compiledArgs[0];
1171
+ const orderClause = buildGroupConcatOrderBy(ctx);
1172
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1173
+ const separatorClause = formatGroupConcatSeparator(ctx);
1174
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
1175
+ }
1176
+
853
1177
  // src/core/functions/standard-strategy.ts
854
- var StandardFunctionStrategy = class _StandardFunctionStrategy {
1178
+ var StandardFunctionStrategy = class {
855
1179
  /**
856
1180
  * Creates a new StandardFunctionStrategy and registers standard functions.
857
1181
  */
858
- constructor() {
859
- this.renderers = /* @__PURE__ */ new Map();
1182
+ constructor(registry2) {
1183
+ this.registry = registry2 ?? new FunctionRegistry();
860
1184
  this.registerStandard();
861
1185
  }
862
1186
  registerStandard() {
863
- this.add("COUNT", ({ compiledArgs }) => compiledArgs.length ? `COUNT(${compiledArgs.join(", ")})` : "COUNT(*)");
864
- this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
865
- this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
866
- this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
867
- this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
868
- this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
869
- this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
870
- this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
871
- this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
872
- this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
873
- this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
874
- this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
875
- this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
876
- this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
877
- this.add("NOW", () => `NOW()`);
878
- this.add("CURRENT_DATE", () => `CURRENT_DATE`);
879
- this.add("CURRENT_TIME", () => `CURRENT_TIME`);
880
- this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
881
- this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
882
- this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
883
- this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
884
- this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
885
- this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
886
- this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
887
- this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
888
- this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
889
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
890
- this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
891
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
892
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
893
- this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
1187
+ this.registerDefinitions(aggregateFunctionDefinitions);
1188
+ this.registerDefinitions(stringFunctionDefinitions);
1189
+ this.registerDefinitions(dateTimeFunctionDefinitions);
1190
+ this.registerDefinitions(numericFunctionDefinitions);
1191
+ this.registerDefinitions(controlFlowFunctionDefinitions);
1192
+ this.registerDefinitions(jsonFunctionDefinitions);
894
1193
  this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
895
1194
  }
1195
+ registerDefinitions(definitions) {
1196
+ this.registry.register(definitions);
1197
+ }
896
1198
  /**
897
1199
  * Registers a renderer for a function name.
898
1200
  * @param name - The function name.
899
1201
  * @param renderer - The renderer function.
900
1202
  */
901
1203
  add(name, renderer) {
902
- this.renderers.set(name, renderer);
1204
+ this.registry.add(name, renderer);
903
1205
  }
904
1206
  /**
905
1207
  * @inheritDoc
906
1208
  */
907
1209
  getRenderer(name) {
908
- return this.renderers.get(name);
1210
+ return this.registry.get(name);
909
1211
  }
910
1212
  /**
911
1213
  * Renders the GROUP_CONCAT function with optional ORDER BY and SEPARATOR.
@@ -913,11 +1215,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
913
1215
  * @returns The rendered SQL string.
914
1216
  */
915
1217
  renderGroupConcat(ctx) {
916
- const arg = ctx.compiledArgs[0];
917
- const orderClause = this.buildOrderByExpression(ctx);
918
- const orderSegment = orderClause ? ` ${orderClause}` : "";
919
- const separatorClause = this.formatGroupConcatSeparator(ctx);
920
- return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
1218
+ return renderStandardGroupConcat(ctx);
921
1219
  }
922
1220
  /**
923
1221
  * Builds the ORDER BY clause for functions like GROUP_CONCAT.
@@ -925,19 +1223,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
925
1223
  * @returns The ORDER BY SQL clause or empty string.
926
1224
  */
927
1225
  buildOrderByExpression(ctx) {
928
- const orderBy = ctx.node.orderBy;
929
- if (!orderBy || orderBy.length === 0) {
930
- return "";
931
- }
932
- const parts = orderBy.map((order) => {
933
- const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
934
- throw new Error("ORDER BY expressions inside functions must be operands");
935
- })();
936
- const collation = order.collation ? ` COLLATE ${order.collation}` : "";
937
- const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
938
- return `${term} ${order.direction}${collation}${nulls}`;
939
- });
940
- return `ORDER BY ${parts.join(", ")}`;
1226
+ return buildGroupConcatOrderBy(ctx);
941
1227
  }
942
1228
  /**
943
1229
  * Formats the SEPARATOR clause for GROUP_CONCAT.
@@ -945,10 +1231,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
945
1231
  * @returns The SEPARATOR SQL clause or empty string.
946
1232
  */
947
1233
  formatGroupConcatSeparator(ctx) {
948
- if (!ctx.node.separator) {
949
- return "";
950
- }
951
- return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
1234
+ return formatGroupConcatSeparator(ctx);
952
1235
  }
953
1236
  /**
954
1237
  * Gets the separator operand for GROUP_CONCAT, defaulting to comma.
@@ -956,14 +1239,24 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
956
1239
  * @returns The separator operand.
957
1240
  */
958
1241
  getGroupConcatSeparatorOperand(ctx) {
959
- return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
1242
+ return getGroupConcatSeparatorOperand(ctx);
960
1243
  }
961
1244
  static {
962
1245
  /** Default separator for GROUP_CONCAT, a comma. */
963
- this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
964
- type: "Literal",
965
- value: ","
966
- };
1246
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
1247
+ }
1248
+ };
1249
+
1250
+ // src/core/functions/standard-table-strategy.ts
1251
+ var StandardTableFunctionStrategy = class {
1252
+ constructor() {
1253
+ this.renderers = /* @__PURE__ */ new Map();
1254
+ }
1255
+ add(key, renderer) {
1256
+ this.renderers.set(key, renderer);
1257
+ }
1258
+ getRenderer(key) {
1259
+ return this.renderers.get(key);
967
1260
  }
968
1261
  };
969
1262
 
@@ -1137,10 +1430,11 @@ var Dialect = class _Dialect {
1137
1430
  const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
1138
1431
  return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
1139
1432
  }
1140
- constructor(functionStrategy) {
1433
+ constructor(functionStrategy, tableFunctionStrategy) {
1141
1434
  this.expressionCompilers = /* @__PURE__ */ new Map();
1142
1435
  this.operandCompilers = /* @__PURE__ */ new Map();
1143
1436
  this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
1437
+ this.tableFunctionStrategy = tableFunctionStrategy || new StandardTableFunctionStrategy();
1144
1438
  this.registerDefaultOperandCompilers();
1145
1439
  this.registerDefaultExpressionCompilers();
1146
1440
  }
@@ -1149,7 +1443,7 @@ var Dialect = class _Dialect {
1149
1443
  * @param functionStrategy - Optional function strategy
1150
1444
  * @returns New Dialect instance
1151
1445
  */
1152
- static create(functionStrategy) {
1446
+ static create(functionStrategy, tableFunctionStrategy) {
1153
1447
  class TestDialect extends _Dialect {
1154
1448
  constructor() {
1155
1449
  super(...arguments);
@@ -1171,7 +1465,7 @@ var Dialect = class _Dialect {
1171
1465
  throw new Error("Not implemented");
1172
1466
  }
1173
1467
  }
1174
- return new TestDialect(functionStrategy);
1468
+ return new TestDialect(functionStrategy, tableFunctionStrategy);
1175
1469
  }
1176
1470
  /**
1177
1471
  * Registers an expression compiler for a specific node type
@@ -1272,6 +1566,11 @@ var Dialect = class _Dialect {
1272
1566
  const right2 = this.compileOperand(arith.right, ctx);
1273
1567
  return `${left2} ${arith.operator} ${right2}`;
1274
1568
  });
1569
+ this.registerExpressionCompiler("BitwiseExpression", (bitwise, ctx) => {
1570
+ const left2 = this.compileOperand(bitwise.left, ctx);
1571
+ const right2 = this.compileOperand(bitwise.right, ctx);
1572
+ return `${left2} ${bitwise.operator} ${right2}`;
1573
+ });
1275
1574
  }
1276
1575
  registerDefaultOperandCompilers() {
1277
1576
  this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
@@ -1341,6 +1640,15 @@ var Dialect = class _Dialect {
1341
1640
  const right2 = this.compileOperand(node.right, ctx);
1342
1641
  return `(${left2} ${node.operator} ${right2})`;
1343
1642
  });
1643
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
1644
+ const left2 = this.compileOperand(node.left, ctx);
1645
+ const right2 = this.compileOperand(node.right, ctx);
1646
+ return `(${left2} ${node.operator} ${right2})`;
1647
+ });
1648
+ this.registerOperandCompiler("Collate", (node, ctx) => {
1649
+ const expr = this.compileOperand(node.expression, ctx);
1650
+ return `${expr} COLLATE ${node.collation}`;
1651
+ });
1344
1652
  }
1345
1653
  // Default fallback, should be overridden by dialects if supported
1346
1654
  compileJsonPath(_node) {
@@ -1373,13 +1681,13 @@ var FunctionTableFormatter = class {
1373
1681
  * @param dialect - The dialect instance for compiling operands.
1374
1682
  * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
1375
1683
  */
1376
- static format(fn5, ctx, dialect) {
1377
- const schemaPart = this.formatSchema(fn5, dialect);
1378
- const args = this.formatArgs(fn5, ctx, dialect);
1379
- const base = this.formatBase(fn5, schemaPart, args, dialect);
1380
- const lateral = this.formatLateral(fn5);
1381
- const alias = this.formatAlias(fn5, dialect);
1382
- const colAliases = this.formatColumnAliases(fn5, dialect);
1684
+ static format(fn8, ctx, dialect) {
1685
+ const schemaPart = this.formatSchema(fn8, dialect);
1686
+ const args = this.formatArgs(fn8, ctx, dialect);
1687
+ const base = this.formatBase(fn8, schemaPart, args);
1688
+ const lateral = this.formatLateral(fn8);
1689
+ const alias = this.formatAlias(fn8, dialect);
1690
+ const colAliases = this.formatColumnAliases(fn8, dialect);
1383
1691
  return `${lateral}${base}${alias}${colAliases}`;
1384
1692
  }
1385
1693
  /**
@@ -1389,9 +1697,9 @@ var FunctionTableFormatter = class {
1389
1697
  * @returns Schema prefix (e.g., "schema.") or empty string.
1390
1698
  * @internal
1391
1699
  */
1392
- static formatSchema(fn5, dialect) {
1393
- if (!fn5.schema) return "";
1394
- const quoted = dialect ? dialect.quoteIdentifier(fn5.schema) : fn5.schema;
1700
+ static formatSchema(fn8, dialect) {
1701
+ if (!fn8.schema) return "";
1702
+ const quoted = dialect ? dialect.quoteIdentifier(fn8.schema) : fn8.schema;
1395
1703
  return `${quoted}.`;
1396
1704
  }
1397
1705
  /**
@@ -1402,8 +1710,8 @@ var FunctionTableFormatter = class {
1402
1710
  * @returns Comma-separated function arguments.
1403
1711
  * @internal
1404
1712
  */
1405
- static formatArgs(fn5, ctx, dialect) {
1406
- return (fn5.args || []).map((a) => {
1713
+ static formatArgs(fn8, ctx, dialect) {
1714
+ return (fn8.args || []).map((a) => {
1407
1715
  if (ctx && dialect) {
1408
1716
  return dialect.compileOperand(a, ctx);
1409
1717
  }
@@ -1419,10 +1727,9 @@ var FunctionTableFormatter = class {
1419
1727
  * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
1420
1728
  * @internal
1421
1729
  */
1422
- static formatBase(fn5, schemaPart, args, dialect) {
1423
- const ordinality = fn5.withOrdinality ? " WITH ORDINALITY" : "";
1424
- const quoted = dialect ? dialect.quoteIdentifier(fn5.name) : fn5.name;
1425
- return `${schemaPart}${quoted}(${args})${ordinality}`;
1730
+ static formatBase(fn8, schemaPart, args) {
1731
+ const ordinality = fn8.withOrdinality ? " WITH ORDINALITY" : "";
1732
+ return `${schemaPart}${fn8.name}(${args})${ordinality}`;
1426
1733
  }
1427
1734
  /**
1428
1735
  * Formats the LATERAL keyword if present.
@@ -1430,8 +1737,8 @@ var FunctionTableFormatter = class {
1430
1737
  * @returns "LATERAL " or empty string.
1431
1738
  * @internal
1432
1739
  */
1433
- static formatLateral(fn5) {
1434
- return fn5.lateral ? "LATERAL " : "";
1740
+ static formatLateral(fn8) {
1741
+ return fn8.lateral ? "LATERAL " : "";
1435
1742
  }
1436
1743
  /**
1437
1744
  * Formats the table alias for the function table.
@@ -1440,9 +1747,9 @@ var FunctionTableFormatter = class {
1440
1747
  * @returns " AS alias" or empty string.
1441
1748
  * @internal
1442
1749
  */
1443
- static formatAlias(fn5, dialect) {
1444
- if (!fn5.alias) return "";
1445
- const quoted = dialect ? dialect.quoteIdentifier(fn5.alias) : fn5.alias;
1750
+ static formatAlias(fn8, dialect) {
1751
+ if (!fn8.alias) return "";
1752
+ const quoted = dialect ? dialect.quoteIdentifier(fn8.alias) : fn8.alias;
1446
1753
  return ` AS ${quoted}`;
1447
1754
  }
1448
1755
  /**
@@ -1452,9 +1759,9 @@ var FunctionTableFormatter = class {
1452
1759
  * @returns "(col1, col2, ...)" or empty string.
1453
1760
  * @internal
1454
1761
  */
1455
- static formatColumnAliases(fn5, dialect) {
1456
- if (!fn5.columnAliases || !fn5.columnAliases.length) return "";
1457
- const aliases = fn5.columnAliases.map((col2) => dialect ? dialect.quoteIdentifier(col2) : col2).join(", ");
1762
+ static formatColumnAliases(fn8, dialect) {
1763
+ if (!fn8.columnAliases || !fn8.columnAliases.length) return "";
1764
+ const aliases = fn8.columnAliases.map((col2) => dialect ? dialect.quoteIdentifier(col2) : col2).join(", ");
1458
1765
  return `(${aliases})`;
1459
1766
  }
1460
1767
  };
@@ -1722,8 +2029,24 @@ var SqlDialectBase = class extends Dialect {
1722
2029
  }
1723
2030
  return this.compileTableSource(tableSource);
1724
2031
  }
1725
- compileFunctionTable(fn5, ctx) {
1726
- return FunctionTableFormatter.format(fn5, ctx, this);
2032
+ compileFunctionTable(fn8, ctx) {
2033
+ const key = fn8.key ?? fn8.name;
2034
+ if (ctx) {
2035
+ const renderer = this.tableFunctionStrategy.getRenderer(key);
2036
+ if (renderer) {
2037
+ const compiledArgs = (fn8.args ?? []).map((arg) => this.compileOperand(arg, ctx));
2038
+ return renderer({
2039
+ node: fn8,
2040
+ compiledArgs,
2041
+ compileOperand: (operand) => this.compileOperand(operand, ctx),
2042
+ quoteIdentifier: this.quoteIdentifier.bind(this)
2043
+ });
2044
+ }
2045
+ if (fn8.key) {
2046
+ throw new Error(`Table function "${key}" is not supported by dialect "${this.dialect}".`);
2047
+ }
2048
+ }
2049
+ return FunctionTableFormatter.format(fn8, ctx, this);
1727
2050
  }
1728
2051
  compileDerivedTable(table, ctx) {
1729
2052
  if (!table.alias) {
@@ -1882,6 +2205,76 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1882
2205
  const separator = ctx.compileOperand(separatorOperand);
1883
2206
  return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1884
2207
  });
2208
+ this.add("CHR", ({ compiledArgs }) => `CHR(${compiledArgs[0]})`);
2209
+ this.add("HOUR", ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})`);
2210
+ this.add("MINUTE", ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})`);
2211
+ this.add("SECOND", ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})`);
2212
+ this.add("QUARTER", ({ compiledArgs }) => `EXTRACT(QUARTER FROM ${compiledArgs[0]})`);
2213
+ this.add("JSON_LENGTH", ({ compiledArgs }) => {
2214
+ if (compiledArgs.length !== 1) throw new Error("JSON_LENGTH expects 1 argument on PostgreSQL");
2215
+ return `jsonb_array_length(${compiledArgs[0]})`;
2216
+ });
2217
+ this.add("JSON_ARRAYAGG", ({ compiledArgs }) => {
2218
+ if (compiledArgs.length !== 1) throw new Error("JSON_ARRAYAGG expects 1 argument on PostgreSQL");
2219
+ return `jsonb_agg(${compiledArgs[0]})`;
2220
+ });
2221
+ this.add("JSON_CONTAINS", ({ compiledArgs }) => {
2222
+ if (compiledArgs.length !== 2) throw new Error("JSON_CONTAINS expects 2 arguments on PostgreSQL");
2223
+ return `(${compiledArgs[0]}::jsonb @> ${compiledArgs[1]}::jsonb)`;
2224
+ });
2225
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2226
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments on PostgreSQL");
2227
+ return `array_append(${compiledArgs[0]}, ${compiledArgs[1]})`;
2228
+ });
2229
+ this.add("JSON_SET", ({ node, compiledArgs }) => {
2230
+ if (compiledArgs.length !== 3) throw new Error("JSON_SET expects exactly 3 arguments on PostgreSQL");
2231
+ const pathNode = node.args[1];
2232
+ if (pathNode.type !== "Literal") {
2233
+ throw new Error("PostgreSQL JSON_SET currently supports literal paths only");
2234
+ }
2235
+ const pathArray = this.formatJsonbPathArray(pathNode);
2236
+ return `jsonb_set(${compiledArgs[0]}, ${pathArray}, ${compiledArgs[2]}::jsonb, true)`;
2237
+ });
2238
+ }
2239
+ formatJsonbPathArray(pathNode) {
2240
+ const rawPath = String(pathNode.value ?? "");
2241
+ if (!rawPath.startsWith("$")) {
2242
+ throw new Error('PostgreSQL JSON_SET paths must start with "$"');
2243
+ }
2244
+ const trimmed = rawPath === "$" ? "" : rawPath.startsWith("$.") ? rawPath.slice(2) : rawPath.slice(1);
2245
+ if (!trimmed) {
2246
+ throw new Error("PostgreSQL JSON_SET requires a non-root path");
2247
+ }
2248
+ if (trimmed.includes("[") || trimmed.includes("]")) {
2249
+ throw new Error("PostgreSQL JSON_SET currently only supports simple dot-separated paths");
2250
+ }
2251
+ const segments = trimmed.split(".").map((segment) => segment.replace(/^['"]?/, "").replace(/['"]?$/, "").trim()).filter(Boolean);
2252
+ if (!segments.length) {
2253
+ throw new Error("PostgreSQL JSON_SET requires at least one path segment");
2254
+ }
2255
+ const escapedSegments = segments.map((segment) => `'${segment.replace(/'/g, "''")}'`);
2256
+ return `ARRAY[${escapedSegments.join(", ")}]`;
2257
+ }
2258
+ };
2259
+
2260
+ // src/core/dialect/postgres/table-functions.ts
2261
+ var PostgresTableFunctionStrategy = class extends StandardTableFunctionStrategy {
2262
+ constructor() {
2263
+ super();
2264
+ this.registerOverrides();
2265
+ }
2266
+ registerOverrides() {
2267
+ this.add("ARRAY_UNNEST", ({ node, compiledArgs, quoteIdentifier }) => {
2268
+ const lateral = node.lateral ?? true;
2269
+ const withOrd = node.withOrdinality ?? false;
2270
+ const base = `unnest(${compiledArgs.join(", ")})${withOrd ? " WITH ORDINALITY" : ""}`;
2271
+ if (node.columnAliases?.length && !node.alias) {
2272
+ throw new Error("tvf(ARRAY_UNNEST) with columnAliases requires an alias.");
2273
+ }
2274
+ const alias = node.alias ? ` AS ${quoteIdentifier(node.alias)}` : "";
2275
+ const cols = node.columnAliases?.length ? `(${node.columnAliases.map(quoteIdentifier).join(", ")})` : "";
2276
+ return `${lateral ? "LATERAL " : ""}${base}${alias}${cols}`;
2277
+ });
1885
2278
  }
1886
2279
  };
1887
2280
 
@@ -1891,8 +2284,20 @@ var PostgresDialect = class extends SqlDialectBase {
1891
2284
  * Creates a new PostgresDialect instance
1892
2285
  */
1893
2286
  constructor() {
1894
- super(new PostgresFunctionStrategy());
2287
+ super(new PostgresFunctionStrategy(), new PostgresTableFunctionStrategy());
1895
2288
  this.dialect = "postgres";
2289
+ this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2290
+ const left2 = this.compileOperand(node.left, ctx);
2291
+ const right2 = this.compileOperand(node.right, ctx);
2292
+ const op = node.operator === "^" ? "#" : node.operator;
2293
+ return `${left2} ${op} ${right2}`;
2294
+ });
2295
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
2296
+ const left2 = this.compileOperand(node.left, ctx);
2297
+ const right2 = this.compileOperand(node.right, ctx);
2298
+ const op = node.operator === "^" ? "#" : node.operator;
2299
+ return `(${left2} ${op} ${right2})`;
2300
+ });
1896
2301
  }
1897
2302
  /**
1898
2303
  * Quotes an identifier using PostgreSQL double-quote syntax
@@ -2001,6 +2406,14 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
2001
2406
  }
2002
2407
  return `DATE(${date})`;
2003
2408
  });
2409
+ this.add("HOUR", ({ compiledArgs }) => `HOUR(${compiledArgs[0]})`);
2410
+ this.add("MINUTE", ({ compiledArgs }) => `MINUTE(${compiledArgs[0]})`);
2411
+ this.add("SECOND", ({ compiledArgs }) => `SECOND(${compiledArgs[0]})`);
2412
+ this.add("QUARTER", ({ compiledArgs }) => `QUARTER(${compiledArgs[0]})`);
2413
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2414
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments (array, value)");
2415
+ return `JSON_ARRAY_APPEND(${compiledArgs[0]}, '$', ${compiledArgs[1]})`;
2416
+ });
2004
2417
  }
2005
2418
  };
2006
2419
 
@@ -2133,6 +2546,30 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
2133
2546
  const separator = ctx.compileOperand(separatorOperand);
2134
2547
  return `GROUP_CONCAT(${arg}, ${separator})`;
2135
2548
  });
2549
+ this.add("HOUR", ({ compiledArgs }) => `CAST(strftime('%H', ${compiledArgs[0]}) AS INTEGER)`);
2550
+ this.add("MINUTE", ({ compiledArgs }) => `CAST(strftime('%M', ${compiledArgs[0]}) AS INTEGER)`);
2551
+ this.add("SECOND", ({ compiledArgs }) => `CAST(strftime('%S', ${compiledArgs[0]}) AS INTEGER)`);
2552
+ this.add("QUARTER", ({ compiledArgs }) => `((CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER) + 2) / 3)`);
2553
+ this.add("JSON_LENGTH", ({ compiledArgs }) => {
2554
+ if (compiledArgs.length === 0 || compiledArgs.length > 2) {
2555
+ throw new Error("JSON_LENGTH expects 1 or 2 arguments on SQLite");
2556
+ }
2557
+ return `json_array_length(${compiledArgs.join(", ")})`;
2558
+ });
2559
+ this.add("JSON_ARRAYAGG", ({ compiledArgs }) => {
2560
+ if (compiledArgs.length !== 1) {
2561
+ throw new Error("JSON_ARRAYAGG expects 1 argument on SQLite");
2562
+ }
2563
+ return `json_group_array(${compiledArgs[0]})`;
2564
+ });
2565
+ this.add("JSON_CONTAINS", () => {
2566
+ throw new Error("JSON_CONTAINS is not supported on SQLite");
2567
+ });
2568
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2569
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments (array, value)");
2570
+ return `json_array_append(${compiledArgs[0]}, '$', ${compiledArgs[1]})`;
2571
+ });
2572
+ this.add("CHR", ({ compiledArgs }) => `CHAR(${compiledArgs[0]})`);
2136
2573
  }
2137
2574
  };
2138
2575
 
@@ -2144,6 +2581,22 @@ var SqliteDialect = class extends SqlDialectBase {
2144
2581
  constructor() {
2145
2582
  super(new SqliteFunctionStrategy());
2146
2583
  this.dialect = "sqlite";
2584
+ this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2585
+ const left2 = this.compileOperand(node.left, ctx);
2586
+ const right2 = this.compileOperand(node.right, ctx);
2587
+ if (node.operator === "^") {
2588
+ return `(${left2} | ${right2}) & ~(${left2} & ${right2})`;
2589
+ }
2590
+ return `${left2} ${node.operator} ${right2}`;
2591
+ });
2592
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
2593
+ const left2 = this.compileOperand(node.left, ctx);
2594
+ const right2 = this.compileOperand(node.right, ctx);
2595
+ if (node.operator === "^") {
2596
+ return `((${left2} | ${right2}) & ~(${left2} & ${right2}))`;
2597
+ }
2598
+ return `(${left2} ${node.operator} ${right2})`;
2599
+ });
2147
2600
  }
2148
2601
  /**
2149
2602
  * Quotes an identifier using SQLite double-quote syntax
@@ -2268,6 +2721,33 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
2268
2721
  const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
2269
2722
  return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
2270
2723
  });
2724
+ this.add("LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2725
+ this.add("CHAR_LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2726
+ this.add("CHARACTER_LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2727
+ this.add("POSITION", ({ compiledArgs }) => `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]})`);
2728
+ this.add("LOCATE", ({ compiledArgs }) => compiledArgs.length === 3 ? `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` : `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]})`);
2729
+ this.add("INSTR", ({ compiledArgs }) => `CHARINDEX(${compiledArgs[1]}, ${compiledArgs[0]})`);
2730
+ this.add("CHR", ({ compiledArgs }) => `CHAR(${compiledArgs[0]})`);
2731
+ this.add("HOUR", ({ compiledArgs }) => `DATEPART(hour, ${compiledArgs[0]})`);
2732
+ this.add("MINUTE", ({ compiledArgs }) => `DATEPART(minute, ${compiledArgs[0]})`);
2733
+ this.add("SECOND", ({ compiledArgs }) => `DATEPART(second, ${compiledArgs[0]})`);
2734
+ this.add("QUARTER", ({ compiledArgs }) => `DATEPART(quarter, ${compiledArgs[0]})`);
2735
+ this.add("JSON_SET", ({ compiledArgs }) => {
2736
+ if (compiledArgs.length !== 3) throw new Error("JSON_SET expects 3 arguments on SQL Server");
2737
+ return `JSON_MODIFY(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})`;
2738
+ });
2739
+ this.add("JSON_LENGTH", () => {
2740
+ throw new Error("JSON_LENGTH is not supported on SQL Server");
2741
+ });
2742
+ this.add("JSON_ARRAYAGG", () => {
2743
+ throw new Error("JSON_ARRAYAGG is not supported on SQL Server");
2744
+ });
2745
+ this.add("JSON_CONTAINS", () => {
2746
+ throw new Error("JSON_CONTAINS is not supported on SQL Server");
2747
+ });
2748
+ this.add("ARRAY_APPEND", () => {
2749
+ throw new Error("ARRAY_APPEND is not supported on SQL Server");
2750
+ });
2271
2751
  }
2272
2752
  };
2273
2753
 
@@ -3372,20 +3852,21 @@ var RelationProjectionHelper = class {
3372
3852
  var assertNever = (value) => {
3373
3853
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
3374
3854
  };
3375
- var baseRelationCondition = (root, relation, rootAlias) => {
3855
+ var baseRelationCondition = (root, relation, rootAlias, targetTableName) => {
3376
3856
  const rootTable = rootAlias || root.name;
3857
+ const targetTable = targetTableName ?? relation.target.name;
3377
3858
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
3378
3859
  const localKey = relation.localKey || defaultLocalKey;
3379
3860
  switch (relation.type) {
3380
3861
  case RelationKinds.HasMany:
3381
3862
  case RelationKinds.HasOne:
3382
3863
  return eq(
3383
- { type: "Column", table: relation.target.name, name: relation.foreignKey },
3864
+ { type: "Column", table: targetTable, name: relation.foreignKey },
3384
3865
  { type: "Column", table: rootTable, name: localKey }
3385
3866
  );
3386
3867
  case RelationKinds.BelongsTo:
3387
3868
  return eq(
3388
- { type: "Column", table: relation.target.name, name: localKey },
3869
+ { type: "Column", table: targetTable, name: localKey },
3389
3870
  { type: "Column", table: rootTable, name: relation.foreignKey }
3390
3871
  );
3391
3872
  case RelationKinds.BelongsToMany:
@@ -3394,7 +3875,7 @@ var baseRelationCondition = (root, relation, rootAlias) => {
3394
3875
  return assertNever(relation);
3395
3876
  }
3396
3877
  };
3397
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
3878
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias, targetTable, targetTableName) => {
3398
3879
  const rootKey = relation.localKey || findPrimaryKey(root);
3399
3880
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
3400
3881
  const rootTable = rootAlias || root.name;
@@ -3407,8 +3888,14 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
3407
3888
  { type: "Table", name: relation.pivotTable.name, schema: relation.pivotTable.schema },
3408
3889
  pivotCondition
3409
3890
  );
3891
+ const targetSource = targetTable ?? {
3892
+ type: "Table",
3893
+ name: relation.target.name,
3894
+ schema: relation.target.schema
3895
+ };
3896
+ const effectiveTargetName = targetTableName ?? relation.target.name;
3410
3897
  let targetCondition = eq(
3411
- { type: "Column", table: relation.target.name, name: targetKey },
3898
+ { type: "Column", table: effectiveTargetName, name: targetKey },
3412
3899
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
3413
3900
  );
3414
3901
  if (extra) {
@@ -3416,24 +3903,25 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
3416
3903
  }
3417
3904
  const targetJoin = createJoinNode(
3418
3905
  joinKind,
3419
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
3906
+ targetSource,
3420
3907
  targetCondition,
3421
3908
  relationName
3422
3909
  );
3423
3910
  return [pivotJoin, targetJoin];
3424
3911
  };
3425
- var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
3426
- const base = baseRelationCondition(root, relation, rootAlias);
3912
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias, targetTableName) => {
3913
+ const base = baseRelationCondition(root, relation, rootAlias, targetTableName);
3427
3914
  return extra ? and(base, extra) : base;
3428
3915
  };
3429
- var buildRelationCorrelation = (root, relation, rootAlias) => {
3430
- return baseRelationCondition(root, relation, rootAlias);
3916
+ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
3917
+ return baseRelationCondition(root, relation, rootAlias, targetTableName);
3431
3918
  };
3432
3919
 
3433
3920
  // src/core/ast/join-metadata.ts
3434
3921
  var getJoinRelationName = (join) => join.meta?.relationName;
3435
3922
 
3436
3923
  // src/query-builder/relation-service.ts
3924
+ var hasRelationForeignKey = (relation) => relation.type !== RelationKinds.BelongsToMany;
3437
3925
  var RelationService = class {
3438
3926
  /**
3439
3927
  * Creates a new RelationService instance
@@ -3458,8 +3946,8 @@ var RelationService = class {
3458
3946
  * @param extraCondition - Additional join condition
3459
3947
  * @returns Relation result with updated state and hydration
3460
3948
  */
3461
- joinRelation(relationName, joinKind, extraCondition) {
3462
- const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
3949
+ joinRelation(relationName, joinKind, extraCondition, tableSource) {
3950
+ const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition, tableSource);
3463
3951
  return { state: nextState, hydration: this.hydration };
3464
3952
  }
3465
3953
  /**
@@ -3488,14 +3976,58 @@ var RelationService = class {
3488
3976
  const relation = this.getRelation(relationName);
3489
3977
  const aliasPrefix = options?.aliasPrefix ?? relationName;
3490
3978
  const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
3979
+ const { selfFilters, crossFilters } = this.splitFilterExpressions(
3980
+ options?.filter,
3981
+ /* @__PURE__ */ new Set([relation.target.name])
3982
+ );
3983
+ const canUseCte = !alreadyJoined && selfFilters.length > 0;
3984
+ const joinFilters = [...crossFilters];
3985
+ if (!canUseCte) {
3986
+ joinFilters.push(...selfFilters);
3987
+ }
3988
+ const joinCondition = this.combineWithAnd(joinFilters);
3989
+ let tableSourceOverride;
3990
+ if (canUseCte) {
3991
+ const cteInfo = this.createFilteredRelationCte(state, relationName, relation, selfFilters);
3992
+ state = cteInfo.state;
3993
+ tableSourceOverride = cteInfo.table;
3994
+ }
3491
3995
  if (!alreadyJoined) {
3492
- const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
3493
- state = joined.state;
3996
+ state = this.withJoin(
3997
+ state,
3998
+ relationName,
3999
+ options?.joinKind ?? JOIN_KINDS.LEFT,
4000
+ joinCondition,
4001
+ tableSourceOverride
4002
+ );
3494
4003
  }
3495
4004
  const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
3496
4005
  state = projectionResult.state;
3497
4006
  hydration = projectionResult.hydration;
3498
- const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
4007
+ if (hasRelationForeignKey(relation)) {
4008
+ const fkColumn = this.table.columns[relation.foreignKey];
4009
+ if (fkColumn) {
4010
+ const hasForeignKeySelected = state.ast.columns.some((col2) => {
4011
+ if (col2.type !== "Column") return false;
4012
+ const node = col2;
4013
+ const alias = node.alias ?? node.name;
4014
+ return alias === relation.foreignKey;
4015
+ });
4016
+ if (!hasForeignKeySelected) {
4017
+ const fkSelectionResult = this.selectColumns(state, hydration, {
4018
+ [relation.foreignKey]: fkColumn
4019
+ });
4020
+ state = fkSelectionResult.state;
4021
+ hydration = fkSelectionResult.hydration;
4022
+ }
4023
+ }
4024
+ }
4025
+ const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
4026
+ const targetPrimaryKey = findPrimaryKey(relation.target);
4027
+ if (!requestedColumns.includes(targetPrimaryKey)) {
4028
+ requestedColumns.push(targetPrimaryKey);
4029
+ }
4030
+ const targetColumns = requestedColumns;
3499
4031
  const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
3500
4032
  return keys.reduce((acc, key) => {
3501
4033
  const def = columns[key];
@@ -3579,27 +4111,42 @@ var RelationService = class {
3579
4111
  * @param extraCondition - Additional join condition
3580
4112
  * @returns Updated query state with join
3581
4113
  */
3582
- withJoin(state, relationName, joinKind, extraCondition) {
4114
+ withJoin(state, relationName, joinKind, extraCondition, tableSource) {
3583
4115
  const relation = this.getRelation(relationName);
3584
4116
  const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
3585
4117
  if (relation.type === RelationKinds.BelongsToMany) {
4118
+ const targetTableSource = tableSource ?? {
4119
+ type: "Table",
4120
+ name: relation.target.name,
4121
+ schema: relation.target.schema
4122
+ };
4123
+ const targetName2 = this.resolveTargetTableName(targetTableSource, relation);
3586
4124
  const joins = buildBelongsToManyJoins(
3587
4125
  this.table,
3588
4126
  relationName,
3589
4127
  relation,
3590
4128
  joinKind,
3591
4129
  extraCondition,
3592
- rootAlias
4130
+ rootAlias,
4131
+ targetTableSource,
4132
+ targetName2
3593
4133
  );
3594
4134
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
3595
4135
  }
3596
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
3597
- const joinNode = createJoinNode(
3598
- joinKind,
3599
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
3600
- condition,
3601
- relationName
4136
+ const targetTable = tableSource ?? {
4137
+ type: "Table",
4138
+ name: relation.target.name,
4139
+ schema: relation.target.schema
4140
+ };
4141
+ const targetName = this.resolveTargetTableName(targetTable, relation);
4142
+ const condition = buildRelationJoinCondition(
4143
+ this.table,
4144
+ relation,
4145
+ extraCondition,
4146
+ rootAlias,
4147
+ targetName
3602
4148
  );
4149
+ const joinNode = createJoinNode(joinKind, targetTable, condition, relationName);
3603
4150
  return this.astService(state).withJoin(joinNode);
3604
4151
  }
3605
4152
  /**
@@ -3616,6 +4163,198 @@ var RelationService = class {
3616
4163
  hydration: hydration.onColumnsSelected(nextState, addedColumns)
3617
4164
  };
3618
4165
  }
4166
+ combineWithAnd(expressions) {
4167
+ if (expressions.length === 0) return void 0;
4168
+ if (expressions.length === 1) return expressions[0];
4169
+ return {
4170
+ type: "LogicalExpression",
4171
+ operator: "AND",
4172
+ operands: expressions
4173
+ };
4174
+ }
4175
+ splitFilterExpressions(filter, allowedTables) {
4176
+ const terms = this.flattenAnd(filter);
4177
+ const selfFilters = [];
4178
+ const crossFilters = [];
4179
+ for (const term of terms) {
4180
+ if (this.isExpressionSelfContained(term, allowedTables)) {
4181
+ selfFilters.push(term);
4182
+ } else {
4183
+ crossFilters.push(term);
4184
+ }
4185
+ }
4186
+ return { selfFilters, crossFilters };
4187
+ }
4188
+ flattenAnd(node) {
4189
+ if (!node) return [];
4190
+ if (node.type === "LogicalExpression" && node.operator === "AND") {
4191
+ return node.operands.flatMap((operand) => this.flattenAnd(operand));
4192
+ }
4193
+ return [node];
4194
+ }
4195
+ isExpressionSelfContained(expr, allowedTables) {
4196
+ const collector = this.collectReferencedTables(expr);
4197
+ if (collector.hasSubquery) return false;
4198
+ if (collector.tables.size === 0) return true;
4199
+ for (const table of collector.tables) {
4200
+ if (!allowedTables.has(table)) {
4201
+ return false;
4202
+ }
4203
+ }
4204
+ return true;
4205
+ }
4206
+ collectReferencedTables(expr) {
4207
+ const collector = {
4208
+ tables: /* @__PURE__ */ new Set(),
4209
+ hasSubquery: false
4210
+ };
4211
+ this.collectFromExpression(expr, collector);
4212
+ return collector;
4213
+ }
4214
+ collectFromExpression(expr, collector) {
4215
+ switch (expr.type) {
4216
+ case "BinaryExpression":
4217
+ this.collectFromOperand(expr.left, collector);
4218
+ this.collectFromOperand(expr.right, collector);
4219
+ break;
4220
+ case "LogicalExpression":
4221
+ expr.operands.forEach((operand) => this.collectFromExpression(operand, collector));
4222
+ break;
4223
+ case "NullExpression":
4224
+ this.collectFromOperand(expr.left, collector);
4225
+ break;
4226
+ case "InExpression":
4227
+ this.collectFromOperand(expr.left, collector);
4228
+ if (Array.isArray(expr.right)) {
4229
+ expr.right.forEach((value) => this.collectFromOperand(value, collector));
4230
+ } else {
4231
+ collector.hasSubquery = true;
4232
+ }
4233
+ break;
4234
+ case "ExistsExpression":
4235
+ collector.hasSubquery = true;
4236
+ break;
4237
+ case "BetweenExpression":
4238
+ this.collectFromOperand(expr.left, collector);
4239
+ this.collectFromOperand(expr.lower, collector);
4240
+ this.collectFromOperand(expr.upper, collector);
4241
+ break;
4242
+ case "ArithmeticExpression":
4243
+ case "BitwiseExpression":
4244
+ this.collectFromOperand(expr.left, collector);
4245
+ this.collectFromOperand(expr.right, collector);
4246
+ break;
4247
+ default:
4248
+ break;
4249
+ }
4250
+ }
4251
+ collectFromOperand(node, collector) {
4252
+ switch (node.type) {
4253
+ case "Column":
4254
+ collector.tables.add(node.table);
4255
+ break;
4256
+ case "Function":
4257
+ node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4258
+ if (node.separator) {
4259
+ this.collectFromOperand(node.separator, collector);
4260
+ }
4261
+ if (node.orderBy) {
4262
+ node.orderBy.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4263
+ }
4264
+ break;
4265
+ case "JsonPath":
4266
+ this.collectFromOperand(node.column, collector);
4267
+ break;
4268
+ case "ScalarSubquery":
4269
+ collector.hasSubquery = true;
4270
+ break;
4271
+ case "CaseExpression":
4272
+ node.conditions.forEach(({ when, then }) => {
4273
+ this.collectFromExpression(when, collector);
4274
+ this.collectFromOperand(then, collector);
4275
+ });
4276
+ if (node.else) {
4277
+ this.collectFromOperand(node.else, collector);
4278
+ }
4279
+ break;
4280
+ case "Cast":
4281
+ this.collectFromOperand(node.expression, collector);
4282
+ break;
4283
+ case "WindowFunction":
4284
+ node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4285
+ node.partitionBy?.forEach((part) => this.collectFromOperand(part, collector));
4286
+ node.orderBy?.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4287
+ break;
4288
+ case "Collate":
4289
+ this.collectFromOperand(node.expression, collector);
4290
+ break;
4291
+ case "ArithmeticExpression":
4292
+ case "BitwiseExpression":
4293
+ this.collectFromOperand(node.left, collector);
4294
+ this.collectFromOperand(node.right, collector);
4295
+ break;
4296
+ case "Literal":
4297
+ case "AliasRef":
4298
+ break;
4299
+ default:
4300
+ break;
4301
+ }
4302
+ }
4303
+ collectFromOrderingTerm(term, collector) {
4304
+ if (isOperandNode(term)) {
4305
+ this.collectFromOperand(term, collector);
4306
+ return;
4307
+ }
4308
+ this.collectFromExpression(term, collector);
4309
+ }
4310
+ createFilteredRelationCte(state, relationName, relation, filters) {
4311
+ const cteName = this.generateUniqueCteName(state, relationName);
4312
+ const predicate = this.combineWithAnd(filters);
4313
+ if (!predicate) {
4314
+ throw new Error("Unable to build filter CTE without predicates.");
4315
+ }
4316
+ const columns = Object.keys(relation.target.columns).map((name) => ({
4317
+ type: "Column",
4318
+ table: relation.target.name,
4319
+ name
4320
+ }));
4321
+ const cteQuery = {
4322
+ type: "SelectQuery",
4323
+ from: { type: "Table", name: relation.target.name, schema: relation.target.schema },
4324
+ columns,
4325
+ joins: [],
4326
+ where: predicate
4327
+ };
4328
+ const nextState = this.astService(state).withCte(cteName, cteQuery);
4329
+ const tableNode3 = {
4330
+ type: "Table",
4331
+ name: cteName,
4332
+ alias: relation.target.name
4333
+ };
4334
+ return { state: nextState, table: tableNode3 };
4335
+ }
4336
+ generateUniqueCteName(state, relationName) {
4337
+ const existing = new Set((state.ast.ctes ?? []).map((cte) => cte.name));
4338
+ let candidate = `${relationName}__filtered`;
4339
+ let suffix = 1;
4340
+ while (existing.has(candidate)) {
4341
+ candidate = `${relationName}__filtered_${suffix}`;
4342
+ suffix += 1;
4343
+ }
4344
+ return candidate;
4345
+ }
4346
+ resolveTargetTableName(target, relation) {
4347
+ if (target.type === "Table") {
4348
+ return target.alias ?? target.name;
4349
+ }
4350
+ if (target.type === "DerivedTable") {
4351
+ return target.alias;
4352
+ }
4353
+ if (target.type === "FunctionTable") {
4354
+ return target.alias ?? relation.target.name;
4355
+ }
4356
+ return relation.target.name;
4357
+ }
3619
4358
  /**
3620
4359
  * Gets a relation definition by name
3621
4360
  * @param relationName - Name of the relation
@@ -3966,6 +4705,18 @@ var DefaultHasManyCollection = class {
3966
4705
  getItems() {
3967
4706
  return this.items;
3968
4707
  }
4708
+ /**
4709
+ * Array-compatible length for testing frameworks.
4710
+ */
4711
+ get length() {
4712
+ return this.items.length;
4713
+ }
4714
+ /**
4715
+ * Enables iteration over the collection like an array.
4716
+ */
4717
+ [Symbol.iterator]() {
4718
+ return this.items[Symbol.iterator]();
4719
+ }
3969
4720
  /**
3970
4721
  * Adds a new child entity to the collection.
3971
4722
  * @param data - Partial data for the new entity
@@ -4065,6 +4816,17 @@ var hideInternal2 = (obj, keys) => {
4065
4816
  }
4066
4817
  };
4067
4818
  var DefaultHasOneReference = class {
4819
+ /**
4820
+ * @param ctx The entity context for tracking changes.
4821
+ * @param meta Metadata for the parent entity.
4822
+ * @param root The parent entity instance.
4823
+ * @param relationName The name of the relation.
4824
+ * @param relation Relation definition.
4825
+ * @param rootTable Table definition of the parent entity.
4826
+ * @param loader Function to load the child entity.
4827
+ * @param createEntity Function to create entity instances from rows.
4828
+ * @param localKey The local key on the parent entity used for the relation.
4829
+ */
4068
4830
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
4069
4831
  this.ctx = ctx;
4070
4832
  this.meta = meta;
@@ -4182,6 +4944,17 @@ var hideInternal3 = (obj, keys) => {
4182
4944
  }
4183
4945
  };
4184
4946
  var DefaultBelongsToReference = class {
4947
+ /**
4948
+ * @param ctx The entity context for tracking changes.
4949
+ * @param meta Metadata for the child entity.
4950
+ * @param root The child entity instance (carrying the foreign key).
4951
+ * @param relationName The name of the relation.
4952
+ * @param relation Relation definition.
4953
+ * @param rootTable Table definition of the child entity.
4954
+ * @param loader Function to load the parent entity.
4955
+ * @param createEntity Function to create entity instances from rows.
4956
+ * @param targetKey The primary key of the target (parent) table.
4957
+ */
4185
4958
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
4186
4959
  this.ctx = ctx;
4187
4960
  this.meta = meta;
@@ -4273,6 +5046,17 @@ var hideInternal4 = (obj, keys) => {
4273
5046
  }
4274
5047
  };
4275
5048
  var DefaultManyToManyCollection = class {
5049
+ /**
5050
+ * @param ctx The entity context for tracking changes.
5051
+ * @param meta Metadata for the root entity.
5052
+ * @param root The root entity instance.
5053
+ * @param relationName The name of the relation.
5054
+ * @param relation Relation definition.
5055
+ * @param rootTable Table definition of the root entity.
5056
+ * @param loader Function to load the collection items.
5057
+ * @param createEntity Function to create entity instances from rows.
5058
+ * @param localKey The local key used for joining.
5059
+ */
4276
5060
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
4277
5061
  this.ctx = ctx;
4278
5062
  this.meta = meta;
@@ -4288,6 +5072,10 @@ var DefaultManyToManyCollection = class {
4288
5072
  hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
4289
5073
  this.hydrateFromCache();
4290
5074
  }
5075
+ /**
5076
+ * Loads the collection items if not already loaded.
5077
+ * @returns A promise that resolves to the array of target entities.
5078
+ */
4291
5079
  async load() {
4292
5080
  if (this.loaded) return this.items;
4293
5081
  const map = await this.loader();
@@ -4303,9 +5091,30 @@ var DefaultManyToManyCollection = class {
4303
5091
  this.loaded = true;
4304
5092
  return this.items;
4305
5093
  }
5094
+ /**
5095
+ * Returns the currently loaded items.
5096
+ * @returns Array of target entities.
5097
+ */
4306
5098
  getItems() {
4307
5099
  return this.items;
4308
5100
  }
5101
+ /**
5102
+ * Array-compatible length for testing frameworks.
5103
+ */
5104
+ get length() {
5105
+ return this.items.length;
5106
+ }
5107
+ /**
5108
+ * Enables iteration over the collection like an array.
5109
+ */
5110
+ [Symbol.iterator]() {
5111
+ return this.items[Symbol.iterator]();
5112
+ }
5113
+ /**
5114
+ * Attaches an entity to the collection.
5115
+ * Registers an 'attach' change in the entity context.
5116
+ * @param target Entity instance or its primary key value.
5117
+ */
4309
5118
  attach(target) {
4310
5119
  const entity = this.ensureEntity(target);
4311
5120
  const id = this.extractId(entity);
@@ -4325,6 +5134,11 @@ var DefaultManyToManyCollection = class {
4325
5134
  { kind: "attach", entity }
4326
5135
  );
4327
5136
  }
5137
+ /**
5138
+ * Detaches an entity from the collection.
5139
+ * Registers a 'detach' change in the entity context.
5140
+ * @param target Entity instance or its primary key value.
5141
+ */
4328
5142
  detach(target) {
4329
5143
  const id = typeof target === "number" || typeof target === "string" ? target : this.extractId(target);
4330
5144
  if (id == null) return;
@@ -4340,6 +5154,11 @@ var DefaultManyToManyCollection = class {
4340
5154
  { kind: "detach", entity: existing }
4341
5155
  );
4342
5156
  }
5157
+ /**
5158
+ * Syncs the collection with a list of IDs.
5159
+ * Attaches missing IDs and detaches IDs not in the list.
5160
+ * @param ids Array of primary key values to sync with.
5161
+ */
4343
5162
  async syncByIds(ids) {
4344
5163
  await this.load();
4345
5164
  const normalized = new Set(ids.map((id) => toKey5(id)));
@@ -4398,10 +5217,27 @@ var DefaultManyToManyCollection = class {
4398
5217
  };
4399
5218
 
4400
5219
  // src/orm/lazy-batch.ts
4401
- var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
4402
- acc[name] = def;
4403
- return acc;
4404
- }, {});
5220
+ var hasColumns = (columns) => Boolean(columns && columns.length > 0);
5221
+ var buildColumnSelection = (table, columns, missingMsg) => {
5222
+ return columns.reduce((acc, column) => {
5223
+ const def = table.columns[column];
5224
+ if (!def) {
5225
+ throw new Error(missingMsg(column));
5226
+ }
5227
+ acc[column] = def;
5228
+ return acc;
5229
+ }, {});
5230
+ };
5231
+ var filterRow = (row, columns) => {
5232
+ const filtered = {};
5233
+ for (const column of columns) {
5234
+ if (column in row) {
5235
+ filtered[column] = row[column];
5236
+ }
5237
+ }
5238
+ return filtered;
5239
+ };
5240
+ var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
4405
5241
  var rowsFromResults = (results) => {
4406
5242
  const rows = [];
4407
5243
  for (const result of results) {
@@ -4433,9 +5269,12 @@ var collectKeysFromRoots = (roots, key) => {
4433
5269
  return collected;
4434
5270
  };
4435
5271
  var buildInListValues = (keys) => Array.from(keys);
4436
- var fetchRowsForKeys = async (ctx, table, column, keys) => {
4437
- const qb = new SelectQueryBuilder(table).select(selectAllColumns(table));
4438
- qb.where(inList(column, buildInListValues(keys)));
5272
+ var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
5273
+ let qb = new SelectQueryBuilder(table).select(selection);
5274
+ qb = qb.where(inList(column, buildInListValues(keys)));
5275
+ if (filter) {
5276
+ qb = qb.where(filter);
5277
+ }
4439
5278
  return executeQuery(ctx, qb);
4440
5279
  };
4441
5280
  var groupRowsByMany = (rows, keyColumn) => {
@@ -4462,7 +5301,7 @@ var groupRowsByUnique = (rows, keyColumn) => {
4462
5301
  }
4463
5302
  return lookup;
4464
5303
  };
4465
- var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
5304
+ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
4466
5305
  const localKey = relation.localKey || findPrimaryKey(rootTable);
4467
5306
  const roots = ctx.getEntitiesForTable(rootTable);
4468
5307
  const keys = collectKeysFromRoots(roots, localKey);
@@ -4471,10 +5310,30 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
4471
5310
  }
4472
5311
  const fkColumn = relation.target.columns[relation.foreignKey];
4473
5312
  if (!fkColumn) return /* @__PURE__ */ new Map();
4474
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
4475
- return groupRowsByMany(rows, relation.foreignKey);
5313
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5314
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5315
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5316
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5317
+ selectedColumns.push(targetPrimaryKey);
5318
+ }
5319
+ const queryColumns = new Set(selectedColumns);
5320
+ queryColumns.add(relation.foreignKey);
5321
+ const selection = buildColumnSelection(
5322
+ relation.target,
5323
+ Array.from(queryColumns),
5324
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5325
+ );
5326
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5327
+ const grouped = groupRowsByMany(rows, relation.foreignKey);
5328
+ if (!requestedColumns) return grouped;
5329
+ const visibleColumns = new Set(selectedColumns);
5330
+ const filtered = /* @__PURE__ */ new Map();
5331
+ for (const [key, bucket] of grouped.entries()) {
5332
+ filtered.set(key, filterRows(bucket, visibleColumns));
5333
+ }
5334
+ return filtered;
4476
5335
  };
4477
- var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
5336
+ var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options) => {
4478
5337
  const localKey = relation.localKey || findPrimaryKey(rootTable);
4479
5338
  const roots = ctx.getEntitiesForTable(rootTable);
4480
5339
  const keys = collectKeysFromRoots(roots, localKey);
@@ -4483,22 +5342,98 @@ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
4483
5342
  }
4484
5343
  const fkColumn = relation.target.columns[relation.foreignKey];
4485
5344
  if (!fkColumn) return /* @__PURE__ */ new Map();
4486
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
4487
- return groupRowsByUnique(rows, relation.foreignKey);
5345
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5346
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5347
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5348
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5349
+ selectedColumns.push(targetPrimaryKey);
5350
+ }
5351
+ const queryColumns = new Set(selectedColumns);
5352
+ queryColumns.add(relation.foreignKey);
5353
+ const selection = buildColumnSelection(
5354
+ relation.target,
5355
+ Array.from(queryColumns),
5356
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5357
+ );
5358
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5359
+ const grouped = groupRowsByUnique(rows, relation.foreignKey);
5360
+ if (!requestedColumns) return grouped;
5361
+ const visibleColumns = new Set(selectedColumns);
5362
+ const filtered = /* @__PURE__ */ new Map();
5363
+ for (const [key, row] of grouped.entries()) {
5364
+ filtered.set(key, filterRow(row, visibleColumns));
5365
+ }
5366
+ return filtered;
4488
5367
  };
4489
- var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
5368
+ var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, options) => {
4490
5369
  const roots = ctx.getEntitiesForTable(rootTable);
4491
- const foreignKeys = collectKeysFromRoots(roots, relation.foreignKey);
5370
+ const getForeignKeys = () => collectKeysFromRoots(roots, relation.foreignKey);
5371
+ let foreignKeys = getForeignKeys();
5372
+ if (!foreignKeys.size) {
5373
+ const pkName = findPrimaryKey(rootTable);
5374
+ const pkColumn2 = rootTable.columns[pkName];
5375
+ const fkColumn = rootTable.columns[relation.foreignKey];
5376
+ if (pkColumn2 && fkColumn) {
5377
+ const missingKeys = /* @__PURE__ */ new Set();
5378
+ const entityByPk = /* @__PURE__ */ new Map();
5379
+ for (const tracked of roots) {
5380
+ const entity = tracked.entity;
5381
+ const pkValue = entity[pkName];
5382
+ if (pkValue === void 0 || pkValue === null) continue;
5383
+ const fkValue = entity[relation.foreignKey];
5384
+ if (fkValue === void 0 || fkValue === null) {
5385
+ missingKeys.add(pkValue);
5386
+ entityByPk.set(pkValue, entity);
5387
+ }
5388
+ }
5389
+ if (missingKeys.size) {
5390
+ const selection2 = buildColumnSelection(
5391
+ rootTable,
5392
+ [pkName, relation.foreignKey],
5393
+ (column) => `Column '${column}' not found on table '${rootTable.name}'`
5394
+ );
5395
+ const keyRows = await fetchRowsForKeys(ctx, rootTable, pkColumn2, missingKeys, selection2);
5396
+ for (const row of keyRows) {
5397
+ const pkValue = row[pkName];
5398
+ if (pkValue === void 0 || pkValue === null) continue;
5399
+ const entity = entityByPk.get(pkValue);
5400
+ if (!entity) continue;
5401
+ const fkValue = row[relation.foreignKey];
5402
+ if (fkValue !== void 0 && fkValue !== null) {
5403
+ entity[relation.foreignKey] = fkValue;
5404
+ }
5405
+ }
5406
+ foreignKeys = getForeignKeys();
5407
+ }
5408
+ }
5409
+ }
4492
5410
  if (!foreignKeys.size) {
4493
5411
  return /* @__PURE__ */ new Map();
4494
5412
  }
4495
5413
  const targetKey = relation.localKey || findPrimaryKey(relation.target);
4496
5414
  const pkColumn = relation.target.columns[targetKey];
4497
5415
  if (!pkColumn) return /* @__PURE__ */ new Map();
4498
- const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys);
4499
- return groupRowsByUnique(rows, targetKey);
5416
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5417
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5418
+ if (!selectedColumns.includes(targetKey)) {
5419
+ selectedColumns.push(targetKey);
5420
+ }
5421
+ const selection = buildColumnSelection(
5422
+ relation.target,
5423
+ selectedColumns,
5424
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5425
+ );
5426
+ const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys, selection, options?.filter);
5427
+ const grouped = groupRowsByUnique(rows, targetKey);
5428
+ if (!requestedColumns) return grouped;
5429
+ const visibleColumns = new Set(selectedColumns);
5430
+ const filtered = /* @__PURE__ */ new Map();
5431
+ for (const [key, row] of grouped.entries()) {
5432
+ filtered.set(key, filterRow(row, visibleColumns));
5433
+ }
5434
+ return filtered;
4500
5435
  };
4501
- var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
5436
+ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, options) => {
4502
5437
  const rootKey = relation.localKey || findPrimaryKey(rootTable);
4503
5438
  const roots = ctx.getEntitiesForTable(rootTable);
4504
5439
  const rootIds = collectKeysFromRoots(roots, rootKey);
@@ -4507,9 +5442,29 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4507
5442
  }
4508
5443
  const pivotColumn = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
4509
5444
  if (!pivotColumn) return /* @__PURE__ */ new Map();
4510
- const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds);
5445
+ const pivotColumnsRequested = hasColumns(options?.pivot?.columns) ? [...options.pivot.columns] : void 0;
5446
+ const useIncludeDefaults = options !== void 0;
5447
+ let pivotSelectedColumns;
5448
+ if (pivotColumnsRequested) {
5449
+ pivotSelectedColumns = [...pivotColumnsRequested];
5450
+ } else if (useIncludeDefaults) {
5451
+ const pivotPk = relation.pivotPrimaryKey || findPrimaryKey(relation.pivotTable);
5452
+ pivotSelectedColumns = relation.defaultPivotColumns ?? buildDefaultPivotColumns(relation, pivotPk);
5453
+ } else {
5454
+ pivotSelectedColumns = Object.keys(relation.pivotTable.columns);
5455
+ }
5456
+ const pivotQueryColumns = new Set(pivotSelectedColumns);
5457
+ pivotQueryColumns.add(relation.pivotForeignKeyToRoot);
5458
+ pivotQueryColumns.add(relation.pivotForeignKeyToTarget);
5459
+ const pivotSelection = buildColumnSelection(
5460
+ relation.pivotTable,
5461
+ Array.from(pivotQueryColumns),
5462
+ (column) => `Column '${column}' not found on pivot table '${relation.pivotTable.name}'`
5463
+ );
5464
+ const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds, pivotSelection);
4511
5465
  const rootLookup = /* @__PURE__ */ new Map();
4512
5466
  const targetIds = /* @__PURE__ */ new Set();
5467
+ const pivotVisibleColumns = new Set(pivotSelectedColumns);
4513
5468
  for (const pivot of pivotRows) {
4514
5469
  const rootValue = pivot[relation.pivotForeignKeyToRoot];
4515
5470
  const targetValue = pivot[relation.pivotForeignKeyToTarget];
@@ -4519,7 +5474,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4519
5474
  const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
4520
5475
  bucket.push({
4521
5476
  targetId: targetValue,
4522
- pivot: { ...pivot }
5477
+ pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
4523
5478
  });
4524
5479
  rootLookup.set(toKey6(rootValue), bucket);
4525
5480
  targetIds.add(targetValue);
@@ -4530,8 +5485,19 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4530
5485
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
4531
5486
  const targetPkColumn = relation.target.columns[targetKey];
4532
5487
  if (!targetPkColumn) return /* @__PURE__ */ new Map();
4533
- const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds);
5488
+ const targetRequestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5489
+ const targetSelectedColumns = targetRequestedColumns ? [...targetRequestedColumns] : Object.keys(relation.target.columns);
5490
+ if (!targetSelectedColumns.includes(targetKey)) {
5491
+ targetSelectedColumns.push(targetKey);
5492
+ }
5493
+ const targetSelection = buildColumnSelection(
5494
+ relation.target,
5495
+ targetSelectedColumns,
5496
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5497
+ );
5498
+ const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds, targetSelection, options?.filter);
4534
5499
  const targetMap = groupRowsByUnique(targetRows, targetKey);
5500
+ const targetVisibleColumns = new Set(targetSelectedColumns);
4535
5501
  const result = /* @__PURE__ */ new Map();
4536
5502
  for (const [rootId, entries] of rootLookup.entries()) {
4537
5503
  const bucket = [];
@@ -4539,7 +5505,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4539
5505
  const targetRow = targetMap.get(toKey6(entry.targetId));
4540
5506
  if (!targetRow) continue;
4541
5507
  bucket.push({
4542
- ...targetRow,
5508
+ ...targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : targetRow,
4543
5509
  _pivot: entry.pivot
4544
5510
  });
4545
5511
  }
@@ -4569,12 +5535,13 @@ var relationLoaderCache = (meta, relationName, factory) => {
4569
5535
  }
4570
5536
  return promise;
4571
5537
  };
4572
- var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
5538
+ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
4573
5539
  const target = { ...row };
4574
5540
  const meta = {
4575
5541
  ctx,
4576
5542
  table,
4577
5543
  lazyRelations: [...lazyRelations],
5544
+ lazyRelationOptions: new Map(lazyRelationOptions),
4578
5545
  relationCache: /* @__PURE__ */ new Map(),
4579
5546
  relationHydration: /* @__PURE__ */ new Map(),
4580
5547
  relationWrappers: /* @__PURE__ */ new Map()
@@ -4615,14 +5582,14 @@ var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
4615
5582
  populateHydrationCache(proxy, row, meta);
4616
5583
  return proxy;
4617
5584
  };
4618
- var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
5585
+ var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
4619
5586
  const pkName = findPrimaryKey(table);
4620
5587
  const pkValue = row[pkName];
4621
5588
  if (pkValue !== void 0 && pkValue !== null) {
4622
5589
  const tracked = ctx.getEntity(table, pkValue);
4623
5590
  if (tracked) return tracked;
4624
5591
  }
4625
- const entity = createEntityProxy(ctx, table, row, lazyRelations);
5592
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
4626
5593
  if (pkValue !== void 0 && pkValue !== null) {
4627
5594
  ctx.trackManaged(table, pkValue, entity);
4628
5595
  } else {
@@ -4672,6 +5639,58 @@ var populateHydrationCache = (entity, row, meta) => {
4672
5639
  }
4673
5640
  }
4674
5641
  };
5642
+ var proxifyRelationWrapper = (wrapper) => {
5643
+ return new Proxy(wrapper, {
5644
+ get(target, prop, receiver) {
5645
+ if (typeof prop === "symbol") {
5646
+ return Reflect.get(target, prop, receiver);
5647
+ }
5648
+ if (prop in target) {
5649
+ return Reflect.get(target, prop, receiver);
5650
+ }
5651
+ const getItems = target.getItems;
5652
+ if (typeof getItems === "function") {
5653
+ const items = getItems.call(target);
5654
+ if (items && prop in items) {
5655
+ const propName = prop;
5656
+ const value = items[propName];
5657
+ return typeof value === "function" ? value.bind(items) : value;
5658
+ }
5659
+ }
5660
+ const getRef = target.get;
5661
+ if (typeof getRef === "function") {
5662
+ const current = getRef.call(target);
5663
+ if (current && prop in current) {
5664
+ const propName = prop;
5665
+ const value = current[propName];
5666
+ return typeof value === "function" ? value.bind(current) : value;
5667
+ }
5668
+ }
5669
+ return void 0;
5670
+ },
5671
+ set(target, prop, value, receiver) {
5672
+ if (typeof prop === "symbol") {
5673
+ return Reflect.set(target, prop, value, receiver);
5674
+ }
5675
+ if (prop in target) {
5676
+ return Reflect.set(target, prop, value, receiver);
5677
+ }
5678
+ const getRef = target.get;
5679
+ if (typeof getRef === "function") {
5680
+ const current = getRef.call(target);
5681
+ if (current && typeof current === "object") {
5682
+ return Reflect.set(current, prop, value);
5683
+ }
5684
+ }
5685
+ const getItems = target.getItems;
5686
+ if (typeof getItems === "function") {
5687
+ const items = getItems.call(target);
5688
+ return Reflect.set(items, prop, value);
5689
+ }
5690
+ return Reflect.set(target, prop, value, receiver);
5691
+ }
5692
+ });
5693
+ };
4675
5694
  var getRelationWrapper = (meta, relationName, owner) => {
4676
5695
  if (meta.relationWrappers.has(relationName)) {
4677
5696
  return meta.relationWrappers.get(relationName);
@@ -4679,12 +5698,13 @@ var getRelationWrapper = (meta, relationName, owner) => {
4679
5698
  const relation = meta.table.relations[relationName];
4680
5699
  if (!relation) return void 0;
4681
5700
  const wrapper = instantiateWrapper(meta, relationName, relation, owner);
4682
- if (wrapper) {
4683
- meta.relationWrappers.set(relationName, wrapper);
4684
- }
4685
- return wrapper;
5701
+ if (!wrapper) return void 0;
5702
+ const proxied = proxifyRelationWrapper(wrapper);
5703
+ meta.relationWrappers.set(relationName, proxied);
5704
+ return proxied;
4686
5705
  };
4687
5706
  var instantiateWrapper = (meta, relationName, relation, owner) => {
5707
+ const lazyOptions = meta.lazyRelationOptions.get(relationName);
4688
5708
  switch (relation.type) {
4689
5709
  case RelationKinds.HasOne: {
4690
5710
  const hasOne2 = relation;
@@ -4692,7 +5712,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4692
5712
  const loader = () => relationLoaderCache(
4693
5713
  meta,
4694
5714
  relationName,
4695
- () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
5715
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, lazyOptions)
4696
5716
  );
4697
5717
  return new DefaultHasOneReference(
4698
5718
  meta.ctx,
@@ -4712,7 +5732,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4712
5732
  const loader = () => relationLoaderCache(
4713
5733
  meta,
4714
5734
  relationName,
4715
- () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
5735
+ () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, lazyOptions)
4716
5736
  );
4717
5737
  return new DefaultHasManyCollection(
4718
5738
  meta.ctx,
@@ -4732,7 +5752,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4732
5752
  const loader = () => relationLoaderCache(
4733
5753
  meta,
4734
5754
  relationName,
4735
- () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
5755
+ () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, lazyOptions)
4736
5756
  );
4737
5757
  return new DefaultBelongsToReference(
4738
5758
  meta.ctx,
@@ -4752,7 +5772,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4752
5772
  const loader = () => relationLoaderCache(
4753
5773
  meta,
4754
5774
  relationName,
4755
- () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
5775
+ () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
4756
5776
  );
4757
5777
  return new DefaultManyToManyCollection(
4758
5778
  meta.ctx,
@@ -4791,11 +5811,17 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
4791
5811
  const compiled = execCtx.dialect.compileSelect(ast);
4792
5812
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
4793
5813
  const rows = flattenResults(executed);
5814
+ const lazyRelations = qb.getLazyRelations();
5815
+ const lazyRelationOptions = qb.getLazyRelationOptions();
4794
5816
  if (ast.setOps && ast.setOps.length > 0) {
4795
- return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
5817
+ const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
5818
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
5819
+ return proxies;
4796
5820
  }
4797
5821
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
4798
- return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
5822
+ const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
5823
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
5824
+ return entities;
4799
5825
  };
4800
5826
  async function executeHydrated(session, qb) {
4801
5827
  return executeWithContexts(session.getExecutionContext(), session, qb);
@@ -4807,6 +5833,52 @@ async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
4807
5833
  }
4808
5834
  return executeWithContexts(execCtx, entityCtx, qb);
4809
5835
  }
5836
+ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOptions) => {
5837
+ if (!lazyRelations.length) return;
5838
+ const tracked = ctx.getEntitiesForTable(table);
5839
+ if (!tracked.length) return;
5840
+ const meta = getEntityMeta(tracked[0].entity);
5841
+ if (!meta) return;
5842
+ for (const relationName of lazyRelations) {
5843
+ const relation = table.relations[relationName];
5844
+ if (!relation) continue;
5845
+ const key = relationName;
5846
+ const options = lazyRelationOptions.get(key);
5847
+ if (!options) {
5848
+ continue;
5849
+ }
5850
+ switch (relation.type) {
5851
+ case RelationKinds.HasOne:
5852
+ await relationLoaderCache(
5853
+ meta,
5854
+ key,
5855
+ () => loadHasOneRelation(ctx, table, key, relation, options)
5856
+ );
5857
+ break;
5858
+ case RelationKinds.HasMany:
5859
+ await relationLoaderCache(
5860
+ meta,
5861
+ key,
5862
+ () => loadHasManyRelation(ctx, table, key, relation, options)
5863
+ );
5864
+ break;
5865
+ case RelationKinds.BelongsTo:
5866
+ await relationLoaderCache(
5867
+ meta,
5868
+ key,
5869
+ () => loadBelongsToRelation(ctx, table, key, relation, options)
5870
+ );
5871
+ break;
5872
+ case RelationKinds.BelongsToMany:
5873
+ await relationLoaderCache(
5874
+ meta,
5875
+ key,
5876
+ () => loadBelongsToManyRelation(ctx, table, key, relation, options)
5877
+ );
5878
+ break;
5879
+ }
5880
+ }
5881
+ };
4810
5882
 
4811
5883
  // src/query-builder/query-resolution.ts
4812
5884
  function resolveSelectQuery(query) {
@@ -4823,7 +5895,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4823
5895
  * @param hydration - Optional hydration manager
4824
5896
  * @param dependencies - Optional query builder dependencies
4825
5897
  */
4826
- constructor(table, state, hydration, dependencies, lazyRelations) {
5898
+ constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
4827
5899
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
4828
5900
  this.env = { table, deps };
4829
5901
  const initialState = state ?? deps.createState(table);
@@ -4833,6 +5905,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4833
5905
  hydration: initialHydration
4834
5906
  };
4835
5907
  this.lazyRelations = new Set(lazyRelations ?? []);
5908
+ this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
4836
5909
  this.columnSelector = deps.createColumnSelector(this.env);
4837
5910
  this.relationManager = deps.createRelationManager(this.env);
4838
5911
  }
@@ -4842,8 +5915,15 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4842
5915
  * @param lazyRelations - Updated lazy relations set
4843
5916
  * @returns New SelectQueryBuilder instance
4844
5917
  */
4845
- clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
4846
- return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
5918
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions)) {
5919
+ return new _SelectQueryBuilder(
5920
+ this.env.table,
5921
+ context.state,
5922
+ context.hydration,
5923
+ this.env.deps,
5924
+ lazyRelations,
5925
+ lazyRelationOptions
5926
+ );
4847
5927
  }
4848
5928
  /**
4849
5929
  * Applies an alias to the root FROM table.
@@ -5090,36 +6170,40 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5090
6170
  /**
5091
6171
  * Includes a relation lazily in the query results
5092
6172
  * @param relationName - Name of the relation to include lazily
6173
+ * @param options - Optional include options for lazy loading
5093
6174
  * @returns New query builder instance with lazy relation inclusion
5094
6175
  */
5095
- includeLazy(relationName) {
5096
- const nextLazy = new Set(this.lazyRelations);
5097
- nextLazy.add(relationName);
5098
- return this.clone(this.context, nextLazy);
5099
- }
5100
- /**
5101
- * Selects columns for a related table in a single hop.
5102
- */
5103
- selectRelationColumns(relationName, ...cols) {
6176
+ includeLazy(relationName, options) {
6177
+ let nextContext = this.context;
5104
6178
  const relation = this.env.table.relations[relationName];
5105
- if (!relation) {
5106
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
5107
- }
5108
- const target = relation.target;
5109
- for (const col2 of cols) {
5110
- if (!target.columns[col2]) {
5111
- throw new Error(
5112
- `Column '${col2}' not found on related table '${target.name}' for relation '${relationName}'`
5113
- );
6179
+ if (relation?.type === RelationKinds.BelongsTo) {
6180
+ const foreignKey = relation.foreignKey;
6181
+ const fkColumn = this.env.table.columns[foreignKey];
6182
+ if (fkColumn) {
6183
+ const hasAlias2 = nextContext.state.ast.columns.some((col2) => {
6184
+ const node = col2;
6185
+ return (node.alias ?? node.name) === foreignKey;
6186
+ });
6187
+ if (!hasAlias2) {
6188
+ nextContext = this.columnSelector.select(nextContext, { [foreignKey]: fkColumn });
6189
+ }
5114
6190
  }
5115
6191
  }
5116
- return this.include(relationName, { columns: cols });
6192
+ const nextLazy = new Set(this.lazyRelations);
6193
+ nextLazy.add(relationName);
6194
+ const nextOptions = new Map(this.lazyRelationOptions);
6195
+ if (options) {
6196
+ nextOptions.set(relationName, options);
6197
+ } else {
6198
+ nextOptions.delete(relationName);
6199
+ }
6200
+ return this.clone(nextContext, nextLazy, nextOptions);
5117
6201
  }
5118
6202
  /**
5119
- * Convenience alias for selecting specific columns from a relation.
6203
+ * Convenience alias for including only specific columns from a relation.
5120
6204
  */
5121
6205
  includePick(relationName, cols) {
5122
- return this.selectRelationColumns(relationName, ...cols);
6206
+ return this.include(relationName, { columns: cols });
5123
6207
  }
5124
6208
  /**
5125
6209
  * Selects columns for the root table and relations from an array of entries
@@ -5132,7 +6216,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5132
6216
  if (entry.type === "root") {
5133
6217
  currBuilder = currBuilder.select(...entry.columns);
5134
6218
  } else {
5135
- currBuilder = currBuilder.selectRelationColumns(entry.relationName, ...entry.columns);
6219
+ currBuilder = currBuilder.include(entry.relationName, { columns: entry.columns });
5136
6220
  }
5137
6221
  }
5138
6222
  return currBuilder;
@@ -5144,6 +6228,13 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5144
6228
  getLazyRelations() {
5145
6229
  return Array.from(this.lazyRelations);
5146
6230
  }
6231
+ /**
6232
+ * Gets lazy relation include options
6233
+ * @returns Map of relation names to include options
6234
+ */
6235
+ getLazyRelationOptions() {
6236
+ return new Map(this.lazyRelationOptions);
6237
+ }
5147
6238
  /**
5148
6239
  * Gets the table definition for this query builder
5149
6240
  * @returns Table definition
@@ -7568,6 +8659,14 @@ var repeat = (value, count2) => fn("REPEAT", [value, count2]);
7568
8659
  var lpad = (value, len, pad) => fn("LPAD", [value, len, pad]);
7569
8660
  var rpad = (value, len, pad) => fn("RPAD", [value, len, pad]);
7570
8661
  var space = (count2) => fn("SPACE", [count2]);
8662
+ var reverse = (value) => fn("REVERSE", [value]);
8663
+ var initcap = (value) => fn("INITCAP", [value]);
8664
+ var md5 = (value) => fn("MD5", [value]);
8665
+ var sha1 = (value) => fn("SHA1", [value]);
8666
+ var sha2 = (value, bits) => fn("SHA2", [value, bits]);
8667
+ var bitLength = (value) => fn("BIT_LENGTH", [value]);
8668
+ var octetLength = (value) => fn("OCTET_LENGTH", [value]);
8669
+ var chr = (code) => fn("CHR", [code]);
7571
8670
 
7572
8671
  // src/core/ddl/introspect/functions/mssql.ts
7573
8672
  var isColumnReference = (value) => typeof value === "object" && value !== null && !("type" in value) && "name" in value && typeof value.name === "string";
@@ -8126,6 +9225,8 @@ var sqrt = (value) => fn3("SQRT", [value]);
8126
9225
  var tan = (value) => fn3("TAN", [value]);
8127
9226
  var trunc = (value, decimals) => decimals === void 0 ? fn3("TRUNC", [value]) : fn3("TRUNC", [value, decimals]);
8128
9227
  var truncate = (value, decimals) => fn3("TRUNCATE", [value, decimals]);
9228
+ var log2 = (value) => fn3("LOG2", [value]);
9229
+ var cbrt = (value) => fn3("CBRT", [value]);
8129
9230
 
8130
9231
  // src/core/functions/datetime.ts
8131
9232
  var isColumnDef3 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
@@ -8137,12 +9238,15 @@ var toOperand4 = (input) => {
8137
9238
  var fn4 = (key, args) => ({
8138
9239
  type: "Function",
8139
9240
  name: key,
9241
+ fn: key,
8140
9242
  args: args.map(toOperand4)
8141
9243
  });
8142
9244
  var now = () => fn4("NOW", []);
8143
9245
  var currentDate = () => fn4("CURRENT_DATE", []);
8144
9246
  var currentTime = () => fn4("CURRENT_TIME", []);
8145
9247
  var utcNow = () => fn4("UTC_NOW", []);
9248
+ var localTime = () => fn4("LOCALTIME", []);
9249
+ var localTimestamp = () => fn4("LOCALTIMESTAMP", []);
8146
9250
  var extract = (part, date) => fn4("EXTRACT", [part, date]);
8147
9251
  var year = (date) => fn4("YEAR", [date]);
8148
9252
  var month = (date) => fn4("MONTH", [date]);
@@ -8157,6 +9261,72 @@ var endOfMonth = (date) => fn4("END_OF_MONTH", [date]);
8157
9261
  var dayOfWeek = (date) => fn4("DAY_OF_WEEK", [date]);
8158
9262
  var weekOfYear = (date) => fn4("WEEK_OF_YEAR", [date]);
8159
9263
  var dateTrunc = (part, date) => fn4("DATE_TRUNC", [part, date]);
9264
+ var age = (timestamp, baseTimestamp) => baseTimestamp === void 0 ? fn4("AGE", [timestamp]) : fn4("AGE", [timestamp, baseTimestamp]);
9265
+ var hour = (date) => fn4("HOUR", [date]);
9266
+ var minute = (date) => fn4("MINUTE", [date]);
9267
+ var second = (date) => fn4("SECOND", [date]);
9268
+ var quarter = (date) => fn4("QUARTER", [date]);
9269
+
9270
+ // src/core/functions/control-flow.ts
9271
+ var isColumnDef4 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9272
+ var toOperand5 = (input) => {
9273
+ if (isOperandNode(input)) return input;
9274
+ if (isColumnDef4(input)) return columnOperand(input);
9275
+ return valueToOperand(input);
9276
+ };
9277
+ var fn5 = (key, args) => ({
9278
+ type: "Function",
9279
+ name: key,
9280
+ fn: key,
9281
+ args: args.map(toOperand5)
9282
+ });
9283
+ var coalesce = (...args) => {
9284
+ if (args.length < 2) throw new Error("coalesce() expects at least 2 arguments");
9285
+ return fn5("COALESCE", args);
9286
+ };
9287
+ var nullif = (val1, val2) => fn5("NULLIF", [val1, val2]);
9288
+ var greatest = (...args) => {
9289
+ if (args.length < 2) throw new Error("greatest() expects at least 2 arguments");
9290
+ return fn5("GREATEST", args);
9291
+ };
9292
+ var least = (...args) => {
9293
+ if (args.length < 2) throw new Error("least() expects at least 2 arguments");
9294
+ return fn5("LEAST", args);
9295
+ };
9296
+ var ifNull = (val, defaultValue) => coalesce(val, defaultValue);
9297
+
9298
+ // src/core/functions/json.ts
9299
+ var isColumnDef5 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9300
+ var toOperand6 = (input) => {
9301
+ if (isOperandNode(input)) return input;
9302
+ if (isColumnDef5(input)) return columnOperand(input);
9303
+ return valueToOperand(input);
9304
+ };
9305
+ var fn6 = (key, args) => ({
9306
+ type: "Function",
9307
+ name: key,
9308
+ fn: key,
9309
+ args: args.map(toOperand6)
9310
+ });
9311
+ var jsonLength = (target, path) => path === void 0 ? fn6("JSON_LENGTH", [target]) : fn6("JSON_LENGTH", [target, path]);
9312
+ var jsonSet = (target, path, value) => fn6("JSON_SET", [target, path, value]);
9313
+ var jsonArrayAgg = (value) => fn6("JSON_ARRAYAGG", [value]);
9314
+ var jsonContains = (target, candidate, path) => path === void 0 ? fn6("JSON_CONTAINS", [target, candidate]) : fn6("JSON_CONTAINS", [target, candidate, path]);
9315
+
9316
+ // src/core/functions/array.ts
9317
+ var isColumnDef6 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9318
+ var toOperand7 = (input) => {
9319
+ if (isOperandNode(input)) return input;
9320
+ if (isColumnDef6(input)) return columnOperand(input);
9321
+ return valueToOperand(input);
9322
+ };
9323
+ var fn7 = (key, args) => ({
9324
+ type: "Function",
9325
+ name: key,
9326
+ fn: key,
9327
+ args: args.map(toOperand7)
9328
+ });
9329
+ var arrayAppend = (array, value) => fn7("ARRAY_APPEND", [array, value]);
8160
9330
 
8161
9331
  // src/orm/als.ts
8162
9332
  var AsyncLocalStorage = class {
@@ -8236,7 +9406,7 @@ var DefaultNamingStrategy = class {
8236
9406
  * @returns Capitalized table name (handles schema-qualified names)
8237
9407
  */
8238
9408
  tableToSymbol(table) {
8239
- const tableName = typeof table === "string" ? table : table.type === "DerivedTable" ? table.alias : table.name;
9409
+ const tableName = typeof table === "string" ? table : table.type === "DerivedTable" ? table.alias : table.type === "FunctionTable" ? table.alias ?? table.name : table.name;
8240
9410
  if (tableName.includes(".")) {
8241
9411
  return tableName.split(".").map((part) => this.capitalize(part)).join("");
8242
9412
  }
@@ -8392,6 +9562,7 @@ var TypeScriptGenerator = class {
8392
9562
  case "CaseExpression":
8393
9563
  case "WindowFunction":
8394
9564
  case "Cast":
9565
+ case "Collate":
8395
9566
  return this.printOperand(term);
8396
9567
  default:
8397
9568
  return this.printExpression(term);
@@ -8454,6 +9625,9 @@ var TypeScriptGenerator = class {
8454
9625
  visitCast(node) {
8455
9626
  return this.printCastOperand(node);
8456
9627
  }
9628
+ visitCollate(node) {
9629
+ return this.printCollateOperand(node);
9630
+ }
8457
9631
  visitAliasRef(node) {
8458
9632
  return `aliasRef('${node.name}')`;
8459
9633
  }
@@ -8465,12 +9639,12 @@ var TypeScriptGenerator = class {
8465
9639
  printBinaryExpression(binary) {
8466
9640
  const left2 = this.printOperand(binary.left);
8467
9641
  const right2 = this.printOperand(binary.right);
8468
- const fn5 = this.mapOp(binary.operator);
9642
+ const fn8 = this.mapOp(binary.operator);
8469
9643
  const args = [left2, right2];
8470
9644
  if (binary.escape) {
8471
9645
  args.push(this.printOperand(binary.escape));
8472
9646
  }
8473
- return `${fn5}(${args.join(", ")})`;
9647
+ return `${fn8}(${args.join(", ")})`;
8474
9648
  }
8475
9649
  /**
8476
9650
  * Prints a logical expression to TypeScript code
@@ -8499,13 +9673,13 @@ var TypeScriptGenerator = class {
8499
9673
  */
8500
9674
  printInExpression(inExpr) {
8501
9675
  const left2 = this.printOperand(inExpr.left);
8502
- const fn5 = this.mapOp(inExpr.operator);
9676
+ const fn8 = this.mapOp(inExpr.operator);
8503
9677
  if (Array.isArray(inExpr.right)) {
8504
9678
  const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
8505
- return `${fn5}(${left2}, [${values}])`;
9679
+ return `${fn8}(${left2}, [${values}])`;
8506
9680
  }
8507
9681
  const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
8508
- return `${fn5}(${left2}, (${subquery}))`;
9682
+ return `${fn8}(${left2}, (${subquery}))`;
8509
9683
  }
8510
9684
  /**
8511
9685
  * Prints a null expression to TypeScript code
@@ -8514,8 +9688,8 @@ var TypeScriptGenerator = class {
8514
9688
  */
8515
9689
  printNullExpression(nullExpr) {
8516
9690
  const left2 = this.printOperand(nullExpr.left);
8517
- const fn5 = this.mapOp(nullExpr.operator);
8518
- return `${fn5}(${left2})`;
9691
+ const fn8 = this.mapOp(nullExpr.operator);
9692
+ return `${fn8}(${left2})`;
8519
9693
  }
8520
9694
  /**
8521
9695
  * Prints a BETWEEN expression to TypeScript code
@@ -8559,9 +9733,9 @@ var TypeScriptGenerator = class {
8559
9733
  * @param fn - Function node
8560
9734
  * @returns TypeScript code representation
8561
9735
  */
8562
- printFunctionOperand(fn5) {
8563
- const args = fn5.args.map((a) => this.printOperand(a)).join(", ");
8564
- return `${fn5.name.toLowerCase()}(${args})`;
9736
+ printFunctionOperand(fn8) {
9737
+ const args = fn8.args.map((a) => this.printOperand(a)).join(", ");
9738
+ return `${fn8.name.toLowerCase()}(${args})`;
8565
9739
  }
8566
9740
  /**
8567
9741
  * Prints a JSON path operand to TypeScript code
@@ -8625,6 +9799,9 @@ var TypeScriptGenerator = class {
8625
9799
  const typeLiteral = node.castType.replace(/'/g, "\\'");
8626
9800
  return `cast(${this.printOperand(node.expression)}, '${typeLiteral}')`;
8627
9801
  }
9802
+ printCollateOperand(node) {
9803
+ return `collate(${this.printOperand(node.expression)}, '${node.collation}')`;
9804
+ }
8628
9805
  /**
8629
9806
  * Converts method chain lines to inline format
8630
9807
  * @param lines - Method chain lines
@@ -8655,10 +9832,20 @@ var IdentityMap = class {
8655
9832
  get bucketsMap() {
8656
9833
  return this.buckets;
8657
9834
  }
9835
+ /**
9836
+ * Retrieves an entity from the identity map if it exists.
9837
+ * @param table The table definition of the entity.
9838
+ * @param pk The primary key value.
9839
+ * @returns The entity instance if found, undefined otherwise.
9840
+ */
8658
9841
  getEntity(table, pk) {
8659
9842
  const bucket = this.buckets.get(table.name);
8660
9843
  return bucket?.get(this.toIdentityKey(pk))?.entity;
8661
9844
  }
9845
+ /**
9846
+ * Registers a tracked entity in the identity map.
9847
+ * @param tracked The tracked entity metadata and instance.
9848
+ */
8662
9849
  register(tracked) {
8663
9850
  if (tracked.pk == null) return;
8664
9851
  const bucket = this.buckets.get(tracked.table.name) ?? /* @__PURE__ */ new Map();
@@ -8670,6 +9857,11 @@ var IdentityMap = class {
8670
9857
  const bucket = this.buckets.get(tracked.table.name);
8671
9858
  bucket?.delete(this.toIdentityKey(tracked.pk));
8672
9859
  }
9860
+ /**
9861
+ * Returns all tracked entities for a specific table.
9862
+ * @param table The table definition.
9863
+ * @returns Array of tracked entities.
9864
+ */
8673
9865
  getEntitiesForTable(table) {
8674
9866
  const bucket = this.buckets.get(table.name);
8675
9867
  return bucket ? Array.from(bucket.values()) : [];
@@ -9794,15 +10986,15 @@ var OrmSession = class {
9794
10986
  * @returns The result of the function
9795
10987
  * @throws If the transaction fails
9796
10988
  */
9797
- async transaction(fn5) {
10989
+ async transaction(fn8) {
9798
10990
  if (!this.executor.capabilities.transactions) {
9799
- const result = await fn5(this);
10991
+ const result = await fn8(this);
9800
10992
  await this.commit();
9801
10993
  return result;
9802
10994
  }
9803
10995
  await this.executor.beginTransaction();
9804
10996
  try {
9805
- const result = await fn5(this);
10997
+ const result = await fn8(this);
9806
10998
  await this.flushWithHooks();
9807
10999
  await this.executor.commitTransaction();
9808
11000
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
@@ -9905,11 +11097,11 @@ var Orm = class {
9905
11097
  * @returns The result of the function
9906
11098
  * @throws If the transaction fails
9907
11099
  */
9908
- async transaction(fn5) {
11100
+ async transaction(fn8) {
9909
11101
  const executor = this.executorFactory.createTransactionalExecutor();
9910
11102
  const session = new OrmSession({ orm: this, executor });
9911
11103
  try {
9912
- return await session.transaction(() => fn5(session));
11104
+ return await session.transaction(() => fn8(session));
9913
11105
  } finally {
9914
11106
  await session.dispose();
9915
11107
  }
@@ -10734,8 +11926,10 @@ export {
10734
11926
  acos,
10735
11927
  add,
10736
11928
  addDomainEvent,
11929
+ age,
10737
11930
  aliasRef,
10738
11931
  and,
11932
+ arrayAppend,
10739
11933
  ascii,
10740
11934
  asin,
10741
11935
  atan,
@@ -10744,16 +11938,24 @@ export {
10744
11938
  belongsTo,
10745
11939
  belongsToMany,
10746
11940
  between,
11941
+ bitAnd,
11942
+ bitLength,
11943
+ bitOr,
11944
+ bitXor,
10747
11945
  bootstrapEntities,
10748
11946
  caseWhen,
10749
11947
  cast,
11948
+ cbrt,
10750
11949
  ceil,
10751
11950
  ceiling,
10752
11951
  char,
10753
11952
  charLength,
11953
+ chr,
10754
11954
  clearExpressionDispatchers,
10755
11955
  clearOperandDispatchers,
11956
+ coalesce,
10756
11957
  col,
11958
+ collate,
10757
11959
  columnOperand,
10758
11960
  concat,
10759
11961
  concatWs,
@@ -10805,18 +12007,23 @@ export {
10805
12007
  getDecoratorMetadata,
10806
12008
  getSchemaIntrospector,
10807
12009
  getTableDefFromEntity,
12010
+ greatest,
10808
12011
  groupConcat,
10809
12012
  gt,
10810
12013
  gte,
10811
12014
  hasMany,
10812
12015
  hasOne,
12016
+ hour,
10813
12017
  hydrateRows,
12018
+ ifNull,
10814
12019
  inList,
10815
12020
  inSubquery,
12021
+ initcap,
10816
12022
  instr,
10817
12023
  introspectSchema,
10818
12024
  isCaseExpressionNode,
10819
12025
  isCastExpressionNode,
12026
+ isCollateExpressionNode,
10820
12027
  isExpressionSelectionNode,
10821
12028
  isFunctionNode,
10822
12029
  isNotNull,
@@ -10824,11 +12031,16 @@ export {
10824
12031
  isOperandNode,
10825
12032
  isValueOperandInput,
10826
12033
  isWindowFunctionNode,
12034
+ jsonArrayAgg,
12035
+ jsonContains,
12036
+ jsonLength,
10827
12037
  jsonPath,
12038
+ jsonSet,
10828
12039
  jsonify,
10829
12040
  lag,
10830
12041
  lastValue,
10831
12042
  lead,
12043
+ least,
10832
12044
  left,
10833
12045
  length,
10834
12046
  like,
@@ -10837,9 +12049,12 @@ export {
10837
12049
  loadBelongsToRelation,
10838
12050
  loadHasManyRelation,
10839
12051
  loadHasOneRelation,
12052
+ localTime,
12053
+ localTimestamp,
10840
12054
  locate,
10841
12055
  log,
10842
12056
  log10,
12057
+ log2,
10843
12058
  logBase,
10844
12059
  lower,
10845
12060
  lpad,
@@ -10847,7 +12062,9 @@ export {
10847
12062
  lte,
10848
12063
  ltrim,
10849
12064
  max,
12065
+ md5,
10850
12066
  min,
12067
+ minute,
10851
12068
  mod,
10852
12069
  month,
10853
12070
  mul,
@@ -10860,12 +12077,15 @@ export {
10860
12077
  notLike,
10861
12078
  now,
10862
12079
  ntile,
12080
+ nullif,
12081
+ octetLength,
10863
12082
  or,
10864
12083
  outerRef,
10865
12084
  pi,
10866
12085
  position,
10867
12086
  pow,
10868
12087
  power,
12088
+ quarter,
10869
12089
  radians,
10870
12090
  rand,
10871
12091
  random,
@@ -10873,22 +12093,30 @@ export {
10873
12093
  registerExpressionDispatcher,
10874
12094
  registerOperandDispatcher,
10875
12095
  registerSchemaIntrospector,
12096
+ relationLoaderCache,
10876
12097
  renderColumnDefinition,
10877
12098
  renderTypeWithArgs,
10878
12099
  repeat,
10879
12100
  replace,
12101
+ reverse,
10880
12102
  right,
10881
12103
  round,
10882
12104
  rowNumber,
10883
12105
  rowsToQueryResult,
10884
12106
  rpad,
10885
12107
  rtrim,
12108
+ second,
10886
12109
  sel,
10887
12110
  selectFromEntity,
12111
+ sha1,
12112
+ sha2,
12113
+ shiftLeft,
12114
+ shiftRight,
10888
12115
  sign,
10889
12116
  sin,
10890
12117
  space,
10891
12118
  sqrt,
12119
+ stddev,
10892
12120
  sub,
10893
12121
  substr,
10894
12122
  sum,
@@ -10904,6 +12132,7 @@ export {
10904
12132
  upper,
10905
12133
  utcNow,
10906
12134
  valueToOperand,
12135
+ variance,
10907
12136
  visitExpression,
10908
12137
  visitOperand,
10909
12138
  weekOfYear,