@dbsp/nql 1.6.0 → 1.7.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.d.ts +48 -42
- package/dist/index.js +509 -54
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -43,6 +43,30 @@ var NqlSemanticException = class extends Error {
|
|
|
43
43
|
this.relatedSymbol = relatedSymbol;
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
+
var DEFAULT_RELATION_TARGET_COLUMN = "id";
|
|
47
|
+
function relationForeignKeys(relation) {
|
|
48
|
+
if (!relation) return void 0;
|
|
49
|
+
const columns = toColumnList(relation.foreignKey);
|
|
50
|
+
return columns.length > 0 ? columns : void 0;
|
|
51
|
+
}
|
|
52
|
+
function relationCardinality(relation) {
|
|
53
|
+
return relation?.type === "hasMany" || relation?.type === "belongsToMany" ? "many" : "one";
|
|
54
|
+
}
|
|
55
|
+
function scalarRelationJoinColumns(relation) {
|
|
56
|
+
if (!relation) return void 0;
|
|
57
|
+
if (relation.type !== "belongsTo" && relation.type !== "hasOne" && relation.type !== "hasMany") {
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
const fkColumns = relationForeignKeys(relation);
|
|
61
|
+
if (!fkColumns) return void 0;
|
|
62
|
+
const sourceKeys = toColumnList(relation.sourceKey);
|
|
63
|
+
const targetKeys = toColumnList(relation.targetKey);
|
|
64
|
+
const sourceJoinColumn = relation.type === "belongsTo" ? fkColumns : sourceKeys.length > 0 ? sourceKeys : [DEFAULT_RELATION_TARGET_COLUMN];
|
|
65
|
+
const targetJoinColumn = relation.type === "belongsTo" ? targetKeys.length > 0 ? targetKeys : [DEFAULT_RELATION_TARGET_COLUMN] : fkColumns;
|
|
66
|
+
return { sourceJoinColumn, targetJoinColumn };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/compiler/column-validator.ts
|
|
46
70
|
var ColumnValidator = class _ColumnValidator {
|
|
47
71
|
constructor(schema) {
|
|
48
72
|
this.schema = schema;
|
|
@@ -90,6 +114,12 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
90
114
|
if (virtualColumns) return virtualColumns;
|
|
91
115
|
return this.schema.getTable(name)?.columns.map((column) => column.name);
|
|
92
116
|
}
|
|
117
|
+
getPhysicalTableColumns(name) {
|
|
118
|
+
return this.schema.getTable(name)?.columns.map((column) => column.name);
|
|
119
|
+
}
|
|
120
|
+
getPseudoColumns(name) {
|
|
121
|
+
return this.schema.getTable(name)?.pseudoColumns ?? [];
|
|
122
|
+
}
|
|
93
123
|
hasPhysicalTable(name) {
|
|
94
124
|
return !this.virtualBindingTables.has(name) && !!this.schema.getTable(name);
|
|
95
125
|
}
|
|
@@ -233,10 +263,54 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
233
263
|
if (!sourceTable) {
|
|
234
264
|
return "the binding source table could not be proven";
|
|
235
265
|
}
|
|
266
|
+
const recursiveReason = this.explainVirtualBindingRecursiveScalarRelationRejection(
|
|
267
|
+
bindingName,
|
|
268
|
+
relationName,
|
|
269
|
+
sourceTable,
|
|
270
|
+
metadata
|
|
271
|
+
);
|
|
272
|
+
if (recursiveReason) return recursiveReason;
|
|
236
273
|
const relation = this.getRelation(sourceTable, relationName);
|
|
237
274
|
if (!relation) {
|
|
238
275
|
return `relation '${relationName}' is not declared on source table '${sourceTable}'`;
|
|
239
276
|
}
|
|
277
|
+
if (relation.type === "belongsToMany") {
|
|
278
|
+
if (relation.recursive !== void 0 || relation.source !== void 0 && relation.source === relation.target) {
|
|
279
|
+
return `relation '${relationName}' is recursive/self-referential; binding relation columns for recursive relations require recursive CTE handling and are not supported (ref-#193)`;
|
|
280
|
+
}
|
|
281
|
+
if (typeof relation.through !== "string" || relation.through.length === 0) {
|
|
282
|
+
return `relation '${relationName}' is many-to-many but does not declare a resolvable junction table (ref-#192)`;
|
|
283
|
+
}
|
|
284
|
+
const sourceColumns2 = toColumnList(relation.sourceKey).length > 0 ? toColumnList(relation.sourceKey) : ["id"];
|
|
285
|
+
const throughSourceColumns = toColumnList(relation.foreignKey);
|
|
286
|
+
const throughTargetColumns = toColumnList(relation.otherKey);
|
|
287
|
+
const targetColumns = toColumnList(relation.targetKey).length > 0 ? toColumnList(relation.targetKey) : ["id"];
|
|
288
|
+
if (sourceColumns2.length !== 1) {
|
|
289
|
+
return `relation '${relationName}' source key is composite; binding many-to-many relation columns require a single source key column (ref-#179)`;
|
|
290
|
+
}
|
|
291
|
+
if (throughSourceColumns.length !== 1) {
|
|
292
|
+
return `relation '${relationName}' junction source FK is composite or missing; binding many-to-many relation columns require a single junction source FK column (ref-#179)`;
|
|
293
|
+
}
|
|
294
|
+
if (throughTargetColumns.length !== 1) {
|
|
295
|
+
return `relation '${relationName}' junction target FK is composite or missing; binding many-to-many relation columns require a single junction target FK column (ref-#179)`;
|
|
296
|
+
}
|
|
297
|
+
if (targetColumns.length !== 1) {
|
|
298
|
+
return `relation '${relationName}' target key is composite; binding many-to-many relation columns require a single target key column (ref-#179)`;
|
|
299
|
+
}
|
|
300
|
+
const [sourceColumn] = sourceColumns2;
|
|
301
|
+
if (sourceColumn === void 0) {
|
|
302
|
+
return `relation '${relationName}' source key is missing; binding many-to-many relation columns require a single source key column (ref-#179)`;
|
|
303
|
+
}
|
|
304
|
+
const directProjection = metadata.directProjectionLineage?.find(
|
|
305
|
+
(projection) => projection.sourceTable === sourceTable && _ColumnValidator.columnsMatch(projection.sourceColumn, sourceColumn)
|
|
306
|
+
);
|
|
307
|
+
if (!directProjection) {
|
|
308
|
+
const available3 = this.virtualBindingTables.get(bindingName)?.join(", ");
|
|
309
|
+
return `relation '${relationName}' source key column '${sourceColumn}' is not projected as a direct source-column projection by binding '${bindingName}'${available3 ? ` (available columns: ${available3})` : ""}`;
|
|
310
|
+
}
|
|
311
|
+
const available2 = this.virtualBindingTables.get(bindingName)?.join(", ");
|
|
312
|
+
return `relation '${relationName}' many-to-many source column '${sourceColumn}' is not available through binding '${bindingName}'${available2 ? ` (available columns: ${available2})` : ""}`;
|
|
313
|
+
}
|
|
240
314
|
const unsupportedReason = explainUnsupportedNqlBindingIncludeHop(
|
|
241
315
|
relationName,
|
|
242
316
|
relation
|
|
@@ -257,6 +331,64 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
257
331
|
const available = this.virtualBindingTables.get(bindingName)?.join(", ");
|
|
258
332
|
return `relation '${relationName}' source columns '${sourceColumns.join(", ")}' are not available through binding '${bindingName}'${available ? ` (available columns: ${available})` : ""}`;
|
|
259
333
|
}
|
|
334
|
+
static recursiveDirection(value) {
|
|
335
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
336
|
+
const direction = value.direction;
|
|
337
|
+
if (direction === "up" || direction === "ancestors") return "up";
|
|
338
|
+
if (direction === "down" || direction === "descendants") return "down";
|
|
339
|
+
return void 0;
|
|
340
|
+
}
|
|
341
|
+
explainVirtualBindingRecursiveScalarRelationRejection(bindingName, relationName, sourceTable, metadata) {
|
|
342
|
+
const lowerRelationName = relationName.toLowerCase();
|
|
343
|
+
const pseudoColumn = this.getPseudoColumns(sourceTable).find(
|
|
344
|
+
(candidate) => candidate.ascendantKeyword?.toLowerCase() === lowerRelationName || candidate.descendantKeyword?.toLowerCase() === lowerRelationName
|
|
345
|
+
);
|
|
346
|
+
if (!pseudoColumn) return void 0;
|
|
347
|
+
const direction = pseudoColumn.ascendantKeyword?.toLowerCase() === lowerRelationName ? "up" : "down";
|
|
348
|
+
const recursiveRelations = this.getRelationsFrom(sourceTable).filter(
|
|
349
|
+
(relation) => relation.recursive !== void 0 && relation.source === relation.target && relation.source === sourceTable && _ColumnValidator.recursiveDirection(relation.recursive) === direction
|
|
350
|
+
);
|
|
351
|
+
if (recursiveRelations.length === 0) {
|
|
352
|
+
return `recursive traversal '${relationName}' is missing recursive relation metadata for direction '${direction}' (ref-#193)`;
|
|
353
|
+
}
|
|
354
|
+
for (const relation of recursiveRelations) {
|
|
355
|
+
const relationFkColumns = toColumnList(relation.foreignKey);
|
|
356
|
+
const fkColumns = relationFkColumns.length > 0 ? relationFkColumns : toColumnList(pseudoColumn.foreignKeyColumn);
|
|
357
|
+
if (fkColumns.length !== 1) {
|
|
358
|
+
return `relation '${relation.name}' self-ref FK is composite or missing; binding recursive relation columns require a single self-ref FK column (ref-#193)`;
|
|
359
|
+
}
|
|
360
|
+
const relationTargetColumns = toColumnList(relation.targetKey);
|
|
361
|
+
const targetColumns = relationTargetColumns.length > 0 ? relationTargetColumns : pseudoColumn.targetColumn !== void 0 ? [pseudoColumn.targetColumn] : [DEFAULT_RELATION_TARGET_COLUMN];
|
|
362
|
+
if (targetColumns.length !== 1) {
|
|
363
|
+
return `relation '${relation.name}' target key is composite; binding recursive relation columns require a single target key column (ref-#193)`;
|
|
364
|
+
}
|
|
365
|
+
const selfRefColumn = fkColumns[0];
|
|
366
|
+
const targetKeyColumn = targetColumns[0];
|
|
367
|
+
if (selfRefColumn === void 0 || targetKeyColumn === void 0) {
|
|
368
|
+
return `relation '${relation.name}' recursive metadata is missing a single-column seed; binding recursive relation columns require a single self-ref FK and target key column (ref-#193)`;
|
|
369
|
+
}
|
|
370
|
+
if (pseudoColumn.foreignKeyColumn !== void 0 && !_ColumnValidator.columnsMatch(
|
|
371
|
+
pseudoColumn.foreignKeyColumn,
|
|
372
|
+
selfRefColumn
|
|
373
|
+
) || pseudoColumn.targetColumn !== void 0 && !_ColumnValidator.columnsMatch(
|
|
374
|
+
pseudoColumn.targetColumn,
|
|
375
|
+
targetKeyColumn
|
|
376
|
+
)) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const seedColumn = direction === "up" ? selfRefColumn : targetKeyColumn;
|
|
380
|
+
const directProjection = metadata.directProjectionLineage?.find(
|
|
381
|
+
(projection) => projection.sourceTable === sourceTable && _ColumnValidator.columnsMatch(projection.sourceColumn, seedColumn)
|
|
382
|
+
);
|
|
383
|
+
if (!directProjection) {
|
|
384
|
+
const available = this.virtualBindingTables.get(bindingName)?.join(", ");
|
|
385
|
+
const seedLabel = direction === "up" ? "self-ref FK column" : "target key column";
|
|
386
|
+
return `recursive traversal '${relationName}' ${seedLabel} '${seedColumn}' is not projected as a direct source-column projection by binding '${bindingName}'${available ? ` (available columns: ${available})` : ""} (ref-#193)`;
|
|
387
|
+
}
|
|
388
|
+
return void 0;
|
|
389
|
+
}
|
|
390
|
+
return `recursive traversal '${relationName}' pseudo metadata does not match a single-column recursive self-reference (ref-#193)`;
|
|
391
|
+
}
|
|
260
392
|
/**
|
|
261
393
|
* Convert camelCase to snake_case for column name matching.
|
|
262
394
|
* NQL queries may use either form (e.g., viewCount or view_count).
|
|
@@ -288,6 +420,14 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
288
420
|
(c) => _ColumnValidator.columnsMatch(column, c.name)
|
|
289
421
|
)?.name;
|
|
290
422
|
}
|
|
423
|
+
resolvePhysicalColumnName(table, column) {
|
|
424
|
+
if (column === "*") return column;
|
|
425
|
+
const tableInfo = this.schema.getTable(table);
|
|
426
|
+
if (!tableInfo) return void 0;
|
|
427
|
+
return tableInfo.columns.find(
|
|
428
|
+
(c) => _ColumnValidator.columnsMatch(column, c.name)
|
|
429
|
+
)?.name;
|
|
430
|
+
}
|
|
291
431
|
validateColumn(table, column) {
|
|
292
432
|
if (column === "*") return;
|
|
293
433
|
const virtualColumns = this.virtualBindingTables.get(table);
|
|
@@ -354,28 +494,6 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
354
494
|
);
|
|
355
495
|
}
|
|
356
496
|
};
|
|
357
|
-
var DEFAULT_RELATION_TARGET_COLUMN = "id";
|
|
358
|
-
function relationForeignKeys(relation) {
|
|
359
|
-
if (!relation) return void 0;
|
|
360
|
-
const columns = toColumnList(relation.foreignKey);
|
|
361
|
-
return columns.length > 0 ? columns : void 0;
|
|
362
|
-
}
|
|
363
|
-
function relationCardinality(relation) {
|
|
364
|
-
return relation?.type === "hasMany" || relation?.type === "belongsToMany" ? "many" : "one";
|
|
365
|
-
}
|
|
366
|
-
function scalarRelationJoinColumns(relation) {
|
|
367
|
-
if (!relation) return void 0;
|
|
368
|
-
if (relation.type !== "belongsTo" && relation.type !== "hasOne" && relation.type !== "hasMany") {
|
|
369
|
-
return void 0;
|
|
370
|
-
}
|
|
371
|
-
const fkColumns = relationForeignKeys(relation);
|
|
372
|
-
if (!fkColumns) return void 0;
|
|
373
|
-
const sourceKeys = toColumnList(relation.sourceKey);
|
|
374
|
-
const targetKeys = toColumnList(relation.targetKey);
|
|
375
|
-
const sourceJoinColumn = relation.type === "belongsTo" ? fkColumns : sourceKeys.length > 0 ? sourceKeys : [DEFAULT_RELATION_TARGET_COLUMN];
|
|
376
|
-
const targetJoinColumn = relation.type === "belongsTo" ? targetKeys.length > 0 ? targetKeys : [DEFAULT_RELATION_TARGET_COLUMN] : fkColumns;
|
|
377
|
-
return { sourceJoinColumn, targetJoinColumn };
|
|
378
|
-
}
|
|
379
497
|
var FORBIDDEN_PARAM_NAMES = /* @__PURE__ */ new Set([
|
|
380
498
|
"__proto__",
|
|
381
499
|
"constructor",
|
|
@@ -652,6 +770,43 @@ function resolveBindingRelationColumn(ctx, bindingName, relationPath, selectedCo
|
|
|
652
770
|
hops
|
|
653
771
|
};
|
|
654
772
|
}
|
|
773
|
+
function resolveBindingRecursiveRelationColumn(ctx, bindingName, traversal, selectedColumn, maxDepth) {
|
|
774
|
+
if (!isBindingTable(ctx, bindingName)) return void 0;
|
|
775
|
+
const traversalName = traversal.toLowerCase();
|
|
776
|
+
const isRecursiveTraversal = ctx.recursiveKeywords.has(traversalName) || traversalName === "ascendant" || traversalName === "descendant";
|
|
777
|
+
const actualBindingName = bindingName;
|
|
778
|
+
if (!ctx.validator) {
|
|
779
|
+
if (!isRecursiveTraversal) return void 0;
|
|
780
|
+
throwBindingRelationColumn182(
|
|
781
|
+
actualBindingName,
|
|
782
|
+
traversalName,
|
|
783
|
+
"model metadata is not available"
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
const virtualRelation = ctx.validator.getVirtualBindingScalarRelation(
|
|
787
|
+
actualBindingName,
|
|
788
|
+
traversalName
|
|
789
|
+
);
|
|
790
|
+
if (!isRecursiveTraversal && !virtualRelation?.recursive) {
|
|
791
|
+
return void 0;
|
|
792
|
+
}
|
|
793
|
+
if (!virtualRelation?.recursive) {
|
|
794
|
+
const reason = ctx.validator.explainVirtualBindingScalarRelationRejection(
|
|
795
|
+
actualBindingName,
|
|
796
|
+
traversalName
|
|
797
|
+
);
|
|
798
|
+
throwBindingRelationColumn182(actualBindingName, traversalName, reason);
|
|
799
|
+
}
|
|
800
|
+
ctx.validator.validateColumn(virtualRelation.targetTable, selectedColumn);
|
|
801
|
+
if (maxDepth === void 0) return virtualRelation;
|
|
802
|
+
return {
|
|
803
|
+
...virtualRelation,
|
|
804
|
+
recursive: {
|
|
805
|
+
...virtualRelation.recursive,
|
|
806
|
+
maxDepth
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}
|
|
655
810
|
function assertNoBindingRelationPath(ctx, bindingName, path) {
|
|
656
811
|
if (!isBindingTable(ctx, bindingName)) return;
|
|
657
812
|
throw new NqlSemanticException(
|
|
@@ -1287,6 +1442,12 @@ var PORTABLE_BINDING_FINAL_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
1287
1442
|
...NQL_SELECT_AGGREGATE_FUNCTIONS,
|
|
1288
1443
|
...NQL_SELECT_VALUE_FUNCTIONS
|
|
1289
1444
|
]);
|
|
1445
|
+
var UnresolvedSelectAllOutputSchemaError = class extends NqlSemanticException {
|
|
1446
|
+
constructor(message) {
|
|
1447
|
+
super(NqlErrorCodes.SEM_INVALID_SYNTAX, message);
|
|
1448
|
+
this.name = "UnresolvedSelectAllOutputSchemaError";
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1290
1451
|
function compileNestedQuery(query, ctx, fns, bindings) {
|
|
1291
1452
|
const savedContext = {
|
|
1292
1453
|
currentFromTable: ctx.currentFromTable,
|
|
@@ -1373,6 +1534,21 @@ function relationForeignKeysOnSource(relation) {
|
|
|
1373
1534
|
const fkColumns = toColumnList(relation.foreignKey);
|
|
1374
1535
|
return fkColumns.length > 0 ? fkColumns : void 0;
|
|
1375
1536
|
}
|
|
1537
|
+
function recursiveDirection(recursive) {
|
|
1538
|
+
if (recursive === null || typeof recursive !== "object") return void 0;
|
|
1539
|
+
const direction = recursive.direction;
|
|
1540
|
+
if (direction === "up" || direction === "ancestors") return "up";
|
|
1541
|
+
if (direction === "down" || direction === "descendants") return "down";
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
function recursiveMaxDepth(recursive) {
|
|
1545
|
+
if (recursive === null || typeof recursive !== "object") return void 0;
|
|
1546
|
+
const maxDepth = recursive.maxDepth;
|
|
1547
|
+
return typeof maxDepth === "number" && Number.isSafeInteger(maxDepth) && maxDepth > 0 ? maxDepth : void 0;
|
|
1548
|
+
}
|
|
1549
|
+
function pseudoColumnMatchesSelfRef(pseudoColumn, sourceTable, selfRefColumn, targetKeyColumn) {
|
|
1550
|
+
return (pseudoColumn.table === void 0 || pseudoColumn.table === sourceTable) && (pseudoColumn.foreignKeyColumn === void 0 || columnsMatch(pseudoColumn.foreignKeyColumn, selfRefColumn)) && (pseudoColumn.targetColumn === void 0 || columnsMatch(pseudoColumn.targetColumn, targetKeyColumn));
|
|
1551
|
+
}
|
|
1376
1552
|
function unsafeBindingRelationReason(intent, ctx, bindingDependencies) {
|
|
1377
1553
|
if (!ctx.validator) return "model metadata is not available";
|
|
1378
1554
|
if (!ctx.validator.hasQualifiedRelationLookup()) {
|
|
@@ -1443,8 +1619,119 @@ function virtualRelationForBinding(relation, sourceTable, directProjectionLineag
|
|
|
1443
1619
|
cardinality: "one"
|
|
1444
1620
|
};
|
|
1445
1621
|
}
|
|
1622
|
+
function singleResolvedColumn(columns) {
|
|
1623
|
+
return columns.length === 1 ? columns[0] : void 0;
|
|
1624
|
+
}
|
|
1625
|
+
function manyToManyVirtualRelationForBinding(relation, sourceTable, directProjectionLineage) {
|
|
1626
|
+
if (relation?.type !== "belongsToMany") return void 0;
|
|
1627
|
+
if (typeof relation.through !== "string" || relation.through.length === 0 || relation.recursive !== void 0 || relation.source !== void 0 && relation.source === relation.target) {
|
|
1628
|
+
return void 0;
|
|
1629
|
+
}
|
|
1630
|
+
const sourceKeys = toColumnList(relation.sourceKey);
|
|
1631
|
+
const sourceColumn = singleResolvedColumn(
|
|
1632
|
+
sourceKeys.length > 0 ? sourceKeys : [DEFAULT_RELATION_TARGET_COLUMN]
|
|
1633
|
+
);
|
|
1634
|
+
const targetKeys = toColumnList(relation.targetKey);
|
|
1635
|
+
const targetColumn = singleResolvedColumn(
|
|
1636
|
+
targetKeys.length > 0 ? targetKeys : [DEFAULT_RELATION_TARGET_COLUMN]
|
|
1637
|
+
);
|
|
1638
|
+
const throughSourceColumn = singleResolvedColumn(
|
|
1639
|
+
toColumnList(relation.foreignKey)
|
|
1640
|
+
);
|
|
1641
|
+
const throughTargetColumn = singleResolvedColumn(
|
|
1642
|
+
toColumnList(relation.otherKey)
|
|
1643
|
+
);
|
|
1644
|
+
if (!sourceColumn || !targetColumn || !throughSourceColumn || !throughTargetColumn) {
|
|
1645
|
+
return void 0;
|
|
1646
|
+
}
|
|
1647
|
+
const sourceProjection = findDirectSourceProjection(
|
|
1648
|
+
sourceTable,
|
|
1649
|
+
sourceColumn,
|
|
1650
|
+
directProjectionLineage
|
|
1651
|
+
);
|
|
1652
|
+
if (!sourceProjection) return void 0;
|
|
1653
|
+
return {
|
|
1654
|
+
relation: relation.name,
|
|
1655
|
+
sourceTable,
|
|
1656
|
+
targetTable: relation.target,
|
|
1657
|
+
sourceColumn: [sourceProjection.outputColumn],
|
|
1658
|
+
targetColumn: [targetColumn],
|
|
1659
|
+
hops: [],
|
|
1660
|
+
through: relation.through,
|
|
1661
|
+
throughSourceColumn,
|
|
1662
|
+
throughTargetColumn,
|
|
1663
|
+
cardinality: "many",
|
|
1664
|
+
relationType: "manyToMany"
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function recursiveVirtualRelationForBinding(relation, sourceTable, directProjectionLineage, pseudoColumns) {
|
|
1668
|
+
const direction = recursiveDirection(relation?.recursive);
|
|
1669
|
+
if (!relation || !direction) return void 0;
|
|
1670
|
+
if (relation.source !== relation.target || relation.source !== sourceTable) {
|
|
1671
|
+
return void 0;
|
|
1672
|
+
}
|
|
1673
|
+
const fkColumns = toColumnList(relation.foreignKey);
|
|
1674
|
+
const targetKeys = toColumnList(relation.targetKey);
|
|
1675
|
+
const pseudoColumn = pseudoColumns.find((candidate) => {
|
|
1676
|
+
const candidateFkColumns = fkColumns.length > 0 ? fkColumns : toColumnList(candidate.foreignKeyColumn);
|
|
1677
|
+
const candidateTargetColumns = targetKeys.length > 0 ? targetKeys : candidate.targetColumn !== void 0 ? [candidate.targetColumn] : [DEFAULT_RELATION_TARGET_COLUMN];
|
|
1678
|
+
const candidateSelfRefColumn = singleResolvedColumn(candidateFkColumns);
|
|
1679
|
+
const candidateTargetKeyColumn = singleResolvedColumn(
|
|
1680
|
+
candidateTargetColumns
|
|
1681
|
+
);
|
|
1682
|
+
return candidateSelfRefColumn !== void 0 && candidateTargetKeyColumn !== void 0 && pseudoColumnMatchesSelfRef(
|
|
1683
|
+
candidate,
|
|
1684
|
+
sourceTable,
|
|
1685
|
+
candidateSelfRefColumn,
|
|
1686
|
+
candidateTargetKeyColumn
|
|
1687
|
+
);
|
|
1688
|
+
});
|
|
1689
|
+
if (!pseudoColumn) return void 0;
|
|
1690
|
+
const selfRefColumn = singleResolvedColumn(
|
|
1691
|
+
fkColumns.length > 0 ? fkColumns : toColumnList(pseudoColumn.foreignKeyColumn)
|
|
1692
|
+
);
|
|
1693
|
+
const targetKeyColumn = singleResolvedColumn(
|
|
1694
|
+
targetKeys.length > 0 ? targetKeys : pseudoColumn.targetColumn !== void 0 ? [pseudoColumn.targetColumn] : [DEFAULT_RELATION_TARGET_COLUMN]
|
|
1695
|
+
);
|
|
1696
|
+
if (!selfRefColumn || !targetKeyColumn) return void 0;
|
|
1697
|
+
const relationName = direction === "up" ? pseudoColumn.ascendantKeyword : pseudoColumn.descendantKeyword;
|
|
1698
|
+
if (!relationName) return void 0;
|
|
1699
|
+
const seedColumn = direction === "up" ? selfRefColumn : targetKeyColumn;
|
|
1700
|
+
const sourceProjection = findDirectSourceProjection(
|
|
1701
|
+
sourceTable,
|
|
1702
|
+
seedColumn,
|
|
1703
|
+
directProjectionLineage
|
|
1704
|
+
);
|
|
1705
|
+
if (!sourceProjection) return void 0;
|
|
1706
|
+
return {
|
|
1707
|
+
relation: relationName.toLowerCase(),
|
|
1708
|
+
sourceTable,
|
|
1709
|
+
targetTable: relation.target,
|
|
1710
|
+
sourceColumn: [sourceProjection.outputColumn],
|
|
1711
|
+
targetColumn: [direction === "up" ? targetKeyColumn : selfRefColumn],
|
|
1712
|
+
hops: [],
|
|
1713
|
+
cardinality: "many",
|
|
1714
|
+
relationType: relation.type,
|
|
1715
|
+
recursive: {
|
|
1716
|
+
direction,
|
|
1717
|
+
maxDepth: recursiveMaxDepth(relation.recursive) ?? 10,
|
|
1718
|
+
selfRefColumn,
|
|
1719
|
+
targetKeyColumn
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1446
1723
|
function scalarVirtualRelationForBinding(relation, sourceTable, directProjectionLineage) {
|
|
1447
1724
|
if (!relation) return void 0;
|
|
1725
|
+
if (relation.type === "belongsToMany") {
|
|
1726
|
+
return manyToManyVirtualRelationForBinding(
|
|
1727
|
+
relation,
|
|
1728
|
+
sourceTable,
|
|
1729
|
+
directProjectionLineage
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
if (relation.recursive !== void 0 || relation.source !== void 0 && relation.source === relation.target) {
|
|
1733
|
+
return void 0;
|
|
1734
|
+
}
|
|
1448
1735
|
if (relation.type !== "belongsTo" && relation.type !== "hasOne" && relation.type !== "hasMany") {
|
|
1449
1736
|
return void 0;
|
|
1450
1737
|
}
|
|
@@ -1493,6 +1780,13 @@ function getBindingRelationFilterMetadata(intent, ctx, outputSchema, bindingDepe
|
|
|
1493
1780
|
);
|
|
1494
1781
|
for (const relationName of relationNames ?? []) {
|
|
1495
1782
|
const relation = ctx.validator?.getRelation(sourceTable, relationName);
|
|
1783
|
+
const recursiveRelation = recursiveVirtualRelationForBinding(
|
|
1784
|
+
relation,
|
|
1785
|
+
sourceTable,
|
|
1786
|
+
outputSchema.directProjectionLineage ?? [],
|
|
1787
|
+
ctx.validator?.getPseudoColumns(sourceTable) ?? []
|
|
1788
|
+
);
|
|
1789
|
+
if (recursiveRelation) scalarRelations.push(recursiveRelation);
|
|
1496
1790
|
const virtualRelation = virtualRelationForBinding(
|
|
1497
1791
|
relation,
|
|
1498
1792
|
sourceTable,
|
|
@@ -1527,6 +1821,15 @@ function validateBindingFinalPath(expr, ctx, bindingName) {
|
|
|
1527
1821
|
}
|
|
1528
1822
|
const firstSegment = segments[0];
|
|
1529
1823
|
if (ctx.pseudoColumnKeywords.has(firstSegment.toLowerCase())) {
|
|
1824
|
+
if (segments.length === 2 && resolveBindingRecursiveRelationColumn(
|
|
1825
|
+
ctx,
|
|
1826
|
+
bindingName,
|
|
1827
|
+
firstSegment,
|
|
1828
|
+
segments[1],
|
|
1829
|
+
expr.depthHint
|
|
1830
|
+
)) {
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1530
1833
|
assertNoBindingRelationConstruct(
|
|
1531
1834
|
ctx,
|
|
1532
1835
|
bindingName,
|
|
@@ -1757,12 +2060,20 @@ function validateBindingFinalSelectClause(clause, ctx, bindingName) {
|
|
|
1757
2060
|
const segments = item.expression.segments;
|
|
1758
2061
|
const firstSegment = segments[0];
|
|
1759
2062
|
if (ctx.pseudoColumnKeywords.has(firstSegment.toLowerCase())) {
|
|
1760
|
-
|
|
2063
|
+
if (segments.length !== 2 || !resolveBindingRecursiveRelationColumn(
|
|
1761
2064
|
ctx,
|
|
1762
2065
|
bindingName,
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
2066
|
+
firstSegment,
|
|
2067
|
+
segments[1],
|
|
2068
|
+
item.expression.depthHint
|
|
2069
|
+
)) {
|
|
2070
|
+
assertNoBindingRelationConstruct(
|
|
2071
|
+
ctx,
|
|
2072
|
+
bindingName,
|
|
2073
|
+
"use pseudo-column traversals",
|
|
2074
|
+
firstSegment
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
1766
2077
|
} else {
|
|
1767
2078
|
resolveBindingRelationColumn(
|
|
1768
2079
|
ctx,
|
|
@@ -2155,8 +2466,7 @@ function resolveSourceOutputColumns(intent, ctx, bindingName) {
|
|
|
2155
2466
|
if (bindingColumns2 !== void 0) return bindingColumns2;
|
|
2156
2467
|
const columns = ctx.validator?.getTableColumns(intent.from);
|
|
2157
2468
|
if (columns !== void 0) return columns;
|
|
2158
|
-
throw new
|
|
2159
|
-
NqlErrorCodes.SEM_INVALID_SYNTAX,
|
|
2469
|
+
throw new UnresolvedSelectAllOutputSchemaError(
|
|
2160
2470
|
`Cannot compute output schema for NQL binding '${bindingName}' from SELECT * on '${intent.from}' without a concrete table schema.`
|
|
2161
2471
|
);
|
|
2162
2472
|
}
|
|
@@ -3539,12 +3849,6 @@ function compileMultiSegmentPath(expr, item, ctx) {
|
|
|
3539
3849
|
const segments = expr.segments;
|
|
3540
3850
|
const firstSegmentLower = segments[0].toLowerCase();
|
|
3541
3851
|
if (ctx.pseudoColumnKeywords.has(firstSegmentLower)) {
|
|
3542
|
-
assertNoBindingRelationConstruct(
|
|
3543
|
-
ctx,
|
|
3544
|
-
ctx.currentFromTable,
|
|
3545
|
-
"use pseudo-column traversals",
|
|
3546
|
-
segments[0]
|
|
3547
|
-
);
|
|
3548
3852
|
const firstSegment = firstSegmentLower;
|
|
3549
3853
|
const depthHint = expr.depthHint;
|
|
3550
3854
|
if (depthHint !== void 0) {
|
|
@@ -3571,6 +3875,44 @@ function compileMultiSegmentPath(expr, item, ctx) {
|
|
|
3571
3875
|
);
|
|
3572
3876
|
}
|
|
3573
3877
|
const targetColumn = segments[i];
|
|
3878
|
+
const bindingRelation2 = traversals.length === 1 ? resolveBindingRecursiveRelationColumn(
|
|
3879
|
+
ctx,
|
|
3880
|
+
ctx.currentFromTable,
|
|
3881
|
+
firstSegment,
|
|
3882
|
+
targetColumn,
|
|
3883
|
+
depthHint
|
|
3884
|
+
) : void 0;
|
|
3885
|
+
if (bindingRelation2) {
|
|
3886
|
+
const relationColumnIntent2 = {
|
|
3887
|
+
kind: "relationColumn",
|
|
3888
|
+
relation: bindingRelation2.relation,
|
|
3889
|
+
column: targetColumn,
|
|
3890
|
+
as: item.alias ?? `${bindingRelation2.relation}.${targetColumn}`
|
|
3891
|
+
};
|
|
3892
|
+
return markNqlTrustedRelationFilter(relationColumnIntent2, {
|
|
3893
|
+
relation: bindingRelation2.relation,
|
|
3894
|
+
targetTable: bindingRelation2.targetTable,
|
|
3895
|
+
sourceColumn: bindingRelation2.sourceColumn,
|
|
3896
|
+
targetColumn: bindingRelation2.targetColumn,
|
|
3897
|
+
hops: bindingRelation2.hops,
|
|
3898
|
+
selectedColumn: targetColumn,
|
|
3899
|
+
...bindingRelation2.cardinality !== void 0 && {
|
|
3900
|
+
cardinality: bindingRelation2.cardinality
|
|
3901
|
+
},
|
|
3902
|
+
...bindingRelation2.relationType !== void 0 && {
|
|
3903
|
+
relationType: bindingRelation2.relationType
|
|
3904
|
+
},
|
|
3905
|
+
...bindingRelation2.recursive !== void 0 && {
|
|
3906
|
+
recursive: bindingRelation2.recursive
|
|
3907
|
+
}
|
|
3908
|
+
});
|
|
3909
|
+
}
|
|
3910
|
+
assertNoBindingRelationConstruct(
|
|
3911
|
+
ctx,
|
|
3912
|
+
ctx.currentFromTable,
|
|
3913
|
+
"use pseudo-column traversals",
|
|
3914
|
+
segments[0]
|
|
3915
|
+
);
|
|
3574
3916
|
if (ctx.currentFromTable) {
|
|
3575
3917
|
validateColumnForTable(ctx, ctx.currentFromTable, targetColumn);
|
|
3576
3918
|
}
|
|
@@ -3627,12 +3969,24 @@ function compileMultiSegmentPath(expr, item, ctx) {
|
|
|
3627
3969
|
sourceColumn: bindingRelation.sourceColumn,
|
|
3628
3970
|
targetColumn: bindingRelation.targetColumn,
|
|
3629
3971
|
hops: bindingRelation.hops,
|
|
3972
|
+
...bindingRelation.through !== void 0 && {
|
|
3973
|
+
through: bindingRelation.through
|
|
3974
|
+
},
|
|
3975
|
+
...bindingRelation.throughSourceColumn !== void 0 && {
|
|
3976
|
+
throughSourceColumn: bindingRelation.throughSourceColumn
|
|
3977
|
+
},
|
|
3978
|
+
...bindingRelation.throughTargetColumn !== void 0 && {
|
|
3979
|
+
throughTargetColumn: bindingRelation.throughTargetColumn
|
|
3980
|
+
},
|
|
3630
3981
|
selectedColumn: column,
|
|
3631
3982
|
...bindingRelation.cardinality !== void 0 && {
|
|
3632
3983
|
cardinality: bindingRelation.cardinality
|
|
3633
3984
|
},
|
|
3634
3985
|
...bindingRelation.relationType !== void 0 && {
|
|
3635
3986
|
relationType: bindingRelation.relationType
|
|
3987
|
+
},
|
|
3988
|
+
...bindingRelation.recursive !== void 0 && {
|
|
3989
|
+
recursive: bindingRelation.recursive
|
|
3636
3990
|
}
|
|
3637
3991
|
}) : relationColumnIntent;
|
|
3638
3992
|
}
|
|
@@ -3781,16 +4135,17 @@ function allowsInternalParams(options) {
|
|
|
3781
4135
|
return internalOptions?.allowInternalParams === true;
|
|
3782
4136
|
}
|
|
3783
4137
|
function isUnresolvedSelectAllOutputSchemaError(error) {
|
|
3784
|
-
return error instanceof
|
|
4138
|
+
return error instanceof UnresolvedSelectAllOutputSchemaError;
|
|
3785
4139
|
}
|
|
3786
|
-
function programSequenceStepFromResult(result, bindName, final, bindingDependencies) {
|
|
4140
|
+
function programSequenceStepFromResult(result, bindName, final, bindingDependencies, snapshotReadBindings) {
|
|
3787
4141
|
if (result.query) {
|
|
3788
4142
|
return {
|
|
3789
4143
|
kind: "query",
|
|
3790
4144
|
query: result.query,
|
|
3791
4145
|
...bindName !== void 0 && { bindName },
|
|
3792
4146
|
final,
|
|
3793
|
-
bindingDependencies
|
|
4147
|
+
bindingDependencies,
|
|
4148
|
+
...bindName !== void 0 && snapshotReadBindings.has(bindName) && { snapshot: true }
|
|
3794
4149
|
};
|
|
3795
4150
|
}
|
|
3796
4151
|
if (result.mutation) {
|
|
@@ -3810,6 +4165,79 @@ function stringArraysEqual(left, right) {
|
|
|
3810
4165
|
function isNqlAstRecord(value) {
|
|
3811
4166
|
return typeof value === "object" && value !== null;
|
|
3812
4167
|
}
|
|
4168
|
+
function isExactPhysicalProjectionColumn(query, ctx, sourceColumn, outputColumn) {
|
|
4169
|
+
const resolvedColumn = ctx.validator?.resolvePhysicalColumnName(
|
|
4170
|
+
query.from,
|
|
4171
|
+
sourceColumn
|
|
4172
|
+
);
|
|
4173
|
+
return resolvedColumn !== void 0 && sourceColumn === resolvedColumn && outputColumn === resolvedColumn;
|
|
4174
|
+
}
|
|
4175
|
+
function hasDirectPhysicalColumnProjection(query, ctx) {
|
|
4176
|
+
const physicalColumns = ctx.validator?.getPhysicalTableColumns(query.from);
|
|
4177
|
+
if (physicalColumns === void 0) return false;
|
|
4178
|
+
const select = query.select;
|
|
4179
|
+
if (!select || select.type === "all") return physicalColumns.length > 0;
|
|
4180
|
+
let projectedColumnCount = 0;
|
|
4181
|
+
const addStarProjection = () => {
|
|
4182
|
+
projectedColumnCount += physicalColumns.length;
|
|
4183
|
+
return physicalColumns.length > 0;
|
|
4184
|
+
};
|
|
4185
|
+
const addDirectProjection = (sourceColumn, outputColumn) => {
|
|
4186
|
+
projectedColumnCount++;
|
|
4187
|
+
return isExactPhysicalProjectionColumn(
|
|
4188
|
+
query,
|
|
4189
|
+
ctx,
|
|
4190
|
+
sourceColumn,
|
|
4191
|
+
outputColumn
|
|
4192
|
+
);
|
|
4193
|
+
};
|
|
4194
|
+
if (select.type === "fields") {
|
|
4195
|
+
for (const field of select.fields) {
|
|
4196
|
+
if (field === "*") {
|
|
4197
|
+
if (!addStarProjection()) return false;
|
|
4198
|
+
} else if (!addDirectProjection(field, field)) {
|
|
4199
|
+
return false;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
return projectedColumnCount > 0;
|
|
4203
|
+
}
|
|
4204
|
+
if (select.type === "aggregate") return false;
|
|
4205
|
+
for (const expr of select.columns) {
|
|
4206
|
+
if (expr.kind === "column" && expr.column === "*") {
|
|
4207
|
+
if (!addStarProjection()) return false;
|
|
4208
|
+
continue;
|
|
4209
|
+
}
|
|
4210
|
+
if (expr.kind === "column") {
|
|
4211
|
+
const outputColumn = expr.as ?? expr.column;
|
|
4212
|
+
if (!addDirectProjection(expr.column, outputColumn)) return false;
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4215
|
+
if (expr.kind === "columnAlias") {
|
|
4216
|
+
if (!addDirectProjection(expr.column, expr.alias)) return false;
|
|
4217
|
+
continue;
|
|
4218
|
+
}
|
|
4219
|
+
return false;
|
|
4220
|
+
}
|
|
4221
|
+
return projectedColumnCount > 0;
|
|
4222
|
+
}
|
|
4223
|
+
function classifyReadBindingSnapshotShape(query, ctx, sourceWasBinding) {
|
|
4224
|
+
if (sourceWasBinding) {
|
|
4225
|
+
return { supported: false, reason: "a binding source" };
|
|
4226
|
+
}
|
|
4227
|
+
if (ctx.validator?.getPhysicalTableColumns(query.from) === void 0) {
|
|
4228
|
+
return {
|
|
4229
|
+
supported: false,
|
|
4230
|
+
reason: `no physical model table source '${query.from}'`
|
|
4231
|
+
};
|
|
4232
|
+
}
|
|
4233
|
+
if (!hasDirectPhysicalColumnProjection(query, ctx)) {
|
|
4234
|
+
return {
|
|
4235
|
+
supported: false,
|
|
4236
|
+
reason: "aliased/computed/aggregate columns"
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
return { supported: true };
|
|
4240
|
+
}
|
|
3813
4241
|
function addReadBindingReference(references, readBindingNames, name) {
|
|
3814
4242
|
if (typeof name === "string" && readBindingNames.has(name)) {
|
|
3815
4243
|
references.add(name);
|
|
@@ -3973,23 +4401,31 @@ function collectTransitiveReadBindingDependencies(directReferences, bindingDepen
|
|
|
3973
4401
|
}
|
|
3974
4402
|
return ordered;
|
|
3975
4403
|
}
|
|
3976
|
-
function rejectReadBindingReferenceAcrossMutation(bindName,
|
|
4404
|
+
function rejectReadBindingReferenceAcrossMutation(bindName, reason) {
|
|
3977
4405
|
throw new NqlSemanticException(
|
|
3978
4406
|
NqlErrorCodes.SEM_INVALID_SYNTAX,
|
|
3979
|
-
`read binding referenced across a mutation (#186):
|
|
4407
|
+
`read binding referenced across a mutation: unsupported snapshot shape (#186): snapshotting currently supports only a direct single-table column projection; binding '${bindName}' has ${reason}, so it cannot be materialized - move the reference before the mutation, or bind the mutation RETURNING result.`
|
|
3980
4408
|
);
|
|
3981
4409
|
}
|
|
3982
|
-
function
|
|
4410
|
+
function markReadBindingReferenceAcrossMutation(bindName, snapshotReadBindings, readBindingSnapshotShapes) {
|
|
4411
|
+
const snapshotShape = readBindingSnapshotShapes.get(bindName);
|
|
4412
|
+
if (snapshotShape?.supported !== true) {
|
|
4413
|
+
rejectReadBindingReferenceAcrossMutation(
|
|
4414
|
+
bindName,
|
|
4415
|
+
snapshotShape?.reason ?? "an unavailable output shape"
|
|
4416
|
+
);
|
|
4417
|
+
}
|
|
4418
|
+
snapshotReadBindings.add(bindName);
|
|
4419
|
+
}
|
|
4420
|
+
function markReadBindingDependenciesRequiringSnapshot(statementBindingDependencies, readBindingDefinitions, lastMutationStatement, snapshotReadBindings, readBindingSnapshotShapes) {
|
|
3983
4421
|
for (const referencedBindName of statementBindingDependencies) {
|
|
3984
4422
|
const definitionIndex = readBindingDefinitions.get(referencedBindName);
|
|
3985
4423
|
if (definitionIndex === void 0) continue;
|
|
3986
4424
|
if (lastMutationStatement > definitionIndex) {
|
|
3987
|
-
|
|
4425
|
+
markReadBindingReferenceAcrossMutation(
|
|
3988
4426
|
referencedBindName,
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
referenceIndex,
|
|
3992
|
-
statementCount
|
|
4427
|
+
snapshotReadBindings,
|
|
4428
|
+
readBindingSnapshotShapes
|
|
3993
4429
|
);
|
|
3994
4430
|
}
|
|
3995
4431
|
}
|
|
@@ -4069,6 +4505,8 @@ var NqlCompiler = class {
|
|
|
4069
4505
|
const seenBindNames = /* @__PURE__ */ new Map();
|
|
4070
4506
|
const nqlProgramSequence = [];
|
|
4071
4507
|
const readBindingDefinitions = /* @__PURE__ */ new Map();
|
|
4508
|
+
const readBindingSnapshotShapes = /* @__PURE__ */ new Map();
|
|
4509
|
+
const snapshotReadBindings = /* @__PURE__ */ new Set();
|
|
4072
4510
|
const definedBindingNames = /* @__PURE__ */ new Set();
|
|
4073
4511
|
const bindingDependencies = /* @__PURE__ */ new Map();
|
|
4074
4512
|
let lastMutationStatement = -1;
|
|
@@ -4094,12 +4532,12 @@ var NqlCompiler = class {
|
|
|
4094
4532
|
readBindingReferences,
|
|
4095
4533
|
bindingDependencies
|
|
4096
4534
|
);
|
|
4097
|
-
|
|
4535
|
+
markReadBindingDependenciesRequiringSnapshot(
|
|
4098
4536
|
statementBindingDependencies,
|
|
4099
4537
|
readBindingDefinitions,
|
|
4100
4538
|
lastMutationStatement,
|
|
4101
|
-
|
|
4102
|
-
|
|
4539
|
+
snapshotReadBindings,
|
|
4540
|
+
readBindingSnapshotShapes
|
|
4103
4541
|
);
|
|
4104
4542
|
lastResult = this.compileSingleStatement(stmt, bindings);
|
|
4105
4543
|
collectCompileResultReadBindingReferences(
|
|
@@ -4111,24 +4549,35 @@ var NqlCompiler = class {
|
|
|
4111
4549
|
readBindingReferences,
|
|
4112
4550
|
bindingDependencies
|
|
4113
4551
|
);
|
|
4114
|
-
|
|
4552
|
+
markReadBindingDependenciesRequiringSnapshot(
|
|
4115
4553
|
statementBindingDependencies,
|
|
4116
4554
|
readBindingDefinitions,
|
|
4117
4555
|
lastMutationStatement,
|
|
4118
|
-
|
|
4119
|
-
|
|
4556
|
+
snapshotReadBindings,
|
|
4557
|
+
readBindingSnapshotShapes
|
|
4120
4558
|
);
|
|
4121
4559
|
if (stmt.type === "mutationPipeline") {
|
|
4122
4560
|
lastMutationStatement = i;
|
|
4123
4561
|
}
|
|
4124
4562
|
if (bindName) {
|
|
4125
4563
|
if (lastResult.query) {
|
|
4564
|
+
const sourceWasBinding = definedBindingNames.has(
|
|
4565
|
+
lastResult.query.from
|
|
4566
|
+
);
|
|
4126
4567
|
const outputSchema = this.registerQueryBindingOutputSchema(
|
|
4127
4568
|
bindName,
|
|
4128
4569
|
lastResult.query,
|
|
4129
4570
|
bindingOutputSchemas,
|
|
4130
4571
|
statementBindingDependencies
|
|
4131
4572
|
);
|
|
4573
|
+
readBindingSnapshotShapes.set(
|
|
4574
|
+
bindName,
|
|
4575
|
+
classifyReadBindingSnapshotShape(
|
|
4576
|
+
lastResult.query,
|
|
4577
|
+
this.ctx,
|
|
4578
|
+
sourceWasBinding
|
|
4579
|
+
)
|
|
4580
|
+
);
|
|
4132
4581
|
bindings.set(bindName, lastResult.query);
|
|
4133
4582
|
if (outputSchema) {
|
|
4134
4583
|
this.ctx.validator?.addVirtualBindingTable(
|
|
@@ -4176,7 +4625,8 @@ var NqlCompiler = class {
|
|
|
4176
4625
|
lastResult,
|
|
4177
4626
|
bindName,
|
|
4178
4627
|
i === program.statements.length - 1,
|
|
4179
|
-
statementBindingDependencies
|
|
4628
|
+
statementBindingDependencies,
|
|
4629
|
+
snapshotReadBindings
|
|
4180
4630
|
);
|
|
4181
4631
|
if (sequenceStep !== void 0) {
|
|
4182
4632
|
nqlProgramSequence.push(sequenceStep);
|
|
@@ -4192,14 +4642,19 @@ var NqlCompiler = class {
|
|
|
4192
4642
|
}
|
|
4193
4643
|
const hasMutationBindings = mutationBindings.size > 0;
|
|
4194
4644
|
const hasBindingOutputSchemas = bindingOutputSchemas.size > 0;
|
|
4195
|
-
const
|
|
4645
|
+
const taggedNqlProgramSequence = nqlProgramSequence.map(
|
|
4646
|
+
(step) => step.kind === "query" && step.bindName !== void 0 && snapshotReadBindings.has(step.bindName) ? { ...step, snapshot: true } : step
|
|
4647
|
+
);
|
|
4648
|
+
const hasNqlProgramSequence = taggedNqlProgramSequence.length === program.statements.length;
|
|
4196
4649
|
if (bindings.size > 0) {
|
|
4197
4650
|
return {
|
|
4198
4651
|
...lastResult,
|
|
4199
4652
|
bindings,
|
|
4200
4653
|
...hasBindingOutputSchemas && { bindingOutputSchemas },
|
|
4201
4654
|
...hasMutationBindings && { mutationBindings },
|
|
4202
|
-
...hasNqlProgramSequence && {
|
|
4655
|
+
...hasNqlProgramSequence && {
|
|
4656
|
+
nqlProgramSequence: taggedNqlProgramSequence
|
|
4657
|
+
}
|
|
4203
4658
|
};
|
|
4204
4659
|
}
|
|
4205
4660
|
return lastResult;
|