@dbsp/adapter-pgsql 1.3.0 → 1.5.0

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
@@ -949,6 +949,42 @@ var init_ast_helpers = __esm({
949
949
  }
950
950
  });
951
951
 
952
+ // src/binding-registry.ts
953
+ function emittedBindName(name, naming) {
954
+ return naming.toDatabase(name);
955
+ }
956
+ function hasBindingName(bindingNames, name, naming) {
957
+ return bindingNames?.has(emittedBindName(name, naming)) ?? false;
958
+ }
959
+ function schemaForFromName(schemaName, fromName, bindingNames, naming) {
960
+ return hasBindingName(bindingNames, fromName, naming) ? void 0 : schemaName;
961
+ }
962
+ function withBindingName(bindingNames, name, naming) {
963
+ const next = new Set(bindingNames ?? []);
964
+ next.add(emittedBindName(name, naming));
965
+ return next;
966
+ }
967
+ var init_binding_registry = __esm({
968
+ "src/binding-registry.ts"() {
969
+ "use strict";
970
+ }
971
+ });
972
+
973
+ // src/dialect-capabilities.ts
974
+ function supportsDialectCapability(capabilities, flag) {
975
+ return capabilities?.[flag] !== false;
976
+ }
977
+ function assertDialectCapability(capabilities, flag, feature) {
978
+ if (!supportsDialectCapability(capabilities, flag)) {
979
+ throw new Error(`${feature} not supported by this adapter`);
980
+ }
981
+ }
982
+ var init_dialect_capabilities = __esm({
983
+ "src/dialect-capabilities.ts"() {
984
+ "use strict";
985
+ }
986
+ });
987
+
952
988
  // src/param-ref.ts
953
989
  function validateParamRef(paramRef) {
954
990
  const errors = [];
@@ -1421,6 +1457,10 @@ var init_custom = __esm({
1421
1457
  });
1422
1458
 
1423
1459
  // src/select-expression-handlers.ts
1460
+ import {
1461
+ getTrustedNqlRelationFilterFields,
1462
+ markNqlTrustedRelationFilter
1463
+ } from "@dbsp/types/internal";
1424
1464
  function handleColumnExpression(expr, rootTable, decisions) {
1425
1465
  const decision = {
1426
1466
  type: "select",
@@ -1549,14 +1589,18 @@ function handleCaseExpression(expr, rootTable, decisions, _applyFilter, convertC
1549
1589
  decisions.push(decision);
1550
1590
  }
1551
1591
  function handleRelationColumnExpression(expr, rootTable, decisions) {
1592
+ const trusted = getTrustedNqlRelationFilterFields(expr);
1593
+ const trustedRelation = typeof trusted?.relation === "string" ? trusted.relation : trusted?.relation.join(".");
1552
1594
  const decision = {
1553
1595
  type: "selectRelationColumn",
1554
- relation: expr.relation,
1555
- column: expr.column ?? "*",
1596
+ relation: trustedRelation ?? expr.relation,
1597
+ column: trusted?.selectedColumn ?? (expr.column ?? "*"),
1556
1598
  table: rootTable
1557
1599
  };
1558
1600
  if (expr.as) decision.alias = expr.as;
1559
- decisions.push(decision);
1601
+ decisions.push(
1602
+ trusted?.selectedColumn !== void 0 ? markNqlTrustedRelationFilter(decision, trusted) : decision
1603
+ );
1560
1604
  }
1561
1605
  function handleArithmeticExpression(expr, rootTable, decisions) {
1562
1606
  const decision = {
@@ -1654,6 +1698,9 @@ var init_select_expression_handlers = __esm({
1654
1698
 
1655
1699
  // src/intent-to-decisions.ts
1656
1700
  import { isParamIntent } from "@dbsp/types";
1701
+ import {
1702
+ getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields2
1703
+ } from "@dbsp/types/internal";
1657
1704
  function intentToDecisions(intent, rootTable) {
1658
1705
  const decisions = [];
1659
1706
  if (intent.select) {
@@ -2039,10 +2086,29 @@ function convertNot(cond, rootTable) {
2039
2086
  if (!subDecision) return null;
2040
2087
  return { type: "whereNot", conditions: [subDecision] };
2041
2088
  }
2089
+ function formatRelationName(relation) {
2090
+ return typeof relation === "string" ? relation : relation.join(".");
2091
+ }
2042
2092
  function convertExistsLike(cond, operator) {
2043
- const targetTable = cond.relation;
2093
+ const rawRelation = cond.relation;
2094
+ const preResolved = getTrustedNqlRelationFilterFields2(cond);
2095
+ const trustedRelation = preResolved?.relation;
2096
+ const relationName = trustedRelation !== void 0 ? formatRelationName(trustedRelation) : formatRelationName(rawRelation);
2097
+ const targetTable = preResolved?.targetTable ?? rawRelation;
2044
2098
  const subDecisions = cond.where ? convertWhere(cond.where, targetTable) : [];
2045
- const base = { type: "where", operator, targetTable };
2099
+ const isPreResolved = preResolved !== void 0;
2100
+ const base = {
2101
+ type: "where",
2102
+ operator,
2103
+ targetTable,
2104
+ ...preResolved !== void 0 && {
2105
+ sourceColumn: preResolved.sourceColumn
2106
+ },
2107
+ ...preResolved !== void 0 && {
2108
+ targetColumn: preResolved.targetColumn
2109
+ },
2110
+ ...isPreResolved && { relationName }
2111
+ };
2046
2112
  return subDecisions.length > 0 ? { ...base, conditions: subDecisions } : base;
2047
2113
  }
2048
2114
  function convertSubquery(cond) {
@@ -2813,6 +2879,7 @@ var init_param_intent = __esm({
2813
2879
  });
2814
2880
 
2815
2881
  // src/handlers/where/utils.ts
2882
+ import { validateTypeName as validateTypeName3 } from "@dbsp/core";
2816
2883
  import { isFieldRef, isParamIntent as isParamIntent3 } from "@dbsp/types";
2817
2884
  function buildColumnRef(column, ctx) {
2818
2885
  if (column.includes(".")) {
@@ -2874,7 +2941,11 @@ function resolveColumnPgType(columnName, ctx) {
2874
2941
  if (!table) return void 0;
2875
2942
  const column = table.columns.find((c) => c.name === columnName);
2876
2943
  if (!column) return void 0;
2877
- if (column.originalDbType) return validateDbTypeName(column.originalDbType);
2944
+ if (column.originalDbType) {
2945
+ const typeName = column.originalDbType.trim();
2946
+ validateTypeName3(typeName);
2947
+ return typeName;
2948
+ }
2878
2949
  return void 0;
2879
2950
  }
2880
2951
  var init_utils = __esm({
@@ -2883,7 +2954,6 @@ var init_utils = __esm({
2883
2954
  init_ast_helpers();
2884
2955
  init_param_intent();
2885
2956
  init_param_ref();
2886
- init_validate();
2887
2957
  init_types();
2888
2958
  init_param_intent();
2889
2959
  }
@@ -2915,6 +2985,7 @@ var init_any = __esm({
2915
2985
  "src/handlers/where/any.ts"() {
2916
2986
  "use strict";
2917
2987
  init_compiler_utils();
2988
+ init_dialect_capabilities();
2918
2989
  init_param_intent();
2919
2990
  init_param_ref();
2920
2991
  init_types();
@@ -2922,6 +2993,11 @@ var init_any = __esm({
2922
2993
  anyHandler = {
2923
2994
  operators: [COLLECTION_OPERATORS.ANY],
2924
2995
  compile(decision, ctx, state) {
2996
+ assertDialectCapability(
2997
+ ctx.dialectCapabilities,
2998
+ "supportsArrayType",
2999
+ "ANY array operator is"
3000
+ );
2925
3001
  const column = decision.column;
2926
3002
  if (!column) {
2927
3003
  throw new Error("ANY handler requires a column");
@@ -3117,6 +3193,9 @@ function buildCorrelation(sourceAlias, sourceColumn, targetAlias, targetColumn,
3117
3193
  const right = columnRef(targetColumn, targetAlias, void 0, ctx.naming);
3118
3194
  return eqExpr(left, right);
3119
3195
  }
3196
+ function schemaForExistsFromName(ctx, fromName) {
3197
+ return schemaForFromName(ctx.schema, fromName, ctx.bindingNames, ctx.naming);
3198
+ }
3120
3199
  function buildExistsSubquery(decision, ctx, state, dispatch) {
3121
3200
  const relation = decision.relation;
3122
3201
  const targetTable = decision.targetTable ?? relation;
@@ -3160,7 +3239,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
3160
3239
  let fromNode = rangeVar(
3161
3240
  targetTable,
3162
3241
  targetAlias,
3163
- ctx.schema,
3242
+ schemaForExistsFromName(ctx, targetTable),
3164
3243
  ctx.naming
3165
3244
  );
3166
3245
  const includeDecisions = decision.include;
@@ -3219,7 +3298,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
3219
3298
  const joinRangeVar = rangeVar(
3220
3299
  joinTargetTable,
3221
3300
  joinAlias,
3222
- ctx.schema,
3301
+ schemaForExistsFromName(ctx, joinTargetTable),
3223
3302
  ctx.naming
3224
3303
  );
3225
3304
  fromNode = joinExpr(joinType, fromNode, joinRangeVar, joinQuals);
@@ -3245,6 +3324,7 @@ var init_exists = __esm({
3245
3324
  "use strict";
3246
3325
  init_assert_field();
3247
3326
  init_ast_helpers();
3327
+ init_binding_registry();
3248
3328
  existsHandler = {
3249
3329
  operators: ["exists", "some"],
3250
3330
  compile(decision, ctx, state, dispatch) {
@@ -3315,6 +3395,21 @@ function createNotInExpr(columnNode, state, values, columnType) {
3315
3395
  }
3316
3396
  };
3317
3397
  }
3398
+ function createLiteralInListExpr(columnNode, state, values, negated) {
3399
+ const paramRefs = values.map((value) => {
3400
+ state.paramIndex++;
3401
+ state.parameters.push(value);
3402
+ return createParamRef(state.paramIndex);
3403
+ });
3404
+ return {
3405
+ A_Expr: {
3406
+ kind: "AEXPR_IN",
3407
+ name: [{ String: { sval: negated ? "<>" : "=" } }],
3408
+ lexpr: columnNode,
3409
+ rexpr: { List: { items: paramRefs } }
3410
+ }
3411
+ };
3412
+ }
3318
3413
  function isWholeArrayParamList(value) {
3319
3414
  return value.length === 1 && isParamIntent4(value[0]) && Array.isArray(value[0].value);
3320
3415
  }
@@ -3330,6 +3425,7 @@ var init_in = __esm({
3330
3425
  "src/handlers/where/in.ts"() {
3331
3426
  "use strict";
3332
3427
  init_ast_helpers();
3428
+ init_dialect_capabilities();
3333
3429
  init_param_intent();
3334
3430
  init_param_ref();
3335
3431
  init_types();
@@ -3358,7 +3454,11 @@ var init_in = __esm({
3358
3454
  }
3359
3455
  const columnNode = buildColumnRef(column, ctx);
3360
3456
  const columnType = resolveColumnPgType(column, ctx);
3361
- if (operator === COLLECTION_OPERATORS.NOT_IN || operator === "notIn") {
3457
+ const isNotIn = operator === COLLECTION_OPERATORS.NOT_IN || operator === "notIn";
3458
+ if (!supportsDialectCapability(ctx.dialectCapabilities, "supportsArrayType")) {
3459
+ return createLiteralInListExpr(columnNode, state, values, isNotIn);
3460
+ }
3461
+ if (isNotIn) {
3362
3462
  return createNotInExpr(columnNode, state, values, columnType);
3363
3463
  }
3364
3464
  return createInExpr(columnNode, state, values, columnType);
@@ -3372,10 +3472,16 @@ var jsonContainsHandler, jsonExistsHandler, jsonComparisonHandler;
3372
3472
  var init_json = __esm({
3373
3473
  "src/handlers/where/json.ts"() {
3374
3474
  "use strict";
3475
+ init_dialect_capabilities();
3375
3476
  init_utils();
3376
3477
  jsonContainsHandler = {
3377
3478
  operators: ["jsonContains", "jsonContainedBy"],
3378
3479
  compile(decision, ctx, state) {
3480
+ assertDialectCapability(
3481
+ ctx.dialectCapabilities,
3482
+ "supportsJsonOperators",
3483
+ "JSON operators are"
3484
+ );
3379
3485
  const column = decision.column;
3380
3486
  if (!column) {
3381
3487
  throw new Error("JSON contains handler requires a column");
@@ -3396,6 +3502,11 @@ var init_json = __esm({
3396
3502
  jsonExistsHandler = {
3397
3503
  operators: ["jsonExists"],
3398
3504
  compile(decision, ctx, state) {
3505
+ assertDialectCapability(
3506
+ ctx.dialectCapabilities,
3507
+ "supportsJsonOperators",
3508
+ "JSON operators are"
3509
+ );
3399
3510
  const column = decision.column;
3400
3511
  if (!column) {
3401
3512
  throw new Error("JSON exists handler requires a column");
@@ -3415,6 +3526,11 @@ var init_json = __esm({
3415
3526
  jsonComparisonHandler = {
3416
3527
  operators: ["jsonComparison"],
3417
3528
  compile(decision, ctx, state) {
3529
+ assertDialectCapability(
3530
+ ctx.dialectCapabilities,
3531
+ "supportsJsonOperators",
3532
+ "JSON operators are"
3533
+ );
3418
3534
  const column = decision.column;
3419
3535
  if (!column) {
3420
3536
  throw new Error("JSON comparison handler requires a column");
@@ -3592,6 +3708,7 @@ var RANGE_OP_MAP, rangeHandler;
3592
3708
  var init_range = __esm({
3593
3709
  "src/handlers/where/range.ts"() {
3594
3710
  "use strict";
3711
+ init_dialect_capabilities();
3595
3712
  init_param_intent();
3596
3713
  init_param_ref();
3597
3714
  init_types();
@@ -3604,6 +3721,11 @@ var init_range = __esm({
3604
3721
  rangeHandler = {
3605
3722
  operators: ["contains", "containedBy", "overlaps"],
3606
3723
  compile(decision, ctx, state) {
3724
+ assertDialectCapability(
3725
+ ctx.dialectCapabilities,
3726
+ "supportsRangeTypes",
3727
+ "Range operators are"
3728
+ );
3607
3729
  const column = decision.column;
3608
3730
  if (!column) {
3609
3731
  throw new Error("Range handler requires a column");
@@ -3651,28 +3773,8 @@ var init_range = __esm({
3651
3773
  }
3652
3774
  });
3653
3775
 
3654
- // src/binding-registry.ts
3655
- function emittedBindName(name, naming) {
3656
- return naming.toDatabase(name);
3657
- }
3658
- function hasBindingName(bindingNames, name, naming) {
3659
- return bindingNames?.has(emittedBindName(name, naming)) ?? false;
3660
- }
3661
- function schemaForFromName(schemaName, fromName, bindingNames, naming) {
3662
- return hasBindingName(bindingNames, fromName, naming) ? void 0 : schemaName;
3663
- }
3664
- function withBindingName(bindingNames, name, naming) {
3665
- const next = new Set(bindingNames ?? []);
3666
- next.add(emittedBindName(name, naming));
3667
- return next;
3668
- }
3669
- var init_binding_registry = __esm({
3670
- "src/binding-registry.ts"() {
3671
- "use strict";
3672
- }
3673
- });
3674
-
3675
3776
  // src/compile-where.ts
3777
+ import { getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields3 } from "@dbsp/types/internal";
3676
3778
  function toHandlerContext(ctx) {
3677
3779
  return {
3678
3780
  naming: ctx.naming,
@@ -3680,12 +3782,15 @@ function toHandlerContext(ctx) {
3680
3782
  currentAlias: ctx.currentAlias ?? ctx.rootTable,
3681
3783
  maxRecursiveDepth: 100,
3682
3784
  ...ctx.schemaName !== void 0 && { schema: ctx.schemaName },
3785
+ ...ctx.dialectCapabilities !== void 0 && {
3786
+ dialectCapabilities: ctx.dialectCapabilities
3787
+ },
3683
3788
  ...ctx.bindingNames !== void 0 && { bindingNames: ctx.bindingNames },
3684
3789
  ...ctx.model !== void 0 && { model: ctx.model },
3685
3790
  ...ctx.outerTable !== void 0 && { outerAlias: ctx.outerTable }
3686
3791
  };
3687
3792
  }
3688
- function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists", bindingNames) {
3793
+ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists", bindingNames, dialectCapabilities) {
3689
3794
  assertNoUnsupportedSubqueryModifiers(intent, use);
3690
3795
  if (intent.where && containsOuterRef(intent.where)) {
3691
3796
  throw new Error(
@@ -3751,6 +3856,8 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
3751
3856
  aliases: /* @__PURE__ */ new Map(),
3752
3857
  paramState: innerState,
3753
3858
  naming,
3859
+ ...schemaName !== void 0 && { schemaName },
3860
+ ...dialectCapabilities !== void 0 && { dialectCapabilities },
3754
3861
  ...bindingNames !== void 0 && { bindingNames },
3755
3862
  compileSubquery: (_nestedIntent, _nestedOffset) => {
3756
3863
  throw new Error(
@@ -3861,11 +3968,16 @@ function handleSubqueryIntent(intent, ctx, _handlerCtx) {
3861
3968
  }
3862
3969
  function handleRelationFilterIntent(intent, ctx) {
3863
3970
  const rf = intent;
3864
- const hops = Array.isArray(rf.relation) ? rf.relation : [rf.relation];
3971
+ const preResolved = getTrustedNqlRelationFilterFields3(rf);
3972
+ const relationPath = preResolved?.relation ?? rf.relation;
3973
+ const hops = Array.isArray(relationPath) ? [...relationPath] : [relationPath];
3865
3974
  const existsKind = rf.mode === "none" || rf.mode === "every" ? "notExists" : "exists";
3866
3975
  const rfWhere = rf.where;
3867
3976
  const isVacuousEvery = rf.mode === "every" && (!rfWhere || typeof rfWhere === "object" && rfWhere.kind === "and" && Array.isArray(rfWhere.conditions) && rfWhere.conditions.length === 0);
3868
3977
  if (isVacuousEvery) {
3978
+ if (preResolved) {
3979
+ return { A_Const: { boolval: { boolval: true } } };
3980
+ }
3869
3981
  if (hops.length <= 1) {
3870
3982
  const relation = hops[0] ?? rf.relation;
3871
3983
  if (!ctx.model) {
@@ -3901,6 +4013,19 @@ function handleRelationFilterIntent(intent, ctx) {
3901
4013
  const innermostWhere = rf.mode === "every" ? { kind: "not", condition: rf.where } : rf.where;
3902
4014
  if (hops.length <= 1) {
3903
4015
  const relation = hops[0] ?? rf.relation;
4016
+ if (preResolved) {
4017
+ return compileWhereIntent(
4018
+ {
4019
+ kind: existsKind,
4020
+ relation,
4021
+ targetTable: preResolved.targetTable,
4022
+ sourceColumn: preResolved.sourceColumn,
4023
+ targetColumn: preResolved.targetColumn,
4024
+ where: innermostWhere
4025
+ },
4026
+ ctx
4027
+ );
4028
+ }
3904
4029
  const resolvedRelation = ctx.model?.getRelation(
3905
4030
  `${ctx.rootTable}.${relation}`
3906
4031
  );
@@ -4175,7 +4300,8 @@ var init_raw_exists = __esm({
4175
4300
  ctx.naming,
4176
4301
  ctx.schema,
4177
4302
  "rawExists",
4178
- ctx.bindingNames
4303
+ ctx.bindingNames,
4304
+ ctx.dialectCapabilities
4179
4305
  );
4180
4306
  if (innerParams) {
4181
4307
  for (const p of innerParams) {
@@ -5984,11 +6110,17 @@ var init_json2 = __esm({
5984
6110
  "src/handlers/expression/json.ts"() {
5985
6111
  "use strict";
5986
6112
  init_ast_helpers();
6113
+ init_dialect_capabilities();
5987
6114
  init_param_intent();
5988
6115
  init_utils();
5989
6116
  jsonExtractHandler = {
5990
6117
  types: ["jsonExtract"],
5991
6118
  compile(decision, ctx, state) {
6119
+ assertDialectCapability(
6120
+ ctx.dialectCapabilities,
6121
+ "supportsJsonOperators",
6122
+ "JSON operators are"
6123
+ );
5992
6124
  const column = decision.column;
5993
6125
  if (!column) {
5994
6126
  throw new Error("JSON extract handler requires a column");
@@ -6016,6 +6148,11 @@ var init_json2 = __esm({
6016
6148
  jsonPathExtractHandler = {
6017
6149
  types: ["jsonPathExtract"],
6018
6150
  compile(decision, ctx, state) {
6151
+ assertDialectCapability(
6152
+ ctx.dialectCapabilities,
6153
+ "supportsJsonOperators",
6154
+ "JSON operators are"
6155
+ );
6019
6156
  const column = decision.column;
6020
6157
  if (!column) {
6021
6158
  throw new Error("JSON path extract handler requires a column");
@@ -7087,13 +7224,18 @@ init_ast_helpers();
7087
7224
  // src/compiler.ts
7088
7225
  init_assert_field();
7089
7226
  init_ast_helpers();
7227
+ init_binding_registry();
7090
7228
  import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
7091
7229
  import {
7092
7230
  isParamIntent as isParamIntent6,
7093
7231
  NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
7094
7232
  NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
7095
7233
  } from "@dbsp/types";
7096
- import { getNqlBindingRefName, isNqlBindingRef } from "@dbsp/types/internal";
7234
+ import {
7235
+ getNqlBindingRefName,
7236
+ getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields4,
7237
+ isNqlBindingRef
7238
+ } from "@dbsp/types/internal";
7097
7239
 
7098
7240
  // src/pgsql-deparser.ts
7099
7241
  function deparse(node) {
@@ -8148,6 +8290,7 @@ function deparseQuoted(ast) {
8148
8290
  }
8149
8291
 
8150
8292
  // src/compiler.ts
8293
+ init_dialect_capabilities();
8151
8294
  init_case_value();
8152
8295
  init_custom();
8153
8296
  init_expression();
@@ -8194,6 +8337,12 @@ function assertNqlSelectWindowFunctionAllowed(functionName) {
8194
8337
  }
8195
8338
  }
8196
8339
  function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
8340
+ const derivedFkColumns = deriveFkColumns(
8341
+ pd,
8342
+ pd.sourceTable ?? rootTable,
8343
+ defaultPk,
8344
+ deriveFk
8345
+ );
8197
8346
  return {
8198
8347
  type: pd.type,
8199
8348
  table: pd.table,
@@ -8204,7 +8353,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
8204
8353
  paramIndex: pd.paramIndex,
8205
8354
  direction: pd.direction,
8206
8355
  joinType: pd.joinType,
8207
- ...deriveFkColumns(pd, pd.sourceTable ?? rootTable, defaultPk, deriveFk),
8356
+ sourceColumn: pd.sourceColumn ?? derivedFkColumns.sourceColumn,
8357
+ targetColumn: pd.targetColumn ?? derivedFkColumns.targetColumn,
8208
8358
  targetTable: pd.targetTable,
8209
8359
  function: pd.function,
8210
8360
  args: pd.args,
@@ -8380,6 +8530,7 @@ var PlanCompiler = class _PlanCompiler {
8380
8530
  defaultPk;
8381
8531
  deriveFk;
8382
8532
  model;
8533
+ dialectCapabilities;
8383
8534
  bindingNames;
8384
8535
  /** Mutable state shared with extracted condition/value compilation functions */
8385
8536
  state = {
@@ -8397,6 +8548,8 @@ var PlanCompiler = class _PlanCompiler {
8397
8548
  rawJoins = [];
8398
8549
  /** CTE nodes from include handlers (e.g., CTE strategy) */
8399
8550
  pendingCtes = [];
8551
+ /** Local aliases for binding relation-column scalar subqueries. */
8552
+ bindingRelationColumnSubqueryIndex = 0;
8400
8553
  /**
8401
8554
  * Maps relation-dotted include path → JOIN alias for multi-hop FK resolution.
8402
8555
  * Populated as join decisions are compiled so later hops can find the
@@ -8415,8 +8568,25 @@ var PlanCompiler = class _PlanCompiler {
8415
8568
  this.defaultPk = options.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
8416
8569
  this.deriveFk = options.deriveFkColumnName ?? defaultFkDerivation;
8417
8570
  this.model = options.model ?? void 0;
8571
+ this.dialectCapabilities = options.dialectCapabilities;
8418
8572
  this.bindingNames = options.bindingNames;
8419
8573
  }
8574
+ childCompilerOptions(overrides = {}) {
8575
+ return {
8576
+ naming: this.naming,
8577
+ ...this.schema !== void 0 && { schema: this.schema },
8578
+ defaultPkColumnName: this.defaultPk,
8579
+ deriveFkColumnName: this.deriveFk,
8580
+ ...this.model !== void 0 && { model: this.model },
8581
+ ...this.dialectCapabilities !== void 0 && {
8582
+ dialectCapabilities: this.dialectCapabilities
8583
+ },
8584
+ ...this.bindingNames !== void 0 && {
8585
+ bindingNames: this.bindingNames
8586
+ },
8587
+ ...overrides
8588
+ };
8589
+ }
8420
8590
  /** Build immutable context for handler-based WHERE compilation */
8421
8591
  handlerCtx() {
8422
8592
  return {
@@ -8427,6 +8597,9 @@ var PlanCompiler = class _PlanCompiler {
8427
8597
  defaultPkColumnName: this.defaultPk,
8428
8598
  deriveFkColumnName: this.deriveFk,
8429
8599
  ...this.schema != null && { schema: this.schema },
8600
+ ...this.dialectCapabilities != null && {
8601
+ dialectCapabilities: this.dialectCapabilities
8602
+ },
8430
8603
  ...this.bindingNames != null && { bindingNames: this.bindingNames },
8431
8604
  ...this.model != null && { model: this.model }
8432
8605
  };
@@ -8737,6 +8910,9 @@ var PlanCompiler = class _PlanCompiler {
8737
8910
  defaultPkColumnName: this.defaultPk,
8738
8911
  deriveFkColumnName: this.deriveFk,
8739
8912
  ...plan.schema ?? this.schema ? { schema: plan.schema ?? this.schema } : {},
8913
+ ...this.dialectCapabilities != null && {
8914
+ dialectCapabilities: this.dialectCapabilities
8915
+ },
8740
8916
  ...this.bindingNames != null && { bindingNames: this.bindingNames },
8741
8917
  ...this.model != null && { model: this.model },
8742
8918
  compileSubquery: (query, paramOffset) => this.compileExpressionSubquery(query, paramOffset),
@@ -8753,18 +8929,66 @@ var PlanCompiler = class _PlanCompiler {
8753
8929
  joins: []
8754
8930
  };
8755
8931
  }
8756
- compileExpressionSubquery(query, paramOffset) {
8757
- const innerCompiler = new _PlanCompiler({
8758
- naming: this.naming,
8759
- ...this.schema !== void 0 && {
8760
- schema: this.schema
8761
- },
8762
- defaultPkColumnName: this.defaultPk,
8763
- deriveFkColumnName: this.deriveFk,
8764
- ...this.bindingNames !== void 0 && {
8765
- bindingNames: this.bindingNames
8932
+ schemaForRangeVar(plan, table) {
8933
+ return schemaForFromName(
8934
+ plan.schema ?? this.schema,
8935
+ table,
8936
+ this.bindingNames,
8937
+ this.naming
8938
+ );
8939
+ }
8940
+ isNqlBindingRoot(plan) {
8941
+ return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
8942
+ }
8943
+ compileBindingRelationColumnSubquery(fields, plan) {
8944
+ if (fields.selectedColumn === void 0) {
8945
+ throw new Error(
8946
+ `NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
8947
+ );
8948
+ }
8949
+ if (fields.cardinality !== "one") {
8950
+ throw new Error(
8951
+ `NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
8952
+ );
8953
+ }
8954
+ const relatedAlias = `rc_${this.bindingRelationColumnSubqueryIndex++}`;
8955
+ const relatedTable = rangeVar(
8956
+ fields.targetTable,
8957
+ relatedAlias,
8958
+ this.schemaForRangeVar(plan, fields.targetTable),
8959
+ this.naming
8960
+ );
8961
+ const relatedColumn = columnRef(
8962
+ fields.selectedColumn,
8963
+ relatedAlias,
8964
+ void 0,
8965
+ this.naming
8966
+ );
8967
+ const relatedJoinColumn = columnRef(
8968
+ fields.targetColumn,
8969
+ relatedAlias,
8970
+ void 0,
8971
+ this.naming
8972
+ );
8973
+ const bindingJoinColumn = columnRef(
8974
+ fields.sourceColumn,
8975
+ plan.rootTable,
8976
+ void 0,
8977
+ this.naming
8978
+ );
8979
+ return {
8980
+ SubLink: {
8981
+ subLinkType: "EXPR_SUBLINK",
8982
+ subselect: selectStmt({
8983
+ targetList: [{ ResTarget: { val: relatedColumn } }],
8984
+ from: [relatedTable],
8985
+ where: eqExpr(relatedJoinColumn, bindingJoinColumn)
8986
+ })
8766
8987
  }
8767
- });
8988
+ };
8989
+ }
8990
+ compileExpressionSubquery(query, paramOffset) {
8991
+ const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
8768
8992
  const innerPlan = {
8769
8993
  rootTable: query.from,
8770
8994
  decisions: intentToDecisions(query, query.from)
@@ -9104,24 +9328,7 @@ var PlanCompiler = class _PlanCompiler {
9104
9328
  const ctx = {
9105
9329
  ...this.createHandlerContext(plan, plan.rootTable),
9106
9330
  compileSubquery(query, paramOffset) {
9107
- const innerCompiler = new _PlanCompiler({
9108
- naming: outerThis.naming,
9109
- ...outerThis.schema !== void 0 && {
9110
- schema: outerThis.schema
9111
- },
9112
- defaultPkColumnName: outerThis.defaultPk,
9113
- deriveFkColumnName: outerThis.deriveFk
9114
- });
9115
- const innerPlan = {
9116
- rootTable: query.from,
9117
- decisions: intentToDecisions(query, query.from)
9118
- };
9119
- const innerResult = innerCompiler.compile(innerPlan);
9120
- const renumbered = renumberParamRefsInAst(
9121
- innerResult.ast,
9122
- paramOffset
9123
- );
9124
- return { ast: renumbered, parameters: innerResult.parameters };
9331
+ return outerThis.compileExpressionSubquery(query, paramOffset);
9125
9332
  }
9126
9333
  };
9127
9334
  const state = this.createHandlerState();
@@ -9157,6 +9364,27 @@ var PlanCompiler = class _PlanCompiler {
9157
9364
  case "selectRelationColumn":
9158
9365
  case "selectPseudoColumn":
9159
9366
  case "selectArithmetic": {
9367
+ if (decision.type === "selectRelationColumn") {
9368
+ const trusted = getTrustedNqlRelationFilterFields4(decision);
9369
+ if (trusted?.selectedColumn !== void 0) {
9370
+ const node2 = this.compileBindingRelationColumnSubquery(
9371
+ trusted,
9372
+ plan
9373
+ );
9374
+ targetList.push({
9375
+ ResTarget: {
9376
+ val: node2,
9377
+ ...decision.alias ? { name: this.naming.toDatabase(decision.alias) } : {}
9378
+ }
9379
+ });
9380
+ break;
9381
+ }
9382
+ if (this.isNqlBindingRoot(plan)) {
9383
+ throw new Error(
9384
+ `NQL binding-final query '${plan.rootTable}' cannot select relation column '${decision.relation ?? "unknown"}.${decision.column ?? "unknown"}' without a trusted compiler proof.`
9385
+ );
9386
+ }
9387
+ }
9160
9388
  ensureExpressionHandlersRegistered();
9161
9389
  const exprType = decision.type === "selectRelationColumn" ? "relationColumn" : decision.type === "selectPseudoColumn" ? "pseudoColumn" : "arithmetic";
9162
9390
  const handler = getExpressionHandler(exprType);
@@ -9307,13 +9535,13 @@ var PlanCompiler = class _PlanCompiler {
9307
9535
  const targetRV = rangeVar(
9308
9536
  pj.table,
9309
9537
  pj.alias,
9310
- plan.schema ?? this.schema,
9538
+ this.schemaForRangeVar(plan, pj.table),
9311
9539
  this.naming
9312
9540
  );
9313
9541
  const base = from.length > 0 ? from[0] : rangeVar(
9314
9542
  plan.rootTable,
9315
9543
  void 0,
9316
- plan.schema ?? this.schema,
9544
+ this.schemaForRangeVar(plan, plan.rootTable),
9317
9545
  this.naming
9318
9546
  );
9319
9547
  from[0] = pj.type === "LEFT JOIN" ? leftJoin(base, targetRV, pj.on) : innerJoin(base, targetRV, pj.on);
@@ -9322,7 +9550,7 @@ var PlanCompiler = class _PlanCompiler {
9322
9550
  const base = from.length > 0 ? from[0] : rangeVar(
9323
9551
  plan.rootTable,
9324
9552
  void 0,
9325
- plan.schema ?? this.schema,
9553
+ this.schemaForRangeVar(plan, plan.rootTable),
9326
9554
  this.naming
9327
9555
  );
9328
9556
  const joinExpr2 = rawJoin;
@@ -9347,7 +9575,7 @@ var PlanCompiler = class _PlanCompiler {
9347
9575
  plan.batchValuesFromNode ? plan.batchValuesFromNode : rangeVar(
9348
9576
  plan.rootTable,
9349
9577
  void 0,
9350
- plan.schema ?? this.schema,
9578
+ this.schemaForRangeVar(plan, plan.rootTable),
9351
9579
  this.naming
9352
9580
  )
9353
9581
  ];
@@ -9494,6 +9722,18 @@ var PlanCompiler = class _PlanCompiler {
9494
9722
  options.withClause = { ctes: this.pendingCtes, recursive: false };
9495
9723
  }
9496
9724
  if (plan.lock) {
9725
+ assertDialectCapability(
9726
+ this.dialectCapabilities,
9727
+ "supportsRowLevelLocks",
9728
+ "Row-level locks are"
9729
+ );
9730
+ if (plan.lock.waitPolicy !== "block") {
9731
+ assertDialectCapability(
9732
+ this.dialectCapabilities,
9733
+ "supportsLockWaitPolicies",
9734
+ "Lock wait policies are"
9735
+ );
9736
+ }
9497
9737
  const mapped = mapLockToAst(plan.lock);
9498
9738
  const hasJoins = this.rawJoins.length > 0;
9499
9739
  options.lockingClause = {
@@ -9503,7 +9743,7 @@ var PlanCompiler = class _PlanCompiler {
9503
9743
  rangeVar(
9504
9744
  plan.rootTable,
9505
9745
  void 0,
9506
- plan.schema ?? this.schema,
9746
+ this.schemaForRangeVar(plan, plan.rootTable),
9507
9747
  this.naming
9508
9748
  )
9509
9749
  ]
@@ -9883,13 +10123,13 @@ var PlanCompiler = class _PlanCompiler {
9883
10123
  const baseTable = larg ?? rangeVar(
9884
10124
  plan.rootTable,
9885
10125
  void 0,
9886
- plan.schema ?? this.schema,
10126
+ this.schemaForRangeVar(plan, plan.rootTable),
9887
10127
  this.naming
9888
10128
  );
9889
10129
  const targetTable = rangeVar(
9890
10130
  decision.targetTable ?? "",
9891
10131
  decision.alias,
9892
- plan.schema ?? this.schema,
10132
+ this.schemaForRangeVar(plan, decision.targetTable ?? ""),
9893
10133
  this.naming
9894
10134
  );
9895
10135
  const onCondition = eqExpr(
@@ -13939,7 +14179,11 @@ init_naming_plugin();
13939
14179
  init_param_ref();
13940
14180
 
13941
14181
  // src/pgsql-adapter.ts
13942
- import { POSTGRESQL_CAPABILITIES as POSTGRESQL_CAPABILITIES2, plan as planFn2 } from "@dbsp/core";
14182
+ import {
14183
+ POSTGRESQL_CAPABILITIES as POSTGRESQL_CAPABILITIES2,
14184
+ plan as planFn2,
14185
+ validateTypeName as validateTypeName4
14186
+ } from "@dbsp/core";
13943
14187
  import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNqlBindingRef2 } from "@dbsp/types/internal";
13944
14188
 
13945
14189
  // src/adapter-compiler-includes.ts
@@ -14136,6 +14380,7 @@ import {
14136
14380
  // src/adapter-compiler-select.ts
14137
14381
  init_assert_field();
14138
14382
  init_ast_helpers();
14383
+ init_binding_registry();
14139
14384
  init_compile_where();
14140
14385
  import { countDistinctRelationPathsByName } from "@dbsp/core";
14141
14386
  init_compiler_utils();
@@ -15174,7 +15419,13 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
15174
15419
  naming,
15175
15420
  outerTable: alias,
15176
15421
  ...schemaName !== void 0 && { schemaName },
15422
+ ...deps.bindingNames !== void 0 && {
15423
+ bindingNames: deps.bindingNames
15424
+ },
15177
15425
  ...model !== void 0 && { model },
15426
+ ...deps.dialectCapabilities !== void 0 && {
15427
+ dialectCapabilities: deps.dialectCapabilities
15428
+ },
15178
15429
  compileSubquery: () => {
15179
15430
  throw new Error(
15180
15431
  "Subquery in BatchValues JOIN ON condition is not supported."
@@ -15214,7 +15465,13 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
15214
15465
  // joined alias (e.g. 'e2' in self-join ON conditions).
15215
15466
  outerTable: tableAlias,
15216
15467
  ...schemaName !== void 0 && { schemaName },
15468
+ ...deps.bindingNames !== void 0 && {
15469
+ bindingNames: deps.bindingNames
15470
+ },
15217
15471
  ...model !== void 0 && { model },
15472
+ ...deps.dialectCapabilities !== void 0 && {
15473
+ dialectCapabilities: deps.dialectCapabilities
15474
+ },
15218
15475
  compileSubquery: () => {
15219
15476
  throw new Error("Subquery in JOIN ON condition is not supported.");
15220
15477
  }
@@ -15223,7 +15480,7 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
15223
15480
  const joinedRangeVar = rangeVar(
15224
15481
  intent.table,
15225
15482
  tableAlias,
15226
- schemaName,
15483
+ schemaForFromName(schemaName, intent.table, deps.bindingNames, naming),
15227
15484
  naming
15228
15485
  );
15229
15486
  results.push({
@@ -15386,6 +15643,9 @@ function compileSelect(plan, options, deps) {
15386
15643
  ...deps.bindingNames !== void 0 && {
15387
15644
  bindingNames: deps.bindingNames
15388
15645
  },
15646
+ ...deps.dialectCapabilities !== void 0 && {
15647
+ dialectCapabilities: deps.dialectCapabilities
15648
+ },
15389
15649
  ...resolvedModelForCompiler != null && {
15390
15650
  model: resolvedModelForCompiler
15391
15651
  }
@@ -15545,7 +15805,7 @@ function compileSourceQueryCte(operation, sourceName, sourceQuery, options, deps
15545
15805
  );
15546
15806
  }
15547
15807
  const sourcePlan = planFn(sourceQuery, model, {
15548
- dialectCapabilities: POSTGRESQL_CAPABILITIES
15808
+ dialectCapabilities: deps.dialectCapabilities ?? POSTGRESQL_CAPABILITIES
15549
15809
  });
15550
15810
  return compileSelect(sourcePlan, options, deps);
15551
15811
  }
@@ -15560,20 +15820,31 @@ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
15560
15820
  parameters: [...sourceCte.parameters, ...parameters]
15561
15821
  };
15562
15822
  }
15823
+ function resolveMutationExistsForeignKey(foreignKey, relationName) {
15824
+ if (typeof foreignKey === "string") return foreignKey;
15825
+ if (!Array.isArray(foreignKey)) return void 0;
15826
+ if (foreignKey.length > 1) {
15827
+ throw new Error(
15828
+ `Mutation exists()/notExists() guards do not support composite foreignKey relations yet: relation '${relationName}' has foreignKey ${JSON.stringify(foreignKey)}. Composite FK correlation is tracked by #179.`
15829
+ );
15830
+ }
15831
+ return foreignKey[0];
15832
+ }
15563
15833
  function resolveExistsRelation(sourceTable, relation, model) {
15564
15834
  if (!model) return { targetTable: relation };
15565
- const rel = model.getRelation(`${sourceTable}.${relation}`);
15835
+ const relationName = `${sourceTable}.${relation}`;
15836
+ const rel = model.getRelation(relationName);
15566
15837
  if (!rel) return { targetTable: relation };
15567
15838
  const targetTable = rel.target;
15568
15839
  if (rel.type === "belongsTo") {
15569
- const fk2 = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
15840
+ const fk2 = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
15570
15841
  return {
15571
15842
  targetTable,
15572
15843
  ...fk2 !== void 0 && { sourceColumn: fk2 },
15573
15844
  targetColumn: "id"
15574
15845
  };
15575
15846
  }
15576
- const fk = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
15847
+ const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
15577
15848
  return {
15578
15849
  targetTable,
15579
15850
  sourceColumn: rel.sourceKey ?? "id",
@@ -15601,7 +15872,11 @@ function resolveExistsIntent(where, sourceTable, deps) {
15601
15872
  if (kind !== "exists" && kind !== "notExists") return where;
15602
15873
  const relation = w.relation;
15603
15874
  const resolved = resolveExistsRelation(sourceTable, relation, deps.model);
15604
- if (resolved.targetTable === relation && !resolved.sourceColumn) return where;
15875
+ const nestedWhere = w.where;
15876
+ const enrichedNestedWhere = nestedWhere !== void 0 ? resolveExistsIntent(nestedWhere, resolved.targetTable, deps) : void 0;
15877
+ if (resolved.targetTable === relation && !resolved.sourceColumn && !resolved.targetColumn && enrichedNestedWhere === nestedWhere) {
15878
+ return where;
15879
+ }
15605
15880
  return {
15606
15881
  ...w,
15607
15882
  targetTable: resolved.targetTable,
@@ -15610,7 +15885,8 @@ function resolveExistsIntent(where, sourceTable, deps) {
15610
15885
  },
15611
15886
  ...resolved.targetColumn !== void 0 && {
15612
15887
  targetColumn: resolved.targetColumn
15613
- }
15888
+ },
15889
+ ...enrichedNestedWhere !== void 0 && { where: enrichedNestedWhere }
15614
15890
  };
15615
15891
  }
15616
15892
  function getColumnTypes(tableName, columns, deps) {
@@ -15636,13 +15912,17 @@ function compileUpsertActionWhere(where, table, state, deps, schemaName) {
15636
15912
  ...schemaName !== void 0 && { schemaName },
15637
15913
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15638
15914
  ...deps.model !== void 0 && { model: deps.model },
15915
+ ...deps.dialectCapabilities !== void 0 && {
15916
+ dialectCapabilities: deps.dialectCapabilities
15917
+ },
15639
15918
  compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
15640
15919
  sqIntent,
15641
15920
  paramOffset,
15642
15921
  deps.naming,
15643
15922
  schemaName,
15644
15923
  "rawExists",
15645
- deps.bindingNames
15924
+ deps.bindingNames,
15925
+ deps.dialectCapabilities
15646
15926
  )
15647
15927
  };
15648
15928
  return compileWhereIntent(where, whereCtx);
@@ -15653,6 +15933,9 @@ function compileInsert2(intent, options, deps) {
15653
15933
  naming: deps.naming,
15654
15934
  rootTable: intent.table,
15655
15935
  ...schemaName !== void 0 && { schema: schemaName },
15936
+ ...deps.dialectCapabilities !== void 0 && {
15937
+ dialectCapabilities: deps.dialectCapabilities
15938
+ },
15656
15939
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15657
15940
  maxRecursiveDepth: 100
15658
15941
  };
@@ -15699,15 +15982,19 @@ function compileInsertFrom2(intent, options, deps) {
15699
15982
  naming: deps.naming,
15700
15983
  rootTable: intent.source,
15701
15984
  ...schemaName !== void 0 && { schema: schemaName },
15985
+ ...deps.dialectCapabilities !== void 0 && {
15986
+ dialectCapabilities: deps.dialectCapabilities
15987
+ },
15702
15988
  ...bindingNames !== void 0 && { bindingNames },
15703
15989
  maxRecursiveDepth: 100
15704
15990
  };
15705
15991
  const state = createCompilerState();
15992
+ const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.source, deps) : void 0;
15706
15993
  const config = {
15707
15994
  targetTable: intent.table,
15708
15995
  sourceTable: intent.source,
15709
15996
  ...intent.columns && { columns: [...intent.columns] },
15710
- ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
15997
+ ...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
15711
15998
  ...intent.limit !== void 0 && { limit: intent.limit },
15712
15999
  ...intent.returning && { returning: [...intent.returning] }
15713
16000
  };
@@ -15730,6 +16017,9 @@ function compileUpdate2(intent, options, deps) {
15730
16017
  ...schemaName !== void 0 && { schema: schemaName },
15731
16018
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15732
16019
  ...resolvedModel !== void 0 && { model: resolvedModel },
16020
+ ...deps.dialectCapabilities !== void 0 && {
16021
+ dialectCapabilities: deps.dialectCapabilities
16022
+ },
15733
16023
  maxRecursiveDepth: 100
15734
16024
  };
15735
16025
  const state = createCompilerState();
@@ -15759,6 +16049,9 @@ function compileBatchUpdate(intent, _options, deps) {
15759
16049
  naming: deps.naming,
15760
16050
  rootTable: intent.table,
15761
16051
  ...schemaName !== void 0 && { schema: schemaName },
16052
+ ...deps.dialectCapabilities !== void 0 && {
16053
+ dialectCapabilities: deps.dialectCapabilities
16054
+ },
15762
16055
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15763
16056
  maxRecursiveDepth: 100
15764
16057
  };
@@ -15789,6 +16082,7 @@ function compileBatchUpdate(intent, _options, deps) {
15789
16082
  })) : void 0;
15790
16083
  let whereGuard;
15791
16084
  if (intent.where) {
16085
+ const resolvedWhere = resolveExistsIntent(intent.where, intent.table, deps);
15792
16086
  const whereCtx = {
15793
16087
  rootTable: intent.table,
15794
16088
  aliases: /* @__PURE__ */ new Map(),
@@ -15799,16 +16093,20 @@ function compileBatchUpdate(intent, _options, deps) {
15799
16093
  bindingNames: deps.bindingNames
15800
16094
  },
15801
16095
  ...deps.model !== void 0 && { model: deps.model },
16096
+ ...deps.dialectCapabilities !== void 0 && {
16097
+ dialectCapabilities: deps.dialectCapabilities
16098
+ },
15802
16099
  compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
15803
16100
  sqIntent,
15804
16101
  paramOffset,
15805
16102
  deps.naming,
15806
16103
  schemaName,
15807
16104
  "rawExists",
15808
- deps.bindingNames
16105
+ deps.bindingNames,
16106
+ deps.dialectCapabilities
15809
16107
  )
15810
16108
  };
15811
- whereGuard = compileWhereIntent(intent.where, whereCtx);
16109
+ whereGuard = compileWhereIntent(resolvedWhere, whereCtx);
15812
16110
  }
15813
16111
  const config = {
15814
16112
  table: intent.table,
@@ -15835,6 +16133,9 @@ function compileDelete2(intent, options, deps) {
15835
16133
  rootTable: intent.table,
15836
16134
  ...schemaName !== void 0 && { schema: schemaName },
15837
16135
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
16136
+ ...deps.dialectCapabilities !== void 0 && {
16137
+ dialectCapabilities: deps.dialectCapabilities
16138
+ },
15838
16139
  maxRecursiveDepth: 100,
15839
16140
  ...resolvedModel !== void 0 && { model: resolvedModel }
15840
16141
  };
@@ -15860,6 +16161,9 @@ function compileUpsert2(intent, options, deps) {
15860
16161
  ...schemaName !== void 0 && { schema: schemaName },
15861
16162
  ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15862
16163
  ...deps.model !== void 0 && { model: deps.model },
16164
+ ...deps.dialectCapabilities !== void 0 && {
16165
+ dialectCapabilities: deps.dialectCapabilities
16166
+ },
15863
16167
  maxRecursiveDepth: 100
15864
16168
  };
15865
16169
  const state = createCompilerState();
@@ -15953,10 +16257,14 @@ function compileUpsertFrom2(intent, options, deps) {
15953
16257
  naming: deps.naming,
15954
16258
  rootTable: intent.source,
15955
16259
  ...schemaName !== void 0 && { schema: schemaName },
16260
+ ...deps.dialectCapabilities !== void 0 && {
16261
+ dialectCapabilities: deps.dialectCapabilities
16262
+ },
15956
16263
  ...bindingNames !== void 0 && { bindingNames },
15957
16264
  maxRecursiveDepth: 100
15958
16265
  };
15959
16266
  const state = createCompilerState();
16267
+ const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.source, deps) : void 0;
15960
16268
  let columns;
15961
16269
  if (intent.columns) {
15962
16270
  columns = [...intent.columns];
@@ -15971,7 +16279,7 @@ function compileUpsertFrom2(intent, options, deps) {
15971
16279
  sourceTable: intent.source,
15972
16280
  conflictColumns: [...intent.conflictColumns],
15973
16281
  ...columns && { columns },
15974
- ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
16282
+ ...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
15975
16283
  ...intent.limit !== void 0 && { limit: intent.limit },
15976
16284
  ...intent.returning && { returning: [...intent.returning] }
15977
16285
  };
@@ -17363,6 +17671,7 @@ function mapFetchDirection(direction) {
17363
17671
 
17364
17672
  // src/pgsql-adapter.ts
17365
17673
  init_validate();
17674
+ var MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS = 32e3;
17366
17675
  function renumberSqlParams2(sql, offset) {
17367
17676
  if (offset === 0) return sql;
17368
17677
  return sql.replace(/\$(\d+)/g, (_match, num) => {
@@ -17370,7 +17679,7 @@ function renumberSqlParams2(sql, offset) {
17370
17679
  });
17371
17680
  }
17372
17681
  function isCompiledNqlQuery(input) {
17373
- return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input);
17682
+ return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input || "runtimeBindings" in input);
17374
17683
  }
17375
17684
  function formatGuardPathSegment(key) {
17376
17685
  return /^[A-Za-z_$][\w$]*$/.test(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
@@ -17445,6 +17754,175 @@ function findDuplicateEmittedNqlBindingName(bindingNames, naming) {
17445
17754
  }
17446
17755
  return void 0;
17447
17756
  }
17757
+ function orderedNqlBindingNames(bundle) {
17758
+ const names = [];
17759
+ const seen = /* @__PURE__ */ new Set();
17760
+ for (const name of bundle.bindings?.keys() ?? []) {
17761
+ names.push(name);
17762
+ seen.add(name);
17763
+ }
17764
+ for (const name of bundle.runtimeBindings?.keys() ?? []) {
17765
+ if (!seen.has(name)) {
17766
+ names.push(name);
17767
+ seen.add(name);
17768
+ }
17769
+ }
17770
+ return names;
17771
+ }
17772
+ function mapRuntimeBindingColumnType(type) {
17773
+ switch (type) {
17774
+ case "string":
17775
+ return "text";
17776
+ case "text":
17777
+ return "text";
17778
+ case "number":
17779
+ case "integer":
17780
+ return "integer";
17781
+ case "bigint":
17782
+ return "bigint";
17783
+ case "decimal":
17784
+ return "numeric";
17785
+ case "boolean":
17786
+ return "boolean";
17787
+ case "date":
17788
+ return "date";
17789
+ case "time":
17790
+ return "time";
17791
+ case "datetime":
17792
+ case "timestamp":
17793
+ return "timestamptz";
17794
+ case "json":
17795
+ case "jsonb":
17796
+ return "jsonb";
17797
+ case "uuid":
17798
+ return "uuid";
17799
+ case "daterange":
17800
+ return "daterange";
17801
+ case "tsrange":
17802
+ return "tsrange";
17803
+ case "tstzrange":
17804
+ return "tstzrange";
17805
+ case "int4range":
17806
+ return "int4range";
17807
+ case "int8range":
17808
+ return "int8range";
17809
+ case "numrange":
17810
+ return "numrange";
17811
+ default:
17812
+ return void 0;
17813
+ }
17814
+ }
17815
+ function findRuntimeBindingSourceTable(model, sourceTable) {
17816
+ return model.getTable(sourceTable) ?? [...model.tables.values()].find((table) => table.name === sourceTable);
17817
+ }
17818
+ function resolveRuntimeBindingColumnType(bindingName, sourceTable, columnName) {
17819
+ const column = sourceTable.columns.find(
17820
+ (candidate) => candidate.name === columnName
17821
+ );
17822
+ if (column === void 0) {
17823
+ throw new Error(
17824
+ `NQL runtime binding '${bindingName}' cannot resolve projected column '${columnName}' on source mutation table '${sourceTable.name}'.`
17825
+ );
17826
+ }
17827
+ const dbType = column.originalDbType?.trim() || mapRuntimeBindingColumnType(column.type);
17828
+ if (dbType === void 0 || dbType.trim() === "") {
17829
+ throw new Error(
17830
+ `NQL runtime binding '${bindingName}' cannot resolve a PostgreSQL type for projected column '${columnName}' on source mutation table '${sourceTable.name}'.`
17831
+ );
17832
+ }
17833
+ const typeName = dbType.trim();
17834
+ try {
17835
+ validateTypeName4(typeName);
17836
+ } catch (error) {
17837
+ const reason = error instanceof Error ? error.message : String(error);
17838
+ throw new Error(
17839
+ `NQL runtime binding '${bindingName}' cannot use PostgreSQL cast type for projected column '${columnName}': ${reason}`
17840
+ );
17841
+ }
17842
+ return typeName;
17843
+ }
17844
+ function resolveRuntimeBindingColumnTypes(name, binding, model, sourceTableName) {
17845
+ if (model === void 0) {
17846
+ throw new Error(
17847
+ `NQL runtime binding '${name}' cannot materialize non-empty rows because no model is available for source-table column type resolution.`
17848
+ );
17849
+ }
17850
+ const sourceTable = findRuntimeBindingSourceTable(model, sourceTableName);
17851
+ if (sourceTable === void 0) {
17852
+ throw new Error(
17853
+ `NQL runtime binding '${name}' cannot resolve source mutation table '${sourceTableName}' in the model.`
17854
+ );
17855
+ }
17856
+ return binding.columns.map(
17857
+ (column) => resolveRuntimeBindingColumnType(name, sourceTable, column)
17858
+ );
17859
+ }
17860
+ function assertRuntimeBindingValuesParameterCount(name, binding, parameterOffset) {
17861
+ const valueParameterCount = binding.rows.length * binding.columns.length;
17862
+ if (valueParameterCount <= MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS && parameterOffset + valueParameterCount <= MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS) {
17863
+ return;
17864
+ }
17865
+ const totalParameterCount = parameterOffset + valueParameterCount;
17866
+ throw new Error(
17867
+ `NQL runtime binding '${name}' would materialize ${valueParameterCount} VALUES parameters; limit is ${MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS}. Current parameter offset is ${parameterOffset}, which would make ${totalParameterCount} total parameters before compiling the final statement.`
17868
+ );
17869
+ }
17870
+ function compileNqlRuntimeBindingCte(name, binding, naming, parameterOffset, sourceTable, schemaName, model) {
17871
+ if (binding.columns.length === 0) {
17872
+ throw new Error(
17873
+ `NQL runtime binding '${name}' cannot be materialized without projected columns.`
17874
+ );
17875
+ }
17876
+ const cteName = quoteIdent2(emittedBindName(name, naming), "alias");
17877
+ const columnSql = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
17878
+ if (sourceTable === void 0) {
17879
+ throw new Error(
17880
+ `NQL runtime binding '${name}' cannot materialize a typed relation because its source mutation table is unavailable.`
17881
+ );
17882
+ }
17883
+ const projectedColumns = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
17884
+ const sourceAnchorSql = `SELECT ${projectedColumns} FROM ${qualifyTableIdent(sourceTable, schemaName, naming)} WHERE false`;
17885
+ if (binding.rows.length === 0) {
17886
+ return {
17887
+ cte: `${cteName} (${columnSql}) as (${sourceAnchorSql})`,
17888
+ parameters: []
17889
+ };
17890
+ }
17891
+ assertRuntimeBindingValuesParameterCount(name, binding, parameterOffset);
17892
+ const columnTypes = resolveRuntimeBindingColumnTypes(
17893
+ name,
17894
+ binding,
17895
+ model,
17896
+ sourceTable
17897
+ );
17898
+ const parameters = [];
17899
+ let nextParam = parameterOffset + 1;
17900
+ const valuesSql = binding.rows.map((row) => {
17901
+ const placeholders = binding.columns.map((column, columnIndex) => {
17902
+ parameters.push(row[column]);
17903
+ return `$${nextParam++}::${columnTypes[columnIndex]}`;
17904
+ });
17905
+ return `(${placeholders.join(", ")})`;
17906
+ }).join(", ");
17907
+ return {
17908
+ cte: `${cteName} (${columnSql}) as (${sourceAnchorSql} UNION ALL VALUES ${valuesSql})`,
17909
+ parameters
17910
+ };
17911
+ }
17912
+ function createNqlBindingSelectPlan(query) {
17913
+ return {
17914
+ rootTable: query.from,
17915
+ decisions: [],
17916
+ warnings: [],
17917
+ ctes: [],
17918
+ intent: query,
17919
+ metadata: {
17920
+ planningTimeMs: 0,
17921
+ relationsAnalyzed: 0,
17922
+ isAmbiguous: false
17923
+ }
17924
+ };
17925
+ }
17448
17926
  var PgsqlAdapter = class _PgsqlAdapter {
17449
17927
  pool;
17450
17928
  client;
@@ -17521,6 +17999,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
17521
17999
  // `||` (not `??`): empty string is treated as "no override" and falls back to this.schemaName (which may be a configured schema or undefined)
17522
18000
  schemaName: options?.schemaName || this.schemaName,
17523
18001
  model: options?.model ?? this.model,
18002
+ dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities,
17524
18003
  defaultPk: this.defaultPk,
17525
18004
  deriveFk: this.deriveFk,
17526
18005
  ...bindingNames !== void 0 && { bindingNames }
@@ -17601,22 +18080,17 @@ var PgsqlAdapter = class _PgsqlAdapter {
17601
18080
  }
17602
18081
  compileNqlBundleLeaf(bundle, options, bindingNames) {
17603
18082
  if (bundle.query !== void 0) {
17604
- const naming = this.buildCompileDeps(options, bindingNames).naming;
17605
- if (hasBindingName(bindingNames, bundle.query.from, naming)) {
17606
- throw new Error(
17607
- `NQL binding '${bundle.query.from}' cannot be used as the final FROM source yet. Use the binding from a mutation source or WHERE IN predicate, or select from a real table.`
17608
- );
17609
- }
17610
- const model = this.requireNqlCompileModel(options);
17611
- const planReport = planFn2(bundle.query, model, {
17612
- dialectCapabilities: this.dialectCapabilities
18083
+ const deps = this.buildCompileDeps(options, bindingNames);
18084
+ const queryFromBinding = hasBindingName(
18085
+ bindingNames,
18086
+ bundle.query.from,
18087
+ deps.naming
18088
+ );
18089
+ const planReport = queryFromBinding ? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
18090
+ dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
17613
18091
  });
17614
18092
  return guardCompiledQuery(
17615
- compileSelect(
17616
- planReport,
17617
- options,
17618
- this.buildCompileDeps(options, bindingNames)
17619
- ),
18093
+ compileSelect(planReport, options, deps),
17620
18094
  "NQL query"
17621
18095
  );
17622
18096
  }
@@ -17657,20 +18131,41 @@ var PgsqlAdapter = class _PgsqlAdapter {
17657
18131
  compileNqlBundle(bundle, options) {
17658
18132
  const ctes = [];
17659
18133
  const parameters = [];
17660
- const naming = this.buildCompileDeps(options).naming;
17661
- const duplicateEmittedBinding = bundle.bindings !== void 0 ? findDuplicateEmittedNqlBindingName(bundle.bindings.keys(), naming) : void 0;
18134
+ const deps = this.buildCompileDeps(options);
18135
+ const { naming } = deps;
18136
+ const bindingNamesInOrder = orderedNqlBindingNames(bundle);
18137
+ const duplicateEmittedBinding = bindingNamesInOrder.length > 0 ? findDuplicateEmittedNqlBindingName(bindingNamesInOrder, naming) : void 0;
17662
18138
  if (duplicateEmittedBinding !== void 0) {
17663
18139
  throw new Error(
17664
18140
  `NQL bindings '${duplicateEmittedBinding.originalName}' and '${duplicateEmittedBinding.duplicateName}' emit to duplicate CTE name '${duplicateEmittedBinding.emittedName}'. NQL binding names must be unique after database naming.`
17665
18141
  );
17666
18142
  }
17667
- const bindingNames = bundle.bindings !== void 0 ? new Set(
17668
- [...bundle.bindings.keys()].map(
17669
- (name) => emittedBindName(name, naming)
17670
- )
18143
+ const bindingNames = bindingNamesInOrder.length > 0 ? new Set(
18144
+ bindingNamesInOrder.map((name) => emittedBindName(name, naming))
17671
18145
  ) : void 0;
17672
18146
  this.assertNqlBindingNamesDisjointFromTables(bindingNames, options);
17673
- for (const [name, queryIntent] of bundle.bindings ?? []) {
18147
+ for (const name of bindingNamesInOrder) {
18148
+ const runtimeBinding = bundle.runtimeBindings?.get(name);
18149
+ if (runtimeBinding !== void 0) {
18150
+ const compiledRuntimeBinding = compileNqlRuntimeBindingCte(
18151
+ name,
18152
+ runtimeBinding,
18153
+ naming,
18154
+ parameters.length,
18155
+ bundle.mutationBindings?.get(name)?.table,
18156
+ deps.schemaName,
18157
+ deps.model
18158
+ );
18159
+ ctes.push(compiledRuntimeBinding.cte);
18160
+ parameters.push(...compiledRuntimeBinding.parameters);
18161
+ continue;
18162
+ }
18163
+ const queryIntent = bundle.bindings?.get(name);
18164
+ if (queryIntent === void 0) {
18165
+ throw new Error(
18166
+ `NQL binding '${name}' has no query intent or runtime rows to materialize.`
18167
+ );
18168
+ }
17674
18169
  const cteName = quoteIdent2(emittedBindName(name, naming), "alias");
17675
18170
  const bindingBundle = bundle.mutationBindings?.has(name) ? { mutation: bundle.mutationBindings.get(name) } : { query: queryIntent };
17676
18171
  const compiled2 = this.compileNqlBundleLeaf(
@@ -17745,6 +18240,11 @@ var PgsqlAdapter = class _PgsqlAdapter {
17745
18240
  // =========================================================================
17746
18241
  /**
17747
18242
  * Compile a plan to executable SQL.
18243
+ *
18244
+ * When passed a direct `CompiledNqlQuery`, the adapter trusts that the bundle
18245
+ * has already been semantically validated by the NQL compiler. This method
18246
+ * still performs adapter-owned SQL safety checks for emitted binding names
18247
+ * before CTE emission.
17748
18248
  */
17749
18249
  compile(plan, options) {
17750
18250
  if (isCompiledNqlQuery(plan)) {
@@ -17796,10 +18296,12 @@ var PgsqlAdapter = class _PgsqlAdapter {
17796
18296
  compileSelectExpression(expr) {
17797
18297
  const naming = this.naming;
17798
18298
  const schemaName = this.schemaName;
18299
+ const dialectCapabilities = this.dialectCapabilities;
17799
18300
  const state = createCompilerState();
17800
18301
  const ctx = {
17801
18302
  naming,
17802
18303
  ...schemaName !== void 0 && { schema: schemaName },
18304
+ dialectCapabilities,
17803
18305
  rootTable: "",
17804
18306
  maxRecursiveDepth: 100,
17805
18307
  // Wire compileSubquery so that SubqueryExpressionIntent nested inside
@@ -17809,7 +18311,8 @@ var PgsqlAdapter = class _PgsqlAdapter {
17809
18311
  compileSubquery(query, paramOffset) {
17810
18312
  const innerCompiler = new PlanCompiler({
17811
18313
  naming,
17812
- ...schemaName !== void 0 && { schema: schemaName }
18314
+ ...schemaName !== void 0 && { schema: schemaName },
18315
+ dialectCapabilities
17813
18316
  });
17814
18317
  const innerPlan = {
17815
18318
  rootTable: query.from,
@@ -17943,19 +18446,22 @@ var PgsqlAdapter = class _PgsqlAdapter {
17943
18446
  );
17944
18447
  }
17945
18448
  compileSetOperationWithBindings(intent, model, options, bindingNames) {
17946
- const compileFn = createLeafCompileFn(
17947
- {
17948
- dialectCapabilities: this.dialectCapabilities,
17949
- compile: (planReport, leafOptions) => compileSelect(
17950
- planReport,
17951
- leafOptions,
17952
- this.buildCompileDeps(leafOptions, bindingNames)
17953
- )
17954
- },
17955
- model,
17956
- planFn2,
17957
- options
17958
- );
18449
+ const compileFn = (query) => {
18450
+ const leafOptions = {
18451
+ ...options,
18452
+ model
18453
+ };
18454
+ const deps = this.buildCompileDeps(leafOptions, bindingNames);
18455
+ const queryFromBinding = hasBindingName(
18456
+ bindingNames,
18457
+ query.from,
18458
+ deps.naming
18459
+ );
18460
+ const planReport = queryFromBinding ? createNqlBindingSelectPlan(query) : planFn2(query, model, {
18461
+ dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
18462
+ });
18463
+ return compileSelect(planReport, leafOptions, deps);
18464
+ };
17959
18465
  const result = compileSetOperation(intent, compileFn);
17960
18466
  return guardCompiledQuery(
17961
18467
  {