@sap/cds-compiler 5.9.4 → 6.0.12
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 +117 -319
- package/README.md +1 -1
- package/bin/cds_update_identifiers.js +3 -5
- package/bin/cdsc.js +24 -9
- package/bin/cdshi.js +1 -1
- package/bin/cdsse.js +4 -4
- package/doc/CHANGELOG_BETA.md +11 -0
- package/doc/CHANGELOG_DEPRECATED.md +29 -0
- package/lib/api/main.js +8 -5
- package/lib/api/options.js +12 -10
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +191 -99
- package/lib/base/messages.js +35 -21
- package/lib/base/model.js +14 -24
- package/lib/checks/actionsFunctions.js +10 -20
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/elements.js +35 -10
- package/lib/checks/enums.js +31 -0
- package/lib/checks/foreignKeys.js +2 -2
- package/lib/checks/hasPersistedElements.js +5 -0
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/managedWithoutKeys.js +5 -4
- package/lib/checks/queryNoDbArtifacts.js +10 -8
- package/lib/checks/types.js +5 -5
- package/lib/checks/validator.js +6 -4
- package/lib/compiler/assert-consistency.js +13 -9
- package/lib/compiler/checks.js +20 -52
- package/lib/compiler/define.js +31 -6
- package/lib/compiler/extend.js +5 -1
- package/lib/compiler/generate.js +14 -17
- package/lib/compiler/populate.js +8 -31
- package/lib/compiler/propagator.js +21 -35
- package/lib/compiler/resolve.js +64 -29
- package/lib/compiler/shared.js +16 -4
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +1 -1
- package/lib/edm/annotations/edmJson.js +23 -20
- package/lib/edm/annotations/genericTranslation.js +12 -10
- package/lib/edm/csn2edm.js +50 -56
- package/lib/edm/edm.js +33 -28
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +54 -88
- package/lib/edm/edmUtils.js +9 -12
- package/lib/gen/BaseParser.js +63 -52
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1153 -1165
- package/lib/gen/Dictionary.json +21 -1
- package/lib/json/from-csn.js +70 -43
- package/lib/json/to-csn.js +6 -8
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/main.d.ts +58 -24
- package/lib/model/cloneCsn.js +3 -0
- package/lib/model/csnUtils.js +28 -39
- package/lib/model/xprAsTree.js +23 -9
- package/lib/modelCompare/compare.js +5 -4
- package/lib/optionProcessor.js +24 -17
- package/lib/parsers/AstBuildingParser.js +81 -25
- package/lib/parsers/XprTree.js +57 -3
- package/lib/parsers/identifiers.js +1 -1
- package/lib/parsers/index.js +0 -3
- package/lib/render/manageConstraints.js +25 -25
- package/lib/render/toCdl.js +173 -170
- package/lib/render/toHdbcds.js +126 -128
- package/lib/render/toRename.js +7 -7
- package/lib/render/toSql.js +128 -125
- package/lib/render/utils/common.js +47 -22
- package/lib/render/utils/delta.js +25 -25
- package/lib/render/utils/operators.js +2 -2
- package/lib/render/utils/pretty.js +5 -5
- package/lib/render/utils/sql.js +13 -13
- package/lib/render/utils/standardDatabaseFunctions.js +115 -103
- package/lib/render/utils/unique.js +4 -4
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +2 -2
- package/lib/transform/db/associations.js +6 -7
- package/lib/transform/db/assocsToQueries/utils.js +4 -5
- package/lib/transform/db/backlinks.js +12 -9
- package/lib/transform/db/cdsPersistence.js +8 -7
- package/lib/transform/db/constraints.js +13 -10
- package/lib/transform/db/expansion.js +7 -3
- package/lib/transform/db/flattening.js +4 -14
- package/lib/transform/db/processSqlServices.js +2 -1
- package/lib/transform/db/temporal.js +5 -7
- package/lib/transform/db/views.js +2 -4
- package/lib/transform/draft/db.js +8 -8
- package/lib/transform/draft/odata.js +10 -7
- package/lib/transform/forOdata.js +10 -5
- package/lib/transform/forRelationalDB.js +5 -75
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/createForeignKeys.js +11 -10
- package/lib/transform/odata/flattening.js +8 -4
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/transformUtils.js +4 -8
- package/lib/transform/translateAssocsToJoins.js +14 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
- package/lib/utils/objectUtils.js +0 -17
- package/package.json +10 -13
- package/share/messages/def-upcoming-virtual-change.md +1 -1
- package/LICENSE +0 -37
- package/bin/cds_remove_invalid_whitespace.js +0 -138
- package/doc/CHANGELOG_ARCHIVE.md +0 -3604
- package/lib/gen/genericAntlrParser.js +0 -3
- package/lib/gen/language.checksum +0 -1
- package/lib/gen/language.interp +0 -456
- package/lib/gen/language.tokens +0 -180
- package/lib/gen/languageLexer.interp +0 -439
- package/lib/gen/languageLexer.js +0 -1483
- package/lib/gen/languageLexer.tokens +0 -167
- package/lib/gen/languageParser.js +0 -24941
- package/lib/language/antlrParser.js +0 -205
- package/lib/language/errorStrategy.js +0 -646
- package/lib/language/genericAntlrParser.js +0 -1572
- package/lib/parsers/CdlGrammar.g4 +0 -2070
package/lib/model/csnUtils.js
CHANGED
|
@@ -63,7 +63,6 @@ function getUtils( model, universalReady ) {
|
|
|
63
63
|
getContextOfArtifact,
|
|
64
64
|
addStringAnnotationTo,
|
|
65
65
|
getServiceName,
|
|
66
|
-
hasAnnotationValue,
|
|
67
66
|
getFinalTypeInfo,
|
|
68
67
|
get$combined,
|
|
69
68
|
getQueryPrimarySource,
|
|
@@ -238,7 +237,8 @@ function getUtils( model, universalReady ) {
|
|
|
238
237
|
// Return true if 'node' is a managed association element
|
|
239
238
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
240
239
|
function isManagedAssociation( node ) {
|
|
241
|
-
|
|
240
|
+
// Since v6, managed to-many associations don't get any keys, hence don't require it.
|
|
241
|
+
return node.target !== undefined && node.on === undefined;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
/**
|
|
@@ -631,34 +631,15 @@ function traverseFrom( from, queryCallback, path = [] ) {
|
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
|
|
635
634
|
/**
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
* | Expected
|
|
639
|
-
* | true | false | null | arb val
|
|
640
|
-
* Anno Val |-------|-------|-------|--------
|
|
641
|
-
* true | true | false | false | false
|
|
642
|
-
* false | false | true | false | false
|
|
643
|
-
* null | false | true | true | false
|
|
644
|
-
* arb val | false | false | false | true/false
|
|
635
|
+
* Returns true if the artifact should be skipped during persistence.
|
|
636
|
+
* Respects special value `if-unused` by Node runtime.
|
|
645
637
|
*
|
|
646
|
-
*
|
|
647
|
-
* 'null' and 'false'. Expecting 'null' for an annotation value 'false' returns
|
|
648
|
-
* 'false'.
|
|
649
|
-
*
|
|
650
|
-
* @param {CSN.Artifact} artifact
|
|
651
|
-
* @param {string} annotationName Name of the annotation (including the at-sign)
|
|
652
|
-
* @param {any} expected
|
|
653
|
-
* @param {boolean} caseInsensitive
|
|
638
|
+
* @param {CSN.Artifact} art
|
|
654
639
|
* @returns {boolean}
|
|
655
640
|
*/
|
|
656
|
-
function
|
|
657
|
-
|
|
658
|
-
return artifact[annotationName] === expected || artifact[annotationName] === null;
|
|
659
|
-
else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
|
|
660
|
-
return artifact[annotationName].toLowerCase() === expected.toLowerCase();
|
|
661
|
-
return artifact[annotationName] === expected;
|
|
641
|
+
function hasPersistenceSkipAnnotation( art ) {
|
|
642
|
+
return art['@cds.persistence.skip'] && art['@cds.persistence.skip'] !== 'if-unused';
|
|
662
643
|
}
|
|
663
644
|
|
|
664
645
|
/**
|
|
@@ -682,8 +663,7 @@ function isEdmPropertyRendered( elementCsn, options ) {
|
|
|
682
663
|
const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
|
|
683
664
|
const isNavigable = elementCsn.target
|
|
684
665
|
? (elementCsn['@odata.navigable'] === undefined ||
|
|
685
|
-
|
|
686
|
-
(elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
|
|
666
|
+
(elementCsn['@odata.navigable'] === null || !!elementCsn['@odata.navigable'])) : true;
|
|
687
667
|
// Foreign Keys can be ignored
|
|
688
668
|
if (elementCsn['@odata.foreignKey4'])
|
|
689
669
|
return isNotIgnored && renderForeignKey;
|
|
@@ -920,7 +900,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
|
|
|
920
900
|
* @returns {boolean}
|
|
921
901
|
*/
|
|
922
902
|
function isPersistedOnDatabase( art ) {
|
|
923
|
-
return !(art.kind === 'entity' && (art.abstract ||
|
|
903
|
+
return !(art.kind === 'entity' && (art.abstract || hasPersistenceSkipAnnotation(art)));
|
|
924
904
|
}
|
|
925
905
|
/**
|
|
926
906
|
* Check if the given artifact will be persisted on the database via `CREATE VIEW`
|
|
@@ -932,9 +912,9 @@ function isPersistedAsView( artifact ) {
|
|
|
932
912
|
return artifact && artifact.kind === 'entity' &&
|
|
933
913
|
!artifact.$ignore &&
|
|
934
914
|
!artifact.abstract &&
|
|
935
|
-
((artifact.query || artifact.projection) && !
|
|
936
|
-
!
|
|
937
|
-
!
|
|
915
|
+
((artifact.query || artifact.projection) && !artifact['@cds.persistence.table']) &&
|
|
916
|
+
!hasPersistenceSkipAnnotation(artifact) &&
|
|
917
|
+
!artifact['@cds.persistence.exists'];
|
|
938
918
|
}
|
|
939
919
|
/**
|
|
940
920
|
* Check if the given artifact will be persisted on the database via `CREATE TABLE`
|
|
@@ -946,9 +926,9 @@ function isPersistedAsTable( artifact ) {
|
|
|
946
926
|
return artifact.kind === 'entity' &&
|
|
947
927
|
!artifact.$ignore &&
|
|
948
928
|
!artifact.abstract &&
|
|
949
|
-
(!artifact.query && !artifact.projection ||
|
|
950
|
-
!
|
|
951
|
-
!
|
|
929
|
+
(!artifact.query && !artifact.projection || artifact['@cds.persistence.table']) &&
|
|
930
|
+
!hasPersistenceSkipAnnotation(artifact) &&
|
|
931
|
+
!artifact['@cds.persistence.exists'];
|
|
952
932
|
}
|
|
953
933
|
|
|
954
934
|
/**
|
|
@@ -1127,8 +1107,9 @@ function moveAnnotationsAndDoc( sourceNode, targetNode, overwrite = false ) {
|
|
|
1127
1107
|
* filter: Positive filter. If it returns true, annotations for the referenced artifact
|
|
1128
1108
|
* will be applied.
|
|
1129
1109
|
* applyToElements: Whether to apply annotations to elements or only to artifacts
|
|
1110
|
+
* @param {Function} error The error function
|
|
1130
1111
|
*/
|
|
1131
|
-
function applyAnnotationsFromExtensions( csn, config ) {
|
|
1112
|
+
function applyAnnotationsFromExtensions( csn, config, error ) {
|
|
1132
1113
|
if (!csn.extensions)
|
|
1133
1114
|
return;
|
|
1134
1115
|
|
|
@@ -1137,6 +1118,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1137
1118
|
for (let i = 0; i < csn.extensions.length; ++i) {
|
|
1138
1119
|
const ext = csn.extensions[i];
|
|
1139
1120
|
const name = ext.annotate || ext.extend;
|
|
1121
|
+
if (isAnnotateDraftAdminDataWithTenantIndependent(ext))
|
|
1122
|
+
error(null, [ 'extensions', i ], { name }, '$(NAME) can\'t be tenant independent');
|
|
1140
1123
|
if (name && filter(name)) {
|
|
1141
1124
|
const def = csn.definitions[name];
|
|
1142
1125
|
if (def) {
|
|
@@ -1153,6 +1136,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1153
1136
|
}
|
|
1154
1137
|
|
|
1155
1138
|
csn.extensions = csn.extensions.filter(ext => ext);
|
|
1139
|
+
if (!csn.extensions.length)
|
|
1140
|
+
delete csn.extensions;
|
|
1156
1141
|
|
|
1157
1142
|
function applyAnnotationsToElements( ext, def ) {
|
|
1158
1143
|
// Only the definition is arrayed but the extension is not since
|
|
@@ -1177,6 +1162,10 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1177
1162
|
if (Object.keys(ext.elements).length === 0)
|
|
1178
1163
|
delete ext.elements;
|
|
1179
1164
|
}
|
|
1165
|
+
|
|
1166
|
+
function isAnnotateDraftAdminDataWithTenantIndependent( ext ) {
|
|
1167
|
+
return ext.annotate && ext.annotate.endsWith('.DraftAdministrativeData') && ext['@cds.tenant.independent'];
|
|
1168
|
+
}
|
|
1180
1169
|
}
|
|
1181
1170
|
|
|
1182
1171
|
/**
|
|
@@ -1187,8 +1176,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1187
1176
|
*/
|
|
1188
1177
|
function hasValidSkipOrExists( artifact ) {
|
|
1189
1178
|
return artifact.kind === 'entity' &&
|
|
1190
|
-
(
|
|
1191
|
-
|
|
1179
|
+
(artifact['@cds.persistence.exists'] ||
|
|
1180
|
+
hasPersistenceSkipAnnotation(artifact));
|
|
1192
1181
|
}
|
|
1193
1182
|
|
|
1194
1183
|
/**
|
|
@@ -1419,7 +1408,7 @@ module.exports = {
|
|
|
1419
1408
|
forEachMember,
|
|
1420
1409
|
forEachMemberRecursively,
|
|
1421
1410
|
forAllQueries,
|
|
1422
|
-
|
|
1411
|
+
hasPersistenceSkipAnnotation,
|
|
1423
1412
|
isEdmPropertyRendered,
|
|
1424
1413
|
getArtifactDatabaseNameOf,
|
|
1425
1414
|
getResultingName,
|
package/lib/model/xprAsTree.js
CHANGED
|
@@ -7,19 +7,26 @@
|
|
|
7
7
|
//
|
|
8
8
|
// Function/assoc arguments and filter conditions are
|
|
9
9
|
// - traversed with expressionAsTree() and conditionAsTree(),
|
|
10
|
-
// - not traversed with exprAsTree() and condAsTree()
|
|
10
|
+
// - not traversed with exprAsTree() and condAsTree(), you might need to call
|
|
11
|
+
// splitClauses() to cover `order by` in the last function argument
|
|
11
12
|
|
|
12
13
|
'use strict';
|
|
13
14
|
|
|
14
|
-
const
|
|
15
|
+
const { csnAsTree, splitClauses } = require( '../parsers/XprTree' );
|
|
15
16
|
|
|
16
17
|
function conditionAsTree( args ) {
|
|
17
18
|
args.forEach( expressionAsTree );
|
|
18
|
-
return
|
|
19
|
+
return asTree( args );
|
|
19
20
|
}
|
|
20
21
|
function condAsTree( args ) {
|
|
21
22
|
args.forEach( exprAsTree );
|
|
22
|
-
return
|
|
23
|
+
return asTree( args );
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function asTree( args ) {
|
|
27
|
+
return (args.length === 2 && args[0] === 'over' && args[1]?.xpr && !args[1].func)
|
|
28
|
+
? [ 'over', { xpr: splitClauses( args[1].xpr, true ) } ]
|
|
29
|
+
: csnAsTree( args );
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
function expressionAsTree( expr ) {
|
|
@@ -31,11 +38,17 @@ function expressionAsTree( expr ) {
|
|
|
31
38
|
}
|
|
32
39
|
if (expr.list) // expression, ref
|
|
33
40
|
expr.list.forEach( expressionAsTree );
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
const { args } = expr;
|
|
42
|
+
if (args) { // expression, ref
|
|
43
|
+
if (!Array.isArray( args )) {
|
|
44
|
+
Object.values( args ).forEach( expressionAsTree );
|
|
45
|
+
}
|
|
46
|
+
else if (args.length) {
|
|
47
|
+
args.forEach( expressionAsTree );
|
|
48
|
+
const last = args.at( -1 );
|
|
49
|
+
if (last?.xpr && last.xpr.length > 4) // order by in last argument
|
|
50
|
+
last.xpr = splitClauses( last.xpr, true );
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
53
|
if (expr.ref) // expression
|
|
41
54
|
expr.ref.forEach( expressionAsTree );
|
|
@@ -68,4 +81,5 @@ module.exports = {
|
|
|
68
81
|
condAsTree,
|
|
69
82
|
expressionAsTree,
|
|
70
83
|
exprAsTree,
|
|
84
|
+
splitClauses: nodes => splitClauses( nodes, true ),
|
|
71
85
|
};
|
|
@@ -57,20 +57,21 @@ function validateCsnVersions(beforeModel, afterModel, options) {
|
|
|
57
57
|
|
|
58
58
|
if (!beforeVersionParts || beforeVersionParts.length < 2) {
|
|
59
59
|
const { error, throwWithAnyError } = makeMessageFunction(beforeModel, options, 'modelCompare');
|
|
60
|
-
error(
|
|
60
|
+
error('api-invalid-version', null, { version: beforeVersion || 'unknown' });
|
|
61
61
|
throwWithAnyError();
|
|
62
62
|
}
|
|
63
63
|
if (!afterVersionParts || afterVersionParts.length < 2) {
|
|
64
64
|
const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
|
|
65
|
-
error(
|
|
65
|
+
error('api-invalid-version', null, { version: afterVersion || 'unknown' });
|
|
66
66
|
throwWithAnyError();
|
|
67
67
|
}
|
|
68
68
|
if (beforeVersionParts[0] > afterVersionParts[0] && !(options && options.allowCsnDowngrade)) {
|
|
69
69
|
const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
|
|
70
70
|
|
|
71
71
|
const { version } = require('../../package.json');
|
|
72
|
-
error(
|
|
73
|
-
|
|
72
|
+
error('api-invalid-version', null, {
|
|
73
|
+
'#': 'migrationComparison', value: afterVersion, othervalue: beforeVersion, version,
|
|
74
|
+
});
|
|
74
75
|
throwWithAnyError();
|
|
75
76
|
}
|
|
76
77
|
}
|
package/lib/optionProcessor.js
CHANGED
|
@@ -18,7 +18,6 @@ const { availableBetaFlags } = require('./base/model');
|
|
|
18
18
|
const optionProcessor = createOptionProcessor();
|
|
19
19
|
|
|
20
20
|
// General options
|
|
21
|
-
// FIXME: Since they mainly affect the compiler, they could also live near main.compile
|
|
22
21
|
optionProcessor
|
|
23
22
|
.option('-h, --help')
|
|
24
23
|
.option('-v, --version')
|
|
@@ -40,17 +39,18 @@ optionProcessor
|
|
|
40
39
|
.option(' --debug <id-list>')
|
|
41
40
|
.option('-E, --enrich-csn')
|
|
42
41
|
.option('-R, --raw-output <name>')
|
|
43
|
-
.option(' --new-parser')
|
|
44
42
|
.option(' --internal-msg')
|
|
45
43
|
.option(' --beta-mode')
|
|
46
44
|
.option(' --beta <list>')
|
|
47
45
|
.option(' --deprecated <list>')
|
|
48
46
|
.option(' --direct-backend')
|
|
47
|
+
.option(' --direct-messages')
|
|
49
48
|
.option(' --fallback-parser <type>', { valid: [ 'auto!', 'cdl', 'csn', 'csn!' ] })
|
|
50
49
|
.option(' --shuffle <seed>') // 0 | 1..4294967296
|
|
51
50
|
.option(' --test-mode')
|
|
52
51
|
.option(' --test-sort-csn')
|
|
53
52
|
.option(' --doc-comment')
|
|
53
|
+
.option(' --propagate-doc-comments')
|
|
54
54
|
.option(' --add-texts-language-assoc')
|
|
55
55
|
.option(' --localized-without-coalesce')
|
|
56
56
|
.option(' --tenant-discriminator')
|
|
@@ -119,7 +119,6 @@ optionProcessor
|
|
|
119
119
|
with name = "+", write complete XSN, long!
|
|
120
120
|
--tenant-discriminator Add tenant fields to entities
|
|
121
121
|
--internal-msg Write raw messages with call stack to <stdout>/<stderr>
|
|
122
|
-
--new-parser Use the new CDL parser
|
|
123
122
|
--beta-mode Enable all unsupported, incomplete (beta) features
|
|
124
123
|
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
|
|
125
124
|
Valid values are:
|
|
@@ -130,8 +129,6 @@ optionProcessor
|
|
|
130
129
|
.join(`\n${ ' '.repeat(30) }`)
|
|
131
130
|
}
|
|
132
131
|
--deprecated <list> Comma separated list of deprecated options.
|
|
133
|
-
Valid values are:
|
|
134
|
-
eagerPersistenceForGeneratedEntities
|
|
135
132
|
--fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
|
|
136
133
|
parser as a fallback. Valid values are:
|
|
137
134
|
cdl : Use CDL parser
|
|
@@ -142,6 +139,8 @@ optionProcessor
|
|
|
142
139
|
Can only be used with certain new CSN based backends. Combination with
|
|
143
140
|
other flags is limited, e.g. --test-mode will not run a consistency check.
|
|
144
141
|
No recompilation is triggered in case of errors. cdsc will dump.
|
|
142
|
+
--direct-messages Messages are written directly to console.error when an error/warning/… is detected.
|
|
143
|
+
Useful to see some messages before a compiler dump.
|
|
145
144
|
--shuffle <seed> If provided, some internal processing sequences are changed, most notably by
|
|
146
145
|
using a shuffled version of ‹model›.definitions. <seed> should be a number
|
|
147
146
|
between 1 and 4294967296, the compiler uses a random number in that range if the
|
|
@@ -153,6 +152,7 @@ optionProcessor
|
|
|
153
152
|
OData CSN, CDL order and more. When --test-mode is enabled, this
|
|
154
153
|
option is implicitly enabled as well.
|
|
155
154
|
--doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
|
|
155
|
+
--propagate-doc-comments Propagate doc comments ('--doc-comment')
|
|
156
156
|
--add-texts-language-assoc In generated texts entities, add association "language"
|
|
157
157
|
to "sap.common.Languages" if it exists
|
|
158
158
|
--localized-without-coalesce Omit coalesce in localized convenience views
|
|
@@ -163,7 +163,7 @@ optionProcessor
|
|
|
163
163
|
--skip-name-check Skip certain name checks, e.g. that there must be no '.' in element names.
|
|
164
164
|
|
|
165
165
|
Commands
|
|
166
|
-
H, toHana [options] <files...> Generate HANA CDS source files
|
|
166
|
+
H, toHana [options] <files...> (deprecated) Generate HANA CDS source files
|
|
167
167
|
O, toOdata [options] <files...> Generate ODATA metadata and annotations
|
|
168
168
|
C, toCdl <files...> Generate CDS source files
|
|
169
169
|
Q, toSql [options] <files...> Generate SQL DDL statements
|
|
@@ -204,10 +204,14 @@ optionProcessor.command('H, toHana')
|
|
|
204
204
|
.option(' --assert-integrity-type <type>', { valid: [ 'RT', 'DB' ], ignoreCase: true })
|
|
205
205
|
.option(' --pre2134ReferentialConstraintNames')
|
|
206
206
|
.option(' --disable-hana-comments')
|
|
207
|
-
.option(' --standard-database-functions')
|
|
207
|
+
.option(' --no-standard-database-functions')
|
|
208
208
|
.help(`
|
|
209
209
|
Usage: cdsc toHana [options] <files...>
|
|
210
210
|
|
|
211
|
+
====================================================
|
|
212
|
+
DEPRECATED! Since v5, this backend is deprecated!
|
|
213
|
+
====================================================
|
|
214
|
+
|
|
211
215
|
Generate HANA CDS source files, or CSN.
|
|
212
216
|
|
|
213
217
|
Options
|
|
@@ -241,7 +245,7 @@ optionProcessor.command('H, toHana')
|
|
|
241
245
|
DB : Create database constraints for associations
|
|
242
246
|
--pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
|
|
243
247
|
--disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
|
|
244
|
-
--standard-database-functions
|
|
248
|
+
--no-standard-database-functions Disable rendering of standard database function mappings.
|
|
245
249
|
`);
|
|
246
250
|
|
|
247
251
|
optionProcessor.command('O, toOdata')
|
|
@@ -294,7 +298,7 @@ optionProcessor.command('O, toOdata')
|
|
|
294
298
|
{ prefix: { alias, ns, uri }, ... }
|
|
295
299
|
--odata-no-creator Omit creator identification in API
|
|
296
300
|
-n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
|
|
297
|
-
the corresponding database name (see "--sql-mapping" for "
|
|
301
|
+
the corresponding database name (see "--sql-mapping" for "toSql")
|
|
298
302
|
plain : (default) Names in uppercase and flattened with underscores
|
|
299
303
|
quoted : Names in original case as in CDL. Entity names with dots,
|
|
300
304
|
but element names flattened with underscores
|
|
@@ -319,8 +323,8 @@ optionProcessor.command('J, forJava')
|
|
|
319
323
|
|
|
320
324
|
optionProcessor.command('C, toCdl')
|
|
321
325
|
.option('-h, --help')
|
|
322
|
-
.option(' --render-cdl-definition-nesting')
|
|
323
|
-
.option(' --render-cdl-common-namespace')
|
|
326
|
+
.option(' --no-render-cdl-definition-nesting')
|
|
327
|
+
.option(' --no-render-cdl-common-namespace')
|
|
324
328
|
.help(`
|
|
325
329
|
Usage: cdsc toCdl [options] <files...>
|
|
326
330
|
|
|
@@ -328,10 +332,10 @@ optionProcessor.command('C, toCdl')
|
|
|
328
332
|
|
|
329
333
|
Options
|
|
330
334
|
-h, --help Show this help text
|
|
331
|
-
--render-cdl-definition-nesting If set, definitions will be nested
|
|
335
|
+
--no-render-cdl-definition-nesting If set, definitions will be nested
|
|
332
336
|
inside services/contexts instead of having only top-level
|
|
333
337
|
definitions.
|
|
334
|
-
--render-cdl-common-namespace If true and render-cdl-definition-nesting
|
|
338
|
+
--no-render-cdl-common-namespace If true and render-cdl-definition-nesting
|
|
335
339
|
is set, a common namespace will be extracted and rendered.
|
|
336
340
|
`);
|
|
337
341
|
|
|
@@ -355,9 +359,10 @@ optionProcessor.command('Q, toSql')
|
|
|
355
359
|
.option(' --generated-by-comment')
|
|
356
360
|
.option(' --better-sqlite-session-variables <bool>')
|
|
357
361
|
.option(' --transitive-localized-views')
|
|
358
|
-
.option(' --boolean-equality')
|
|
362
|
+
.option(' --no-boolean-equality')
|
|
359
363
|
.option(' --with-hana-associations <bool>', { valid: [ 'true', 'false' ] })
|
|
360
|
-
.option(' --standard-database-functions')
|
|
364
|
+
.option(' --no-standard-database-functions')
|
|
365
|
+
.option(' --dollar-now-as-timestamp')
|
|
361
366
|
.help(`
|
|
362
367
|
Usage: cdsc toSql [options] <files...>
|
|
363
368
|
|
|
@@ -420,8 +425,10 @@ optionProcessor.command('Q, toSql')
|
|
|
420
425
|
Enable or disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
|
|
421
426
|
true : (default) Render "WITH ASSOCIATIONS"
|
|
422
427
|
false : Do not render "WITH ASSOCIATIONS"
|
|
423
|
-
--standard-database-functions
|
|
424
|
-
--boolean-equality
|
|
428
|
+
--no-standard-database-functions Disable rendering of standard database function mappings.
|
|
429
|
+
--no-boolean-equality Enable support for boolean logic '!=' operator.
|
|
430
|
+
--dollar-now-as-timestamp Render '$now' variable as 'CURRENT_TIMESTAMP' instead of 'SESSION_CONTEXT(…)'
|
|
431
|
+
for SQL dialect 'hana'.
|
|
425
432
|
`);
|
|
426
433
|
|
|
427
434
|
optionProcessor.command('toRename')
|
|
@@ -44,7 +44,6 @@ const extensionsCode = {
|
|
|
44
44
|
service: 'extend service',
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const PRECEDENCE_OF_IN_PREDICATE = 10;
|
|
48
47
|
const PRECEDENCE_OF_EQUAL = 10;
|
|
49
48
|
|
|
50
49
|
class AstBuildingParser extends BaseParser {
|
|
@@ -114,6 +113,8 @@ class AstBuildingParser extends BaseParser {
|
|
|
114
113
|
err.expectedTokens = this.expectingArray();
|
|
115
114
|
}
|
|
116
115
|
|
|
116
|
+
// Guards, Prepare Commands and Lookahead Methods -----------------------------
|
|
117
|
+
|
|
117
118
|
tableWithoutAs() { // not used in <guard=…>, only called by other guard
|
|
118
119
|
// TODO TOOL: if the tool properly creates `default: this.giR()`, this
|
|
119
120
|
// condition method is most likely not necessary
|
|
@@ -136,7 +137,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
136
137
|
* - <guard=queryOnLeft, arg=tableWithoutAs>: …after having checked
|
|
137
138
|
* whether the next token is no (reserved or unreserved) keyword
|
|
138
139
|
*/
|
|
139
|
-
queryOnLeft(
|
|
140
|
+
queryOnLeft( arg, test ) {
|
|
140
141
|
if (arg === 'tableWithoutAs') {
|
|
141
142
|
if (this.tableWithoutAs())
|
|
142
143
|
return true;
|
|
@@ -177,8 +178,6 @@ class AstBuildingParser extends BaseParser {
|
|
|
177
178
|
const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
|
|
178
179
|
const generic = this.dynamic_.generic?.[text];
|
|
179
180
|
if (tryGenericIntro) {
|
|
180
|
-
if (this.dynamic_.generic?.IN === 'separator')
|
|
181
|
-
this.prec_ = PRECEDENCE_OF_IN_PREDICATE; // only expressions if `in` is separator
|
|
182
181
|
if (generic !== 'expr')
|
|
183
182
|
return (generic === 'intro') ? 'GenericIntro' : type;
|
|
184
183
|
// if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
|
|
@@ -220,7 +219,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
220
219
|
}
|
|
221
220
|
}
|
|
222
221
|
|
|
223
|
-
inSelectItem(
|
|
222
|
+
inSelectItem( arg ) { // <prepare=…>
|
|
224
223
|
this.dynamic_.inSelectItem = arg ||
|
|
225
224
|
(this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand');
|
|
226
225
|
}
|
|
@@ -251,7 +250,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
251
250
|
return next === '*' || next === '{';
|
|
252
251
|
}
|
|
253
252
|
|
|
254
|
-
notAfterEntityArgOrFilter( mode ) { // TODO: for <hide>
|
|
253
|
+
notAfterEntityArgOrFilter( _arg, mode ) { // TODO: for <hide>
|
|
255
254
|
if (mode !== 'M')
|
|
256
255
|
return false;
|
|
257
256
|
const { type } = this.lb();
|
|
@@ -263,9 +262,18 @@ class AstBuildingParser extends BaseParser {
|
|
|
263
262
|
|
|
264
263
|
// <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
|
|
265
264
|
// completion for `… default 3 not ~;` → currently just `null` but hey
|
|
266
|
-
isNegatedRelation(
|
|
265
|
+
isNegatedRelation( prec ) {
|
|
267
266
|
return this.tokens[this.tokenIdx + 1]?.keyword === 'null' ||
|
|
268
|
-
this.precNone_(
|
|
267
|
+
this.precNone_( prec );
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// TODO: as leanCondition ? `order` should probably appear in the message for
|
|
271
|
+
// test3/Compiler/GrammarRobustness/InvalidSelectInWhere.err.cds
|
|
272
|
+
orderByLimitRestriction( _arg, mode ) {
|
|
273
|
+
if (mode && (!this.$allowOrderByLimit || this.precPost_( mode, 0 )))
|
|
274
|
+
return true;
|
|
275
|
+
this.$allowOrderByLimit = !mode;
|
|
276
|
+
return false;
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
isNamedArg() {
|
|
@@ -303,7 +311,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
303
311
|
/**
|
|
304
312
|
* `annotation` def is only allowed top-level. TODO: combine with `extensionRestriction`
|
|
305
313
|
*/
|
|
306
|
-
vocabularyRestriction( test ) {
|
|
314
|
+
vocabularyRestriction( _arg, test ) {
|
|
307
315
|
if (!test)
|
|
308
316
|
this.dynamic_.inBlock = this.tokenIdx;
|
|
309
317
|
return this.dynamic_.inBlock ?? this.dynamic_.inExtension;
|
|
@@ -319,7 +327,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
319
327
|
* Currently only to restrict it to a single `Id` for published associations.
|
|
320
328
|
* No extra syntax-unexpected-assoc for failure.
|
|
321
329
|
*/
|
|
322
|
-
columnExpr(
|
|
330
|
+
columnExpr( arg, mode ) {
|
|
323
331
|
if (mode)
|
|
324
332
|
return !this.columnExpr$;
|
|
325
333
|
if (arg)
|
|
@@ -331,7 +339,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
331
339
|
return true;
|
|
332
340
|
}
|
|
333
341
|
|
|
334
|
-
nestedExpand( mode ) {
|
|
342
|
+
nestedExpand( _arg, mode ) {
|
|
335
343
|
if (!mode)
|
|
336
344
|
this.nestedExpand$ = this.tokenIdx;
|
|
337
345
|
return this.nestedExpand$ !== this.tokenIdx;
|
|
@@ -375,7 +383,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
375
383
|
* - DEFAULT: true if `default` had been provided
|
|
376
384
|
* - NOTNULL: true if `null` or `not null` had been provided
|
|
377
385
|
*/
|
|
378
|
-
elementRestriction(
|
|
386
|
+
elementRestriction( arg, test ) {
|
|
379
387
|
let { elementCtx } = this.dynamic_;
|
|
380
388
|
if (test) {
|
|
381
389
|
if (elementCtx?.[0] === arg)
|
|
@@ -418,7 +426,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
418
426
|
'Unexpected $(OFFENDING) after $(KEYWORD) clause, expecting $(EXPECTING)' );
|
|
419
427
|
}
|
|
420
428
|
|
|
421
|
-
noRepeatedCardinality( mode ) {
|
|
429
|
+
noRepeatedCardinality( _arg, mode ) {
|
|
422
430
|
if (this.tokens[this.tokenIdx - 2]?.type !== ']')
|
|
423
431
|
return false;
|
|
424
432
|
if (mode === 'M')
|
|
@@ -443,7 +451,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
443
451
|
*
|
|
444
452
|
* Beware: mentioned in leanConditions, i.e. executed in predictions!
|
|
445
453
|
*/
|
|
446
|
-
afterBrace(
|
|
454
|
+
afterBrace( arg, test ) {
|
|
447
455
|
if (!test) {
|
|
448
456
|
if (arg === 'normal' && this.lb().type !== '}') {
|
|
449
457
|
const { type, keyword } = this.la();
|
|
@@ -487,7 +495,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
487
495
|
/**
|
|
488
496
|
* For annotations at the beginning of columns outside parentheses
|
|
489
497
|
*/
|
|
490
|
-
annoInSameLine( test ) {
|
|
498
|
+
annoInSameLine( _arg, test ) {
|
|
491
499
|
if (!test)
|
|
492
500
|
this.dynamic_.safeAnno = true;
|
|
493
501
|
return !this.dynamic_.safeAnno &&
|
|
@@ -499,7 +507,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
499
507
|
* - `...` can appear in the top-level array value only and not after `...`
|
|
500
508
|
* without `up to`.
|
|
501
509
|
*/
|
|
502
|
-
arrayAnno(
|
|
510
|
+
arrayAnno( arg, test ) {
|
|
503
511
|
if (!test) {
|
|
504
512
|
this.dynamic_.arrayAnno = [ !this.dynamic_.arrayAnno ];
|
|
505
513
|
}
|
|
@@ -532,7 +540,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
532
540
|
return this.tokens[this.tokenIdx + 1]?.text !== ':';
|
|
533
541
|
}
|
|
534
542
|
|
|
535
|
-
fail( mode ) {
|
|
543
|
+
fail( _arg, mode ) {
|
|
536
544
|
// TODO TOOL: the following test belongs to the BaseParser.js:
|
|
537
545
|
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
538
546
|
this.conditionStackLength == null && // after error recover
|
|
@@ -569,13 +577,27 @@ class AstBuildingParser extends BaseParser {
|
|
|
569
577
|
|
|
570
578
|
noAssignmentInSameLine() {
|
|
571
579
|
const next = this.la();
|
|
572
|
-
if (next.text === '@' && next.line <= this.lb().endLine
|
|
580
|
+
if (next.text === '@' && next.location.line <= this.lb().location.endLine &&
|
|
581
|
+
// TODO Tool Runtime: it is probably better to skip tokens directly
|
|
582
|
+
// do not report error if the '@' is not correct:
|
|
583
|
+
this.s !== null && this.tokenIdx > this.recoverTokenIdx) {
|
|
573
584
|
this.warning( 'syntax-missing-semicolon', next, { code: ';' },
|
|
574
585
|
// eslint-disable-next-line @stylistic/js/max-len
|
|
575
586
|
'Add a $(CODE) and/or newline before the annotation assignment to indicate that it belongs to the next statement' );
|
|
576
587
|
}
|
|
577
588
|
}
|
|
578
589
|
|
|
590
|
+
reportDubiousAnnoSpacing() {
|
|
591
|
+
const at = this.lb();
|
|
592
|
+
const before = this.tokens[this.tokenIdx - 2];
|
|
593
|
+
if (before?.type === 'Id' && before.location.endLine === at.location.line &&
|
|
594
|
+
before.location.endCol === at.location.col) {
|
|
595
|
+
this.warning( 'syntax-expecting-anno-space', at.location, { code: '@' },
|
|
596
|
+
'Expecting a space before the $(CODE) starting an annotation assignment' );
|
|
597
|
+
}
|
|
598
|
+
this.reportUnexpectedSpace( at );
|
|
599
|
+
}
|
|
600
|
+
|
|
579
601
|
// For :param, #variant, #symbol, @(…) and @Begin and `@` inside annotation paths,
|
|
580
602
|
// inside `.*` and `.{`
|
|
581
603
|
reportUnexpectedSpace( prefix = this.lb(),
|
|
@@ -604,6 +626,8 @@ class AstBuildingParser extends BaseParser {
|
|
|
604
626
|
return prefixLoc;
|
|
605
627
|
}
|
|
606
628
|
|
|
629
|
+
// Locations for ASTs ---------------------------------------------------------
|
|
630
|
+
|
|
607
631
|
startLocation( { location } = this.lr() ) {
|
|
608
632
|
return {
|
|
609
633
|
__proto__: Location.prototype,
|
|
@@ -749,8 +773,23 @@ class AstBuildingParser extends BaseParser {
|
|
|
749
773
|
return ast;
|
|
750
774
|
}
|
|
751
775
|
|
|
776
|
+
virtualOrImplicit( art ) {
|
|
777
|
+
const token = this.lb();
|
|
778
|
+
const ref = art.value.func || art.value;
|
|
779
|
+
if (!art.virtual ||
|
|
780
|
+
ref.path[0].location.tokenIndex < token.location.tokenIndex ||
|
|
781
|
+
this.la().text === '{') {
|
|
782
|
+
this.classifyImplicitName( 'ItemImplicit', ref );
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
token.parsedAs = 'ItemAlias';
|
|
786
|
+
art.name = ref.path[0];
|
|
787
|
+
art.value = undefined;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
752
791
|
classifyImplicitName( category, ref ) {
|
|
753
|
-
if (!ref || ref.path) {
|
|
792
|
+
if (!ref || ref.path) { // TODO: func
|
|
754
793
|
const tokenIndex = ref?.path.at(-1)?.location.tokenIndex;
|
|
755
794
|
const token = this.prevTokenWithIndex( tokenIndex ) ?? this.tokens[this.tokenIdx - 1];
|
|
756
795
|
const { parsedAs } = token;
|
|
@@ -1043,10 +1082,8 @@ class AstBuildingParser extends BaseParser {
|
|
|
1043
1082
|
line: chosen.location.line,
|
|
1044
1083
|
col: chosen.location.col,
|
|
1045
1084
|
};
|
|
1046
|
-
if (erroneous.val === chosen.val)
|
|
1047
|
-
|
|
1048
|
-
this.warning( 'syntax-duplicate-equal-clause', erroneous.location, args );
|
|
1049
|
-
}
|
|
1085
|
+
if (erroneous.val === chosen.val)
|
|
1086
|
+
this.message( 'syntax-duplicate-equal-clause', erroneous.location, args );
|
|
1050
1087
|
// TODO extra msg text 'syntax-duplicate-clause' for noRepeatedCardinality()
|
|
1051
1088
|
}
|
|
1052
1089
|
|
|
@@ -1169,6 +1206,21 @@ class AstBuildingParser extends BaseParser {
|
|
|
1169
1206
|
};
|
|
1170
1207
|
}
|
|
1171
1208
|
|
|
1209
|
+
valuePathAstWithNew( expr, path ) {
|
|
1210
|
+
path = this.valuePathAst( path );
|
|
1211
|
+
if (path.op?.val !== 'ixpr') {
|
|
1212
|
+
expr.args.push( path );
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
const ref = path.args[0];
|
|
1216
|
+
const op = { val: 'ixpr', location: expr.args[0].location };
|
|
1217
|
+
const location = this.combineLocation( expr.args[0], ref );
|
|
1218
|
+
path.args[0] = { op, args: [ expr.args[0], ref ], location };
|
|
1219
|
+
expr.args = path.args;
|
|
1220
|
+
}
|
|
1221
|
+
this.attachLocation( expr );
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1172
1224
|
valuePathAst( ref ) {
|
|
1173
1225
|
// TODO: XSN representation of functions is a bit strange - rework
|
|
1174
1226
|
// TODO: rework this function
|
|
@@ -1189,8 +1241,12 @@ class AstBuildingParser extends BaseParser {
|
|
|
1189
1241
|
if (filter) // TODO v7: make this be reported via guard, as error
|
|
1190
1242
|
this.message( 'syntax-unexpected-filter', filter.location, {} );
|
|
1191
1243
|
// TODO: XSN representation of functions is a bit strange - rework
|
|
1192
|
-
|
|
1193
|
-
|
|
1244
|
+
return this.attachLocation( {
|
|
1245
|
+
op: { location, val: 'call' },
|
|
1246
|
+
func: ref,
|
|
1247
|
+
args,
|
|
1248
|
+
location: ref.location,
|
|
1249
|
+
} );
|
|
1194
1250
|
}
|
|
1195
1251
|
|
|
1196
1252
|
// $syntax === ':' => path(P: 1)
|