@dbsp/core 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
@@ -5524,7 +5524,7 @@ async function runMutationWithHooksInner(opts, store) {
5524
5524
  opts.onHookError
5525
5525
  );
5526
5526
  if (prepared.returnAfterMutationResult) {
5527
- return transformed;
5527
+ return prepared.mapAfterMutationResult?.(result, transformed) ?? transformed;
5528
5528
  }
5529
5529
  }
5530
5530
  return result;
@@ -6168,15 +6168,59 @@ var NQL_RAW_FRAGMENT = /* @__PURE__ */ Symbol("NqlRawFragment");
6168
6168
  function hasNqlBindings(bundle) {
6169
6169
  return (bundle.bindings?.size ?? 0) > 0;
6170
6170
  }
6171
- function assertNoMutationBindingBodies(bundle) {
6172
- const mutationBindings = bundle.mutationBindings;
6173
- if (!mutationBindings || mutationBindings.size === 0) {
6174
- return;
6171
+ function isBindingFinalQuery(bundle) {
6172
+ return bundle.query !== void 0 && (bundle.bindings?.has(bundle.query.from) ?? false);
6173
+ }
6174
+ function formatBindingRelationPath(path) {
6175
+ return typeof path === "string" ? path : path.join(".");
6176
+ }
6177
+ function findBindingFinalRelationFilter(where) {
6178
+ switch (where.kind) {
6179
+ case "and":
6180
+ case "or":
6181
+ for (const condition of where.conditions) {
6182
+ const found = findBindingFinalRelationFilter(condition);
6183
+ if (found !== void 0) return found;
6184
+ }
6185
+ return void 0;
6186
+ case "not":
6187
+ return findBindingFinalRelationFilter(where.condition);
6188
+ case "relationFilter":
6189
+ return formatBindingRelationPath(where.relation);
6190
+ case "exists":
6191
+ case "notExists":
6192
+ return where.relation;
6193
+ case "rawExists":
6194
+ case "rawNotExists":
6195
+ return "raw EXISTS";
6196
+ default:
6197
+ return void 0;
6198
+ }
6199
+ }
6200
+ function assertBindingFinalQueryCanUseSyntheticPlan(intent) {
6201
+ const relationColumns = intent.select?.type === "expressions" ? intent.select.columns.filter((column) => column.kind === "relationColumn").map((column) => column.relation) : [];
6202
+ const relationFilter = intent.where ? findBindingFinalRelationFilter(intent.where) : void 0;
6203
+ const havingRelationFilter = intent.having ? findBindingFinalRelationFilter(intent.having) : void 0;
6204
+ if ((intent.include?.length ?? 0) > 0 || relationColumns.length > 0 || (intent.joins?.length ?? 0) > 0 || relationFilter !== void 0 || havingRelationFilter !== void 0) {
6205
+ throw new Error(
6206
+ `NQL binding-final query '${intent.from}' cannot select relation columns, use includes, joins, or relation filters. Relation planning requires a physical model table, not a CTE binding.`
6207
+ );
6175
6208
  }
6176
- const bindNames = [...mutationBindings.keys()].map((name) => `'${name}'`).join(", ");
6177
- throw new Error(
6178
- `NQL tag programs cannot use mutation bodies in | bind clauses (${bindNames}); writable binding CTEs are disabled for tag execution (#173). Use a read-only bind before a single final mutation, or execute the mutation explicitly.`
6179
- );
6209
+ }
6210
+ function createBindingFinalPlan(intent) {
6211
+ assertBindingFinalQueryCanUseSyntheticPlan(intent);
6212
+ return {
6213
+ rootTable: intent.from,
6214
+ decisions: [],
6215
+ warnings: [],
6216
+ ctes: [],
6217
+ intent,
6218
+ metadata: {
6219
+ planningTimeMs: 0,
6220
+ relationsAnalyzed: 0,
6221
+ isAmbiguous: false
6222
+ }
6223
+ };
6180
6224
  }
6181
6225
  function nqlRaw(fragment) {
6182
6226
  if (typeof fragment !== "string") {
@@ -6289,7 +6333,250 @@ function assembleNqlTemplate(strings, values) {
6289
6333
  sourceError: findInternalParamSourceError(query, generatedRanges)
6290
6334
  };
6291
6335
  }
6292
- function createNqlTag(schemaDefinition, model, adapter, schemaName, hookStore, onHookError, inTransaction) {
6336
+ function hasMutationBindingBodies(bundle) {
6337
+ return (bundle.mutationBindings?.size ?? 0) > 0;
6338
+ }
6339
+ function requireMutationBindingColumns(bundle, bindName) {
6340
+ const columns = bundle.bindingOutputSchemas?.get(bindName)?.columns;
6341
+ if (columns === void 0 || columns.length === 0) {
6342
+ throw new Error(
6343
+ `NQL mutation binding '${bindName}' cannot be materialized because its projected column schema is unavailable.`
6344
+ );
6345
+ }
6346
+ return columns;
6347
+ }
6348
+ function toSnakeCaseIdentifier(identifier) {
6349
+ if (!identifier) return identifier;
6350
+ const leadingUnderscores = identifier.match(/^_+/)?.[0] ?? "";
6351
+ const rest = identifier.slice(leadingUnderscores.length);
6352
+ if (!rest) return identifier;
6353
+ const snakeCase = rest.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase();
6354
+ return leadingUnderscores + snakeCase;
6355
+ }
6356
+ function toDatabaseColumnName(column, dbCasing) {
6357
+ return dbCasing === "snake_case" ? toSnakeCaseIdentifier(column) : column;
6358
+ }
6359
+ function toRuntimeBindingRow(bindName, row, columns, dbCasing) {
6360
+ if (typeof row !== "object" || row === null || Array.isArray(row)) {
6361
+ throw new Error(
6362
+ `NQL mutation binding '${bindName}' returned a non-object row; runtime VALUES materialization requires object rows keyed by projected column name.`
6363
+ );
6364
+ }
6365
+ const source = row;
6366
+ const materialized = {};
6367
+ for (const column of columns) {
6368
+ const dbColumn = toDatabaseColumnName(column, dbCasing);
6369
+ const sourceColumn = Object.hasOwn(source, column) ? column : dbColumn !== column && Object.hasOwn(source, dbColumn) ? dbColumn : void 0;
6370
+ if (sourceColumn === void 0) {
6371
+ throw new Error(
6372
+ `NQL mutation binding '${bindName}' returned a row without projected column '${column}'.`
6373
+ );
6374
+ }
6375
+ materialized[column] = source[sourceColumn];
6376
+ }
6377
+ return materialized;
6378
+ }
6379
+ function createRuntimeBinding(bundle, bindName, rows, dbCasing) {
6380
+ const columns = requireMutationBindingColumns(bundle, bindName);
6381
+ return {
6382
+ columns,
6383
+ rows: rows.map(
6384
+ (row) => toRuntimeBindingRow(bindName, row, columns, dbCasing)
6385
+ )
6386
+ };
6387
+ }
6388
+ function bindingEntryIsFinal(compiledIntent, bindName, boundQuery, sourceBundle) {
6389
+ if (compiledIntent.kind === "query") {
6390
+ return boundQuery === compiledIntent.intent;
6391
+ }
6392
+ return sourceBundle.mutationBindings?.get(bindName) === compiledIntent.intent;
6393
+ }
6394
+ function orderedSequenceStepToProgramStep(step) {
6395
+ const dependencyStep = step;
6396
+ if (step.kind === "query") {
6397
+ return {
6398
+ kind: "query",
6399
+ intent: step.query,
6400
+ ...step.bindName !== void 0 && { bindName: step.bindName },
6401
+ final: step.final,
6402
+ ...dependencyStep.bindingDependencies !== void 0 && {
6403
+ bindingDependencies: dependencyStep.bindingDependencies
6404
+ }
6405
+ };
6406
+ }
6407
+ return {
6408
+ kind: "mutation",
6409
+ intent: step.mutation,
6410
+ ...step.bindName !== void 0 && { bindName: step.bindName },
6411
+ final: step.final,
6412
+ ...dependencyStep.bindingDependencies !== void 0 && {
6413
+ bindingDependencies: dependencyStep.bindingDependencies
6414
+ }
6415
+ };
6416
+ }
6417
+ function snapshotMutationRows(rows) {
6418
+ return rows.map((row) => {
6419
+ if (Array.isArray(row)) {
6420
+ return [...row];
6421
+ }
6422
+ if (row !== null && typeof row === "object") {
6423
+ return { ...row };
6424
+ }
6425
+ return row;
6426
+ });
6427
+ }
6428
+ function assertNqlProgramSteps(compiledIntent, steps) {
6429
+ if (steps.length === 0) {
6430
+ throw new Error("NQL program sequence did not contain any statements.");
6431
+ }
6432
+ const finalIndexes = steps.map((step, index) => step.final ? index : -1).filter((index) => index !== -1);
6433
+ if (finalIndexes.length !== 1 || finalIndexes[0] !== steps.length - 1) {
6434
+ throw new Error(
6435
+ "NQL program sequence must contain exactly one final statement at the end."
6436
+ );
6437
+ }
6438
+ const finalStep = steps.at(-1);
6439
+ if (finalStep === void 0) {
6440
+ throw new Error("NQL program sequence did not contain a final statement.");
6441
+ }
6442
+ if (finalStep.kind !== compiledIntent.kind || finalStep.intent !== compiledIntent.intent) {
6443
+ throw new Error(
6444
+ "NQL program sequence final statement does not match the compiled NQL result."
6445
+ );
6446
+ }
6447
+ const seenBindNames = /* @__PURE__ */ new Set();
6448
+ for (const step of steps) {
6449
+ if (step.bindName === void 0) continue;
6450
+ if (seenBindNames.has(step.bindName)) {
6451
+ throw new Error(
6452
+ `NQL program sequence contains duplicate binding '${step.bindName}'.`
6453
+ );
6454
+ }
6455
+ seenBindNames.add(step.bindName);
6456
+ if (!(compiledIntent.bundle.bindings?.has(step.bindName) ?? false)) {
6457
+ throw new Error(
6458
+ `NQL program sequence references unknown binding '${step.bindName}'.`
6459
+ );
6460
+ }
6461
+ }
6462
+ }
6463
+ function createNqlProgramSteps(compiledIntent) {
6464
+ const sourceBundle = compiledIntent.bundle;
6465
+ if (sourceBundle.nqlProgramSequence !== void 0) {
6466
+ const steps2 = sourceBundle.nqlProgramSequence.map(
6467
+ orderedSequenceStepToProgramStep
6468
+ );
6469
+ assertNqlProgramSteps(compiledIntent, steps2);
6470
+ return steps2;
6471
+ }
6472
+ const bindingEntries = [...sourceBundle.bindings ?? /* @__PURE__ */ new Map()];
6473
+ const steps = bindingEntries.map(
6474
+ ([bindName, boundQuery]) => {
6475
+ const boundMutation = sourceBundle.mutationBindings?.get(bindName);
6476
+ if (boundMutation !== void 0) {
6477
+ return {
6478
+ kind: "mutation",
6479
+ intent: boundMutation,
6480
+ bindName,
6481
+ final: false
6482
+ };
6483
+ }
6484
+ return {
6485
+ kind: "query",
6486
+ intent: boundQuery,
6487
+ bindName,
6488
+ final: false
6489
+ };
6490
+ }
6491
+ );
6492
+ const lastBindingEntry = bindingEntries.at(-1);
6493
+ const finalIsBound = lastBindingEntry !== void 0 && bindingEntryIsFinal(
6494
+ compiledIntent,
6495
+ lastBindingEntry[0],
6496
+ lastBindingEntry[1],
6497
+ sourceBundle
6498
+ );
6499
+ if (finalIsBound) {
6500
+ const lastStep = steps.at(-1);
6501
+ if (lastStep === void 0) return steps;
6502
+ steps[steps.length - 1] = { ...lastStep, final: true };
6503
+ return steps;
6504
+ }
6505
+ if (compiledIntent.kind === "query") {
6506
+ steps.push({
6507
+ kind: "query",
6508
+ intent: compiledIntent.intent,
6509
+ final: true
6510
+ });
6511
+ } else {
6512
+ steps.push({
6513
+ kind: "mutation",
6514
+ intent: compiledIntent.intent,
6515
+ final: true
6516
+ });
6517
+ }
6518
+ assertNqlProgramSteps(compiledIntent, steps);
6519
+ return steps;
6520
+ }
6521
+ function renumberDumpSqlParams(sql2, offset) {
6522
+ if (offset === 0) return sql2;
6523
+ return sql2.replace(/\$(\d+)/g, (_match, num) => {
6524
+ return `$${Number.parseInt(num, 10) + offset}`;
6525
+ });
6526
+ }
6527
+ function joinDumpSequenceSql(sequence) {
6528
+ let parameterOffset = 0;
6529
+ return sequence.map((step) => {
6530
+ const sql2 = renumberDumpSqlParams(step.sql, parameterOffset);
6531
+ parameterOffset += step.params.length;
6532
+ return sql2;
6533
+ }).join(";\n");
6534
+ }
6535
+ function flattenDumpSequenceParams(sequence) {
6536
+ return sequence.flatMap((step) => [...step.params]);
6537
+ }
6538
+ function filterMapByBindingDependencies(source, bindingDependencies) {
6539
+ if (source === void 0) return /* @__PURE__ */ new Map();
6540
+ if (bindingDependencies === void 0) return new Map(source);
6541
+ const filtered = /* @__PURE__ */ new Map();
6542
+ for (const bindName of bindingDependencies) {
6543
+ const value = source.get(bindName);
6544
+ if (value !== void 0) {
6545
+ filtered.set(bindName, value);
6546
+ }
6547
+ }
6548
+ return filtered;
6549
+ }
6550
+ function filterOptionalMapByBindingDependencies(source, bindingDependencies) {
6551
+ const filtered = filterMapByBindingDependencies(source, bindingDependencies);
6552
+ return filtered.size > 0 ? filtered : void 0;
6553
+ }
6554
+ function filterMapByRuntimeBindingDependencies(source, runtimeSourceBindings, bindingDependencies) {
6555
+ if (source === void 0) return /* @__PURE__ */ new Map();
6556
+ if (bindingDependencies === void 0 || runtimeSourceBindings === void 0 || runtimeSourceBindings.size === 0) {
6557
+ return new Map(source);
6558
+ }
6559
+ const dependencies = new Set(bindingDependencies);
6560
+ const filtered = /* @__PURE__ */ new Map();
6561
+ for (const [bindName, value] of source) {
6562
+ if (!runtimeSourceBindings.has(bindName) || dependencies.has(bindName)) {
6563
+ filtered.set(bindName, value);
6564
+ }
6565
+ }
6566
+ return filtered;
6567
+ }
6568
+ function filterOptionalMapByNames(source, names) {
6569
+ if (source === void 0) return void 0;
6570
+ const filtered = /* @__PURE__ */ new Map();
6571
+ for (const name of names) {
6572
+ const value = source.get(name);
6573
+ if (value !== void 0) {
6574
+ filtered.set(name, value);
6575
+ }
6576
+ }
6577
+ return filtered.size > 0 ? filtered : void 0;
6578
+ }
6579
+ function createNqlTag(_schemaDefinition, model, adapter, schemaName, hookStore, onHookError, inTransaction) {
6293
6580
  return function nql(strings, ...values) {
6294
6581
  const assembled = assembleNqlTemplate(strings, values);
6295
6582
  return new NqlBuilderImpl(
@@ -6297,7 +6584,6 @@ function createNqlTag(schemaDefinition, model, adapter, schemaName, hookStore, o
6297
6584
  assembled.params,
6298
6585
  assembled.hasBoundParams,
6299
6586
  assembled.sourceError,
6300
- schemaDefinition,
6301
6587
  model,
6302
6588
  adapter,
6303
6589
  schemaName,
@@ -6313,19 +6599,17 @@ var NqlBuilderImpl = class {
6313
6599
  params;
6314
6600
  hasBoundParams;
6315
6601
  sourceError;
6316
- schemaDefinition;
6317
6602
  model;
6318
6603
  _schemaName;
6319
6604
  adapter;
6320
6605
  hookStore;
6321
6606
  onHookError;
6322
6607
  inTransaction;
6323
- constructor(query, params, hasBoundParams, sourceError, schemaDefinition, model, adapter, schemaName, hookStore, onHookError, inTransaction) {
6608
+ constructor(query, params, hasBoundParams, sourceError, model, adapter, schemaName, hookStore, onHookError, inTransaction) {
6324
6609
  this.query = query;
6325
6610
  this.params = params;
6326
6611
  this.hasBoundParams = hasBoundParams;
6327
6612
  this.sourceError = sourceError;
6328
- this.schemaDefinition = schemaDefinition;
6329
6613
  this.model = model;
6330
6614
  this.adapter = adapter;
6331
6615
  this._schemaName = schemaName;
@@ -6347,7 +6631,7 @@ var NqlBuilderImpl = class {
6347
6631
  };
6348
6632
  const result = nqlCompile(
6349
6633
  this.query,
6350
- this.schemaDefinition,
6634
+ this.model,
6351
6635
  void 0,
6352
6636
  compilerOptions
6353
6637
  );
@@ -6360,7 +6644,6 @@ var NqlBuilderImpl = class {
6360
6644
  if (!bundle) {
6361
6645
  throw new Error("NQL compilation failed: no query AST produced");
6362
6646
  }
6363
- assertNoMutationBindingBodies(bundle);
6364
6647
  if (bundle.query) {
6365
6648
  this._compiled = {
6366
6649
  kind: "query",
@@ -6392,18 +6675,28 @@ var NqlBuilderImpl = class {
6392
6675
  return plan(compiled.intent, this.model);
6393
6676
  }
6394
6677
  plan() {
6395
- return this.planInternal();
6678
+ const compiled = this.compile();
6679
+ if (compiled.kind === "mutation") {
6680
+ throw new Error(
6681
+ "NQL mutations do not have execution plans; use dump() for SQL and parameters."
6682
+ );
6683
+ }
6684
+ return isBindingFinalQuery(compiled.bundle) ? createBindingFinalPlan(compiled.intent) : this.planInternal();
6396
6685
  }
6397
6686
  dump(meta) {
6398
6687
  const compiledIntent = this.compile();
6688
+ if (hasMutationBindingBodies(compiledIntent.bundle)) {
6689
+ return this.dumpNqlProgramSequence(compiledIntent, meta);
6690
+ }
6399
6691
  if (compiledIntent.kind === "mutation") {
6400
6692
  return this.dumpMutation(
6401
- compiledIntent.bundle,
6693
+ this.createFinalNqlStatementBundle(compiledIntent),
6402
6694
  compiledIntent.intent,
6403
6695
  meta
6404
6696
  );
6405
6697
  }
6406
- const planReport = this.planInternal();
6698
+ const bindingFinalQuery = isBindingFinalQuery(compiledIntent.bundle);
6699
+ const planReport = bindingFinalQuery ? createBindingFinalPlan(compiledIntent.intent) : this.planInternal();
6407
6700
  if (!this.adapter) {
6408
6701
  return {
6409
6702
  plan: planReport,
@@ -6412,10 +6705,8 @@ var NqlBuilderImpl = class {
6412
6705
  ...meta !== void 0 && { meta }
6413
6706
  };
6414
6707
  }
6415
- const compiled = hasNqlBindings(compiledIntent.bundle) ? this.adapter.compile(
6416
- compiledIntent.bundle,
6417
- this.nqlBundleCompileOptions()
6418
- ) : this.adapter.compile(planReport);
6708
+ const finalBundle = this.createFinalNqlStatementBundle(compiledIntent);
6709
+ const compiled = bindingFinalQuery || hasNqlBindings(finalBundle) ? this.adapter.compile(finalBundle, this.nqlBundleCompileOptions()) : this.adapter.compile(planReport);
6419
6710
  try {
6420
6711
  return this.adapter.createDump(planReport, compiled, meta);
6421
6712
  } catch (err) {
@@ -6466,6 +6757,268 @@ var NqlBuilderImpl = class {
6466
6757
  ...this._schemaName !== void 0 && { schemaName: this._schemaName }
6467
6758
  };
6468
6759
  }
6760
+ createNqlStatementBundle(statement, bindings, runtimeBindings, sourceBundle, bindingDependencies) {
6761
+ const statementBindings = filterMapByRuntimeBindingDependencies(
6762
+ bindings,
6763
+ sourceBundle.mutationBindings,
6764
+ bindingDependencies
6765
+ );
6766
+ const statementRuntimeBindings = filterMapByBindingDependencies(
6767
+ runtimeBindings,
6768
+ bindingDependencies
6769
+ );
6770
+ const emittedBindingNames = /* @__PURE__ */ new Set([
6771
+ ...statementBindings.keys(),
6772
+ ...statementRuntimeBindings.keys()
6773
+ ]);
6774
+ const statementBindingOutputSchemas = filterOptionalMapByNames(
6775
+ sourceBundle.bindingOutputSchemas,
6776
+ emittedBindingNames
6777
+ );
6778
+ const statementMutationBindings = filterOptionalMapByBindingDependencies(
6779
+ sourceBundle.mutationBindings,
6780
+ bindingDependencies
6781
+ );
6782
+ return {
6783
+ ...statement,
6784
+ ...statementBindings.size > 0 && { bindings: statementBindings },
6785
+ ...statementBindingOutputSchemas !== void 0 && {
6786
+ bindingOutputSchemas: statementBindingOutputSchemas
6787
+ },
6788
+ ...statementMutationBindings !== void 0 && {
6789
+ mutationBindings: statementMutationBindings
6790
+ },
6791
+ ...statementRuntimeBindings.size > 0 && {
6792
+ runtimeBindings: statementRuntimeBindings
6793
+ }
6794
+ };
6795
+ }
6796
+ createFinalNqlStatementBundle(compiledIntent) {
6797
+ const finalStep = createNqlProgramSteps(compiledIntent).at(-1);
6798
+ if (compiledIntent.kind === "query") {
6799
+ return this.createNqlStatementBundle(
6800
+ { query: compiledIntent.intent },
6801
+ compiledIntent.bundle.bindings ?? /* @__PURE__ */ new Map(),
6802
+ compiledIntent.bundle.runtimeBindings ?? /* @__PURE__ */ new Map(),
6803
+ compiledIntent.bundle,
6804
+ finalStep?.bindingDependencies
6805
+ );
6806
+ }
6807
+ return this.createNqlStatementBundle(
6808
+ { mutation: compiledIntent.intent },
6809
+ compiledIntent.bundle.bindings ?? /* @__PURE__ */ new Map(),
6810
+ compiledIntent.bundle.runtimeBindings ?? /* @__PURE__ */ new Map(),
6811
+ compiledIntent.bundle,
6812
+ finalStep?.bindingDependencies
6813
+ );
6814
+ }
6815
+ runMutationStatement(adapter, bundle, intent, inTransaction) {
6816
+ const hasReturning = (intent.returning?.length ?? 0) > 0;
6817
+ return runMutationWithHooks({
6818
+ table: intent.table,
6819
+ intent,
6820
+ hookStore: this.hookStore,
6821
+ onHookError: this.onHookError,
6822
+ schemaName: this._schemaName,
6823
+ inTransaction,
6824
+ prepare: () => {
6825
+ const compiled = adapter.compile(
6826
+ bundle,
6827
+ this.mutationCompileOptions()
6828
+ );
6829
+ return {
6830
+ sql: compiled.sql,
6831
+ parameters: compiled.parameters,
6832
+ execute: async () => {
6833
+ const hookRows = await adapter.execute(compiled);
6834
+ const rawRows = snapshotMutationRows(hookRows);
6835
+ return {
6836
+ rawRows,
6837
+ hookRows,
6838
+ transformedRows: hookRows
6839
+ };
6840
+ },
6841
+ getAfterMutationResult: (result) => hasReturning ? result.hookRows : [],
6842
+ mapAfterMutationResult: (result, transformedRows) => ({
6843
+ rawRows: result.rawRows,
6844
+ hookRows: result.hookRows,
6845
+ transformedRows: [...transformedRows]
6846
+ }),
6847
+ returnAfterMutationResult: hasReturning
6848
+ };
6849
+ }
6850
+ });
6851
+ }
6852
+ async executeNqlProgramSequence(compiledIntent, adapter) {
6853
+ if (!supportsTransactions(adapter)) {
6854
+ throw new ExecutionError({
6855
+ operation: "nql",
6856
+ reason: "NQL programs with mutation bindings require adapter transaction support",
6857
+ fix: "Use an adapter that implements transaction(fn), such as the PostgreSQL adapter."
6858
+ });
6859
+ }
6860
+ return adapter.transaction(async (txAdapter) => {
6861
+ const sourceBundle = compiledIntent.bundle;
6862
+ const priorBindings = /* @__PURE__ */ new Map();
6863
+ const runtimeBindings = /* @__PURE__ */ new Map();
6864
+ const steps = createNqlProgramSteps(compiledIntent);
6865
+ let finalRows = [];
6866
+ for (const step of steps) {
6867
+ if (step.kind === "mutation") {
6868
+ if ((step.intent.returning?.length ?? 0) === 0 && step.bindName) {
6869
+ throw new Error(
6870
+ `NQL mutation binding '${step.bindName}' cannot execute without a RETURNING projection.`
6871
+ );
6872
+ }
6873
+ const statementBundle = this.createNqlStatementBundle(
6874
+ { mutation: step.intent },
6875
+ priorBindings,
6876
+ runtimeBindings,
6877
+ sourceBundle,
6878
+ step.bindingDependencies
6879
+ );
6880
+ const rows = await this.runMutationStatement(
6881
+ txAdapter,
6882
+ statementBundle,
6883
+ step.intent,
6884
+ true
6885
+ );
6886
+ if (step.bindName) {
6887
+ runtimeBindings.set(
6888
+ step.bindName,
6889
+ createRuntimeBinding(
6890
+ sourceBundle,
6891
+ step.bindName,
6892
+ rows.rawRows,
6893
+ txAdapter.dbCasing
6894
+ )
6895
+ );
6896
+ }
6897
+ if (step.final) {
6898
+ finalRows = rows.transformedRows;
6899
+ }
6900
+ } else if (step.final) {
6901
+ const statementBundle = this.createNqlStatementBundle(
6902
+ { query: step.intent },
6903
+ priorBindings,
6904
+ runtimeBindings,
6905
+ sourceBundle,
6906
+ step.bindingDependencies
6907
+ );
6908
+ const compiled = txAdapter.compile(
6909
+ statementBundle,
6910
+ this.nqlBundleCompileOptions()
6911
+ );
6912
+ finalRows = await txAdapter.execute(compiled);
6913
+ }
6914
+ if (step.bindName) {
6915
+ const boundQuery = sourceBundle.bindings?.get(step.bindName);
6916
+ if (boundQuery !== void 0) {
6917
+ priorBindings.set(step.bindName, boundQuery);
6918
+ }
6919
+ }
6920
+ }
6921
+ return finalRows;
6922
+ });
6923
+ }
6924
+ dumpNqlProgramSequence(compiledIntent, meta) {
6925
+ const adapter = this.requireAdapter("dump");
6926
+ const sourceBundle = compiledIntent.bundle;
6927
+ const priorBindings = /* @__PURE__ */ new Map();
6928
+ const runtimeBindings = /* @__PURE__ */ new Map();
6929
+ const sequence = [];
6930
+ const steps = createNqlProgramSteps(compiledIntent);
6931
+ for (const step of steps) {
6932
+ if (step.kind === "mutation") {
6933
+ const statementBundle = this.createNqlStatementBundle(
6934
+ { mutation: step.intent },
6935
+ priorBindings,
6936
+ runtimeBindings,
6937
+ sourceBundle,
6938
+ step.bindingDependencies
6939
+ );
6940
+ const compiled = adapter.compile(
6941
+ statementBundle,
6942
+ this.mutationCompileOptions()
6943
+ );
6944
+ sequence.push({
6945
+ kind: "mutation",
6946
+ ...step.bindName !== void 0 && { bindName: step.bindName },
6947
+ sql: compiled.sql,
6948
+ params: compiled.parameters
6949
+ });
6950
+ if (step.bindName !== void 0) {
6951
+ runtimeBindings.set(step.bindName, {
6952
+ columns: requireMutationBindingColumns(sourceBundle, step.bindName),
6953
+ rows: []
6954
+ });
6955
+ }
6956
+ } else {
6957
+ const statementBundle = this.createNqlStatementBundle(
6958
+ { query: step.intent },
6959
+ priorBindings,
6960
+ runtimeBindings,
6961
+ sourceBundle,
6962
+ step.bindingDependencies
6963
+ );
6964
+ const compiled = adapter.compile(
6965
+ statementBundle,
6966
+ this.nqlBundleCompileOptions()
6967
+ );
6968
+ sequence.push({
6969
+ kind: "query",
6970
+ ...step.bindName !== void 0 && { bindName: step.bindName },
6971
+ sql: compiled.sql,
6972
+ params: compiled.parameters
6973
+ });
6974
+ }
6975
+ if (step.bindName !== void 0) {
6976
+ const boundQuery = sourceBundle.bindings?.get(step.bindName);
6977
+ if (boundQuery !== void 0) {
6978
+ priorBindings.set(step.bindName, boundQuery);
6979
+ }
6980
+ }
6981
+ }
6982
+ const finalStep = steps.at(-1);
6983
+ if (finalStep?.kind === "query") {
6984
+ const planReport = isBindingFinalQuery(compiledIntent.bundle) ? createBindingFinalPlan(finalStep.intent) : this.planInternal();
6985
+ const dumpMeta2 = {
6986
+ compiledAt: /* @__PURE__ */ new Date(),
6987
+ ...this._schemaName !== void 0 && { schema: this._schemaName },
6988
+ ...meta?.queryName !== void 0 && { queryName: meta.queryName },
6989
+ ...meta?.correlationId !== void 0 && {
6990
+ correlationId: meta.correlationId
6991
+ }
6992
+ };
6993
+ return {
6994
+ plan: planReport,
6995
+ sql: joinDumpSequenceSql(sequence),
6996
+ params: flattenDumpSequenceParams(sequence),
6997
+ meta: dumpMeta2,
6998
+ sequence
6999
+ };
7000
+ }
7001
+ if (finalStep?.kind !== "mutation") {
7002
+ throw new Error(
7003
+ "NQL program sequence did not contain a final statement."
7004
+ );
7005
+ }
7006
+ const dumpMeta = {
7007
+ compiledAt: /* @__PURE__ */ new Date(),
7008
+ ...this._schemaName !== void 0 && { schema: this._schemaName },
7009
+ ...meta?.queryName !== void 0 && { queryName: meta.queryName },
7010
+ ...meta?.correlationId !== void 0 && {
7011
+ correlationId: meta.correlationId
7012
+ }
7013
+ };
7014
+ return {
7015
+ sql: joinDumpSequenceSql(sequence),
7016
+ parameters: flattenDumpSequenceParams(sequence),
7017
+ intent: finalStep.intent,
7018
+ meta: dumpMeta,
7019
+ sequence
7020
+ };
7021
+ }
6469
7022
  dumpMutation(bundle, intent, meta) {
6470
7023
  const adapter = this.requireAdapter("dump");
6471
7024
  const compiled = adapter.compile(bundle, this.mutationCompileOptions(meta));
@@ -6492,35 +7045,27 @@ var NqlBuilderImpl = class {
6492
7045
  );
6493
7046
  }
6494
7047
  const compiledIntent = this.compile();
7048
+ if (hasMutationBindingBodies(compiledIntent.bundle)) {
7049
+ return this.executeNqlProgramSequence(compiledIntent, adapter);
7050
+ }
6495
7051
  if (compiledIntent.kind === "mutation") {
6496
- const hasReturning = (compiledIntent.intent.returning?.length ?? 0) > 0;
6497
- return runMutationWithHooks({
6498
- table: compiledIntent.intent.table,
6499
- intent: compiledIntent.intent,
6500
- hookStore: this.hookStore,
6501
- onHookError: this.onHookError,
6502
- schemaName: this._schemaName,
6503
- inTransaction: this.inTransaction,
6504
- prepare: () => {
6505
- const compiled2 = adapter.compile(
6506
- compiledIntent.bundle,
6507
- this.mutationCompileOptions()
6508
- );
6509
- return {
6510
- sql: compiled2.sql,
6511
- parameters: compiled2.parameters,
6512
- execute: () => adapter.execute(compiled2),
6513
- getAfterMutationResult: (result) => hasReturning ? result : [],
6514
- returnAfterMutationResult: hasReturning
6515
- };
6516
- }
6517
- });
7052
+ return (await this.runMutationStatement(
7053
+ adapter,
7054
+ this.createFinalNqlStatementBundle(compiledIntent),
7055
+ compiledIntent.intent,
7056
+ this.inTransaction
7057
+ )).transformedRows;
7058
+ }
7059
+ if (isBindingFinalQuery(compiledIntent.bundle)) {
7060
+ const compiled2 = adapter.compile(
7061
+ this.createFinalNqlStatementBundle(compiledIntent),
7062
+ this.nqlBundleCompileOptions()
7063
+ );
7064
+ return adapter.execute(compiled2);
6518
7065
  }
6519
7066
  const planReport = this.planInternal();
6520
- const compiled = hasNqlBindings(compiledIntent.bundle) ? adapter.compile(
6521
- compiledIntent.bundle,
6522
- this.nqlBundleCompileOptions()
6523
- ) : adapter.compile(planReport);
7067
+ const finalBundle = this.createFinalNqlStatementBundle(compiledIntent);
7068
+ const compiled = hasNqlBindings(finalBundle) ? adapter.compile(finalBundle, this.nqlBundleCompileOptions()) : adapter.compile(planReport);
6524
7069
  return adapter.execute(compiled);
6525
7070
  }
6526
7071
  async run() {
@@ -10422,6 +10967,8 @@ var POSTGRESQL_CAPABILITIES = {
10422
10967
  supportsJsonType: true,
10423
10968
  supportsJsonOperators: true,
10424
10969
  // PG: ->, ->>, @>, <@, ?, #>, #>>
10970
+ supportsRowLevelLocks: true,
10971
+ supportsLockWaitPolicies: true,
10425
10972
  supportsSchemas: true,
10426
10973
  // Include Strategy Capabilities (CORE-006)
10427
10974
  supportsLateralJoin: true,
@@ -10466,6 +11013,9 @@ var MYSQL_CAPABILITIES = {
10466
11013
  supportsJsonType: true,
10467
11014
  supportsJsonOperators: false,
10468
11015
  // MySQL uses JSON_EXTRACT() functions, not operators
11016
+ // Coarse PostgreSQL-shaped lock flags; revisit MySQL with per-strength caps.
11017
+ supportsRowLevelLocks: false,
11018
+ supportsLockWaitPolicies: false,
10469
11019
  supportsSchemas: true,
10470
11020
  // MySQL uses database as schema
10471
11021
  // Include Strategy Capabilities (CORE-006)
@@ -10496,6 +11046,8 @@ var SQLITE_CAPABILITIES = {
10496
11046
  // SQLite 3.38+ (JSON1 extension)
10497
11047
  supportsJsonOperators: false,
10498
11048
  // SQLite uses json_extract() functions
11049
+ supportsRowLevelLocks: false,
11050
+ supportsLockWaitPolicies: false,
10499
11051
  supportsSchemas: false,
10500
11052
  // SQLite uses ATTACH for multiple databases
10501
11053
  // Include Strategy Capabilities (CORE-006)
@@ -10524,6 +11076,8 @@ var DUCKDB_CAPABILITIES = {
10524
11076
  supportsJsonType: true,
10525
11077
  supportsJsonOperators: false,
10526
11078
  // DuckDB uses json_extract() style
11079
+ supportsRowLevelLocks: false,
11080
+ supportsLockWaitPolicies: false,
10527
11081
  supportsSchemas: true,
10528
11082
  // Include Strategy Capabilities (CORE-006)
10529
11083
  supportsLateralJoin: true,
@@ -10553,6 +11107,9 @@ var MSSQL_CAPABILITIES = {
10553
11107
  // SQL Server 2016+
10554
11108
  supportsJsonOperators: false,
10555
11109
  // MSSQL uses JSON_VALUE/JSON_QUERY functions
11110
+ // Coarse PostgreSQL-shaped lock flags; revisit MSSQL hints with per-strength caps.
11111
+ supportsRowLevelLocks: false,
11112
+ supportsLockWaitPolicies: false,
10556
11113
  supportsSchemas: true,
10557
11114
  // Include Strategy Capabilities (CORE-006)
10558
11115
  supportsLateralJoin: true,
@@ -10610,6 +11167,8 @@ function createDialectCapabilities(overrides, options) {
10610
11167
  supportsRangeTypes: false,
10611
11168
  supportsJsonType: false,
10612
11169
  supportsJsonOperators: false,
11170
+ supportsRowLevelLocks: false,
11171
+ supportsLockWaitPolicies: false,
10613
11172
  supportsSchemas: false,
10614
11173
  supportsLateralJoin: false,
10615
11174
  supportsJsonAgg: false,