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.cjs CHANGED
@@ -2,8 +2,8 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __esm = (fn5, res) => function __init() {
6
- return fn5 && (res = (0, fn5[__getOwnPropNames(fn5)[0]])(fn5 = 0)), res;
5
+ var __esm = (fn8, res) => function __init() {
6
+ return fn8 && (res = (0, fn8[__getOwnPropNames(fn8)[0]])(fn8 = 0)), res;
7
7
  };
8
8
  var __export = (target, all) => {
9
9
  for (var name in all)
@@ -74,8 +74,10 @@ __export(index_exports, {
74
74
  acos: () => acos,
75
75
  add: () => add,
76
76
  addDomainEvent: () => addDomainEvent,
77
+ age: () => age,
77
78
  aliasRef: () => aliasRef,
78
79
  and: () => and,
80
+ arrayAppend: () => arrayAppend,
79
81
  ascii: () => ascii,
80
82
  asin: () => asin,
81
83
  atan: () => atan,
@@ -84,16 +86,24 @@ __export(index_exports, {
84
86
  belongsTo: () => belongsTo,
85
87
  belongsToMany: () => belongsToMany,
86
88
  between: () => between,
89
+ bitAnd: () => bitAnd,
90
+ bitLength: () => bitLength,
91
+ bitOr: () => bitOr,
92
+ bitXor: () => bitXor,
87
93
  bootstrapEntities: () => bootstrapEntities,
88
94
  caseWhen: () => caseWhen,
89
95
  cast: () => cast,
96
+ cbrt: () => cbrt,
90
97
  ceil: () => ceil,
91
98
  ceiling: () => ceiling,
92
99
  char: () => char,
93
100
  charLength: () => charLength,
101
+ chr: () => chr,
94
102
  clearExpressionDispatchers: () => clearExpressionDispatchers,
95
103
  clearOperandDispatchers: () => clearOperandDispatchers,
104
+ coalesce: () => coalesce,
96
105
  col: () => col,
106
+ collate: () => collate,
97
107
  columnOperand: () => columnOperand,
98
108
  concat: () => concat,
99
109
  concatWs: () => concatWs,
@@ -145,18 +155,23 @@ __export(index_exports, {
145
155
  getDecoratorMetadata: () => getDecoratorMetadata,
146
156
  getSchemaIntrospector: () => getSchemaIntrospector,
147
157
  getTableDefFromEntity: () => getTableDefFromEntity,
158
+ greatest: () => greatest,
148
159
  groupConcat: () => groupConcat,
149
160
  gt: () => gt,
150
161
  gte: () => gte,
151
162
  hasMany: () => hasMany,
152
163
  hasOne: () => hasOne,
164
+ hour: () => hour,
153
165
  hydrateRows: () => hydrateRows,
166
+ ifNull: () => ifNull,
154
167
  inList: () => inList,
155
168
  inSubquery: () => inSubquery,
169
+ initcap: () => initcap,
156
170
  instr: () => instr,
157
171
  introspectSchema: () => introspectSchema,
158
172
  isCaseExpressionNode: () => isCaseExpressionNode,
159
173
  isCastExpressionNode: () => isCastExpressionNode,
174
+ isCollateExpressionNode: () => isCollateExpressionNode,
160
175
  isExpressionSelectionNode: () => isExpressionSelectionNode,
161
176
  isFunctionNode: () => isFunctionNode,
162
177
  isNotNull: () => isNotNull,
@@ -164,11 +179,16 @@ __export(index_exports, {
164
179
  isOperandNode: () => isOperandNode,
165
180
  isValueOperandInput: () => isValueOperandInput,
166
181
  isWindowFunctionNode: () => isWindowFunctionNode,
182
+ jsonArrayAgg: () => jsonArrayAgg,
183
+ jsonContains: () => jsonContains,
184
+ jsonLength: () => jsonLength,
167
185
  jsonPath: () => jsonPath,
186
+ jsonSet: () => jsonSet,
168
187
  jsonify: () => jsonify,
169
188
  lag: () => lag,
170
189
  lastValue: () => lastValue,
171
190
  lead: () => lead,
191
+ least: () => least,
172
192
  left: () => left,
173
193
  length: () => length,
174
194
  like: () => like,
@@ -177,9 +197,12 @@ __export(index_exports, {
177
197
  loadBelongsToRelation: () => loadBelongsToRelation,
178
198
  loadHasManyRelation: () => loadHasManyRelation,
179
199
  loadHasOneRelation: () => loadHasOneRelation,
200
+ localTime: () => localTime,
201
+ localTimestamp: () => localTimestamp,
180
202
  locate: () => locate,
181
203
  log: () => log,
182
204
  log10: () => log10,
205
+ log2: () => log2,
183
206
  logBase: () => logBase,
184
207
  lower: () => lower,
185
208
  lpad: () => lpad,
@@ -187,7 +210,9 @@ __export(index_exports, {
187
210
  lte: () => lte,
188
211
  ltrim: () => ltrim,
189
212
  max: () => max,
213
+ md5: () => md5,
190
214
  min: () => min,
215
+ minute: () => minute,
191
216
  mod: () => mod,
192
217
  month: () => month,
193
218
  mul: () => mul,
@@ -200,12 +225,15 @@ __export(index_exports, {
200
225
  notLike: () => notLike,
201
226
  now: () => now,
202
227
  ntile: () => ntile,
228
+ nullif: () => nullif,
229
+ octetLength: () => octetLength,
203
230
  or: () => or,
204
231
  outerRef: () => outerRef,
205
232
  pi: () => pi,
206
233
  position: () => position,
207
234
  pow: () => pow,
208
235
  power: () => power,
236
+ quarter: () => quarter,
209
237
  radians: () => radians,
210
238
  rand: () => rand,
211
239
  random: () => random,
@@ -213,22 +241,30 @@ __export(index_exports, {
213
241
  registerExpressionDispatcher: () => registerExpressionDispatcher,
214
242
  registerOperandDispatcher: () => registerOperandDispatcher,
215
243
  registerSchemaIntrospector: () => registerSchemaIntrospector,
244
+ relationLoaderCache: () => relationLoaderCache,
216
245
  renderColumnDefinition: () => renderColumnDefinition,
217
246
  renderTypeWithArgs: () => renderTypeWithArgs,
218
247
  repeat: () => repeat,
219
248
  replace: () => replace,
249
+ reverse: () => reverse,
220
250
  right: () => right,
221
251
  round: () => round,
222
252
  rowNumber: () => rowNumber,
223
253
  rowsToQueryResult: () => rowsToQueryResult,
224
254
  rpad: () => rpad,
225
255
  rtrim: () => rtrim,
256
+ second: () => second,
226
257
  sel: () => sel,
227
258
  selectFromEntity: () => selectFromEntity,
259
+ sha1: () => sha1,
260
+ sha2: () => sha2,
261
+ shiftLeft: () => shiftLeft,
262
+ shiftRight: () => shiftRight,
228
263
  sign: () => sign,
229
264
  sin: () => sin,
230
265
  space: () => space,
231
266
  sqrt: () => sqrt,
267
+ stddev: () => stddev,
232
268
  sub: () => sub,
233
269
  substr: () => substr,
234
270
  sum: () => sum,
@@ -244,6 +280,7 @@ __export(index_exports, {
244
280
  upper: () => upper,
245
281
  utcNow: () => utcNow,
246
282
  valueToOperand: () => valueToOperand,
283
+ variance: () => variance,
247
284
  visitExpression: () => visitExpression,
248
285
  visitOperand: () => visitOperand,
249
286
  weekOfYear: () => weekOfYear,
@@ -586,7 +623,9 @@ var operandTypes = /* @__PURE__ */ new Set([
586
623
  "CaseExpression",
587
624
  "Cast",
588
625
  "WindowFunction",
589
- "ArithmeticExpression"
626
+ "ArithmeticExpression",
627
+ "BitwiseExpression",
628
+ "Collate"
590
629
  ]);
591
630
  var hasTypeProperty = (value) => typeof value === "object" && value !== null && "type" in value;
592
631
  var isOperandNode = (node) => {
@@ -596,6 +635,7 @@ var isOperandNode = (node) => {
596
635
  var isFunctionNode = (node) => isOperandNode(node) && node.type === "Function";
597
636
  var isCaseExpressionNode = (node) => isOperandNode(node) && node.type === "CaseExpression";
598
637
  var isCastExpressionNode = (node) => isOperandNode(node) && node.type === "Cast";
638
+ var isCollateExpressionNode = (node) => isOperandNode(node) && node.type === "Collate";
599
639
  var isWindowFunctionNode = (node) => isOperandNode(node) && node.type === "WindowFunction";
600
640
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isCastExpressionNode(node) || isWindowFunctionNode(node);
601
641
 
@@ -720,6 +760,17 @@ var add = (left2, right2) => createArithmeticExpression("+", left2, right2);
720
760
  var sub = (left2, right2) => createArithmeticExpression("-", left2, right2);
721
761
  var mul = (left2, right2) => createArithmeticExpression("*", left2, right2);
722
762
  var div = (left2, right2) => createArithmeticExpression("/", left2, right2);
763
+ var createBitwiseExpression = (operator, left2, right2) => ({
764
+ type: "BitwiseExpression",
765
+ left: toOperand(left2),
766
+ operator,
767
+ right: toOperand(right2)
768
+ });
769
+ var bitAnd = (left2, right2) => createBitwiseExpression("&", left2, right2);
770
+ var bitOr = (left2, right2) => createBitwiseExpression("|", left2, right2);
771
+ var bitXor = (left2, right2) => createBitwiseExpression("^", left2, right2);
772
+ var shiftLeft = (left2, right2) => createBitwiseExpression("<<", left2, right2);
773
+ var shiftRight = (left2, right2) => createBitwiseExpression(">>", left2, right2);
723
774
  var jsonPath = (col2, path) => ({
724
775
  type: "JsonPath",
725
776
  column: columnOperand(col2),
@@ -748,6 +799,11 @@ var notExists = (subquery) => ({
748
799
  operator: "NOT EXISTS",
749
800
  subquery
750
801
  });
802
+ var collate = (expression, collation) => ({
803
+ type: "Collate",
804
+ expression: toOperand(expression),
805
+ collation
806
+ });
751
807
 
752
808
  // src/core/ast/window-functions.ts
753
809
  var buildWindowFunction = (name, args = [], partitionBy, orderBy) => {
@@ -893,6 +949,8 @@ var groupConcat = (col2, options) => ({
893
949
  orderBy: options?.orderBy?.map(toOrderByNode),
894
950
  separator: options?.separator !== void 0 ? valueToOperand(options.separator) : void 0
895
951
  });
952
+ var stddev = buildAggregate("STDDEV");
953
+ var variance = buildAggregate("VARIANCE");
896
954
 
897
955
  // src/core/ast/expression-visitor.ts
898
956
  var DispatcherRegistry = class _DispatcherRegistry {
@@ -966,6 +1024,9 @@ var visitExpression = (node, visitor) => {
966
1024
  case "ArithmeticExpression":
967
1025
  if (visitor.visitArithmeticExpression) return visitor.visitArithmeticExpression(node);
968
1026
  break;
1027
+ case "BitwiseExpression":
1028
+ if (visitor.visitBitwiseExpression) return visitor.visitBitwiseExpression(node);
1029
+ break;
969
1030
  default:
970
1031
  break;
971
1032
  }
@@ -1003,6 +1064,9 @@ var visitOperand = (node, visitor) => {
1003
1064
  case "Cast":
1004
1065
  if (visitor.visitCast) return visitor.visitCast(node);
1005
1066
  break;
1067
+ case "Collate":
1068
+ if (visitor.visitCollate) return visitor.visitCollate(node);
1069
+ break;
1006
1070
  default:
1007
1071
  break;
1008
1072
  }
@@ -1073,62 +1137,337 @@ var derivedTable = (query, alias, columnAliases) => ({
1073
1137
  columnAliases
1074
1138
  });
1075
1139
 
1140
+ // src/core/functions/function-registry.ts
1141
+ var FunctionRegistry = class {
1142
+ constructor() {
1143
+ this.renderers = /* @__PURE__ */ new Map();
1144
+ }
1145
+ /**
1146
+ * Registers or overrides a renderer for the given function name.
1147
+ */
1148
+ add(name, renderer) {
1149
+ this.renderers.set(name, renderer);
1150
+ }
1151
+ /**
1152
+ * Registers a batch of definitions.
1153
+ */
1154
+ register(definitions) {
1155
+ for (const definition of definitions) {
1156
+ this.add(definition.name, definition.renderer);
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Merges another registry into this one, allowing overrides from the other source.
1161
+ */
1162
+ merge(other) {
1163
+ for (const [name, renderer] of other.renderers.entries()) {
1164
+ this.renderers.set(name, renderer);
1165
+ }
1166
+ }
1167
+ /**
1168
+ * Retrieves a renderer by function name.
1169
+ */
1170
+ get(name) {
1171
+ return this.renderers.get(name);
1172
+ }
1173
+ };
1174
+
1175
+ // src/core/functions/definitions/helpers.ts
1176
+ function unaryRenderer(name) {
1177
+ return ({ compiledArgs }) => `${name}(${compiledArgs[0]})`;
1178
+ }
1179
+ function binaryRenderer(name) {
1180
+ return ({ compiledArgs }) => `${name}(${compiledArgs[0]}, ${compiledArgs[1]})`;
1181
+ }
1182
+ function variadicRenderer(name) {
1183
+ return ({ compiledArgs }) => `${name}(${compiledArgs.join(", ")})`;
1184
+ }
1185
+ function noArgsRenderer(name) {
1186
+ return () => `${name}()`;
1187
+ }
1188
+
1189
+ // src/core/functions/definitions/aggregate.ts
1190
+ var aggregateFunctionDefinitions = [
1191
+ {
1192
+ name: "COUNT",
1193
+ renderer: ({ compiledArgs }) => compiledArgs.length ? `COUNT(${compiledArgs.join(", ")})` : "COUNT(*)"
1194
+ },
1195
+ { name: "SUM", renderer: unaryRenderer("SUM") },
1196
+ { name: "AVG", renderer: unaryRenderer("AVG") },
1197
+ { name: "MIN", renderer: unaryRenderer("MIN") },
1198
+ { name: "MAX", renderer: unaryRenderer("MAX") },
1199
+ { name: "STDDEV", renderer: unaryRenderer("STDDEV") },
1200
+ { name: "VARIANCE", renderer: unaryRenderer("VARIANCE") }
1201
+ ];
1202
+
1203
+ // src/core/functions/definitions/string.ts
1204
+ var stringFunctionDefinitions = [
1205
+ { name: "UPPER", renderer: unaryRenderer("UPPER") },
1206
+ { name: "LOWER", renderer: unaryRenderer("LOWER") },
1207
+ { name: "LENGTH", renderer: unaryRenderer("LENGTH") },
1208
+ { name: "CHAR_LENGTH", renderer: unaryRenderer("CHAR_LENGTH") },
1209
+ { name: "CHARACTER_LENGTH", renderer: unaryRenderer("CHARACTER_LENGTH") },
1210
+ { name: "TRIM", renderer: unaryRenderer("TRIM") },
1211
+ { name: "LTRIM", renderer: unaryRenderer("LTRIM") },
1212
+ { name: "RTRIM", renderer: unaryRenderer("RTRIM") },
1213
+ { name: "SUBSTRING", renderer: variadicRenderer("SUBSTRING") },
1214
+ { name: "SUBSTR", renderer: variadicRenderer("SUBSTR") },
1215
+ { name: "CONCAT", renderer: variadicRenderer("CONCAT") },
1216
+ { name: "CONCAT_WS", renderer: variadicRenderer("CONCAT_WS") },
1217
+ { name: "ASCII", renderer: unaryRenderer("ASCII") },
1218
+ { name: "CHAR", renderer: variadicRenderer("CHAR") },
1219
+ {
1220
+ name: "POSITION",
1221
+ renderer: ({ compiledArgs }) => `POSITION(${compiledArgs[0]} IN ${compiledArgs[1]})`
1222
+ },
1223
+ { name: "REPLACE", renderer: ({ compiledArgs }) => `REPLACE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
1224
+ { name: "REPEAT", renderer: binaryRenderer("REPEAT") },
1225
+ { name: "LPAD", renderer: ({ compiledArgs }) => `LPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
1226
+ { name: "RPAD", renderer: ({ compiledArgs }) => `RPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
1227
+ { name: "LEFT", renderer: binaryRenderer("LEFT") },
1228
+ { name: "RIGHT", renderer: binaryRenderer("RIGHT") },
1229
+ { name: "INSTR", renderer: binaryRenderer("INSTR") },
1230
+ {
1231
+ name: "LOCATE",
1232
+ renderer: ({ compiledArgs }) => compiledArgs.length === 3 ? `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` : `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
1233
+ },
1234
+ { name: "SPACE", renderer: unaryRenderer("SPACE") },
1235
+ { name: "REVERSE", renderer: unaryRenderer("REVERSE") },
1236
+ { name: "INITCAP", renderer: unaryRenderer("INITCAP") },
1237
+ { name: "MD5", renderer: unaryRenderer("MD5") },
1238
+ { name: "SHA1", renderer: unaryRenderer("SHA1") },
1239
+ { name: "SHA2", renderer: ({ compiledArgs }) => `SHA2(${compiledArgs[0]}, ${compiledArgs[1]})` }
1240
+ ];
1241
+
1242
+ // src/core/functions/definitions/datetime.ts
1243
+ var dateTimeFunctionDefinitions = [
1244
+ { name: "NOW", renderer: noArgsRenderer("NOW") },
1245
+ { name: "CURRENT_DATE", renderer: () => "CURRENT_DATE" },
1246
+ { name: "CURRENT_TIME", renderer: () => "CURRENT_TIME" },
1247
+ {
1248
+ name: "EXTRACT",
1249
+ renderer: ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`
1250
+ },
1251
+ { name: "YEAR", renderer: ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})` },
1252
+ { name: "MONTH", renderer: ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})` },
1253
+ { name: "DAY", renderer: ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})` },
1254
+ { name: "HOUR", renderer: ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})` },
1255
+ { name: "MINUTE", renderer: ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})` },
1256
+ { name: "SECOND", renderer: ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})` },
1257
+ { name: "QUARTER", renderer: ({ compiledArgs }) => `EXTRACT(QUARTER FROM ${compiledArgs[0]})` },
1258
+ { name: "DATE_ADD", renderer: ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
1259
+ { name: "DATE_SUB", renderer: ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
1260
+ { name: "DATE_DIFF", renderer: ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})` },
1261
+ { name: "DATE_FORMAT", renderer: ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})` },
1262
+ { name: "UNIX_TIMESTAMP", renderer: noArgsRenderer("UNIX_TIMESTAMP") },
1263
+ { name: "FROM_UNIXTIME", renderer: ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})` },
1264
+ { name: "END_OF_MONTH", renderer: ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})` },
1265
+ { name: "DAY_OF_WEEK", renderer: ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})` },
1266
+ { name: "WEEK_OF_YEAR", renderer: ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})` },
1267
+ { name: "DATE_TRUNC", renderer: ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` },
1268
+ {
1269
+ name: "AGE",
1270
+ renderer: ({ compiledArgs }) => compiledArgs.length === 1 ? `AGE(${compiledArgs[0]})` : `AGE(${compiledArgs[0]}, ${compiledArgs[1]})`
1271
+ },
1272
+ { name: "LOCALTIME", renderer: () => "LOCALTIME" },
1273
+ { name: "LOCALTIMESTAMP", renderer: () => "LOCALTIMESTAMP" }
1274
+ ];
1275
+
1276
+ // src/core/functions/definitions/numeric.ts
1277
+ var numericFunctionDefinitions = [
1278
+ { name: "ABS", renderer: unaryRenderer("ABS") },
1279
+ { name: "BIT_LENGTH", renderer: unaryRenderer("BIT_LENGTH") },
1280
+ { name: "OCTET_LENGTH", renderer: unaryRenderer("OCTET_LENGTH") },
1281
+ { name: "CHR", renderer: unaryRenderer("CHR") },
1282
+ { name: "LOG2", renderer: unaryRenderer("LOG2") },
1283
+ { name: "CBRT", renderer: unaryRenderer("CBRT") },
1284
+ { name: "ACOS", renderer: unaryRenderer("ACOS") },
1285
+ { name: "ASIN", renderer: unaryRenderer("ASIN") },
1286
+ { name: "ATAN", renderer: unaryRenderer("ATAN") },
1287
+ { name: "ATAN2", renderer: binaryRenderer("ATAN2") },
1288
+ { name: "CEIL", renderer: unaryRenderer("CEIL") },
1289
+ { name: "CEILING", renderer: unaryRenderer("CEILING") },
1290
+ { name: "COS", renderer: unaryRenderer("COS") },
1291
+ { name: "COT", renderer: unaryRenderer("COT") },
1292
+ { name: "DEGREES", renderer: unaryRenderer("DEGREES") },
1293
+ { name: "EXP", renderer: unaryRenderer("EXP") },
1294
+ { name: "FLOOR", renderer: unaryRenderer("FLOOR") },
1295
+ { name: "LN", renderer: unaryRenderer("LN") },
1296
+ {
1297
+ name: "LOG",
1298
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `LOG(${compiledArgs[0]}, ${compiledArgs[1]})` : `LOG(${compiledArgs[0]})`
1299
+ },
1300
+ { name: "LOG10", renderer: unaryRenderer("LOG10") },
1301
+ { name: "LOG_BASE", renderer: binaryRenderer("LOG") },
1302
+ { name: "MOD", renderer: binaryRenderer("MOD") },
1303
+ { name: "PI", renderer: noArgsRenderer("PI") },
1304
+ { name: "POWER", renderer: binaryRenderer("POWER") },
1305
+ { name: "POW", renderer: binaryRenderer("POW") },
1306
+ { name: "RADIANS", renderer: unaryRenderer("RADIANS") },
1307
+ { name: "RANDOM", renderer: noArgsRenderer("RANDOM") },
1308
+ { name: "RAND", renderer: noArgsRenderer("RAND") },
1309
+ {
1310
+ name: "ROUND",
1311
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `ROUND(${compiledArgs[0]}, ${compiledArgs[1]})` : `ROUND(${compiledArgs[0]})`
1312
+ },
1313
+ { name: "SIGN", renderer: unaryRenderer("SIGN") },
1314
+ { name: "SIN", renderer: unaryRenderer("SIN") },
1315
+ { name: "SQRT", renderer: unaryRenderer("SQRT") },
1316
+ { name: "TAN", renderer: unaryRenderer("TAN") },
1317
+ {
1318
+ name: "TRUNC",
1319
+ renderer: ({ compiledArgs }) => compiledArgs.length === 2 ? `TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` : `TRUNC(${compiledArgs[0]})`
1320
+ },
1321
+ {
1322
+ name: "TRUNCATE",
1323
+ renderer: ({ compiledArgs }) => `TRUNCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
1324
+ }
1325
+ ];
1326
+
1327
+ // src/core/functions/definitions/control-flow.ts
1328
+ var controlFlowFunctionDefinitions = [
1329
+ {
1330
+ name: "COALESCE",
1331
+ renderer: ({ compiledArgs }) => `COALESCE(${compiledArgs.join(", ")})`
1332
+ },
1333
+ {
1334
+ name: "NULLIF",
1335
+ renderer: ({ compiledArgs }) => `NULLIF(${compiledArgs[0]}, ${compiledArgs[1]})`
1336
+ },
1337
+ {
1338
+ name: "GREATEST",
1339
+ renderer: ({ compiledArgs }) => `GREATEST(${compiledArgs.join(", ")})`
1340
+ },
1341
+ {
1342
+ name: "LEAST",
1343
+ renderer: ({ compiledArgs }) => `LEAST(${compiledArgs.join(", ")})`
1344
+ },
1345
+ {
1346
+ name: "IFNULL",
1347
+ renderer: ({ compiledArgs }) => `IFNULL(${compiledArgs[0]}, ${compiledArgs[1]})`
1348
+ }
1349
+ ];
1350
+
1351
+ // src/core/functions/definitions/json.ts
1352
+ var jsonFunctionDefinitions = [
1353
+ {
1354
+ name: "JSON_LENGTH",
1355
+ renderer: ({ compiledArgs }) => {
1356
+ if (compiledArgs.length === 0 || compiledArgs.length > 2) {
1357
+ throw new Error("JSON_LENGTH expects 1 or 2 arguments");
1358
+ }
1359
+ return `JSON_LENGTH(${compiledArgs.join(", ")})`;
1360
+ }
1361
+ },
1362
+ {
1363
+ name: "JSON_SET",
1364
+ renderer: ({ compiledArgs }) => {
1365
+ if (compiledArgs.length < 3 || (compiledArgs.length - 1) % 2 !== 0) {
1366
+ throw new Error("JSON_SET expects a JSON document followed by one or more path/value pairs");
1367
+ }
1368
+ return `JSON_SET(${compiledArgs.join(", ")})`;
1369
+ }
1370
+ },
1371
+ {
1372
+ name: "JSON_ARRAYAGG",
1373
+ renderer: ({ compiledArgs }) => {
1374
+ if (compiledArgs.length !== 1) {
1375
+ throw new Error("JSON_ARRAYAGG expects exactly one argument");
1376
+ }
1377
+ return `JSON_ARRAYAGG(${compiledArgs[0]})`;
1378
+ }
1379
+ },
1380
+ {
1381
+ name: "JSON_CONTAINS",
1382
+ renderer: ({ compiledArgs }) => {
1383
+ if (compiledArgs.length < 2 || compiledArgs.length > 3) {
1384
+ throw new Error("JSON_CONTAINS expects two or three arguments");
1385
+ }
1386
+ return `JSON_CONTAINS(${compiledArgs.join(", ")})`;
1387
+ }
1388
+ },
1389
+ {
1390
+ name: "ARRAY_APPEND",
1391
+ renderer: ({ compiledArgs }) => {
1392
+ if (compiledArgs.length !== 2) {
1393
+ throw new Error("ARRAY_APPEND expects exactly two arguments");
1394
+ }
1395
+ return `ARRAY_APPEND(${compiledArgs[0]}, ${compiledArgs[1]})`;
1396
+ }
1397
+ }
1398
+ ];
1399
+
1400
+ // src/core/functions/group-concat-helpers.ts
1401
+ var DEFAULT_GROUP_CONCAT_SEPARATOR = {
1402
+ type: "Literal",
1403
+ value: ","
1404
+ };
1405
+ function buildGroupConcatOrderBy(ctx) {
1406
+ const orderBy = ctx.node.orderBy;
1407
+ if (!orderBy || orderBy.length === 0) {
1408
+ return "";
1409
+ }
1410
+ const parts = orderBy.map((order) => {
1411
+ const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
1412
+ throw new Error("ORDER BY expressions inside functions must be operands");
1413
+ })();
1414
+ const collation = order.collation ? ` COLLATE ${order.collation}` : "";
1415
+ const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
1416
+ return `${term} ${order.direction}${collation}${nulls}`;
1417
+ });
1418
+ return `ORDER BY ${parts.join(", ")}`;
1419
+ }
1420
+ function formatGroupConcatSeparator(ctx) {
1421
+ if (!ctx.node.separator) {
1422
+ return "";
1423
+ }
1424
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
1425
+ }
1426
+ function getGroupConcatSeparatorOperand(ctx) {
1427
+ return ctx.node.separator ?? DEFAULT_GROUP_CONCAT_SEPARATOR;
1428
+ }
1429
+ function renderStandardGroupConcat(ctx) {
1430
+ const arg = ctx.compiledArgs[0];
1431
+ const orderClause = buildGroupConcatOrderBy(ctx);
1432
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1433
+ const separatorClause = formatGroupConcatSeparator(ctx);
1434
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
1435
+ }
1436
+
1076
1437
  // src/core/functions/standard-strategy.ts
1077
- var StandardFunctionStrategy = class _StandardFunctionStrategy {
1438
+ var StandardFunctionStrategy = class {
1078
1439
  /**
1079
1440
  * Creates a new StandardFunctionStrategy and registers standard functions.
1080
1441
  */
1081
- constructor() {
1082
- this.renderers = /* @__PURE__ */ new Map();
1442
+ constructor(registry2) {
1443
+ this.registry = registry2 ?? new FunctionRegistry();
1083
1444
  this.registerStandard();
1084
1445
  }
1085
1446
  registerStandard() {
1086
- this.add("COUNT", ({ compiledArgs }) => compiledArgs.length ? `COUNT(${compiledArgs.join(", ")})` : "COUNT(*)");
1087
- this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
1088
- this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
1089
- this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
1090
- this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
1091
- this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
1092
- this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
1093
- this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
1094
- this.add("LENGTH", ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
1095
- this.add("TRIM", ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
1096
- this.add("LTRIM", ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
1097
- this.add("RTRIM", ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
1098
- this.add("SUBSTRING", ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(", ")})`);
1099
- this.add("CONCAT", ({ compiledArgs }) => `CONCAT(${compiledArgs.join(", ")})`);
1100
- this.add("NOW", () => `NOW()`);
1101
- this.add("CURRENT_DATE", () => `CURRENT_DATE`);
1102
- this.add("CURRENT_TIME", () => `CURRENT_TIME`);
1103
- this.add("EXTRACT", ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
1104
- this.add("YEAR", ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
1105
- this.add("MONTH", ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
1106
- this.add("DAY", ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
1107
- this.add("DATE_ADD", ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
1108
- this.add("DATE_SUB", ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
1109
- this.add("DATE_DIFF", ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
1110
- this.add("DATE_FORMAT", ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
1111
- this.add("UNIX_TIMESTAMP", () => `UNIX_TIMESTAMP()`);
1112
- this.add("FROM_UNIXTIME", ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
1113
- this.add("END_OF_MONTH", ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
1114
- this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
1115
- this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
1116
- this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
1447
+ this.registerDefinitions(aggregateFunctionDefinitions);
1448
+ this.registerDefinitions(stringFunctionDefinitions);
1449
+ this.registerDefinitions(dateTimeFunctionDefinitions);
1450
+ this.registerDefinitions(numericFunctionDefinitions);
1451
+ this.registerDefinitions(controlFlowFunctionDefinitions);
1452
+ this.registerDefinitions(jsonFunctionDefinitions);
1117
1453
  this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
1118
1454
  }
1455
+ registerDefinitions(definitions) {
1456
+ this.registry.register(definitions);
1457
+ }
1119
1458
  /**
1120
1459
  * Registers a renderer for a function name.
1121
1460
  * @param name - The function name.
1122
1461
  * @param renderer - The renderer function.
1123
1462
  */
1124
1463
  add(name, renderer) {
1125
- this.renderers.set(name, renderer);
1464
+ this.registry.add(name, renderer);
1126
1465
  }
1127
1466
  /**
1128
1467
  * @inheritDoc
1129
1468
  */
1130
1469
  getRenderer(name) {
1131
- return this.renderers.get(name);
1470
+ return this.registry.get(name);
1132
1471
  }
1133
1472
  /**
1134
1473
  * Renders the GROUP_CONCAT function with optional ORDER BY and SEPARATOR.
@@ -1136,11 +1475,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
1136
1475
  * @returns The rendered SQL string.
1137
1476
  */
1138
1477
  renderGroupConcat(ctx) {
1139
- const arg = ctx.compiledArgs[0];
1140
- const orderClause = this.buildOrderByExpression(ctx);
1141
- const orderSegment = orderClause ? ` ${orderClause}` : "";
1142
- const separatorClause = this.formatGroupConcatSeparator(ctx);
1143
- return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
1478
+ return renderStandardGroupConcat(ctx);
1144
1479
  }
1145
1480
  /**
1146
1481
  * Builds the ORDER BY clause for functions like GROUP_CONCAT.
@@ -1148,19 +1483,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
1148
1483
  * @returns The ORDER BY SQL clause or empty string.
1149
1484
  */
1150
1485
  buildOrderByExpression(ctx) {
1151
- const orderBy = ctx.node.orderBy;
1152
- if (!orderBy || orderBy.length === 0) {
1153
- return "";
1154
- }
1155
- const parts = orderBy.map((order) => {
1156
- const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
1157
- throw new Error("ORDER BY expressions inside functions must be operands");
1158
- })();
1159
- const collation = order.collation ? ` COLLATE ${order.collation}` : "";
1160
- const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
1161
- return `${term} ${order.direction}${collation}${nulls}`;
1162
- });
1163
- return `ORDER BY ${parts.join(", ")}`;
1486
+ return buildGroupConcatOrderBy(ctx);
1164
1487
  }
1165
1488
  /**
1166
1489
  * Formats the SEPARATOR clause for GROUP_CONCAT.
@@ -1168,10 +1491,7 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
1168
1491
  * @returns The SEPARATOR SQL clause or empty string.
1169
1492
  */
1170
1493
  formatGroupConcatSeparator(ctx) {
1171
- if (!ctx.node.separator) {
1172
- return "";
1173
- }
1174
- return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
1494
+ return formatGroupConcatSeparator(ctx);
1175
1495
  }
1176
1496
  /**
1177
1497
  * Gets the separator operand for GROUP_CONCAT, defaulting to comma.
@@ -1179,14 +1499,24 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
1179
1499
  * @returns The separator operand.
1180
1500
  */
1181
1501
  getGroupConcatSeparatorOperand(ctx) {
1182
- return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
1502
+ return getGroupConcatSeparatorOperand(ctx);
1183
1503
  }
1184
1504
  static {
1185
1505
  /** Default separator for GROUP_CONCAT, a comma. */
1186
- this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
1187
- type: "Literal",
1188
- value: ","
1189
- };
1506
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
1507
+ }
1508
+ };
1509
+
1510
+ // src/core/functions/standard-table-strategy.ts
1511
+ var StandardTableFunctionStrategy = class {
1512
+ constructor() {
1513
+ this.renderers = /* @__PURE__ */ new Map();
1514
+ }
1515
+ add(key, renderer) {
1516
+ this.renderers.set(key, renderer);
1517
+ }
1518
+ getRenderer(key) {
1519
+ return this.renderers.get(key);
1190
1520
  }
1191
1521
  };
1192
1522
 
@@ -1360,10 +1690,11 @@ var Dialect = class _Dialect {
1360
1690
  const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
1361
1691
  return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
1362
1692
  }
1363
- constructor(functionStrategy) {
1693
+ constructor(functionStrategy, tableFunctionStrategy) {
1364
1694
  this.expressionCompilers = /* @__PURE__ */ new Map();
1365
1695
  this.operandCompilers = /* @__PURE__ */ new Map();
1366
1696
  this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
1697
+ this.tableFunctionStrategy = tableFunctionStrategy || new StandardTableFunctionStrategy();
1367
1698
  this.registerDefaultOperandCompilers();
1368
1699
  this.registerDefaultExpressionCompilers();
1369
1700
  }
@@ -1372,7 +1703,7 @@ var Dialect = class _Dialect {
1372
1703
  * @param functionStrategy - Optional function strategy
1373
1704
  * @returns New Dialect instance
1374
1705
  */
1375
- static create(functionStrategy) {
1706
+ static create(functionStrategy, tableFunctionStrategy) {
1376
1707
  class TestDialect extends _Dialect {
1377
1708
  constructor() {
1378
1709
  super(...arguments);
@@ -1394,7 +1725,7 @@ var Dialect = class _Dialect {
1394
1725
  throw new Error("Not implemented");
1395
1726
  }
1396
1727
  }
1397
- return new TestDialect(functionStrategy);
1728
+ return new TestDialect(functionStrategy, tableFunctionStrategy);
1398
1729
  }
1399
1730
  /**
1400
1731
  * Registers an expression compiler for a specific node type
@@ -1495,6 +1826,11 @@ var Dialect = class _Dialect {
1495
1826
  const right2 = this.compileOperand(arith.right, ctx);
1496
1827
  return `${left2} ${arith.operator} ${right2}`;
1497
1828
  });
1829
+ this.registerExpressionCompiler("BitwiseExpression", (bitwise, ctx) => {
1830
+ const left2 = this.compileOperand(bitwise.left, ctx);
1831
+ const right2 = this.compileOperand(bitwise.right, ctx);
1832
+ return `${left2} ${bitwise.operator} ${right2}`;
1833
+ });
1498
1834
  }
1499
1835
  registerDefaultOperandCompilers() {
1500
1836
  this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
@@ -1564,6 +1900,15 @@ var Dialect = class _Dialect {
1564
1900
  const right2 = this.compileOperand(node.right, ctx);
1565
1901
  return `(${left2} ${node.operator} ${right2})`;
1566
1902
  });
1903
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
1904
+ const left2 = this.compileOperand(node.left, ctx);
1905
+ const right2 = this.compileOperand(node.right, ctx);
1906
+ return `(${left2} ${node.operator} ${right2})`;
1907
+ });
1908
+ this.registerOperandCompiler("Collate", (node, ctx) => {
1909
+ const expr = this.compileOperand(node.expression, ctx);
1910
+ return `${expr} COLLATE ${node.collation}`;
1911
+ });
1567
1912
  }
1568
1913
  // Default fallback, should be overridden by dialects if supported
1569
1914
  compileJsonPath(_node) {
@@ -1596,13 +1941,13 @@ var FunctionTableFormatter = class {
1596
1941
  * @param dialect - The dialect instance for compiling operands.
1597
1942
  * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
1598
1943
  */
1599
- static format(fn5, ctx, dialect) {
1600
- const schemaPart = this.formatSchema(fn5, dialect);
1601
- const args = this.formatArgs(fn5, ctx, dialect);
1602
- const base = this.formatBase(fn5, schemaPart, args, dialect);
1603
- const lateral = this.formatLateral(fn5);
1604
- const alias = this.formatAlias(fn5, dialect);
1605
- const colAliases = this.formatColumnAliases(fn5, dialect);
1944
+ static format(fn8, ctx, dialect) {
1945
+ const schemaPart = this.formatSchema(fn8, dialect);
1946
+ const args = this.formatArgs(fn8, ctx, dialect);
1947
+ const base = this.formatBase(fn8, schemaPart, args);
1948
+ const lateral = this.formatLateral(fn8);
1949
+ const alias = this.formatAlias(fn8, dialect);
1950
+ const colAliases = this.formatColumnAliases(fn8, dialect);
1606
1951
  return `${lateral}${base}${alias}${colAliases}`;
1607
1952
  }
1608
1953
  /**
@@ -1612,9 +1957,9 @@ var FunctionTableFormatter = class {
1612
1957
  * @returns Schema prefix (e.g., "schema.") or empty string.
1613
1958
  * @internal
1614
1959
  */
1615
- static formatSchema(fn5, dialect) {
1616
- if (!fn5.schema) return "";
1617
- const quoted = dialect ? dialect.quoteIdentifier(fn5.schema) : fn5.schema;
1960
+ static formatSchema(fn8, dialect) {
1961
+ if (!fn8.schema) return "";
1962
+ const quoted = dialect ? dialect.quoteIdentifier(fn8.schema) : fn8.schema;
1618
1963
  return `${quoted}.`;
1619
1964
  }
1620
1965
  /**
@@ -1625,8 +1970,8 @@ var FunctionTableFormatter = class {
1625
1970
  * @returns Comma-separated function arguments.
1626
1971
  * @internal
1627
1972
  */
1628
- static formatArgs(fn5, ctx, dialect) {
1629
- return (fn5.args || []).map((a) => {
1973
+ static formatArgs(fn8, ctx, dialect) {
1974
+ return (fn8.args || []).map((a) => {
1630
1975
  if (ctx && dialect) {
1631
1976
  return dialect.compileOperand(a, ctx);
1632
1977
  }
@@ -1642,10 +1987,9 @@ var FunctionTableFormatter = class {
1642
1987
  * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
1643
1988
  * @internal
1644
1989
  */
1645
- static formatBase(fn5, schemaPart, args, dialect) {
1646
- const ordinality = fn5.withOrdinality ? " WITH ORDINALITY" : "";
1647
- const quoted = dialect ? dialect.quoteIdentifier(fn5.name) : fn5.name;
1648
- return `${schemaPart}${quoted}(${args})${ordinality}`;
1990
+ static formatBase(fn8, schemaPart, args) {
1991
+ const ordinality = fn8.withOrdinality ? " WITH ORDINALITY" : "";
1992
+ return `${schemaPart}${fn8.name}(${args})${ordinality}`;
1649
1993
  }
1650
1994
  /**
1651
1995
  * Formats the LATERAL keyword if present.
@@ -1653,8 +1997,8 @@ var FunctionTableFormatter = class {
1653
1997
  * @returns "LATERAL " or empty string.
1654
1998
  * @internal
1655
1999
  */
1656
- static formatLateral(fn5) {
1657
- return fn5.lateral ? "LATERAL " : "";
2000
+ static formatLateral(fn8) {
2001
+ return fn8.lateral ? "LATERAL " : "";
1658
2002
  }
1659
2003
  /**
1660
2004
  * Formats the table alias for the function table.
@@ -1663,9 +2007,9 @@ var FunctionTableFormatter = class {
1663
2007
  * @returns " AS alias" or empty string.
1664
2008
  * @internal
1665
2009
  */
1666
- static formatAlias(fn5, dialect) {
1667
- if (!fn5.alias) return "";
1668
- const quoted = dialect ? dialect.quoteIdentifier(fn5.alias) : fn5.alias;
2010
+ static formatAlias(fn8, dialect) {
2011
+ if (!fn8.alias) return "";
2012
+ const quoted = dialect ? dialect.quoteIdentifier(fn8.alias) : fn8.alias;
1669
2013
  return ` AS ${quoted}`;
1670
2014
  }
1671
2015
  /**
@@ -1675,9 +2019,9 @@ var FunctionTableFormatter = class {
1675
2019
  * @returns "(col1, col2, ...)" or empty string.
1676
2020
  * @internal
1677
2021
  */
1678
- static formatColumnAliases(fn5, dialect) {
1679
- if (!fn5.columnAliases || !fn5.columnAliases.length) return "";
1680
- const aliases = fn5.columnAliases.map((col2) => dialect ? dialect.quoteIdentifier(col2) : col2).join(", ");
2022
+ static formatColumnAliases(fn8, dialect) {
2023
+ if (!fn8.columnAliases || !fn8.columnAliases.length) return "";
2024
+ const aliases = fn8.columnAliases.map((col2) => dialect ? dialect.quoteIdentifier(col2) : col2).join(", ");
1681
2025
  return `(${aliases})`;
1682
2026
  }
1683
2027
  };
@@ -1945,8 +2289,24 @@ var SqlDialectBase = class extends Dialect {
1945
2289
  }
1946
2290
  return this.compileTableSource(tableSource);
1947
2291
  }
1948
- compileFunctionTable(fn5, ctx) {
1949
- return FunctionTableFormatter.format(fn5, ctx, this);
2292
+ compileFunctionTable(fn8, ctx) {
2293
+ const key = fn8.key ?? fn8.name;
2294
+ if (ctx) {
2295
+ const renderer = this.tableFunctionStrategy.getRenderer(key);
2296
+ if (renderer) {
2297
+ const compiledArgs = (fn8.args ?? []).map((arg) => this.compileOperand(arg, ctx));
2298
+ return renderer({
2299
+ node: fn8,
2300
+ compiledArgs,
2301
+ compileOperand: (operand) => this.compileOperand(operand, ctx),
2302
+ quoteIdentifier: this.quoteIdentifier.bind(this)
2303
+ });
2304
+ }
2305
+ if (fn8.key) {
2306
+ throw new Error(`Table function "${key}" is not supported by dialect "${this.dialect}".`);
2307
+ }
2308
+ }
2309
+ return FunctionTableFormatter.format(fn8, ctx, this);
1950
2310
  }
1951
2311
  compileDerivedTable(table, ctx) {
1952
2312
  if (!table.alias) {
@@ -2105,6 +2465,76 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
2105
2465
  const separator = ctx.compileOperand(separatorOperand);
2106
2466
  return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
2107
2467
  });
2468
+ this.add("CHR", ({ compiledArgs }) => `CHR(${compiledArgs[0]})`);
2469
+ this.add("HOUR", ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})`);
2470
+ this.add("MINUTE", ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})`);
2471
+ this.add("SECOND", ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})`);
2472
+ this.add("QUARTER", ({ compiledArgs }) => `EXTRACT(QUARTER FROM ${compiledArgs[0]})`);
2473
+ this.add("JSON_LENGTH", ({ compiledArgs }) => {
2474
+ if (compiledArgs.length !== 1) throw new Error("JSON_LENGTH expects 1 argument on PostgreSQL");
2475
+ return `jsonb_array_length(${compiledArgs[0]})`;
2476
+ });
2477
+ this.add("JSON_ARRAYAGG", ({ compiledArgs }) => {
2478
+ if (compiledArgs.length !== 1) throw new Error("JSON_ARRAYAGG expects 1 argument on PostgreSQL");
2479
+ return `jsonb_agg(${compiledArgs[0]})`;
2480
+ });
2481
+ this.add("JSON_CONTAINS", ({ compiledArgs }) => {
2482
+ if (compiledArgs.length !== 2) throw new Error("JSON_CONTAINS expects 2 arguments on PostgreSQL");
2483
+ return `(${compiledArgs[0]}::jsonb @> ${compiledArgs[1]}::jsonb)`;
2484
+ });
2485
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2486
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments on PostgreSQL");
2487
+ return `array_append(${compiledArgs[0]}, ${compiledArgs[1]})`;
2488
+ });
2489
+ this.add("JSON_SET", ({ node, compiledArgs }) => {
2490
+ if (compiledArgs.length !== 3) throw new Error("JSON_SET expects exactly 3 arguments on PostgreSQL");
2491
+ const pathNode = node.args[1];
2492
+ if (pathNode.type !== "Literal") {
2493
+ throw new Error("PostgreSQL JSON_SET currently supports literal paths only");
2494
+ }
2495
+ const pathArray = this.formatJsonbPathArray(pathNode);
2496
+ return `jsonb_set(${compiledArgs[0]}, ${pathArray}, ${compiledArgs[2]}::jsonb, true)`;
2497
+ });
2498
+ }
2499
+ formatJsonbPathArray(pathNode) {
2500
+ const rawPath = String(pathNode.value ?? "");
2501
+ if (!rawPath.startsWith("$")) {
2502
+ throw new Error('PostgreSQL JSON_SET paths must start with "$"');
2503
+ }
2504
+ const trimmed = rawPath === "$" ? "" : rawPath.startsWith("$.") ? rawPath.slice(2) : rawPath.slice(1);
2505
+ if (!trimmed) {
2506
+ throw new Error("PostgreSQL JSON_SET requires a non-root path");
2507
+ }
2508
+ if (trimmed.includes("[") || trimmed.includes("]")) {
2509
+ throw new Error("PostgreSQL JSON_SET currently only supports simple dot-separated paths");
2510
+ }
2511
+ const segments = trimmed.split(".").map((segment) => segment.replace(/^['"]?/, "").replace(/['"]?$/, "").trim()).filter(Boolean);
2512
+ if (!segments.length) {
2513
+ throw new Error("PostgreSQL JSON_SET requires at least one path segment");
2514
+ }
2515
+ const escapedSegments = segments.map((segment) => `'${segment.replace(/'/g, "''")}'`);
2516
+ return `ARRAY[${escapedSegments.join(", ")}]`;
2517
+ }
2518
+ };
2519
+
2520
+ // src/core/dialect/postgres/table-functions.ts
2521
+ var PostgresTableFunctionStrategy = class extends StandardTableFunctionStrategy {
2522
+ constructor() {
2523
+ super();
2524
+ this.registerOverrides();
2525
+ }
2526
+ registerOverrides() {
2527
+ this.add("ARRAY_UNNEST", ({ node, compiledArgs, quoteIdentifier }) => {
2528
+ const lateral = node.lateral ?? true;
2529
+ const withOrd = node.withOrdinality ?? false;
2530
+ const base = `unnest(${compiledArgs.join(", ")})${withOrd ? " WITH ORDINALITY" : ""}`;
2531
+ if (node.columnAliases?.length && !node.alias) {
2532
+ throw new Error("tvf(ARRAY_UNNEST) with columnAliases requires an alias.");
2533
+ }
2534
+ const alias = node.alias ? ` AS ${quoteIdentifier(node.alias)}` : "";
2535
+ const cols = node.columnAliases?.length ? `(${node.columnAliases.map(quoteIdentifier).join(", ")})` : "";
2536
+ return `${lateral ? "LATERAL " : ""}${base}${alias}${cols}`;
2537
+ });
2108
2538
  }
2109
2539
  };
2110
2540
 
@@ -2114,8 +2544,20 @@ var PostgresDialect = class extends SqlDialectBase {
2114
2544
  * Creates a new PostgresDialect instance
2115
2545
  */
2116
2546
  constructor() {
2117
- super(new PostgresFunctionStrategy());
2547
+ super(new PostgresFunctionStrategy(), new PostgresTableFunctionStrategy());
2118
2548
  this.dialect = "postgres";
2549
+ this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2550
+ const left2 = this.compileOperand(node.left, ctx);
2551
+ const right2 = this.compileOperand(node.right, ctx);
2552
+ const op = node.operator === "^" ? "#" : node.operator;
2553
+ return `${left2} ${op} ${right2}`;
2554
+ });
2555
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
2556
+ const left2 = this.compileOperand(node.left, ctx);
2557
+ const right2 = this.compileOperand(node.right, ctx);
2558
+ const op = node.operator === "^" ? "#" : node.operator;
2559
+ return `(${left2} ${op} ${right2})`;
2560
+ });
2119
2561
  }
2120
2562
  /**
2121
2563
  * Quotes an identifier using PostgreSQL double-quote syntax
@@ -2224,6 +2666,14 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
2224
2666
  }
2225
2667
  return `DATE(${date})`;
2226
2668
  });
2669
+ this.add("HOUR", ({ compiledArgs }) => `HOUR(${compiledArgs[0]})`);
2670
+ this.add("MINUTE", ({ compiledArgs }) => `MINUTE(${compiledArgs[0]})`);
2671
+ this.add("SECOND", ({ compiledArgs }) => `SECOND(${compiledArgs[0]})`);
2672
+ this.add("QUARTER", ({ compiledArgs }) => `QUARTER(${compiledArgs[0]})`);
2673
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2674
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments (array, value)");
2675
+ return `JSON_ARRAY_APPEND(${compiledArgs[0]}, '$', ${compiledArgs[1]})`;
2676
+ });
2227
2677
  }
2228
2678
  };
2229
2679
 
@@ -2356,6 +2806,30 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
2356
2806
  const separator = ctx.compileOperand(separatorOperand);
2357
2807
  return `GROUP_CONCAT(${arg}, ${separator})`;
2358
2808
  });
2809
+ this.add("HOUR", ({ compiledArgs }) => `CAST(strftime('%H', ${compiledArgs[0]}) AS INTEGER)`);
2810
+ this.add("MINUTE", ({ compiledArgs }) => `CAST(strftime('%M', ${compiledArgs[0]}) AS INTEGER)`);
2811
+ this.add("SECOND", ({ compiledArgs }) => `CAST(strftime('%S', ${compiledArgs[0]}) AS INTEGER)`);
2812
+ this.add("QUARTER", ({ compiledArgs }) => `((CAST(strftime('%m', ${compiledArgs[0]}) AS INTEGER) + 2) / 3)`);
2813
+ this.add("JSON_LENGTH", ({ compiledArgs }) => {
2814
+ if (compiledArgs.length === 0 || compiledArgs.length > 2) {
2815
+ throw new Error("JSON_LENGTH expects 1 or 2 arguments on SQLite");
2816
+ }
2817
+ return `json_array_length(${compiledArgs.join(", ")})`;
2818
+ });
2819
+ this.add("JSON_ARRAYAGG", ({ compiledArgs }) => {
2820
+ if (compiledArgs.length !== 1) {
2821
+ throw new Error("JSON_ARRAYAGG expects 1 argument on SQLite");
2822
+ }
2823
+ return `json_group_array(${compiledArgs[0]})`;
2824
+ });
2825
+ this.add("JSON_CONTAINS", () => {
2826
+ throw new Error("JSON_CONTAINS is not supported on SQLite");
2827
+ });
2828
+ this.add("ARRAY_APPEND", ({ compiledArgs }) => {
2829
+ if (compiledArgs.length !== 2) throw new Error("ARRAY_APPEND expects 2 arguments (array, value)");
2830
+ return `json_array_append(${compiledArgs[0]}, '$', ${compiledArgs[1]})`;
2831
+ });
2832
+ this.add("CHR", ({ compiledArgs }) => `CHAR(${compiledArgs[0]})`);
2359
2833
  }
2360
2834
  };
2361
2835
 
@@ -2367,6 +2841,22 @@ var SqliteDialect = class extends SqlDialectBase {
2367
2841
  constructor() {
2368
2842
  super(new SqliteFunctionStrategy());
2369
2843
  this.dialect = "sqlite";
2844
+ this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2845
+ const left2 = this.compileOperand(node.left, ctx);
2846
+ const right2 = this.compileOperand(node.right, ctx);
2847
+ if (node.operator === "^") {
2848
+ return `(${left2} | ${right2}) & ~(${left2} & ${right2})`;
2849
+ }
2850
+ return `${left2} ${node.operator} ${right2}`;
2851
+ });
2852
+ this.registerOperandCompiler("BitwiseExpression", (node, ctx) => {
2853
+ const left2 = this.compileOperand(node.left, ctx);
2854
+ const right2 = this.compileOperand(node.right, ctx);
2855
+ if (node.operator === "^") {
2856
+ return `((${left2} | ${right2}) & ~(${left2} & ${right2}))`;
2857
+ }
2858
+ return `(${left2} ${node.operator} ${right2})`;
2859
+ });
2370
2860
  }
2371
2861
  /**
2372
2862
  * Quotes an identifier using SQLite double-quote syntax
@@ -2491,6 +2981,33 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
2491
2981
  const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
2492
2982
  return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
2493
2983
  });
2984
+ this.add("LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2985
+ this.add("CHAR_LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2986
+ this.add("CHARACTER_LENGTH", ({ compiledArgs }) => `LEN(${compiledArgs[0]})`);
2987
+ this.add("POSITION", ({ compiledArgs }) => `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]})`);
2988
+ this.add("LOCATE", ({ compiledArgs }) => compiledArgs.length === 3 ? `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` : `CHARINDEX(${compiledArgs[0]}, ${compiledArgs[1]})`);
2989
+ this.add("INSTR", ({ compiledArgs }) => `CHARINDEX(${compiledArgs[1]}, ${compiledArgs[0]})`);
2990
+ this.add("CHR", ({ compiledArgs }) => `CHAR(${compiledArgs[0]})`);
2991
+ this.add("HOUR", ({ compiledArgs }) => `DATEPART(hour, ${compiledArgs[0]})`);
2992
+ this.add("MINUTE", ({ compiledArgs }) => `DATEPART(minute, ${compiledArgs[0]})`);
2993
+ this.add("SECOND", ({ compiledArgs }) => `DATEPART(second, ${compiledArgs[0]})`);
2994
+ this.add("QUARTER", ({ compiledArgs }) => `DATEPART(quarter, ${compiledArgs[0]})`);
2995
+ this.add("JSON_SET", ({ compiledArgs }) => {
2996
+ if (compiledArgs.length !== 3) throw new Error("JSON_SET expects 3 arguments on SQL Server");
2997
+ return `JSON_MODIFY(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})`;
2998
+ });
2999
+ this.add("JSON_LENGTH", () => {
3000
+ throw new Error("JSON_LENGTH is not supported on SQL Server");
3001
+ });
3002
+ this.add("JSON_ARRAYAGG", () => {
3003
+ throw new Error("JSON_ARRAYAGG is not supported on SQL Server");
3004
+ });
3005
+ this.add("JSON_CONTAINS", () => {
3006
+ throw new Error("JSON_CONTAINS is not supported on SQL Server");
3007
+ });
3008
+ this.add("ARRAY_APPEND", () => {
3009
+ throw new Error("ARRAY_APPEND is not supported on SQL Server");
3010
+ });
2494
3011
  }
2495
3012
  };
2496
3013
 
@@ -3595,20 +4112,21 @@ var RelationProjectionHelper = class {
3595
4112
  var assertNever = (value) => {
3596
4113
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
3597
4114
  };
3598
- var baseRelationCondition = (root, relation, rootAlias) => {
4115
+ var baseRelationCondition = (root, relation, rootAlias, targetTableName) => {
3599
4116
  const rootTable = rootAlias || root.name;
4117
+ const targetTable = targetTableName ?? relation.target.name;
3600
4118
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
3601
4119
  const localKey = relation.localKey || defaultLocalKey;
3602
4120
  switch (relation.type) {
3603
4121
  case RelationKinds.HasMany:
3604
4122
  case RelationKinds.HasOne:
3605
4123
  return eq(
3606
- { type: "Column", table: relation.target.name, name: relation.foreignKey },
4124
+ { type: "Column", table: targetTable, name: relation.foreignKey },
3607
4125
  { type: "Column", table: rootTable, name: localKey }
3608
4126
  );
3609
4127
  case RelationKinds.BelongsTo:
3610
4128
  return eq(
3611
- { type: "Column", table: relation.target.name, name: localKey },
4129
+ { type: "Column", table: targetTable, name: localKey },
3612
4130
  { type: "Column", table: rootTable, name: relation.foreignKey }
3613
4131
  );
3614
4132
  case RelationKinds.BelongsToMany:
@@ -3617,7 +4135,7 @@ var baseRelationCondition = (root, relation, rootAlias) => {
3617
4135
  return assertNever(relation);
3618
4136
  }
3619
4137
  };
3620
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
4138
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias, targetTable, targetTableName) => {
3621
4139
  const rootKey = relation.localKey || findPrimaryKey(root);
3622
4140
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
3623
4141
  const rootTable = rootAlias || root.name;
@@ -3630,8 +4148,14 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
3630
4148
  { type: "Table", name: relation.pivotTable.name, schema: relation.pivotTable.schema },
3631
4149
  pivotCondition
3632
4150
  );
4151
+ const targetSource = targetTable ?? {
4152
+ type: "Table",
4153
+ name: relation.target.name,
4154
+ schema: relation.target.schema
4155
+ };
4156
+ const effectiveTargetName = targetTableName ?? relation.target.name;
3633
4157
  let targetCondition = eq(
3634
- { type: "Column", table: relation.target.name, name: targetKey },
4158
+ { type: "Column", table: effectiveTargetName, name: targetKey },
3635
4159
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
3636
4160
  );
3637
4161
  if (extra) {
@@ -3639,24 +4163,25 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
3639
4163
  }
3640
4164
  const targetJoin = createJoinNode(
3641
4165
  joinKind,
3642
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
4166
+ targetSource,
3643
4167
  targetCondition,
3644
4168
  relationName
3645
4169
  );
3646
4170
  return [pivotJoin, targetJoin];
3647
4171
  };
3648
- var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
3649
- const base = baseRelationCondition(root, relation, rootAlias);
4172
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias, targetTableName) => {
4173
+ const base = baseRelationCondition(root, relation, rootAlias, targetTableName);
3650
4174
  return extra ? and(base, extra) : base;
3651
4175
  };
3652
- var buildRelationCorrelation = (root, relation, rootAlias) => {
3653
- return baseRelationCondition(root, relation, rootAlias);
4176
+ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
4177
+ return baseRelationCondition(root, relation, rootAlias, targetTableName);
3654
4178
  };
3655
4179
 
3656
4180
  // src/core/ast/join-metadata.ts
3657
4181
  var getJoinRelationName = (join) => join.meta?.relationName;
3658
4182
 
3659
4183
  // src/query-builder/relation-service.ts
4184
+ var hasRelationForeignKey = (relation) => relation.type !== RelationKinds.BelongsToMany;
3660
4185
  var RelationService = class {
3661
4186
  /**
3662
4187
  * Creates a new RelationService instance
@@ -3681,8 +4206,8 @@ var RelationService = class {
3681
4206
  * @param extraCondition - Additional join condition
3682
4207
  * @returns Relation result with updated state and hydration
3683
4208
  */
3684
- joinRelation(relationName, joinKind, extraCondition) {
3685
- const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
4209
+ joinRelation(relationName, joinKind, extraCondition, tableSource) {
4210
+ const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition, tableSource);
3686
4211
  return { state: nextState, hydration: this.hydration };
3687
4212
  }
3688
4213
  /**
@@ -3711,14 +4236,58 @@ var RelationService = class {
3711
4236
  const relation = this.getRelation(relationName);
3712
4237
  const aliasPrefix = options?.aliasPrefix ?? relationName;
3713
4238
  const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
4239
+ const { selfFilters, crossFilters } = this.splitFilterExpressions(
4240
+ options?.filter,
4241
+ /* @__PURE__ */ new Set([relation.target.name])
4242
+ );
4243
+ const canUseCte = !alreadyJoined && selfFilters.length > 0;
4244
+ const joinFilters = [...crossFilters];
4245
+ if (!canUseCte) {
4246
+ joinFilters.push(...selfFilters);
4247
+ }
4248
+ const joinCondition = this.combineWithAnd(joinFilters);
4249
+ let tableSourceOverride;
4250
+ if (canUseCte) {
4251
+ const cteInfo = this.createFilteredRelationCte(state, relationName, relation, selfFilters);
4252
+ state = cteInfo.state;
4253
+ tableSourceOverride = cteInfo.table;
4254
+ }
3714
4255
  if (!alreadyJoined) {
3715
- const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
3716
- state = joined.state;
4256
+ state = this.withJoin(
4257
+ state,
4258
+ relationName,
4259
+ options?.joinKind ?? JOIN_KINDS.LEFT,
4260
+ joinCondition,
4261
+ tableSourceOverride
4262
+ );
3717
4263
  }
3718
4264
  const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
3719
4265
  state = projectionResult.state;
3720
4266
  hydration = projectionResult.hydration;
3721
- const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
4267
+ if (hasRelationForeignKey(relation)) {
4268
+ const fkColumn = this.table.columns[relation.foreignKey];
4269
+ if (fkColumn) {
4270
+ const hasForeignKeySelected = state.ast.columns.some((col2) => {
4271
+ if (col2.type !== "Column") return false;
4272
+ const node = col2;
4273
+ const alias = node.alias ?? node.name;
4274
+ return alias === relation.foreignKey;
4275
+ });
4276
+ if (!hasForeignKeySelected) {
4277
+ const fkSelectionResult = this.selectColumns(state, hydration, {
4278
+ [relation.foreignKey]: fkColumn
4279
+ });
4280
+ state = fkSelectionResult.state;
4281
+ hydration = fkSelectionResult.hydration;
4282
+ }
4283
+ }
4284
+ }
4285
+ const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
4286
+ const targetPrimaryKey = findPrimaryKey(relation.target);
4287
+ if (!requestedColumns.includes(targetPrimaryKey)) {
4288
+ requestedColumns.push(targetPrimaryKey);
4289
+ }
4290
+ const targetColumns = requestedColumns;
3722
4291
  const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
3723
4292
  return keys.reduce((acc, key) => {
3724
4293
  const def = columns[key];
@@ -3802,27 +4371,42 @@ var RelationService = class {
3802
4371
  * @param extraCondition - Additional join condition
3803
4372
  * @returns Updated query state with join
3804
4373
  */
3805
- withJoin(state, relationName, joinKind, extraCondition) {
4374
+ withJoin(state, relationName, joinKind, extraCondition, tableSource) {
3806
4375
  const relation = this.getRelation(relationName);
3807
4376
  const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
3808
4377
  if (relation.type === RelationKinds.BelongsToMany) {
4378
+ const targetTableSource = tableSource ?? {
4379
+ type: "Table",
4380
+ name: relation.target.name,
4381
+ schema: relation.target.schema
4382
+ };
4383
+ const targetName2 = this.resolveTargetTableName(targetTableSource, relation);
3809
4384
  const joins = buildBelongsToManyJoins(
3810
4385
  this.table,
3811
4386
  relationName,
3812
4387
  relation,
3813
4388
  joinKind,
3814
4389
  extraCondition,
3815
- rootAlias
4390
+ rootAlias,
4391
+ targetTableSource,
4392
+ targetName2
3816
4393
  );
3817
4394
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
3818
4395
  }
3819
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
3820
- const joinNode = createJoinNode(
3821
- joinKind,
3822
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
3823
- condition,
3824
- relationName
4396
+ const targetTable = tableSource ?? {
4397
+ type: "Table",
4398
+ name: relation.target.name,
4399
+ schema: relation.target.schema
4400
+ };
4401
+ const targetName = this.resolveTargetTableName(targetTable, relation);
4402
+ const condition = buildRelationJoinCondition(
4403
+ this.table,
4404
+ relation,
4405
+ extraCondition,
4406
+ rootAlias,
4407
+ targetName
3825
4408
  );
4409
+ const joinNode = createJoinNode(joinKind, targetTable, condition, relationName);
3826
4410
  return this.astService(state).withJoin(joinNode);
3827
4411
  }
3828
4412
  /**
@@ -3839,6 +4423,198 @@ var RelationService = class {
3839
4423
  hydration: hydration.onColumnsSelected(nextState, addedColumns)
3840
4424
  };
3841
4425
  }
4426
+ combineWithAnd(expressions) {
4427
+ if (expressions.length === 0) return void 0;
4428
+ if (expressions.length === 1) return expressions[0];
4429
+ return {
4430
+ type: "LogicalExpression",
4431
+ operator: "AND",
4432
+ operands: expressions
4433
+ };
4434
+ }
4435
+ splitFilterExpressions(filter, allowedTables) {
4436
+ const terms = this.flattenAnd(filter);
4437
+ const selfFilters = [];
4438
+ const crossFilters = [];
4439
+ for (const term of terms) {
4440
+ if (this.isExpressionSelfContained(term, allowedTables)) {
4441
+ selfFilters.push(term);
4442
+ } else {
4443
+ crossFilters.push(term);
4444
+ }
4445
+ }
4446
+ return { selfFilters, crossFilters };
4447
+ }
4448
+ flattenAnd(node) {
4449
+ if (!node) return [];
4450
+ if (node.type === "LogicalExpression" && node.operator === "AND") {
4451
+ return node.operands.flatMap((operand) => this.flattenAnd(operand));
4452
+ }
4453
+ return [node];
4454
+ }
4455
+ isExpressionSelfContained(expr, allowedTables) {
4456
+ const collector = this.collectReferencedTables(expr);
4457
+ if (collector.hasSubquery) return false;
4458
+ if (collector.tables.size === 0) return true;
4459
+ for (const table of collector.tables) {
4460
+ if (!allowedTables.has(table)) {
4461
+ return false;
4462
+ }
4463
+ }
4464
+ return true;
4465
+ }
4466
+ collectReferencedTables(expr) {
4467
+ const collector = {
4468
+ tables: /* @__PURE__ */ new Set(),
4469
+ hasSubquery: false
4470
+ };
4471
+ this.collectFromExpression(expr, collector);
4472
+ return collector;
4473
+ }
4474
+ collectFromExpression(expr, collector) {
4475
+ switch (expr.type) {
4476
+ case "BinaryExpression":
4477
+ this.collectFromOperand(expr.left, collector);
4478
+ this.collectFromOperand(expr.right, collector);
4479
+ break;
4480
+ case "LogicalExpression":
4481
+ expr.operands.forEach((operand) => this.collectFromExpression(operand, collector));
4482
+ break;
4483
+ case "NullExpression":
4484
+ this.collectFromOperand(expr.left, collector);
4485
+ break;
4486
+ case "InExpression":
4487
+ this.collectFromOperand(expr.left, collector);
4488
+ if (Array.isArray(expr.right)) {
4489
+ expr.right.forEach((value) => this.collectFromOperand(value, collector));
4490
+ } else {
4491
+ collector.hasSubquery = true;
4492
+ }
4493
+ break;
4494
+ case "ExistsExpression":
4495
+ collector.hasSubquery = true;
4496
+ break;
4497
+ case "BetweenExpression":
4498
+ this.collectFromOperand(expr.left, collector);
4499
+ this.collectFromOperand(expr.lower, collector);
4500
+ this.collectFromOperand(expr.upper, collector);
4501
+ break;
4502
+ case "ArithmeticExpression":
4503
+ case "BitwiseExpression":
4504
+ this.collectFromOperand(expr.left, collector);
4505
+ this.collectFromOperand(expr.right, collector);
4506
+ break;
4507
+ default:
4508
+ break;
4509
+ }
4510
+ }
4511
+ collectFromOperand(node, collector) {
4512
+ switch (node.type) {
4513
+ case "Column":
4514
+ collector.tables.add(node.table);
4515
+ break;
4516
+ case "Function":
4517
+ node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4518
+ if (node.separator) {
4519
+ this.collectFromOperand(node.separator, collector);
4520
+ }
4521
+ if (node.orderBy) {
4522
+ node.orderBy.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4523
+ }
4524
+ break;
4525
+ case "JsonPath":
4526
+ this.collectFromOperand(node.column, collector);
4527
+ break;
4528
+ case "ScalarSubquery":
4529
+ collector.hasSubquery = true;
4530
+ break;
4531
+ case "CaseExpression":
4532
+ node.conditions.forEach(({ when, then }) => {
4533
+ this.collectFromExpression(when, collector);
4534
+ this.collectFromOperand(then, collector);
4535
+ });
4536
+ if (node.else) {
4537
+ this.collectFromOperand(node.else, collector);
4538
+ }
4539
+ break;
4540
+ case "Cast":
4541
+ this.collectFromOperand(node.expression, collector);
4542
+ break;
4543
+ case "WindowFunction":
4544
+ node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4545
+ node.partitionBy?.forEach((part) => this.collectFromOperand(part, collector));
4546
+ node.orderBy?.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4547
+ break;
4548
+ case "Collate":
4549
+ this.collectFromOperand(node.expression, collector);
4550
+ break;
4551
+ case "ArithmeticExpression":
4552
+ case "BitwiseExpression":
4553
+ this.collectFromOperand(node.left, collector);
4554
+ this.collectFromOperand(node.right, collector);
4555
+ break;
4556
+ case "Literal":
4557
+ case "AliasRef":
4558
+ break;
4559
+ default:
4560
+ break;
4561
+ }
4562
+ }
4563
+ collectFromOrderingTerm(term, collector) {
4564
+ if (isOperandNode(term)) {
4565
+ this.collectFromOperand(term, collector);
4566
+ return;
4567
+ }
4568
+ this.collectFromExpression(term, collector);
4569
+ }
4570
+ createFilteredRelationCte(state, relationName, relation, filters) {
4571
+ const cteName = this.generateUniqueCteName(state, relationName);
4572
+ const predicate = this.combineWithAnd(filters);
4573
+ if (!predicate) {
4574
+ throw new Error("Unable to build filter CTE without predicates.");
4575
+ }
4576
+ const columns = Object.keys(relation.target.columns).map((name) => ({
4577
+ type: "Column",
4578
+ table: relation.target.name,
4579
+ name
4580
+ }));
4581
+ const cteQuery = {
4582
+ type: "SelectQuery",
4583
+ from: { type: "Table", name: relation.target.name, schema: relation.target.schema },
4584
+ columns,
4585
+ joins: [],
4586
+ where: predicate
4587
+ };
4588
+ const nextState = this.astService(state).withCte(cteName, cteQuery);
4589
+ const tableNode3 = {
4590
+ type: "Table",
4591
+ name: cteName,
4592
+ alias: relation.target.name
4593
+ };
4594
+ return { state: nextState, table: tableNode3 };
4595
+ }
4596
+ generateUniqueCteName(state, relationName) {
4597
+ const existing = new Set((state.ast.ctes ?? []).map((cte) => cte.name));
4598
+ let candidate = `${relationName}__filtered`;
4599
+ let suffix = 1;
4600
+ while (existing.has(candidate)) {
4601
+ candidate = `${relationName}__filtered_${suffix}`;
4602
+ suffix += 1;
4603
+ }
4604
+ return candidate;
4605
+ }
4606
+ resolveTargetTableName(target, relation) {
4607
+ if (target.type === "Table") {
4608
+ return target.alias ?? target.name;
4609
+ }
4610
+ if (target.type === "DerivedTable") {
4611
+ return target.alias;
4612
+ }
4613
+ if (target.type === "FunctionTable") {
4614
+ return target.alias ?? relation.target.name;
4615
+ }
4616
+ return relation.target.name;
4617
+ }
3842
4618
  /**
3843
4619
  * Gets a relation definition by name
3844
4620
  * @param relationName - Name of the relation
@@ -4189,6 +4965,18 @@ var DefaultHasManyCollection = class {
4189
4965
  getItems() {
4190
4966
  return this.items;
4191
4967
  }
4968
+ /**
4969
+ * Array-compatible length for testing frameworks.
4970
+ */
4971
+ get length() {
4972
+ return this.items.length;
4973
+ }
4974
+ /**
4975
+ * Enables iteration over the collection like an array.
4976
+ */
4977
+ [Symbol.iterator]() {
4978
+ return this.items[Symbol.iterator]();
4979
+ }
4192
4980
  /**
4193
4981
  * Adds a new child entity to the collection.
4194
4982
  * @param data - Partial data for the new entity
@@ -4288,6 +5076,17 @@ var hideInternal2 = (obj, keys) => {
4288
5076
  }
4289
5077
  };
4290
5078
  var DefaultHasOneReference = class {
5079
+ /**
5080
+ * @param ctx The entity context for tracking changes.
5081
+ * @param meta Metadata for the parent entity.
5082
+ * @param root The parent entity instance.
5083
+ * @param relationName The name of the relation.
5084
+ * @param relation Relation definition.
5085
+ * @param rootTable Table definition of the parent entity.
5086
+ * @param loader Function to load the child entity.
5087
+ * @param createEntity Function to create entity instances from rows.
5088
+ * @param localKey The local key on the parent entity used for the relation.
5089
+ */
4291
5090
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
4292
5091
  this.ctx = ctx;
4293
5092
  this.meta = meta;
@@ -4405,6 +5204,17 @@ var hideInternal3 = (obj, keys) => {
4405
5204
  }
4406
5205
  };
4407
5206
  var DefaultBelongsToReference = class {
5207
+ /**
5208
+ * @param ctx The entity context for tracking changes.
5209
+ * @param meta Metadata for the child entity.
5210
+ * @param root The child entity instance (carrying the foreign key).
5211
+ * @param relationName The name of the relation.
5212
+ * @param relation Relation definition.
5213
+ * @param rootTable Table definition of the child entity.
5214
+ * @param loader Function to load the parent entity.
5215
+ * @param createEntity Function to create entity instances from rows.
5216
+ * @param targetKey The primary key of the target (parent) table.
5217
+ */
4408
5218
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
4409
5219
  this.ctx = ctx;
4410
5220
  this.meta = meta;
@@ -4496,6 +5306,17 @@ var hideInternal4 = (obj, keys) => {
4496
5306
  }
4497
5307
  };
4498
5308
  var DefaultManyToManyCollection = class {
5309
+ /**
5310
+ * @param ctx The entity context for tracking changes.
5311
+ * @param meta Metadata for the root entity.
5312
+ * @param root The root entity instance.
5313
+ * @param relationName The name of the relation.
5314
+ * @param relation Relation definition.
5315
+ * @param rootTable Table definition of the root entity.
5316
+ * @param loader Function to load the collection items.
5317
+ * @param createEntity Function to create entity instances from rows.
5318
+ * @param localKey The local key used for joining.
5319
+ */
4499
5320
  constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
4500
5321
  this.ctx = ctx;
4501
5322
  this.meta = meta;
@@ -4511,6 +5332,10 @@ var DefaultManyToManyCollection = class {
4511
5332
  hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
4512
5333
  this.hydrateFromCache();
4513
5334
  }
5335
+ /**
5336
+ * Loads the collection items if not already loaded.
5337
+ * @returns A promise that resolves to the array of target entities.
5338
+ */
4514
5339
  async load() {
4515
5340
  if (this.loaded) return this.items;
4516
5341
  const map = await this.loader();
@@ -4526,9 +5351,30 @@ var DefaultManyToManyCollection = class {
4526
5351
  this.loaded = true;
4527
5352
  return this.items;
4528
5353
  }
5354
+ /**
5355
+ * Returns the currently loaded items.
5356
+ * @returns Array of target entities.
5357
+ */
4529
5358
  getItems() {
4530
5359
  return this.items;
4531
5360
  }
5361
+ /**
5362
+ * Array-compatible length for testing frameworks.
5363
+ */
5364
+ get length() {
5365
+ return this.items.length;
5366
+ }
5367
+ /**
5368
+ * Enables iteration over the collection like an array.
5369
+ */
5370
+ [Symbol.iterator]() {
5371
+ return this.items[Symbol.iterator]();
5372
+ }
5373
+ /**
5374
+ * Attaches an entity to the collection.
5375
+ * Registers an 'attach' change in the entity context.
5376
+ * @param target Entity instance or its primary key value.
5377
+ */
4532
5378
  attach(target) {
4533
5379
  const entity = this.ensureEntity(target);
4534
5380
  const id = this.extractId(entity);
@@ -4548,6 +5394,11 @@ var DefaultManyToManyCollection = class {
4548
5394
  { kind: "attach", entity }
4549
5395
  );
4550
5396
  }
5397
+ /**
5398
+ * Detaches an entity from the collection.
5399
+ * Registers a 'detach' change in the entity context.
5400
+ * @param target Entity instance or its primary key value.
5401
+ */
4551
5402
  detach(target) {
4552
5403
  const id = typeof target === "number" || typeof target === "string" ? target : this.extractId(target);
4553
5404
  if (id == null) return;
@@ -4563,6 +5414,11 @@ var DefaultManyToManyCollection = class {
4563
5414
  { kind: "detach", entity: existing }
4564
5415
  );
4565
5416
  }
5417
+ /**
5418
+ * Syncs the collection with a list of IDs.
5419
+ * Attaches missing IDs and detaches IDs not in the list.
5420
+ * @param ids Array of primary key values to sync with.
5421
+ */
4566
5422
  async syncByIds(ids) {
4567
5423
  await this.load();
4568
5424
  const normalized = new Set(ids.map((id) => toKey5(id)));
@@ -4621,10 +5477,27 @@ var DefaultManyToManyCollection = class {
4621
5477
  };
4622
5478
 
4623
5479
  // src/orm/lazy-batch.ts
4624
- var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
4625
- acc[name] = def;
4626
- return acc;
4627
- }, {});
5480
+ var hasColumns = (columns) => Boolean(columns && columns.length > 0);
5481
+ var buildColumnSelection = (table, columns, missingMsg) => {
5482
+ return columns.reduce((acc, column) => {
5483
+ const def = table.columns[column];
5484
+ if (!def) {
5485
+ throw new Error(missingMsg(column));
5486
+ }
5487
+ acc[column] = def;
5488
+ return acc;
5489
+ }, {});
5490
+ };
5491
+ var filterRow = (row, columns) => {
5492
+ const filtered = {};
5493
+ for (const column of columns) {
5494
+ if (column in row) {
5495
+ filtered[column] = row[column];
5496
+ }
5497
+ }
5498
+ return filtered;
5499
+ };
5500
+ var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
4628
5501
  var rowsFromResults = (results) => {
4629
5502
  const rows = [];
4630
5503
  for (const result of results) {
@@ -4656,9 +5529,12 @@ var collectKeysFromRoots = (roots, key) => {
4656
5529
  return collected;
4657
5530
  };
4658
5531
  var buildInListValues = (keys) => Array.from(keys);
4659
- var fetchRowsForKeys = async (ctx, table, column, keys) => {
4660
- const qb = new SelectQueryBuilder(table).select(selectAllColumns(table));
4661
- qb.where(inList(column, buildInListValues(keys)));
5532
+ var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
5533
+ let qb = new SelectQueryBuilder(table).select(selection);
5534
+ qb = qb.where(inList(column, buildInListValues(keys)));
5535
+ if (filter) {
5536
+ qb = qb.where(filter);
5537
+ }
4662
5538
  return executeQuery(ctx, qb);
4663
5539
  };
4664
5540
  var groupRowsByMany = (rows, keyColumn) => {
@@ -4685,7 +5561,7 @@ var groupRowsByUnique = (rows, keyColumn) => {
4685
5561
  }
4686
5562
  return lookup;
4687
5563
  };
4688
- var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
5564
+ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
4689
5565
  const localKey = relation.localKey || findPrimaryKey(rootTable);
4690
5566
  const roots = ctx.getEntitiesForTable(rootTable);
4691
5567
  const keys = collectKeysFromRoots(roots, localKey);
@@ -4694,10 +5570,30 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
4694
5570
  }
4695
5571
  const fkColumn = relation.target.columns[relation.foreignKey];
4696
5572
  if (!fkColumn) return /* @__PURE__ */ new Map();
4697
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
4698
- return groupRowsByMany(rows, relation.foreignKey);
5573
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5574
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5575
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5576
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5577
+ selectedColumns.push(targetPrimaryKey);
5578
+ }
5579
+ const queryColumns = new Set(selectedColumns);
5580
+ queryColumns.add(relation.foreignKey);
5581
+ const selection = buildColumnSelection(
5582
+ relation.target,
5583
+ Array.from(queryColumns),
5584
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5585
+ );
5586
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5587
+ const grouped = groupRowsByMany(rows, relation.foreignKey);
5588
+ if (!requestedColumns) return grouped;
5589
+ const visibleColumns = new Set(selectedColumns);
5590
+ const filtered = /* @__PURE__ */ new Map();
5591
+ for (const [key, bucket] of grouped.entries()) {
5592
+ filtered.set(key, filterRows(bucket, visibleColumns));
5593
+ }
5594
+ return filtered;
4699
5595
  };
4700
- var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
5596
+ var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options) => {
4701
5597
  const localKey = relation.localKey || findPrimaryKey(rootTable);
4702
5598
  const roots = ctx.getEntitiesForTable(rootTable);
4703
5599
  const keys = collectKeysFromRoots(roots, localKey);
@@ -4706,22 +5602,98 @@ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
4706
5602
  }
4707
5603
  const fkColumn = relation.target.columns[relation.foreignKey];
4708
5604
  if (!fkColumn) return /* @__PURE__ */ new Map();
4709
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
4710
- return groupRowsByUnique(rows, relation.foreignKey);
5605
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5606
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5607
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5608
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5609
+ selectedColumns.push(targetPrimaryKey);
5610
+ }
5611
+ const queryColumns = new Set(selectedColumns);
5612
+ queryColumns.add(relation.foreignKey);
5613
+ const selection = buildColumnSelection(
5614
+ relation.target,
5615
+ Array.from(queryColumns),
5616
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5617
+ );
5618
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5619
+ const grouped = groupRowsByUnique(rows, relation.foreignKey);
5620
+ if (!requestedColumns) return grouped;
5621
+ const visibleColumns = new Set(selectedColumns);
5622
+ const filtered = /* @__PURE__ */ new Map();
5623
+ for (const [key, row] of grouped.entries()) {
5624
+ filtered.set(key, filterRow(row, visibleColumns));
5625
+ }
5626
+ return filtered;
4711
5627
  };
4712
- var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
5628
+ var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, options) => {
4713
5629
  const roots = ctx.getEntitiesForTable(rootTable);
4714
- const foreignKeys = collectKeysFromRoots(roots, relation.foreignKey);
5630
+ const getForeignKeys = () => collectKeysFromRoots(roots, relation.foreignKey);
5631
+ let foreignKeys = getForeignKeys();
5632
+ if (!foreignKeys.size) {
5633
+ const pkName = findPrimaryKey(rootTable);
5634
+ const pkColumn2 = rootTable.columns[pkName];
5635
+ const fkColumn = rootTable.columns[relation.foreignKey];
5636
+ if (pkColumn2 && fkColumn) {
5637
+ const missingKeys = /* @__PURE__ */ new Set();
5638
+ const entityByPk = /* @__PURE__ */ new Map();
5639
+ for (const tracked of roots) {
5640
+ const entity = tracked.entity;
5641
+ const pkValue = entity[pkName];
5642
+ if (pkValue === void 0 || pkValue === null) continue;
5643
+ const fkValue = entity[relation.foreignKey];
5644
+ if (fkValue === void 0 || fkValue === null) {
5645
+ missingKeys.add(pkValue);
5646
+ entityByPk.set(pkValue, entity);
5647
+ }
5648
+ }
5649
+ if (missingKeys.size) {
5650
+ const selection2 = buildColumnSelection(
5651
+ rootTable,
5652
+ [pkName, relation.foreignKey],
5653
+ (column) => `Column '${column}' not found on table '${rootTable.name}'`
5654
+ );
5655
+ const keyRows = await fetchRowsForKeys(ctx, rootTable, pkColumn2, missingKeys, selection2);
5656
+ for (const row of keyRows) {
5657
+ const pkValue = row[pkName];
5658
+ if (pkValue === void 0 || pkValue === null) continue;
5659
+ const entity = entityByPk.get(pkValue);
5660
+ if (!entity) continue;
5661
+ const fkValue = row[relation.foreignKey];
5662
+ if (fkValue !== void 0 && fkValue !== null) {
5663
+ entity[relation.foreignKey] = fkValue;
5664
+ }
5665
+ }
5666
+ foreignKeys = getForeignKeys();
5667
+ }
5668
+ }
5669
+ }
4715
5670
  if (!foreignKeys.size) {
4716
5671
  return /* @__PURE__ */ new Map();
4717
5672
  }
4718
5673
  const targetKey = relation.localKey || findPrimaryKey(relation.target);
4719
5674
  const pkColumn = relation.target.columns[targetKey];
4720
5675
  if (!pkColumn) return /* @__PURE__ */ new Map();
4721
- const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys);
4722
- return groupRowsByUnique(rows, targetKey);
5676
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5677
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5678
+ if (!selectedColumns.includes(targetKey)) {
5679
+ selectedColumns.push(targetKey);
5680
+ }
5681
+ const selection = buildColumnSelection(
5682
+ relation.target,
5683
+ selectedColumns,
5684
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5685
+ );
5686
+ const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys, selection, options?.filter);
5687
+ const grouped = groupRowsByUnique(rows, targetKey);
5688
+ if (!requestedColumns) return grouped;
5689
+ const visibleColumns = new Set(selectedColumns);
5690
+ const filtered = /* @__PURE__ */ new Map();
5691
+ for (const [key, row] of grouped.entries()) {
5692
+ filtered.set(key, filterRow(row, visibleColumns));
5693
+ }
5694
+ return filtered;
4723
5695
  };
4724
- var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
5696
+ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, options) => {
4725
5697
  const rootKey = relation.localKey || findPrimaryKey(rootTable);
4726
5698
  const roots = ctx.getEntitiesForTable(rootTable);
4727
5699
  const rootIds = collectKeysFromRoots(roots, rootKey);
@@ -4730,9 +5702,29 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4730
5702
  }
4731
5703
  const pivotColumn = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
4732
5704
  if (!pivotColumn) return /* @__PURE__ */ new Map();
4733
- const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds);
5705
+ const pivotColumnsRequested = hasColumns(options?.pivot?.columns) ? [...options.pivot.columns] : void 0;
5706
+ const useIncludeDefaults = options !== void 0;
5707
+ let pivotSelectedColumns;
5708
+ if (pivotColumnsRequested) {
5709
+ pivotSelectedColumns = [...pivotColumnsRequested];
5710
+ } else if (useIncludeDefaults) {
5711
+ const pivotPk = relation.pivotPrimaryKey || findPrimaryKey(relation.pivotTable);
5712
+ pivotSelectedColumns = relation.defaultPivotColumns ?? buildDefaultPivotColumns(relation, pivotPk);
5713
+ } else {
5714
+ pivotSelectedColumns = Object.keys(relation.pivotTable.columns);
5715
+ }
5716
+ const pivotQueryColumns = new Set(pivotSelectedColumns);
5717
+ pivotQueryColumns.add(relation.pivotForeignKeyToRoot);
5718
+ pivotQueryColumns.add(relation.pivotForeignKeyToTarget);
5719
+ const pivotSelection = buildColumnSelection(
5720
+ relation.pivotTable,
5721
+ Array.from(pivotQueryColumns),
5722
+ (column) => `Column '${column}' not found on pivot table '${relation.pivotTable.name}'`
5723
+ );
5724
+ const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds, pivotSelection);
4734
5725
  const rootLookup = /* @__PURE__ */ new Map();
4735
5726
  const targetIds = /* @__PURE__ */ new Set();
5727
+ const pivotVisibleColumns = new Set(pivotSelectedColumns);
4736
5728
  for (const pivot of pivotRows) {
4737
5729
  const rootValue = pivot[relation.pivotForeignKeyToRoot];
4738
5730
  const targetValue = pivot[relation.pivotForeignKeyToTarget];
@@ -4742,7 +5734,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4742
5734
  const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
4743
5735
  bucket.push({
4744
5736
  targetId: targetValue,
4745
- pivot: { ...pivot }
5737
+ pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
4746
5738
  });
4747
5739
  rootLookup.set(toKey6(rootValue), bucket);
4748
5740
  targetIds.add(targetValue);
@@ -4753,8 +5745,19 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4753
5745
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
4754
5746
  const targetPkColumn = relation.target.columns[targetKey];
4755
5747
  if (!targetPkColumn) return /* @__PURE__ */ new Map();
4756
- const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds);
5748
+ const targetRequestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5749
+ const targetSelectedColumns = targetRequestedColumns ? [...targetRequestedColumns] : Object.keys(relation.target.columns);
5750
+ if (!targetSelectedColumns.includes(targetKey)) {
5751
+ targetSelectedColumns.push(targetKey);
5752
+ }
5753
+ const targetSelection = buildColumnSelection(
5754
+ relation.target,
5755
+ targetSelectedColumns,
5756
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5757
+ );
5758
+ const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds, targetSelection, options?.filter);
4757
5759
  const targetMap = groupRowsByUnique(targetRows, targetKey);
5760
+ const targetVisibleColumns = new Set(targetSelectedColumns);
4758
5761
  const result = /* @__PURE__ */ new Map();
4759
5762
  for (const [rootId, entries] of rootLookup.entries()) {
4760
5763
  const bucket = [];
@@ -4762,7 +5765,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
4762
5765
  const targetRow = targetMap.get(toKey6(entry.targetId));
4763
5766
  if (!targetRow) continue;
4764
5767
  bucket.push({
4765
- ...targetRow,
5768
+ ...targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : targetRow,
4766
5769
  _pivot: entry.pivot
4767
5770
  });
4768
5771
  }
@@ -4792,12 +5795,13 @@ var relationLoaderCache = (meta, relationName, factory) => {
4792
5795
  }
4793
5796
  return promise;
4794
5797
  };
4795
- var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
5798
+ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
4796
5799
  const target = { ...row };
4797
5800
  const meta = {
4798
5801
  ctx,
4799
5802
  table,
4800
5803
  lazyRelations: [...lazyRelations],
5804
+ lazyRelationOptions: new Map(lazyRelationOptions),
4801
5805
  relationCache: /* @__PURE__ */ new Map(),
4802
5806
  relationHydration: /* @__PURE__ */ new Map(),
4803
5807
  relationWrappers: /* @__PURE__ */ new Map()
@@ -4838,14 +5842,14 @@ var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
4838
5842
  populateHydrationCache(proxy, row, meta);
4839
5843
  return proxy;
4840
5844
  };
4841
- var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
5845
+ var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
4842
5846
  const pkName = findPrimaryKey(table);
4843
5847
  const pkValue = row[pkName];
4844
5848
  if (pkValue !== void 0 && pkValue !== null) {
4845
5849
  const tracked = ctx.getEntity(table, pkValue);
4846
5850
  if (tracked) return tracked;
4847
5851
  }
4848
- const entity = createEntityProxy(ctx, table, row, lazyRelations);
5852
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
4849
5853
  if (pkValue !== void 0 && pkValue !== null) {
4850
5854
  ctx.trackManaged(table, pkValue, entity);
4851
5855
  } else {
@@ -4895,6 +5899,58 @@ var populateHydrationCache = (entity, row, meta) => {
4895
5899
  }
4896
5900
  }
4897
5901
  };
5902
+ var proxifyRelationWrapper = (wrapper) => {
5903
+ return new Proxy(wrapper, {
5904
+ get(target, prop, receiver) {
5905
+ if (typeof prop === "symbol") {
5906
+ return Reflect.get(target, prop, receiver);
5907
+ }
5908
+ if (prop in target) {
5909
+ return Reflect.get(target, prop, receiver);
5910
+ }
5911
+ const getItems = target.getItems;
5912
+ if (typeof getItems === "function") {
5913
+ const items = getItems.call(target);
5914
+ if (items && prop in items) {
5915
+ const propName = prop;
5916
+ const value = items[propName];
5917
+ return typeof value === "function" ? value.bind(items) : value;
5918
+ }
5919
+ }
5920
+ const getRef = target.get;
5921
+ if (typeof getRef === "function") {
5922
+ const current = getRef.call(target);
5923
+ if (current && prop in current) {
5924
+ const propName = prop;
5925
+ const value = current[propName];
5926
+ return typeof value === "function" ? value.bind(current) : value;
5927
+ }
5928
+ }
5929
+ return void 0;
5930
+ },
5931
+ set(target, prop, value, receiver) {
5932
+ if (typeof prop === "symbol") {
5933
+ return Reflect.set(target, prop, value, receiver);
5934
+ }
5935
+ if (prop in target) {
5936
+ return Reflect.set(target, prop, value, receiver);
5937
+ }
5938
+ const getRef = target.get;
5939
+ if (typeof getRef === "function") {
5940
+ const current = getRef.call(target);
5941
+ if (current && typeof current === "object") {
5942
+ return Reflect.set(current, prop, value);
5943
+ }
5944
+ }
5945
+ const getItems = target.getItems;
5946
+ if (typeof getItems === "function") {
5947
+ const items = getItems.call(target);
5948
+ return Reflect.set(items, prop, value);
5949
+ }
5950
+ return Reflect.set(target, prop, value, receiver);
5951
+ }
5952
+ });
5953
+ };
4898
5954
  var getRelationWrapper = (meta, relationName, owner) => {
4899
5955
  if (meta.relationWrappers.has(relationName)) {
4900
5956
  return meta.relationWrappers.get(relationName);
@@ -4902,12 +5958,13 @@ var getRelationWrapper = (meta, relationName, owner) => {
4902
5958
  const relation = meta.table.relations[relationName];
4903
5959
  if (!relation) return void 0;
4904
5960
  const wrapper = instantiateWrapper(meta, relationName, relation, owner);
4905
- if (wrapper) {
4906
- meta.relationWrappers.set(relationName, wrapper);
4907
- }
4908
- return wrapper;
5961
+ if (!wrapper) return void 0;
5962
+ const proxied = proxifyRelationWrapper(wrapper);
5963
+ meta.relationWrappers.set(relationName, proxied);
5964
+ return proxied;
4909
5965
  };
4910
5966
  var instantiateWrapper = (meta, relationName, relation, owner) => {
5967
+ const lazyOptions = meta.lazyRelationOptions.get(relationName);
4911
5968
  switch (relation.type) {
4912
5969
  case RelationKinds.HasOne: {
4913
5970
  const hasOne2 = relation;
@@ -4915,7 +5972,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4915
5972
  const loader = () => relationLoaderCache(
4916
5973
  meta,
4917
5974
  relationName,
4918
- () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
5975
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, lazyOptions)
4919
5976
  );
4920
5977
  return new DefaultHasOneReference(
4921
5978
  meta.ctx,
@@ -4935,7 +5992,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4935
5992
  const loader = () => relationLoaderCache(
4936
5993
  meta,
4937
5994
  relationName,
4938
- () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
5995
+ () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, lazyOptions)
4939
5996
  );
4940
5997
  return new DefaultHasManyCollection(
4941
5998
  meta.ctx,
@@ -4955,7 +6012,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4955
6012
  const loader = () => relationLoaderCache(
4956
6013
  meta,
4957
6014
  relationName,
4958
- () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
6015
+ () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, lazyOptions)
4959
6016
  );
4960
6017
  return new DefaultBelongsToReference(
4961
6018
  meta.ctx,
@@ -4975,7 +6032,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
4975
6032
  const loader = () => relationLoaderCache(
4976
6033
  meta,
4977
6034
  relationName,
4978
- () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
6035
+ () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
4979
6036
  );
4980
6037
  return new DefaultManyToManyCollection(
4981
6038
  meta.ctx,
@@ -5014,11 +6071,17 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
5014
6071
  const compiled = execCtx.dialect.compileSelect(ast);
5015
6072
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
5016
6073
  const rows = flattenResults(executed);
6074
+ const lazyRelations = qb.getLazyRelations();
6075
+ const lazyRelationOptions = qb.getLazyRelationOptions();
5017
6076
  if (ast.setOps && ast.setOps.length > 0) {
5018
- return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
6077
+ const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6078
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6079
+ return proxies;
5019
6080
  }
5020
6081
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
5021
- return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
6082
+ const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6083
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6084
+ return entities;
5022
6085
  };
5023
6086
  async function executeHydrated(session, qb) {
5024
6087
  return executeWithContexts(session.getExecutionContext(), session, qb);
@@ -5030,6 +6093,52 @@ async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
5030
6093
  }
5031
6094
  return executeWithContexts(execCtx, entityCtx, qb);
5032
6095
  }
6096
+ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOptions) => {
6097
+ if (!lazyRelations.length) return;
6098
+ const tracked = ctx.getEntitiesForTable(table);
6099
+ if (!tracked.length) return;
6100
+ const meta = getEntityMeta(tracked[0].entity);
6101
+ if (!meta) return;
6102
+ for (const relationName of lazyRelations) {
6103
+ const relation = table.relations[relationName];
6104
+ if (!relation) continue;
6105
+ const key = relationName;
6106
+ const options = lazyRelationOptions.get(key);
6107
+ if (!options) {
6108
+ continue;
6109
+ }
6110
+ switch (relation.type) {
6111
+ case RelationKinds.HasOne:
6112
+ await relationLoaderCache(
6113
+ meta,
6114
+ key,
6115
+ () => loadHasOneRelation(ctx, table, key, relation, options)
6116
+ );
6117
+ break;
6118
+ case RelationKinds.HasMany:
6119
+ await relationLoaderCache(
6120
+ meta,
6121
+ key,
6122
+ () => loadHasManyRelation(ctx, table, key, relation, options)
6123
+ );
6124
+ break;
6125
+ case RelationKinds.BelongsTo:
6126
+ await relationLoaderCache(
6127
+ meta,
6128
+ key,
6129
+ () => loadBelongsToRelation(ctx, table, key, relation, options)
6130
+ );
6131
+ break;
6132
+ case RelationKinds.BelongsToMany:
6133
+ await relationLoaderCache(
6134
+ meta,
6135
+ key,
6136
+ () => loadBelongsToManyRelation(ctx, table, key, relation, options)
6137
+ );
6138
+ break;
6139
+ }
6140
+ }
6141
+ };
5033
6142
 
5034
6143
  // src/query-builder/query-resolution.ts
5035
6144
  function resolveSelectQuery(query) {
@@ -5046,7 +6155,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5046
6155
  * @param hydration - Optional hydration manager
5047
6156
  * @param dependencies - Optional query builder dependencies
5048
6157
  */
5049
- constructor(table, state, hydration, dependencies, lazyRelations) {
6158
+ constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
5050
6159
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
5051
6160
  this.env = { table, deps };
5052
6161
  const initialState = state ?? deps.createState(table);
@@ -5056,6 +6165,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5056
6165
  hydration: initialHydration
5057
6166
  };
5058
6167
  this.lazyRelations = new Set(lazyRelations ?? []);
6168
+ this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
5059
6169
  this.columnSelector = deps.createColumnSelector(this.env);
5060
6170
  this.relationManager = deps.createRelationManager(this.env);
5061
6171
  }
@@ -5065,8 +6175,15 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5065
6175
  * @param lazyRelations - Updated lazy relations set
5066
6176
  * @returns New SelectQueryBuilder instance
5067
6177
  */
5068
- clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
5069
- return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
6178
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions)) {
6179
+ return new _SelectQueryBuilder(
6180
+ this.env.table,
6181
+ context.state,
6182
+ context.hydration,
6183
+ this.env.deps,
6184
+ lazyRelations,
6185
+ lazyRelationOptions
6186
+ );
5070
6187
  }
5071
6188
  /**
5072
6189
  * Applies an alias to the root FROM table.
@@ -5313,36 +6430,40 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5313
6430
  /**
5314
6431
  * Includes a relation lazily in the query results
5315
6432
  * @param relationName - Name of the relation to include lazily
6433
+ * @param options - Optional include options for lazy loading
5316
6434
  * @returns New query builder instance with lazy relation inclusion
5317
6435
  */
5318
- includeLazy(relationName) {
5319
- const nextLazy = new Set(this.lazyRelations);
5320
- nextLazy.add(relationName);
5321
- return this.clone(this.context, nextLazy);
5322
- }
5323
- /**
5324
- * Selects columns for a related table in a single hop.
5325
- */
5326
- selectRelationColumns(relationName, ...cols) {
6436
+ includeLazy(relationName, options) {
6437
+ let nextContext = this.context;
5327
6438
  const relation = this.env.table.relations[relationName];
5328
- if (!relation) {
5329
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
5330
- }
5331
- const target = relation.target;
5332
- for (const col2 of cols) {
5333
- if (!target.columns[col2]) {
5334
- throw new Error(
5335
- `Column '${col2}' not found on related table '${target.name}' for relation '${relationName}'`
5336
- );
6439
+ if (relation?.type === RelationKinds.BelongsTo) {
6440
+ const foreignKey = relation.foreignKey;
6441
+ const fkColumn = this.env.table.columns[foreignKey];
6442
+ if (fkColumn) {
6443
+ const hasAlias2 = nextContext.state.ast.columns.some((col2) => {
6444
+ const node = col2;
6445
+ return (node.alias ?? node.name) === foreignKey;
6446
+ });
6447
+ if (!hasAlias2) {
6448
+ nextContext = this.columnSelector.select(nextContext, { [foreignKey]: fkColumn });
6449
+ }
5337
6450
  }
5338
6451
  }
5339
- return this.include(relationName, { columns: cols });
6452
+ const nextLazy = new Set(this.lazyRelations);
6453
+ nextLazy.add(relationName);
6454
+ const nextOptions = new Map(this.lazyRelationOptions);
6455
+ if (options) {
6456
+ nextOptions.set(relationName, options);
6457
+ } else {
6458
+ nextOptions.delete(relationName);
6459
+ }
6460
+ return this.clone(nextContext, nextLazy, nextOptions);
5340
6461
  }
5341
6462
  /**
5342
- * Convenience alias for selecting specific columns from a relation.
6463
+ * Convenience alias for including only specific columns from a relation.
5343
6464
  */
5344
6465
  includePick(relationName, cols) {
5345
- return this.selectRelationColumns(relationName, ...cols);
6466
+ return this.include(relationName, { columns: cols });
5346
6467
  }
5347
6468
  /**
5348
6469
  * Selects columns for the root table and relations from an array of entries
@@ -5355,7 +6476,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5355
6476
  if (entry.type === "root") {
5356
6477
  currBuilder = currBuilder.select(...entry.columns);
5357
6478
  } else {
5358
- currBuilder = currBuilder.selectRelationColumns(entry.relationName, ...entry.columns);
6479
+ currBuilder = currBuilder.include(entry.relationName, { columns: entry.columns });
5359
6480
  }
5360
6481
  }
5361
6482
  return currBuilder;
@@ -5367,6 +6488,13 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5367
6488
  getLazyRelations() {
5368
6489
  return Array.from(this.lazyRelations);
5369
6490
  }
6491
+ /**
6492
+ * Gets lazy relation include options
6493
+ * @returns Map of relation names to include options
6494
+ */
6495
+ getLazyRelationOptions() {
6496
+ return new Map(this.lazyRelationOptions);
6497
+ }
5370
6498
  /**
5371
6499
  * Gets the table definition for this query builder
5372
6500
  * @returns Table definition
@@ -7791,6 +8919,14 @@ var repeat = (value, count2) => fn("REPEAT", [value, count2]);
7791
8919
  var lpad = (value, len, pad) => fn("LPAD", [value, len, pad]);
7792
8920
  var rpad = (value, len, pad) => fn("RPAD", [value, len, pad]);
7793
8921
  var space = (count2) => fn("SPACE", [count2]);
8922
+ var reverse = (value) => fn("REVERSE", [value]);
8923
+ var initcap = (value) => fn("INITCAP", [value]);
8924
+ var md5 = (value) => fn("MD5", [value]);
8925
+ var sha1 = (value) => fn("SHA1", [value]);
8926
+ var sha2 = (value, bits) => fn("SHA2", [value, bits]);
8927
+ var bitLength = (value) => fn("BIT_LENGTH", [value]);
8928
+ var octetLength = (value) => fn("OCTET_LENGTH", [value]);
8929
+ var chr = (code) => fn("CHR", [code]);
7794
8930
 
7795
8931
  // src/core/ddl/introspect/functions/mssql.ts
7796
8932
  var isColumnReference = (value) => typeof value === "object" && value !== null && !("type" in value) && "name" in value && typeof value.name === "string";
@@ -8349,6 +9485,8 @@ var sqrt = (value) => fn3("SQRT", [value]);
8349
9485
  var tan = (value) => fn3("TAN", [value]);
8350
9486
  var trunc = (value, decimals) => decimals === void 0 ? fn3("TRUNC", [value]) : fn3("TRUNC", [value, decimals]);
8351
9487
  var truncate = (value, decimals) => fn3("TRUNCATE", [value, decimals]);
9488
+ var log2 = (value) => fn3("LOG2", [value]);
9489
+ var cbrt = (value) => fn3("CBRT", [value]);
8352
9490
 
8353
9491
  // src/core/functions/datetime.ts
8354
9492
  var isColumnDef3 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
@@ -8360,12 +9498,15 @@ var toOperand4 = (input) => {
8360
9498
  var fn4 = (key, args) => ({
8361
9499
  type: "Function",
8362
9500
  name: key,
9501
+ fn: key,
8363
9502
  args: args.map(toOperand4)
8364
9503
  });
8365
9504
  var now = () => fn4("NOW", []);
8366
9505
  var currentDate = () => fn4("CURRENT_DATE", []);
8367
9506
  var currentTime = () => fn4("CURRENT_TIME", []);
8368
9507
  var utcNow = () => fn4("UTC_NOW", []);
9508
+ var localTime = () => fn4("LOCALTIME", []);
9509
+ var localTimestamp = () => fn4("LOCALTIMESTAMP", []);
8369
9510
  var extract = (part, date) => fn4("EXTRACT", [part, date]);
8370
9511
  var year = (date) => fn4("YEAR", [date]);
8371
9512
  var month = (date) => fn4("MONTH", [date]);
@@ -8380,6 +9521,72 @@ var endOfMonth = (date) => fn4("END_OF_MONTH", [date]);
8380
9521
  var dayOfWeek = (date) => fn4("DAY_OF_WEEK", [date]);
8381
9522
  var weekOfYear = (date) => fn4("WEEK_OF_YEAR", [date]);
8382
9523
  var dateTrunc = (part, date) => fn4("DATE_TRUNC", [part, date]);
9524
+ var age = (timestamp, baseTimestamp) => baseTimestamp === void 0 ? fn4("AGE", [timestamp]) : fn4("AGE", [timestamp, baseTimestamp]);
9525
+ var hour = (date) => fn4("HOUR", [date]);
9526
+ var minute = (date) => fn4("MINUTE", [date]);
9527
+ var second = (date) => fn4("SECOND", [date]);
9528
+ var quarter = (date) => fn4("QUARTER", [date]);
9529
+
9530
+ // src/core/functions/control-flow.ts
9531
+ var isColumnDef4 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9532
+ var toOperand5 = (input) => {
9533
+ if (isOperandNode(input)) return input;
9534
+ if (isColumnDef4(input)) return columnOperand(input);
9535
+ return valueToOperand(input);
9536
+ };
9537
+ var fn5 = (key, args) => ({
9538
+ type: "Function",
9539
+ name: key,
9540
+ fn: key,
9541
+ args: args.map(toOperand5)
9542
+ });
9543
+ var coalesce = (...args) => {
9544
+ if (args.length < 2) throw new Error("coalesce() expects at least 2 arguments");
9545
+ return fn5("COALESCE", args);
9546
+ };
9547
+ var nullif = (val1, val2) => fn5("NULLIF", [val1, val2]);
9548
+ var greatest = (...args) => {
9549
+ if (args.length < 2) throw new Error("greatest() expects at least 2 arguments");
9550
+ return fn5("GREATEST", args);
9551
+ };
9552
+ var least = (...args) => {
9553
+ if (args.length < 2) throw new Error("least() expects at least 2 arguments");
9554
+ return fn5("LEAST", args);
9555
+ };
9556
+ var ifNull = (val, defaultValue) => coalesce(val, defaultValue);
9557
+
9558
+ // src/core/functions/json.ts
9559
+ var isColumnDef5 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9560
+ var toOperand6 = (input) => {
9561
+ if (isOperandNode(input)) return input;
9562
+ if (isColumnDef5(input)) return columnOperand(input);
9563
+ return valueToOperand(input);
9564
+ };
9565
+ var fn6 = (key, args) => ({
9566
+ type: "Function",
9567
+ name: key,
9568
+ fn: key,
9569
+ args: args.map(toOperand6)
9570
+ });
9571
+ var jsonLength = (target, path) => path === void 0 ? fn6("JSON_LENGTH", [target]) : fn6("JSON_LENGTH", [target, path]);
9572
+ var jsonSet = (target, path, value) => fn6("JSON_SET", [target, path, value]);
9573
+ var jsonArrayAgg = (value) => fn6("JSON_ARRAYAGG", [value]);
9574
+ var jsonContains = (target, candidate, path) => path === void 0 ? fn6("JSON_CONTAINS", [target, candidate]) : fn6("JSON_CONTAINS", [target, candidate, path]);
9575
+
9576
+ // src/core/functions/array.ts
9577
+ var isColumnDef6 = (val) => !!val && typeof val === "object" && "type" in val && "name" in val;
9578
+ var toOperand7 = (input) => {
9579
+ if (isOperandNode(input)) return input;
9580
+ if (isColumnDef6(input)) return columnOperand(input);
9581
+ return valueToOperand(input);
9582
+ };
9583
+ var fn7 = (key, args) => ({
9584
+ type: "Function",
9585
+ name: key,
9586
+ fn: key,
9587
+ args: args.map(toOperand7)
9588
+ });
9589
+ var arrayAppend = (array, value) => fn7("ARRAY_APPEND", [array, value]);
8383
9590
 
8384
9591
  // src/orm/als.ts
8385
9592
  var AsyncLocalStorage = class {
@@ -8459,7 +9666,7 @@ var DefaultNamingStrategy = class {
8459
9666
  * @returns Capitalized table name (handles schema-qualified names)
8460
9667
  */
8461
9668
  tableToSymbol(table) {
8462
- const tableName = typeof table === "string" ? table : table.type === "DerivedTable" ? table.alias : table.name;
9669
+ const tableName = typeof table === "string" ? table : table.type === "DerivedTable" ? table.alias : table.type === "FunctionTable" ? table.alias ?? table.name : table.name;
8463
9670
  if (tableName.includes(".")) {
8464
9671
  return tableName.split(".").map((part) => this.capitalize(part)).join("");
8465
9672
  }
@@ -8615,6 +9822,7 @@ var TypeScriptGenerator = class {
8615
9822
  case "CaseExpression":
8616
9823
  case "WindowFunction":
8617
9824
  case "Cast":
9825
+ case "Collate":
8618
9826
  return this.printOperand(term);
8619
9827
  default:
8620
9828
  return this.printExpression(term);
@@ -8677,6 +9885,9 @@ var TypeScriptGenerator = class {
8677
9885
  visitCast(node) {
8678
9886
  return this.printCastOperand(node);
8679
9887
  }
9888
+ visitCollate(node) {
9889
+ return this.printCollateOperand(node);
9890
+ }
8680
9891
  visitAliasRef(node) {
8681
9892
  return `aliasRef('${node.name}')`;
8682
9893
  }
@@ -8688,12 +9899,12 @@ var TypeScriptGenerator = class {
8688
9899
  printBinaryExpression(binary) {
8689
9900
  const left2 = this.printOperand(binary.left);
8690
9901
  const right2 = this.printOperand(binary.right);
8691
- const fn5 = this.mapOp(binary.operator);
9902
+ const fn8 = this.mapOp(binary.operator);
8692
9903
  const args = [left2, right2];
8693
9904
  if (binary.escape) {
8694
9905
  args.push(this.printOperand(binary.escape));
8695
9906
  }
8696
- return `${fn5}(${args.join(", ")})`;
9907
+ return `${fn8}(${args.join(", ")})`;
8697
9908
  }
8698
9909
  /**
8699
9910
  * Prints a logical expression to TypeScript code
@@ -8722,13 +9933,13 @@ var TypeScriptGenerator = class {
8722
9933
  */
8723
9934
  printInExpression(inExpr) {
8724
9935
  const left2 = this.printOperand(inExpr.left);
8725
- const fn5 = this.mapOp(inExpr.operator);
9936
+ const fn8 = this.mapOp(inExpr.operator);
8726
9937
  if (Array.isArray(inExpr.right)) {
8727
9938
  const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
8728
- return `${fn5}(${left2}, [${values}])`;
9939
+ return `${fn8}(${left2}, [${values}])`;
8729
9940
  }
8730
9941
  const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
8731
- return `${fn5}(${left2}, (${subquery}))`;
9942
+ return `${fn8}(${left2}, (${subquery}))`;
8732
9943
  }
8733
9944
  /**
8734
9945
  * Prints a null expression to TypeScript code
@@ -8737,8 +9948,8 @@ var TypeScriptGenerator = class {
8737
9948
  */
8738
9949
  printNullExpression(nullExpr) {
8739
9950
  const left2 = this.printOperand(nullExpr.left);
8740
- const fn5 = this.mapOp(nullExpr.operator);
8741
- return `${fn5}(${left2})`;
9951
+ const fn8 = this.mapOp(nullExpr.operator);
9952
+ return `${fn8}(${left2})`;
8742
9953
  }
8743
9954
  /**
8744
9955
  * Prints a BETWEEN expression to TypeScript code
@@ -8782,9 +9993,9 @@ var TypeScriptGenerator = class {
8782
9993
  * @param fn - Function node
8783
9994
  * @returns TypeScript code representation
8784
9995
  */
8785
- printFunctionOperand(fn5) {
8786
- const args = fn5.args.map((a) => this.printOperand(a)).join(", ");
8787
- return `${fn5.name.toLowerCase()}(${args})`;
9996
+ printFunctionOperand(fn8) {
9997
+ const args = fn8.args.map((a) => this.printOperand(a)).join(", ");
9998
+ return `${fn8.name.toLowerCase()}(${args})`;
8788
9999
  }
8789
10000
  /**
8790
10001
  * Prints a JSON path operand to TypeScript code
@@ -8848,6 +10059,9 @@ var TypeScriptGenerator = class {
8848
10059
  const typeLiteral = node.castType.replace(/'/g, "\\'");
8849
10060
  return `cast(${this.printOperand(node.expression)}, '${typeLiteral}')`;
8850
10061
  }
10062
+ printCollateOperand(node) {
10063
+ return `collate(${this.printOperand(node.expression)}, '${node.collation}')`;
10064
+ }
8851
10065
  /**
8852
10066
  * Converts method chain lines to inline format
8853
10067
  * @param lines - Method chain lines
@@ -8878,10 +10092,20 @@ var IdentityMap = class {
8878
10092
  get bucketsMap() {
8879
10093
  return this.buckets;
8880
10094
  }
10095
+ /**
10096
+ * Retrieves an entity from the identity map if it exists.
10097
+ * @param table The table definition of the entity.
10098
+ * @param pk The primary key value.
10099
+ * @returns The entity instance if found, undefined otherwise.
10100
+ */
8881
10101
  getEntity(table, pk) {
8882
10102
  const bucket = this.buckets.get(table.name);
8883
10103
  return bucket?.get(this.toIdentityKey(pk))?.entity;
8884
10104
  }
10105
+ /**
10106
+ * Registers a tracked entity in the identity map.
10107
+ * @param tracked The tracked entity metadata and instance.
10108
+ */
8885
10109
  register(tracked) {
8886
10110
  if (tracked.pk == null) return;
8887
10111
  const bucket = this.buckets.get(tracked.table.name) ?? /* @__PURE__ */ new Map();
@@ -8893,6 +10117,11 @@ var IdentityMap = class {
8893
10117
  const bucket = this.buckets.get(tracked.table.name);
8894
10118
  bucket?.delete(this.toIdentityKey(tracked.pk));
8895
10119
  }
10120
+ /**
10121
+ * Returns all tracked entities for a specific table.
10122
+ * @param table The table definition.
10123
+ * @returns Array of tracked entities.
10124
+ */
8896
10125
  getEntitiesForTable(table) {
8897
10126
  const bucket = this.buckets.get(table.name);
8898
10127
  return bucket ? Array.from(bucket.values()) : [];
@@ -10017,15 +11246,15 @@ var OrmSession = class {
10017
11246
  * @returns The result of the function
10018
11247
  * @throws If the transaction fails
10019
11248
  */
10020
- async transaction(fn5) {
11249
+ async transaction(fn8) {
10021
11250
  if (!this.executor.capabilities.transactions) {
10022
- const result = await fn5(this);
11251
+ const result = await fn8(this);
10023
11252
  await this.commit();
10024
11253
  return result;
10025
11254
  }
10026
11255
  await this.executor.beginTransaction();
10027
11256
  try {
10028
- const result = await fn5(this);
11257
+ const result = await fn8(this);
10029
11258
  await this.flushWithHooks();
10030
11259
  await this.executor.commitTransaction();
10031
11260
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
@@ -10128,11 +11357,11 @@ var Orm = class {
10128
11357
  * @returns The result of the function
10129
11358
  * @throws If the transaction fails
10130
11359
  */
10131
- async transaction(fn5) {
11360
+ async transaction(fn8) {
10132
11361
  const executor = this.executorFactory.createTransactionalExecutor();
10133
11362
  const session = new OrmSession({ orm: this, executor });
10134
11363
  try {
10135
- return await session.transaction(() => fn5(session));
11364
+ return await session.transaction(() => fn8(session));
10136
11365
  } finally {
10137
11366
  await session.dispose();
10138
11367
  }
@@ -10958,8 +12187,10 @@ function createPooledExecutorFactory(opts) {
10958
12187
  acos,
10959
12188
  add,
10960
12189
  addDomainEvent,
12190
+ age,
10961
12191
  aliasRef,
10962
12192
  and,
12193
+ arrayAppend,
10963
12194
  ascii,
10964
12195
  asin,
10965
12196
  atan,
@@ -10968,16 +12199,24 @@ function createPooledExecutorFactory(opts) {
10968
12199
  belongsTo,
10969
12200
  belongsToMany,
10970
12201
  between,
12202
+ bitAnd,
12203
+ bitLength,
12204
+ bitOr,
12205
+ bitXor,
10971
12206
  bootstrapEntities,
10972
12207
  caseWhen,
10973
12208
  cast,
12209
+ cbrt,
10974
12210
  ceil,
10975
12211
  ceiling,
10976
12212
  char,
10977
12213
  charLength,
12214
+ chr,
10978
12215
  clearExpressionDispatchers,
10979
12216
  clearOperandDispatchers,
12217
+ coalesce,
10980
12218
  col,
12219
+ collate,
10981
12220
  columnOperand,
10982
12221
  concat,
10983
12222
  concatWs,
@@ -11029,18 +12268,23 @@ function createPooledExecutorFactory(opts) {
11029
12268
  getDecoratorMetadata,
11030
12269
  getSchemaIntrospector,
11031
12270
  getTableDefFromEntity,
12271
+ greatest,
11032
12272
  groupConcat,
11033
12273
  gt,
11034
12274
  gte,
11035
12275
  hasMany,
11036
12276
  hasOne,
12277
+ hour,
11037
12278
  hydrateRows,
12279
+ ifNull,
11038
12280
  inList,
11039
12281
  inSubquery,
12282
+ initcap,
11040
12283
  instr,
11041
12284
  introspectSchema,
11042
12285
  isCaseExpressionNode,
11043
12286
  isCastExpressionNode,
12287
+ isCollateExpressionNode,
11044
12288
  isExpressionSelectionNode,
11045
12289
  isFunctionNode,
11046
12290
  isNotNull,
@@ -11048,11 +12292,16 @@ function createPooledExecutorFactory(opts) {
11048
12292
  isOperandNode,
11049
12293
  isValueOperandInput,
11050
12294
  isWindowFunctionNode,
12295
+ jsonArrayAgg,
12296
+ jsonContains,
12297
+ jsonLength,
11051
12298
  jsonPath,
12299
+ jsonSet,
11052
12300
  jsonify,
11053
12301
  lag,
11054
12302
  lastValue,
11055
12303
  lead,
12304
+ least,
11056
12305
  left,
11057
12306
  length,
11058
12307
  like,
@@ -11061,9 +12310,12 @@ function createPooledExecutorFactory(opts) {
11061
12310
  loadBelongsToRelation,
11062
12311
  loadHasManyRelation,
11063
12312
  loadHasOneRelation,
12313
+ localTime,
12314
+ localTimestamp,
11064
12315
  locate,
11065
12316
  log,
11066
12317
  log10,
12318
+ log2,
11067
12319
  logBase,
11068
12320
  lower,
11069
12321
  lpad,
@@ -11071,7 +12323,9 @@ function createPooledExecutorFactory(opts) {
11071
12323
  lte,
11072
12324
  ltrim,
11073
12325
  max,
12326
+ md5,
11074
12327
  min,
12328
+ minute,
11075
12329
  mod,
11076
12330
  month,
11077
12331
  mul,
@@ -11084,12 +12338,15 @@ function createPooledExecutorFactory(opts) {
11084
12338
  notLike,
11085
12339
  now,
11086
12340
  ntile,
12341
+ nullif,
12342
+ octetLength,
11087
12343
  or,
11088
12344
  outerRef,
11089
12345
  pi,
11090
12346
  position,
11091
12347
  pow,
11092
12348
  power,
12349
+ quarter,
11093
12350
  radians,
11094
12351
  rand,
11095
12352
  random,
@@ -11097,22 +12354,30 @@ function createPooledExecutorFactory(opts) {
11097
12354
  registerExpressionDispatcher,
11098
12355
  registerOperandDispatcher,
11099
12356
  registerSchemaIntrospector,
12357
+ relationLoaderCache,
11100
12358
  renderColumnDefinition,
11101
12359
  renderTypeWithArgs,
11102
12360
  repeat,
11103
12361
  replace,
12362
+ reverse,
11104
12363
  right,
11105
12364
  round,
11106
12365
  rowNumber,
11107
12366
  rowsToQueryResult,
11108
12367
  rpad,
11109
12368
  rtrim,
12369
+ second,
11110
12370
  sel,
11111
12371
  selectFromEntity,
12372
+ sha1,
12373
+ sha2,
12374
+ shiftLeft,
12375
+ shiftRight,
11112
12376
  sign,
11113
12377
  sin,
11114
12378
  space,
11115
12379
  sqrt,
12380
+ stddev,
11116
12381
  sub,
11117
12382
  substr,
11118
12383
  sum,
@@ -11128,6 +12393,7 @@ function createPooledExecutorFactory(opts) {
11128
12393
  upper,
11129
12394
  utcNow,
11130
12395
  valueToOperand,
12396
+ variance,
11131
12397
  visitExpression,
11132
12398
  visitOperand,
11133
12399
  weekOfYear,