@dbsp/nql 1.9.5 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.js +176 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ColumnType, CompiledNqlQuery } from '@dbsp/types';
|
|
1
|
+
import { ColumnType, ColumnJsReadType, CompiledNqlQuery } from '@dbsp/types';
|
|
2
2
|
export { CompiledNqlQuery, DeleteIntent, ExpressionIntent, IncludeIntent, InsertIntent, MutationIntent, OrderByIntent, QueryIntent, SelectAllIntent, SelectFieldsIntent, SelectIntent, SelectWithExpressionsIntent, UpdateIntent, UpsertIntent, WhereAndIntent, WhereComparisonIntent, WhereInIntent, WhereIntent, WhereLikeIntent, WhereNotIntent, WhereNullIntent, WhereOrIntent, WhereRangeIntent } from '@dbsp/types';
|
|
3
3
|
import * as chevrotain from 'chevrotain';
|
|
4
4
|
import { Lexer, CstParser, CstNode, IToken } from 'chevrotain';
|
|
@@ -463,6 +463,7 @@ interface ColumnValidatorPseudoColumn {
|
|
|
463
463
|
interface ColumnValidatorTableColumn {
|
|
464
464
|
readonly name: string;
|
|
465
465
|
readonly type?: ColumnType;
|
|
466
|
+
readonly js?: ColumnJsReadType;
|
|
466
467
|
readonly originalDbType?: string;
|
|
467
468
|
readonly originalDbTypeSchema?: string;
|
|
468
469
|
readonly originalDbTypeSchemaScope?: 'target' | 'absolute';
|
|
@@ -529,6 +530,7 @@ declare class NqlCompiler {
|
|
|
529
530
|
private registerQueryBindingOutputSchema;
|
|
530
531
|
private canonicalizeMutationBinding;
|
|
531
532
|
private getMutationBindingOutputSchema;
|
|
533
|
+
private buildMutationReturningDeclaredOutputs;
|
|
532
534
|
/**
|
|
533
535
|
* Build `columnTypes`/`columnTypesUnavailable` for a mutation-RETURNING
|
|
534
536
|
* binding. #213 B2: detection consumes `ctx.lastMutationReturningItems`
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NQL_INTERNAL_COMPILER_OPTIONS, explainUnsupportedNqlBindingIncludeHop, markNqlTrustedRelationFilter, createNqlBindingRef, isNqlBindingRef, getNqlBindingRefName } from '@dbsp/types/internal';
|
|
1
|
+
import { NQL_INTERNAL_COMPILER_OPTIONS, explainUnsupportedNqlBindingIncludeHop, markNqlTrustedRelationFilter, getTrustedNqlRelationFilterFields, createNqlBindingRef, isNqlBindingRef, getNqlBindingRefName } from '@dbsp/types/internal';
|
|
2
2
|
import { NQL_SELECT_AGGREGATE_FUNCTIONS, NQL_SELECT_VALUE_FUNCTIONS, NQL_SELECT_JSON_FUNCTIONS, NQL_SELECT_SCALAR_FUNCTIONS, toColumnList, isNqlSelectWindowFunctionAllowed, isRankingWindowFunction, isParamIntent } from '@dbsp/types';
|
|
3
3
|
import { createToken, Lexer, CstParser } from 'chevrotain';
|
|
4
4
|
|
|
@@ -181,6 +181,12 @@ var ColumnValidator = class _ColumnValidator {
|
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
|
+
getTableColumnJsReadType(table, column) {
|
|
185
|
+
const resolvedName = this.resolvePhysicalColumnName(table, column);
|
|
186
|
+
if (resolvedName === void 0) return void 0;
|
|
187
|
+
const columnInfo = this.schema.getTable(table)?.columns.find((candidate) => candidate.name === resolvedName);
|
|
188
|
+
return columnInfo?.type === "bigint" ? columnInfo.js : void 0;
|
|
189
|
+
}
|
|
184
190
|
getPseudoColumns(name) {
|
|
185
191
|
return this.schema.getTable(name)?.pseudoColumns ?? [];
|
|
186
192
|
}
|
|
@@ -1610,6 +1616,13 @@ function toSnakeCase(name) {
|
|
|
1610
1616
|
function columnsMatch(left, right) {
|
|
1611
1617
|
return left === right || toSnakeCase(left) === toSnakeCase(right);
|
|
1612
1618
|
}
|
|
1619
|
+
var scalarOneShape = {
|
|
1620
|
+
kind: "scalar",
|
|
1621
|
+
cardinality: "one"
|
|
1622
|
+
};
|
|
1623
|
+
function unknownShape(reason) {
|
|
1624
|
+
return { kind: "unknown", reason };
|
|
1625
|
+
}
|
|
1613
1626
|
function expressionIntentContainsAggregate(value) {
|
|
1614
1627
|
if (!value || typeof value !== "object") return false;
|
|
1615
1628
|
const record = value;
|
|
@@ -2578,13 +2591,84 @@ function getQueryOutputSchema(intent, ctx, bindingName, bindingDependencies = []
|
|
|
2578
2591
|
const columns = [];
|
|
2579
2592
|
const seen = /* @__PURE__ */ new Set();
|
|
2580
2593
|
const directProjectionLineage = [];
|
|
2594
|
+
const declaredOutputCandidates = /* @__PURE__ */ new Map();
|
|
2581
2595
|
const typeCandidates = [];
|
|
2582
2596
|
const sourceIsPhysicalTable = ctx.validator?.hasPhysicalTable(intent.from) ?? false;
|
|
2583
2597
|
const sourceBindingSchema = sourceIsPhysicalTable ? void 0 : sourceBindingSchemas?.get(intent.from);
|
|
2584
2598
|
const addColumn = (column) => addUnique(columns, seen, column);
|
|
2585
2599
|
const resolveSourceColumn = (column) => ctx.validator?.resolveColumnName(intent.from, column) ?? column;
|
|
2600
|
+
const addDeclaredOutputCandidate = (outputKey, descriptor) => {
|
|
2601
|
+
const candidates = declaredOutputCandidates.get(outputKey) ?? [];
|
|
2602
|
+
candidates.push(descriptor);
|
|
2603
|
+
declaredOutputCandidates.set(outputKey, candidates);
|
|
2604
|
+
};
|
|
2605
|
+
const unresolvedOutput = (outputKey, reason) => ({
|
|
2606
|
+
outputKey,
|
|
2607
|
+
source: { kind: "unresolved", reason },
|
|
2608
|
+
shape: unknownShape(reason)
|
|
2609
|
+
});
|
|
2610
|
+
const modelColumnOutput = (outputKey, table, column, shape = scalarOneShape) => {
|
|
2611
|
+
const js = ctx.validator?.getTableColumnJsReadType(table, column);
|
|
2612
|
+
return {
|
|
2613
|
+
outputKey,
|
|
2614
|
+
source: {
|
|
2615
|
+
kind: "modelColumn",
|
|
2616
|
+
table,
|
|
2617
|
+
column,
|
|
2618
|
+
...js !== void 0 ? { js } : {}
|
|
2619
|
+
},
|
|
2620
|
+
shape
|
|
2621
|
+
};
|
|
2622
|
+
};
|
|
2623
|
+
const withOutputKey = (descriptor, outputKey) => ({
|
|
2624
|
+
outputKey,
|
|
2625
|
+
source: descriptor.source,
|
|
2626
|
+
shape: descriptor.shape
|
|
2627
|
+
});
|
|
2628
|
+
const resolveDirectProjectionOutput = (outputKey, resolvedSourceColumn) => {
|
|
2629
|
+
if (sourceIsPhysicalTable) {
|
|
2630
|
+
return modelColumnOutput(outputKey, intent.from, resolvedSourceColumn);
|
|
2631
|
+
}
|
|
2632
|
+
const sourceDescriptors = sourceBindingSchema?.declaredOutputs?.filter(
|
|
2633
|
+
(descriptor) => columnsMatch(descriptor.outputKey, resolvedSourceColumn)
|
|
2634
|
+
);
|
|
2635
|
+
if (sourceDescriptors?.length === 1) {
|
|
2636
|
+
const [descriptor] = sourceDescriptors;
|
|
2637
|
+
if (descriptor !== void 0) return withOutputKey(descriptor, outputKey);
|
|
2638
|
+
}
|
|
2639
|
+
return unresolvedOutput(
|
|
2640
|
+
outputKey,
|
|
2641
|
+
`binding output '${outputKey}' did not resolve to a declared source column`
|
|
2642
|
+
);
|
|
2643
|
+
};
|
|
2644
|
+
const resolvePhysicalRelationColumnOutput = (outputKey, expr) => {
|
|
2645
|
+
if (!sourceIsPhysicalTable || !ctx.validator) return void 0;
|
|
2646
|
+
const relationPath = expr.relation.split(".");
|
|
2647
|
+
if (relationPath.length === 0 || relationPath.some((part) => part === "")) {
|
|
2648
|
+
return void 0;
|
|
2649
|
+
}
|
|
2650
|
+
let targetTable = intent.from;
|
|
2651
|
+
for (const relationName of relationPath) {
|
|
2652
|
+
const relation = ctx.validator.getRelation(targetTable, relationName);
|
|
2653
|
+
if (relation === void 0) return void 0;
|
|
2654
|
+
if (relation.type !== "belongsTo" && relation.type !== "hasOne" || relation.recursive !== void 0) {
|
|
2655
|
+
return void 0;
|
|
2656
|
+
}
|
|
2657
|
+
targetTable = relation.target;
|
|
2658
|
+
}
|
|
2659
|
+
const resolvedColumn = ctx.validator.resolvePhysicalColumnName(
|
|
2660
|
+
targetTable,
|
|
2661
|
+
expr.column
|
|
2662
|
+
);
|
|
2663
|
+
if (resolvedColumn === void 0) return void 0;
|
|
2664
|
+
return modelColumnOutput(outputKey, targetTable, resolvedColumn);
|
|
2665
|
+
};
|
|
2586
2666
|
const addDirectProjection = (outputColumn, sourceColumn) => {
|
|
2587
2667
|
const resolvedSourceColumn = resolveSourceColumn(sourceColumn);
|
|
2668
|
+
addDeclaredOutputCandidate(
|
|
2669
|
+
outputColumn,
|
|
2670
|
+
resolveDirectProjectionOutput(outputColumn, resolvedSourceColumn)
|
|
2671
|
+
);
|
|
2588
2672
|
let typeInfo;
|
|
2589
2673
|
let untypeableReason = "unresolvable-source";
|
|
2590
2674
|
if (sourceIsPhysicalTable) {
|
|
@@ -2609,10 +2693,51 @@ function getQueryOutputSchema(intent, ctx, bindingName, bindingDependencies = []
|
|
|
2609
2693
|
});
|
|
2610
2694
|
};
|
|
2611
2695
|
const addUntypedColumn = (outputColumn, reason) => {
|
|
2696
|
+
addDeclaredOutputCandidate(
|
|
2697
|
+
outputColumn,
|
|
2698
|
+
unresolvedOutput(
|
|
2699
|
+
outputColumn,
|
|
2700
|
+
`binding output '${outputColumn}' has no proven scalar model column source (${reason})`
|
|
2701
|
+
)
|
|
2702
|
+
);
|
|
2612
2703
|
typeCandidates.push({ column: outputColumn, untypeable: reason });
|
|
2613
2704
|
addColumn(outputColumn);
|
|
2614
2705
|
};
|
|
2706
|
+
const addRelationColumn = (outputColumn, expr) => {
|
|
2707
|
+
const trusted = getTrustedNqlRelationFilterFields(expr);
|
|
2708
|
+
const targetTable = trusted?.hops.at(-1)?.target ?? trusted?.targetTable;
|
|
2709
|
+
const isScalarRelationColumn = trusted?.selectedColumn !== void 0 && trusted.cardinality === "one" && trusted.recursive === void 0 && (trusted.relationType === "belongsTo" || trusted.relationType === "hasOne");
|
|
2710
|
+
const descriptor = targetTable !== void 0 && isScalarRelationColumn ? modelColumnOutput(outputColumn, targetTable, trusted.selectedColumn) : trusted !== void 0 ? targetTable !== void 0 && trusted.selectedColumn !== void 0 ? modelColumnOutput(
|
|
2711
|
+
outputColumn,
|
|
2712
|
+
targetTable,
|
|
2713
|
+
trusted.selectedColumn,
|
|
2714
|
+
{
|
|
2715
|
+
kind: "array",
|
|
2716
|
+
cardinality: "many"
|
|
2717
|
+
}
|
|
2718
|
+
) : void 0 : expr.kind === "relationColumn" ? resolvePhysicalRelationColumnOutput(outputColumn, expr) : void 0;
|
|
2719
|
+
addDeclaredOutputCandidate(
|
|
2720
|
+
outputColumn,
|
|
2721
|
+
descriptor ?? unresolvedOutput(
|
|
2722
|
+
outputColumn,
|
|
2723
|
+
`relation output '${outputColumn}' has no proven scalar model column source`
|
|
2724
|
+
)
|
|
2725
|
+
);
|
|
2726
|
+
typeCandidates.push({
|
|
2727
|
+
column: outputColumn,
|
|
2728
|
+
untypeable: "relation-column"
|
|
2729
|
+
});
|
|
2730
|
+
addColumn(outputColumn);
|
|
2731
|
+
};
|
|
2615
2732
|
const addCountAggregateColumn = (outputColumn) => {
|
|
2733
|
+
addDeclaredOutputCandidate(outputColumn, {
|
|
2734
|
+
outputKey: outputColumn,
|
|
2735
|
+
source: {
|
|
2736
|
+
kind: "expression",
|
|
2737
|
+
reason: `aggregate output '${outputColumn}' has no scalar model column source`
|
|
2738
|
+
},
|
|
2739
|
+
shape: { kind: "aggregate-scalar", aggregate: "count" }
|
|
2740
|
+
});
|
|
2616
2741
|
typeCandidates.push({
|
|
2617
2742
|
column: outputColumn,
|
|
2618
2743
|
typed: { kind: "aggregate", fn: "count" }
|
|
@@ -2627,8 +2752,22 @@ function getQueryOutputSchema(intent, ctx, bindingName, bindingDependencies = []
|
|
|
2627
2752
|
const finalizeOutputSchema = () => {
|
|
2628
2753
|
const outputSchema = { columns, directProjectionLineage };
|
|
2629
2754
|
const typesResult = buildBindingColumnTypes(columns, typeCandidates);
|
|
2755
|
+
const declaredOutputs = columns.map((column) => {
|
|
2756
|
+
const candidates = declaredOutputCandidates.get(column) ?? [];
|
|
2757
|
+
if (candidates.length !== 1) {
|
|
2758
|
+
return unresolvedOutput(
|
|
2759
|
+
column,
|
|
2760
|
+
`binding output '${column}' has ambiguous declared output sources`
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
return candidates[0] ?? unresolvedOutput(
|
|
2764
|
+
column,
|
|
2765
|
+
`binding output '${column}' has no declared output source`
|
|
2766
|
+
);
|
|
2767
|
+
});
|
|
2630
2768
|
return {
|
|
2631
2769
|
columns: outputSchema.columns,
|
|
2770
|
+
declaredOutputs,
|
|
2632
2771
|
relationFilters: getBindingRelationFilterMetadata(
|
|
2633
2772
|
intent,
|
|
2634
2773
|
ctx,
|
|
@@ -2696,10 +2835,11 @@ function getQueryOutputSchema(intent, ctx, bindingName, bindingDependencies = []
|
|
|
2696
2835
|
addDirectProjection(outputColumn, expr.column);
|
|
2697
2836
|
} else if (expr.kind === "aggregate" && expr.function === "count") {
|
|
2698
2837
|
addCountAggregateColumn(outputColumn);
|
|
2838
|
+
} else if (expr.kind === "relationColumn") {
|
|
2839
|
+
addRelationColumn(outputColumn, expr);
|
|
2699
2840
|
} else {
|
|
2700
2841
|
let reason = "computed-expression";
|
|
2701
|
-
if (expr.kind === "
|
|
2702
|
-
else if (expr.kind === "aggregate") reason = "unsupported-aggregate";
|
|
2842
|
+
if (expr.kind === "aggregate") reason = "unsupported-aggregate";
|
|
2703
2843
|
addUntypedColumn(outputColumn, reason);
|
|
2704
2844
|
}
|
|
2705
2845
|
}
|
|
@@ -4838,6 +4978,10 @@ var NqlCompiler = class {
|
|
|
4838
4978
|
if (columns2 !== void 0) {
|
|
4839
4979
|
return {
|
|
4840
4980
|
columns: columns2,
|
|
4981
|
+
declaredOutputs: this.buildMutationReturningDeclaredOutputs(
|
|
4982
|
+
mutation.table,
|
|
4983
|
+
columns2
|
|
4984
|
+
),
|
|
4841
4985
|
...this.buildMutationReturningColumnTypes(mutation.table, columns2)
|
|
4842
4986
|
};
|
|
4843
4987
|
}
|
|
@@ -4859,14 +5003,43 @@ var NqlCompiler = class {
|
|
|
4859
5003
|
});
|
|
4860
5004
|
return {
|
|
4861
5005
|
columns: columns2,
|
|
5006
|
+
declaredOutputs: this.buildMutationReturningDeclaredOutputs(
|
|
5007
|
+
mutation.table,
|
|
5008
|
+
columns2
|
|
5009
|
+
),
|
|
4862
5010
|
...this.buildMutationReturningColumnTypes(mutation.table, columns2)
|
|
4863
5011
|
};
|
|
4864
5012
|
}
|
|
4865
5013
|
return {
|
|
4866
5014
|
columns,
|
|
5015
|
+
declaredOutputs: this.buildMutationReturningDeclaredOutputs(
|
|
5016
|
+
mutation.table,
|
|
5017
|
+
columns
|
|
5018
|
+
),
|
|
4867
5019
|
...this.buildMutationReturningColumnTypes(mutation.table, columns)
|
|
4868
5020
|
};
|
|
4869
5021
|
}
|
|
5022
|
+
buildMutationReturningDeclaredOutputs(table, columns) {
|
|
5023
|
+
const items = this.ctx.lastMutationReturningItems;
|
|
5024
|
+
return columns.map((column, index) => {
|
|
5025
|
+
const item = items !== void 0 && items !== "star" ? items[index] : void 0;
|
|
5026
|
+
const sourceColumn = item !== void 0 ? this.ctx.validator?.resolveColumnName(table, item.source) ?? item.source : column;
|
|
5027
|
+
const js = this.ctx.validator?.getTableColumnJsReadType(
|
|
5028
|
+
table,
|
|
5029
|
+
sourceColumn
|
|
5030
|
+
);
|
|
5031
|
+
return {
|
|
5032
|
+
outputKey: column,
|
|
5033
|
+
source: {
|
|
5034
|
+
kind: "modelColumn",
|
|
5035
|
+
table,
|
|
5036
|
+
column: sourceColumn,
|
|
5037
|
+
...js !== void 0 && { js }
|
|
5038
|
+
},
|
|
5039
|
+
shape: { kind: "scalar", cardinality: "one" }
|
|
5040
|
+
};
|
|
5041
|
+
});
|
|
5042
|
+
}
|
|
4870
5043
|
/**
|
|
4871
5044
|
* Build `columnTypes`/`columnTypesUnavailable` for a mutation-RETURNING
|
|
4872
5045
|
* binding. #213 B2: detection consumes `ctx.lastMutationReturningItems`
|