@narrative.io/data-collaboration-sdk-ts 2.4.0 → 2.6.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/build/nql/Ast.d.ts +120 -4
- package/build/nql/Ast.js +36 -0
- package/build/nql/AstParser.d.ts +5 -43
- package/build/nql/AstParser.js +26 -101
- package/build/nql/AstTraverse.js +0 -9
- package/build/nql/NqlBuilder.js +8 -48
- package/build/nql/Template.d.ts +23 -0
- package/build/nql/Template.js +170 -0
- package/package.json +3 -3
package/build/nql/Ast.d.ts
CHANGED
|
@@ -1,3 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mapping of AST (Abstract Syntax Tree) node types to their corresponding TypeScript types.
|
|
3
|
+
* This object acts as a type registry for the various components of the NQL (Notation Query Language) AST.
|
|
4
|
+
*/
|
|
5
|
+
declare const NqlAstTypes: {
|
|
6
|
+
agg_function: NqlAggFunction;
|
|
7
|
+
binary_op: NqlBinaryOp;
|
|
8
|
+
case: NqlCase;
|
|
9
|
+
function: NqlFunction;
|
|
10
|
+
literal: NqlLit<NqlSimpleType>;
|
|
11
|
+
string_literal: NqlLit<NqlStringType>;
|
|
12
|
+
long_literal: NqlLit<NqlLongType>;
|
|
13
|
+
double_literal: NqlLit<NqlDoubleType>;
|
|
14
|
+
boolean_literal: NqlLit<NqlBooleanType>;
|
|
15
|
+
timestamp_literal: NqlLit<NqlTimestampType>;
|
|
16
|
+
column: NqlIdentColumn;
|
|
17
|
+
table: NqlIdentTable;
|
|
18
|
+
join: NqlJoin;
|
|
19
|
+
type_spec: NqlTypeSpec;
|
|
20
|
+
operator: NqlOp;
|
|
21
|
+
window: NqlWindow;
|
|
22
|
+
unknown: NqlUnknown;
|
|
23
|
+
select: NqlSelect;
|
|
24
|
+
create_materialized_view: NqlCreateMaterializedView;
|
|
25
|
+
explain: NqlExplain;
|
|
26
|
+
array: NqlArrayType;
|
|
27
|
+
binary: NqlBinaryType;
|
|
28
|
+
bool: NqlBooleanType;
|
|
29
|
+
double: NqlDoubleType;
|
|
30
|
+
long: NqlLongType;
|
|
31
|
+
interval: NqlIntervalType;
|
|
32
|
+
object: NqlObjectType;
|
|
33
|
+
other: NqlOtherType;
|
|
34
|
+
string: NqlStringType;
|
|
35
|
+
timestamp: NqlTimestampType;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Represents the logical operators that can be used to combine constraints in a NQL query.
|
|
39
|
+
*/
|
|
40
|
+
type NqlTemplateConstraintLogicalOperator = "ANY_OF" | "ALL_OF" | "NONE_OF";
|
|
41
|
+
/**
|
|
42
|
+
* Defines a constraint that can be applied in a placeholder node. A constraint can consist of
|
|
43
|
+
* individual conditions or groups of nested conditions combined using a logical operator.
|
|
44
|
+
*
|
|
45
|
+
* @template T The type of conditions or nested constraint groups contained within this constraint.
|
|
46
|
+
*/
|
|
47
|
+
interface NqlTemplateConstraint<T> {
|
|
48
|
+
operator: NqlTemplateConstraintLogicalOperator;
|
|
49
|
+
constraints: (T | NqlTemplateConstraintGroup<T>)[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Represents a group of constraints combined under a single logical operator. This allows for
|
|
53
|
+
* nested and complex logical expressions within a NQL query.
|
|
54
|
+
*
|
|
55
|
+
* @extends NqlTemplateConstraint<T> Inherits the structure of NqlConstraint for nested grouping.
|
|
56
|
+
* @template T The type of conditions or nested constraint groups contained within this constraint group.
|
|
57
|
+
*/
|
|
58
|
+
interface NqlTemplateConstraintGroup<T> extends NqlTemplateConstraint<T> {
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Represents the keys from the NqlAstTypes object, used to identify the type of AST node.
|
|
62
|
+
*/
|
|
63
|
+
type NqlAstType = keyof typeof NqlAstTypes;
|
|
64
|
+
/**
|
|
65
|
+
* Extracts the TypeScript type(s) associated with a given NQL AST type or array of AST types.
|
|
66
|
+
* This utility type dynamically maps one or more NqlAstType keys to their corresponding types.
|
|
67
|
+
*
|
|
68
|
+
* @template T A single NqlAstType or an array of NqlAstType values.
|
|
69
|
+
*/
|
|
70
|
+
type ExtractNqlType<T extends NqlAstType | NqlAstType[]> = T extends NqlAstType[] ? {
|
|
71
|
+
[K in keyof T]: T[K] extends NqlAstType ? (typeof NqlAstTypes)[T[K]] : never;
|
|
72
|
+
} : T extends NqlAstType ? (typeof NqlAstTypes)[T] : never;
|
|
73
|
+
/**
|
|
74
|
+
* Extracts the TypeScript type(s) associated with the expected type(s) of a `NqlPlaceholder`.
|
|
75
|
+
* This utility type conditionally checks if a given type `T` is a `NqlPlaceholder` with a generic
|
|
76
|
+
* type parameter `U`. If `T` is a `NqlPlaceholder`, it uses the `ExtractNqlType` utility to map
|
|
77
|
+
* `U` (the generic type parameter of the `NqlPlaceholder`) to its corresponding TypeScript type(s).
|
|
78
|
+
* If `T` is not a `NqlPlaceholder`, the type resolves to `never`.
|
|
79
|
+
*
|
|
80
|
+
* This type is useful for type inference and ensuring type safety when working with AST nodes
|
|
81
|
+
* that may be placeholders, enabling conditional type logic based on the structure of the AST.
|
|
82
|
+
*
|
|
83
|
+
* @template T - The type to check against `NqlPlaceholder`. This can be any type within the
|
|
84
|
+
* `NqlAst` hierarchy, or more broadly, any type in the system.
|
|
85
|
+
*
|
|
86
|
+
* @returns The extracted TypeScript type(s) if `T` is a `NqlPlaceholder`, otherwise `never`.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Given a placeholder type with expected type 'string_literal':
|
|
90
|
+
* type PlaceholderStringLiteral = NqlPlaceholder<'string_literal'>;
|
|
91
|
+
*
|
|
92
|
+
* // The extracted type would be `NqlLit<NqlStringType>`:
|
|
93
|
+
* type ExtractedType = ExtractPlaceholderType<PlaceholderStringLiteral>;
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* // When the type is not a `NqlPlaceholder`, `never` is returned:
|
|
97
|
+
* type NonPlaceholderType = NqlAst; // Assuming NqlAst is not a placeholder.
|
|
98
|
+
* type ExtractedType = ExtractPlaceholderType<NonPlaceholderType>; // `never`
|
|
99
|
+
*/
|
|
100
|
+
export type ExtractPlaceholderType<T> = T extends NqlPlaceholder<infer U> ? ExtractNqlType<U> : never;
|
|
101
|
+
/**
|
|
102
|
+
* Represents a placeholder within a NQL query template. Placeholders are used to dynamically insert
|
|
103
|
+
* values, columns, or other AST nodes into a query based on certain constraints and expectations.
|
|
104
|
+
*
|
|
105
|
+
* @template T A single NqlAstType or an array of NqlAstType values indicating the expected type(s)
|
|
106
|
+
* for the AST node(s) that can fill this placeholder.
|
|
107
|
+
*/
|
|
108
|
+
interface NqlPlaceholder<T extends NqlAstType | NqlAstType[] = any> {
|
|
109
|
+
type: "placeholder";
|
|
110
|
+
identifier: string;
|
|
111
|
+
expectedType: T;
|
|
112
|
+
optional: boolean;
|
|
113
|
+
cardinality: "one" | "many";
|
|
114
|
+
constraints?: NqlTemplateConstraint<ExtractNqlType<T>>;
|
|
115
|
+
nql: string;
|
|
116
|
+
}
|
|
1
117
|
type Quantifier = "all" | "distinct";
|
|
2
118
|
interface NqlAggFunction {
|
|
3
119
|
nql: string;
|
|
@@ -77,10 +193,10 @@ interface NqlTimestampType {
|
|
|
77
193
|
type: "timestamp";
|
|
78
194
|
}
|
|
79
195
|
type NqlSimpleType = NqlArrayType | NqlBinaryType | NqlBooleanType | NqlDoubleType | NqlLongType | NqlIntervalType | NqlObjectType | NqlOtherType | NqlStringType | NqlTimestampType;
|
|
80
|
-
interface NqlLit {
|
|
196
|
+
interface NqlLit<T = NqlSimpleType> {
|
|
81
197
|
nql: string;
|
|
82
198
|
as?: string;
|
|
83
|
-
value_type:
|
|
199
|
+
value_type: T;
|
|
84
200
|
value: string | null;
|
|
85
201
|
type: "literal";
|
|
86
202
|
}
|
|
@@ -180,5 +296,5 @@ interface NqlAstList extends Array<NqlAst> {
|
|
|
180
296
|
export declare function isAstArray(n: NqlAst): n is NqlAstList;
|
|
181
297
|
type NqlAst = {
|
|
182
298
|
type: string;
|
|
183
|
-
} & (NqlAstList | NqlAggFunction | NqlBinaryOp | NqlCase | NqlFunction | NqlLit | NqlIdent | NqlJoin | NqlTypeSpec | NqlOp | NqlWindow | NqlUnknown | NqlSelect | NqlCreateMaterializedView | NqlExplain);
|
|
184
|
-
export type { NqlAst, NqlAstList, NqlAggFunction, NqlBinaryOp, NqlBudget, NqlCase, NqlFunction, NqlSimpleType, NqlLit, NqlIdent, NqlIdentColumn, NqlIdentTable, NqlJoin, NqlJoinType, NqlJoinConditionType, NqlTypeSpec, NqlOp, NqlWindow, NqlUnknown, NqlSelect, NqlCreateMaterializedView, NqlExplain, };
|
|
299
|
+
} & (NqlAstList | NqlAggFunction | NqlBinaryOp | NqlCase | NqlFunction | NqlLit | NqlIdent | NqlJoin | NqlTypeSpec | NqlOp | NqlWindow | NqlUnknown | NqlSelect | NqlCreateMaterializedView | NqlExplain | NqlWhen | NqlElse | NqlPlaceholder);
|
|
300
|
+
export type { NqlAst, NqlAstList, NqlAggFunction, NqlBinaryOp, NqlBudget, NqlCase, NqlFunction, NqlSimpleType, NqlLit, NqlIdent, NqlIdentColumn, NqlIdentTable, NqlJoin, NqlJoinType, NqlJoinConditionType, NqlTypeSpec, NqlOp, NqlWindow, NqlUnknown, NqlSelect, NqlCreateMaterializedView, NqlExplain, NqlAstType, NqlPlaceholder, };
|
package/build/nql/Ast.js
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mapping of AST (Abstract Syntax Tree) node types to their corresponding TypeScript types.
|
|
3
|
+
* This object acts as a type registry for the various components of the NQL (Notation Query Language) AST.
|
|
4
|
+
*/
|
|
5
|
+
const NqlAstTypes = {
|
|
6
|
+
agg_function: null,
|
|
7
|
+
binary_op: null,
|
|
8
|
+
case: null,
|
|
9
|
+
function: null,
|
|
10
|
+
literal: null,
|
|
11
|
+
string_literal: null,
|
|
12
|
+
long_literal: null,
|
|
13
|
+
double_literal: null,
|
|
14
|
+
boolean_literal: null,
|
|
15
|
+
timestamp_literal: null,
|
|
16
|
+
column: null,
|
|
17
|
+
table: null,
|
|
18
|
+
join: null,
|
|
19
|
+
type_spec: null,
|
|
20
|
+
operator: null,
|
|
21
|
+
window: null,
|
|
22
|
+
unknown: null,
|
|
23
|
+
select: null,
|
|
24
|
+
create_materialized_view: null,
|
|
25
|
+
explain: null,
|
|
26
|
+
array: null,
|
|
27
|
+
binary: null,
|
|
28
|
+
bool: null,
|
|
29
|
+
double: null,
|
|
30
|
+
long: null,
|
|
31
|
+
interval: null,
|
|
32
|
+
object: null,
|
|
33
|
+
other: null,
|
|
34
|
+
string: null,
|
|
35
|
+
timestamp: null,
|
|
36
|
+
};
|
|
1
37
|
export function isAstArray(n) {
|
|
2
38
|
return Array.isArray(n);
|
|
3
39
|
}
|
package/build/nql/AstParser.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export interface Select {
|
|
|
17
17
|
from: Table[] | Raw;
|
|
18
18
|
where: BooleanExpression | Raw | null;
|
|
19
19
|
}
|
|
20
|
-
export type Output = ColumnRef | Lit | Raw
|
|
21
|
-
export type Expression =
|
|
20
|
+
export type Output = ColumnRef | Lit | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
|
|
21
|
+
export type Expression = BooleanExpression | ColumnRef | Lit | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
|
|
22
22
|
export interface Raw {
|
|
23
23
|
type: "nql";
|
|
24
24
|
as?: string;
|
|
@@ -29,11 +29,8 @@ export interface Deduplication {
|
|
|
29
29
|
expressions: Expression[];
|
|
30
30
|
}
|
|
31
31
|
export type ColumnRef = AttributeRef | DatasetColumnRef | RawRef;
|
|
32
|
-
export type ArithmeticExpression = BinaryArithmeticExpression | UnaryMinus;
|
|
33
|
-
export type BinaryArithmeticExpression = Add | Divide | Multiply | Subtract;
|
|
34
32
|
export type BooleanExpression = And | Between | Equals | NotEquals | Gt | Gte | In | IsNull | Like | Lt | Lte | Not | Or;
|
|
35
|
-
export type
|
|
36
|
-
export type Table = RosettaTable | DatasetTable | RawTable;
|
|
33
|
+
export type Table = RosettaTable | DatasetTable | RawTable | ast.NqlPlaceholder<ast.NqlAstType>;
|
|
37
34
|
export interface RosettaTable {
|
|
38
35
|
type: "rosetta_stone";
|
|
39
36
|
as?: string;
|
|
@@ -80,37 +77,6 @@ export interface Lit {
|
|
|
80
77
|
value_type: ast.NqlSimpleType;
|
|
81
78
|
value: string | null;
|
|
82
79
|
}
|
|
83
|
-
export interface FunctionCall {
|
|
84
|
-
type: "function";
|
|
85
|
-
as?: string;
|
|
86
|
-
args: Expression[];
|
|
87
|
-
name: string;
|
|
88
|
-
}
|
|
89
|
-
export interface Add {
|
|
90
|
-
type: "+";
|
|
91
|
-
as?: string;
|
|
92
|
-
operands: Expression[];
|
|
93
|
-
}
|
|
94
|
-
export interface Divide {
|
|
95
|
-
type: "/";
|
|
96
|
-
as?: string;
|
|
97
|
-
operands: Expression[];
|
|
98
|
-
}
|
|
99
|
-
export interface Multiply {
|
|
100
|
-
type: "*";
|
|
101
|
-
as?: string;
|
|
102
|
-
operands: Expression[];
|
|
103
|
-
}
|
|
104
|
-
export interface Subtract {
|
|
105
|
-
type: "-";
|
|
106
|
-
as?: string;
|
|
107
|
-
operands: Expression[];
|
|
108
|
-
}
|
|
109
|
-
export interface UnaryMinus {
|
|
110
|
-
type: "unary_-";
|
|
111
|
-
as?: string;
|
|
112
|
-
operand: Expression;
|
|
113
|
-
}
|
|
114
80
|
export interface And {
|
|
115
81
|
type: "and";
|
|
116
82
|
as?: string;
|
|
@@ -190,12 +156,7 @@ export interface Like {
|
|
|
190
156
|
value: Expression;
|
|
191
157
|
pattern: string;
|
|
192
158
|
}
|
|
193
|
-
export
|
|
194
|
-
type: "||";
|
|
195
|
-
as?: string;
|
|
196
|
-
operands: Expression[];
|
|
197
|
-
}
|
|
198
|
-
export type Node = Select | Expression | Deduplication | Table | BooleanExpression | ArithmeticExpression | StringExpression | Join | AttributeRef | DatasetColumnRef | RawRef | Lit | FunctionCall | Add | Divide | Multiply | Subtract | UnaryMinus | And | Or | Not | Between | Equals | NotEquals | Lt | Lte | Gt | Gte | In | IsNull | Like | Concat | Raw | ast.NqlBinaryOp;
|
|
159
|
+
export type Node = Select | Expression | Deduplication | Table | BooleanExpression | Join | AttributeRef | DatasetColumnRef | RawRef | Lit | And | Or | Not | Between | Equals | NotEquals | Lt | Lte | Gt | Gte | In | IsNull | Like | Raw | ast.NqlBinaryOp;
|
|
199
160
|
export declare function parseStatement(n: ast.NqlAst): Statement | Raw;
|
|
200
161
|
export declare function parseCreateMaterializedView(n: ast.NqlCreateMaterializedView): CreateMaterializedView;
|
|
201
162
|
export declare function parseExplain(n: ast.NqlExplain): Explain | Raw;
|
|
@@ -205,3 +166,4 @@ export declare function parseWhere(n: ast.NqlAst): BooleanExpression | Raw;
|
|
|
205
166
|
export declare function parseDeduplication(n: ast.NqlSelect): Deduplication | Raw | null;
|
|
206
167
|
export declare function parseExpression(n: ast.NqlAst): Expression;
|
|
207
168
|
export declare function parseOutput(n: ast.NqlAst): Output;
|
|
169
|
+
export declare function extractPlaceholders(n: Node): Node[];
|
package/build/nql/AstParser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ast from "./Ast";
|
|
2
|
+
import { traverseParsedAst } from "./AstTraverse";
|
|
2
3
|
export function parseStatement(n) {
|
|
3
4
|
switch (n.type) {
|
|
4
5
|
case "create_materialized_view":
|
|
@@ -49,6 +50,8 @@ export function parseFrom(n) {
|
|
|
49
50
|
return parseJoin(n);
|
|
50
51
|
case "table":
|
|
51
52
|
return [parseTable(n)];
|
|
53
|
+
case "placeholder":
|
|
54
|
+
return [n];
|
|
52
55
|
default:
|
|
53
56
|
return [parseRawTable(n)];
|
|
54
57
|
}
|
|
@@ -100,11 +103,11 @@ export function parseDeduplication(n) {
|
|
|
100
103
|
return n.qualify !== null ? nqlOrFail(n.qualify) : null;
|
|
101
104
|
}
|
|
102
105
|
export function parseExpression(n) {
|
|
103
|
-
const parse = oneOf(
|
|
106
|
+
const parse = oneOf(parseBooleanExpression, parseColumnRef, parseLit, parseStringExpression, parsePlaceholder);
|
|
104
107
|
return parse(n);
|
|
105
108
|
}
|
|
106
109
|
export function parseOutput(n) {
|
|
107
|
-
const parse = oneOf(parseColumnRef, parseLit);
|
|
110
|
+
const parse = oneOf(parseColumnRef, parseLit, parsePlaceholder);
|
|
108
111
|
return parse(n);
|
|
109
112
|
}
|
|
110
113
|
function parseBooleanExpression(n) {
|
|
@@ -160,99 +163,12 @@ function parseBooleanExpression(n) {
|
|
|
160
163
|
function parseStringExpression(n) {
|
|
161
164
|
switch (n.type) {
|
|
162
165
|
case "binary_op": {
|
|
163
|
-
switch (n.name) {
|
|
164
|
-
case "||":
|
|
165
|
-
return parseConcat(n);
|
|
166
|
-
default:
|
|
167
|
-
return nqlOrFail(n);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
default:
|
|
171
166
|
return nqlOrFail(n);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function parseConcat(n) {
|
|
175
|
-
const operands = parseOperands(n, "||");
|
|
176
|
-
const concat = {
|
|
177
|
-
type: "||",
|
|
178
|
-
as: n.as,
|
|
179
|
-
operands,
|
|
180
|
-
};
|
|
181
|
-
return concat;
|
|
182
|
-
}
|
|
183
|
-
function parseArithmetic(n) {
|
|
184
|
-
switch (n.type) {
|
|
185
|
-
case "binary_op": {
|
|
186
|
-
switch (n.name) {
|
|
187
|
-
case "+":
|
|
188
|
-
return parseAdd(n);
|
|
189
|
-
case "/":
|
|
190
|
-
return parseDivide(n);
|
|
191
|
-
case "*":
|
|
192
|
-
return parseMultiply(n);
|
|
193
|
-
case "-":
|
|
194
|
-
return parseSubtract(n);
|
|
195
|
-
default:
|
|
196
|
-
return nqlOrFail(n);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
case "operator": {
|
|
200
|
-
switch (n.name) {
|
|
201
|
-
case "-":
|
|
202
|
-
return parseUnaryMinus(n);
|
|
203
|
-
default:
|
|
204
|
-
return nqlOrFail(n);
|
|
205
|
-
}
|
|
206
167
|
}
|
|
207
168
|
default:
|
|
208
169
|
return nqlOrFail(n);
|
|
209
170
|
}
|
|
210
171
|
}
|
|
211
|
-
function parseAdd(n) {
|
|
212
|
-
const operands = parseOperands(n, "+");
|
|
213
|
-
const add = {
|
|
214
|
-
type: "+",
|
|
215
|
-
as: n.as,
|
|
216
|
-
operands,
|
|
217
|
-
};
|
|
218
|
-
return add;
|
|
219
|
-
}
|
|
220
|
-
function parseDivide(n) {
|
|
221
|
-
const operands = parseOperands(n, "/");
|
|
222
|
-
const divide = {
|
|
223
|
-
type: "/",
|
|
224
|
-
as: n.as,
|
|
225
|
-
operands,
|
|
226
|
-
};
|
|
227
|
-
return divide;
|
|
228
|
-
}
|
|
229
|
-
function parseMultiply(n) {
|
|
230
|
-
const operands = parseOperands(n, "*");
|
|
231
|
-
const mult = {
|
|
232
|
-
type: "*",
|
|
233
|
-
as: n.as,
|
|
234
|
-
operands,
|
|
235
|
-
};
|
|
236
|
-
return mult;
|
|
237
|
-
}
|
|
238
|
-
function parseSubtract(n) {
|
|
239
|
-
const operands = parseOperands(n, "-");
|
|
240
|
-
const sub = {
|
|
241
|
-
type: "-",
|
|
242
|
-
as: n.as,
|
|
243
|
-
operands,
|
|
244
|
-
};
|
|
245
|
-
return sub;
|
|
246
|
-
}
|
|
247
|
-
function parseUnaryMinus(n) {
|
|
248
|
-
const operand = parseExpression(n.args[0]);
|
|
249
|
-
const minus = {
|
|
250
|
-
type: "unary_-",
|
|
251
|
-
as: n.as,
|
|
252
|
-
operand,
|
|
253
|
-
};
|
|
254
|
-
return minus;
|
|
255
|
-
}
|
|
256
172
|
function parseBetween(n) {
|
|
257
173
|
const operand = parseExpression(n.args[0]);
|
|
258
174
|
const lower = parseExpression(n.args[1]);
|
|
@@ -301,20 +217,16 @@ function parseColumnRef(n) {
|
|
|
301
217
|
return nqlOrFail(n);
|
|
302
218
|
}
|
|
303
219
|
}
|
|
304
|
-
function
|
|
220
|
+
function parsePlaceholder(n) {
|
|
305
221
|
switch (n.type) {
|
|
306
|
-
case "
|
|
307
|
-
|
|
308
|
-
const fn = {
|
|
309
|
-
type: "function",
|
|
310
|
-
as: n.as,
|
|
311
|
-
args,
|
|
312
|
-
name: n.name,
|
|
313
|
-
};
|
|
314
|
-
return fn;
|
|
222
|
+
case "placeholder": {
|
|
223
|
+
return n;
|
|
315
224
|
}
|
|
316
225
|
default:
|
|
317
|
-
return
|
|
226
|
+
return {
|
|
227
|
+
type: "nql",
|
|
228
|
+
nql: "",
|
|
229
|
+
};
|
|
318
230
|
}
|
|
319
231
|
}
|
|
320
232
|
function parseLit(n) {
|
|
@@ -519,7 +431,10 @@ function parseJoin(n) {
|
|
|
519
431
|
conditionType: n.condition_type,
|
|
520
432
|
joinType: n.join_type,
|
|
521
433
|
};
|
|
522
|
-
|
|
434
|
+
// ??? I'm not sure about this logic ??? - nj
|
|
435
|
+
if (right[0].type !== "placeholder") {
|
|
436
|
+
right[0].join = join;
|
|
437
|
+
}
|
|
523
438
|
return left.concat(right);
|
|
524
439
|
}
|
|
525
440
|
function parseTable(n) {
|
|
@@ -551,6 +466,16 @@ function parseRawTable(n) {
|
|
|
551
466
|
};
|
|
552
467
|
return table;
|
|
553
468
|
}
|
|
469
|
+
export function extractPlaceholders(n) {
|
|
470
|
+
const placeholders = [];
|
|
471
|
+
const addPlaceholder = (n) => {
|
|
472
|
+
if (n.type === "placeholder") {
|
|
473
|
+
placeholders.push(n);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
traverseParsedAst(n, addPlaceholder);
|
|
477
|
+
return placeholders;
|
|
478
|
+
}
|
|
554
479
|
/**
|
|
555
480
|
* Selects the first successful expression from a list of parsers.
|
|
556
481
|
*
|
package/build/nql/AstTraverse.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
function findTraversalTargets(node) {
|
|
2
2
|
switch (node.type) {
|
|
3
|
-
case "function":
|
|
4
|
-
return node.args;
|
|
5
|
-
case "+":
|
|
6
|
-
case "/":
|
|
7
|
-
case "*":
|
|
8
|
-
case "-":
|
|
9
3
|
case "and":
|
|
10
4
|
case "or":
|
|
11
|
-
case "||":
|
|
12
5
|
return node.operands;
|
|
13
|
-
case "unary_-":
|
|
14
|
-
return [node.operand];
|
|
15
6
|
case "is_null":
|
|
16
7
|
return [node.operand];
|
|
17
8
|
case "like":
|
package/build/nql/NqlBuilder.js
CHANGED
|
@@ -64,13 +64,15 @@ function compileTableRefs(tables) {
|
|
|
64
64
|
return aliased("narrative.rosetta_stone", table.as, false);
|
|
65
65
|
case "raw_table":
|
|
66
66
|
return aliased(table.nql, table.as, false);
|
|
67
|
+
default:
|
|
68
|
+
throw new Error("unreachable");
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
function compileTableRef(table) {
|
|
70
72
|
let stmt = "";
|
|
71
73
|
// NB: by convention the first table in list will never have a join associated with it,
|
|
72
74
|
// hence the leading ' ' below does what we want.
|
|
73
|
-
if (table.join !== undefined) {
|
|
75
|
+
if ("join" in table && table.join !== undefined) {
|
|
74
76
|
switch (table.join.joinType) {
|
|
75
77
|
case "comma": {
|
|
76
78
|
stmt += ",";
|
|
@@ -111,7 +113,8 @@ function compileTableRefs(tables) {
|
|
|
111
113
|
if (
|
|
112
114
|
// Prefer explicit `join !== undefined` to the use of the `?` operator so that typescript
|
|
113
115
|
// compile knows that the join is defined in the subsequent lexical scope.
|
|
114
|
-
|
|
116
|
+
"join" in table &&
|
|
117
|
+
table.join !== undefined &&
|
|
115
118
|
table.join.condition !== null &&
|
|
116
119
|
table.join.conditionType !== "none") {
|
|
117
120
|
switch (table.join.conditionType) {
|
|
@@ -150,7 +153,7 @@ export function compileOutput(n) {
|
|
|
150
153
|
case "nql":
|
|
151
154
|
return raw(n);
|
|
152
155
|
}
|
|
153
|
-
|
|
156
|
+
throw new Error("unreachable");
|
|
154
157
|
}
|
|
155
158
|
export function compileExpression(n) {
|
|
156
159
|
switch (n.type) {
|
|
@@ -166,16 +169,6 @@ export function compileExpression(n) {
|
|
|
166
169
|
return compileBinaryOperator(">", [n.left, n.right], n.as);
|
|
167
170
|
case ">=":
|
|
168
171
|
return compileBinaryOperator(">=", [n.left, n.right], n.as);
|
|
169
|
-
case "+":
|
|
170
|
-
return compileBinaryOperator("+", n.operands, n.as);
|
|
171
|
-
case "/":
|
|
172
|
-
return compileBinaryOperator("/", n.operands, n.as);
|
|
173
|
-
case "*":
|
|
174
|
-
return compileBinaryOperator("*", n.operands, n.as);
|
|
175
|
-
case "-":
|
|
176
|
-
return compileBinaryOperator("-", n.operands, n.as);
|
|
177
|
-
case "||":
|
|
178
|
-
return compileBinaryOperator("||", n.operands, n.as);
|
|
179
172
|
case "and":
|
|
180
173
|
return compileBinaryOperator("AND", n.operands, n.as);
|
|
181
174
|
case "attribute_ref":
|
|
@@ -184,8 +177,6 @@ export function compileExpression(n) {
|
|
|
184
177
|
return compileBetween(n.operand, n.lower, n.upper, n.as);
|
|
185
178
|
case "dataset_column_ref":
|
|
186
179
|
return compileDatasetColumnRef(n.datasetId, n.column, n.as);
|
|
187
|
-
case "function":
|
|
188
|
-
return compileFunctionCall(n.name, n.args, n.as);
|
|
189
180
|
case "in":
|
|
190
181
|
return compileIn(n.expression, n.values, n.negated, n.as);
|
|
191
182
|
case "is_null":
|
|
@@ -202,10 +193,8 @@ export function compileExpression(n) {
|
|
|
202
193
|
return raw(n);
|
|
203
194
|
case "raw_ref":
|
|
204
195
|
return compileRawRef(n.nql, n.as);
|
|
205
|
-
case "unary_-":
|
|
206
|
-
return compileUnaryMinus(n.operand, n.as);
|
|
207
196
|
}
|
|
208
|
-
|
|
197
|
+
throw new Error("unreachable");
|
|
209
198
|
}
|
|
210
199
|
function compileAttributeRef(column, as, aliasParens) {
|
|
211
200
|
const segments = column.split(".");
|
|
@@ -237,35 +226,6 @@ function compileDatasetColumnRef(datasetId, column, as, aliasParen) {
|
|
|
237
226
|
}, "");
|
|
238
227
|
return aliased(`company_data.${quote(datasetId.toString())}.${formattedColumn}`, as, false);
|
|
239
228
|
}
|
|
240
|
-
function compileFunctionCall(name, args, as) {
|
|
241
|
-
// Calling convenions for functions are all over the place:
|
|
242
|
-
// - CAST is invoked as CAST($value AS $type)
|
|
243
|
-
// - EXTRACT is invoked as EXTRACT($part FROM $value)
|
|
244
|
-
// - etc., etc., with many exceptions
|
|
245
|
-
// We'll likely wanto types for all the functions we know how to support, or have every function
|
|
246
|
-
// described by the API, including providing a template for its calling convention. For now the
|
|
247
|
-
// below will get us a to a v1.
|
|
248
|
-
// Useful reference: https://calcite.apache.org/docs/reference.html#arithmetic-operators-and-functions
|
|
249
|
-
const argsNql = args.map((e) => compileExpression(e));
|
|
250
|
-
let callNql;
|
|
251
|
-
switch (name) {
|
|
252
|
-
case "CAST":
|
|
253
|
-
callNql = `CAST(${argsNql[0]} AS ${argsNql[1]})`;
|
|
254
|
-
break;
|
|
255
|
-
case "EXTRACT":
|
|
256
|
-
callNql = `EXTRACT(${argsNql[0]} FROM ${argsNql[1]})`;
|
|
257
|
-
break;
|
|
258
|
-
case "TRIM":
|
|
259
|
-
callNql = `TRIM(${argsNql[0]} ${argsNql[1]} FROM ${argsNql[2]})`;
|
|
260
|
-
break;
|
|
261
|
-
default: {
|
|
262
|
-
// hope for the best. definitely does not work in all cases.
|
|
263
|
-
callNql =
|
|
264
|
-
argsNql.length > 0 ? `${name}(${argsNql.join(", ")})` : `${name}`;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return aliased(callNql, as);
|
|
268
|
-
}
|
|
269
229
|
function compileIn(expression, values, negated, as) {
|
|
270
230
|
const op = !negated ? "IN" : "NOT IN";
|
|
271
231
|
const compiledValues = isArray(values)
|
|
@@ -342,7 +302,7 @@ function quote(s) {
|
|
|
342
302
|
return `${quoteChar}${s.replace(quoteChar, `\\${quoteChar}`)}${quoteChar}`;
|
|
343
303
|
}
|
|
344
304
|
function raw(n) {
|
|
345
|
-
return aliased(n.nql, n.as);
|
|
305
|
+
return aliased(n.nql, n.as, true);
|
|
346
306
|
}
|
|
347
307
|
function isArray(ns) {
|
|
348
308
|
return Array.isArray(ns);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NqlAst, NqlAstType, NqlPlaceholder } from "./Ast";
|
|
2
|
+
/**
|
|
3
|
+
* Traverses an AST (Abstract Syntax Tree) or an array of AST nodes, and replaces all placeholder nodes
|
|
4
|
+
* found within the tree with the result of a provided callback function. This function operates recursively,
|
|
5
|
+
* handling various types of nodes including those with `args`, `left`, `right`, `condition`, `value`, `columns`,
|
|
6
|
+
* `from`, `where`, `having`, `group_by`, `order_by`, `windows`, and `with` properties. The callback is invoked
|
|
7
|
+
* with each placeholder node encountered, and the return value of the callback replaces the placeholder in the AST.
|
|
8
|
+
*
|
|
9
|
+
* @template T A union type of `NqlAstType` or an array of `NqlAstType`, indicating the expected type(s) of the placeholder's content.
|
|
10
|
+
* @param {NqlAst|NqlAst[]} node The root AST node or an array of AST nodes to traverse.
|
|
11
|
+
* @param {function(NqlPlaceholder<T>): NqlAst} callback A function that takes a placeholder node and returns an AST node to replace it.
|
|
12
|
+
* This function is called for every placeholder node found in the AST.
|
|
13
|
+
* @returns {void} This function does not return a value; it modifies the AST nodes in place.
|
|
14
|
+
*
|
|
15
|
+
* @todo Validate that the replaced nodes adhere to the constraints of the placholder node (if any).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Given an AST node or array of nodes `ast` and a function `replacePlaceholder`:
|
|
19
|
+
* visitAndReplacePlaceholders(ast, replacePlaceholder);
|
|
20
|
+
* // This will traverse `ast`, calling `replacePlaceholder` for each placeholder node,
|
|
21
|
+
* // replacing them in place with the returned node from `replacePlaceholder`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function visitAndReplacePlaceholders<T extends NqlAstType | NqlAstType[]>(node: NqlAst | NqlAst[], callback: (placeholderNode: NqlPlaceholder<T>) => NqlAst): void;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
function isAstNode(node) {
|
|
2
|
+
if (node === undefined) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (node === null) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (typeof node === "string") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return !Array.isArray(node) && typeof node === "object" && "type" in node;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Traverses an AST (Abstract Syntax Tree) or an array of AST nodes, and replaces all placeholder nodes
|
|
15
|
+
* found within the tree with the result of a provided callback function. This function operates recursively,
|
|
16
|
+
* handling various types of nodes including those with `args`, `left`, `right`, `condition`, `value`, `columns`,
|
|
17
|
+
* `from`, `where`, `having`, `group_by`, `order_by`, `windows`, and `with` properties. The callback is invoked
|
|
18
|
+
* with each placeholder node encountered, and the return value of the callback replaces the placeholder in the AST.
|
|
19
|
+
*
|
|
20
|
+
* @template T A union type of `NqlAstType` or an array of `NqlAstType`, indicating the expected type(s) of the placeholder's content.
|
|
21
|
+
* @param {NqlAst|NqlAst[]} node The root AST node or an array of AST nodes to traverse.
|
|
22
|
+
* @param {function(NqlPlaceholder<T>): NqlAst} callback A function that takes a placeholder node and returns an AST node to replace it.
|
|
23
|
+
* This function is called for every placeholder node found in the AST.
|
|
24
|
+
* @returns {void} This function does not return a value; it modifies the AST nodes in place.
|
|
25
|
+
*
|
|
26
|
+
* @todo Validate that the replaced nodes adhere to the constraints of the placholder node (if any).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Given an AST node or array of nodes `ast` and a function `replacePlaceholder`:
|
|
30
|
+
* visitAndReplacePlaceholders(ast, replacePlaceholder);
|
|
31
|
+
* // This will traverse `ast`, calling `replacePlaceholder` for each placeholder node,
|
|
32
|
+
* // replacing them in place with the returned node from `replacePlaceholder`.
|
|
33
|
+
*/
|
|
34
|
+
export function visitAndReplacePlaceholders(node, callback) {
|
|
35
|
+
if (Array.isArray(node)) {
|
|
36
|
+
// Using for...of for array iteration
|
|
37
|
+
for (const [i, childNode] of node.entries()) {
|
|
38
|
+
if (childNode.type === "placeholder") {
|
|
39
|
+
node[i] = callback(childNode);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
visitAndReplacePlaceholders(childNode, callback);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (isAstNode(node)) {
|
|
47
|
+
if ("args" in node) {
|
|
48
|
+
node.args = node.args.map((arg) => {
|
|
49
|
+
if (arg.type === "placeholder") {
|
|
50
|
+
return callback(arg);
|
|
51
|
+
}
|
|
52
|
+
return arg;
|
|
53
|
+
});
|
|
54
|
+
for (const arg of node.args) {
|
|
55
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if ("left" in node) {
|
|
59
|
+
if (node.left.type === "placeholder") {
|
|
60
|
+
node.left = callback(node.left);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
visitAndReplacePlaceholders(node.left, callback);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if ("right" in node) {
|
|
67
|
+
if (node.right.type === "placeholder") {
|
|
68
|
+
node.right = callback(node.right);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
visitAndReplacePlaceholders(node.right, callback);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if ("condition" in node && node.condition) {
|
|
75
|
+
if (node.condition.type === "placeholder") {
|
|
76
|
+
node.condition = callback(node.condition);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
visitAndReplacePlaceholders(node.condition, callback);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if ("value" in node && isAstNode(node.value)) {
|
|
83
|
+
if (node.value.type === "placeholder") {
|
|
84
|
+
node.value = callback(node.value);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
visitAndReplacePlaceholders(node.value, callback);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if ("columns" in node) {
|
|
91
|
+
node.columns = node.columns.map((arg) => {
|
|
92
|
+
if (arg.type === "placeholder") {
|
|
93
|
+
return callback(arg);
|
|
94
|
+
}
|
|
95
|
+
return arg;
|
|
96
|
+
});
|
|
97
|
+
for (const arg of node.columns) {
|
|
98
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if ("from" in node && node.from) {
|
|
102
|
+
if (node.from.type === "placeholder") {
|
|
103
|
+
node.from = callback(node.from);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
visitAndReplacePlaceholders(node.from, callback);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if ("where" in node && node.where) {
|
|
110
|
+
if (node.where.type === "placeholder") {
|
|
111
|
+
node.where = callback(node.where);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
visitAndReplacePlaceholders(node.where, callback);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if ("having" in node && node.having) {
|
|
118
|
+
if (node.having.type === "placeholder") {
|
|
119
|
+
node.having = callback(node.having);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
visitAndReplacePlaceholders(node.having, callback);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if ("group_by" in node && node.group_by) {
|
|
126
|
+
node.group_by = node.group_by.map((arg) => {
|
|
127
|
+
if (arg.type === "placeholder") {
|
|
128
|
+
return callback(arg);
|
|
129
|
+
}
|
|
130
|
+
return arg;
|
|
131
|
+
});
|
|
132
|
+
for (const arg of node.group_by) {
|
|
133
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if ("order_by" in node && node.order_by) {
|
|
137
|
+
node.order_by = node.order_by.map((arg) => {
|
|
138
|
+
if (arg.type === "placeholder") {
|
|
139
|
+
return callback(arg);
|
|
140
|
+
}
|
|
141
|
+
return arg;
|
|
142
|
+
});
|
|
143
|
+
for (const arg of node.order_by) {
|
|
144
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if ("windows" in node && node.windows) {
|
|
148
|
+
node.windows = node.windows.map((arg) => {
|
|
149
|
+
if (arg.type === "placeholder") {
|
|
150
|
+
return callback(arg);
|
|
151
|
+
}
|
|
152
|
+
return arg;
|
|
153
|
+
});
|
|
154
|
+
for (const arg of node.windows) {
|
|
155
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if ("with" in node && node.with) {
|
|
159
|
+
node.with = node.with.map((arg) => {
|
|
160
|
+
if (arg.type === "placeholder") {
|
|
161
|
+
return callback(arg);
|
|
162
|
+
}
|
|
163
|
+
return arg;
|
|
164
|
+
});
|
|
165
|
+
for (const arg of node.with) {
|
|
166
|
+
visitAndReplacePlaceholders(arg, callback);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narrative.io/data-collaboration-sdk-ts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"repository": "github:narrative-io/data-collaboration-sdk-ts",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@types/jest": "29.5.12",
|
|
30
30
|
"babel-jest": "29.7.0",
|
|
31
31
|
"jest": "29.7.0",
|
|
32
|
-
"lefthook": "1.6.
|
|
32
|
+
"lefthook": "1.6.12",
|
|
33
33
|
"ts-jest": "29.1.2"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
@@ -42,4 +42,4 @@
|
|
|
42
42
|
"files": [
|
|
43
43
|
"build"
|
|
44
44
|
]
|
|
45
|
-
}
|
|
45
|
+
}
|