@dbsp/adapter-pgsql 1.1.1 → 1.2.1

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.
package/dist/index.js CHANGED
@@ -1074,6 +1074,19 @@ var init_param_ref = __esm({
1074
1074
  }
1075
1075
  });
1076
1076
 
1077
+ // src/handlers/expression/param-value.ts
1078
+ function bindParameter(value, state) {
1079
+ const idx = ++state.paramIndex;
1080
+ state.parameters.push(value);
1081
+ return createParamRef(idx);
1082
+ }
1083
+ var init_param_value = __esm({
1084
+ "src/handlers/expression/param-value.ts"() {
1085
+ "use strict";
1086
+ init_param_ref();
1087
+ }
1088
+ });
1089
+
1077
1090
  // src/handlers/expression/case-value.ts
1078
1091
  function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler) {
1079
1092
  if (value === null || value === void 0) {
@@ -1083,12 +1096,12 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
1083
1096
  return columnRef(value, alias, schema, naming);
1084
1097
  }
1085
1098
  if (typeof value !== "object") {
1086
- const idx = ++state.paramIndex;
1087
- state.parameters.push(value);
1088
- return createParamRef(idx);
1099
+ return bindParameter(value, state);
1089
1100
  }
1090
1101
  const expr = value;
1091
1102
  switch (expr.kind) {
1103
+ case "param":
1104
+ return bindParameter(expr.value, state);
1092
1105
  case "literal":
1093
1106
  if (expr.value === null || expr.value === void 0)
1094
1107
  return nullConstNode();
@@ -1099,11 +1112,7 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
1099
1112
  return integerNode(expr.value);
1100
1113
  return floatNode(String(expr.value));
1101
1114
  }
1102
- {
1103
- const idx = ++state.paramIndex;
1104
- state.parameters.push(expr.value);
1105
- return createParamRef(idx);
1106
- }
1115
+ return bindParameter(expr.value, state);
1107
1116
  case "column":
1108
1117
  return columnRef(expr.column, alias, schema, naming);
1109
1118
  case "arithmetic": {
@@ -1139,9 +1148,7 @@ function resolveCaseValue(value, alias, schema, naming, state, nestedCaseHandler
1139
1148
  }
1140
1149
  // falls through
1141
1150
  default: {
1142
- const idx = ++state.paramIndex;
1143
- state.parameters.push(value);
1144
- return createParamRef(idx);
1151
+ return bindParameter(value, state);
1145
1152
  }
1146
1153
  }
1147
1154
  }
@@ -1149,7 +1156,7 @@ var init_case_value = __esm({
1149
1156
  "src/handlers/expression/case-value.ts"() {
1150
1157
  "use strict";
1151
1158
  init_ast_helpers();
1152
- init_param_ref();
1159
+ init_param_value();
1153
1160
  }
1154
1161
  });
1155
1162
 
@@ -1348,7 +1355,9 @@ function compileExpressionIntent(intent, ctx, state) {
1348
1355
  }
1349
1356
  case "relationColumn": {
1350
1357
  const rc = intent;
1351
- const alias = state.aliases.get(rc.relation) ?? rc.relation;
1358
+ const relationSegments = rc.relation.split(".");
1359
+ const leaf = relationSegments[relationSegments.length - 1] ?? rc.relation;
1360
+ const alias = state.aliases.get(rc.relation) ?? state.aliases.get(leaf) ?? leaf;
1352
1361
  return columnRef(rc.column, alias, void 0, ctx.naming);
1353
1362
  }
1354
1363
  case "case": {
@@ -1428,7 +1437,17 @@ function handleAggregateExpression(expr, rootTable, decisions, applyFilter) {
1428
1437
  const aggAs = expr.as;
1429
1438
  const aggDistinct = expr.distinct;
1430
1439
  const aggFilter = expr.filter;
1431
- if (aggFunc === "count" && !aggField) {
1440
+ const aggExtraArgs = Array.isArray(expr.extraArgs) ? expr.extraArgs : void 0;
1441
+ if (!aggField && aggExtraArgs?.length) {
1442
+ const decision = {
1443
+ type: "selectNqlFunction",
1444
+ function: aggFunc,
1445
+ args: aggExtraArgs,
1446
+ table: rootTable
1447
+ };
1448
+ if (aggAs) decision.alias = aggAs;
1449
+ decisions.push(decision);
1450
+ } else if (aggFunc === "count" && !aggField) {
1432
1451
  const decision = {
1433
1452
  type: "selectFunction",
1434
1453
  function: "count",
@@ -1480,6 +1499,16 @@ function handleRawExpression(expr, rootTable, decisions) {
1480
1499
  if (expr.as) decision.alias = expr.as;
1481
1500
  decisions.push(decision);
1482
1501
  }
1502
+ function handleFunctionExpression(expr, rootTable, decisions) {
1503
+ const decision = {
1504
+ type: "selectNqlFunction",
1505
+ function: expr.name,
1506
+ args: expr.args ?? [],
1507
+ table: rootTable
1508
+ };
1509
+ if (expr.as) decision.alias = expr.as;
1510
+ decisions.push(decision);
1511
+ }
1483
1512
  function handleWindowExpression(expr, rootTable, decisions) {
1484
1513
  const windowFunc = expr.function;
1485
1514
  const windowAlias = expr.alias;
@@ -1592,6 +1621,7 @@ var init_select_expression_handlers = __esm({
1592
1621
  aggregate: (expr, rootTable, decisions, applyFilter) => handleAggregateExpression(expr, rootTable, decisions, applyFilter),
1593
1622
  coalesce: (expr, rootTable, decisions) => handleCoalesceExpression(expr, rootTable, decisions),
1594
1623
  raw: (expr, rootTable, decisions) => handleRawExpression(expr, rootTable, decisions),
1624
+ function: (expr, rootTable, decisions) => handleFunctionExpression(expr, rootTable, decisions),
1595
1625
  window: (expr, rootTable, decisions) => handleWindowExpression(expr, rootTable, decisions),
1596
1626
  case: (expr, rootTable, decisions, applyFilter, convertCondition) => handleCaseExpression(
1597
1627
  expr,
@@ -1601,7 +1631,8 @@ var init_select_expression_handlers = __esm({
1601
1631
  convertCondition
1602
1632
  ),
1603
1633
  relationColumn: (expr, rootTable, decisions) => handleRelationColumnExpression(expr, rootTable, decisions),
1604
- // pseudoColumn: intentional no-op — planner hints, not SQL
1634
+ pseudoColumn: () => {
1635
+ },
1605
1636
  arithmetic: (expr, rootTable, decisions) => handleArithmeticExpression(expr, rootTable, decisions),
1606
1637
  jsonExtract: (expr, rootTable, decisions) => handleJsonExtractExpression(expr, rootTable, decisions),
1607
1638
  jsonPathExtract: (expr, rootTable, decisions) => handleJsonPathExtractExpression(expr, rootTable, decisions),
@@ -1612,12 +1643,17 @@ var init_select_expression_handlers = __esm({
1612
1643
  ref: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1613
1644
  param: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1614
1645
  cast: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1615
- unary: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions)
1646
+ unary: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1647
+ subquery: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1648
+ namedArg: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1649
+ star: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions),
1650
+ array: (expr, rootTable, decisions) => handleCustomExpressionSelect(expr, rootTable, decisions)
1616
1651
  };
1617
1652
  }
1618
1653
  });
1619
1654
 
1620
1655
  // src/intent-to-decisions.ts
1656
+ import { isParamIntent } from "@dbsp/types";
1621
1657
  function intentToDecisions(intent, rootTable) {
1622
1658
  const decisions = [];
1623
1659
  if (intent.select) {
@@ -1680,16 +1716,18 @@ function convertSelect(select, rootTable) {
1680
1716
  const columns = select.columns;
1681
1717
  for (const exprUnknown of columns) {
1682
1718
  const expr = exprUnknown;
1683
- const handler = EXPRESSION_HANDLERS[expr.kind];
1684
- if (handler) {
1685
- handler(
1686
- expr,
1687
- rootTable,
1688
- decisions,
1689
- applyFilterCondition,
1690
- convertWhereCondition
1691
- );
1719
+ const kind = typeof expr.kind === "string" ? expr.kind : String(expr.kind);
1720
+ const handler = EXPRESSION_HANDLERS[kind];
1721
+ if (!handler) {
1722
+ throw new UnknownSelectExpressionKindError(kind);
1692
1723
  }
1724
+ handler(
1725
+ expr,
1726
+ rootTable,
1727
+ decisions,
1728
+ (decision, filter, table) => applyFilterCondition(decision, filter, table),
1729
+ (condition, table) => convertWhereCondition(condition, table)
1730
+ );
1693
1731
  }
1694
1732
  return decisions;
1695
1733
  }
@@ -1822,10 +1860,12 @@ function isOuterRef(value) {
1822
1860
  function containsOuterRef(where) {
1823
1861
  if (!where || typeof where !== "object") return false;
1824
1862
  const w = where;
1863
+ if (isParamIntent(w)) return false;
1825
1864
  if (isOuterRef(w)) return true;
1826
1865
  for (const value of Object.values(w)) {
1827
1866
  if (Array.isArray(value)) {
1828
- for (const item of value) {
1867
+ for (let i = 0; i < value.length; i++) {
1868
+ const item = value[i];
1829
1869
  if (containsOuterRef(item)) return true;
1830
1870
  }
1831
1871
  } else if (typeof value === "object" && value !== null) {
@@ -2122,7 +2162,7 @@ function convertWhereCondition(condition, rootTable) {
2122
2162
  table: rootTable
2123
2163
  };
2124
2164
  }
2125
- case "jsonContains":
2165
+ case "jsonContains": {
2126
2166
  return {
2127
2167
  type: "where",
2128
2168
  column: cond.field,
@@ -2130,6 +2170,7 @@ function convertWhereCondition(condition, rootTable) {
2130
2170
  value: cond.value,
2131
2171
  table: rootTable
2132
2172
  };
2173
+ }
2133
2174
  case "any":
2134
2175
  return {
2135
2176
  type: "where",
@@ -2186,10 +2227,20 @@ function convertOrderBy(order, rootTable) {
2186
2227
  }
2187
2228
  return decision;
2188
2229
  }
2230
+ var UnknownSelectExpressionKindError;
2189
2231
  var init_intent_to_decisions = __esm({
2190
2232
  "src/intent-to-decisions.ts"() {
2191
2233
  "use strict";
2192
2234
  init_select_expression_handlers();
2235
+ UnknownSelectExpressionKindError = class extends Error {
2236
+ constructor(kind) {
2237
+ super(`Unknown SELECT expression kind: ${kind}`);
2238
+ this.kind = kind;
2239
+ this.name = "UnknownSelectExpressionKindError";
2240
+ }
2241
+ kind;
2242
+ code = "ERR_ADAPTER_UNKNOWN_SELECT_EXPRESSION_KIND";
2243
+ };
2193
2244
  }
2194
2245
  });
2195
2246
 
@@ -2750,12 +2801,24 @@ var init_compiler_utils = __esm({
2750
2801
  }
2751
2802
  });
2752
2803
 
2804
+ // src/param-intent.ts
2805
+ import { isParamIntent as isParamIntent2 } from "@dbsp/types";
2806
+ function unwrapParamIntent(value) {
2807
+ return isParamIntent2(value) ? value.value : value;
2808
+ }
2809
+ var init_param_intent = __esm({
2810
+ "src/param-intent.ts"() {
2811
+ "use strict";
2812
+ }
2813
+ });
2814
+
2753
2815
  // src/handlers/where/utils.ts
2754
- import { isFieldRef } from "@dbsp/types";
2816
+ import { isFieldRef, isParamIntent as isParamIntent3 } from "@dbsp/types";
2755
2817
  function buildColumnRef(column, ctx) {
2756
2818
  if (column.includes(".")) {
2757
- const dotIndex = column.indexOf(".");
2758
- const table = column.substring(0, dotIndex);
2819
+ const dotIndex = column.lastIndexOf(".");
2820
+ const relation = column.substring(0, dotIndex);
2821
+ const table = ctx.aliases?.get(relation) ?? relation;
2759
2822
  const col = column.substring(dotIndex + 1);
2760
2823
  return columnRef(col, table, void 0, ctx.naming);
2761
2824
  }
@@ -2763,15 +2826,27 @@ function buildColumnRef(column, ctx) {
2763
2826
  return columnRef(column, alias, void 0, ctx.naming);
2764
2827
  }
2765
2828
  function buildParamRef(value, state) {
2766
- if (isParamRef(value)) {
2767
- state.parameters.push(value.value);
2768
- return createParamRef(value.paramIndex);
2829
+ const boundValue = unwrapParamIntent(value);
2830
+ if (isParamRef(boundValue)) {
2831
+ state.parameters.push(boundValue.value);
2832
+ return createParamRef(boundValue.paramIndex);
2769
2833
  }
2770
2834
  state.paramIndex++;
2771
- state.parameters.push(value);
2835
+ state.parameters.push(boundValue);
2772
2836
  return createParamRef(state.paramIndex);
2773
2837
  }
2774
- function compileValue(value, state, columnType) {
2838
+ function compileValue(value, state, columnType, forceParam = false) {
2839
+ const boundValue = unwrapParamIntent(value);
2840
+ if (isParamIntent3(value)) {
2841
+ const idx2 = ++state.paramIndex;
2842
+ state.parameters.push(boundValue);
2843
+ return columnType ? createTypeCastParamRef(idx2, columnType) : createParamRef(idx2);
2844
+ }
2845
+ if (forceParam) {
2846
+ const idx2 = ++state.paramIndex;
2847
+ state.parameters.push(boundValue);
2848
+ return columnType ? createTypeCastParamRef(idx2, columnType) : createParamRef(idx2);
2849
+ }
2775
2850
  if (value === null || value === void 0) {
2776
2851
  return nullConstNode();
2777
2852
  }
@@ -2783,7 +2858,10 @@ function compileValue(value, state, columnType) {
2783
2858
  state.parameters.push(value);
2784
2859
  return columnType ? createTypeCastParamRef(idx, columnType) : createParamRef(idx);
2785
2860
  }
2786
- function compileValueOrFieldRef(value, ctx, state, columnType) {
2861
+ function compileValueOrFieldRef(value, ctx, state, columnType, forceParam = false) {
2862
+ if (forceParam || isParamIntent3(value)) {
2863
+ return compileValue(value, state, columnType, true);
2864
+ }
2787
2865
  if (isFieldRef(value)) {
2788
2866
  const alias = value.scope === "outer" ? ctx.outerAlias ?? ctx.rootTable : ctx.currentAlias ?? ctx.rootTable;
2789
2867
  return columnRef(value.column, alias, void 0, ctx.naming);
@@ -2803,9 +2881,11 @@ var init_utils = __esm({
2803
2881
  "src/handlers/where/utils.ts"() {
2804
2882
  "use strict";
2805
2883
  init_ast_helpers();
2884
+ init_param_intent();
2806
2885
  init_param_ref();
2807
2886
  init_validate();
2808
2887
  init_types();
2888
+ init_param_intent();
2809
2889
  }
2810
2890
  });
2811
2891
 
@@ -2835,6 +2915,7 @@ var init_any = __esm({
2835
2915
  "src/handlers/where/any.ts"() {
2836
2916
  "use strict";
2837
2917
  init_compiler_utils();
2918
+ init_param_intent();
2838
2919
  init_param_ref();
2839
2920
  init_types();
2840
2921
  init_utils();
@@ -2845,7 +2926,8 @@ var init_any = __esm({
2845
2926
  if (!column) {
2846
2927
  throw new Error("ANY handler requires a column");
2847
2928
  }
2848
- const values = Array.isArray(decision.values) ? decision.values : [];
2929
+ const rawValues = unwrapParamIntent(decision.values);
2930
+ const values = Array.isArray(rawValues) ? rawValues : [];
2849
2931
  const columnNode = buildColumnRef(column, ctx);
2850
2932
  let pgBaseType;
2851
2933
  if (decision.dataType) {
@@ -2875,6 +2957,7 @@ var betweenHandler;
2875
2957
  var init_between = __esm({
2876
2958
  "src/handlers/where/between.ts"() {
2877
2959
  "use strict";
2960
+ init_param_intent();
2878
2961
  init_param_ref();
2879
2962
  init_utils();
2880
2963
  betweenHandler = {
@@ -2884,16 +2967,18 @@ var init_between = __esm({
2884
2967
  if (!column) {
2885
2968
  throw new Error("BETWEEN handler requires a column");
2886
2969
  }
2887
- const range = decision.value;
2970
+ const range = unwrapParamIntent(decision.value);
2888
2971
  if (!Array.isArray(range) || range.length !== 2) {
2889
2972
  throw new Error("BETWEEN condition requires [min, max] array");
2890
2973
  }
2891
2974
  const columnNode = buildColumnRef(column, ctx);
2975
+ const minValue = unwrapParamIntent(range[0]);
2976
+ const maxValue = unwrapParamIntent(range[1]);
2892
2977
  const minIdx = ++state.paramIndex;
2893
- state.parameters.push(range[0]);
2978
+ state.parameters.push(minValue);
2894
2979
  const minNode = createParamRef(minIdx);
2895
2980
  const maxIdx = ++state.paramIndex;
2896
- state.parameters.push(range[1]);
2981
+ state.parameters.push(maxValue);
2897
2982
  const maxNode = createParamRef(maxIdx);
2898
2983
  return {
2899
2984
  A_Expr: {
@@ -2969,6 +3054,7 @@ var init_custom_expression = __esm({
2969
3054
  "src/handlers/where/custom-expression.ts"() {
2970
3055
  "use strict";
2971
3056
  init_ast_helpers();
3057
+ init_param_intent();
2972
3058
  init_param_ref();
2973
3059
  init_custom();
2974
3060
  OP_MAP = {
@@ -2994,7 +3080,7 @@ var init_custom_expression = __esm({
2994
3080
  return leftNode;
2995
3081
  }
2996
3082
  const idx = ++state.paramIndex;
2997
- state.parameters.push(decision.value);
3083
+ state.parameters.push(unwrapParamIntent(decision.value));
2998
3084
  const rightNode = createParamRef(idx);
2999
3085
  const rawOp = decision.subqueryOperator ?? decision.operator ?? "=";
3000
3086
  const sqlOp = OP_MAP[rawOp];
@@ -3202,6 +3288,7 @@ var init_exists = __esm({
3202
3288
  });
3203
3289
 
3204
3290
  // src/handlers/where/in.ts
3291
+ import { isParamIntent as isParamIntent4 } from "@dbsp/types";
3205
3292
  function createInExpr(columnNode, state, values, columnType) {
3206
3293
  state.paramIndex++;
3207
3294
  state.parameters.push(values);
@@ -3228,11 +3315,22 @@ function createNotInExpr(columnNode, state, values, columnType) {
3228
3315
  }
3229
3316
  };
3230
3317
  }
3318
+ function isWholeArrayParamList(value) {
3319
+ return value.length === 1 && isParamIntent4(value[0]) && Array.isArray(value[0].value);
3320
+ }
3321
+ function unwrapCompilerInListValues(value) {
3322
+ const values = [];
3323
+ for (const item of value) {
3324
+ values.push(unwrapParamIntent(item));
3325
+ }
3326
+ return values;
3327
+ }
3231
3328
  var inHandler;
3232
3329
  var init_in = __esm({
3233
3330
  "src/handlers/where/in.ts"() {
3234
3331
  "use strict";
3235
3332
  init_ast_helpers();
3333
+ init_param_intent();
3236
3334
  init_param_ref();
3237
3335
  init_types();
3238
3336
  init_utils();
@@ -3241,7 +3339,7 @@ var init_in = __esm({
3241
3339
  compile(decision, ctx, state) {
3242
3340
  const operator = decision.operator ?? "in";
3243
3341
  const column = decision.column;
3244
- const value = decision.value;
3342
+ const value = unwrapParamIntent(decision.value);
3245
3343
  if (!column) {
3246
3344
  throw new Error("In handler requires a column");
3247
3345
  }
@@ -3251,7 +3349,7 @@ var init_in = __esm({
3251
3349
  `[in handler] Received a non-array value for operator '${operator}' on column '${column}'. This is a compiler bug: ${hint}. File a bug report.`
3252
3350
  );
3253
3351
  }
3254
- const values = value;
3352
+ const values = isWholeArrayParamList(value) ? value[0].value : unwrapCompilerInListValues(value);
3255
3353
  if (values.length === 0) {
3256
3354
  if (operator === COLLECTION_OPERATORS.NOT_IN || operator === "notIn") {
3257
3355
  return booleanConstNode(true);
@@ -3494,6 +3592,7 @@ var RANGE_OP_MAP, rangeHandler;
3494
3592
  var init_range = __esm({
3495
3593
  "src/handlers/where/range.ts"() {
3496
3594
  "use strict";
3595
+ init_param_intent();
3497
3596
  init_param_ref();
3498
3597
  init_types();
3499
3598
  init_utils();
@@ -3511,11 +3610,14 @@ var init_range = __esm({
3511
3610
  }
3512
3611
  const operator = decision.operator ?? "contains";
3513
3612
  const pgOp = RANGE_OP_MAP[operator] ?? operator;
3514
- const value = decision.value;
3613
+ const rawValue = decision.value;
3614
+ const value = unwrapParamIntent(rawValue);
3515
3615
  const columnNode = buildColumnRef(column, ctx);
3516
3616
  let paramValue;
3517
3617
  let isScalar = false;
3518
- if (isRangeValue(value)) {
3618
+ if (value !== rawValue) {
3619
+ paramValue = value;
3620
+ } else if (isRangeValue(value)) {
3519
3621
  const lower = value.lower ?? "";
3520
3622
  const upper = value.upper ?? "";
3521
3623
  paramValue = `[${lower},${upper})`;
@@ -3699,7 +3801,7 @@ function handleExpressionIntent(intent, ctx, handlerCtx) {
3699
3801
  return leftNode;
3700
3802
  }
3701
3803
  const idx = ++ctx.paramState.paramIndex;
3702
- ctx.paramState.parameters.push(exprIntent.value);
3804
+ ctx.paramState.parameters.push(unwrapParamIntent(exprIntent.value));
3703
3805
  const rightNode = createParamRef(idx);
3704
3806
  const sqlOp = OP_MAP2[exprIntent.operator] ?? "=";
3705
3807
  return binaryExpr(sqlOp, leftNode, rightNode);
@@ -3981,9 +4083,13 @@ function compileWhereIntent(intent, ctx) {
3981
4083
  if (exprRefResult !== null) return exprRefResult;
3982
4084
  }
3983
4085
  const needsColumn = intent.kind === "comparison" || intent.kind === "null";
4086
+ const rawIntent = intent;
3984
4087
  const bridged = needsColumn ? {
3985
4088
  ...intent,
3986
- column: intent.field
4089
+ column: rawIntent.field,
4090
+ ..."value" in rawIntent && {
4091
+ value: rawIntent.value
4092
+ }
3987
4093
  } : intent;
3988
4094
  return dispatcher(bridged, handlerCtx, ctx.paramState);
3989
4095
  }
@@ -3999,6 +4105,7 @@ var init_compile_where = __esm({
3999
4105
  init_utils();
4000
4106
  init_intent_to_decisions();
4001
4107
  init_naming_plugin();
4108
+ init_param_intent();
4002
4109
  init_param_ref();
4003
4110
  registerWhereDispatcherFactory(createWhereDispatcher);
4004
4111
  OP_MAP2 = {
@@ -4037,7 +4144,8 @@ var init_raw_exists = __esm({
4037
4144
  subIntent,
4038
4145
  state.paramIndex,
4039
4146
  ctx.naming,
4040
- ctx.schema
4147
+ ctx.schema,
4148
+ "rawExists"
4041
4149
  );
4042
4150
  if (innerParams) {
4043
4151
  for (const p of innerParams) {
@@ -4169,6 +4277,7 @@ var init_relation_filter = __esm({
4169
4277
  });
4170
4278
 
4171
4279
  // src/subquery-emission.ts
4280
+ import { isParamIntent as isParamIntent5 } from "@dbsp/types";
4172
4281
  function assertNoDroppedDecisionModifiers(decision, use) {
4173
4282
  const d = decision;
4174
4283
  const unsupported = [];
@@ -4287,6 +4396,12 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
4287
4396
  if (decision.limit != null) {
4288
4397
  if (typeof decision.limit === "number") {
4289
4398
  stmt.limitCount = integerNode(decision.limit);
4399
+ } else if (isParamIntent5(decision.limit)) {
4400
+ state.parameters.push(unwrapParamIntent(decision.limit));
4401
+ state.paramIndex++;
4402
+ stmt.limitCount = {
4403
+ ParamRef: { number: state.paramIndex }
4404
+ };
4290
4405
  } else {
4291
4406
  const limitObj = decision.limit;
4292
4407
  if (typeof limitObj.paramIndex !== "number") {
@@ -4304,6 +4419,7 @@ var init_subquery_emission = __esm({
4304
4419
  "use strict";
4305
4420
  init_ast_helpers();
4306
4421
  init_intent_to_decisions();
4422
+ init_param_intent();
4307
4423
  }
4308
4424
  });
4309
4425
 
@@ -4714,12 +4830,13 @@ var init_join = __esm({
4714
4830
  const targets = [];
4715
4831
  const columns = decision.columns;
4716
4832
  const columnAliases = decision.columnAliases;
4833
+ const hydrationPrefix = decision.hydrationPrefix ?? relation ?? targetAlias;
4717
4834
  if (columns && columns.length > 0) {
4718
4835
  if (columns.length === 1 && columns[0] === "*") {
4719
4836
  targets.push(starTarget(targetAlias, ctx.naming));
4720
4837
  } else {
4721
4838
  for (const col of columns) {
4722
- const outputAlias = columnAliases?.[col] ?? `${relation}.${col}`;
4839
+ const outputAlias = columnAliases?.[col] ?? `${hydrationPrefix}.${col}`;
4723
4840
  targets.push(columnTarget(col, outputAlias, targetAlias, ctx.naming));
4724
4841
  }
4725
4842
  }
@@ -5048,6 +5165,7 @@ __export(handlers_exports, {
5048
5165
  genericWindowHandler: () => genericWindowHandler,
5049
5166
  getExpressionHandler: () => getExpressionHandler,
5050
5167
  getIncludeHandler: () => getIncludeHandler,
5168
+ getNqlSafeExpressionHandler: () => getNqlSafeExpressionHandler,
5051
5169
  getRegisteredOperators: () => getRegisteredOperators,
5052
5170
  getRegistryStats: () => getRegistryStats,
5053
5171
  getWhereHandler: () => getWhereHandler,
@@ -5158,6 +5276,10 @@ function getExpressionHandler(type) {
5158
5276
  }
5159
5277
  return handler;
5160
5278
  }
5279
+ function getNqlSafeExpressionHandler(type) {
5280
+ const handler = expressionHandlers.get(type);
5281
+ return handler?.nqlSafe === true ? handler : void 0;
5282
+ }
5161
5283
  function getIncludeHandler(strategy) {
5162
5284
  const handler = includeHandlers.get(strategy);
5163
5285
  if (!handler) {
@@ -5179,7 +5301,7 @@ function ensureHandlersRegistered() {
5179
5301
  handlersInitialized = true;
5180
5302
  registerAllWhereHandlers();
5181
5303
  }
5182
- function normalizeToDecision(input) {
5304
+ function normalizeToDecision(input, ctx) {
5183
5305
  if (input.column !== void 0) {
5184
5306
  const raw2 = input;
5185
5307
  if (raw2.jsonPath && raw2.jsonPath.length > 0) {
@@ -5202,7 +5324,7 @@ function normalizeToDecision(input) {
5202
5324
  type: op3,
5203
5325
  operator: op3,
5204
5326
  conditions: (raw.conditions ?? []).map(
5205
- (c) => normalizeToDecision(c)
5327
+ (c) => normalizeToDecision(c, ctx)
5206
5328
  )
5207
5329
  };
5208
5330
  }
@@ -5214,7 +5336,7 @@ function normalizeToDecision(input) {
5214
5336
  switch (kind) {
5215
5337
  case "comparison": {
5216
5338
  if (raw.jsonPath) {
5217
- return {
5339
+ const decision = {
5218
5340
  type: "where",
5219
5341
  column: raw.field,
5220
5342
  operator: "jsonComparison",
@@ -5222,6 +5344,7 @@ function normalizeToDecision(input) {
5222
5344
  jsonPath: raw.jsonPath,
5223
5345
  jsonMode: raw.jsonMode ?? "text"
5224
5346
  };
5347
+ return decision;
5225
5348
  }
5226
5349
  return {
5227
5350
  type: "where",
@@ -5235,7 +5358,7 @@ function normalizeToDecision(input) {
5235
5358
  type: "and",
5236
5359
  operator: "and",
5237
5360
  conditions: (raw.conditions ?? []).map(
5238
- (c) => normalizeToDecision(c)
5361
+ (c) => normalizeToDecision(c, ctx)
5239
5362
  )
5240
5363
  };
5241
5364
  case "or":
@@ -5243,14 +5366,14 @@ function normalizeToDecision(input) {
5243
5366
  type: "or",
5244
5367
  operator: "or",
5245
5368
  conditions: (raw.conditions ?? []).map(
5246
- (c) => normalizeToDecision(c)
5369
+ (c) => normalizeToDecision(c, ctx)
5247
5370
  )
5248
5371
  };
5249
5372
  case "not":
5250
5373
  return {
5251
5374
  type: "not",
5252
5375
  operator: "not",
5253
- conditions: [normalizeToDecision(raw.condition)]
5376
+ conditions: [normalizeToDecision(raw.condition, ctx)]
5254
5377
  };
5255
5378
  case "null":
5256
5379
  return {
@@ -5274,7 +5397,7 @@ function normalizeToDecision(input) {
5274
5397
  );
5275
5398
  const rawSelect = sub.select;
5276
5399
  const selectColumn = typeof rawSelect === "string" ? rawSelect : isSelectWithFields(rawSelect) ? rawSelect.fields?.[0] ?? "*" : "*";
5277
- const subConditions = sub.where ? [normalizeToDecision(sub.where)] : [];
5400
+ const subConditions = sub.where ? [normalizeToDecision(sub.where, ctx)] : [];
5278
5401
  const rawLimit = sub.limit;
5279
5402
  const rawOrderBy = sub.orderBy;
5280
5403
  return {
@@ -5309,13 +5432,14 @@ function normalizeToDecision(input) {
5309
5432
  operator: raw.caseInsensitive ? "ilike" : "like",
5310
5433
  value: raw.pattern
5311
5434
  };
5312
- case "jsonContains":
5435
+ case "jsonContains": {
5313
5436
  return {
5314
5437
  type: "where",
5315
5438
  column: raw.field,
5316
5439
  operator: raw.reversed ? "jsonContainedBy" : "jsonContains",
5317
5440
  value: raw.value
5318
5441
  };
5442
+ }
5319
5443
  case "jsonExists":
5320
5444
  return {
5321
5445
  type: "where",
@@ -5326,7 +5450,7 @@ function normalizeToDecision(input) {
5326
5450
  case "exists":
5327
5451
  case "notExists": {
5328
5452
  const relation = raw.relation;
5329
- const conditions = raw.where ? [normalizeToDecision(raw.where)] : void 0;
5453
+ const conditions = raw.where ? [normalizeToDecision(raw.where, ctx)] : void 0;
5330
5454
  const targetTable = raw.targetTable ?? relation;
5331
5455
  const sourceColumn = raw.sourceColumn;
5332
5456
  const targetColumn = raw.targetColumn;
@@ -5355,7 +5479,7 @@ function normalizeToDecision(input) {
5355
5479
  function createWhereDispatcher() {
5356
5480
  return (decision, ctx, state) => {
5357
5481
  ensureHandlersRegistered();
5358
- const normalized = normalizeToDecision(decision);
5482
+ const normalized = normalizeToDecision(decision, ctx);
5359
5483
  const rawOperator = normalized.operator ?? "=";
5360
5484
  const operator = OPERATOR_ALIASES[rawOperator] ?? rawOperator;
5361
5485
  const handler = getWhereHandler(operator);
@@ -5508,16 +5632,18 @@ function resolveOperand(operand, ctx, state) {
5508
5632
  const alias = ctx.currentAlias ?? ctx.rootTable;
5509
5633
  return columnRef(operand, alias, void 0, ctx.naming);
5510
5634
  }
5511
- const idx = ++state.paramIndex;
5512
- state.parameters.push(operand);
5513
- return createParamRef(idx);
5635
+ if (typeof operand === "object" && operand !== null && "kind" in operand && ctx.compileNqlSelectExpression) {
5636
+ return ctx.compileNqlSelectExpression(operand, ctx, state);
5637
+ }
5638
+ return bindParameter(unwrapParamIntent(operand), state);
5514
5639
  }
5515
5640
  var arithmeticHandler;
5516
5641
  var init_arithmetic = __esm({
5517
5642
  "src/handlers/expression/arithmetic.ts"() {
5518
5643
  "use strict";
5519
5644
  init_ast_helpers();
5520
- init_param_ref();
5645
+ init_param_intent();
5646
+ init_param_value();
5521
5647
  arithmeticHandler = {
5522
5648
  types: ["arithmetic", "math", "calc"],
5523
5649
  compile(decision, ctx, state) {
@@ -5552,8 +5678,9 @@ var init_case = __esm({
5552
5678
  "src/handlers/expression/case.ts"() {
5553
5679
  "use strict";
5554
5680
  init_ast_helpers();
5555
- init_param_ref();
5681
+ init_param_intent();
5556
5682
  init_case_value();
5683
+ init_param_value();
5557
5684
  caseHandler = {
5558
5685
  types: ["case", "CASE", "caseWhen"],
5559
5686
  compile(decision, ctx, state) {
@@ -5599,13 +5726,12 @@ var init_case = __esm({
5599
5726
  const tableAlias = ctx.currentAlias ?? ctx.rootTable;
5600
5727
  const testExpr = columnRef(column, tableAlias, void 0, ctx.naming);
5601
5728
  const args = conditions.map((cond) => {
5602
- const whenParamNumber = ++state.paramIndex;
5603
5729
  const whenDecision = cond.when;
5604
- state.parameters.push(whenDecision.value ?? cond.when);
5605
- const whenExpr = createParamRef(whenParamNumber);
5606
- const thenParamNumber = ++state.paramIndex;
5607
- state.parameters.push(cond.then);
5608
- const thenResult = createParamRef(thenParamNumber);
5730
+ const whenExpr = bindParameter(
5731
+ unwrapParamIntent(whenDecision.value ?? cond.when),
5732
+ state
5733
+ );
5734
+ const thenResult = bindParameter(unwrapParamIntent(cond.then), state);
5609
5735
  const caseWhen = {
5610
5736
  expr: whenExpr,
5611
5737
  result: thenResult
@@ -5614,9 +5740,7 @@ var init_case = __esm({
5614
5740
  });
5615
5741
  let defresult;
5616
5742
  if (elseValue !== void 0) {
5617
- const paramNumber = ++state.paramIndex;
5618
- state.parameters.push(elseValue);
5619
- defresult = createParamRef(paramNumber);
5743
+ defresult = bindParameter(unwrapParamIntent(elseValue), state);
5620
5744
  }
5621
5745
  const caseExpr = {
5622
5746
  arg: testExpr,
@@ -5642,18 +5766,21 @@ function buildValueNode(value, ctx, state) {
5642
5766
  return columnRef(decision.column, tableAlias, void 0, ctx.naming);
5643
5767
  }
5644
5768
  }
5645
- const paramNumber = ++state.paramIndex;
5646
- state.parameters.push(value);
5647
- return createParamRef(paramNumber);
5769
+ if (typeof value === "object" && value !== null && "kind" in value && ctx.compileNqlSelectExpression) {
5770
+ return ctx.compileNqlSelectExpression(value, ctx, state);
5771
+ }
5772
+ return bindParameter(unwrapParamIntent(value), state);
5648
5773
  }
5649
5774
  var coalesceHandler, nullIfHandler, greatestHandler, leastHandler;
5650
5775
  var init_coalesce = __esm({
5651
5776
  "src/handlers/expression/coalesce.ts"() {
5652
5777
  "use strict";
5653
5778
  init_ast_helpers();
5654
- init_param_ref();
5779
+ init_param_intent();
5780
+ init_param_value();
5655
5781
  coalesceHandler = {
5656
5782
  types: ["coalesce", "COALESCE", "ifNull", "nvl"],
5783
+ nqlSafe: true,
5657
5784
  compile(decision, ctx, state) {
5658
5785
  const args = decision.args;
5659
5786
  const column = decision.column;
@@ -5693,9 +5820,7 @@ var init_coalesce = __esm({
5693
5820
  }
5694
5821
  const tableAlias = ctx.currentAlias ?? ctx.rootTable;
5695
5822
  const colRef = columnRef(column, tableAlias, void 0, ctx.naming);
5696
- const paramNumber = ++state.paramIndex;
5697
- state.parameters.push(value);
5698
- const valueRef = createParamRef(paramNumber);
5823
+ const valueRef = bindParameter(unwrapParamIntent(value), state);
5699
5824
  return {
5700
5825
  NullIfExpr: {
5701
5826
  args: [colRef, valueRef]
@@ -5793,11 +5918,30 @@ var init_column = __esm({
5793
5918
  });
5794
5919
 
5795
5920
  // src/handlers/expression/json.ts
5921
+ function normalizeJsonPathArgs(args) {
5922
+ if (!args || args.length === 0) return [];
5923
+ if (args.length === 1) {
5924
+ const first = args[0];
5925
+ const unwrappedFirst = unwrapParamIntent(first);
5926
+ if (first !== unwrappedFirst) {
5927
+ return unwrappedFirst;
5928
+ }
5929
+ if (Array.isArray(first)) {
5930
+ return first.map(unwrapParamIntent);
5931
+ }
5932
+ if (typeof first === "string" && first.startsWith("{") && first.endsWith("}")) {
5933
+ const inner = first.slice(1, -1);
5934
+ return inner.length === 0 ? [] : inner.split(",");
5935
+ }
5936
+ }
5937
+ return args.map(unwrapParamIntent);
5938
+ }
5796
5939
  var jsonExtractHandler, jsonPathExtractHandler;
5797
5940
  var init_json2 = __esm({
5798
5941
  "src/handlers/expression/json.ts"() {
5799
5942
  "use strict";
5800
5943
  init_ast_helpers();
5944
+ init_param_intent();
5801
5945
  init_utils();
5802
5946
  jsonExtractHandler = {
5803
5947
  types: ["jsonExtract"],
@@ -5834,10 +5978,10 @@ var init_json2 = __esm({
5834
5978
  throw new Error("JSON path extract handler requires a column");
5835
5979
  }
5836
5980
  const mode = decision.jsonMode ?? "text";
5837
- const arrayLiteral = decision.args?.[0] ?? "{}";
5981
+ const path = normalizeJsonPathArgs(decision.args);
5838
5982
  const alias = ctx.currentAlias ?? ctx.rootTable;
5839
5983
  const left = columnRef(column, alias, void 0, ctx.naming);
5840
- const right = compileValue(arrayLiteral, state);
5984
+ const right = compileValue(path, state);
5841
5985
  const op3 = mode === "text" ? "#>>" : "#>";
5842
5986
  return {
5843
5987
  A_Expr: {
@@ -6432,6 +6576,8 @@ var rawHandler, sqlFunctionHandler, literalHandler;
6432
6576
  var init_raw = __esm({
6433
6577
  "src/handlers/expression/raw.ts"() {
6434
6578
  "use strict";
6579
+ init_param_intent();
6580
+ init_param_value();
6435
6581
  rawHandler = {
6436
6582
  types: ["raw", "RAW", "rawSql", "rawExpression"],
6437
6583
  compile(decision, ctx, _state) {
@@ -6466,17 +6612,9 @@ var init_raw = __esm({
6466
6612
  if (args && Array.isArray(args)) {
6467
6613
  for (const arg of args) {
6468
6614
  if (typeof arg === "object" && arg !== null && "type" in arg) {
6469
- const paramNumber = ++state.paramIndex;
6470
- state.parameters.push(arg);
6471
- argNodes.push({
6472
- ParamRef: { number: paramNumber }
6473
- });
6615
+ argNodes.push(bindParameter(unwrapParamIntent(arg), state));
6474
6616
  } else {
6475
- const paramNumber = ++state.paramIndex;
6476
- state.parameters.push(arg);
6477
- argNodes.push({
6478
- ParamRef: { number: paramNumber }
6479
- });
6617
+ argNodes.push(bindParameter(unwrapParamIntent(arg), state));
6480
6618
  }
6481
6619
  }
6482
6620
  }
@@ -6550,7 +6688,7 @@ var init_relation = __esm({
6550
6688
  if (relation.includes(".")) {
6551
6689
  const segments = relation.split(".");
6552
6690
  const leaf = segments[segments.length - 1];
6553
- alias = state.aliases.get(leaf) ?? state.aliases.get(relation) ?? leaf;
6691
+ alias = state.aliases.get(relation) ?? state.aliases.get(leaf) ?? leaf;
6554
6692
  } else {
6555
6693
  alias = state.aliases.get(relation) ?? relation;
6556
6694
  }
@@ -6698,13 +6836,9 @@ function createLagLeadHandler(funcName, types) {
6698
6836
  const colRef = columnRef(column, tableAlias, void 0, ctx.naming);
6699
6837
  const args = [colRef];
6700
6838
  const offset = decision.args?.[0] ?? 1;
6701
- const offsetParamNumber = ++state.paramIndex;
6702
- state.parameters.push(offset);
6703
- args.push(createParamRef(offsetParamNumber));
6839
+ args.push(bindParameter(unwrapParamIntent(offset), state));
6704
6840
  if (decision.value !== void 0) {
6705
- const defaultParamNumber = ++state.paramIndex;
6706
- state.parameters.push(decision.value);
6707
- args.push(createParamRef(defaultParamNumber));
6841
+ args.push(bindParameter(unwrapParamIntent(decision.value), state));
6708
6842
  }
6709
6843
  return buildWindowFunction(funcName, args, decision, ctx);
6710
6844
  }
@@ -6730,7 +6864,8 @@ var init_window = __esm({
6730
6864
  "src/handlers/expression/window.ts"() {
6731
6865
  "use strict";
6732
6866
  init_ast_helpers();
6733
- init_param_ref();
6867
+ init_param_intent();
6868
+ init_param_value();
6734
6869
  WINDOW_FRAME_DEFAULT = 1034;
6735
6870
  rowNumberHandler = createNoArgWindowHandler("row_number", [
6736
6871
  "rowNumber",
@@ -6747,9 +6882,7 @@ var init_window = __esm({
6747
6882
  types: ["ntile", "NTILE"],
6748
6883
  compile(decision, ctx, state) {
6749
6884
  const n = decision.value ?? decision.args?.[0] ?? 4;
6750
- const paramNumber = ++state.paramIndex;
6751
- state.parameters.push(n);
6752
- const nRef = createParamRef(paramNumber);
6885
+ const nRef = bindParameter(unwrapParamIntent(n), state);
6753
6886
  return buildWindowFunction("ntile", [nRef], decision, ctx);
6754
6887
  }
6755
6888
  };
@@ -6790,15 +6923,11 @@ var init_window = __esm({
6790
6923
  }
6791
6924
  if (decision.args && Array.isArray(decision.args)) {
6792
6925
  for (const arg of decision.args) {
6793
- const paramNumber = ++state.paramIndex;
6794
- state.parameters.push(arg);
6795
- args.push(createParamRef(paramNumber));
6926
+ args.push(bindParameter(unwrapParamIntent(arg), state));
6796
6927
  }
6797
6928
  }
6798
6929
  if (decision.value !== void 0) {
6799
- const defaultParamNumber = ++state.paramIndex;
6800
- state.parameters.push(decision.value);
6801
- args.push(createParamRef(defaultParamNumber));
6930
+ args.push(bindParameter(unwrapParamIntent(decision.value), state));
6802
6931
  }
6803
6932
  return buildWindowFunction(funcName, args, decision, ctx);
6804
6933
  }
@@ -6915,6 +7044,12 @@ init_ast_helpers();
6915
7044
  // src/compiler.ts
6916
7045
  init_assert_field();
6917
7046
  init_ast_helpers();
7047
+ import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
7048
+ import {
7049
+ isParamIntent as isParamIntent6,
7050
+ NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
7051
+ NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
7052
+ } from "@dbsp/types";
6918
7053
 
6919
7054
  // src/pgsql-deparser.ts
6920
7055
  function deparse(node) {
@@ -7972,6 +8107,7 @@ function deparseQuoted(ast) {
7972
8107
  init_case_value();
7973
8108
  init_custom();
7974
8109
  init_expression();
8110
+ init_param_value();
7975
8111
  init_window();
7976
8112
  init_include();
7977
8113
  init_shared();
@@ -7980,9 +8116,39 @@ init_types();
7980
8116
  init_utils();
7981
8117
  init_intent_to_decisions();
7982
8118
  init_naming_plugin();
8119
+ init_param_intent();
7983
8120
  init_param_ref();
7984
8121
  init_subquery_emission();
8122
+ init_validate();
7985
8123
  registerWhereDispatcherFactory(createWhereDispatcher);
8124
+ var UnhandledNqlSelectExpressionKindError = class extends Error {
8125
+ constructor(kind) {
8126
+ super(`Unhandled NQL SELECT expression intent kind: ${kind}`);
8127
+ this.kind = kind;
8128
+ this.name = "UnhandledNqlSelectExpressionKindError";
8129
+ }
8130
+ kind;
8131
+ code = "ERR_ADAPTER_UNHANDLED_NQL_SELECT_EXPRESSION_KIND";
8132
+ };
8133
+ var UnsupportedNqlSelectFunctionError = class extends Error {
8134
+ constructor(functionName) {
8135
+ super(`Unsupported function in SELECT context: ${functionName}()`);
8136
+ this.functionName = functionName;
8137
+ this.name = "UnsupportedNqlSelectFunctionError";
8138
+ }
8139
+ functionName;
8140
+ code = "ERR_ADAPTER_UNSUPPORTED_NQL_SELECT_FUNCTION";
8141
+ };
8142
+ function assertNqlSelectScalarFunctionAllowed(functionName) {
8143
+ if (!NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST.has(functionName.toLowerCase())) {
8144
+ throw new UnsupportedNqlSelectFunctionError(functionName);
8145
+ }
8146
+ }
8147
+ function assertNqlSelectWindowFunctionAllowed(functionName) {
8148
+ if (!NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST.has(functionName.toLowerCase())) {
8149
+ throw new UnsupportedNqlSelectFunctionError(functionName);
8150
+ }
8151
+ }
7986
8152
  function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
7987
8153
  return {
7988
8154
  type: pd.type,
@@ -8006,6 +8172,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
8006
8172
  strategy: pd.choice === "subquery" ? "json_agg" : pd.choice,
8007
8173
  relation: pd.relation ?? pd.relationName,
8008
8174
  relationName: pd.relationName,
8175
+ relationPath: pd.relationPath,
8176
+ hydrationPrefix: pd.hydrationPrefix,
8009
8177
  relationType: pd.relationType,
8010
8178
  foreignKey: pd.foreignKey,
8011
8179
  parentKey: pd.parentKey,
@@ -8058,6 +8226,77 @@ function isPrecompiledJoinDecision(d) {
8058
8226
  function isBatchValuesJoinDecision(d) {
8059
8227
  return isPrecompiledJoinDecision(d) && d.batchValuesParams !== void 0;
8060
8228
  }
8229
+ function getRelationIdentityPath(decision, rootTable) {
8230
+ if (decision.relationPath) return decision.relationPath;
8231
+ const relationName = decision.relationName ?? decision.relation;
8232
+ if (!relationName && !decision.targetTable) return void 0;
8233
+ const source = decision.sourceTable ?? rootTable;
8234
+ const target = decision.targetTable ?? "";
8235
+ const sourceColumn = decision.sourceColumn ?? decision.foreignKey ?? "";
8236
+ return `__legacy__:${source}:${relationName ?? target}:${sourceColumn}:${target}`;
8237
+ }
8238
+ function getParentRelationPath(relationPath) {
8239
+ if (!relationPath || relationPath.startsWith("__legacy__:")) return void 0;
8240
+ const lastDot = relationPath.lastIndexOf(".");
8241
+ return lastDot > 0 ? relationPath.slice(0, lastDot) : void 0;
8242
+ }
8243
+ function mergeColumnLists(current, next) {
8244
+ if (!current) return next ? [...next] : void 0;
8245
+ if (!next) return current;
8246
+ if (current.includes("*") || next.includes("*")) return ["*"];
8247
+ const merged = [...current];
8248
+ for (const column of next) {
8249
+ if (!merged.includes(column)) merged.push(column);
8250
+ }
8251
+ return merged;
8252
+ }
8253
+ function mergeDuplicateJoinIncludeDecisions(decisions, rootTable) {
8254
+ const merged = [];
8255
+ const seenByPath = /* @__PURE__ */ new Map();
8256
+ for (const decision of decisions) {
8257
+ const identityPath = decision.type === "includeStrategy" && decision.choice === "join" ? getRelationIdentityPath(decision, rootTable) : void 0;
8258
+ if (!identityPath) {
8259
+ merged.push(decision);
8260
+ continue;
8261
+ }
8262
+ const existing = seenByPath.get(identityPath);
8263
+ if (!existing) {
8264
+ const copy = {
8265
+ ...decision,
8266
+ ...decision.columns ? { columns: [...decision.columns] } : {},
8267
+ ...decision.columnAliases ? { columnAliases: { ...decision.columnAliases } } : {},
8268
+ ...decision.conditions ? { conditions: [...decision.conditions] } : {}
8269
+ };
8270
+ seenByPath.set(identityPath, copy);
8271
+ merged.push(copy);
8272
+ continue;
8273
+ }
8274
+ const existingJoinType = existing.joinType ?? "left";
8275
+ const nextJoinType = decision.joinType ?? "left";
8276
+ if (existingJoinType !== nextJoinType) {
8277
+ throw new InvalidOperationError2(
8278
+ "include",
8279
+ `Conflicting join options for relation path '${identityPath}'. Already registered as ${existingJoinType}, got ${nextJoinType}.`
8280
+ );
8281
+ }
8282
+ const mutable = existing;
8283
+ const columns = mergeColumnLists(existing.columns, decision.columns);
8284
+ if (columns) mutable.columns = columns;
8285
+ if (decision.columnAliases) {
8286
+ mutable.columnAliases = {
8287
+ ...decision.columnAliases,
8288
+ ...existing.columnAliases ?? {}
8289
+ };
8290
+ }
8291
+ if (decision.conditions && decision.conditions.length > 0) {
8292
+ mutable.conditions = [
8293
+ ...existing.conditions ?? [],
8294
+ ...decision.conditions
8295
+ ];
8296
+ }
8297
+ }
8298
+ return merged;
8299
+ }
8061
8300
  function renumberParamRefsInAst(node, offset) {
8062
8301
  if (offset === 0) return node;
8063
8302
  return renumberNode(node, offset);
@@ -8114,13 +8353,14 @@ var PlanCompiler = class _PlanCompiler {
8114
8353
  /** CTE nodes from include handlers (e.g., CTE strategy) */
8115
8354
  pendingCtes = [];
8116
8355
  /**
8117
- * Maps joined targetTable → alias for multi-hop FK resolution.
8118
- * Populated as join decisions are compiled so later hops can find
8119
- * the correct source alias (e.g., 'symbols' 'callee').
8356
+ * Maps relation-dotted include path JOIN alias for multi-hop FK resolution.
8357
+ * Populated as join decisions are compiled so later hops can find the
8358
+ * correct source alias (e.g., 'callee.file' reads parent path 'callee').
8120
8359
  */
8121
8360
  joinAliasMap = /* @__PURE__ */ new Map();
8122
8361
  /**
8123
8362
  * Tracks all join aliases in use for the current query.
8363
+ * Entries are stored in emitted database-alias space, after naming.toDatabase().
8124
8364
  * Ensures no two JOINs share the same alias (DOUBLE-ALIAS prevention).
8125
8365
  */
8126
8366
  usedJoinAliases = /* @__PURE__ */ new Set();
@@ -8136,6 +8376,7 @@ var PlanCompiler = class _PlanCompiler {
8136
8376
  return {
8137
8377
  naming: this.naming,
8138
8378
  rootTable: this.currentRootTable,
8379
+ aliases: this.resolvedJoinAliases(),
8139
8380
  maxRecursiveDepth: 100,
8140
8381
  defaultPkColumnName: this.defaultPk,
8141
8382
  deriveFkColumnName: this.deriveFk,
@@ -8143,6 +8384,31 @@ var PlanCompiler = class _PlanCompiler {
8143
8384
  ...this.model != null && { model: this.model }
8144
8385
  };
8145
8386
  }
8387
+ findAliasForLegacySourceTable(sourceTable) {
8388
+ let alias;
8389
+ for (const entry of this.joinAliasMap.values()) {
8390
+ if (entry.targetTable === sourceTable) alias = entry.alias;
8391
+ }
8392
+ return alias;
8393
+ }
8394
+ emittedJoinAlias(alias) {
8395
+ const dbAlias = this.naming.toDatabase(alias);
8396
+ validateIdentifier(dbAlias, "alias");
8397
+ return dbAlias;
8398
+ }
8399
+ resolvedJoinAliases() {
8400
+ const aliases = /* @__PURE__ */ new Map();
8401
+ for (const [relationPath, entry] of this.joinAliasMap) {
8402
+ const isLegacyPath = relationPath.startsWith("__legacy__:");
8403
+ if (!isLegacyPath) {
8404
+ aliases.set(relationPath, entry.alias);
8405
+ }
8406
+ if (entry.relationName && (isLegacyPath || relationPath === entry.relationName)) {
8407
+ aliases.set(entry.relationName, entry.alias);
8408
+ }
8409
+ }
8410
+ return aliases;
8411
+ }
8146
8412
  /**
8147
8413
  * Dispatch a PlanDecision through the unified WHERE handler system.
8148
8414
  * Bridges PlanCompiler's state to handler types, calls dispatcher, syncs back.
@@ -8293,7 +8559,21 @@ var PlanCompiler = class _PlanCompiler {
8293
8559
  const combined = condNodes.length === 1 ? condNodes[0] : andExpr(...condNodes);
8294
8560
  handlerDecision._compiledFilterWhere = combined;
8295
8561
  }
8296
- const sourceAlias = decision.sourceTable && decision.sourceTable !== plan.rootTable ? this.joinAliasMap.get(decision.sourceTable) ?? decision.sourceTable : plan.rootTable;
8562
+ const relationIdentityPath = decision.choice === "join" ? getRelationIdentityPath(decision, plan.rootTable) : void 0;
8563
+ const requestedJoinType = decision.joinType ?? "left";
8564
+ const existingJoin = relationIdentityPath ? this.joinAliasMap.get(relationIdentityPath) : void 0;
8565
+ if (existingJoin) {
8566
+ if (existingJoin.joinType !== requestedJoinType) {
8567
+ throw new InvalidOperationError2(
8568
+ "include",
8569
+ `Conflicting join options for relation path '${relationIdentityPath}'. Already registered as ${existingJoin.joinType}, got ${requestedJoinType}.`
8570
+ );
8571
+ }
8572
+ return {};
8573
+ }
8574
+ const parentRelationPath = getParentRelationPath(relationIdentityPath);
8575
+ const parentAlias = parentRelationPath ? this.joinAliasMap.get(parentRelationPath)?.alias : void 0;
8576
+ const sourceAlias = parentAlias ?? (decision.sourceTable && decision.sourceTable !== plan.rootTable ? this.findAliasForLegacySourceTable(decision.sourceTable) ?? decision.sourceTable : plan.rootTable);
8297
8577
  const ctx = {
8298
8578
  ...this.handlerCtx(),
8299
8579
  currentAlias: sourceAlias
@@ -8310,11 +8590,13 @@ var PlanCompiler = class _PlanCompiler {
8310
8590
  const candidateAlias = handlerDecision.relation ?? handlerDecision.targetTable ?? handlerDecision.relationName;
8311
8591
  if (candidateAlias) {
8312
8592
  let alias = candidateAlias;
8593
+ let emittedAlias = this.emittedJoinAlias(alias);
8313
8594
  let counter = 1;
8314
- while (this.usedJoinAliases.has(alias)) {
8595
+ while (this.usedJoinAliases.has(emittedAlias)) {
8315
8596
  alias = `${candidateAlias}_${counter++}`;
8597
+ emittedAlias = this.emittedJoinAlias(alias);
8316
8598
  }
8317
- this.usedJoinAliases.add(alias);
8599
+ this.usedJoinAliases.add(emittedAlias);
8318
8600
  finalJoinAlias = alias;
8319
8601
  if (alias !== candidateAlias) {
8320
8602
  handlerDecision.relation = alias;
@@ -8323,11 +8605,15 @@ var PlanCompiler = class _PlanCompiler {
8323
8605
  }
8324
8606
  const result = handler.compile(handlerDecision, ctx, handlerState);
8325
8607
  this.state.paramIndex = handlerState.paramIndex;
8326
- if (decision.choice === "join" && decision.targetTable && (finalJoinAlias ?? decision.relationName)) {
8327
- this.joinAliasMap.set(
8328
- decision.targetTable,
8329
- finalJoinAlias ?? decision.relationName
8330
- );
8608
+ if (decision.choice === "join" && relationIdentityPath && finalJoinAlias) {
8609
+ this.joinAliasMap.set(relationIdentityPath, {
8610
+ alias: finalJoinAlias,
8611
+ joinType: requestedJoinType,
8612
+ ...decision.targetTable && { targetTable: decision.targetTable },
8613
+ ...(decision.relationName ?? decision.relation) && {
8614
+ relationName: decision.relationName ?? decision.relation
8615
+ }
8616
+ });
8331
8617
  }
8332
8618
  const out = {};
8333
8619
  if (result.targets) out.targets = result.targets;
@@ -8353,6 +8639,7 @@ var PlanCompiler = class _PlanCompiler {
8353
8639
  this.rawJoins = [];
8354
8640
  this.pendingCtes = [];
8355
8641
  this.joinAliasMap = /* @__PURE__ */ new Map();
8642
+ this.usedJoinAliases = /* @__PURE__ */ new Set();
8356
8643
  const queryType = this.detectQueryType(plan.decisions);
8357
8644
  let ast;
8358
8645
  switch (queryType) {
@@ -8398,11 +8685,14 @@ var PlanCompiler = class _PlanCompiler {
8398
8685
  naming: this.naming,
8399
8686
  rootTable: plan.rootTable,
8400
8687
  currentAlias: currentAlias ?? plan.rootTable,
8688
+ aliases: this.resolvedJoinAliases(),
8401
8689
  maxRecursiveDepth: 100,
8402
8690
  defaultPkColumnName: this.defaultPk,
8403
8691
  deriveFkColumnName: this.deriveFk,
8404
8692
  ...plan.schema ?? this.schema ? { schema: plan.schema ?? this.schema } : {},
8405
- ...this.model != null && { model: this.model }
8693
+ ...this.model != null && { model: this.model },
8694
+ compileSubquery: (query, paramOffset) => this.compileExpressionSubquery(query, paramOffset),
8695
+ compileNqlSelectExpression: (value, handlerCtx, state) => this.compileNqlFunctionArg(value, handlerCtx, state)
8406
8696
  };
8407
8697
  }
8408
8698
  /** Build a fresh HandlerCompilerState sharing the current parameter array. */
@@ -8411,10 +8701,251 @@ var PlanCompiler = class _PlanCompiler {
8411
8701
  parameters: this.state.parameters,
8412
8702
  paramIndex: this.state.paramIndex,
8413
8703
  ctes: /* @__PURE__ */ new Map(),
8414
- aliases: /* @__PURE__ */ new Map(),
8704
+ aliases: this.resolvedJoinAliases(),
8415
8705
  joins: []
8416
8706
  };
8417
8707
  }
8708
+ compileExpressionSubquery(query, paramOffset) {
8709
+ const innerCompiler = new _PlanCompiler({
8710
+ naming: this.naming,
8711
+ ...this.schema !== void 0 && {
8712
+ schema: this.schema
8713
+ },
8714
+ defaultPkColumnName: this.defaultPk,
8715
+ deriveFkColumnName: this.deriveFk
8716
+ });
8717
+ const innerPlan = {
8718
+ rootTable: query.from,
8719
+ decisions: intentToDecisions(query, query.from)
8720
+ };
8721
+ const innerResult = innerCompiler.compile(innerPlan);
8722
+ const renumbered = renumberParamRefsInAst(innerResult.ast, paramOffset);
8723
+ return { ast: renumbered, parameters: innerResult.parameters };
8724
+ }
8725
+ compileGenericNqlFunction(functionName, args, ctx, state) {
8726
+ assertNqlSelectScalarFunctionAllowed(functionName);
8727
+ validateIdentifier(functionName, "function");
8728
+ const argNodes = args.map(
8729
+ (arg) => this.compileNqlFunctionArg(arg, ctx, state)
8730
+ );
8731
+ return funcCall(functionName, argNodes);
8732
+ }
8733
+ compileNqlFunctionArg(arg, ctx, state) {
8734
+ if (typeof arg === "string") {
8735
+ return buildColumnRef(arg, ctx);
8736
+ }
8737
+ if (typeof arg === "object" && arg !== null) {
8738
+ const record = arg;
8739
+ if (typeof record.$ref === "string") {
8740
+ return buildColumnRef(record.$ref, ctx);
8741
+ }
8742
+ if (typeof record.kind !== "string") {
8743
+ const legacyKind = typeof record.$op === "string" ? `$op:${record.$op}` : typeof record.$fn === "string" ? `$fn:${record.$fn}` : "object";
8744
+ throw new UnhandledNqlSelectExpressionKindError(legacyKind);
8745
+ }
8746
+ switch (record.kind) {
8747
+ case "column":
8748
+ if (typeof record.column !== "string") {
8749
+ throw new Error("NQL column expression requires a column name");
8750
+ }
8751
+ return buildColumnRef(record.column, ctx);
8752
+ case "columnAlias":
8753
+ if (typeof record.column !== "string") {
8754
+ throw new Error(
8755
+ "NQL columnAlias expression requires a column name"
8756
+ );
8757
+ }
8758
+ return buildColumnRef(record.column, ctx);
8759
+ case "relationColumn": {
8760
+ const relation = record.relation;
8761
+ const column = record.column;
8762
+ if (typeof relation !== "string" || typeof column !== "string") {
8763
+ throw new Error(
8764
+ "NQL relationColumn expression requires relation and column"
8765
+ );
8766
+ }
8767
+ return buildColumnRef(`${relation}.${column}`, ctx);
8768
+ }
8769
+ case "param":
8770
+ if (!("value" in record)) {
8771
+ throw new Error("NQL param expression requires a value");
8772
+ }
8773
+ return bindParameter(record.value, state);
8774
+ case "literal":
8775
+ if (!("value" in record)) {
8776
+ throw new Error("NQL literal expression requires a value");
8777
+ }
8778
+ return compileValue(record.value, state);
8779
+ case "function": {
8780
+ const nestedName = record.name;
8781
+ const nestedArgs = record.args;
8782
+ if (typeof nestedName !== "string") {
8783
+ throw new Error("NQL function argument requires a function name");
8784
+ }
8785
+ return this.compileGenericNqlFunction(
8786
+ nestedName,
8787
+ Array.isArray(nestedArgs) ? nestedArgs : [],
8788
+ ctx,
8789
+ state
8790
+ );
8791
+ }
8792
+ case "coalesce": {
8793
+ const handler = getExpressionHandler("coalesce");
8794
+ return handler.compile(
8795
+ {
8796
+ type: "coalesce",
8797
+ args: Array.isArray(record.fields) ? record.fields : []
8798
+ },
8799
+ ctx,
8800
+ state
8801
+ );
8802
+ }
8803
+ case "aggregate": {
8804
+ const fn4 = record.function;
8805
+ if (typeof fn4 !== "string") {
8806
+ throw new Error(
8807
+ "NQL aggregate expression requires a function name"
8808
+ );
8809
+ }
8810
+ const aggregateArgs = [];
8811
+ if (record.field === "*") {
8812
+ aggregateArgs.push({ kind: "star" });
8813
+ } else if (typeof record.field === "string") {
8814
+ aggregateArgs.push(record.field);
8815
+ }
8816
+ if (Array.isArray(record.extraArgs)) {
8817
+ aggregateArgs.push(...record.extraArgs);
8818
+ }
8819
+ return this.compileGenericNqlFunction(fn4, aggregateArgs, ctx, state);
8820
+ }
8821
+ case "arithmetic": {
8822
+ const operator = record.operator;
8823
+ if (typeof operator !== "string") {
8824
+ throw new Error("NQL arithmetic expression requires an operator");
8825
+ }
8826
+ if (!("left" in record) || !("right" in record)) {
8827
+ throw new Error(
8828
+ "NQL arithmetic expression requires left and right operands"
8829
+ );
8830
+ }
8831
+ const handler = getExpressionHandler("arithmetic");
8832
+ return handler.compile(
8833
+ {
8834
+ type: "arithmetic",
8835
+ operator,
8836
+ args: [record.left, record.right]
8837
+ },
8838
+ ctx,
8839
+ state
8840
+ );
8841
+ }
8842
+ case "case":
8843
+ return this.compileNqlCaseExpressionArg(record, ctx, state);
8844
+ case "jsonExtract": {
8845
+ const handler = getExpressionHandler("jsonExtract");
8846
+ return handler.compile(
8847
+ {
8848
+ type: "jsonExtract",
8849
+ column: record.field,
8850
+ args: Array.isArray(record.path) ? record.path : [],
8851
+ jsonMode: record.mode
8852
+ },
8853
+ ctx,
8854
+ state
8855
+ );
8856
+ }
8857
+ case "jsonPathExtract": {
8858
+ const handler = getExpressionHandler("jsonPathExtract");
8859
+ return handler.compile(
8860
+ {
8861
+ type: "jsonPathExtract",
8862
+ column: record.field,
8863
+ args: Array.isArray(record.path) ? [record.path] : typeof record.path === "string" ? [record.path] : [],
8864
+ jsonMode: record.mode
8865
+ },
8866
+ ctx,
8867
+ state
8868
+ );
8869
+ }
8870
+ case "window":
8871
+ return genericWindowHandler.compile(
8872
+ {
8873
+ type: "window",
8874
+ function: record.function,
8875
+ column: record.field,
8876
+ args: typeof record.offset === "number" ? [record.offset] : void 0,
8877
+ value: record.defaultValue,
8878
+ partition: record.over?.partitionBy,
8879
+ orderBy: record.over?.orderBy?.map((item) => ({
8880
+ column: item.field,
8881
+ direction: item.direction?.toUpperCase()
8882
+ }))
8883
+ },
8884
+ ctx,
8885
+ state
8886
+ );
8887
+ case "customOp":
8888
+ case "customFn":
8889
+ case "ref":
8890
+ case "cast":
8891
+ case "unary":
8892
+ case "namedArg":
8893
+ case "star":
8894
+ case "array":
8895
+ case "subquery":
8896
+ return compileExpressionIntent(
8897
+ record,
8898
+ ctx,
8899
+ state
8900
+ );
8901
+ default:
8902
+ throw new UnhandledNqlSelectExpressionKindError(record.kind);
8903
+ }
8904
+ }
8905
+ return compileValue(arg, state);
8906
+ }
8907
+ compileNqlCaseExpressionArg(expr, ctx, state) {
8908
+ const whenClauses = expr.when;
8909
+ if (!Array.isArray(whenClauses) || whenClauses.length === 0) {
8910
+ throw new Error("NQL CASE expression requires at least one WHEN clause");
8911
+ }
8912
+ const dispatcher = createWhereDispatcher();
8913
+ const args = whenClauses.map((branch) => {
8914
+ const condition = branch.condition;
8915
+ const result = branch.result;
8916
+ const conditionDecision = convertWhereCondition(
8917
+ condition,
8918
+ ctx.rootTable
8919
+ );
8920
+ if (!conditionDecision) {
8921
+ throw new Error("NQL CASE WHEN condition could not be compiled");
8922
+ }
8923
+ const whenNode = dispatcher(
8924
+ mapToHandlerDecision(
8925
+ conditionDecision,
8926
+ ctx.rootTable,
8927
+ this.defaultPk,
8928
+ this.deriveFk
8929
+ ),
8930
+ ctx,
8931
+ state
8932
+ );
8933
+ const thenNode = this.compileNqlFunctionArg(result, ctx, state);
8934
+ return {
8935
+ CaseWhen: {
8936
+ expr: whenNode,
8937
+ result: thenNode
8938
+ }
8939
+ };
8940
+ });
8941
+ const defresult = expr.else !== void 0 ? this.compileNqlFunctionArg(expr.else, ctx, state) : void 0;
8942
+ return {
8943
+ CaseExpr: {
8944
+ args,
8945
+ ...defresult !== void 0 ? { defresult } : {}
8946
+ }
8947
+ };
8948
+ }
8418
8949
  /**
8419
8950
  * Compile a SELECT-list target via expression handler.
8420
8951
  * Wraps the node in a ResTarget and pushes it onto targetList.
@@ -8468,6 +8999,41 @@ var PlanCompiler = class _PlanCompiler {
8468
8999
  });
8469
9000
  break;
8470
9001
  }
9002
+ case "selectNqlFunction": {
9003
+ ensureExpressionHandlersRegistered();
9004
+ const funcType = decision.function;
9005
+ if (!funcType) break;
9006
+ assertNqlSelectScalarFunctionAllowed(funcType);
9007
+ const ctx = this.createHandlerContext(
9008
+ plan,
9009
+ decision.table ?? plan.rootTable
9010
+ );
9011
+ const state = this.createHandlerState();
9012
+ const safeHandler = getNqlSafeExpressionHandler(funcType);
9013
+ const node = safeHandler ? safeHandler.compile(
9014
+ mapToHandlerDecision(
9015
+ decision,
9016
+ plan.rootTable,
9017
+ this.defaultPk,
9018
+ this.deriveFk
9019
+ ),
9020
+ ctx,
9021
+ state
9022
+ ) : this.compileGenericNqlFunction(
9023
+ funcType,
9024
+ decision.args ?? [],
9025
+ ctx,
9026
+ state
9027
+ );
9028
+ this.state.paramIndex = state.paramIndex;
9029
+ targetList.push({
9030
+ ResTarget: {
9031
+ val: node,
9032
+ ...decision.alias ? { name: this.naming.toDatabase(decision.alias) } : {}
9033
+ }
9034
+ });
9035
+ break;
9036
+ }
8471
9037
  case "selectExpression": {
8472
9038
  if (decision.expressionType === "case") {
8473
9039
  const caseNode = this.compileCaseExpression(decision);
@@ -8564,6 +9130,7 @@ var PlanCompiler = class _PlanCompiler {
8564
9130
  case "selectWindow": {
8565
9131
  const winFuncName = decision.function;
8566
9132
  if (!winFuncName) break;
9133
+ assertNqlSelectWindowFunctionAllowed(winFuncName);
8567
9134
  const winHandler = genericWindowHandler;
8568
9135
  const ctx = this.createHandlerContext(
8569
9136
  plan,
@@ -8592,11 +9159,7 @@ var PlanCompiler = class _PlanCompiler {
8592
9159
  * Compile an includeStrategy decision and register its results.
8593
9160
  * Pushes targets onto targetList, raw joins / CTEs onto instance collections.
8594
9161
  */
8595
- compileIncludeDecision(decision, plan, targetList) {
8596
- const includeResult = this.compileIncludeViaHandler(decision, plan);
8597
- if (includeResult.targets) {
8598
- targetList.push(...includeResult.targets);
8599
- }
9162
+ registerIncludeCompilationResult(includeResult) {
8600
9163
  if (includeResult.rawJoin) {
8601
9164
  this.rawJoins.push(includeResult.rawJoin);
8602
9165
  }
@@ -8607,6 +9170,27 @@ var PlanCompiler = class _PlanCompiler {
8607
9170
  this.pendingCtes.push(includeResult.cte);
8608
9171
  }
8609
9172
  }
9173
+ compileIncludeDecision(decision, plan, targetList, precompiled) {
9174
+ const includeResult = precompiled ?? this.compileIncludeViaHandler(decision, plan);
9175
+ if (includeResult.targets) {
9176
+ targetList.push(...includeResult.targets);
9177
+ }
9178
+ if (!precompiled) {
9179
+ this.registerIncludeCompilationResult(includeResult);
9180
+ }
9181
+ }
9182
+ compileJoinIncludeAllocationPass(decisions, plan) {
9183
+ const includeResults = /* @__PURE__ */ new Map();
9184
+ for (const decision of decisions) {
9185
+ if (decision.type !== "includeStrategy" || decision.choice !== "join") {
9186
+ continue;
9187
+ }
9188
+ const includeResult = this.compileIncludeViaHandler(decision, plan);
9189
+ includeResults.set(decision, includeResult);
9190
+ this.registerIncludeCompilationResult(includeResult);
9191
+ }
9192
+ return includeResults;
9193
+ }
8610
9194
  /**
8611
9195
  * Fold a WHERE-family decision into an existing where expression.
8612
9196
  * Returns the updated (or new) where node.
@@ -8726,7 +9310,11 @@ var PlanCompiler = class _PlanCompiler {
8726
9310
  if (decision.choice !== "join" || !decision.conditions || decision.conditions.length === 0) {
8727
9311
  return where;
8728
9312
  }
8729
- const joinAlias = decision.relationName;
9313
+ const relationIdentityPath = getRelationIdentityPath(
9314
+ decision,
9315
+ this.currentRootTable
9316
+ );
9317
+ const joinAlias = (relationIdentityPath ? this.joinAliasMap.get(relationIdentityPath)?.alias : void 0) ?? decision.relationName;
8730
9318
  for (const cond of decision.conditions) {
8731
9319
  const condExpr = this.dispatchWhere(
8732
9320
  cond,
@@ -8736,6 +9324,14 @@ var PlanCompiler = class _PlanCompiler {
8736
9324
  }
8737
9325
  return where;
8738
9326
  }
9327
+ reserveManualJoinAliases(decisions) {
9328
+ for (const decision of decisions) {
9329
+ if (decision.type !== "join") continue;
9330
+ const alias = decision.alias ?? decision.targetTable;
9331
+ if (!alias) continue;
9332
+ this.usedJoinAliases.add(this.emittedJoinAlias(alias));
9333
+ }
9334
+ }
8739
9335
  /**
8740
9336
  * Apply a single join decision to the FROM clause in-place.
8741
9337
  * Chains multiple joins by wrapping from[0] as the left-arg each time.
@@ -8800,14 +9396,11 @@ var PlanCompiler = class _PlanCompiler {
8800
9396
  */
8801
9397
  compileGroupByDecision(decision) {
8802
9398
  const gbCol = decision.column;
8803
- const gbDot = gbCol.indexOf(".");
9399
+ const gbDot = gbCol.lastIndexOf(".");
8804
9400
  if (gbDot !== -1) {
8805
- return columnRef(
8806
- gbCol.slice(gbDot + 1),
8807
- gbCol.slice(0, gbDot),
8808
- void 0,
8809
- this.naming
8810
- );
9401
+ const relation = gbCol.slice(0, gbDot);
9402
+ const alias = this.resolvedJoinAliases().get(relation) ?? relation;
9403
+ return columnRef(gbCol.slice(gbDot + 1), alias, void 0, this.naming);
8811
9404
  }
8812
9405
  return columnRef(gbCol, decision.table, void 0, this.naming);
8813
9406
  }
@@ -8853,19 +9446,31 @@ var PlanCompiler = class _PlanCompiler {
8853
9446
  return selectStmt(options);
8854
9447
  }
8855
9448
  compileSelect(plan) {
9449
+ const decisions = mergeDuplicateJoinIncludeDecisions(
9450
+ plan.decisions,
9451
+ plan.rootTable
9452
+ );
9453
+ this.reserveManualJoinAliases(decisions);
9454
+ const includeJoinResults = this.compileJoinIncludeAllocationPass(
9455
+ decisions,
9456
+ plan
9457
+ );
9458
+ this.state.aliases = this.resolvedJoinAliases();
8856
9459
  const targetList = [];
8857
9460
  const from = this.compileFromClause(plan);
8858
9461
  let where;
8859
9462
  const orderBy = [];
9463
+ const orderByDecisions = [];
8860
9464
  const groupBy = [];
8861
9465
  let having;
8862
9466
  let limit;
8863
9467
  let offset;
8864
9468
  let distinct = false;
8865
- for (const decision of plan.decisions) {
9469
+ for (const decision of decisions) {
8866
9470
  switch (decision.type) {
8867
9471
  case "select":
8868
9472
  case "selectFunction":
9473
+ case "selectNqlFunction":
8869
9474
  case "selectExpression":
8870
9475
  case "selectRelationColumn":
8871
9476
  case "selectPseudoColumn":
@@ -8875,7 +9480,12 @@ var PlanCompiler = class _PlanCompiler {
8875
9480
  this.compileSelectTarget(decision, plan, targetList);
8876
9481
  break;
8877
9482
  case "includeStrategy":
8878
- this.compileIncludeDecision(decision, plan, targetList);
9483
+ this.compileIncludeDecision(
9484
+ decision,
9485
+ plan,
9486
+ targetList,
9487
+ includeJoinResults.get(decision)
9488
+ );
8879
9489
  where = this.compileIncludeWhereConditions(decision, where);
8880
9490
  break;
8881
9491
  case "where":
@@ -8888,8 +9498,7 @@ var PlanCompiler = class _PlanCompiler {
8888
9498
  this.compileJoinDecision(decision, plan, from);
8889
9499
  break;
8890
9500
  case "orderBy": {
8891
- const obNode = this.compileOrderByDecision(decision, plan);
8892
- if (obNode) orderBy.push(obNode);
9501
+ orderByDecisions.push(decision);
8893
9502
  break;
8894
9503
  }
8895
9504
  case "groupBy":
@@ -8903,6 +9512,10 @@ var PlanCompiler = class _PlanCompiler {
8903
9512
  case "limit":
8904
9513
  if (typeof decision.limit === "number") {
8905
9514
  limit = integerNode(decision.limit);
9515
+ } else if (isParamIntent6(decision.limit)) {
9516
+ this.state.parameters.push(unwrapParamIntent(decision.limit));
9517
+ this.state.paramIndex++;
9518
+ limit = createParamRef(this.state.paramIndex);
8906
9519
  } else if (decision.limit?.paramIndex !== void 0) {
8907
9520
  limit = createParamRef(decision.limit.paramIndex);
8908
9521
  this.state.parameters.push(void 0);
@@ -8911,6 +9524,10 @@ var PlanCompiler = class _PlanCompiler {
8911
9524
  case "offset":
8912
9525
  if (typeof decision.offset === "number") {
8913
9526
  offset = integerNode(decision.offset);
9527
+ } else if (isParamIntent6(decision.offset)) {
9528
+ this.state.parameters.push(unwrapParamIntent(decision.offset));
9529
+ this.state.paramIndex++;
9530
+ offset = createParamRef(this.state.paramIndex);
8914
9531
  } else if (decision.offset?.paramIndex !== void 0) {
8915
9532
  offset = createParamRef(decision.offset.paramIndex);
8916
9533
  this.state.parameters.push(void 0);
@@ -8928,6 +9545,10 @@ var PlanCompiler = class _PlanCompiler {
8928
9545
  break;
8929
9546
  }
8930
9547
  }
9548
+ for (const decision of orderByDecisions) {
9549
+ const obNode = this.compileOrderByDecision(decision, plan);
9550
+ if (obNode) orderBy.push(obNode);
9551
+ }
8931
9552
  this.flushPendingJoins(from, plan);
8932
9553
  return this.buildSelectStmt(
8933
9554
  targetList,
@@ -12520,8 +13141,10 @@ function matchGlob(pattern, value) {
12520
13141
  init_ast_helpers();
12521
13142
  init_compiler_utils();
12522
13143
  init_handlers();
13144
+ init_param_intent();
12523
13145
  init_param_ref();
12524
13146
  import { isSqlRaw } from "@dbsp/core";
13147
+ import { isParamIntent as isParamIntent7 } from "@dbsp/types";
12525
13148
  function buildReturningList(columns, tableRef, ctx) {
12526
13149
  if (!columns || columns.length === 0) return void 0;
12527
13150
  const { naming } = ctx;
@@ -12779,7 +13402,7 @@ function compileInsertFrom(config, ctx, state) {
12779
13402
  }
12780
13403
  let limitCount;
12781
13404
  if (config.limit !== void 0) {
12782
- limitCount = { A_Const: { ival: { ival: config.limit } } };
13405
+ limitCount = limitToNode(config.limit, state);
12783
13406
  }
12784
13407
  const sourceRelation = {
12785
13408
  relname: dbSourceTable,
@@ -12854,7 +13477,7 @@ function compileUpsertFrom(config, ctx, state) {
12854
13477
  }
12855
13478
  let limitCount;
12856
13479
  if (config.limit !== void 0) {
12857
- limitCount = { A_Const: { ival: { ival: config.limit } } };
13480
+ limitCount = limitToNode(config.limit, state);
12858
13481
  }
12859
13482
  const sourceRelation = {
12860
13483
  relname: dbSourceTable,
@@ -12928,11 +13551,22 @@ var RANGE_TYPES = /* @__PURE__ */ new Set([
12928
13551
  "int8range",
12929
13552
  "numrange"
12930
13553
  ]);
12931
- function valueToNode(value, state, dbType) {
12932
- if (value === null || value === void 0) {
13554
+ function valueToNode(value, state, dbType, forceParam = false) {
13555
+ const isParam = isParamIntent7(value);
13556
+ const boundValue = unwrapParamIntent(value);
13557
+ if (boundValue === null || boundValue === void 0) {
13558
+ if (forceParam || isParam) {
13559
+ state.parameters.push(boundValue);
13560
+ state.paramIndex++;
13561
+ return dbType && RANGE_TYPES.has(dbType) ? createTypeCastParamRef(state.paramIndex, dbType) : {
13562
+ ParamRef: {
13563
+ number: state.paramIndex
13564
+ }
13565
+ };
13566
+ }
12933
13567
  return { A_Const: { isnull: true } };
12934
13568
  }
12935
- state.parameters.push(value);
13569
+ state.parameters.push(boundValue);
12936
13570
  state.paramIndex++;
12937
13571
  if (dbType && RANGE_TYPES.has(dbType)) {
12938
13572
  return createTypeCastParamRef(state.paramIndex, dbType);
@@ -12943,6 +13577,18 @@ function valueToNode(value, state, dbType) {
12943
13577
  }
12944
13578
  };
12945
13579
  }
13580
+ function limitToNode(limit, state) {
13581
+ if (isParamIntent7(limit)) {
13582
+ state.parameters.push(unwrapParamIntent(limit));
13583
+ state.paramIndex++;
13584
+ return {
13585
+ ParamRef: {
13586
+ number: state.paramIndex
13587
+ }
13588
+ };
13589
+ }
13590
+ return { A_Const: { ival: { ival: limit } } };
13591
+ }
12946
13592
  function compileMutation(decision, ctx, state) {
12947
13593
  const type = decision.type;
12948
13594
  const table = decision.table ?? ctx.rootTable;
@@ -12982,6 +13628,7 @@ function compileMutation(decision, ctx, state) {
12982
13628
  init_ast_helpers();
12983
13629
  init_compiler_utils();
12984
13630
  init_handlers();
13631
+ init_param_intent();
12985
13632
  init_param_ref();
12986
13633
  function buildOnConflictClause(config, ctx, state) {
12987
13634
  const naming = ctx.naming;
@@ -13141,7 +13788,7 @@ function compileUnnestUpsert(config, ctx, state) {
13141
13788
  }
13142
13789
  function valueToParam(state, value) {
13143
13790
  if (value !== void 0) {
13144
- state.parameters.push(value);
13791
+ state.parameters.push(unwrapParamIntent(value));
13145
13792
  }
13146
13793
  state.paramIndex++;
13147
13794
  return {
@@ -13382,7 +14029,7 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
13382
14029
  // src/adapter-compiler-mutations.ts
13383
14030
  init_compile_where();
13384
14031
  init_compiler_utils();
13385
- import { InvalidOperationError as InvalidOperationError2, isSqlRaw as isSqlRaw2 } from "@dbsp/core";
14032
+ import { InvalidOperationError as InvalidOperationError3, isSqlRaw as isSqlRaw2 } from "@dbsp/core";
13386
14033
  init_handlers();
13387
14034
  function whereIntentAsDecision(where) {
13388
14035
  return where;
@@ -13460,9 +14107,8 @@ function compileInsert2(intent, options, deps) {
13460
14107
  const state = createCompilerState();
13461
14108
  const firstRow = intent.values?.[0] ?? {};
13462
14109
  const columns = Object.keys(firstRow);
13463
- const values = (intent.values ?? []).map(
13464
- (row) => columns.map((col) => row[col])
13465
- );
14110
+ const rows = intent.values ?? [];
14111
+ const values = rows.map((row) => columns.map((col) => row[col]));
13466
14112
  const columnTypes = getColumnTypes(intent.table, columns, deps);
13467
14113
  const config = {
13468
14114
  table: intent.table,
@@ -13473,7 +14119,7 @@ function compileInsert2(intent, options, deps) {
13473
14119
  };
13474
14120
  const maxBatchSize = options?.maxBatchSize;
13475
14121
  if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
13476
- throw new InvalidOperationError2(
14122
+ throw new InvalidOperationError3(
13477
14123
  "insert",
13478
14124
  `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
13479
14125
  );
@@ -13549,7 +14195,7 @@ function compileBatchUpdate(intent, _options, deps) {
13549
14195
  };
13550
14196
  const state = createCompilerState();
13551
14197
  if (intent.updates.length === 0) {
13552
- throw new InvalidOperationError2(
14198
+ throw new InvalidOperationError3(
13553
14199
  "update",
13554
14200
  "batchSet requires at least one row"
13555
14201
  );
@@ -13558,7 +14204,7 @@ function compileBatchUpdate(intent, _options, deps) {
13558
14204
  const matchColumns = [...intent.matchColumns];
13559
14205
  for (const mc of matchColumns) {
13560
14206
  if (!allColumns.includes(mc)) {
13561
- throw new InvalidOperationError2(
14207
+ throw new InvalidOperationError3(
13562
14208
  "update",
13563
14209
  `Match column "${mc}" not found in update data. Each row must include the match column(s).`
13564
14210
  );
@@ -13581,7 +14227,13 @@ function compileBatchUpdate(intent, _options, deps) {
13581
14227
  naming: deps.naming,
13582
14228
  ...schemaName !== void 0 && { schemaName },
13583
14229
  ...deps.model !== void 0 && { model: deps.model },
13584
- compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(sqIntent, paramOffset, deps.naming, schemaName)
14230
+ compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
14231
+ sqIntent,
14232
+ paramOffset,
14233
+ deps.naming,
14234
+ schemaName,
14235
+ "rawExists"
14236
+ )
13585
14237
  };
13586
14238
  whereGuard = compileWhereIntent(intent.where, whereCtx);
13587
14239
  }
@@ -13685,7 +14337,7 @@ function compileUpsert2(intent, options, deps) {
13685
14337
  };
13686
14338
  const maxBatchSize = options?.maxBatchSize;
13687
14339
  if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
13688
- throw new InvalidOperationError2(
14340
+ throw new InvalidOperationError3(
13689
14341
  "upsert",
13690
14342
  `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
13691
14343
  );
@@ -13738,6 +14390,7 @@ function compileUpsertFrom2(intent, options, deps) {
13738
14390
  init_assert_field();
13739
14391
  init_ast_helpers();
13740
14392
  init_compile_where();
14393
+ import { countDistinctRelationPathsByName } from "@dbsp/core";
13741
14394
  init_compiler_utils();
13742
14395
  init_types();
13743
14396
  init_intent_to_decisions();
@@ -13746,6 +14399,7 @@ init_param_ref();
13746
14399
  // src/plan-decision-extractor.ts
13747
14400
  init_assert_field();
13748
14401
  init_intent_to_decisions();
14402
+ import { deriveRelationPathFromIntentPath } from "@dbsp/core";
13749
14403
  function findExistsIntents(where) {
13750
14404
  if (!where || typeof where !== "object") return [];
13751
14405
  const w = where;
@@ -13790,7 +14444,9 @@ function resolveIncludeByPath(includes, intentPath, relationName) {
13790
14444
  }
13791
14445
  if (resolved) return resolved;
13792
14446
  }
13793
- return includes.find((i) => i.relation === relationName);
14447
+ return includes.find(
14448
+ (i) => i.relation === relationName || i.via === relationName
14449
+ );
13794
14450
  }
13795
14451
  function deriveForeignKey(context, deriveFk = defaultFkDerivation, defaultPk = DEFAULT_PK_COLUMN) {
13796
14452
  const fk = context.foreignKey ?? context.sourceFK;
@@ -13815,24 +14471,6 @@ function mapComparisonOperator(op3) {
13815
14471
  };
13816
14472
  return map[op3] ?? "=";
13817
14473
  }
13818
- function valueToNode2(value) {
13819
- if (typeof value === "string") {
13820
- return { A_Const: { sval: { sval: value } } };
13821
- }
13822
- if (typeof value === "number") {
13823
- if (Number.isInteger(value)) {
13824
- return { A_Const: { ival: { ival: value } } };
13825
- }
13826
- return { A_Const: { fval: { fval: String(value) } } };
13827
- }
13828
- if (typeof value === "boolean") {
13829
- return { A_Const: { boolval: { boolval: value } } };
13830
- }
13831
- if (value === null) {
13832
- return { A_Const: { isnull: true } };
13833
- }
13834
- return { A_Const: { sval: { sval: String(value) } } };
13835
- }
13836
14474
  function convertWhereToDecisions(where, table) {
13837
14475
  if (!where || typeof where !== "object") return [];
13838
14476
  const w = where;
@@ -14499,6 +15137,11 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
14499
15137
  type: "includeStrategy",
14500
15138
  choice: effectiveChoice,
14501
15139
  relationName,
15140
+ relationPath: deriveRelationPathFromIntentPath(
15141
+ Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
15142
+ context.intentPath,
15143
+ relationName
15144
+ ) ?? relationName,
14502
15145
  targetTable: context.target,
14503
15146
  ...context.sourceTable && { sourceTable: context.sourceTable },
14504
15147
  ...relationType && { relationType },
@@ -14513,6 +15156,11 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
14513
15156
  const relationName = context.relation ?? context.includeAlias;
14514
15157
  if (!context.target || !relationName) return void 0;
14515
15158
  const intentPath = context?.intentPath;
15159
+ const relationPath = deriveRelationPathFromIntentPath(
15160
+ Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
15161
+ intentPath,
15162
+ relationName
15163
+ ) ?? relationName;
14516
15164
  const includeIntent = resolveIncludeByPath(
14517
15165
  plan.intent?.include,
14518
15166
  intentPath,
@@ -14540,6 +15188,7 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
14540
15188
  type: "includeStrategy",
14541
15189
  choice: "join",
14542
15190
  relationName,
15191
+ relationPath,
14543
15192
  targetTable: context.target,
14544
15193
  ...context.sourceTable && { sourceTable: context.sourceTable },
14545
15194
  ...relationType && { relationType },
@@ -14585,6 +15234,7 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
14585
15234
  type: "includeStrategy",
14586
15235
  choice: "join",
14587
15236
  relationName: alias,
15237
+ relationPath: alias,
14588
15238
  targetTable: rel.target,
14589
15239
  sourceTable,
14590
15240
  ...rel.type && {
@@ -14881,22 +15531,11 @@ function buildRelationColumnsMap(decisions, includedRelations) {
14881
15531
  }
14882
15532
  return map;
14883
15533
  }
14884
- function findRelationMapKey(map, relationName) {
14885
- if (map.has(relationName)) return relationName;
14886
- const suffix = `.${relationName}`;
14887
- for (const key of map.keys()) {
14888
- if (key.endsWith(suffix)) return key;
14889
- }
14890
- return void 0;
14891
- }
14892
15534
  function injectAndValidateRelationColumns(enrichedUnifiedDecisions, relationColumnsMap, model) {
14893
15535
  if (relationColumnsMap.size === 0) return;
14894
15536
  for (const d of enrichedUnifiedDecisions) {
14895
15537
  if (d.type === "includeStrategy" && d.relationName) {
14896
- const mapKey = findRelationMapKey(
14897
- relationColumnsMap,
14898
- d.relationName
14899
- );
15538
+ const mapKey = d.relationPath ?? d.relationName;
14900
15539
  const entries = mapKey ? relationColumnsMap.get(mapKey) : void 0;
14901
15540
  if (entries) {
14902
15541
  const mut = d;
@@ -14931,6 +15570,27 @@ function injectAndValidateRelationColumns(enrichedUnifiedDecisions, relationColu
14931
15570
  }
14932
15571
  }
14933
15572
  }
15573
+ function applyJoinHydrationPrefixes(decisions) {
15574
+ const usages = [];
15575
+ for (const d of decisions) {
15576
+ if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15577
+ continue;
15578
+ }
15579
+ const relationName = d.relationName;
15580
+ const relationPath = d.relationPath ?? relationName;
15581
+ usages.push({ relationName, relationPath });
15582
+ }
15583
+ const pathCountsByRelation = countDistinctRelationPathsByName(usages);
15584
+ for (const d of decisions) {
15585
+ if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15586
+ continue;
15587
+ }
15588
+ const relationName = d.relationName;
15589
+ const relationPath = d.relationPath ?? relationName;
15590
+ const usesFullPath = (pathCountsByRelation.get(relationName) ?? 0) > 1;
15591
+ d.hydrationPrefix = usesFullPath ? relationPath : relationName;
15592
+ }
15593
+ }
14934
15594
  function enrichRangeDecisions(allDecisions, model, rootTable) {
14935
15595
  if (!model) return;
14936
15596
  for (let i = 0; i < allDecisions.length; i++) {
@@ -15019,6 +15679,7 @@ function compileSelect(plan, options, deps) {
15019
15679
  ...allUnifiedIncludeDecisions
15020
15680
  ];
15021
15681
  stripJoinColumnsForAggregation(enrichedUnifiedDecisions, execIntent);
15682
+ applyJoinHydrationPrefixes(enrichedUnifiedDecisions);
15022
15683
  const includedRelations = new Set(
15023
15684
  enrichedUnifiedDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
15024
15685
  );
@@ -15116,6 +15777,7 @@ function compileWithIncludes(plan, options, deps) {
15116
15777
  init_ast_helpers();
15117
15778
  init_compiler_utils();
15118
15779
  init_handlers();
15780
+ init_utils();
15119
15781
  init_param_ref();
15120
15782
 
15121
15783
  // src/recursive/cte-compiler.ts
@@ -15829,6 +16491,7 @@ function buildEdgeTableRecursiveCte(config) {
15829
16491
  // src/adapter-compiler-recursive.ts
15830
16492
  function compileRecursive(report, _model, _options, deps) {
15831
16493
  const schemaName = deps.schemaName;
16494
+ const state = createCompilerState();
15832
16495
  const intent = report.intent;
15833
16496
  const traversal = intent.traversal;
15834
16497
  const trackPath = intent.track?.path !== void 0;
@@ -15848,7 +16511,7 @@ function compileRecursive(report, _model, _options, deps) {
15848
16511
  const selectColumns = Array.from(/* @__PURE__ */ new Set([nodeIdColumn, ...startSelect]));
15849
16512
  const edgeFrom = traversal.direction === "in" ? traversal.edgeTo : traversal.edgeFrom;
15850
16513
  const edgeTo = traversal.direction === "in" ? traversal.edgeFrom : traversal.edgeTo;
15851
- const anchorWhere = intent.start.where ? buildRecursiveAnchorWhere(intent.start.where, "__n", deps) : void 0;
16514
+ const anchorWhere = intent.start.where ? buildRecursiveAnchorWhere(intent.start.where, "__n", deps, state) : void 0;
15852
16515
  const base = {
15853
16516
  cteAlias: intent.cteName,
15854
16517
  table,
@@ -15974,11 +16637,10 @@ function compileRecursive(report, _model, _options, deps) {
15974
16637
  const sql = deparseQuoted({ SelectStmt: selectStmt2 });
15975
16638
  return {
15976
16639
  sql,
15977
- parameters: []
16640
+ parameters: state.parameters
15978
16641
  };
15979
16642
  }
15980
- function compileCteQuery(intent, _options, deps) {
15981
- const schemaName = deps.schemaName;
16643
+ function compileCteQuery(intent, options, deps) {
15982
16644
  const state = createCompilerState();
15983
16645
  const allCteParams = [];
15984
16646
  const cteSqlFragments = [];
@@ -15992,8 +16654,8 @@ function compileCteQuery(intent, _options, deps) {
15992
16654
  isRecursive = true;
15993
16655
  const { sql: cteSql, params: cteParams } = buildRawCte(
15994
16656
  cte,
15995
- schemaName,
15996
- deps
16657
+ deps,
16658
+ options
15997
16659
  );
15998
16660
  allCteParams.push(...cteParams);
15999
16661
  cteSqlFragments.push(cteSql);
@@ -16011,12 +16673,7 @@ function compileCteQuery(intent, _options, deps) {
16011
16673
  isAmbiguous: false
16012
16674
  }
16013
16675
  };
16014
- const innerCompileOptions = schemaName !== void 0 ? { schemaName } : {};
16015
- const innerCompiled = compileSelect(
16016
- innerPlanReport,
16017
- innerCompileOptions,
16018
- deps
16019
- );
16676
+ const innerCompiled = compileSelect(innerPlanReport, options, deps);
16020
16677
  const currentParamOffset = allCteParams.length;
16021
16678
  const renumberedInnerSql = currentParamOffset > 0 ? innerCompiled.sql.replace(
16022
16679
  /\$([0-9]+)/g,
@@ -16031,7 +16688,6 @@ function compileCteQuery(intent, _options, deps) {
16031
16688
  );
16032
16689
  }
16033
16690
  }
16034
- const outerCompileOptions = schemaName !== void 0 ? { schemaName } : {};
16035
16691
  const outerPlanReport = {
16036
16692
  rootTable: intent.query.from,
16037
16693
  decisions: [],
@@ -16044,11 +16700,7 @@ function compileCteQuery(intent, _options, deps) {
16044
16700
  isAmbiguous: false
16045
16701
  }
16046
16702
  };
16047
- const outerCompiled = compileSelect(
16048
- outerPlanReport,
16049
- outerCompileOptions,
16050
- deps
16051
- );
16703
+ const outerCompiled = compileSelect(outerPlanReport, options, deps);
16052
16704
  const cteParamCount = allCteParams.length;
16053
16705
  const renumberedOuterSql = cteParamCount > 0 ? outerCompiled.sql.replace(
16054
16706
  /\$([0-9]+)/g,
@@ -16114,8 +16766,7 @@ function buildUnnestCte(cte, state, deps) {
16114
16766
  }
16115
16767
  };
16116
16768
  }
16117
- function buildRawCte(cte, schemaName, deps) {
16118
- const compileOptions = schemaName !== void 0 ? { schemaName } : {};
16769
+ function buildRawCte(cte, deps, options) {
16119
16770
  const basePlanReport = {
16120
16771
  rootTable: cte.base.from,
16121
16772
  decisions: [],
@@ -16124,7 +16775,7 @@ function buildRawCte(cte, schemaName, deps) {
16124
16775
  intent: cte.base,
16125
16776
  metadata: { planningTimeMs: 0, relationsAnalyzed: 0, isAmbiguous: false }
16126
16777
  };
16127
- const baseCompiled = compileSelect(basePlanReport, compileOptions, deps);
16778
+ const baseCompiled = compileSelect(basePlanReport, options, deps);
16128
16779
  const stepPlanReport = {
16129
16780
  rootTable: cte.step.from,
16130
16781
  decisions: [],
@@ -16133,7 +16784,7 @@ function buildRawCte(cte, schemaName, deps) {
16133
16784
  intent: cte.step,
16134
16785
  metadata: { planningTimeMs: 0, relationsAnalyzed: 0, isAmbiguous: false }
16135
16786
  };
16136
- const stepCompiled = compileSelect(stepPlanReport, compileOptions, deps);
16787
+ const stepCompiled = compileSelect(stepPlanReport, options, deps);
16137
16788
  const baseParamCount = baseCompiled.parameters.length;
16138
16789
  const renumberedStepSql = baseParamCount > 0 ? stepCompiled.sql.replace(
16139
16790
  /\$([0-9]+)/g,
@@ -16159,7 +16810,7 @@ function buildRawCte(cte, schemaName, deps) {
16159
16810
  params: allParams
16160
16811
  };
16161
16812
  }
16162
- function buildRecursiveAnchorWhere(where, tableAlias, deps) {
16813
+ function buildRecursiveAnchorWhere(where, tableAlias, deps, state) {
16163
16814
  if (!where || typeof where !== "object") {
16164
16815
  return { A_Const: { boolval: { boolval: true } } };
16165
16816
  }
@@ -16176,7 +16827,7 @@ function buildRecursiveAnchorWhere(where, tableAlias, deps) {
16176
16827
  }
16177
16828
  };
16178
16829
  const op3 = mapComparisonOperator(w.operator);
16179
- const right = valueToNode2(w.value);
16830
+ const right = compileValue(w.value, state, void 0, true);
16180
16831
  return {
16181
16832
  A_Expr: {
16182
16833
  kind: "AEXPR_OP",
@@ -16188,14 +16839,14 @@ function buildRecursiveAnchorWhere(where, tableAlias, deps) {
16188
16839
  }
16189
16840
  case "and": {
16190
16841
  const conditions = w.conditions.map(
16191
- (c) => buildRecursiveAnchorWhere(c, tableAlias, deps)
16842
+ (c) => buildRecursiveAnchorWhere(c, tableAlias, deps, state)
16192
16843
  );
16193
16844
  if (conditions.length === 1) return conditions[0];
16194
16845
  return { BoolExpr: { boolop: "AND_EXPR", args: conditions } };
16195
16846
  }
16196
16847
  case "or": {
16197
16848
  const conditions = w.conditions.map(
16198
- (c) => buildRecursiveAnchorWhere(c, tableAlias, deps)
16849
+ (c) => buildRecursiveAnchorWhere(c, tableAlias, deps, state)
16199
16850
  );
16200
16851
  if (conditions.length === 1) return conditions[0];
16201
16852
  return { BoolExpr: { boolop: "OR_EXPR", args: conditions } };
@@ -16374,12 +17025,12 @@ function compileLeafOrBranch(intent, compileFn) {
16374
17025
  const result = compileFn(intent);
16375
17026
  return { sql: result.sql, parameters: result.parameters };
16376
17027
  }
16377
- function createLeafCompileFn(adapter, model, planFn2) {
17028
+ function createLeafCompileFn(adapter, model, planFn2, options) {
16378
17029
  return (query) => {
16379
17030
  const planReport = planFn2(query, model, {
16380
17031
  dialectCapabilities: adapter.dialectCapabilities
16381
17032
  });
16382
- return adapter.compile(planReport, { model });
17033
+ return adapter.compile(planReport, { ...options, model });
16383
17034
  };
16384
17035
  }
16385
17036
 
@@ -16498,6 +17149,15 @@ function mapFetchDirection(direction) {
16498
17149
 
16499
17150
  // src/pgsql-adapter.ts
16500
17151
  init_validate();
17152
+ function renumberSqlParams(sql, offset) {
17153
+ if (offset === 0) return sql;
17154
+ return sql.replace(/\$(\d+)/g, (_match, num) => {
17155
+ return `$${Number.parseInt(num, 10) + offset}`;
17156
+ });
17157
+ }
17158
+ function isCompiledNqlQuery(input) {
17159
+ return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input);
17160
+ }
16501
17161
  var PgsqlAdapter = class _PgsqlAdapter {
16502
17162
  pool;
16503
17163
  client;
@@ -16577,6 +17237,124 @@ var PgsqlAdapter = class _PgsqlAdapter {
16577
17237
  deriveFk: this.deriveFk
16578
17238
  };
16579
17239
  }
17240
+ requireNqlCompileModel(options) {
17241
+ const model = options?.model ?? this.model;
17242
+ if (model === void 0) {
17243
+ throw new Error(
17244
+ "Compiling an NQL bundle requires a model. Pass { model } to adapter.compile(compiledNql, { model }) or configure the adapter with a model."
17245
+ );
17246
+ }
17247
+ return model;
17248
+ }
17249
+ compileNqlMutation(bundle, options) {
17250
+ const mutation = bundle.mutation;
17251
+ if (mutation === void 0) {
17252
+ throw new Error("NQL bundle did not contain a mutation intent.");
17253
+ }
17254
+ switch (mutation.type) {
17255
+ case "insert":
17256
+ return compileInsert2(
17257
+ mutation,
17258
+ options,
17259
+ this.buildCompileDeps(options)
17260
+ );
17261
+ case "insert_from":
17262
+ return compileInsertFrom2(
17263
+ mutation,
17264
+ options,
17265
+ this.buildCompileDeps(options)
17266
+ );
17267
+ case "update":
17268
+ return compileUpdate2(
17269
+ mutation,
17270
+ options,
17271
+ this.buildCompileDeps(options)
17272
+ );
17273
+ case "delete":
17274
+ return compileDelete2(
17275
+ mutation,
17276
+ options,
17277
+ this.buildCompileDeps(options)
17278
+ );
17279
+ case "upsert":
17280
+ return compileUpsert2(
17281
+ mutation,
17282
+ options,
17283
+ this.buildCompileDeps(options)
17284
+ );
17285
+ case "upsert_from":
17286
+ return compileUpsertFrom2(
17287
+ mutation,
17288
+ options,
17289
+ this.buildCompileDeps(options)
17290
+ );
17291
+ }
17292
+ throw new Error(
17293
+ `Unsupported NQL mutation type: ${mutation.type}`
17294
+ );
17295
+ }
17296
+ compileNqlBundleLeaf(bundle, options) {
17297
+ if (bundle.query !== void 0) {
17298
+ const model = this.requireNqlCompileModel(options);
17299
+ const planReport = planFn(bundle.query, model, {
17300
+ dialectCapabilities: this.dialectCapabilities
17301
+ });
17302
+ return compileSelect(
17303
+ planReport,
17304
+ options,
17305
+ this.buildCompileDeps(options)
17306
+ );
17307
+ }
17308
+ if (bundle.cteQuery !== void 0) {
17309
+ return compileCteQuery(
17310
+ bundle.cteQuery,
17311
+ options,
17312
+ this.buildCompileDeps(options)
17313
+ );
17314
+ }
17315
+ if (bundle.setOperation !== void 0) {
17316
+ const model = this.requireNqlCompileModel(options);
17317
+ return this.compileSetOperation(
17318
+ bundle.setOperation,
17319
+ model,
17320
+ options
17321
+ );
17322
+ }
17323
+ if (bundle.mutation !== void 0) {
17324
+ return this.compileNqlMutation(bundle, options);
17325
+ }
17326
+ throw new Error("NQL bundle did not contain a compilable intent.");
17327
+ }
17328
+ compileNqlBundle(bundle, options) {
17329
+ const ctes = [];
17330
+ const parameters = [];
17331
+ for (const [name, queryIntent] of bundle.bindings ?? []) {
17332
+ const cteName = quoteIdent2(name, "alias");
17333
+ const bindingBundle = bundle.mutationBindings?.has(name) ? { mutation: bundle.mutationBindings.get(name) } : { query: queryIntent };
17334
+ const compiled2 = this.compileNqlBundleLeaf(bindingBundle, options);
17335
+ ctes.push(
17336
+ `${cteName} as (${renumberSqlParams(compiled2.sql, parameters.length)})`
17337
+ );
17338
+ parameters.push(...compiled2.parameters);
17339
+ }
17340
+ const leafBundle = {
17341
+ ...bundle.query !== void 0 && { query: bundle.query },
17342
+ ...bundle.cteQuery !== void 0 && { cteQuery: bundle.cteQuery },
17343
+ ...bundle.mutation !== void 0 && { mutation: bundle.mutation },
17344
+ ...bundle.returning !== void 0 && { returning: bundle.returning },
17345
+ ...bundle.setOperation !== void 0 && {
17346
+ setOperation: bundle.setOperation
17347
+ }
17348
+ };
17349
+ const compiled = this.compileNqlBundleLeaf(leafBundle, options);
17350
+ if (ctes.length === 0) {
17351
+ return compiled;
17352
+ }
17353
+ return {
17354
+ sql: `WITH ${ctes.join(", ")} ${renumberSqlParams(compiled.sql, parameters.length)}`,
17355
+ parameters: [...parameters, ...compiled.parameters]
17356
+ };
17357
+ }
16580
17358
  /**
16581
17359
  * Returns the pool/client executor, or throws if in compile-only mode.
16582
17360
  */
@@ -16616,6 +17394,9 @@ var PgsqlAdapter = class _PgsqlAdapter {
16616
17394
  * Compile a plan to executable SQL.
16617
17395
  */
16618
17396
  compile(plan, options) {
17397
+ if (isCompiledNqlQuery(plan)) {
17398
+ return this.compileNqlBundle(plan, options);
17399
+ }
16619
17400
  return compileSelect(plan, options, this.buildCompileDeps(options));
16620
17401
  }
16621
17402
  /**
@@ -16779,8 +17560,8 @@ var PgsqlAdapter = class _PgsqlAdapter {
16779
17560
  /**
16780
17561
  * Compile a set operation (UNION / INTERSECT / EXCEPT) to SQL.
16781
17562
  */
16782
- compileSetOperation(intent, model, _options) {
16783
- const compileFn = createLeafCompileFn(this, model, planFn);
17563
+ compileSetOperation(intent, model, options) {
17564
+ const compileFn = createLeafCompileFn(this, model, planFn, options);
16784
17565
  const result = compileSetOperation(intent, compileFn);
16785
17566
  return {
16786
17567
  sql: result.sql,