@sap/cds-compiler 6.7.3 → 6.9.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/CHANGELOG.md +70 -0
- package/README.md +4 -0
- package/bin/cdsc.js +5 -5
- package/bin/cdshi.js +1 -0
- package/bin/cdsse.js +1 -1
- package/lib/api/main.js +17 -9
- package/lib/api/options.js +5 -2
- package/lib/api/validate.js +1 -1
- package/lib/base/builtins.js +13 -9
- package/lib/{model → base}/csnRefs.js +8 -10
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +68 -4
- package/lib/base/messages.js +4 -2
- package/lib/{optionProcessor.js → base/optionProcessor.js} +5 -3
- package/lib/base/{model.js → specialOptions.js} +16 -39
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/featureFlags.js +54 -24
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/onConditions.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/validator.js +10 -14
- package/lib/compiler/assert-consistency.js +11 -9
- package/lib/compiler/base.js +5 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +3 -3
- package/lib/compiler/define.js +6 -3
- package/lib/{base → compiler}/dictionaries.js +4 -3
- package/lib/compiler/extend.js +121 -21
- package/lib/compiler/generate.js +2 -2
- package/lib/compiler/index.js +11 -3
- package/lib/compiler/kick-start.js +1 -1
- package/lib/compiler/lsp-api.js +3 -3
- package/lib/compiler/populate.js +6 -7
- package/lib/compiler/resolve.js +53 -36
- package/lib/compiler/shared.js +68 -18
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +28 -27
- package/lib/compiler/xpr-rewrite.js +3 -3
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
- package/lib/edm/annotations/edmJson.js +2 -4
- package/lib/edm/annotations/genericTranslation.js +51 -7
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmAnnoPreprocessor.js +1 -1
- package/lib/edm/edmInboundChecks.js +2 -1
- package/lib/edm/edmPreprocessor.js +3 -3
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +59 -108
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2052 -1965
- package/lib/gen/Dictionary.json +67 -7
- package/lib/json/from-csn.js +14 -14
- package/lib/json/to-csn.js +77 -38
- package/lib/main.js +3 -3
- package/lib/model/csnUtils.js +2 -2
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +1 -0
- package/lib/parsers/AstBuildingParser.js +83 -33
- package/lib/parsers/index.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +49 -30
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toSql.js +16 -7
- package/lib/render/utils/common.js +11 -3
- package/lib/render/utils/sql.js +14 -5
- package/lib/render/utils/standardDatabaseFunctions.js +108 -99
- package/lib/sql-identifier.js +9 -1
- package/lib/{model → tool-lib}/enrichCsn.js +3 -2
- package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
- package/lib/transform/addTenantFields.js +1 -1
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
- package/lib/transform/db/backlinks.js +2 -2
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +2 -1
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/temporal.js +30 -5
- package/lib/transform/db/views.js +16 -20
- package/lib/transform/draft/db.js +1 -2
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +6 -5
- package/lib/transform/effective/main.js +24 -4
- package/lib/transform/effective/types.js +1 -1
- package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
- package/lib/transform/forOdata.js +25 -7
- package/lib/transform/forRelationalDB.js +48 -12
- package/lib/transform/localized.js +2 -2
- package/lib/transform/odata/createForeignKeys.js +1 -1
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/odata/toFinalBaseType.js +3 -2
- package/lib/transform/odata/typesExposure.js +3 -2
- package/lib/transform/transformUtils.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +2 -1
- package/lib/transform/tupleExpansion.js +44 -4
- package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
- package/lib/transform/universalCsn/utils.js +1 -1
- package/lib/{base → utils}/lazyload.js +9 -0
- package/lib/{base → utils}/node-helpers.js +2 -0
- package/lib/utils/objectUtils.js +29 -6
- package/lib/{base → utils}/optionProcessorHelper.js +16 -6
- package/package.json +3 -40
- /package/lib/{model → base}/cloneCsn.js +0 -0
- /package/lib/{model/api.js → base/model-api.js} +0 -0
- /package/lib/{api → base}/trace.js +0 -0
- /package/lib/{model → base}/xprAsTree.js +0 -0
- /package/lib/{inspect → tool-lib}/index.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
- /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
- /package/lib/{base → utils}/shuffle.js +0 -0
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
const BaseParser = require( '../gen/BaseParser' );
|
|
4
4
|
|
|
5
5
|
const { Location } = require( '../base/location' );
|
|
6
|
-
const { dictAdd, dictAddArray } = require('../
|
|
6
|
+
const { dictAdd, dictAddArray } = require('../compiler/dictionaries');
|
|
7
7
|
const { functionsWithoutParentheses } = require('./identifiers');
|
|
8
8
|
|
|
9
9
|
const { pathName } = require('../compiler/utils');
|
|
10
10
|
const { quotedLiteralPatterns, specialFunctions } = require('../compiler/builtins');
|
|
11
|
+
const { primaryExprProperties } = require('../base/builtins');
|
|
11
12
|
|
|
12
13
|
const { parseMultiLineStringLiteral } = require('../language/multiLineStringParser');
|
|
13
14
|
const { normalizeNewLine, normalizeNumberString } = require('../language/textUtils');
|
|
@@ -39,7 +40,7 @@ const valueTokens = {
|
|
|
39
40
|
true: true,
|
|
40
41
|
};
|
|
41
42
|
const valueTokensLength = Object.values( valueTokens ).length;
|
|
42
|
-
//
|
|
43
|
+
// if expectedArray contains all the following tokens, replace them by 'Literal'
|
|
43
44
|
const literalTokens = {
|
|
44
45
|
Number: true,
|
|
45
46
|
QuotedLiteral: true,
|
|
@@ -51,6 +52,29 @@ const literalTokens = {
|
|
|
51
52
|
};
|
|
52
53
|
const literalTokensLength = Object.values( literalTokens ).length;
|
|
53
54
|
|
|
55
|
+
// if expectedArray contains all the following tokens, replace them by 'Comparison'
|
|
56
|
+
const comparisonTokens = {
|
|
57
|
+
'=': true,
|
|
58
|
+
'<>': true,
|
|
59
|
+
'>': true,
|
|
60
|
+
'>=': true,
|
|
61
|
+
'<': true,
|
|
62
|
+
'<=': true,
|
|
63
|
+
'!=': true,
|
|
64
|
+
'==': true,
|
|
65
|
+
};
|
|
66
|
+
const comparisonTokensLength = Object.values( comparisonTokens ).length;
|
|
67
|
+
|
|
68
|
+
// if expectedArray contains all the following tokens, replace them by 'Operator'
|
|
69
|
+
const operatorTokens = {
|
|
70
|
+
'+': true,
|
|
71
|
+
'-': true,
|
|
72
|
+
'*': true,
|
|
73
|
+
'/': true,
|
|
74
|
+
'||': true,
|
|
75
|
+
};
|
|
76
|
+
const operatorTokensLength = Object.values( operatorTokens ).length;
|
|
77
|
+
|
|
54
78
|
const extensionDicts = {
|
|
55
79
|
elements: true, enum: true, params: true, returns: true,
|
|
56
80
|
};
|
|
@@ -77,11 +101,6 @@ const extensionsCode = {
|
|
|
77
101
|
const PRECEDENCE_OF_EQUAL = 10;
|
|
78
102
|
|
|
79
103
|
class AstBuildingParser extends BaseParser {
|
|
80
|
-
leanConditions = {
|
|
81
|
-
afterBrace: true,
|
|
82
|
-
fail: true,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
104
|
constructor( lexer, keywords, table, options, messageFunctions ) {
|
|
86
105
|
super( lexer, keywords, table ); // lexer has file
|
|
87
106
|
this.options = options;
|
|
@@ -127,6 +146,19 @@ class AstBuildingParser extends BaseParser {
|
|
|
127
146
|
if (withoutLiteralTokens.length + literalTokensLength === array.length)
|
|
128
147
|
array = [ 'Literal', ...withoutLiteralTokens ];
|
|
129
148
|
}
|
|
149
|
+
|
|
150
|
+
const withoutComparisonTokens = raw ? array : array.filter(
|
|
151
|
+
tok => comparisonTokens[tok] !== true
|
|
152
|
+
);
|
|
153
|
+
if (withoutComparisonTokens.length + comparisonTokensLength === array.length)
|
|
154
|
+
array = [ 'Comparison', ...withoutComparisonTokens ];
|
|
155
|
+
|
|
156
|
+
const withoutOperatorTokens = raw ? array : array.filter(
|
|
157
|
+
tok => operatorTokens[tok] !== true
|
|
158
|
+
);
|
|
159
|
+
if (withoutOperatorTokens.length + operatorTokensLength === array.length)
|
|
160
|
+
array = [ 'Operator', ...withoutOperatorTokens ];
|
|
161
|
+
|
|
130
162
|
return array.map( tok => this.antlrName( tok ) )
|
|
131
163
|
.sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
|
|
132
164
|
}
|
|
@@ -165,7 +197,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
165
197
|
queryOnLeftSloppyAlias( _arg, mode ) {
|
|
166
198
|
if (mode === 'M' || this.isNoKeywordInRuleFollow( _arg, mode ))
|
|
167
199
|
return true;
|
|
168
|
-
// TODO TOOL: have a base parser method for the test
|
|
200
|
+
// TODO TOOL: have a base parser method for the test - or extra mode (recovery)
|
|
169
201
|
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
170
202
|
this.conditionStackLength == null) // after error recovery
|
|
171
203
|
return false;
|
|
@@ -202,7 +234,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
202
234
|
}
|
|
203
235
|
|
|
204
236
|
prepareSpecialFunction() {
|
|
205
|
-
const func = this.
|
|
237
|
+
const func = this.lb(2).keyword?.toUpperCase();
|
|
206
238
|
// TODO: use lower-case in specialFunctions
|
|
207
239
|
const spec = specialFunctions[func];
|
|
208
240
|
this.dynamic_.call = { func, argPos: 0 };
|
|
@@ -225,7 +257,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
225
257
|
if (generic !== 'expr')
|
|
226
258
|
return (generic === 'intro') ? 'GenericIntro' : type;
|
|
227
259
|
// if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
|
|
228
|
-
const next = this.
|
|
260
|
+
const next = this.lb(-1);
|
|
229
261
|
if (next && next.type !== ',' && next.type !== ')' &&
|
|
230
262
|
this.dynamic_.generic[next.keyword?.toUpperCase?.()] !== 'separator')
|
|
231
263
|
return 'GenericIntro';
|
|
@@ -273,8 +305,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
273
305
|
this.dynamic_.inSelectItem ??= [];
|
|
274
306
|
this.dynamic_.inSelectItem = [ ...this.dynamic_.inSelectItem ];
|
|
275
307
|
}
|
|
276
|
-
this.dynamic_.inSelectItem.push(arg ||
|
|
277
|
-
(this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand'));
|
|
308
|
+
this.dynamic_.inSelectItem.push(arg || (this.lb(2).type === '.' ? 'inline' : 'expand'));
|
|
278
309
|
}
|
|
279
310
|
|
|
280
311
|
/**
|
|
@@ -285,7 +316,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
285
316
|
modifierRestriction() {
|
|
286
317
|
const top = this.dynamic_.inSelectItem?.at(-1);
|
|
287
318
|
const insideExpand = this.dynamic_.inSelectItem?.includes('expand');
|
|
288
|
-
const next = this.
|
|
319
|
+
const next = this.la()?.keyword;
|
|
289
320
|
return insideExpand || (next !== 'key' && top === 'inline');
|
|
290
321
|
}
|
|
291
322
|
modifierRestrictionError( args, offending ) {
|
|
@@ -304,7 +335,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
304
335
|
return false;
|
|
305
336
|
// TODO: it would be best to set this.dynamic_.inSelectItem to null in filters
|
|
306
337
|
// (as <prepare>)
|
|
307
|
-
const next = this.
|
|
338
|
+
const next = this.lb(-1)?.type;
|
|
308
339
|
return next === '*' || next === '{';
|
|
309
340
|
}
|
|
310
341
|
|
|
@@ -321,21 +352,22 @@ class AstBuildingParser extends BaseParser {
|
|
|
321
352
|
// <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
|
|
322
353
|
// completion for `… default 3 not ~;` → currently just `null` but hey
|
|
323
354
|
isNegatedRelation( prec ) {
|
|
324
|
-
return this.
|
|
355
|
+
return this.lb(-1)?.keyword === 'null' ||
|
|
325
356
|
this.precNone_( prec );
|
|
326
357
|
}
|
|
327
358
|
|
|
328
359
|
// TODO: as leanCondition ? `order` should probably appear in the message for
|
|
329
360
|
// test3/Compiler/GrammarRobustness/InvalidSelectInWhere.err.cds
|
|
330
361
|
orderByLimitRestriction( _arg, mode ) {
|
|
331
|
-
if (mode &&
|
|
362
|
+
if (mode &&
|
|
363
|
+
(!this.$allowOrderByLimit && mode !== 'M' || this.precPost_( mode, 0 )))
|
|
332
364
|
return true;
|
|
333
365
|
this.$allowOrderByLimit = !mode;
|
|
334
366
|
return false;
|
|
335
367
|
}
|
|
336
368
|
|
|
337
369
|
isNamedArg() {
|
|
338
|
-
const type = this.
|
|
370
|
+
const type = this.lb(-1)?.type;
|
|
339
371
|
return type !== ':' && type !== '=>';
|
|
340
372
|
}
|
|
341
373
|
|
|
@@ -485,13 +517,13 @@ class AstBuildingParser extends BaseParser {
|
|
|
485
517
|
}
|
|
486
518
|
|
|
487
519
|
noRepeatedCardinality( _arg, mode ) {
|
|
488
|
-
if (this.
|
|
520
|
+
if (this.lb(2)?.type !== ']')
|
|
489
521
|
return false;
|
|
490
522
|
if (mode === 'M')
|
|
491
523
|
return true;
|
|
492
524
|
// currently just warning if same cardinality provided twice
|
|
493
525
|
const same = { one: '1', many: '*' }[this.la().keyword];
|
|
494
|
-
return this.
|
|
526
|
+
return this.lb(3)?.text !== same;
|
|
495
527
|
}
|
|
496
528
|
noRepeatedCardinalityError( args ) {
|
|
497
529
|
let openIdx = this.tokenIdx - 2;
|
|
@@ -569,7 +601,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
569
601
|
const { arrayAnno } = this.dynamic_;
|
|
570
602
|
if (!arrayAnno[0])
|
|
571
603
|
return arrayAnno[0] == null ? 'duplicate' : arg;
|
|
572
|
-
arrayAnno[0] = this.
|
|
604
|
+
arrayAnno[0] = this.lb(-1)?.keyword;
|
|
573
605
|
}
|
|
574
606
|
else if (arg === 'bracket') { // syntax-invalid-ellipsis
|
|
575
607
|
// closing bracket not allowed if last `...` in array is with `up to
|
|
@@ -591,7 +623,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
591
623
|
}
|
|
592
624
|
|
|
593
625
|
beforeColon() {
|
|
594
|
-
return this.
|
|
626
|
+
return this.lb(-1)?.text !== ':';
|
|
595
627
|
}
|
|
596
628
|
|
|
597
629
|
fail( _arg, mode ) {
|
|
@@ -615,13 +647,13 @@ class AstBuildingParser extends BaseParser {
|
|
|
615
647
|
return;
|
|
616
648
|
// assumes no value < -1:
|
|
617
649
|
const location = (tokenAhead > 0)
|
|
618
|
-
? this.combineLocation( this.la(), this.
|
|
619
|
-
: this.
|
|
650
|
+
? this.combineLocation( this.la(), this.lb(-tokenAhead) )
|
|
651
|
+
: this.lb(-tokenAhead).location;
|
|
620
652
|
this.error( msgId, location, textArgs );
|
|
621
653
|
}
|
|
622
654
|
|
|
623
655
|
warnIfColonFollows( name ) {
|
|
624
|
-
if (this.
|
|
656
|
+
if (this.la().type === ':') {
|
|
625
657
|
this.warning( 'syntax-missing-parens', name,
|
|
626
658
|
{ code: '@‹anno›', op: ':', newcode: '@(‹anno›…)' },
|
|
627
659
|
// eslint-disable-next-line @stylistic/max-len
|
|
@@ -643,7 +675,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
643
675
|
|
|
644
676
|
reportDubiousAnnoSpacing() {
|
|
645
677
|
const at = this.lb();
|
|
646
|
-
const before = this.
|
|
678
|
+
const before = this.lb(2);
|
|
647
679
|
if (before?.type === 'Id' && before.location.endLine === at.location.line &&
|
|
648
680
|
before.location.endCol === at.location.col) {
|
|
649
681
|
this.warning( 'syntax-expecting-anno-space', at.location, { code: '@' },
|
|
@@ -851,7 +883,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
851
883
|
classifyImplicitName( category, ref ) {
|
|
852
884
|
if (!ref || ref.path) { // TODO: func
|
|
853
885
|
const tokenIndex = ref?.path.at(-1)?.location.tokenIndex;
|
|
854
|
-
const token = this.prevTokenWithIndex( tokenIndex ) ?? this.
|
|
886
|
+
const token = this.prevTokenWithIndex( tokenIndex ) ?? this.lb();
|
|
855
887
|
const { parsedAs } = token;
|
|
856
888
|
if (parsedAs && parsedAs !== 'token' && parsedAs !== 'keyword')
|
|
857
889
|
token.parsedAs = category;
|
|
@@ -1060,7 +1092,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
1060
1092
|
|
|
1061
1093
|
// TODO: can we remove `;`/EOF from the expected-set for `annotate Foo with ⎀`?
|
|
1062
1094
|
checkWith( keyword ) {
|
|
1063
|
-
if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.
|
|
1095
|
+
if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.la().type ))
|
|
1064
1096
|
return;
|
|
1065
1097
|
const tok = this.la();
|
|
1066
1098
|
const docTokenIndex = this.docCommentIndex &&
|
|
@@ -1115,15 +1147,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1115
1147
|
}
|
|
1116
1148
|
|
|
1117
1149
|
// see also <guard=nestedExpand>
|
|
1118
|
-
reportExpandInline( column, isInline ) {
|
|
1119
|
-
// called before matching `{`
|
|
1150
|
+
reportExpandInline( column, sqlStyle, isInline ) {
|
|
1151
|
+
// called before matching `{` (or `*` with inline)
|
|
1120
1152
|
if (column.value && !column.value.path) {
|
|
1121
1153
|
// improve error location when using "inline" `.{…}` after ref (arguments and
|
|
1122
1154
|
// filters not covered, not worth the effort); after an expression where
|
|
1123
1155
|
// the last token is an identifier, not the `.` is wrong, but the `{`:
|
|
1124
|
-
const token = (isInline && this.
|
|
1125
|
-
? this.lb()
|
|
1126
|
-
: this.la();
|
|
1156
|
+
const token = (isInline && this.lb(2)?.type !== 'Id')
|
|
1157
|
+
? this.lb() // e.g. with `func() .{` → the `.` is wrong
|
|
1158
|
+
: this.la(); // after `current_date` → the `{` is wrong
|
|
1127
1159
|
this.error( 'syntax-unexpected-nested-proj', token,
|
|
1128
1160
|
{ code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }' },
|
|
1129
1161
|
'Unexpected $(CODE); nested projections can only be used after a reference' );
|
|
@@ -1132,6 +1164,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1132
1164
|
// - no errors for refs inside expand/inline, but for refs in sibling expr
|
|
1133
1165
|
// - think about: reference to these (sub) elements from other view
|
|
1134
1166
|
}
|
|
1167
|
+
else if (sqlStyle && this.la().text === '{') {
|
|
1168
|
+
this.message( 'syntax-invalid-nested-proj', this.la(),
|
|
1169
|
+
{
|
|
1170
|
+
code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }',
|
|
1171
|
+
keyword: 'select from … { … }',
|
|
1172
|
+
},
|
|
1173
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
1174
|
+
'Unexpected $(CODE); nested projections can only be used within a CDL-style $(KEYWORD)' );
|
|
1175
|
+
}
|
|
1135
1176
|
}
|
|
1136
1177
|
|
|
1137
1178
|
reportDuplicateClause( prop, erroneous, chosen, code ) {
|
|
@@ -1161,6 +1202,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1161
1202
|
}
|
|
1162
1203
|
}
|
|
1163
1204
|
|
|
1205
|
+
checkStructProps( dict ) {
|
|
1206
|
+
const special = primaryExprProperties.find( prop => dict[prop] !== undefined );
|
|
1207
|
+
if (special || dict['=']) {
|
|
1208
|
+
const prop = special || '=';
|
|
1209
|
+
this.message( 'syntax-invalid-anno-struct', dict[prop].name.location,
|
|
1210
|
+
{ '#': (special ? 'std' : 'ref'), prop } );
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1164
1214
|
// TODO: remove the check from the parser; move it to shared.js
|
|
1165
1215
|
checkTypeArgs( art ) {
|
|
1166
1216
|
const args = art.$typeArgs;
|
|
@@ -1175,7 +1225,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
1175
1225
|
locationOfPrevTokens( offset ) {
|
|
1176
1226
|
// TODO: use combined location of lb() and la() and move actions accordingly
|
|
1177
1227
|
// (for error recovery)
|
|
1178
|
-
const { file, line, col } = this.
|
|
1228
|
+
const { file, line, col } = this.lb( offset ).location;
|
|
1179
1229
|
const { endLine, endCol } = this.lb().location;
|
|
1180
1230
|
return {
|
|
1181
1231
|
file,
|
package/lib/parsers/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const lazyload = require('../
|
|
5
|
+
const lazyload = require('../utils/lazyload')( module );
|
|
6
6
|
const { CompilerAssertion } = require( '../base/error' );
|
|
7
7
|
const { createMessageFunctions } = require( '../base/messages' );
|
|
8
8
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
@@ -11,7 +11,7 @@ const { transformForRelationalDBWithCsn } = require('../transform/forRelationalD
|
|
|
11
11
|
const {
|
|
12
12
|
renderReferentialConstraint, getIdentifierUtils,
|
|
13
13
|
} = require('./utils/sql');
|
|
14
|
-
const { sortCsn } = require('../
|
|
14
|
+
const { sortCsn } = require('../base/cloneCsn');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Used only by `cdsc manageConstraints`.
|
package/lib/render/toCdl.js
CHANGED
|
@@ -20,14 +20,14 @@ const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEsc
|
|
|
20
20
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
21
21
|
const { normalizeTypeRef, forEachDefinition } = require('../model/csnUtils');
|
|
22
22
|
const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
|
|
23
|
-
const { isBetaEnabled } = require('../base/
|
|
23
|
+
const { isBetaEnabled } = require('../base/specialOptions');
|
|
24
24
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
25
25
|
const { typeParameters, specialFunctions } = require('../compiler/builtins');
|
|
26
26
|
const { isAnnotationExpression } = require('../base/builtins');
|
|
27
27
|
const { forEach } = require('../utils/objectUtils');
|
|
28
28
|
const { isBuiltinType } = require('../base/builtins');
|
|
29
|
-
const { cloneFullCsn } = require('../
|
|
30
|
-
const { getKeysDict, implicitAs } = require('../
|
|
29
|
+
const { cloneFullCsn } = require('../base/cloneCsn');
|
|
30
|
+
const { getKeysDict, implicitAs } = require('../base/csnRefs');
|
|
31
31
|
const { undelimitedIdentifierRegex } = require('../parsers/identifiers');
|
|
32
32
|
const { getNormalizedQuery } = require('../model/csnUtils');
|
|
33
33
|
const {
|
|
@@ -675,41 +675,60 @@ class CsnToCdl {
|
|
|
675
675
|
|
|
676
676
|
// If there is nothing to extend, e.g. only annotations, don't render an
|
|
677
677
|
// empty element list. This would end up in diffs with parseCdl CSN.
|
|
678
|
-
if (!ext.elements && !ext.columns && !ext.actions && !ext.enum
|
|
678
|
+
if (!ext.elements && !ext.columns && !ext.actions && !ext.enum &&
|
|
679
|
+
!ext.where && !ext.groupBy && !ext.having && !ext.orderBy && !ext.limit) {
|
|
679
680
|
result += `${ env.indent }extend ${ extName };\n`;
|
|
680
681
|
return result;
|
|
681
682
|
}
|
|
682
683
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
684
|
+
if (ext.elements || ext.columns || ext.actions || ext.enum) {
|
|
685
|
+
// We have the "old-style" prefix syntax and the "new-style" postfix "with <type>" syntax.
|
|
686
|
+
// The former one can not only extend (sub-)elements but also actions in the same statement whereas
|
|
687
|
+
// the latter cannot.
|
|
688
|
+
// If there are actions, check if there are also elements/columns, and if so, use the prefix notation.
|
|
689
|
+
const usePrefixNotation = ext.actions && (ext.columns || ext.elements);
|
|
690
|
+
if (usePrefixNotation)
|
|
691
|
+
result += `${ env.indent }extend ${ this.getExtendPrefixVariant(ext) } ${ extName } with {\n`;
|
|
692
|
+
else
|
|
693
|
+
result += `${ env.indent }extend ${ extName } with ${ this.getExtendPostfixVariant(ext) }{\n`;
|
|
692
694
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
+
if (ext.columns)
|
|
696
|
+
result += this.renderViewColumns(ext, env.withIncreasedIndent());
|
|
695
697
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
+
else if (ext.elements || ext.enum)
|
|
699
|
+
result += this.renderExtendStatementElements(ext, env);
|
|
698
700
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
701
|
+
// Not part of if/else cascade, because it may be in postfix notation.
|
|
702
|
+
if (ext.actions) {
|
|
703
|
+
const childEnv = env.withIncreasedIndent();
|
|
704
|
+
let actions = '';
|
|
705
|
+
forEach(ext.actions, (actionName, action) => {
|
|
706
|
+
actions += this.renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]), true);
|
|
707
|
+
});
|
|
708
|
+
if (!usePrefixNotation)
|
|
709
|
+
result += actions;
|
|
710
|
+
else if (actions !== '')
|
|
711
|
+
result += `${ env.indent }} actions {\n${ actions }`;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
result += `${ env.indent }}`;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
result += `${ env.indent }extend ${ extName } with`;
|
|
710
718
|
}
|
|
711
719
|
|
|
712
|
-
|
|
720
|
+
if (ext.where)
|
|
721
|
+
result += ` where ${ this.exprRenderer.renderExpr(ext.where, env.withSubPath([ 'where' ])) }`;
|
|
722
|
+
if (ext.groupBy)
|
|
723
|
+
result += ` group by ${ ext.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ') }`;
|
|
724
|
+
if (ext.having)
|
|
725
|
+
result += ` having ${ this.exprRenderer.renderExpr(ext.having, env.withSubPath([ 'having' ])) }`;
|
|
726
|
+
if (ext.orderBy)
|
|
727
|
+
result += ` order by ${ ext.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ') }`;
|
|
728
|
+
if (ext.limit)
|
|
729
|
+
result += ` ${ this.renderLimit(ext.limit, env.withSubPath([ 'limit' ])) }`;
|
|
730
|
+
|
|
731
|
+
result += ';\n';
|
|
713
732
|
return result;
|
|
714
733
|
}
|
|
715
734
|
|
|
@@ -1997,7 +2016,7 @@ class CsnToCdl {
|
|
|
1997
2016
|
return `#${ annoValue['#'] }`;
|
|
1998
2017
|
}
|
|
1999
2018
|
// Shorthand for absolute path (as string)
|
|
2000
|
-
else if (annoValue['=']
|
|
2019
|
+
else if (typeof annoValue['='] === 'string') {
|
|
2001
2020
|
if (annoValue['='].startsWith('@'))
|
|
2002
2021
|
return this.quoteAnnotationPathIfRequired(annoValue['='], env);
|
|
2003
2022
|
return this.quotePathIfRequired(annoValue['='], env);
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -18,13 +18,13 @@ const {
|
|
|
18
18
|
renderReferentialConstraint,
|
|
19
19
|
} = require('./utils/sql');
|
|
20
20
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
21
|
-
const { isDeprecatedEnabled } = require('../base/
|
|
21
|
+
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
22
22
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
23
23
|
const { timetrace } = require('../utils/timetrace');
|
|
24
24
|
|
|
25
25
|
const { smartId, delimitedId } = require('../sql-identifier');
|
|
26
26
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
27
|
-
const { pathId } = require('../
|
|
27
|
+
const { pathId } = require('../base/csnRefs');
|
|
28
28
|
|
|
29
29
|
const $PROJECTION = '$projection';
|
|
30
30
|
const $SELF = '$self';
|
package/lib/render/toSql.js
CHANGED
|
@@ -24,15 +24,15 @@ const {
|
|
|
24
24
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
25
25
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
26
26
|
const { timetrace } = require('../utils/timetrace');
|
|
27
|
-
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/
|
|
27
|
+
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/specialOptions');
|
|
28
28
|
const sqlIdentifiers = require('../sql-identifier');
|
|
29
|
-
const { sortCsn } = require('../
|
|
29
|
+
const { sortCsn } = require('../base/cloneCsn');
|
|
30
30
|
const { manageConstraints, manageConstraint } = require('./manageConstraints');
|
|
31
31
|
const { renderUniqueConstraintString, renderUniqueConstraintDrop, renderUniqueConstraintAdd } = require('./utils/unique');
|
|
32
32
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
33
|
-
const { pathId } = require('../
|
|
33
|
+
const { pathId } = require('../base/csnRefs');
|
|
34
34
|
const { transformExprOperators } = require('./utils/operators');
|
|
35
|
-
const { exprAsTree, condAsTree } = require('../
|
|
35
|
+
const { exprAsTree, condAsTree } = require('../base/xprAsTree');
|
|
36
36
|
|
|
37
37
|
class SqlRenderEnvironment {
|
|
38
38
|
indent = '';
|
|
@@ -114,7 +114,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
|
|
|
114
114
|
const {
|
|
115
115
|
error, warning, info, throwWithAnyError,
|
|
116
116
|
} = messageFunctions;
|
|
117
|
-
const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options);
|
|
117
|
+
const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options, warning);
|
|
118
118
|
const reportedMissingReplacements = Object.create(null);
|
|
119
119
|
|
|
120
120
|
const exprRenderer = createExpressionRenderer({
|
|
@@ -795,7 +795,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
|
|
|
795
795
|
return '';
|
|
796
796
|
env = env.withSubPath([ 'elements', elementName ]);
|
|
797
797
|
const isPostgresAlterColumn = env.alterMode && env.changeType === 'migration' && options.sqlDialect === 'postgres';
|
|
798
|
-
const quotedElementName = quoteSqlId(elementName);
|
|
798
|
+
const quotedElementName = quoteSqlId(elementName, env.path);
|
|
799
799
|
if (duplicateChecker)
|
|
800
800
|
duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
|
|
801
801
|
|
|
@@ -1422,6 +1422,15 @@ function toSqlDdl( csn, options, messageFunctions ) {
|
|
|
1422
1422
|
if (isBuiltinType(elm.type)) {
|
|
1423
1423
|
// cds.Integer => render as INTEGER (no quotes)
|
|
1424
1424
|
result += renderBuiltinType(elm.type);
|
|
1425
|
+
|
|
1426
|
+
// On H2 & sqlite the underlying data type for a cds.Vector is a binary, needing a length.
|
|
1427
|
+
// The data is stored as a vector of 4 byte real numbers with dimensions given by length. Additional 4 bytes are used to store the dimensions.
|
|
1428
|
+
if (
|
|
1429
|
+
elm.type === 'cds.Vector' && elm.length &&
|
|
1430
|
+
(options.sqlDialect === 'h2' || options.sqlDialect === 'sqlite')
|
|
1431
|
+
)
|
|
1432
|
+
elm.length = elm.length * 4 + 4;
|
|
1433
|
+
|
|
1425
1434
|
result += renderTypeParameters(elm);
|
|
1426
1435
|
}
|
|
1427
1436
|
else {
|
|
@@ -1451,7 +1460,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
|
|
|
1451
1460
|
* @returns {string} Rendered type
|
|
1452
1461
|
*/
|
|
1453
1462
|
function renderBuiltinType( typeName ) {
|
|
1454
|
-
const types = cdsToSqlTypes[options.sqlDialect];
|
|
1463
|
+
const types = cdsToSqlTypes[options.sqliteRealAffinityForDecimal && options.sqlDialect === 'sqlite' ? 'sqlitereal' : options.sqlDialect];
|
|
1455
1464
|
const result = types?.[typeName] || cdsToSqlTypes.standard[typeName];
|
|
1456
1465
|
if (!result && options.testMode)
|
|
1457
1466
|
throw new CompilerAssertion(`Expected to find a type mapping for ${ typeName }`);
|
|
@@ -21,7 +21,7 @@ const {
|
|
|
21
21
|
hasValidSkipOrExists, forEachDefinition, getNamespace, getUnderscoredName,
|
|
22
22
|
} = require('../../model/csnUtils');
|
|
23
23
|
|
|
24
|
-
const { implicitAs } = require('../../
|
|
24
|
+
const { implicitAs } = require('../../base/csnRefs');
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Render the given function
|
|
@@ -294,7 +294,7 @@ const cdsToSqlTypes = {
|
|
|
294
294
|
'cds.Binary': 'BINARY_BLOB',
|
|
295
295
|
'cds.hana.BINARY': 'BINARY_BLOB',
|
|
296
296
|
'cds.hana.SMALLDECIMAL': 'SMALLDECIMAL',
|
|
297
|
-
'cds.Vector': '
|
|
297
|
+
'cds.Vector': 'BLOB',
|
|
298
298
|
'cds.Map': 'JSON_TEXT', // '_TEXT' suffix required for text affinity
|
|
299
299
|
},
|
|
300
300
|
plain: {
|
|
@@ -309,6 +309,7 @@ const cdsToSqlTypes = {
|
|
|
309
309
|
'cds.DecimalFloat': 'DECFLOAT',
|
|
310
310
|
'cds.DateTime': 'TIMESTAMP(0)',
|
|
311
311
|
'cds.Timestamp': 'TIMESTAMP(7)',
|
|
312
|
+
'cds.Vector': 'VARBINARY',
|
|
312
313
|
'cds.Map': 'JSON',
|
|
313
314
|
'cds.UInt8': 'SMALLINT', // See #13870; not equivalent, but smallint can hold >byte
|
|
314
315
|
},
|
|
@@ -320,11 +321,18 @@ const cdsToSqlTypes = {
|
|
|
320
321
|
'cds.Binary': 'BYTEA',
|
|
321
322
|
'cds.Double': 'FLOAT8',
|
|
322
323
|
'cds.UInt8': 'SMALLINT', // See #13870; not equivalent, but smallint can hold >byte
|
|
323
|
-
'cds.Vector': '
|
|
324
|
+
'cds.Vector': 'VECTOR', // vector is the datatype for [pgvector](https://github.com/pgvector/pgvector?tab=readme-ov-file)
|
|
324
325
|
'cds.Map': 'JSONB',
|
|
325
326
|
},
|
|
326
327
|
};
|
|
327
328
|
|
|
329
|
+
cdsToSqlTypes.sqlitereal = {
|
|
330
|
+
...cdsToSqlTypes.sqlite,
|
|
331
|
+
'cds.Decimal': 'REAL_DECIMAL',
|
|
332
|
+
'cds.DecimalFloat': 'REAL_DECIMAL',
|
|
333
|
+
'cds.hana.SMALLDECIMAL': 'REAL_SMALLDECIMAL',
|
|
334
|
+
};
|
|
335
|
+
|
|
328
336
|
// Type mapping from cds type names to HDBCDS type names:
|
|
329
337
|
// Only those types, that need mapping, are listed.
|
|
330
338
|
const cdsToHdbcdsTypes = {
|
package/lib/render/utils/sql.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const { getResultingName } = require('../../model/csnUtils');
|
|
6
|
-
const { smartId, delimitedId } = require('../../sql-identifier');
|
|
6
|
+
const { smartId, delimitedId, sqlDialects } = require('../../sql-identifier');
|
|
7
7
|
const { ModelError } = require('../../base/error');
|
|
8
|
-
const { setProp } = require('../../
|
|
8
|
+
const { setProp } = require('../../utils/objectUtils');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Render a given referential constraint as part of a SQL CREATE TABLE statement, or as .hdbconstraint artefact.
|
|
@@ -66,10 +66,12 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
|
|
|
66
66
|
/**
|
|
67
67
|
* Get functions which can be used to prepare and quote SQL identifiers based on the options provided.
|
|
68
68
|
*
|
|
69
|
+
* @param {CSN.Model} csn
|
|
69
70
|
* @param {CSN.Options} options
|
|
71
|
+
* @param {function} warning
|
|
70
72
|
* @returns quoteSqlId and prepareIdentifier function
|
|
71
73
|
*/
|
|
72
|
-
function getIdentifierUtils( csn, options ) {
|
|
74
|
+
function getIdentifierUtils( csn, options, warning ) {
|
|
73
75
|
return { quoteSqlId, prepareIdentifier, renderArtifactName };
|
|
74
76
|
/**
|
|
75
77
|
* Return 'name' with appropriate "-quotes.
|
|
@@ -81,10 +83,17 @@ function getIdentifierUtils( csn, options ) {
|
|
|
81
83
|
* Complain about names that collide with known SQL keywords or functions
|
|
82
84
|
*
|
|
83
85
|
* @param {string} name Identifier to quote
|
|
86
|
+
* @param {CSN.Path} path Path to the identifier in the CSN model (for warnings)
|
|
84
87
|
* @returns {string} Quoted identifier
|
|
85
88
|
*/
|
|
86
|
-
function quoteSqlId( name ) {
|
|
89
|
+
function quoteSqlId( name, path ) {
|
|
87
90
|
name = prepareIdentifier(name);
|
|
91
|
+
const s = sqlDialects[options.sqlDialect];
|
|
92
|
+
if ( warning && name.length > s.maxIdentifierLength ) {
|
|
93
|
+
warning( 'query-invalid-identifier',
|
|
94
|
+
path,
|
|
95
|
+
{ name: options.sqlDialect, number: s.maxIdentifierLength });
|
|
96
|
+
}
|
|
88
97
|
|
|
89
98
|
switch (options.sqlMapping) {
|
|
90
99
|
case 'plain':
|
|
@@ -139,7 +148,7 @@ function getIdentifierUtils( csn, options ) {
|
|
|
139
148
|
* @returns {string} Artifact name
|
|
140
149
|
*/
|
|
141
150
|
function renderArtifactName( artifactName ) {
|
|
142
|
-
return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName));
|
|
151
|
+
return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName), [ 'definitions', artifactName ] );
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|