@syncular/typegen 0.6.0 → 0.8.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/README.md +47 -42
- package/dist/cli.js +1 -4
- package/dist/emit-queries-dart.js +9 -8
- package/dist/emit-queries-kotlin.js +9 -8
- package/dist/emit-queries-swift.js +9 -8
- package/dist/emit-queries.js +10 -13
- package/dist/fmt.js +29 -4
- package/dist/lsp.js +16 -28
- package/dist/query.d.ts +6 -11
- package/dist/query.js +29 -0
- package/dist/syql-ast.d.ts +12 -13
- package/dist/syql-ast.js +26 -20
- package/dist/syql-lowering.js +65 -42
- package/dist/syql-parser.d.ts +19 -18
- package/dist/syql-parser.js +341 -93
- package/dist/syql-semantics.d.ts +4 -7
- package/dist/syql-semantics.js +121 -65
- package/dist/syql-template-parser.d.ts +5 -20
- package/dist/syql-template-parser.js +116 -109
- package/dist/syql-validator.d.ts +2 -2
- package/dist/syql-validator.js +251 -224
- package/package.json +3 -3
- package/src/cli.ts +1 -3
- package/src/emit-queries-dart.ts +7 -7
- package/src/emit-queries-kotlin.ts +7 -7
- package/src/emit-queries-swift.ts +7 -7
- package/src/emit-queries.ts +9 -12
- package/src/fmt.ts +32 -4
- package/src/lsp.ts +16 -28
- package/src/query.ts +42 -12
- package/src/syql-ast.ts +38 -34
- package/src/syql-lowering.ts +69 -43
- package/src/syql-parser.ts +472 -134
- package/src/syql-semantics.ts +158 -87
- package/src/syql-template-parser.ts +119 -163
- package/src/syql-validator.ts +330 -336
package/src/syql-ast.ts
CHANGED
|
@@ -33,16 +33,8 @@ export type SyqlSemanticTemplateNode =
|
|
|
33
33
|
| {
|
|
34
34
|
readonly kind: 'when';
|
|
35
35
|
readonly controls: readonly string[];
|
|
36
|
+
readonly explicitPresence: readonly boolean[];
|
|
36
37
|
readonly body: readonly SyqlSemanticTemplateNode[];
|
|
37
|
-
}
|
|
38
|
-
| {
|
|
39
|
-
readonly kind: 'scope' | 'cover';
|
|
40
|
-
readonly bindings: readonly {
|
|
41
|
-
readonly qualifier: string;
|
|
42
|
-
readonly column: string;
|
|
43
|
-
readonly operator: 'equal' | 'in';
|
|
44
|
-
readonly values: readonly string[];
|
|
45
|
-
}[];
|
|
46
38
|
};
|
|
47
39
|
|
|
48
40
|
export interface SyqlSemanticTemplate {
|
|
@@ -56,11 +48,13 @@ export type SyqlSemanticQueryParameter =
|
|
|
56
48
|
readonly name: string;
|
|
57
49
|
readonly optional: boolean;
|
|
58
50
|
readonly type?: SyqlSemanticType;
|
|
51
|
+
readonly default?: false;
|
|
59
52
|
}
|
|
60
53
|
| {
|
|
61
|
-
readonly kind: '
|
|
54
|
+
readonly kind: 'range';
|
|
62
55
|
readonly name: string;
|
|
63
|
-
readonly optional:
|
|
56
|
+
readonly optional: boolean;
|
|
57
|
+
readonly type?: SyqlSemanticType;
|
|
64
58
|
}
|
|
65
59
|
| {
|
|
66
60
|
readonly kind: 'group';
|
|
@@ -85,8 +79,13 @@ export type SyqlSemanticDeclaration =
|
|
|
85
79
|
| {
|
|
86
80
|
readonly kind: 'query';
|
|
87
81
|
readonly name: string;
|
|
82
|
+
readonly sync: boolean;
|
|
83
|
+
readonly syncBy?: {
|
|
84
|
+
readonly qualifier: string;
|
|
85
|
+
readonly column: string;
|
|
86
|
+
};
|
|
88
87
|
readonly parameters: readonly SyqlSemanticQueryParameter[];
|
|
89
|
-
readonly
|
|
88
|
+
readonly statement: SyqlSemanticTemplate;
|
|
90
89
|
readonly sort?: {
|
|
91
90
|
readonly control: string;
|
|
92
91
|
readonly defaultProfile: string;
|
|
@@ -95,12 +94,11 @@ export type SyqlSemanticDeclaration =
|
|
|
95
94
|
readonly order: SyqlSemanticTemplate;
|
|
96
95
|
}[];
|
|
97
96
|
};
|
|
98
|
-
readonly
|
|
97
|
+
readonly limit?: {
|
|
99
98
|
readonly control: string;
|
|
100
99
|
readonly defaultSize: number;
|
|
101
100
|
readonly maxSize: number;
|
|
102
101
|
};
|
|
103
|
-
readonly identity?: readonly string[];
|
|
104
102
|
};
|
|
105
103
|
|
|
106
104
|
export interface SyqlSemanticFile {
|
|
@@ -135,8 +133,14 @@ function semanticMember(member: SyqlGroupMember): {
|
|
|
135
133
|
function semanticParameter(
|
|
136
134
|
parameter: SyqlQueryParameter,
|
|
137
135
|
): SyqlSemanticQueryParameter {
|
|
138
|
-
if (parameter.kind === '
|
|
139
|
-
|
|
136
|
+
if (parameter.kind === 'range') {
|
|
137
|
+
const type = semanticType(parameter.type);
|
|
138
|
+
return {
|
|
139
|
+
kind: 'range',
|
|
140
|
+
name: parameter.name,
|
|
141
|
+
optional: parameter.optional,
|
|
142
|
+
...(type === undefined ? {} : { type }),
|
|
143
|
+
};
|
|
140
144
|
}
|
|
141
145
|
if (parameter.kind === 'group') {
|
|
142
146
|
return {
|
|
@@ -152,6 +156,7 @@ function semanticParameter(
|
|
|
152
156
|
name: parameter.name,
|
|
153
157
|
optional: parameter.optional,
|
|
154
158
|
...(type === undefined ? {} : { type }),
|
|
159
|
+
...(parameter.default === undefined ? {} : { default: parameter.default }),
|
|
155
160
|
};
|
|
156
161
|
}
|
|
157
162
|
|
|
@@ -181,18 +186,11 @@ function semanticNode(
|
|
|
181
186
|
return {
|
|
182
187
|
kind: 'when',
|
|
183
188
|
controls: node.controls,
|
|
189
|
+
explicitPresence: node.explicitPresence,
|
|
184
190
|
body: semanticNodes(node.body.nodes),
|
|
185
191
|
};
|
|
186
192
|
}
|
|
187
|
-
return
|
|
188
|
-
kind: node.kind,
|
|
189
|
-
bindings: node.bindings.map((binding) => ({
|
|
190
|
-
qualifier: binding.column.qualifier,
|
|
191
|
-
column: binding.column.name,
|
|
192
|
-
operator: binding.operator,
|
|
193
|
-
values: binding.values.map((value) => value.name),
|
|
194
|
-
})),
|
|
195
|
-
};
|
|
193
|
+
return undefined;
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
function semanticNodes(
|
|
@@ -231,8 +229,17 @@ function semanticDeclaration(
|
|
|
231
229
|
return {
|
|
232
230
|
kind: 'query',
|
|
233
231
|
name: declaration.name,
|
|
232
|
+
sync: declaration.sync,
|
|
233
|
+
...(declaration.syncBy === undefined
|
|
234
|
+
? {}
|
|
235
|
+
: {
|
|
236
|
+
syncBy: {
|
|
237
|
+
qualifier: declaration.syncBy.qualifier,
|
|
238
|
+
column: declaration.syncBy.column,
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
234
241
|
parameters: declaration.parameters.map(semanticParameter),
|
|
235
|
-
|
|
242
|
+
statement: semanticTemplate(declaration.statement),
|
|
236
243
|
...(declaration.sort === undefined
|
|
237
244
|
? {}
|
|
238
245
|
: {
|
|
@@ -245,18 +252,15 @@ function semanticDeclaration(
|
|
|
245
252
|
})),
|
|
246
253
|
},
|
|
247
254
|
}),
|
|
248
|
-
...(declaration.
|
|
255
|
+
...(declaration.limit === undefined
|
|
249
256
|
? {}
|
|
250
257
|
: {
|
|
251
|
-
|
|
252
|
-
control: declaration.
|
|
253
|
-
defaultSize: declaration.
|
|
254
|
-
maxSize: declaration.
|
|
258
|
+
limit: {
|
|
259
|
+
control: declaration.limit.control,
|
|
260
|
+
defaultSize: declaration.limit.defaultSize,
|
|
261
|
+
maxSize: declaration.limit.maxSize,
|
|
255
262
|
},
|
|
256
263
|
}),
|
|
257
|
-
...(declaration.identity === undefined
|
|
258
|
-
? {}
|
|
259
|
-
: { identity: declaration.identity.fields }),
|
|
260
264
|
};
|
|
261
265
|
}
|
|
262
266
|
|
package/src/syql-lowering.ts
CHANGED
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
import { lexSyqlSqlSource } from './syql-lexer';
|
|
19
19
|
import type {
|
|
20
20
|
SyqlLogicalQuery,
|
|
21
|
-
SyqlLogicalReactiveNode,
|
|
22
21
|
SyqlLogicalTemplateNode,
|
|
23
22
|
SyqlLogicalWhenNode,
|
|
24
23
|
} from './syql-semantics';
|
|
24
|
+
import { syqlRangeEndBind, syqlRangeStartBind } from './syql-semantics';
|
|
25
25
|
import type { SyqlValidatedQuery } from './syql-validator';
|
|
26
26
|
|
|
27
27
|
export type SyqlLoweringErrorCode =
|
|
@@ -50,7 +50,7 @@ const DEFAULT_NAMING: QueryNamingOptions = {
|
|
|
50
50
|
backend: 'auto',
|
|
51
51
|
};
|
|
52
52
|
const DEFAULT_ENUMERATION_LIMIT = 256;
|
|
53
|
-
const
|
|
53
|
+
const LIMIT_BIND = '__syqlLimit';
|
|
54
54
|
|
|
55
55
|
interface ValueOwner {
|
|
56
56
|
readonly kind: 'value' | 'group-member';
|
|
@@ -70,17 +70,6 @@ function fail(
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function renderDirective(node: SyqlLogicalReactiveNode): string {
|
|
74
|
-
const predicates = node.directive.bindings.map((binding) => {
|
|
75
|
-
const column = `${binding.column.qualifier}.${binding.column.name}`;
|
|
76
|
-
const values = binding.values.map((value) => `:${value.name}`);
|
|
77
|
-
return binding.operator === 'equal'
|
|
78
|
-
? `${column} = ${values[0]}`
|
|
79
|
-
: `${column} in (${values.join(', ')})`;
|
|
80
|
-
});
|
|
81
|
-
return `(${predicates.join(' and ')})`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
73
|
function renderTemplate(
|
|
85
74
|
nodes: readonly SyqlLogicalTemplateNode[],
|
|
86
75
|
conditions: ReadonlyMap<SyqlLogicalWhenNode, number>,
|
|
@@ -98,9 +87,6 @@ function renderTemplate(
|
|
|
98
87
|
if (node.kind === 'predicate') {
|
|
99
88
|
return `(${renderTemplate(node.body, conditions, mode)})`;
|
|
100
89
|
}
|
|
101
|
-
if (node.kind === 'scope' || node.kind === 'cover') {
|
|
102
|
-
return renderDirective(node);
|
|
103
|
-
}
|
|
104
90
|
if (node.kind !== 'when') {
|
|
105
91
|
throw new Error('revision-1 lowering found an unknown logical node');
|
|
106
92
|
}
|
|
@@ -148,7 +134,7 @@ function composeControls(
|
|
|
148
134
|
sql: string,
|
|
149
135
|
file: string,
|
|
150
136
|
sortSql: string | undefined,
|
|
151
|
-
|
|
137
|
+
limit: SyqlValidatedQuery['limit'],
|
|
152
138
|
): string {
|
|
153
139
|
let out = sql.trim();
|
|
154
140
|
if (sortSql !== undefined) {
|
|
@@ -157,10 +143,10 @@ function composeControls(
|
|
|
157
143
|
.slice(insertion)
|
|
158
144
|
.trimStart()}`.trim();
|
|
159
145
|
}
|
|
160
|
-
if (
|
|
146
|
+
if (limit !== undefined) {
|
|
161
147
|
// `coalesce` gives the generator's metadata-only execution a valid value;
|
|
162
148
|
// runtimes still validate and bind the effective size before execution.
|
|
163
|
-
out = `${out.trimEnd()} limit min(coalesce(:${
|
|
149
|
+
out = `${out.trimEnd()} limit min(coalesce(:${LIMIT_BIND}, ${limit.defaultSize}), ${limit.maxSize})`;
|
|
164
150
|
}
|
|
165
151
|
return out;
|
|
166
152
|
}
|
|
@@ -179,7 +165,7 @@ function headersFor(
|
|
|
179
165
|
names: readonly string[],
|
|
180
166
|
valueOwners: ReadonlyMap<string, ValueOwner>,
|
|
181
167
|
conditionBinds: ReadonlyMap<string, number>,
|
|
182
|
-
|
|
168
|
+
limit: SyqlValidatedQuery['limit'],
|
|
183
169
|
query: SyqlLogicalQuery,
|
|
184
170
|
): string {
|
|
185
171
|
return names
|
|
@@ -187,7 +173,7 @@ function headersFor(
|
|
|
187
173
|
const value = valueOwners.get(name);
|
|
188
174
|
if (value !== undefined) return `-- param :${name} ${value.type}`;
|
|
189
175
|
if (conditionBinds.has(name)) return `-- param :${name} boolean`;
|
|
190
|
-
if (name ===
|
|
176
|
+
if (name === LIMIT_BIND && limit !== undefined)
|
|
191
177
|
return `-- param :${name} integer`;
|
|
192
178
|
return fail(
|
|
193
179
|
'SYQL7002_INTERNAL_LOWERING',
|
|
@@ -203,7 +189,7 @@ function planBind(
|
|
|
203
189
|
valueOwners: ReadonlyMap<string, ValueOwner>,
|
|
204
190
|
conditionBinds: ReadonlyMap<string, number>,
|
|
205
191
|
conditions: readonly SyqlLogicalWhenNode[],
|
|
206
|
-
|
|
192
|
+
limit: SyqlValidatedQuery['limit'],
|
|
207
193
|
query: SyqlLogicalQuery,
|
|
208
194
|
): QuerySyqlPlanBind {
|
|
209
195
|
const value = valueOwners.get(name);
|
|
@@ -229,12 +215,12 @@ function planBind(
|
|
|
229
215
|
controls: (conditions[condition] as SyqlLogicalWhenNode).controls,
|
|
230
216
|
};
|
|
231
217
|
}
|
|
232
|
-
if (name ===
|
|
218
|
+
if (name === LIMIT_BIND && limit !== undefined) {
|
|
233
219
|
return {
|
|
234
|
-
kind: '
|
|
220
|
+
kind: 'limit',
|
|
235
221
|
name,
|
|
236
222
|
type: 'integer',
|
|
237
|
-
input:
|
|
223
|
+
input: limit.control,
|
|
238
224
|
};
|
|
239
225
|
}
|
|
240
226
|
return fail(
|
|
@@ -253,7 +239,7 @@ function publicInputs(
|
|
|
253
239
|
const publicNames = [
|
|
254
240
|
...declaration.parameters.map((parameter) => parameter.name),
|
|
255
241
|
...(declaration.sort === undefined ? [] : [declaration.sort.control]),
|
|
256
|
-
...(declaration.
|
|
242
|
+
...(declaration.limit === undefined ? [] : [declaration.limit.control]),
|
|
257
243
|
];
|
|
258
244
|
const mapped = new Map(
|
|
259
245
|
buildNamingMap(
|
|
@@ -267,12 +253,35 @@ function publicInputs(
|
|
|
267
253
|
const result: QuerySyqlPublicInput[] = declaration.parameters.map(
|
|
268
254
|
(parameter) => {
|
|
269
255
|
const langName = mapped.get(parameter.name) as string;
|
|
270
|
-
if (parameter.kind === '
|
|
256
|
+
if (parameter.kind === 'range') {
|
|
257
|
+
const type = validated.bindTypes.get(
|
|
258
|
+
syqlRangeStartBind(parameter.name),
|
|
259
|
+
);
|
|
260
|
+
if (type === undefined) {
|
|
261
|
+
return fail(
|
|
262
|
+
'SYQL7002_INTERNAL_LOWERING',
|
|
263
|
+
query,
|
|
264
|
+
`range ${parameter.name} has no resolved element type`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
271
267
|
return {
|
|
272
|
-
kind: '
|
|
268
|
+
kind: 'group',
|
|
273
269
|
name: parameter.name,
|
|
274
270
|
langName,
|
|
275
|
-
|
|
271
|
+
members: [
|
|
272
|
+
{
|
|
273
|
+
name: 'start',
|
|
274
|
+
langName: 'start',
|
|
275
|
+
type: type.base,
|
|
276
|
+
nullable: type.nullable,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'end',
|
|
280
|
+
langName: 'end',
|
|
281
|
+
type: type.base,
|
|
282
|
+
nullable: type.nullable,
|
|
283
|
+
},
|
|
284
|
+
],
|
|
276
285
|
};
|
|
277
286
|
}
|
|
278
287
|
if (parameter.kind === 'group') {
|
|
@@ -320,7 +329,10 @@ function publicInputs(
|
|
|
320
329
|
langName,
|
|
321
330
|
type: type.base,
|
|
322
331
|
nullable: type.nullable,
|
|
323
|
-
required: !parameter.optional,
|
|
332
|
+
required: !parameter.optional && parameter.default === undefined,
|
|
333
|
+
...(parameter.default === undefined
|
|
334
|
+
? {}
|
|
335
|
+
: { default: parameter.default }),
|
|
324
336
|
};
|
|
325
337
|
},
|
|
326
338
|
);
|
|
@@ -343,13 +355,13 @@ function publicInputs(
|
|
|
343
355
|
})),
|
|
344
356
|
});
|
|
345
357
|
}
|
|
346
|
-
if (validated.
|
|
358
|
+
if (validated.limit !== undefined) {
|
|
347
359
|
result.push({
|
|
348
|
-
kind: '
|
|
349
|
-
name: validated.
|
|
350
|
-
langName: mapped.get(validated.
|
|
351
|
-
defaultSize: validated.
|
|
352
|
-
maxSize: validated.
|
|
360
|
+
kind: 'limit',
|
|
361
|
+
name: validated.limit.control,
|
|
362
|
+
langName: mapped.get(validated.limit.control) as string,
|
|
363
|
+
defaultSize: validated.limit.defaultSize,
|
|
364
|
+
maxSize: validated.limit.maxSize,
|
|
353
365
|
});
|
|
354
366
|
}
|
|
355
367
|
return result;
|
|
@@ -360,8 +372,21 @@ function valueOwners(
|
|
|
360
372
|
): ReadonlyMap<string, ValueOwner> {
|
|
361
373
|
const owners = new Map<string, ValueOwner>();
|
|
362
374
|
for (const parameter of validated.logical.declaration.parameters) {
|
|
363
|
-
if (parameter.kind === '
|
|
364
|
-
|
|
375
|
+
if (parameter.kind === 'range') {
|
|
376
|
+
for (const [name, member] of [
|
|
377
|
+
[syqlRangeStartBind(parameter.name), 'start'],
|
|
378
|
+
[syqlRangeEndBind(parameter.name), 'end'],
|
|
379
|
+
] as const) {
|
|
380
|
+
const type = validated.bindTypes.get(name);
|
|
381
|
+
if (type === undefined) continue;
|
|
382
|
+
owners.set(name, {
|
|
383
|
+
kind: 'group-member',
|
|
384
|
+
input: parameter.name,
|
|
385
|
+
member,
|
|
386
|
+
type: type.base,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
} else if (parameter.kind === 'group') {
|
|
365
390
|
for (const member of parameter.members) {
|
|
366
391
|
const type = validated.bindTypes.get(member.name);
|
|
367
392
|
if (type === undefined) continue;
|
|
@@ -415,7 +440,7 @@ function lowerStatement(
|
|
|
415
440
|
names,
|
|
416
441
|
owners,
|
|
417
442
|
conditionBinds,
|
|
418
|
-
validated.
|
|
443
|
+
validated.limit,
|
|
419
444
|
query,
|
|
420
445
|
);
|
|
421
446
|
const candidate = headers.length === 0 ? sql : `${headers}\n${sql}`;
|
|
@@ -429,7 +454,8 @@ function lowerStatement(
|
|
|
429
454
|
...naming,
|
|
430
455
|
internalParams: [
|
|
431
456
|
...conditionBinds.keys(),
|
|
432
|
-
...(validated.
|
|
457
|
+
...(validated.limit === undefined ? [] : [LIMIT_BIND]),
|
|
458
|
+
...[...owners.keys()].filter((name) => name.startsWith('__syqlRange')),
|
|
433
459
|
],
|
|
434
460
|
},
|
|
435
461
|
);
|
|
@@ -450,7 +476,7 @@ function lowerStatement(
|
|
|
450
476
|
owners,
|
|
451
477
|
conditionBinds,
|
|
452
478
|
query.conditions,
|
|
453
|
-
validated.
|
|
479
|
+
validated.limit,
|
|
454
480
|
query,
|
|
455
481
|
),
|
|
456
482
|
),
|
|
@@ -500,7 +526,7 @@ export function lowerSyqlQuery(
|
|
|
500
526
|
body,
|
|
501
527
|
logical.module.file,
|
|
502
528
|
profile.sql,
|
|
503
|
-
validated.
|
|
529
|
+
validated.limit,
|
|
504
530
|
);
|
|
505
531
|
const lowered = lowerStatement(
|
|
506
532
|
validated,
|
|
@@ -555,7 +581,7 @@ export function lowerSyqlQuery(
|
|
|
555
581
|
body,
|
|
556
582
|
logical.module.file,
|
|
557
583
|
profile.sql,
|
|
558
|
-
validated.
|
|
584
|
+
validated.limit,
|
|
559
585
|
);
|
|
560
586
|
const lowered = lowerStatement(
|
|
561
587
|
validated,
|