@dbsp/adapter-pgsql 1.2.0 → 1.3.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
@@ -1355,7 +1355,9 @@ function compileExpressionIntent(intent, ctx, state) {
1355
1355
  }
1356
1356
  case "relationColumn": {
1357
1357
  const rc = intent;
1358
- 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;
1359
1361
  return columnRef(rc.column, alias, void 0, ctx.naming);
1360
1362
  }
1361
1363
  case "case": {
@@ -2814,8 +2816,9 @@ var init_param_intent = __esm({
2814
2816
  import { isFieldRef, isParamIntent as isParamIntent3 } from "@dbsp/types";
2815
2817
  function buildColumnRef(column, ctx) {
2816
2818
  if (column.includes(".")) {
2817
- const dotIndex = column.indexOf(".");
2818
- 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;
2819
2822
  const col = column.substring(dotIndex + 1);
2820
2823
  return columnRef(col, table, void 0, ctx.naming);
2821
2824
  }
@@ -3648,6 +3651,27 @@ var init_range = __esm({
3648
3651
  }
3649
3652
  });
3650
3653
 
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
+
3651
3675
  // src/compile-where.ts
3652
3676
  function toHandlerContext(ctx) {
3653
3677
  return {
@@ -3656,11 +3680,12 @@ function toHandlerContext(ctx) {
3656
3680
  currentAlias: ctx.currentAlias ?? ctx.rootTable,
3657
3681
  maxRecursiveDepth: 100,
3658
3682
  ...ctx.schemaName !== void 0 && { schema: ctx.schemaName },
3683
+ ...ctx.bindingNames !== void 0 && { bindingNames: ctx.bindingNames },
3659
3684
  ...ctx.model !== void 0 && { model: ctx.model },
3660
3685
  ...ctx.outerTable !== void 0 && { outerAlias: ctx.outerTable }
3661
3686
  };
3662
3687
  }
3663
- function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists") {
3688
+ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists", bindingNames) {
3664
3689
  assertNoUnsupportedSubqueryModifiers(intent, use);
3665
3690
  if (intent.where && containsOuterRef(intent.where)) {
3666
3691
  throw new Error(
@@ -3705,9 +3730,14 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
3705
3730
  }
3706
3731
  const stmt = {
3707
3732
  targetList,
3708
- // Bug 3 fix: propagate schema name from outer context so schema-scoped
3709
- // queries generate "schema"."table" AS alias instead of bare "table" AS alias.
3710
- fromClause: [rangeVar(targetTable, innerAlias, schemaName, naming)]
3733
+ fromClause: [
3734
+ rangeVar(
3735
+ targetTable,
3736
+ innerAlias,
3737
+ schemaForFromName(schemaName, targetTable, bindingNames, naming),
3738
+ naming
3739
+ )
3740
+ ]
3711
3741
  };
3712
3742
  let paramCount = 0;
3713
3743
  let innerParameters = [];
@@ -3721,6 +3751,7 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
3721
3751
  aliases: /* @__PURE__ */ new Map(),
3722
3752
  paramState: innerState,
3723
3753
  naming,
3754
+ ...bindingNames !== void 0 && { bindingNames },
3724
3755
  compileSubquery: (_nestedIntent, _nestedOffset) => {
3725
3756
  throw new Error(
3726
3757
  "buildSubqueryFromIntent: nested subquery not supported"
@@ -4098,6 +4129,7 @@ var init_compile_where = __esm({
4098
4129
  init_custom();
4099
4130
  init_handlers();
4100
4131
  init_assert_field();
4132
+ init_binding_registry();
4101
4133
  init_types();
4102
4134
  init_utils();
4103
4135
  init_intent_to_decisions();
@@ -4142,7 +4174,8 @@ var init_raw_exists = __esm({
4142
4174
  state.paramIndex,
4143
4175
  ctx.naming,
4144
4176
  ctx.schema,
4145
- "rawExists"
4177
+ "rawExists",
4178
+ ctx.bindingNames
4146
4179
  );
4147
4180
  if (innerParams) {
4148
4181
  for (const p of innerParams) {
@@ -4378,7 +4411,19 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
4378
4411
  }
4379
4412
  const stmt = {
4380
4413
  targetList: [{ ResTarget: { val: targetVal } }],
4381
- fromClause: [rangeVar(targetTable, targetAlias, ctx.schema, ctx.naming)],
4414
+ fromClause: [
4415
+ rangeVar(
4416
+ targetTable,
4417
+ targetAlias,
4418
+ schemaForFromName(
4419
+ ctx.schema,
4420
+ targetTable,
4421
+ ctx.bindingNames,
4422
+ ctx.naming
4423
+ ),
4424
+ ctx.naming
4425
+ )
4426
+ ],
4382
4427
  ...whereClause && { whereClause }
4383
4428
  };
4384
4429
  if (decision.orderBy && decision.orderBy.length > 0) {
@@ -4415,6 +4460,7 @@ var init_subquery_emission = __esm({
4415
4460
  "src/subquery-emission.ts"() {
4416
4461
  "use strict";
4417
4462
  init_ast_helpers();
4463
+ init_binding_registry();
4418
4464
  init_intent_to_decisions();
4419
4465
  init_param_intent();
4420
4466
  }
@@ -6685,7 +6731,7 @@ var init_relation = __esm({
6685
6731
  if (relation.includes(".")) {
6686
6732
  const segments = relation.split(".");
6687
6733
  const leaf = segments[segments.length - 1];
6688
- alias = state.aliases.get(leaf) ?? state.aliases.get(relation) ?? leaf;
6734
+ alias = state.aliases.get(relation) ?? state.aliases.get(leaf) ?? leaf;
6689
6735
  } else {
6690
6736
  alias = state.aliases.get(relation) ?? relation;
6691
6737
  }
@@ -7047,6 +7093,7 @@ import {
7047
7093
  NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
7048
7094
  NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
7049
7095
  } from "@dbsp/types";
7096
+ import { getNqlBindingRefName, isNqlBindingRef } from "@dbsp/types/internal";
7050
7097
 
7051
7098
  // src/pgsql-deparser.ts
7052
7099
  function deparse(node) {
@@ -8333,6 +8380,7 @@ var PlanCompiler = class _PlanCompiler {
8333
8380
  defaultPk;
8334
8381
  deriveFk;
8335
8382
  model;
8383
+ bindingNames;
8336
8384
  /** Mutable state shared with extracted condition/value compilation functions */
8337
8385
  state = {
8338
8386
  parameters: [],
@@ -8357,6 +8405,7 @@ var PlanCompiler = class _PlanCompiler {
8357
8405
  joinAliasMap = /* @__PURE__ */ new Map();
8358
8406
  /**
8359
8407
  * Tracks all join aliases in use for the current query.
8408
+ * Entries are stored in emitted database-alias space, after naming.toDatabase().
8360
8409
  * Ensures no two JOINs share the same alias (DOUBLE-ALIAS prevention).
8361
8410
  */
8362
8411
  usedJoinAliases = /* @__PURE__ */ new Set();
@@ -8366,16 +8415,19 @@ var PlanCompiler = class _PlanCompiler {
8366
8415
  this.defaultPk = options.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
8367
8416
  this.deriveFk = options.deriveFkColumnName ?? defaultFkDerivation;
8368
8417
  this.model = options.model ?? void 0;
8418
+ this.bindingNames = options.bindingNames;
8369
8419
  }
8370
8420
  /** Build immutable context for handler-based WHERE compilation */
8371
8421
  handlerCtx() {
8372
8422
  return {
8373
8423
  naming: this.naming,
8374
8424
  rootTable: this.currentRootTable,
8425
+ aliases: this.resolvedJoinAliases(),
8375
8426
  maxRecursiveDepth: 100,
8376
8427
  defaultPkColumnName: this.defaultPk,
8377
8428
  deriveFkColumnName: this.deriveFk,
8378
8429
  ...this.schema != null && { schema: this.schema },
8430
+ ...this.bindingNames != null && { bindingNames: this.bindingNames },
8379
8431
  ...this.model != null && { model: this.model }
8380
8432
  };
8381
8433
  }
@@ -8386,6 +8438,24 @@ var PlanCompiler = class _PlanCompiler {
8386
8438
  }
8387
8439
  return alias;
8388
8440
  }
8441
+ emittedJoinAlias(alias) {
8442
+ const dbAlias = this.naming.toDatabase(alias);
8443
+ validateIdentifier(dbAlias, "alias");
8444
+ return dbAlias;
8445
+ }
8446
+ resolvedJoinAliases() {
8447
+ const aliases = /* @__PURE__ */ new Map();
8448
+ for (const [relationPath, entry] of this.joinAliasMap) {
8449
+ const isLegacyPath = relationPath.startsWith("__legacy__:");
8450
+ if (!isLegacyPath) {
8451
+ aliases.set(relationPath, entry.alias);
8452
+ }
8453
+ if (entry.relationName && (isLegacyPath || relationPath === entry.relationName)) {
8454
+ aliases.set(entry.relationName, entry.alias);
8455
+ }
8456
+ }
8457
+ return aliases;
8458
+ }
8389
8459
  /**
8390
8460
  * Dispatch a PlanDecision through the unified WHERE handler system.
8391
8461
  * Bridges PlanCompiler's state to handler types, calls dispatcher, syncs back.
@@ -8567,12 +8637,13 @@ var PlanCompiler = class _PlanCompiler {
8567
8637
  const candidateAlias = handlerDecision.relation ?? handlerDecision.targetTable ?? handlerDecision.relationName;
8568
8638
  if (candidateAlias) {
8569
8639
  let alias = candidateAlias;
8640
+ let emittedAlias = this.emittedJoinAlias(alias);
8570
8641
  let counter = 1;
8571
- while (this.usedJoinAliases.has(alias)) {
8642
+ while (this.usedJoinAliases.has(emittedAlias)) {
8572
8643
  alias = `${candidateAlias}_${counter++}`;
8644
+ emittedAlias = this.emittedJoinAlias(alias);
8573
8645
  }
8574
- validateIdentifier(alias, "alias");
8575
- this.usedJoinAliases.add(alias);
8646
+ this.usedJoinAliases.add(emittedAlias);
8576
8647
  finalJoinAlias = alias;
8577
8648
  if (alias !== candidateAlias) {
8578
8649
  handlerDecision.relation = alias;
@@ -8585,7 +8656,10 @@ var PlanCompiler = class _PlanCompiler {
8585
8656
  this.joinAliasMap.set(relationIdentityPath, {
8586
8657
  alias: finalJoinAlias,
8587
8658
  joinType: requestedJoinType,
8588
- ...decision.targetTable && { targetTable: decision.targetTable }
8659
+ ...decision.targetTable && { targetTable: decision.targetTable },
8660
+ ...(decision.relationName ?? decision.relation) && {
8661
+ relationName: decision.relationName ?? decision.relation
8662
+ }
8589
8663
  });
8590
8664
  }
8591
8665
  const out = {};
@@ -8658,10 +8732,12 @@ var PlanCompiler = class _PlanCompiler {
8658
8732
  naming: this.naming,
8659
8733
  rootTable: plan.rootTable,
8660
8734
  currentAlias: currentAlias ?? plan.rootTable,
8735
+ aliases: this.resolvedJoinAliases(),
8661
8736
  maxRecursiveDepth: 100,
8662
8737
  defaultPkColumnName: this.defaultPk,
8663
8738
  deriveFkColumnName: this.deriveFk,
8664
8739
  ...plan.schema ?? this.schema ? { schema: plan.schema ?? this.schema } : {},
8740
+ ...this.bindingNames != null && { bindingNames: this.bindingNames },
8665
8741
  ...this.model != null && { model: this.model },
8666
8742
  compileSubquery: (query, paramOffset) => this.compileExpressionSubquery(query, paramOffset),
8667
8743
  compileNqlSelectExpression: (value, handlerCtx, state) => this.compileNqlFunctionArg(value, handlerCtx, state)
@@ -8673,7 +8749,7 @@ var PlanCompiler = class _PlanCompiler {
8673
8749
  parameters: this.state.parameters,
8674
8750
  paramIndex: this.state.paramIndex,
8675
8751
  ctes: /* @__PURE__ */ new Map(),
8676
- aliases: /* @__PURE__ */ new Map(),
8752
+ aliases: this.resolvedJoinAliases(),
8677
8753
  joins: []
8678
8754
  };
8679
8755
  }
@@ -8684,7 +8760,10 @@ var PlanCompiler = class _PlanCompiler {
8684
8760
  schema: this.schema
8685
8761
  },
8686
8762
  defaultPkColumnName: this.defaultPk,
8687
- deriveFkColumnName: this.deriveFk
8763
+ deriveFkColumnName: this.deriveFk,
8764
+ ...this.bindingNames !== void 0 && {
8765
+ bindingNames: this.bindingNames
8766
+ }
8688
8767
  });
8689
8768
  const innerPlan = {
8690
8769
  rootTable: query.from,
@@ -8703,14 +8782,14 @@ var PlanCompiler = class _PlanCompiler {
8703
8782
  return funcCall(functionName, argNodes);
8704
8783
  }
8705
8784
  compileNqlFunctionArg(arg, ctx, state) {
8785
+ if (isNqlBindingRef(arg)) {
8786
+ return buildColumnRef(getNqlBindingRefName(arg), ctx);
8787
+ }
8706
8788
  if (typeof arg === "string") {
8707
8789
  return buildColumnRef(arg, ctx);
8708
8790
  }
8709
8791
  if (typeof arg === "object" && arg !== null) {
8710
8792
  const record = arg;
8711
- if (typeof record.$ref === "string") {
8712
- return buildColumnRef(record.$ref, ctx);
8713
- }
8714
8793
  if (typeof record.kind !== "string") {
8715
8794
  const legacyKind = typeof record.$op === "string" ? `$op:${record.$op}` : typeof record.$fn === "string" ? `$fn:${record.$fn}` : "object";
8716
8795
  throw new UnhandledNqlSelectExpressionKindError(legacyKind);
@@ -9131,11 +9210,7 @@ var PlanCompiler = class _PlanCompiler {
9131
9210
  * Compile an includeStrategy decision and register its results.
9132
9211
  * Pushes targets onto targetList, raw joins / CTEs onto instance collections.
9133
9212
  */
9134
- compileIncludeDecision(decision, plan, targetList) {
9135
- const includeResult = this.compileIncludeViaHandler(decision, plan);
9136
- if (includeResult.targets) {
9137
- targetList.push(...includeResult.targets);
9138
- }
9213
+ registerIncludeCompilationResult(includeResult) {
9139
9214
  if (includeResult.rawJoin) {
9140
9215
  this.rawJoins.push(includeResult.rawJoin);
9141
9216
  }
@@ -9146,6 +9221,27 @@ var PlanCompiler = class _PlanCompiler {
9146
9221
  this.pendingCtes.push(includeResult.cte);
9147
9222
  }
9148
9223
  }
9224
+ compileIncludeDecision(decision, plan, targetList, precompiled) {
9225
+ const includeResult = precompiled ?? this.compileIncludeViaHandler(decision, plan);
9226
+ if (includeResult.targets) {
9227
+ targetList.push(...includeResult.targets);
9228
+ }
9229
+ if (!precompiled) {
9230
+ this.registerIncludeCompilationResult(includeResult);
9231
+ }
9232
+ }
9233
+ compileJoinIncludeAllocationPass(decisions, plan) {
9234
+ const includeResults = /* @__PURE__ */ new Map();
9235
+ for (const decision of decisions) {
9236
+ if (decision.type !== "includeStrategy" || decision.choice !== "join") {
9237
+ continue;
9238
+ }
9239
+ const includeResult = this.compileIncludeViaHandler(decision, plan);
9240
+ includeResults.set(decision, includeResult);
9241
+ this.registerIncludeCompilationResult(includeResult);
9242
+ }
9243
+ return includeResults;
9244
+ }
9149
9245
  /**
9150
9246
  * Fold a WHERE-family decision into an existing where expression.
9151
9247
  * Returns the updated (or new) where node.
@@ -9279,6 +9375,14 @@ var PlanCompiler = class _PlanCompiler {
9279
9375
  }
9280
9376
  return where;
9281
9377
  }
9378
+ reserveManualJoinAliases(decisions) {
9379
+ for (const decision of decisions) {
9380
+ if (decision.type !== "join") continue;
9381
+ const alias = decision.alias ?? decision.targetTable;
9382
+ if (!alias) continue;
9383
+ this.usedJoinAliases.add(this.emittedJoinAlias(alias));
9384
+ }
9385
+ }
9282
9386
  /**
9283
9387
  * Apply a single join decision to the FROM clause in-place.
9284
9388
  * Chains multiple joins by wrapping from[0] as the left-arg each time.
@@ -9337,22 +9441,35 @@ var PlanCompiler = class _PlanCompiler {
9337
9441
  }
9338
9442
  return void 0;
9339
9443
  }
9444
+ /**
9445
+ * Compile a column reference that may use dotted 'relation.column' notation.
9446
+ */
9447
+ compileRelationAwareColumnRef(column, table) {
9448
+ const dot = column.lastIndexOf(".");
9449
+ if (dot !== -1) {
9450
+ const relation = column.slice(0, dot);
9451
+ const alias = this.resolvedJoinAliases().get(relation) ?? relation;
9452
+ return columnRef(column.slice(dot + 1), alias, void 0, this.naming);
9453
+ }
9454
+ return columnRef(column, table, void 0, this.naming);
9455
+ }
9340
9456
  /**
9341
9457
  * Compile a single groupBy decision into a ColumnRef AST node.
9342
9458
  * Supports dotted 'relation.column' notation for joined tables.
9343
9459
  */
9344
9460
  compileGroupByDecision(decision) {
9345
- const gbCol = decision.column;
9346
- const gbDot = gbCol.indexOf(".");
9347
- if (gbDot !== -1) {
9348
- return columnRef(
9349
- gbCol.slice(gbDot + 1),
9350
- gbCol.slice(0, gbDot),
9351
- void 0,
9352
- this.naming
9353
- );
9354
- }
9355
- return columnRef(gbCol, decision.table, void 0, this.naming);
9461
+ return this.compileRelationAwareColumnRef(
9462
+ decision.column,
9463
+ decision.table
9464
+ );
9465
+ }
9466
+ /**
9467
+ * Compile a DISTINCT ON column into a ColumnRef AST node.
9468
+ * Mirrors GROUP BY relation-alias resolution while preserving unqualified
9469
+ * DISTINCT ON columns as unqualified references.
9470
+ */
9471
+ compileDistinctOnColumn(column) {
9472
+ return this.compileRelationAwareColumnRef(column, void 0);
9356
9473
  }
9357
9474
  /**
9358
9475
  * Assemble the final SelectStmt from all accumulated clause nodes.
@@ -9400,10 +9517,17 @@ var PlanCompiler = class _PlanCompiler {
9400
9517
  plan.decisions,
9401
9518
  plan.rootTable
9402
9519
  );
9520
+ this.reserveManualJoinAliases(decisions);
9521
+ const includeJoinResults = this.compileJoinIncludeAllocationPass(
9522
+ decisions,
9523
+ plan
9524
+ );
9525
+ this.state.aliases = this.resolvedJoinAliases();
9403
9526
  const targetList = [];
9404
9527
  const from = this.compileFromClause(plan);
9405
9528
  let where;
9406
9529
  const orderBy = [];
9530
+ const orderByDecisions = [];
9407
9531
  const groupBy = [];
9408
9532
  let having;
9409
9533
  let limit;
@@ -9423,7 +9547,12 @@ var PlanCompiler = class _PlanCompiler {
9423
9547
  this.compileSelectTarget(decision, plan, targetList);
9424
9548
  break;
9425
9549
  case "includeStrategy":
9426
- this.compileIncludeDecision(decision, plan, targetList);
9550
+ this.compileIncludeDecision(
9551
+ decision,
9552
+ plan,
9553
+ targetList,
9554
+ includeJoinResults.get(decision)
9555
+ );
9427
9556
  where = this.compileIncludeWhereConditions(decision, where);
9428
9557
  break;
9429
9558
  case "where":
@@ -9436,8 +9565,7 @@ var PlanCompiler = class _PlanCompiler {
9436
9565
  this.compileJoinDecision(decision, plan, from);
9437
9566
  break;
9438
9567
  case "orderBy": {
9439
- const obNode = this.compileOrderByDecision(decision, plan);
9440
- if (obNode) orderBy.push(obNode);
9568
+ orderByDecisions.push(decision);
9441
9569
  break;
9442
9570
  }
9443
9571
  case "groupBy":
@@ -9478,12 +9606,16 @@ var PlanCompiler = class _PlanCompiler {
9478
9606
  case "distinctOn":
9479
9607
  if (decision.columns && decision.columns.length > 0) {
9480
9608
  distinct = decision.columns.map(
9481
- (col) => columnRef(col, void 0, void 0, this.naming)
9609
+ (col) => this.compileDistinctOnColumn(col)
9482
9610
  );
9483
9611
  }
9484
9612
  break;
9485
9613
  }
9486
9614
  }
9615
+ for (const decision of orderByDecisions) {
9616
+ const obNode = this.compileOrderByDecision(decision, plan);
9617
+ if (obNode) orderBy.push(obNode);
9618
+ }
9487
9619
  this.flushPendingJoins(from, plan);
9488
9620
  return this.buildSelectStmt(
9489
9621
  targetList,
@@ -13074,6 +13206,7 @@ function matchGlob(pattern, value) {
13074
13206
 
13075
13207
  // src/mutations/mutation-compiler.ts
13076
13208
  init_ast_helpers();
13209
+ init_binding_registry();
13077
13210
  init_compiler_utils();
13078
13211
  init_handlers();
13079
13212
  init_param_intent();
@@ -13299,12 +13432,18 @@ function compileInsertFrom(config, ctx, state) {
13299
13432
  const _dbTargetTable = naming.toDatabase(config.targetTable);
13300
13433
  const dbSourceTable = naming.toDatabase(config.sourceTable);
13301
13434
  const sourceAlias = config.sourceTable;
13435
+ const sourceSchema = schemaForFromName(
13436
+ ctx.schema,
13437
+ config.sourceTable,
13438
+ ctx.bindingNames,
13439
+ naming
13440
+ );
13302
13441
  const dbColumns = config.columns?.map((c) => naming.toDatabase(c));
13303
13442
  let targetList;
13304
13443
  if (config.columns && config.columns.length > 0) {
13305
13444
  targetList = config.columns.map(
13306
13445
  (col) => resTarget(
13307
- columnRef(col, sourceAlias, ctx.schema, naming),
13446
+ columnRef(col, sourceAlias, sourceSchema, naming),
13308
13447
  naming.toDatabase(col)
13309
13448
  )
13310
13449
  );
@@ -13344,8 +13483,8 @@ function compileInsertFrom(config, ctx, state) {
13344
13483
  inh: true,
13345
13484
  relpersistence: "p"
13346
13485
  };
13347
- if (ctx.schema) {
13348
- sourceRelation.schemaname = naming.toDatabase(ctx.schema);
13486
+ if (sourceSchema) {
13487
+ sourceRelation.schemaname = naming.toDatabase(sourceSchema);
13349
13488
  }
13350
13489
  const selectQuery = {
13351
13490
  SelectStmt: {
@@ -13374,12 +13513,18 @@ function compileUpsertFrom(config, ctx, state) {
13374
13513
  const naming = ctx.naming;
13375
13514
  const dbSourceTable = naming.toDatabase(config.sourceTable);
13376
13515
  const sourceAlias = config.sourceTable;
13516
+ const sourceSchema = schemaForFromName(
13517
+ ctx.schema,
13518
+ config.sourceTable,
13519
+ ctx.bindingNames,
13520
+ naming
13521
+ );
13377
13522
  const dbColumns = config.columns?.map((c) => naming.toDatabase(c));
13378
13523
  let targetList;
13379
13524
  if (config.columns && config.columns.length > 0) {
13380
13525
  targetList = config.columns.map(
13381
13526
  (col) => resTarget(
13382
- columnRef(col, sourceAlias, ctx.schema, naming),
13527
+ columnRef(col, sourceAlias, sourceSchema, naming),
13383
13528
  naming.toDatabase(col)
13384
13529
  )
13385
13530
  );
@@ -13419,8 +13564,8 @@ function compileUpsertFrom(config, ctx, state) {
13419
13564
  inh: true,
13420
13565
  relpersistence: "p"
13421
13566
  };
13422
- if (ctx.schema) {
13423
- sourceRelation.schemaname = naming.toDatabase(ctx.schema);
13567
+ if (sourceSchema) {
13568
+ sourceRelation.schemaname = naming.toDatabase(sourceSchema);
13424
13569
  }
13425
13570
  const selectQuery = {
13426
13571
  SelectStmt: {
@@ -13565,6 +13710,28 @@ init_compiler_utils();
13565
13710
  init_handlers();
13566
13711
  init_param_intent();
13567
13712
  init_param_ref();
13713
+ function buildWhereClause(conditions, ctx, state) {
13714
+ if (!conditions || conditions.length === 0) return void 0;
13715
+ const dispatch = createWhereDispatcher();
13716
+ if (conditions.length === 1) {
13717
+ return dispatch(conditions[0], ctx, state);
13718
+ }
13719
+ const nodes = conditions.map((condition) => dispatch(condition, ctx, state));
13720
+ return {
13721
+ BoolExpr: { boolop: "AND_EXPR", args: nodes }
13722
+ };
13723
+ }
13724
+ function buildActionWhereClause(config, ctx, state) {
13725
+ if (config.actionWhereIntent) {
13726
+ if (!config.compileActionWhere) {
13727
+ throw new Error(
13728
+ "Upsert actionWhereIntent requires compileActionWhere callback"
13729
+ );
13730
+ }
13731
+ return config.compileActionWhere(config.actionWhereIntent, state);
13732
+ }
13733
+ return buildWhereClause(config.actionWhere, ctx, state);
13734
+ }
13568
13735
  function buildOnConflictClause(config, ctx, state) {
13569
13736
  const naming = ctx.naming;
13570
13737
  const action = config.conflictAction;
@@ -13578,18 +13745,12 @@ function buildOnConflictClause(config, ctx, state) {
13578
13745
  }))
13579
13746
  };
13580
13747
  if (config.conflictTarget.where && config.conflictTarget.where.length > 0) {
13581
- const dispatch = createWhereDispatcher();
13582
- const conditions = config.conflictTarget.where;
13583
- let whereClause;
13584
- if (conditions.length === 1) {
13585
- whereClause = dispatch(conditions[0], ctx, state);
13586
- } else {
13587
- const nodes = conditions.map((c) => dispatch(c, ctx, state));
13588
- whereClause = {
13589
- BoolExpr: { boolop: "AND_EXPR", args: nodes }
13590
- };
13591
- }
13592
- infer.whereClause = whereClause;
13748
+ const whereClause2 = buildWhereClause(
13749
+ config.conflictTarget.where,
13750
+ ctx,
13751
+ state
13752
+ );
13753
+ if (whereClause2) infer.whereClause = whereClause2;
13593
13754
  }
13594
13755
  } else if (config.conflictTarget.constraint) {
13595
13756
  infer = {
@@ -13635,10 +13796,12 @@ function buildOnConflictClause(config, ctx, state) {
13635
13796
  }
13636
13797
  };
13637
13798
  });
13799
+ const whereClause = buildActionWhereClause(config, ctx, state);
13638
13800
  return {
13639
13801
  action: "ONCONFLICT_UPDATE",
13640
13802
  ...infer && { infer },
13641
- targetList
13803
+ targetList,
13804
+ ...whereClause && { whereClause }
13642
13805
  };
13643
13806
  }
13644
13807
  function compileUpsert(config, ctx, state) {
@@ -13776,7 +13939,8 @@ init_naming_plugin();
13776
13939
  init_param_ref();
13777
13940
 
13778
13941
  // src/pgsql-adapter.ts
13779
- import { POSTGRESQL_CAPABILITIES, plan as planFn } from "@dbsp/core";
13942
+ import { POSTGRESQL_CAPABILITIES as POSTGRESQL_CAPABILITIES2, plan as planFn2 } from "@dbsp/core";
13943
+ import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNqlBindingRef2 } from "@dbsp/types/internal";
13780
13944
 
13781
13945
  // src/adapter-compiler-includes.ts
13782
13946
  init_ast_helpers();
@@ -13962,449 +14126,97 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
13962
14126
  }
13963
14127
 
13964
14128
  // src/adapter-compiler-mutations.ts
14129
+ import {
14130
+ InvalidOperationError as InvalidOperationError3,
14131
+ isSqlRaw as isSqlRaw2,
14132
+ POSTGRESQL_CAPABILITIES,
14133
+ plan as planFn
14134
+ } from "@dbsp/core";
14135
+
14136
+ // src/adapter-compiler-select.ts
14137
+ init_assert_field();
14138
+ init_ast_helpers();
13965
14139
  init_compile_where();
14140
+ import { countDistinctRelationPathsByName } from "@dbsp/core";
13966
14141
  init_compiler_utils();
13967
- import { InvalidOperationError as InvalidOperationError3, isSqlRaw as isSqlRaw2 } from "@dbsp/core";
13968
- init_handlers();
13969
- function whereIntentAsDecision(where) {
13970
- return where;
13971
- }
13972
- function resolveExistsRelation(sourceTable, relation, model) {
13973
- if (!model) return { targetTable: relation };
13974
- const rel = model.getRelation(`${sourceTable}.${relation}`);
13975
- if (!rel) return { targetTable: relation };
13976
- const targetTable = rel.target;
13977
- if (rel.type === "belongsTo") {
13978
- const fk = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
13979
- return {
13980
- targetTable,
13981
- ...fk !== void 0 && { sourceColumn: fk },
13982
- targetColumn: "id"
13983
- };
13984
- }
13985
- return { targetTable };
13986
- }
13987
- function resolveExistsIntent(where, sourceTable, deps) {
14142
+ init_types();
14143
+ init_intent_to_decisions();
14144
+ init_param_ref();
14145
+
14146
+ // src/plan-decision-extractor.ts
14147
+ init_assert_field();
14148
+ init_intent_to_decisions();
14149
+ import { deriveRelationPathFromIntentPath } from "@dbsp/core";
14150
+ function findExistsIntents(where) {
14151
+ if (!where || typeof where !== "object") return [];
13988
14152
  const w = where;
13989
- const kind = w.kind;
13990
- if (kind === "and" || kind === "or") {
13991
- const conditions = w.conditions;
13992
- if (!conditions) return where;
13993
- const enriched = conditions.map(
13994
- (c) => resolveExistsIntent(c, sourceTable, deps)
13995
- );
13996
- const changed = enriched.some((c, i) => c !== conditions[i]);
13997
- return changed ? { ...w, conditions: enriched } : where;
13998
- }
13999
- if (kind === "not") {
14000
- const condition = w.condition;
14001
- if (!condition) return where;
14002
- const enriched = resolveExistsIntent(condition, sourceTable, deps);
14003
- return enriched !== condition ? { ...w, condition: enriched } : where;
14153
+ if (w.kind === "exists" || w.kind === "notExists" || w.kind === "relationFilter") {
14154
+ return [w];
14004
14155
  }
14005
- if (kind !== "exists" && kind !== "notExists") return where;
14006
- const relation = w.relation;
14007
- const resolved = resolveExistsRelation(sourceTable, relation, deps.model);
14008
- if (resolved.targetTable === relation && !resolved.sourceColumn) return where;
14009
- return {
14010
- ...w,
14011
- targetTable: resolved.targetTable,
14012
- ...resolved.sourceColumn !== void 0 && {
14013
- sourceColumn: resolved.sourceColumn
14014
- },
14015
- ...resolved.targetColumn !== void 0 && {
14016
- targetColumn: resolved.targetColumn
14156
+ const results = [];
14157
+ if (w.conditions && Array.isArray(w.conditions)) {
14158
+ for (const c of w.conditions) {
14159
+ results.push(...findExistsIntents(c));
14017
14160
  }
14018
- };
14161
+ }
14162
+ if (w.condition) {
14163
+ results.push(...findExistsIntents(w.condition));
14164
+ }
14165
+ return results;
14019
14166
  }
14020
- function getColumnTypes(tableName, columns, deps) {
14021
- if (!deps.model) return void 0;
14022
- const table = deps.model.getTable(tableName);
14023
- if (!table) return void 0;
14024
- let result;
14025
- for (const col of columns) {
14026
- const columnIR = table.columns.find((c) => c.name === col);
14027
- if (columnIR) {
14028
- result ??= {};
14029
- result[col] = columnIR.originalDbType ?? columnIR.type;
14167
+ function resolveRelation(model, sourceTable, relationName) {
14168
+ const rel = model.getRelation(`${sourceTable}.${relationName}`);
14169
+ if (!rel) return void 0;
14170
+ const foreignKey = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
14171
+ const relationType = rel.type;
14172
+ return { target: rel.target, foreignKey, relationType };
14173
+ }
14174
+ function resolveIncludeAlias(context) {
14175
+ return context.relation ?? context.includeAlias;
14176
+ }
14177
+ function resolveIncludeByPath(includes, intentPath, relationName) {
14178
+ if (!includes) return void 0;
14179
+ if (intentPath) {
14180
+ const indexPattern = /include\[(\d+)\]/g;
14181
+ let current = includes;
14182
+ let resolved;
14183
+ let execResult = indexPattern.exec(intentPath);
14184
+ while (execResult !== null) {
14185
+ const idx = parseInt(execResult[1], 10);
14186
+ const item = current[idx];
14187
+ if (!item) break;
14188
+ resolved = item;
14189
+ current = item.include ?? [];
14190
+ execResult = indexPattern.exec(intentPath);
14030
14191
  }
14192
+ if (resolved) return resolved;
14031
14193
  }
14032
- return result;
14194
+ return includes.find(
14195
+ (i) => i.relation === relationName || i.via === relationName
14196
+ );
14033
14197
  }
14034
- function compileInsert2(intent, options, deps) {
14035
- const schemaName = deps.schemaName;
14036
- const ctx = {
14037
- naming: deps.naming,
14038
- rootTable: intent.table,
14039
- ...schemaName !== void 0 && { schema: schemaName },
14040
- maxRecursiveDepth: 100
14041
- };
14042
- const state = createCompilerState();
14043
- const firstRow = intent.values?.[0] ?? {};
14044
- const columns = Object.keys(firstRow);
14045
- const rows = intent.values ?? [];
14046
- const values = rows.map((row) => columns.map((col) => row[col]));
14047
- const columnTypes = getColumnTypes(intent.table, columns, deps);
14048
- const config = {
14049
- table: intent.table,
14050
- columns,
14051
- values,
14052
- ...intent.returning && { returning: [...intent.returning] },
14053
- ...columnTypes && { columnTypes }
14054
- };
14055
- const maxBatchSize = options?.maxBatchSize;
14056
- if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
14057
- throw new InvalidOperationError3(
14058
- "insert",
14059
- `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
14060
- );
14198
+ function deriveForeignKey(context, deriveFk = defaultFkDerivation, defaultPk = DEFAULT_PK_COLUMN) {
14199
+ const fk = context.foreignKey ?? context.sourceFK;
14200
+ if (fk) return fk;
14201
+ if (!context.relationType) return void 0;
14202
+ if (context.relationType === "belongsTo") {
14203
+ return context.target ? deriveFk(context.target, defaultPk) : void 0;
14061
14204
  }
14062
- const batchThreshold = options?.batchThreshold ?? 50;
14063
- const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
14064
- const ast = useUnnest ? compileUnnestInsert(config, ctx, state) : compileInsert(config, ctx, state);
14065
- const sql = deparseQuoted(ast);
14066
- return {
14067
- sql,
14068
- parameters: state.parameters
14205
+ return context.sourceTable ? deriveFk(context.sourceTable, defaultPk) : void 0;
14206
+ }
14207
+ function mapComparisonOperator(op3) {
14208
+ const map = {
14209
+ eq: "=",
14210
+ neq: "!=",
14211
+ gt: ">",
14212
+ gte: ">=",
14213
+ lt: "<",
14214
+ lte: "<=",
14215
+ like: "LIKE",
14216
+ ilike: "ILIKE",
14217
+ isDistinctFrom: "IS DISTINCT FROM"
14069
14218
  };
14070
- }
14071
- function compileInsertFrom2(intent, _options, deps) {
14072
- const schemaName = deps.schemaName;
14073
- const ctx = {
14074
- naming: deps.naming,
14075
- rootTable: intent.source,
14076
- ...schemaName !== void 0 && { schema: schemaName },
14077
- maxRecursiveDepth: 100
14078
- };
14079
- const state = createCompilerState();
14080
- const config = {
14081
- targetTable: intent.table,
14082
- sourceTable: intent.source,
14083
- ...intent.columns && { columns: [...intent.columns] },
14084
- ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
14085
- ...intent.limit !== void 0 && { limit: intent.limit },
14086
- ...intent.returning && { returning: [...intent.returning] }
14087
- };
14088
- const ast = compileInsertFrom(config, ctx, state);
14089
- const sql = deparseQuoted(ast);
14090
- return {
14091
- sql,
14092
- parameters: state.parameters
14093
- };
14094
- }
14095
- function compileUpdate2(intent, _options, deps) {
14096
- const schemaName = deps.schemaName;
14097
- const ctx = {
14098
- naming: deps.naming,
14099
- rootTable: intent.table,
14100
- ...schemaName !== void 0 && { schema: schemaName },
14101
- maxRecursiveDepth: 100
14102
- };
14103
- const state = createCompilerState();
14104
- const setColumns = Object.keys(intent.set ?? {});
14105
- const columnTypes = getColumnTypes(intent.table, setColumns, deps);
14106
- const config = {
14107
- table: intent.table,
14108
- set: Object.entries(intent.set ?? {}).map(([column, value]) => ({
14109
- column,
14110
- value
14111
- })),
14112
- ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
14113
- ...intent.returning && { returning: [...intent.returning] },
14114
- ...columnTypes && { columnTypes }
14115
- };
14116
- const ast = compileUpdate(config, ctx, state);
14117
- const sql = deparseQuoted(ast);
14118
- return {
14119
- sql,
14120
- parameters: state.parameters
14121
- };
14122
- }
14123
- function compileBatchUpdate(intent, _options, deps) {
14124
- const schemaName = deps.schemaName;
14125
- const ctx = {
14126
- naming: deps.naming,
14127
- rootTable: intent.table,
14128
- ...schemaName !== void 0 && { schema: schemaName },
14129
- maxRecursiveDepth: 100
14130
- };
14131
- const state = createCompilerState();
14132
- if (intent.updates.length === 0) {
14133
- throw new InvalidOperationError3(
14134
- "update",
14135
- "batchSet requires at least one row"
14136
- );
14137
- }
14138
- const allColumns = Object.keys(intent.updates[0]);
14139
- const matchColumns = [...intent.matchColumns];
14140
- for (const mc of matchColumns) {
14141
- if (!allColumns.includes(mc)) {
14142
- throw new InvalidOperationError3(
14143
- "update",
14144
- `Match column "${mc}" not found in update data. Each row must include the match column(s).`
14145
- );
14146
- }
14147
- }
14148
- const values = intent.updates.map((row) => allColumns.map((col) => row[col]));
14149
- validateBatchCardinality(allColumns, values);
14150
- const columnArrays = transposeToColumnArrays(allColumns, values);
14151
- const columnTypes = getColumnTypes(intent.table, allColumns, deps);
14152
- const scalarSet = intent.scalarSet ? Object.entries(intent.scalarSet).map(([column, value]) => ({
14153
- column,
14154
- value
14155
- })) : void 0;
14156
- let whereGuard;
14157
- if (intent.where) {
14158
- const whereCtx = {
14159
- rootTable: intent.table,
14160
- aliases: /* @__PURE__ */ new Map(),
14161
- paramState: state,
14162
- naming: deps.naming,
14163
- ...schemaName !== void 0 && { schemaName },
14164
- ...deps.model !== void 0 && { model: deps.model },
14165
- compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
14166
- sqIntent,
14167
- paramOffset,
14168
- deps.naming,
14169
- schemaName,
14170
- "rawExists"
14171
- )
14172
- };
14173
- whereGuard = compileWhereIntent(intent.where, whereCtx);
14174
- }
14175
- const config = {
14176
- table: intent.table,
14177
- matchColumns,
14178
- allColumns,
14179
- columnArrays,
14180
- ...scalarSet && { scalarSet },
14181
- ...intent.returning && { returning: [...intent.returning] },
14182
- ...columnTypes && { columnTypes },
14183
- ...whereGuard !== void 0 && { whereGuard }
14184
- };
14185
- const ast = compileUnnestUpdate(config, ctx, state);
14186
- const sql = deparseQuoted(ast);
14187
- return {
14188
- sql,
14189
- parameters: state.parameters
14190
- };
14191
- }
14192
- function compileDelete2(intent, options, deps) {
14193
- const schemaName = deps.schemaName;
14194
- const resolvedModel = options?.model ?? deps.model;
14195
- const ctx = {
14196
- naming: deps.naming,
14197
- rootTable: intent.table,
14198
- ...schemaName !== void 0 && { schema: schemaName },
14199
- maxRecursiveDepth: 100,
14200
- ...resolvedModel !== void 0 && { model: resolvedModel }
14201
- };
14202
- const state = createCompilerState();
14203
- const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
14204
- const config = {
14205
- table: intent.table,
14206
- ...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
14207
- ...intent.returning && { returning: [...intent.returning] }
14208
- };
14209
- const ast = compileDelete(config, ctx, state);
14210
- const sql = deparseQuoted(ast);
14211
- return {
14212
- sql,
14213
- parameters: state.parameters
14214
- };
14215
- }
14216
- function compileUpsert2(intent, options, deps) {
14217
- const schemaName = deps.schemaName;
14218
- const ctx = {
14219
- naming: deps.naming,
14220
- rootTable: intent.table,
14221
- ...schemaName !== void 0 && { schema: schemaName },
14222
- maxRecursiveDepth: 100
14223
- };
14224
- const state = createCompilerState();
14225
- const firstRow = intent.values?.[0] ?? {};
14226
- const rawExprs = {};
14227
- const scalarSet = {};
14228
- if (intent.action.type === "doUpdate" && intent.action.set) {
14229
- for (const [key, val] of Object.entries(intent.action.set)) {
14230
- if (isSqlRaw2(val)) {
14231
- rawExprs[key] = val.sql;
14232
- } else {
14233
- scalarSet[key] = val;
14234
- }
14235
- }
14236
- }
14237
- const hasScalarSet = Object.keys(scalarSet).length > 0;
14238
- const mergedFirstRow = hasScalarSet ? { ...firstRow, ...scalarSet } : firstRow;
14239
- const columns = Object.keys(mergedFirstRow);
14240
- const values = (intent.values ?? []).map((row) => {
14241
- const mergedRow = hasScalarSet ? { ...row, ...scalarSet } : row;
14242
- return columns.map((col) => mergedRow[col]);
14243
- });
14244
- const conflictTarget = {};
14245
- if ("columns" in intent.onConflict) {
14246
- conflictTarget.columns = [...intent.onConflict.columns];
14247
- } else if ("constraint" in intent.onConflict) {
14248
- conflictTarget.constraint = intent.onConflict.constraint;
14249
- }
14250
- const conflictAction = intent.action.type === "doNothing" ? "nothing" : "update";
14251
- let updateColumns;
14252
- if (intent.action.type === "doUpdate") {
14253
- if (intent.action.set) {
14254
- updateColumns = Object.keys(intent.action.set);
14255
- } else {
14256
- const conflictCols = "columns" in intent.onConflict ? intent.onConflict.columns : [];
14257
- updateColumns = columns.filter((col) => !conflictCols.includes(col));
14258
- }
14259
- }
14260
- const columnTypes = getColumnTypes(intent.table, columns, deps);
14261
- const hasRawExprs = Object.keys(rawExprs).length > 0;
14262
- const config = {
14263
- table: intent.table,
14264
- columns,
14265
- values,
14266
- conflictTarget,
14267
- conflictAction,
14268
- ...updateColumns && { updateColumns },
14269
- ...intent.returning && { returning: [...intent.returning] },
14270
- ...columnTypes && { columnTypes },
14271
- ...hasRawExprs && { updateExpressions: rawExprs }
14272
- };
14273
- const maxBatchSize = options?.maxBatchSize;
14274
- if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
14275
- throw new InvalidOperationError3(
14276
- "upsert",
14277
- `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
14278
- );
14279
- }
14280
- const batchThreshold = options?.batchThreshold ?? 50;
14281
- const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
14282
- const ast = useUnnest ? compileUnnestUpsert(config, ctx, state) : compileUpsert(config, ctx, state);
14283
- const sql = deparseQuoted(ast);
14284
- return {
14285
- sql,
14286
- parameters: state.parameters
14287
- };
14288
- }
14289
- function compileUpsertFrom2(intent, options, deps) {
14290
- const schemaName = deps.schemaName;
14291
- const ctx = {
14292
- naming: deps.naming,
14293
- rootTable: intent.source,
14294
- ...schemaName !== void 0 && { schema: schemaName },
14295
- maxRecursiveDepth: 100
14296
- };
14297
- const state = createCompilerState();
14298
- let columns;
14299
- if (intent.columns) {
14300
- columns = [...intent.columns];
14301
- } else if (options?.model) {
14302
- const targetTable = options.model.getTable(intent.table);
14303
- if (targetTable) {
14304
- columns = targetTable.columns.map((c) => c.name);
14305
- }
14306
- }
14307
- const config = {
14308
- targetTable: intent.table,
14309
- sourceTable: intent.source,
14310
- conflictColumns: [...intent.conflictColumns],
14311
- ...columns && { columns },
14312
- ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
14313
- ...intent.limit !== void 0 && { limit: intent.limit },
14314
- ...intent.returning && { returning: [...intent.returning] }
14315
- };
14316
- const ast = compileUpsertFrom(config, ctx, state);
14317
- const sql = deparseQuoted(ast);
14318
- return {
14319
- sql,
14320
- parameters: state.parameters
14321
- };
14322
- }
14323
-
14324
- // src/adapter-compiler-select.ts
14325
- init_assert_field();
14326
- init_ast_helpers();
14327
- init_compile_where();
14328
- import { countDistinctRelationPathsByName } from "@dbsp/core";
14329
- init_compiler_utils();
14330
- init_types();
14331
- init_intent_to_decisions();
14332
- init_param_ref();
14333
-
14334
- // src/plan-decision-extractor.ts
14335
- init_assert_field();
14336
- init_intent_to_decisions();
14337
- import { deriveRelationPathFromIntentPath } from "@dbsp/core";
14338
- function findExistsIntents(where) {
14339
- if (!where || typeof where !== "object") return [];
14340
- const w = where;
14341
- if (w.kind === "exists" || w.kind === "notExists" || w.kind === "relationFilter") {
14342
- return [w];
14343
- }
14344
- const results = [];
14345
- if (w.conditions && Array.isArray(w.conditions)) {
14346
- for (const c of w.conditions) {
14347
- results.push(...findExistsIntents(c));
14348
- }
14349
- }
14350
- if (w.condition) {
14351
- results.push(...findExistsIntents(w.condition));
14352
- }
14353
- return results;
14354
- }
14355
- function resolveRelation(model, sourceTable, relationName) {
14356
- const rel = model.getRelation(`${sourceTable}.${relationName}`);
14357
- if (!rel) return void 0;
14358
- const foreignKey = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
14359
- const relationType = rel.type;
14360
- return { target: rel.target, foreignKey, relationType };
14361
- }
14362
- function resolveIncludeAlias(context) {
14363
- return context.relation ?? context.includeAlias;
14364
- }
14365
- function resolveIncludeByPath(includes, intentPath, relationName) {
14366
- if (!includes) return void 0;
14367
- if (intentPath) {
14368
- const indexPattern = /include\[(\d+)\]/g;
14369
- let current = includes;
14370
- let resolved;
14371
- let execResult = indexPattern.exec(intentPath);
14372
- while (execResult !== null) {
14373
- const idx = parseInt(execResult[1], 10);
14374
- const item = current[idx];
14375
- if (!item) break;
14376
- resolved = item;
14377
- current = item.include ?? [];
14378
- execResult = indexPattern.exec(intentPath);
14379
- }
14380
- if (resolved) return resolved;
14381
- }
14382
- return includes.find(
14383
- (i) => i.relation === relationName || i.via === relationName
14384
- );
14385
- }
14386
- function deriveForeignKey(context, deriveFk = defaultFkDerivation, defaultPk = DEFAULT_PK_COLUMN) {
14387
- const fk = context.foreignKey ?? context.sourceFK;
14388
- if (fk) return fk;
14389
- if (!context.relationType) return void 0;
14390
- if (context.relationType === "belongsTo") {
14391
- return context.target ? deriveFk(context.target, defaultPk) : void 0;
14392
- }
14393
- return context.sourceTable ? deriveFk(context.sourceTable, defaultPk) : void 0;
14394
- }
14395
- function mapComparisonOperator(op3) {
14396
- const map = {
14397
- eq: "=",
14398
- neq: "!=",
14399
- gt: ">",
14400
- gte: ">=",
14401
- lt: "<",
14402
- lte: "<=",
14403
- like: "LIKE",
14404
- ilike: "ILIKE",
14405
- isDistinctFrom: "IS DISTINCT FROM"
14406
- };
14407
- return map[op3] ?? "=";
14219
+ return map[op3] ?? "=";
14408
14220
  }
14409
14221
  function convertWhereToDecisions(where, table) {
14410
14222
  if (!where || typeof where !== "object") return [];
@@ -15424,288 +15236,754 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
15424
15236
  });
15425
15237
  }
15426
15238
  }
15427
- return results;
15239
+ return results;
15240
+ }
15241
+ function stripJoinColumnsForAggregation(decisions, intent) {
15242
+ const isAggregateOnly = intent.select && "type" in intent.select && intent.select.type === "aggregate" && !("fields" in intent.select && intent.select.fields);
15243
+ const isDistinct = intent.distinct === true;
15244
+ const hasGroupBy = intent.groupBy && intent.groupBy.length > 0;
15245
+ const hasExplicitColumns = intent.select && "type" in intent.select && intent.select.type === "expressions";
15246
+ if (isAggregateOnly || isDistinct || hasGroupBy || hasExplicitColumns) {
15247
+ for (const d of decisions) {
15248
+ if (d.type === "includeStrategy" && d.choice === "join") {
15249
+ d.columns = [];
15250
+ }
15251
+ }
15252
+ }
15253
+ }
15254
+ function buildRelationColumnsMap(decisions, includedRelations) {
15255
+ const map = /* @__PURE__ */ new Map();
15256
+ for (const d of decisions) {
15257
+ if (!(d.type === "selectRelationColumn" && d.relation && d.column))
15258
+ continue;
15259
+ const col = d.column;
15260
+ const alias = d.alias;
15261
+ const fullRelation = d.relation;
15262
+ const rootRelation = fullRelation.split(".")[0] ?? "";
15263
+ if (!includedRelations.has(rootRelation)) continue;
15264
+ const mapKey = fullRelation;
15265
+ if (col === "*") {
15266
+ map.set(mapKey, [{ col: "*" }]);
15267
+ continue;
15268
+ }
15269
+ const existing = map.get(mapKey);
15270
+ if (existing) {
15271
+ if (existing.length === 1 && existing[0]?.col === "*") continue;
15272
+ if (!existing.some((e) => e.col === col)) {
15273
+ existing.push({ col, ...alias !== void 0 && { alias } });
15274
+ }
15275
+ } else {
15276
+ map.set(mapKey, [{ col, ...alias !== void 0 && { alias } }]);
15277
+ }
15278
+ }
15279
+ return map;
15280
+ }
15281
+ function injectAndValidateRelationColumns(enrichedUnifiedDecisions, relationColumnsMap, model) {
15282
+ if (relationColumnsMap.size === 0) return;
15283
+ for (const d of enrichedUnifiedDecisions) {
15284
+ if (d.type === "includeStrategy" && d.relationName) {
15285
+ const mapKey = d.relationPath ?? d.relationName;
15286
+ const entries = mapKey ? relationColumnsMap.get(mapKey) : void 0;
15287
+ if (entries) {
15288
+ const mut = d;
15289
+ mut.columns = entries.map((e) => e.col);
15290
+ const aliasMap = {};
15291
+ for (const { col, alias } of entries) {
15292
+ if (alias) aliasMap[col] = alias;
15293
+ }
15294
+ if (Object.keys(aliasMap).length > 0) {
15295
+ mut.columnAliases = aliasMap;
15296
+ }
15297
+ }
15298
+ }
15299
+ }
15300
+ if (!model) return;
15301
+ for (const d of enrichedUnifiedDecisions) {
15302
+ if (d.type === "includeStrategy" && d.columns && d.targetTable && !(d.columns.length === 1 && d.columns[0] === "*")) {
15303
+ const targetTable = model.getTable(d.targetTable);
15304
+ if (targetTable) {
15305
+ const validColumnNames = new Set(
15306
+ targetTable.columns.map((c) => c.name)
15307
+ );
15308
+ const invalid = d.columns.filter(
15309
+ (c) => !validColumnNames.has(c)
15310
+ );
15311
+ if (invalid.length > 0) {
15312
+ throw new Error(
15313
+ `Unknown column(s) ${invalid.map((c) => `'${c}'`).join(", ")} in relation '${d.relationName}' (table '${d.targetTable}'). Available: ${[...validColumnNames].join(", ")}`
15314
+ );
15315
+ }
15316
+ }
15317
+ }
15318
+ }
15319
+ }
15320
+ function applyJoinHydrationPrefixes(decisions) {
15321
+ const usages = [];
15322
+ for (const d of decisions) {
15323
+ if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15324
+ continue;
15325
+ }
15326
+ const relationName = d.relationName;
15327
+ const relationPath = d.relationPath ?? relationName;
15328
+ usages.push({ relationName, relationPath });
15329
+ }
15330
+ const pathCountsByRelation = countDistinctRelationPathsByName(usages);
15331
+ for (const d of decisions) {
15332
+ if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15333
+ continue;
15334
+ }
15335
+ const relationName = d.relationName;
15336
+ const relationPath = d.relationPath ?? relationName;
15337
+ const usesFullPath = (pathCountsByRelation.get(relationName) ?? 0) > 1;
15338
+ d.hydrationPrefix = usesFullPath ? relationPath : relationName;
15339
+ }
15340
+ }
15341
+ function enrichRangeDecisions(allDecisions, model, rootTable) {
15342
+ if (!model) return;
15343
+ for (let i = 0; i < allDecisions.length; i++) {
15344
+ const d = allDecisions[i];
15345
+ if (d && d.type === "where" && (d.operator === "contains" || d.operator === "containedBy" || d.operator === "overlaps")) {
15346
+ const tableName = d.table || rootTable;
15347
+ const table = model.getTable(tableName);
15348
+ if (table) {
15349
+ const col = table.columns.find((c) => c.name === d.column);
15350
+ if (col?.type.endsWith("range")) {
15351
+ allDecisions[i] = { ...d, dataType: col.type };
15352
+ }
15353
+ }
15354
+ }
15355
+ }
15356
+ }
15357
+ function buildSimplifiedPlanReport(plan, allDecisions, schemaName) {
15358
+ const bvFromSource = plan.intent?.batchValuesSource;
15359
+ const batchValuesFromFields = bvFromSource ? (() => {
15360
+ const { rangeFunction, params } = buildBatchValuesRangeFn(
15361
+ bvFromSource,
15362
+ 1
15363
+ );
15364
+ return {
15365
+ batchValuesFromNode: rangeFunction,
15366
+ batchValuesFromParams: params
15367
+ };
15368
+ })() : {};
15369
+ return {
15370
+ rootTable: plan.rootTable,
15371
+ decisions: allDecisions,
15372
+ ...schemaName ? { schema: schemaName } : {},
15373
+ ...plan.intent?.existsWrap ? { existsWrap: true } : {},
15374
+ ...plan.intent?.lock ? { lock: plan.intent.lock } : {},
15375
+ ...batchValuesFromFields
15376
+ };
15377
+ }
15378
+ function compileSelect(plan, options, deps) {
15379
+ const schemaName = deps.schemaName;
15380
+ const resolvedModelForCompiler = options?.model ?? deps.model;
15381
+ const compilerOptions = {
15382
+ naming: deps.naming,
15383
+ ...schemaName && { schema: schemaName },
15384
+ defaultPkColumnName: deps.defaultPk,
15385
+ deriveFkColumnName: deps.deriveFk,
15386
+ ...deps.bindingNames !== void 0 && {
15387
+ bindingNames: deps.bindingNames
15388
+ },
15389
+ ...resolvedModelForCompiler != null && {
15390
+ model: resolvedModelForCompiler
15391
+ }
15392
+ };
15393
+ const execIntent = plan.executableIntent ?? plan.intent;
15394
+ const planForCompilation = plan.executableIntent !== void 0 ? { ...plan, intent: plan.executableIntent } : plan;
15395
+ let simplifiedPlan;
15396
+ if (execIntent) {
15397
+ let decisions = intentToDecisions(execIntent, plan.rootTable);
15398
+ const resolvedModel = options?.model ?? deps.model;
15399
+ if (resolvedModel) {
15400
+ decisions = convertDottedFieldsToExists(
15401
+ decisions,
15402
+ plan.rootTable,
15403
+ resolvedModel
15404
+ );
15405
+ }
15406
+ enrichExistsDecisionsInPlace(
15407
+ decisions,
15408
+ planForCompilation,
15409
+ options?.model ?? deps.model
15410
+ );
15411
+ const unifiedIncludeDecisions = extractAllIncludeDecisions(
15412
+ planForCompilation,
15413
+ deps.defaultPk,
15414
+ deps.deriveFk
15415
+ );
15416
+ const coveredByPlanner = new Set(
15417
+ unifiedIncludeDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
15418
+ );
15419
+ const synthesizedModel = options?.model ?? deps.model;
15420
+ const synthesizedJoins = synthesizedModel ? synthesizeMissingJoinDecisions(
15421
+ planForCompilation,
15422
+ coveredByPlanner,
15423
+ synthesizedModel,
15424
+ deps.defaultPk,
15425
+ deps.deriveFk
15426
+ ) : [];
15427
+ const allUnifiedIncludeDecisions = synthesizedJoins.length > 0 ? [...unifiedIncludeDecisions, ...synthesizedJoins] : unifiedIncludeDecisions;
15428
+ const enrichedUnifiedDecisions = [
15429
+ ...allUnifiedIncludeDecisions
15430
+ ];
15431
+ stripJoinColumnsForAggregation(enrichedUnifiedDecisions, execIntent);
15432
+ applyJoinHydrationPrefixes(enrichedUnifiedDecisions);
15433
+ const includedRelations = new Set(
15434
+ enrichedUnifiedDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
15435
+ );
15436
+ if (includedRelations.size > 0) {
15437
+ const relationColumnsMap = buildRelationColumnsMap(
15438
+ decisions,
15439
+ includedRelations
15440
+ );
15441
+ injectAndValidateRelationColumns(
15442
+ enrichedUnifiedDecisions,
15443
+ relationColumnsMap,
15444
+ options?.model ?? deps.model
15445
+ );
15446
+ }
15447
+ const deduplicatedDecisions = includedRelations.size > 0 ? decisions.filter((d) => {
15448
+ if (d.type === "selectRelationColumn" && d.relation) {
15449
+ const rel = d.relation;
15450
+ const rootRelation = rel.split(".")[0] ?? rel;
15451
+ if (includedRelations.has(rootRelation)) {
15452
+ return false;
15453
+ }
15454
+ }
15455
+ return true;
15456
+ }) : decisions;
15457
+ const joinIntentDecisions = execIntent?.joins && execIntent.joins.length > 0 ? compileJoinIntents(
15458
+ execIntent.joins,
15459
+ plan.rootTable,
15460
+ schemaName,
15461
+ deps
15462
+ ) : [];
15463
+ const allDecisions = [
15464
+ ...deduplicatedDecisions,
15465
+ ...enrichedUnifiedDecisions,
15466
+ ...joinIntentDecisions
15467
+ ];
15468
+ const rangeModel = options?.model ?? deps.model;
15469
+ enrichRangeDecisions(allDecisions, rangeModel, plan.rootTable);
15470
+ simplifiedPlan = buildSimplifiedPlanReport(
15471
+ planForCompilation,
15472
+ allDecisions,
15473
+ schemaName
15474
+ );
15475
+ } else {
15476
+ simplifiedPlan = {
15477
+ rootTable: plan.rootTable,
15478
+ decisions: bridgeLegacyDecisions(plan.decisions),
15479
+ ...schemaName ? { schema: schemaName } : {}
15480
+ };
15481
+ }
15482
+ const result = compilePlan(simplifiedPlan, compilerOptions);
15483
+ return {
15484
+ sql: result.sql,
15485
+ parameters: result.parameters
15486
+ };
15428
15487
  }
15429
- function stripJoinColumnsForAggregation(decisions, intent) {
15430
- const isAggregateOnly = intent.select && "type" in intent.select && intent.select.type === "aggregate" && !("fields" in intent.select && intent.select.fields);
15431
- const isDistinct = intent.distinct === true;
15432
- const hasGroupBy = intent.groupBy && intent.groupBy.length > 0;
15433
- const hasExplicitColumns = intent.select && "type" in intent.select && intent.select.type === "expressions";
15434
- if (isAggregateOnly || isDistinct || hasGroupBy || hasExplicitColumns) {
15435
- for (const d of decisions) {
15436
- if (d.type === "includeStrategy" && d.choice === "join") {
15437
- d.columns = [];
15438
- }
15488
+ function compileWithIncludes(plan, options, deps) {
15489
+ const main = compileSelect(plan, options, deps);
15490
+ const subqueryIncludes = [];
15491
+ for (const d of plan.decisions) {
15492
+ if (d.type !== "include-strategy" || d.choice !== "subquery") continue;
15493
+ const ctx = d.context;
15494
+ if (!ctx.target) continue;
15495
+ const relationName = ctx.includeAlias ?? ctx.relation;
15496
+ if (!relationName) continue;
15497
+ const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
15498
+ const fk = Array.isArray(rawFk) ? rawFk[0] : rawFk;
15499
+ const isBelongsTo = ctx.relationType === "belongsTo";
15500
+ const sourceKey = isBelongsTo ? fk : "id";
15501
+ const targetFk = isBelongsTo ? "id" : fk;
15502
+ const includeIntent = plan.intent?.include?.find(
15503
+ (i) => i.relation === relationName || i.relation === ctx.includeAlias
15504
+ );
15505
+ const entry = {
15506
+ relationName,
15507
+ targetTable: ctx.target,
15508
+ foreignKey: targetFk,
15509
+ sourceKey,
15510
+ sourceTable: ctx.sourceTable ?? plan.rootTable
15511
+ };
15512
+ if (typeof ctx.relationType === "string") {
15513
+ entry.relationType = ctx.relationType;
15439
15514
  }
15440
- }
15441
- }
15442
- function buildRelationColumnsMap(decisions, includedRelations) {
15443
- const map = /* @__PURE__ */ new Map();
15444
- for (const d of decisions) {
15445
- if (!(d.type === "selectRelationColumn" && d.relation && d.column))
15446
- continue;
15447
- const col = d.column;
15448
- const alias = d.alias;
15449
- const fullRelation = d.relation;
15450
- const rootRelation = fullRelation.split(".")[0] ?? "";
15451
- if (!includedRelations.has(rootRelation)) continue;
15452
- const mapKey = fullRelation;
15453
- if (col === "*") {
15454
- map.set(mapKey, [{ col: "*" }]);
15455
- continue;
15515
+ if (includeIntent?.select != null) {
15516
+ entry.select = includeIntent.select;
15456
15517
  }
15457
- const existing = map.get(mapKey);
15458
- if (existing) {
15459
- if (existing.length === 1 && existing[0]?.col === "*") continue;
15460
- if (!existing.some((e) => e.col === col)) {
15461
- existing.push({ col, ...alias !== void 0 && { alias } });
15462
- }
15463
- } else {
15464
- map.set(mapKey, [{ col, ...alias !== void 0 && { alias } }]);
15518
+ if (includeIntent?.where != null) {
15519
+ entry.where = includeIntent.where;
15465
15520
  }
15521
+ subqueryIncludes.push(entry);
15466
15522
  }
15467
- return map;
15523
+ return { main, subqueryIncludes };
15468
15524
  }
15469
- function injectAndValidateRelationColumns(enrichedUnifiedDecisions, relationColumnsMap, model) {
15470
- if (relationColumnsMap.size === 0) return;
15471
- for (const d of enrichedUnifiedDecisions) {
15472
- if (d.type === "includeStrategy" && d.relationName) {
15473
- const mapKey = d.relationPath ?? d.relationName;
15474
- const entries = mapKey ? relationColumnsMap.get(mapKey) : void 0;
15475
- if (entries) {
15476
- const mut = d;
15477
- mut.columns = entries.map((e) => e.col);
15478
- const aliasMap = {};
15479
- for (const { col, alias } of entries) {
15480
- if (alias) aliasMap[col] = alias;
15481
- }
15482
- if (Object.keys(aliasMap).length > 0) {
15483
- mut.columnAliases = aliasMap;
15484
- }
15485
- }
15486
- }
15525
+
15526
+ // src/adapter-compiler-mutations.ts
15527
+ init_binding_registry();
15528
+ init_compile_where();
15529
+ init_compiler_utils();
15530
+ init_handlers();
15531
+ function whereIntentAsDecision(where) {
15532
+ return where;
15533
+ }
15534
+ function renumberSqlParams(sql, offset) {
15535
+ if (offset === 0) return sql;
15536
+ return sql.replace(/\$(\d+)/g, (_match, num) => {
15537
+ return `$${Number.parseInt(num, 10) + offset}`;
15538
+ });
15539
+ }
15540
+ function compileSourceQueryCte(operation, sourceName, sourceQuery, options, deps) {
15541
+ const model = deps.model;
15542
+ if (model === void 0) {
15543
+ throw new Error(
15544
+ `${operation} with sourceQuery requires a model to emit the source CTE for '${sourceName}'.`
15545
+ );
15487
15546
  }
15488
- if (!model) return;
15489
- for (const d of enrichedUnifiedDecisions) {
15490
- if (d.type === "includeStrategy" && d.columns && d.targetTable && !(d.columns.length === 1 && d.columns[0] === "*")) {
15491
- const targetTable = model.getTable(d.targetTable);
15492
- if (targetTable) {
15493
- const validColumnNames = new Set(
15494
- targetTable.columns.map((c) => c.name)
15495
- );
15496
- const invalid = d.columns.filter(
15497
- (c) => !validColumnNames.has(c)
15498
- );
15499
- if (invalid.length > 0) {
15500
- throw new Error(
15501
- `Unknown column(s) ${invalid.map((c) => `'${c}'`).join(", ")} in relation '${d.relationName}' (table '${d.targetTable}'). Available: ${[...validColumnNames].join(", ")}`
15502
- );
15503
- }
15504
- }
15547
+ const sourcePlan = planFn(sourceQuery, model, {
15548
+ dialectCapabilities: POSTGRESQL_CAPABILITIES
15549
+ });
15550
+ return compileSelect(sourcePlan, options, deps);
15551
+ }
15552
+ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
15553
+ if (sourceCte === void 0) {
15554
+ return { sql, parameters };
15555
+ }
15556
+ const cteParamCount = sourceCte.parameters.length;
15557
+ const sourceCteName = emittedBindName(sourceName, naming);
15558
+ return {
15559
+ sql: `WITH ${quoteIdent2(sourceCteName, "alias")} as (${sourceCte.sql}) ${renumberSqlParams(sql, cteParamCount)}`,
15560
+ parameters: [...sourceCte.parameters, ...parameters]
15561
+ };
15562
+ }
15563
+ function resolveExistsRelation(sourceTable, relation, model) {
15564
+ if (!model) return { targetTable: relation };
15565
+ const rel = model.getRelation(`${sourceTable}.${relation}`);
15566
+ if (!rel) return { targetTable: relation };
15567
+ const targetTable = rel.target;
15568
+ if (rel.type === "belongsTo") {
15569
+ const fk2 = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
15570
+ return {
15571
+ targetTable,
15572
+ ...fk2 !== void 0 && { sourceColumn: fk2 },
15573
+ targetColumn: "id"
15574
+ };
15575
+ }
15576
+ const fk = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
15577
+ return {
15578
+ targetTable,
15579
+ sourceColumn: rel.sourceKey ?? "id",
15580
+ ...fk !== void 0 && { targetColumn: fk }
15581
+ };
15582
+ }
15583
+ function resolveExistsIntent(where, sourceTable, deps) {
15584
+ const w = where;
15585
+ const kind = w.kind;
15586
+ if (kind === "and" || kind === "or") {
15587
+ const conditions = w.conditions;
15588
+ if (!conditions) return where;
15589
+ const enriched = conditions.map(
15590
+ (c) => resolveExistsIntent(c, sourceTable, deps)
15591
+ );
15592
+ const changed = enriched.some((c, i) => c !== conditions[i]);
15593
+ return changed ? { ...w, conditions: enriched } : where;
15594
+ }
15595
+ if (kind === "not") {
15596
+ const condition = w.condition;
15597
+ if (!condition) return where;
15598
+ const enriched = resolveExistsIntent(condition, sourceTable, deps);
15599
+ return enriched !== condition ? { ...w, condition: enriched } : where;
15600
+ }
15601
+ if (kind !== "exists" && kind !== "notExists") return where;
15602
+ const relation = w.relation;
15603
+ const resolved = resolveExistsRelation(sourceTable, relation, deps.model);
15604
+ if (resolved.targetTable === relation && !resolved.sourceColumn) return where;
15605
+ return {
15606
+ ...w,
15607
+ targetTable: resolved.targetTable,
15608
+ ...resolved.sourceColumn !== void 0 && {
15609
+ sourceColumn: resolved.sourceColumn
15610
+ },
15611
+ ...resolved.targetColumn !== void 0 && {
15612
+ targetColumn: resolved.targetColumn
15613
+ }
15614
+ };
15615
+ }
15616
+ function getColumnTypes(tableName, columns, deps) {
15617
+ if (!deps.model) return void 0;
15618
+ const table = deps.model.getTable(tableName);
15619
+ if (!table) return void 0;
15620
+ let result;
15621
+ for (const col of columns) {
15622
+ const columnIR = table.columns.find((c) => c.name === col);
15623
+ if (columnIR) {
15624
+ result ??= {};
15625
+ result[col] = columnIR.originalDbType ?? columnIR.type;
15505
15626
  }
15506
15627
  }
15628
+ return result;
15629
+ }
15630
+ function compileUpsertActionWhere(where, table, state, deps, schemaName) {
15631
+ const whereCtx = {
15632
+ rootTable: table,
15633
+ aliases: /* @__PURE__ */ new Map(),
15634
+ paramState: state,
15635
+ naming: deps.naming,
15636
+ ...schemaName !== void 0 && { schemaName },
15637
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15638
+ ...deps.model !== void 0 && { model: deps.model },
15639
+ compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
15640
+ sqIntent,
15641
+ paramOffset,
15642
+ deps.naming,
15643
+ schemaName,
15644
+ "rawExists",
15645
+ deps.bindingNames
15646
+ )
15647
+ };
15648
+ return compileWhereIntent(where, whereCtx);
15649
+ }
15650
+ function compileInsert2(intent, options, deps) {
15651
+ const schemaName = deps.schemaName;
15652
+ const ctx = {
15653
+ naming: deps.naming,
15654
+ rootTable: intent.table,
15655
+ ...schemaName !== void 0 && { schema: schemaName },
15656
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15657
+ maxRecursiveDepth: 100
15658
+ };
15659
+ const state = createCompilerState();
15660
+ const firstRow = intent.values?.[0] ?? {};
15661
+ const columns = Object.keys(firstRow);
15662
+ const rows = intent.values ?? [];
15663
+ const values = rows.map((row) => columns.map((col) => row[col]));
15664
+ const columnTypes = getColumnTypes(intent.table, columns, deps);
15665
+ const config = {
15666
+ table: intent.table,
15667
+ columns,
15668
+ values,
15669
+ ...intent.returning && { returning: [...intent.returning] },
15670
+ ...columnTypes && { columnTypes }
15671
+ };
15672
+ const maxBatchSize = options?.maxBatchSize;
15673
+ if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
15674
+ throw new InvalidOperationError3(
15675
+ "insert",
15676
+ `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
15677
+ );
15678
+ }
15679
+ const batchThreshold = options?.batchThreshold ?? 50;
15680
+ const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
15681
+ const ast = useUnnest ? compileUnnestInsert(config, ctx, state) : compileInsert(config, ctx, state);
15682
+ const sql = deparseQuoted(ast);
15683
+ return {
15684
+ sql,
15685
+ parameters: state.parameters
15686
+ };
15687
+ }
15688
+ function compileInsertFrom2(intent, options, deps) {
15689
+ const schemaName = deps.schemaName;
15690
+ const sourceCte = intent.sourceQuery !== void 0 && !hasBindingName(deps.bindingNames, intent.source, deps.naming) ? compileSourceQueryCte(
15691
+ "compileInsertFrom",
15692
+ intent.source,
15693
+ intent.sourceQuery,
15694
+ options,
15695
+ deps
15696
+ ) : void 0;
15697
+ const bindingNames = intent.sourceQuery !== void 0 ? withBindingName(deps.bindingNames, intent.source, deps.naming) : deps.bindingNames;
15698
+ const ctx = {
15699
+ naming: deps.naming,
15700
+ rootTable: intent.source,
15701
+ ...schemaName !== void 0 && { schema: schemaName },
15702
+ ...bindingNames !== void 0 && { bindingNames },
15703
+ maxRecursiveDepth: 100
15704
+ };
15705
+ const state = createCompilerState();
15706
+ const config = {
15707
+ targetTable: intent.table,
15708
+ sourceTable: intent.source,
15709
+ ...intent.columns && { columns: [...intent.columns] },
15710
+ ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
15711
+ ...intent.limit !== void 0 && { limit: intent.limit },
15712
+ ...intent.returning && { returning: [...intent.returning] }
15713
+ };
15714
+ const ast = compileInsertFrom(config, ctx, state);
15715
+ const sql = deparseQuoted(ast);
15716
+ return prependSourceCte(
15717
+ sql,
15718
+ state.parameters,
15719
+ intent.source,
15720
+ sourceCte,
15721
+ deps.naming
15722
+ );
15507
15723
  }
15508
- function applyJoinHydrationPrefixes(decisions) {
15509
- const usages = [];
15510
- for (const d of decisions) {
15511
- if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15512
- continue;
15513
- }
15514
- const relationName = d.relationName;
15515
- const relationPath = d.relationPath ?? relationName;
15516
- usages.push({ relationName, relationPath });
15724
+ function compileUpdate2(intent, options, deps) {
15725
+ const schemaName = deps.schemaName;
15726
+ const resolvedModel = options?.model ?? deps.model;
15727
+ const ctx = {
15728
+ naming: deps.naming,
15729
+ rootTable: intent.table,
15730
+ ...schemaName !== void 0 && { schema: schemaName },
15731
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15732
+ ...resolvedModel !== void 0 && { model: resolvedModel },
15733
+ maxRecursiveDepth: 100
15734
+ };
15735
+ const state = createCompilerState();
15736
+ const setColumns = Object.keys(intent.set ?? {});
15737
+ const columnTypes = getColumnTypes(intent.table, setColumns, deps);
15738
+ const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
15739
+ const config = {
15740
+ table: intent.table,
15741
+ set: Object.entries(intent.set ?? {}).map(([column, value]) => ({
15742
+ column,
15743
+ value
15744
+ })),
15745
+ ...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
15746
+ ...intent.returning && { returning: [...intent.returning] },
15747
+ ...columnTypes && { columnTypes }
15748
+ };
15749
+ const ast = compileUpdate(config, ctx, state);
15750
+ const sql = deparseQuoted(ast);
15751
+ return {
15752
+ sql,
15753
+ parameters: state.parameters
15754
+ };
15755
+ }
15756
+ function compileBatchUpdate(intent, _options, deps) {
15757
+ const schemaName = deps.schemaName;
15758
+ const ctx = {
15759
+ naming: deps.naming,
15760
+ rootTable: intent.table,
15761
+ ...schemaName !== void 0 && { schema: schemaName },
15762
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15763
+ maxRecursiveDepth: 100
15764
+ };
15765
+ const state = createCompilerState();
15766
+ if (intent.updates.length === 0) {
15767
+ throw new InvalidOperationError3(
15768
+ "update",
15769
+ "batchSet requires at least one row"
15770
+ );
15517
15771
  }
15518
- const pathCountsByRelation = countDistinctRelationPathsByName(usages);
15519
- for (const d of decisions) {
15520
- if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
15521
- continue;
15772
+ const allColumns = Object.keys(intent.updates[0]);
15773
+ const matchColumns = [...intent.matchColumns];
15774
+ for (const mc of matchColumns) {
15775
+ if (!allColumns.includes(mc)) {
15776
+ throw new InvalidOperationError3(
15777
+ "update",
15778
+ `Match column "${mc}" not found in update data. Each row must include the match column(s).`
15779
+ );
15522
15780
  }
15523
- const relationName = d.relationName;
15524
- const relationPath = d.relationPath ?? relationName;
15525
- const usesFullPath = (pathCountsByRelation.get(relationName) ?? 0) > 1;
15526
- d.hydrationPrefix = usesFullPath ? relationPath : relationName;
15527
15781
  }
15528
- }
15529
- function enrichRangeDecisions(allDecisions, model, rootTable) {
15530
- if (!model) return;
15531
- for (let i = 0; i < allDecisions.length; i++) {
15532
- const d = allDecisions[i];
15533
- if (d && d.type === "where" && (d.operator === "contains" || d.operator === "containedBy" || d.operator === "overlaps")) {
15534
- const tableName = d.table || rootTable;
15535
- const table = model.getTable(tableName);
15536
- if (table) {
15537
- const col = table.columns.find((c) => c.name === d.column);
15538
- if (col?.type.endsWith("range")) {
15539
- allDecisions[i] = { ...d, dataType: col.type };
15540
- }
15541
- }
15542
- }
15782
+ const values = intent.updates.map((row) => allColumns.map((col) => row[col]));
15783
+ validateBatchCardinality(allColumns, values);
15784
+ const columnArrays = transposeToColumnArrays(allColumns, values);
15785
+ const columnTypes = getColumnTypes(intent.table, allColumns, deps);
15786
+ const scalarSet = intent.scalarSet ? Object.entries(intent.scalarSet).map(([column, value]) => ({
15787
+ column,
15788
+ value
15789
+ })) : void 0;
15790
+ let whereGuard;
15791
+ if (intent.where) {
15792
+ const whereCtx = {
15793
+ rootTable: intent.table,
15794
+ aliases: /* @__PURE__ */ new Map(),
15795
+ paramState: state,
15796
+ naming: deps.naming,
15797
+ ...schemaName !== void 0 && { schemaName },
15798
+ ...deps.bindingNames !== void 0 && {
15799
+ bindingNames: deps.bindingNames
15800
+ },
15801
+ ...deps.model !== void 0 && { model: deps.model },
15802
+ compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
15803
+ sqIntent,
15804
+ paramOffset,
15805
+ deps.naming,
15806
+ schemaName,
15807
+ "rawExists",
15808
+ deps.bindingNames
15809
+ )
15810
+ };
15811
+ whereGuard = compileWhereIntent(intent.where, whereCtx);
15543
15812
  }
15813
+ const config = {
15814
+ table: intent.table,
15815
+ matchColumns,
15816
+ allColumns,
15817
+ columnArrays,
15818
+ ...scalarSet && { scalarSet },
15819
+ ...intent.returning && { returning: [...intent.returning] },
15820
+ ...columnTypes && { columnTypes },
15821
+ ...whereGuard !== void 0 && { whereGuard }
15822
+ };
15823
+ const ast = compileUnnestUpdate(config, ctx, state);
15824
+ const sql = deparseQuoted(ast);
15825
+ return {
15826
+ sql,
15827
+ parameters: state.parameters
15828
+ };
15544
15829
  }
15545
- function buildSimplifiedPlanReport(plan, allDecisions, schemaName) {
15546
- const bvFromSource = plan.intent?.batchValuesSource;
15547
- const batchValuesFromFields = bvFromSource ? (() => {
15548
- const { rangeFunction, params } = buildBatchValuesRangeFn(
15549
- bvFromSource,
15550
- 1
15551
- );
15552
- return {
15553
- batchValuesFromNode: rangeFunction,
15554
- batchValuesFromParams: params
15555
- };
15556
- })() : {};
15830
+ function compileDelete2(intent, options, deps) {
15831
+ const schemaName = deps.schemaName;
15832
+ const resolvedModel = options?.model ?? deps.model;
15833
+ const ctx = {
15834
+ naming: deps.naming,
15835
+ rootTable: intent.table,
15836
+ ...schemaName !== void 0 && { schema: schemaName },
15837
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15838
+ maxRecursiveDepth: 100,
15839
+ ...resolvedModel !== void 0 && { model: resolvedModel }
15840
+ };
15841
+ const state = createCompilerState();
15842
+ const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
15843
+ const config = {
15844
+ table: intent.table,
15845
+ ...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
15846
+ ...intent.returning && { returning: [...intent.returning] }
15847
+ };
15848
+ const ast = compileDelete(config, ctx, state);
15849
+ const sql = deparseQuoted(ast);
15557
15850
  return {
15558
- rootTable: plan.rootTable,
15559
- decisions: allDecisions,
15560
- ...schemaName ? { schema: schemaName } : {},
15561
- ...plan.intent?.existsWrap ? { existsWrap: true } : {},
15562
- ...plan.intent?.lock ? { lock: plan.intent.lock } : {},
15563
- ...batchValuesFromFields
15851
+ sql,
15852
+ parameters: state.parameters
15564
15853
  };
15565
15854
  }
15566
- function compileSelect(plan, options, deps) {
15855
+ function compileUpsert2(intent, options, deps) {
15567
15856
  const schemaName = deps.schemaName;
15568
- const resolvedModelForCompiler = options?.model ?? deps.model;
15569
- const compilerOptions = {
15857
+ const ctx = {
15570
15858
  naming: deps.naming,
15571
- ...schemaName && { schema: schemaName },
15572
- defaultPkColumnName: deps.defaultPk,
15573
- deriveFkColumnName: deps.deriveFk,
15574
- ...resolvedModelForCompiler != null && {
15575
- model: resolvedModelForCompiler
15576
- }
15859
+ rootTable: intent.table,
15860
+ ...schemaName !== void 0 && { schema: schemaName },
15861
+ ...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
15862
+ ...deps.model !== void 0 && { model: deps.model },
15863
+ maxRecursiveDepth: 100
15577
15864
  };
15578
- const execIntent = plan.executableIntent ?? plan.intent;
15579
- const planForCompilation = plan.executableIntent !== void 0 ? { ...plan, intent: plan.executableIntent } : plan;
15580
- let simplifiedPlan;
15581
- if (execIntent) {
15582
- let decisions = intentToDecisions(execIntent, plan.rootTable);
15583
- const resolvedModel = options?.model ?? deps.model;
15584
- if (resolvedModel) {
15585
- decisions = convertDottedFieldsToExists(
15586
- decisions,
15587
- plan.rootTable,
15588
- resolvedModel
15589
- );
15865
+ const state = createCompilerState();
15866
+ const firstRow = intent.values?.[0] ?? {};
15867
+ const rawExprs = {};
15868
+ const scalarSet = {};
15869
+ if (intent.action.type === "doUpdate" && intent.action.set) {
15870
+ for (const [key, val] of Object.entries(intent.action.set)) {
15871
+ if (isSqlRaw2(val)) {
15872
+ rawExprs[key] = val.sql;
15873
+ } else {
15874
+ scalarSet[key] = val;
15875
+ }
15590
15876
  }
15591
- enrichExistsDecisionsInPlace(
15592
- decisions,
15593
- planForCompilation,
15594
- options?.model ?? deps.model
15595
- );
15596
- const unifiedIncludeDecisions = extractAllIncludeDecisions(
15597
- planForCompilation,
15598
- deps.defaultPk,
15599
- deps.deriveFk
15600
- );
15601
- const coveredByPlanner = new Set(
15602
- unifiedIncludeDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
15603
- );
15604
- const synthesizedModel = options?.model ?? deps.model;
15605
- const synthesizedJoins = synthesizedModel ? synthesizeMissingJoinDecisions(
15606
- planForCompilation,
15607
- coveredByPlanner,
15608
- synthesizedModel,
15609
- deps.defaultPk,
15610
- deps.deriveFk
15611
- ) : [];
15612
- const allUnifiedIncludeDecisions = synthesizedJoins.length > 0 ? [...unifiedIncludeDecisions, ...synthesizedJoins] : unifiedIncludeDecisions;
15613
- const enrichedUnifiedDecisions = [
15614
- ...allUnifiedIncludeDecisions
15615
- ];
15616
- stripJoinColumnsForAggregation(enrichedUnifiedDecisions, execIntent);
15617
- applyJoinHydrationPrefixes(enrichedUnifiedDecisions);
15618
- const includedRelations = new Set(
15619
- enrichedUnifiedDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
15620
- );
15621
- if (includedRelations.size > 0) {
15622
- const relationColumnsMap = buildRelationColumnsMap(
15623
- decisions,
15624
- includedRelations
15625
- );
15626
- injectAndValidateRelationColumns(
15627
- enrichedUnifiedDecisions,
15628
- relationColumnsMap,
15629
- options?.model ?? deps.model
15630
- );
15877
+ }
15878
+ const hasScalarSet = Object.keys(scalarSet).length > 0;
15879
+ const mergedFirstRow = hasScalarSet ? { ...firstRow, ...scalarSet } : firstRow;
15880
+ const columns = Object.keys(mergedFirstRow);
15881
+ const values = (intent.values ?? []).map((row) => {
15882
+ const mergedRow = hasScalarSet ? { ...row, ...scalarSet } : row;
15883
+ return columns.map((col) => mergedRow[col]);
15884
+ });
15885
+ const conflictTarget = {};
15886
+ if ("columns" in intent.onConflict) {
15887
+ conflictTarget.columns = [...intent.onConflict.columns];
15888
+ } else if ("constraint" in intent.onConflict) {
15889
+ conflictTarget.constraint = intent.onConflict.constraint;
15890
+ }
15891
+ const conflictAction = intent.action.type === "doNothing" ? "nothing" : "update";
15892
+ let updateColumns;
15893
+ if (intent.action.type === "doUpdate") {
15894
+ if (intent.action.set) {
15895
+ updateColumns = Object.keys(intent.action.set);
15896
+ } else {
15897
+ const conflictCols = "columns" in intent.onConflict ? intent.onConflict.columns : [];
15898
+ updateColumns = columns.filter((col) => !conflictCols.includes(col));
15631
15899
  }
15632
- const deduplicatedDecisions = includedRelations.size > 0 ? decisions.filter((d) => {
15633
- if (d.type === "selectRelationColumn" && d.relation) {
15634
- const rel = d.relation;
15635
- const rootRelation = rel.split(".")[0] ?? rel;
15636
- if (includedRelations.has(rootRelation)) {
15637
- return false;
15638
- }
15639
- }
15640
- return true;
15641
- }) : decisions;
15642
- const joinIntentDecisions = execIntent?.joins && execIntent.joins.length > 0 ? compileJoinIntents(
15643
- execIntent.joins,
15644
- plan.rootTable,
15645
- schemaName,
15646
- deps
15647
- ) : [];
15648
- const allDecisions = [
15649
- ...deduplicatedDecisions,
15650
- ...enrichedUnifiedDecisions,
15651
- ...joinIntentDecisions
15652
- ];
15653
- const rangeModel = options?.model ?? deps.model;
15654
- enrichRangeDecisions(allDecisions, rangeModel, plan.rootTable);
15655
- simplifiedPlan = buildSimplifiedPlanReport(
15656
- planForCompilation,
15657
- allDecisions,
15658
- schemaName
15900
+ }
15901
+ const columnTypes = getColumnTypes(intent.table, columns, deps);
15902
+ const hasRawExprs = Object.keys(rawExprs).length > 0;
15903
+ const actionWhere = intent.action.type === "doUpdate" && intent.action.where ? intent.action.where : void 0;
15904
+ const resolvedActionWhere = actionWhere ? resolveExistsIntent(actionWhere, intent.table, deps) : void 0;
15905
+ const config = {
15906
+ table: intent.table,
15907
+ columns,
15908
+ values,
15909
+ conflictTarget,
15910
+ conflictAction,
15911
+ ...updateColumns && { updateColumns },
15912
+ ...intent.returning && { returning: [...intent.returning] },
15913
+ ...columnTypes && { columnTypes },
15914
+ ...hasRawExprs && { updateExpressions: rawExprs },
15915
+ ...resolvedActionWhere && {
15916
+ actionWhereIntent: resolvedActionWhere,
15917
+ compileActionWhere: (where, paramState) => compileUpsertActionWhere(
15918
+ where,
15919
+ intent.table,
15920
+ paramState,
15921
+ deps,
15922
+ schemaName
15923
+ )
15924
+ }
15925
+ };
15926
+ const maxBatchSize = options?.maxBatchSize;
15927
+ if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
15928
+ throw new InvalidOperationError3(
15929
+ "upsert",
15930
+ `Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
15659
15931
  );
15660
- } else {
15661
- simplifiedPlan = {
15662
- rootTable: plan.rootTable,
15663
- decisions: bridgeLegacyDecisions(plan.decisions),
15664
- ...schemaName ? { schema: schemaName } : {}
15665
- };
15666
15932
  }
15667
- const result = compilePlan(simplifiedPlan, compilerOptions);
15933
+ const batchThreshold = options?.batchThreshold ?? 50;
15934
+ const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
15935
+ const ast = useUnnest ? compileUnnestUpsert(config, ctx, state) : compileUpsert(config, ctx, state);
15936
+ const sql = deparseQuoted(ast);
15668
15937
  return {
15669
- sql: result.sql,
15670
- parameters: result.parameters
15938
+ sql,
15939
+ parameters: state.parameters
15671
15940
  };
15672
15941
  }
15673
- function compileWithIncludes(plan, options, deps) {
15674
- const main = compileSelect(plan, options, deps);
15675
- const subqueryIncludes = [];
15676
- for (const d of plan.decisions) {
15677
- if (d.type !== "include-strategy" || d.choice !== "subquery") continue;
15678
- const ctx = d.context;
15679
- if (!ctx.target) continue;
15680
- const relationName = ctx.includeAlias ?? ctx.relation;
15681
- if (!relationName) continue;
15682
- const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
15683
- const fk = Array.isArray(rawFk) ? rawFk[0] : rawFk;
15684
- const isBelongsTo = ctx.relationType === "belongsTo";
15685
- const sourceKey = isBelongsTo ? fk : "id";
15686
- const targetFk = isBelongsTo ? "id" : fk;
15687
- const includeIntent = plan.intent?.include?.find(
15688
- (i) => i.relation === relationName || i.relation === ctx.includeAlias
15689
- );
15690
- const entry = {
15691
- relationName,
15692
- targetTable: ctx.target,
15693
- foreignKey: targetFk,
15694
- sourceKey,
15695
- sourceTable: ctx.sourceTable ?? plan.rootTable
15696
- };
15697
- if (typeof ctx.relationType === "string") {
15698
- entry.relationType = ctx.relationType;
15699
- }
15700
- if (includeIntent?.select != null) {
15701
- entry.select = includeIntent.select;
15702
- }
15703
- if (includeIntent?.where != null) {
15704
- entry.where = includeIntent.where;
15942
+ function compileUpsertFrom2(intent, options, deps) {
15943
+ const schemaName = deps.schemaName;
15944
+ const sourceCte = intent.sourceQuery !== void 0 && !hasBindingName(deps.bindingNames, intent.source, deps.naming) ? compileSourceQueryCte(
15945
+ "compileUpsertFrom",
15946
+ intent.source,
15947
+ intent.sourceQuery,
15948
+ options,
15949
+ deps
15950
+ ) : void 0;
15951
+ const bindingNames = intent.sourceQuery !== void 0 ? withBindingName(deps.bindingNames, intent.source, deps.naming) : deps.bindingNames;
15952
+ const ctx = {
15953
+ naming: deps.naming,
15954
+ rootTable: intent.source,
15955
+ ...schemaName !== void 0 && { schema: schemaName },
15956
+ ...bindingNames !== void 0 && { bindingNames },
15957
+ maxRecursiveDepth: 100
15958
+ };
15959
+ const state = createCompilerState();
15960
+ let columns;
15961
+ if (intent.columns) {
15962
+ columns = [...intent.columns];
15963
+ } else if (options?.model) {
15964
+ const targetTable = options.model.getTable(intent.table);
15965
+ if (targetTable) {
15966
+ columns = targetTable.columns.map((c) => c.name);
15705
15967
  }
15706
- subqueryIncludes.push(entry);
15707
15968
  }
15708
- return { main, subqueryIncludes };
15969
+ const config = {
15970
+ targetTable: intent.table,
15971
+ sourceTable: intent.source,
15972
+ conflictColumns: [...intent.conflictColumns],
15973
+ ...columns && { columns },
15974
+ ...intent.where && { where: [whereIntentAsDecision(intent.where)] },
15975
+ ...intent.limit !== void 0 && { limit: intent.limit },
15976
+ ...intent.returning && { returning: [...intent.returning] }
15977
+ };
15978
+ const ast = compileUpsertFrom(config, ctx, state);
15979
+ const sql = deparseQuoted(ast);
15980
+ return prependSourceCte(
15981
+ sql,
15982
+ state.parameters,
15983
+ intent.source,
15984
+ sourceCte,
15985
+ deps.naming
15986
+ );
15709
15987
  }
15710
15988
 
15711
15989
  // src/adapter-compiler-recursive.ts
@@ -16794,6 +17072,7 @@ function buildRecursiveAnchorWhere(where, tableAlias, deps, state) {
16794
17072
  // src/pgsql-adapter.ts
16795
17073
  init_assert_field();
16796
17074
  init_ast_helpers();
17075
+ init_binding_registry();
16797
17076
 
16798
17077
  // src/ddl/index-operations.ts
16799
17078
  init_validate();
@@ -16960,9 +17239,9 @@ function compileLeafOrBranch(intent, compileFn) {
16960
17239
  const result = compileFn(intent);
16961
17240
  return { sql: result.sql, parameters: result.parameters };
16962
17241
  }
16963
- function createLeafCompileFn(adapter, model, planFn2, options) {
17242
+ function createLeafCompileFn(adapter, model, planFn3, options) {
16964
17243
  return (query) => {
16965
- const planReport = planFn2(query, model, {
17244
+ const planReport = planFn3(query, model, {
16966
17245
  dialectCapabilities: adapter.dialectCapabilities
16967
17246
  });
16968
17247
  return adapter.compile(planReport, { ...options, model });
@@ -17084,7 +17363,7 @@ function mapFetchDirection(direction) {
17084
17363
 
17085
17364
  // src/pgsql-adapter.ts
17086
17365
  init_validate();
17087
- function renumberSqlParams(sql, offset) {
17366
+ function renumberSqlParams2(sql, offset) {
17088
17367
  if (offset === 0) return sql;
17089
17368
  return sql.replace(/\$(\d+)/g, (_match, num) => {
17090
17369
  return `$${Number.parseInt(num, 10) + offset}`;
@@ -17093,6 +17372,79 @@ function renumberSqlParams(sql, offset) {
17093
17372
  function isCompiledNqlQuery(input) {
17094
17373
  return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input);
17095
17374
  }
17375
+ function formatGuardPathSegment(key) {
17376
+ return /^[A-Za-z_$][\w$]*$/.test(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
17377
+ }
17378
+ function findNqlBindingRefMarker(value, path, seen = /* @__PURE__ */ new WeakSet()) {
17379
+ if (value === null || typeof value !== "object") {
17380
+ return void 0;
17381
+ }
17382
+ if (isNqlBindingRef2(value)) {
17383
+ return {
17384
+ ref: getNqlBindingRefName2(value),
17385
+ path
17386
+ };
17387
+ }
17388
+ if (seen.has(value)) {
17389
+ return void 0;
17390
+ }
17391
+ seen.add(value);
17392
+ if (Array.isArray(value)) {
17393
+ for (let i = 0; i < value.length; i++) {
17394
+ const found = findNqlBindingRefMarker(value[i], `${path}[${i}]`, seen);
17395
+ if (found) return found;
17396
+ }
17397
+ return void 0;
17398
+ }
17399
+ const record = value;
17400
+ for (const key of Object.keys(record)) {
17401
+ const found = findNqlBindingRefMarker(
17402
+ record[key],
17403
+ `${path}${formatGuardPathSegment(key)}`,
17404
+ seen
17405
+ );
17406
+ if (found) return found;
17407
+ }
17408
+ return void 0;
17409
+ }
17410
+ function guardCompiledQuery(query, context) {
17411
+ const found = findNqlBindingRefMarker(query.parameters, "parameters");
17412
+ if (found) {
17413
+ throw new Error(
17414
+ `NQL binding reference marker '${found.ref}' survived into emitted SQL parameters at ${found.path} while compiling ${context}. Binding references must resolve to CTE subqueries before SQL emission.`
17415
+ );
17416
+ }
17417
+ return query;
17418
+ }
17419
+ function guardCompileResultWithIncludes(result, context) {
17420
+ return {
17421
+ ...result,
17422
+ main: guardCompiledQuery(result.main, context)
17423
+ };
17424
+ }
17425
+ function findPhysicalTableNameCollision(model, bindingName, naming) {
17426
+ for (const [modelTableName, table] of model.tables) {
17427
+ if (bindingName === table.name) return table.name;
17428
+ if (bindingName === modelTableName) return table.name;
17429
+ const emittedTableName = naming.toDatabase(table.name);
17430
+ if (bindingName === emittedTableName) return emittedTableName;
17431
+ const emittedModelTableName = naming.toDatabase(modelTableName);
17432
+ if (bindingName === emittedModelTableName) return emittedModelTableName;
17433
+ }
17434
+ return void 0;
17435
+ }
17436
+ function findDuplicateEmittedNqlBindingName(bindingNames, naming) {
17437
+ const seen = /* @__PURE__ */ new Map();
17438
+ for (const bindingName of bindingNames) {
17439
+ const emittedName = emittedBindName(bindingName, naming);
17440
+ const originalName = seen.get(emittedName);
17441
+ if (originalName !== void 0 && originalName !== bindingName) {
17442
+ return { originalName, duplicateName: bindingName, emittedName };
17443
+ }
17444
+ seen.set(emittedName, bindingName);
17445
+ }
17446
+ return void 0;
17447
+ }
17096
17448
  var PgsqlAdapter = class _PgsqlAdapter {
17097
17449
  pool;
17098
17450
  client;
@@ -17159,17 +17511,19 @@ var PgsqlAdapter = class _PgsqlAdapter {
17159
17511
  ...overrides
17160
17512
  };
17161
17513
  }
17162
- buildCompileDeps(options) {
17514
+ buildCompileDeps(options, bindingNames) {
17163
17515
  if (options?.schemaName) {
17164
17516
  validateIdentifier(options.schemaName, "schema");
17165
17517
  }
17518
+ const naming = options?.naming ?? this.naming;
17166
17519
  return {
17167
- naming: this.naming,
17520
+ naming,
17168
17521
  // `||` (not `??`): empty string is treated as "no override" and falls back to this.schemaName (which may be a configured schema or undefined)
17169
17522
  schemaName: options?.schemaName || this.schemaName,
17170
17523
  model: options?.model ?? this.model,
17171
17524
  defaultPk: this.defaultPk,
17172
- deriveFk: this.deriveFk
17525
+ deriveFk: this.deriveFk,
17526
+ ...bindingNames !== void 0 && { bindingNames }
17173
17527
  };
17174
17528
  }
17175
17529
  requireNqlCompileModel(options) {
@@ -17181,7 +17535,24 @@ var PgsqlAdapter = class _PgsqlAdapter {
17181
17535
  }
17182
17536
  return model;
17183
17537
  }
17184
- compileNqlMutation(bundle, options) {
17538
+ assertNqlBindingNamesDisjointFromTables(bindingNames, options) {
17539
+ if (bindingNames === void 0 || bindingNames.size === 0) return;
17540
+ const model = this.requireNqlCompileModel(options);
17541
+ const naming = this.buildCompileDeps(options, bindingNames).naming;
17542
+ for (const bindingName of bindingNames) {
17543
+ const physicalTableName = findPhysicalTableNameCollision(
17544
+ model,
17545
+ bindingName,
17546
+ naming
17547
+ );
17548
+ if (physicalTableName !== void 0) {
17549
+ throw new Error(
17550
+ `NQL binding '${bindingName}' collides with physical table name '${physicalTableName}'. NQL binding names must be disjoint from model table names.`
17551
+ );
17552
+ }
17553
+ }
17554
+ }
17555
+ compileNqlMutation(bundle, options, bindingNames) {
17185
17556
  const mutation = bundle.mutation;
17186
17557
  if (mutation === void 0) {
17187
17558
  throw new Error("NQL bundle did not contain a mutation intent.");
@@ -17191,84 +17562,124 @@ var PgsqlAdapter = class _PgsqlAdapter {
17191
17562
  return compileInsert2(
17192
17563
  mutation,
17193
17564
  options,
17194
- this.buildCompileDeps(options)
17565
+ this.buildCompileDeps(options, bindingNames)
17195
17566
  );
17196
17567
  case "insert_from":
17197
17568
  return compileInsertFrom2(
17198
17569
  mutation,
17199
17570
  options,
17200
- this.buildCompileDeps(options)
17571
+ this.buildCompileDeps(options, bindingNames)
17201
17572
  );
17202
17573
  case "update":
17203
17574
  return compileUpdate2(
17204
17575
  mutation,
17205
17576
  options,
17206
- this.buildCompileDeps(options)
17577
+ this.buildCompileDeps(options, bindingNames)
17207
17578
  );
17208
17579
  case "delete":
17209
17580
  return compileDelete2(
17210
17581
  mutation,
17211
17582
  options,
17212
- this.buildCompileDeps(options)
17583
+ this.buildCompileDeps(options, bindingNames)
17213
17584
  );
17214
17585
  case "upsert":
17215
17586
  return compileUpsert2(
17216
17587
  mutation,
17217
17588
  options,
17218
- this.buildCompileDeps(options)
17589
+ this.buildCompileDeps(options, bindingNames)
17219
17590
  );
17220
17591
  case "upsert_from":
17221
17592
  return compileUpsertFrom2(
17222
17593
  mutation,
17223
17594
  options,
17224
- this.buildCompileDeps(options)
17595
+ this.buildCompileDeps(options, bindingNames)
17225
17596
  );
17226
17597
  }
17227
17598
  throw new Error(
17228
17599
  `Unsupported NQL mutation type: ${mutation.type}`
17229
17600
  );
17230
17601
  }
17231
- compileNqlBundleLeaf(bundle, options) {
17602
+ compileNqlBundleLeaf(bundle, options, bindingNames) {
17232
17603
  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
+ }
17233
17610
  const model = this.requireNqlCompileModel(options);
17234
- const planReport = planFn(bundle.query, model, {
17611
+ const planReport = planFn2(bundle.query, model, {
17235
17612
  dialectCapabilities: this.dialectCapabilities
17236
17613
  });
17237
- return compileSelect(
17238
- planReport,
17239
- options,
17240
- this.buildCompileDeps(options)
17614
+ return guardCompiledQuery(
17615
+ compileSelect(
17616
+ planReport,
17617
+ options,
17618
+ this.buildCompileDeps(options, bindingNames)
17619
+ ),
17620
+ "NQL query"
17241
17621
  );
17242
17622
  }
17243
17623
  if (bundle.cteQuery !== void 0) {
17244
- return compileCteQuery(
17245
- bundle.cteQuery,
17246
- options,
17247
- this.buildCompileDeps(options)
17624
+ return guardCompiledQuery(
17625
+ compileCteQuery(
17626
+ bundle.cteQuery,
17627
+ options,
17628
+ this.buildCompileDeps(options, bindingNames)
17629
+ ),
17630
+ "NQL CTE query"
17248
17631
  );
17249
17632
  }
17250
17633
  if (bundle.setOperation !== void 0) {
17251
17634
  const model = this.requireNqlCompileModel(options);
17252
- return this.compileSetOperation(
17253
- bundle.setOperation,
17254
- model,
17255
- options
17635
+ return guardCompiledQuery(
17636
+ this.compileSetOperationWithBindings(
17637
+ bundle.setOperation,
17638
+ model,
17639
+ options,
17640
+ bindingNames
17641
+ ),
17642
+ "NQL set operation"
17256
17643
  );
17257
17644
  }
17258
17645
  if (bundle.mutation !== void 0) {
17259
- return this.compileNqlMutation(bundle, options);
17646
+ return guardCompiledQuery(
17647
+ this.compileNqlMutation(
17648
+ bundle,
17649
+ options,
17650
+ bindingNames
17651
+ ),
17652
+ "NQL mutation"
17653
+ );
17260
17654
  }
17261
17655
  throw new Error("NQL bundle did not contain a compilable intent.");
17262
17656
  }
17263
17657
  compileNqlBundle(bundle, options) {
17264
17658
  const ctes = [];
17265
17659
  const parameters = [];
17660
+ const naming = this.buildCompileDeps(options).naming;
17661
+ const duplicateEmittedBinding = bundle.bindings !== void 0 ? findDuplicateEmittedNqlBindingName(bundle.bindings.keys(), naming) : void 0;
17662
+ if (duplicateEmittedBinding !== void 0) {
17663
+ throw new Error(
17664
+ `NQL bindings '${duplicateEmittedBinding.originalName}' and '${duplicateEmittedBinding.duplicateName}' emit to duplicate CTE name '${duplicateEmittedBinding.emittedName}'. NQL binding names must be unique after database naming.`
17665
+ );
17666
+ }
17667
+ const bindingNames = bundle.bindings !== void 0 ? new Set(
17668
+ [...bundle.bindings.keys()].map(
17669
+ (name) => emittedBindName(name, naming)
17670
+ )
17671
+ ) : void 0;
17672
+ this.assertNqlBindingNamesDisjointFromTables(bindingNames, options);
17266
17673
  for (const [name, queryIntent] of bundle.bindings ?? []) {
17267
- const cteName = quoteIdent2(name, "alias");
17674
+ const cteName = quoteIdent2(emittedBindName(name, naming), "alias");
17268
17675
  const bindingBundle = bundle.mutationBindings?.has(name) ? { mutation: bundle.mutationBindings.get(name) } : { query: queryIntent };
17269
- const compiled2 = this.compileNqlBundleLeaf(bindingBundle, options);
17676
+ const compiled2 = this.compileNqlBundleLeaf(
17677
+ bindingBundle,
17678
+ options,
17679
+ bindingNames
17680
+ );
17270
17681
  ctes.push(
17271
- `${cteName} as (${renumberSqlParams(compiled2.sql, parameters.length)})`
17682
+ `${cteName} as (${renumberSqlParams2(compiled2.sql, parameters.length)})`
17272
17683
  );
17273
17684
  parameters.push(...compiled2.parameters);
17274
17685
  }
@@ -17281,14 +17692,21 @@ var PgsqlAdapter = class _PgsqlAdapter {
17281
17692
  setOperation: bundle.setOperation
17282
17693
  }
17283
17694
  };
17284
- const compiled = this.compileNqlBundleLeaf(leafBundle, options);
17695
+ const compiled = this.compileNqlBundleLeaf(
17696
+ leafBundle,
17697
+ options,
17698
+ bindingNames
17699
+ );
17285
17700
  if (ctes.length === 0) {
17286
- return compiled;
17701
+ return guardCompiledQuery(compiled, "NQL bundle");
17287
17702
  }
17288
- return {
17289
- sql: `WITH ${ctes.join(", ")} ${renumberSqlParams(compiled.sql, parameters.length)}`,
17290
- parameters: [...parameters, ...compiled.parameters]
17291
- };
17703
+ return guardCompiledQuery(
17704
+ {
17705
+ sql: `WITH ${ctes.join(", ")} ${renumberSqlParams2(compiled.sql, parameters.length)}`,
17706
+ parameters: [...parameters, ...compiled.parameters]
17707
+ },
17708
+ "NQL bundle"
17709
+ );
17292
17710
  }
17293
17711
  /**
17294
17712
  * Returns the pool/client executor, or throws if in compile-only mode.
@@ -17308,7 +17726,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
17308
17726
  }
17309
17727
  /** PostgreSQL dialect capabilities for planner strategy selection */
17310
17728
  get dialectCapabilities() {
17311
- return POSTGRESQL_CAPABILITIES;
17729
+ return POSTGRESQL_CAPABILITIES2;
17312
17730
  }
17313
17731
  /**
17314
17732
  * DB column casing convention used by this adapter.
@@ -17332,16 +17750,18 @@ var PgsqlAdapter = class _PgsqlAdapter {
17332
17750
  if (isCompiledNqlQuery(plan)) {
17333
17751
  return this.compileNqlBundle(plan, options);
17334
17752
  }
17335
- return compileSelect(plan, options, this.buildCompileDeps(options));
17753
+ return guardCompiledQuery(
17754
+ compileSelect(plan, options, this.buildCompileDeps(options)),
17755
+ "select plan"
17756
+ );
17336
17757
  }
17337
17758
  /**
17338
17759
  * Compile a plan with includes, returning subquery include metadata (DX-033).
17339
17760
  */
17340
17761
  compileWithIncludes(plan, options) {
17341
- return compileWithIncludes(
17342
- plan,
17343
- options,
17344
- this.buildCompileDeps(options)
17762
+ return guardCompileResultWithIncludes(
17763
+ compileWithIncludes(plan, options, this.buildCompileDeps(options)),
17764
+ "select plan with includes"
17345
17765
  );
17346
17766
  }
17347
17767
  /**
@@ -17354,11 +17774,14 @@ var PgsqlAdapter = class _PgsqlAdapter {
17354
17774
  * @returns Compiled query for fetching related records
17355
17775
  */
17356
17776
  compileSubqueryInclude(info, parentIds, options) {
17357
- return compileSubqueryInclude(
17358
- info,
17359
- parentIds,
17360
- options,
17361
- this.buildCompileDeps(options)
17777
+ return guardCompiledQuery(
17778
+ compileSubqueryInclude(
17779
+ info,
17780
+ parentIds,
17781
+ options,
17782
+ this.buildCompileDeps(options)
17783
+ ),
17784
+ "subquery include"
17362
17785
  );
17363
17786
  }
17364
17787
  /**
@@ -17402,7 +17825,10 @@ var PgsqlAdapter = class _PgsqlAdapter {
17402
17825
  targetList: [{ ResTarget: { val: node } }]
17403
17826
  });
17404
17827
  const sql = deparseQuoted(ast);
17405
- return { sql, parameters: state.parameters };
17828
+ return guardCompiledQuery(
17829
+ { sql, parameters: state.parameters },
17830
+ "select expression"
17831
+ );
17406
17832
  }
17407
17833
  /**
17408
17834
  * Compile an insert intent to executable SQL.
@@ -17412,24 +17838,29 @@ var PgsqlAdapter = class _PgsqlAdapter {
17412
17838
  * - rows > batchThreshold OR batchThreshold === 0: SELECT unnest($1::type[]),...
17413
17839
  */
17414
17840
  compileInsert(intent, options) {
17415
- return compileInsert2(intent, options, this.buildCompileDeps(options));
17841
+ return guardCompiledQuery(
17842
+ compileInsert2(intent, options, this.buildCompileDeps(options)),
17843
+ "insert"
17844
+ );
17416
17845
  }
17417
17846
  /**
17418
17847
  * Compile an insert-from intent to executable SQL (NQL-ALIGN).
17419
17848
  * INSERT INTO target (cols) SELECT cols FROM source WHERE ... LIMIT ... RETURNING ...
17420
17849
  */
17421
17850
  compileInsertFrom(intent, options) {
17422
- return compileInsertFrom2(
17423
- intent,
17424
- options,
17425
- this.buildCompileDeps(options)
17851
+ return guardCompiledQuery(
17852
+ compileInsertFrom2(intent, options, this.buildCompileDeps(options)),
17853
+ "insert from"
17426
17854
  );
17427
17855
  }
17428
17856
  /**
17429
17857
  * Compile an update intent to executable SQL.
17430
17858
  */
17431
17859
  compileUpdate(intent, options) {
17432
- return compileUpdate2(intent, options, this.buildCompileDeps(options));
17860
+ return guardCompiledQuery(
17861
+ compileUpdate2(intent, options, this.buildCompileDeps(options)),
17862
+ "update"
17863
+ );
17433
17864
  }
17434
17865
  /**
17435
17866
  * Compile a batch update intent to executable SQL using unnest FROM strategy (BATCH-001).
@@ -17441,33 +17872,37 @@ var PgsqlAdapter = class _PgsqlAdapter {
17441
17872
  * [RETURNING ...]
17442
17873
  */
17443
17874
  compileBatchUpdate(intent, options) {
17444
- return compileBatchUpdate(
17445
- intent,
17446
- options,
17447
- this.buildCompileDeps(options)
17875
+ return guardCompiledQuery(
17876
+ compileBatchUpdate(intent, options, this.buildCompileDeps(options)),
17877
+ "batch update"
17448
17878
  );
17449
17879
  }
17450
17880
  /**
17451
17881
  * Compile a delete intent to executable SQL.
17452
17882
  */
17453
17883
  compileDelete(intent, options) {
17454
- return compileDelete2(intent, options, this.buildCompileDeps(options));
17884
+ return guardCompiledQuery(
17885
+ compileDelete2(intent, options, this.buildCompileDeps(options)),
17886
+ "delete"
17887
+ );
17455
17888
  }
17456
17889
  /**
17457
17890
  * Compile an upsert intent to executable SQL (DX-026).
17458
17891
  */
17459
17892
  compileUpsert(intent, options) {
17460
- return compileUpsert2(intent, options, this.buildCompileDeps(options));
17893
+ return guardCompiledQuery(
17894
+ compileUpsert2(intent, options, this.buildCompileDeps(options)),
17895
+ "upsert"
17896
+ );
17461
17897
  }
17462
17898
  /**
17463
17899
  * Compile an upsert-from intent to executable SQL (NQL-BIND).
17464
17900
  * INSERT INTO target SELECT ... FROM source ON CONFLICT (cols) DO UPDATE SET ...
17465
17901
  */
17466
17902
  compileUpsertFrom(intent, options) {
17467
- return compileUpsertFrom2(
17468
- intent,
17469
- options,
17470
- this.buildCompileDeps(options)
17903
+ return guardCompiledQuery(
17904
+ compileUpsertFrom2(intent, options, this.buildCompileDeps(options)),
17905
+ "upsert from"
17471
17906
  );
17472
17907
  }
17473
17908
  /**
@@ -17475,11 +17910,14 @@ var PgsqlAdapter = class _PgsqlAdapter {
17475
17910
  * Supports adjacency-list and edge-table traversal modes.
17476
17911
  */
17477
17912
  compileRecursive(report, model, options) {
17478
- return compileRecursive(
17479
- report,
17480
- model,
17481
- options,
17482
- this.buildCompileDeps(options)
17913
+ return guardCompiledQuery(
17914
+ compileRecursive(
17915
+ report,
17916
+ model,
17917
+ options,
17918
+ this.buildCompileDeps(options)
17919
+ ),
17920
+ "recursive query"
17483
17921
  );
17484
17922
  }
17485
17923
  /**
@@ -17490,18 +17928,42 @@ var PgsqlAdapter = class _PgsqlAdapter {
17490
17928
  * to start after CTE params and prepend WITH clause.
17491
17929
  */
17492
17930
  compileCteQuery(intent, options) {
17493
- return compileCteQuery(intent, options, this.buildCompileDeps(options));
17931
+ return guardCompiledQuery(
17932
+ compileCteQuery(intent, options, this.buildCompileDeps(options)),
17933
+ "CTE query"
17934
+ );
17494
17935
  }
17495
17936
  /**
17496
17937
  * Compile a set operation (UNION / INTERSECT / EXCEPT) to SQL.
17497
17938
  */
17498
17939
  compileSetOperation(intent, model, options) {
17499
- const compileFn = createLeafCompileFn(this, model, planFn, options);
17940
+ return guardCompiledQuery(
17941
+ this.compileSetOperationWithBindings(intent, model, options),
17942
+ "set operation"
17943
+ );
17944
+ }
17945
+ 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
+ );
17500
17959
  const result = compileSetOperation(intent, compileFn);
17501
- return {
17502
- sql: result.sql,
17503
- parameters: result.parameters
17504
- };
17960
+ return guardCompiledQuery(
17961
+ {
17962
+ sql: result.sql,
17963
+ parameters: result.parameters
17964
+ },
17965
+ "set operation"
17966
+ );
17505
17967
  }
17506
17968
  /**
17507
17969
  * Create a dump for observability.