@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
package/lib/compiler/extend.js
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
const { weakRefLocation } = require('../base/location');
|
|
6
6
|
const { searchName } = require('../base/messages');
|
|
7
|
-
const { isDeprecatedEnabled } = require('../base/
|
|
8
|
-
const { dictAdd, pushToDict, dictForEach } = require('
|
|
7
|
+
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
8
|
+
const { dictAdd, pushToDict, dictForEach } = require('./dictionaries');
|
|
9
9
|
const { kindProperties, dictKinds } = require('./base');
|
|
10
10
|
const {
|
|
11
11
|
setLink,
|
|
12
12
|
setArtifactLink,
|
|
13
13
|
copyExpr,
|
|
14
|
-
|
|
14
|
+
setExpandStatus,
|
|
15
15
|
linkToOrigin,
|
|
16
16
|
initItemsLinks,
|
|
17
17
|
setMemberParent,
|
|
@@ -84,6 +84,8 @@ function extend( model ) {
|
|
|
84
84
|
const includesNonShadowedFirst
|
|
85
85
|
= isDeprecatedEnabled( model.options, '_includesNonShadowedFirst' );
|
|
86
86
|
|
|
87
|
+
const includeCollisions = [];
|
|
88
|
+
|
|
87
89
|
forEachGeneric( model, 'definitions', tagCompositionTargets );
|
|
88
90
|
dictForEach( model.$collectedExtensions, e => e._extensions.forEach( tagCompositionTargets ) );
|
|
89
91
|
// remark: tagging on extensions works _before_ running extendArtifactBefore() on each artifact
|
|
@@ -174,7 +176,7 @@ function extend( model ) {
|
|
|
174
176
|
// elements etc. TODO: do that more specifically on the dicts (via symbol)
|
|
175
177
|
// Probably better: we could use the _extensions dict prop directly in to-csn
|
|
176
178
|
if (art.$inferred)
|
|
177
|
-
|
|
179
|
+
setExpandStatus( art, 'annotate' );
|
|
178
180
|
if (Array.isArray( art._extensions )) {
|
|
179
181
|
checkExtensionsKind( art._extensions, art );
|
|
180
182
|
transformArtifactExtensions( art );
|
|
@@ -258,7 +260,8 @@ function extend( model ) {
|
|
|
258
260
|
checkReturnsExtension( ext, art );
|
|
259
261
|
}
|
|
260
262
|
// if (art.elements || art.enum || art.kind === 'annotate')
|
|
261
|
-
moveDictExtensions( art, extensionsMap,
|
|
263
|
+
moveDictExtensions( art, extensionsMap,
|
|
264
|
+
(art.enum ? 'enum' : 'elements'), false );
|
|
262
265
|
}
|
|
263
266
|
}
|
|
264
267
|
|
|
@@ -399,7 +402,9 @@ function extend( model ) {
|
|
|
399
402
|
pushTo$add( dict, ext );
|
|
400
403
|
}
|
|
401
404
|
else if (prop.charAt(0) === '@' || prop === 'doc' || prop === 'columns' ||
|
|
402
|
-
|
|
405
|
+
prop === 'groupBy' || prop === 'where' || prop === 'having' ||
|
|
406
|
+
prop === 'orderBy' || prop === 'limit' || prop === 'length' ||
|
|
407
|
+
prop === 'scale' || prop === 'precision' || prop === 'srid') {
|
|
403
408
|
if (!isAutoItemsOrReturns)
|
|
404
409
|
pushToDict( dict, prop, ext );
|
|
405
410
|
}
|
|
@@ -577,7 +582,7 @@ function extend( model ) {
|
|
|
577
582
|
else if (prop === 'columns') {
|
|
578
583
|
const { query } = art;
|
|
579
584
|
for (const col of ext.columns)
|
|
580
|
-
col.$extended =
|
|
585
|
+
col.$extended = 'columns';
|
|
581
586
|
|
|
582
587
|
if (art.kind === 'annotate' && art.$inferred === '')
|
|
583
588
|
return; // internal super-annotate for unknown artifacts
|
|
@@ -593,6 +598,10 @@ function extend( model ) {
|
|
|
593
598
|
query.columns.push( ...ext.columns );
|
|
594
599
|
ext.columns.forEach( col => changeParentLinks( col, query ) );
|
|
595
600
|
}
|
|
601
|
+
else if (prop === 'groupBy' || prop === 'where' || prop === 'having' ||
|
|
602
|
+
prop === 'orderBy' || prop === 'limit') {
|
|
603
|
+
applyQueryClause( prop, ext, art );
|
|
604
|
+
}
|
|
596
605
|
else if (typeParameters.list.includes( prop )) {
|
|
597
606
|
const typeExts = art.$typeExts || (art.$typeExts = {});
|
|
598
607
|
typeExts[prop] = ext;
|
|
@@ -603,6 +612,39 @@ function extend( model ) {
|
|
|
603
612
|
}
|
|
604
613
|
}
|
|
605
614
|
|
|
615
|
+
function applyQueryClause( prop, ext, art ) {
|
|
616
|
+
const { query } = art;
|
|
617
|
+
const clause = ext[prop];
|
|
618
|
+
const isArray = Array.isArray( clause );
|
|
619
|
+
|
|
620
|
+
if (prop !== 'limit') {
|
|
621
|
+
const items = isArray ? clause : [ clause ];
|
|
622
|
+
for (const item of items) {
|
|
623
|
+
item.$extended = prop;
|
|
624
|
+
setLink( item, '_block', ext._block );
|
|
625
|
+
setLink( item, '_outer', query );
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (!query?.from?.path) {
|
|
629
|
+
const variant = (query?.from || query)?.op?.val || 'std';
|
|
630
|
+
const loc = isArray ? clause[$location] : clause.location;
|
|
631
|
+
error( `extend-${ prop.toLowerCase() }`, [ loc, ext ], { '#': variant, art } );
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (isArray) {
|
|
635
|
+
if (!query[prop])
|
|
636
|
+
query[prop] = [];
|
|
637
|
+
query[prop].push( ...clause );
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
if (query[prop]) {
|
|
641
|
+
error( 'ext-unexpected-sql-clause', [ clause.location, ext ], { art, keyword: prop } );
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
query[prop] = clause;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
606
648
|
function changeParentLinks( art, queryOrMain ) {
|
|
607
649
|
// TODO: we might also change the implicit name (if name.id is a number,
|
|
608
650
|
// adding the previous column lenght - 1) for better error messages
|
|
@@ -838,18 +880,18 @@ function extend( model ) {
|
|
|
838
880
|
}
|
|
839
881
|
|
|
840
882
|
function moveDictExtensions( art, extensionsMap, artProp, extProp = artProp ) {
|
|
841
|
-
// TODO:
|
|
842
|
-
const extensions = extensionsMap[extProp];
|
|
883
|
+
// TODO: setExpandStatus
|
|
884
|
+
const extensions = extensionsMap[extProp || 'elements'];
|
|
843
885
|
if (!extensions)
|
|
844
886
|
return;
|
|
845
887
|
|
|
846
888
|
for (const ext of extensions) {
|
|
847
889
|
let dictCheck = (art.kind !== 'annotate'); // no check in super annotate statement
|
|
848
|
-
forEachGeneric( ext, extProp, ( elemExt, name ) => {
|
|
890
|
+
forEachGeneric( ext, extProp || (ext.enum ? 'enum' : 'elements'), ( elemExt, name ) => {
|
|
849
891
|
if (elemExt.kind !== 'annotate' && elemExt.kind !== 'extend') // TODO: specified elems
|
|
850
892
|
return; // definitions inside extend, already handled
|
|
851
893
|
dictCheck = dictCheck && checkRemainingMemberExtensions( art, elemExt, artProp, name );
|
|
852
|
-
const elem = art[artProp]?.[name] || annotateFor( art, extProp, name );
|
|
894
|
+
const elem = art[artProp]?.[name] || annotateFor( art, extProp || 'elements', name );
|
|
853
895
|
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null ) );
|
|
854
896
|
// TODO: why null for annotate?
|
|
855
897
|
ensureArtifactNotProcessed( elem );
|
|
@@ -1055,11 +1097,23 @@ function extend( model ) {
|
|
|
1055
1097
|
|
|
1056
1098
|
function checkRemainingMainExtensions( art, ext ) {
|
|
1057
1099
|
const refCtx = extensionRefContext( ext );
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1100
|
+
if (resolvePath( ext.name, refCtx, ext ) && art?.builtin) {
|
|
1101
|
+
if (ext.kind === 'extend') {
|
|
1102
|
+
// extending built-ins with elements/enums already gives an error
|
|
1103
|
+
warning( 'ext-unexpected-builtin', [ ext.name.location, ext ], {}, // error v8?
|
|
1104
|
+
'Built-in types should not be extended' ); // keep the text general
|
|
1105
|
+
const typeProp = typeParameters.list.find( p => ext[p] );
|
|
1106
|
+
if (typeProp) {
|
|
1107
|
+
const location = ext.$typeArgs?.[$location] || ext[typeProp].location;
|
|
1108
|
+
message( 'ext-unexpected-type-property', [ location, ext ], {}, // error v7
|
|
1109
|
+
'Built-in types can\'t be extended with type properties' );
|
|
1110
|
+
// see also 'ext-invalid-type-property'
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
info( 'anno-builtin', [ ext.name.location, ext ], {} ); // TODO: better location?
|
|
1115
|
+
}
|
|
1116
|
+
// TODO: remove built-ins as CC candidates via accept property of ./shared.js
|
|
1063
1117
|
}
|
|
1064
1118
|
}
|
|
1065
1119
|
|
|
@@ -1142,10 +1196,33 @@ function extend( model ) {
|
|
|
1142
1196
|
// TODO: complain if $inferred
|
|
1143
1197
|
// checkExtensionsKind( extensions, art );
|
|
1144
1198
|
extendMembers( extensions, art );
|
|
1199
|
+
reportIncludeCollisions( art );
|
|
1145
1200
|
// TODO: complain about element extensions inside projection
|
|
1146
1201
|
return true;
|
|
1147
1202
|
}
|
|
1148
1203
|
|
|
1204
|
+
function reportIncludeCollisions( art ) {
|
|
1205
|
+
const grouped = Object.create( null );
|
|
1206
|
+
for (const {
|
|
1207
|
+
prop, name, existing, elem,
|
|
1208
|
+
} of includeCollisions) {
|
|
1209
|
+
const key = `${ prop }:${ name }`;
|
|
1210
|
+
if (!grouped[key])
|
|
1211
|
+
grouped[key] = { prop, name, collisions: new Set( [ existing ] ) };
|
|
1212
|
+
grouped[key].collisions.add( elem );
|
|
1213
|
+
}
|
|
1214
|
+
for (const key in grouped) {
|
|
1215
|
+
const { prop, name, collisions } = grouped[key];
|
|
1216
|
+
const member = art[prop]?.[name];
|
|
1217
|
+
if (member?.$inferred === 'include') {
|
|
1218
|
+
const arts = [ ...collisions ].map( m => m._origin._main );
|
|
1219
|
+
message( 'ext-duplicate-include', [ art.name.location, member ],
|
|
1220
|
+
{ '#': prop, name, sorted_arts: arts } );
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
includeCollisions.length = 0;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1149
1226
|
function extendMembers( extensions, art ) {
|
|
1150
1227
|
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1151
1228
|
const elemExtensions = [];
|
|
@@ -1154,7 +1231,11 @@ function extend( model ) {
|
|
|
1154
1231
|
// TODO: use same sequence as in chooseAssignment() - better: use common code with that fn
|
|
1155
1232
|
// console.log('EM:',art.name,extensions,art._extensions)
|
|
1156
1233
|
for (const ext of extensions) { // those in extMap.includes
|
|
1157
|
-
if (art.$inferred) {
|
|
1234
|
+
if (art.$inferred === 'include') {
|
|
1235
|
+
error( 'ref-expected-direct-structure', [ ext.name.location, ext ],
|
|
1236
|
+
{ '#': 'elements', art } );
|
|
1237
|
+
}
|
|
1238
|
+
else if (art.$inferred) {
|
|
1158
1239
|
error( 'extend-for-generated', [ ext.name.location, ext ], { art, keyword: 'extend' },
|
|
1159
1240
|
'You can\'t use $(KEYWORD) on the generated $(ART)' ); // or with inferred elements
|
|
1160
1241
|
}
|
|
@@ -1298,6 +1379,8 @@ function extend( model ) {
|
|
|
1298
1379
|
|
|
1299
1380
|
if (obj !== parent && obj.elements && parent.enum) { // applying the extension
|
|
1300
1381
|
initElementsAsEnum();
|
|
1382
|
+
if (parent.enum[$inferred])
|
|
1383
|
+
setExpandStatus( parent, 'extend' );
|
|
1301
1384
|
}
|
|
1302
1385
|
else {
|
|
1303
1386
|
if (checkDefinitions( construct, parent, 'elements', obj.elements || false ))
|
|
@@ -1341,7 +1424,11 @@ function extend( model ) {
|
|
|
1341
1424
|
e.kind = 'enum';
|
|
1342
1425
|
hasElement = true; // warning with CDL input or `name: {}` in CSN input
|
|
1343
1426
|
}
|
|
1344
|
-
if (
|
|
1427
|
+
if (!parent.type || parent.type.$inferred || (parent._main || parent).query) {
|
|
1428
|
+
error( 'extend-type', [ obj.elements[$location], construct ], {},
|
|
1429
|
+
'Only structures or enum types can be extended with elements/enums' );
|
|
1430
|
+
}
|
|
1431
|
+
else if (hasElement) {
|
|
1345
1432
|
warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
|
|
1346
1433
|
{ code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
|
|
1347
1434
|
}
|
|
@@ -1404,13 +1491,18 @@ function extend( model ) {
|
|
|
1404
1491
|
|
|
1405
1492
|
if (isQueryExtension && elem.kind === 'element') {
|
|
1406
1493
|
error( 'extend-query', [ elem.location, construct ], // TODO: searchName ?
|
|
1407
|
-
{ code: 'extend
|
|
1494
|
+
{ code: 'extend … with columns' },
|
|
1408
1495
|
'Use $(CODE) to add select items to the query entity' );
|
|
1409
1496
|
return;
|
|
1410
1497
|
}
|
|
1411
1498
|
|
|
1412
1499
|
const existing = parent[prop]?.[name];
|
|
1413
1500
|
const add = construct !== parent && (!existing || elem.$inferred !== 'include');
|
|
1501
|
+
if (!add && existing?.$inferred === 'include' && elem.$inferred === 'include') {
|
|
1502
|
+
includeCollisions.push( {
|
|
1503
|
+
prop, name, existing, elem,
|
|
1504
|
+
} );
|
|
1505
|
+
}
|
|
1414
1506
|
// don't dump with `entity T {}; extend T with { extend e {}; e {}; e {} };`:
|
|
1415
1507
|
const { $duplicates } = elem;
|
|
1416
1508
|
if ($duplicates === true && add)
|
|
@@ -1424,6 +1516,9 @@ function extend( model ) {
|
|
|
1424
1516
|
|
|
1425
1517
|
// for a correct home path, setMemberParent needed to be called
|
|
1426
1518
|
|
|
1519
|
+
if (parent[prop]?.[$inferred])
|
|
1520
|
+
setExpandStatus( parent, 'extend' );
|
|
1521
|
+
|
|
1427
1522
|
if (!elem.value || elem.kind !== 'element')
|
|
1428
1523
|
return;
|
|
1429
1524
|
// remark: potential enum elements have already been turned into enums
|
|
@@ -1502,10 +1597,14 @@ function extend( model ) {
|
|
|
1502
1597
|
error( 'ref-expected-direct-structure', [ location, construct ],
|
|
1503
1598
|
{ '#': variant, art: parent } );
|
|
1504
1599
|
}
|
|
1505
|
-
else
|
|
1600
|
+
else if (prop !== 'enum' || !parent.enum ||
|
|
1601
|
+
!parent.type || parent.type.$inferred || (parent._main || parent).query) {
|
|
1506
1602
|
error( 'extend-type', [ location, construct ], {},
|
|
1507
1603
|
'Only structures or enum types can be extended with elements/enums' );
|
|
1508
1604
|
}
|
|
1605
|
+
else {
|
|
1606
|
+
return true;
|
|
1607
|
+
}
|
|
1509
1608
|
}
|
|
1510
1609
|
else if (prop === 'elements') {
|
|
1511
1610
|
error( 'def-unexpected-elements', [ location, construct ], {},
|
|
@@ -1585,6 +1684,7 @@ function extend( model ) {
|
|
|
1585
1684
|
// TODO two kind of messages:
|
|
1586
1685
|
// Error 'More than one include defines element "A"' (at include ref)
|
|
1587
1686
|
// Warning 'Overwrites definition from include "I" (at elem def)
|
|
1687
|
+
const propagateKeys = art.kind !== 'type' || !model.options.v7KeyPropagation;
|
|
1588
1688
|
const parent = ext === art && art;
|
|
1589
1689
|
const members = ext[prop];
|
|
1590
1690
|
// if (members)console.log( 'EXT:', prop, art.kind, art.name.id, ...Object.keys(members));
|
|
@@ -1624,7 +1724,7 @@ function extend( model ) {
|
|
|
1624
1724
|
elem.$inferred = 'include';
|
|
1625
1725
|
if (origin.masked) // TODO(v6): remove 'masked'
|
|
1626
1726
|
elem.masked = Object.assign( { $inferred: 'include' }, origin.masked );
|
|
1627
|
-
if (origin.key)
|
|
1727
|
+
if (origin.key && propagateKeys)
|
|
1628
1728
|
elem.key = Object.assign( { $inferred: 'include' }, origin.key );
|
|
1629
1729
|
if (origin.value && origin.$syntax === 'calc') {
|
|
1630
1730
|
// TODO: If paths become invalid in the new artifact, should we mark
|
package/lib/compiler/generate.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable no-nested-ternary */
|
|
6
6
|
|
|
7
|
-
const { isDeprecatedEnabled } = require('../base/
|
|
8
|
-
const { dictAdd } = require('
|
|
7
|
+
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
8
|
+
const { dictAdd } = require('./dictionaries');
|
|
9
9
|
const {
|
|
10
10
|
setLink,
|
|
11
11
|
setArtifactLink,
|
package/lib/compiler/index.js
CHANGED
|
@@ -32,8 +32,8 @@ const check = require('./checks');
|
|
|
32
32
|
|
|
33
33
|
const { Location, emptyWeakLocation } = require('../base/location');
|
|
34
34
|
const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
|
|
35
|
-
const { checkRemovedDeprecatedFlags } = require('../base/
|
|
36
|
-
const { promiseAllDoNotRejectImmediately } = require('../
|
|
35
|
+
const { checkRemovedDeprecatedFlags } = require('../base/specialOptions');
|
|
36
|
+
const { promiseAllDoNotRejectImmediately } = require('../utils/node-helpers');
|
|
37
37
|
const { cdsFs, fileExtension } = require('../utils/file');
|
|
38
38
|
|
|
39
39
|
const fs = require('fs');
|
|
@@ -504,6 +504,8 @@ function compileDoXSync( model ) {
|
|
|
504
504
|
// TODO: think about making this work
|
|
505
505
|
|
|
506
506
|
resolve( model );
|
|
507
|
+
if (options.$cdsReplSupport)
|
|
508
|
+
return model;
|
|
507
509
|
tweakAssocs( model );
|
|
508
510
|
assertConsistency( model );
|
|
509
511
|
check( model );
|
|
@@ -554,9 +556,15 @@ async function compileDoX( model ) {
|
|
|
554
556
|
|
|
555
557
|
model.definitions = model.$functions.shuffleDict( model.definitions );
|
|
556
558
|
// Shuffling extensions is more difficult due to intra-file extensions of same artifact
|
|
559
|
+
// TODO: shuffle _before_ populate()
|
|
557
560
|
// TODO: think about making this work
|
|
558
561
|
|
|
559
|
-
|
|
562
|
+
resolve( model );
|
|
563
|
+
if (options.$cdsReplSupport)
|
|
564
|
+
return model;
|
|
565
|
+
await checkAsyncAbortFlag( options.abortSignal );
|
|
566
|
+
|
|
567
|
+
for (const phase of [ tweakAssocs, assertConsistency, check ]) {
|
|
560
568
|
phase( model );
|
|
561
569
|
// eslint-disable-next-line no-await-in-loop
|
|
562
570
|
await checkAsyncAbortFlag( options.abortSignal );
|
package/lib/compiler/lsp-api.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// This files includes an iterator over "semantic tokens" in an XSN model.
|
|
9
9
|
// "Semantic tokens" are identifiers, but also the "return" parameter.
|
|
10
|
-
// See <../../internalDoc/lsp/
|
|
10
|
+
// See <../../internalDoc/lsp/SemanticTokenCrawling.md> for details.
|
|
11
11
|
|
|
12
12
|
const { CompilerAssertion } = require('../base/error');
|
|
13
13
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
@@ -190,7 +190,7 @@ function* artifactTokens( art ) {
|
|
|
190
190
|
* @returns {Generator<LspSemanticTokenEvent>}
|
|
191
191
|
*/
|
|
192
192
|
function* extensionTokens( ext ) {
|
|
193
|
-
if (ext.kind !== 'extend' && ext.kind !== 'annotate')
|
|
193
|
+
if (ext.kind !== 'extend' && ext.kind !== 'annotate' || !ext.name)
|
|
194
194
|
return null;
|
|
195
195
|
|
|
196
196
|
const wasApplied = ext.name._artifact && !ext.name._artifact.$inferred;
|
|
@@ -203,7 +203,7 @@ function* extensionTokens( ext ) {
|
|
|
203
203
|
|
|
204
204
|
// We need to traverse all dictionaries that could themselves contain
|
|
205
205
|
// extensions. Enum extensions or columns don't need to be traversed,
|
|
206
|
-
// for example, because there can't be inner extensions.
|
|
206
|
+
// for example, because there can't be inner extensions. TODO: still?
|
|
207
207
|
yield* dictOf( extensionTokens )( ext.params );
|
|
208
208
|
yield* dictOf( extensionTokens )( ext.actions );
|
|
209
209
|
yield* dictOf( extensionTokens )( ext.elements );
|
package/lib/compiler/populate.js
CHANGED
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
|
-
const { isDeprecatedEnabled } = require('../base/
|
|
20
|
+
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
21
21
|
const {
|
|
22
22
|
dictAdd, dictAddArray, dictFirst, dictForEach,
|
|
23
|
-
} = require('
|
|
23
|
+
} = require('./dictionaries');
|
|
24
24
|
const { weakLocation, weakRefLocation } = require('../base/location');
|
|
25
25
|
const { CompilerAssertion } = require('../base/error');
|
|
26
26
|
|
|
@@ -36,7 +36,6 @@ const {
|
|
|
36
36
|
dependsOn,
|
|
37
37
|
proxyCopyMembers,
|
|
38
38
|
setExpandStatus,
|
|
39
|
-
setExpandStatusAnnotate,
|
|
40
39
|
dependsOnSilent,
|
|
41
40
|
columnRefStartsWithSelf,
|
|
42
41
|
forEachDefinition,
|
|
@@ -218,8 +217,8 @@ function populate( model ) {
|
|
|
218
217
|
populateGeneratedEntity( a );
|
|
219
218
|
if (a.includes)
|
|
220
219
|
a.includes.forEach( i => resolveInclude( i, a ) );
|
|
221
|
-
extendArtifactAdd( a );
|
|
222
220
|
art = populateArtifact( a, art ) || a;
|
|
221
|
+
extendArtifactAdd( a );
|
|
223
222
|
setLink( a, '_effectiveType', art );
|
|
224
223
|
a.$effectiveSeqNo = ++effectiveSeqNo;
|
|
225
224
|
// console.log('PE:',require('../model/revealInternalProperties').ref(a))
|
|
@@ -614,7 +613,7 @@ function populate( model ) {
|
|
|
614
613
|
}
|
|
615
614
|
|
|
616
615
|
if (wasAnnotated)
|
|
617
|
-
|
|
616
|
+
setExpandStatus( art, 'annotate' );
|
|
618
617
|
|
|
619
618
|
// TODO: We don't check enum$, yet! We first need to fix expansion for
|
|
620
619
|
// `cast(elem as EnumType)` (see #9421)
|
|
@@ -668,7 +667,7 @@ function populate( model ) {
|
|
|
668
667
|
}
|
|
669
668
|
}
|
|
670
669
|
if (wasAnnotated)
|
|
671
|
-
|
|
670
|
+
setExpandStatus( art, 'annotate' );
|
|
672
671
|
|
|
673
672
|
for (const id in art.foreignKeys$) {
|
|
674
673
|
const specifiedElement = art.foreignKeys$[id];
|
|
@@ -767,7 +766,7 @@ function populate( model ) {
|
|
|
767
766
|
const { targetMax } = path[path.length - 1].cardinality ||
|
|
768
767
|
getInheritedProp( assoc, 'cardinality' ) || {};
|
|
769
768
|
if (targetMax && (targetMax.val === '*' || targetMax.val > 1)) {
|
|
770
|
-
elem.items = { location: elem.expand[$location] };
|
|
769
|
+
elem.items = { location: elem.expand[$location], $inferred: 'query' };
|
|
771
770
|
setLink( elem.items, '_outer', elem );
|
|
772
771
|
}
|
|
773
772
|
return initFromColumns( elem, elem.expand );
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
|
|
39
39
|
'use strict';
|
|
40
40
|
|
|
41
|
-
const { isDeprecatedEnabled } = require('../base/
|
|
42
|
-
const { dictAdd } = require('
|
|
41
|
+
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
42
|
+
const { dictAdd, pushToDict } = require('./dictionaries');
|
|
43
43
|
const { weakLocation } = require('../base/location');
|
|
44
44
|
const { combinedLocation } = require('../base/location');
|
|
45
45
|
const { typeParameters } = require('./builtins');
|
|
@@ -285,9 +285,9 @@ function resolve( model ) {
|
|
|
285
285
|
// we don't propagate keys to type projections, see #13575
|
|
286
286
|
return;
|
|
287
287
|
}
|
|
288
|
-
// Second argument
|
|
288
|
+
// Second argument controls whether `key` is only propagated along simple
|
|
289
289
|
// view, i.e. ref or subquery in FROM, not UNION or JOIN.
|
|
290
|
-
traverseQueryPost( view.query,
|
|
290
|
+
traverseQueryPost( view.query, !options.v7KeyPropagation, ( query ) => {
|
|
291
291
|
if (!withExplicitKeys( query ) && inheritKeyProp( query ) &&
|
|
292
292
|
withKeyPropagation( query )) // now the part with messages
|
|
293
293
|
inheritKeyProp( query, true );
|
|
@@ -331,47 +331,47 @@ function resolve( model ) {
|
|
|
331
331
|
return head?.kind === '$tableAlias' && item._artifact?.key;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
function primarySourceNavigation( aliases ) {
|
|
335
|
-
for (const name in aliases)
|
|
336
|
-
return aliases[name].elements;
|
|
337
|
-
return undefined;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
334
|
function withKeyPropagation( query ) {
|
|
341
|
-
const { from } = query;
|
|
335
|
+
const { from, $tableAliases } = query;
|
|
342
336
|
if (!from) // parse error SELECT FROM <EOF>
|
|
343
337
|
return false;
|
|
344
|
-
|
|
345
338
|
let propagateKeys = true; // used instead early RETURN to get more messages
|
|
346
|
-
|
|
347
|
-
if (toMany) {
|
|
348
|
-
propagateKeys = false;
|
|
349
|
-
info( 'query-from-many', [ toMany.location, query ], { art: toMany }, {
|
|
350
|
-
std: 'Key properties are not propagated because a to-many association $(ART) is selected',
|
|
351
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
352
|
-
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
353
|
-
} );
|
|
354
|
-
}
|
|
339
|
+
|
|
355
340
|
// Check that all keys from the source are projected:
|
|
356
|
-
const notProjected =
|
|
357
|
-
const navElems =
|
|
341
|
+
const notProjected = Object.create( null );
|
|
342
|
+
const navElems = query._combined;
|
|
358
343
|
for (const name in navElems) {
|
|
359
344
|
const nav = navElems[name];
|
|
360
|
-
if (nav
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
notProjected.push( nav.name.id );
|
|
345
|
+
if (Array.isArray( nav ))
|
|
346
|
+
nav.forEach( pushNonProjected );
|
|
347
|
+
else
|
|
348
|
+
pushNonProjected( nav );
|
|
365
349
|
}
|
|
366
|
-
|
|
350
|
+
for (const [ alias, names ] of Object.entries( notProjected )) {
|
|
367
351
|
propagateKeys = false;
|
|
368
|
-
|
|
352
|
+
// TODO: mention alias
|
|
353
|
+
const location = $tableAliases[alias].name?.location || from.location;
|
|
354
|
+
info( 'query-missing-keys', [ location, query ], { names },
|
|
369
355
|
{
|
|
370
356
|
std: 'Keys $(NAMES) have not been projected - key properties are not propagated',
|
|
371
357
|
one: 'Key $(NAMES) has not been projected - key properties are not propagated',
|
|
372
358
|
} );
|
|
373
359
|
}
|
|
374
|
-
|
|
360
|
+
if (options.v7KeyPropagation)
|
|
361
|
+
return propagateKeys;
|
|
362
|
+
|
|
363
|
+
// Check that there is no to-many assoc in the FROM reference or in the select
|
|
364
|
+
// item reference (references in expressions are not checked,
|
|
365
|
+
// withAssociation() will see no _artifact links anyway)
|
|
366
|
+
const toMany = withAssociation( from, targetMaxNotOne, true );
|
|
367
|
+
if (toMany) {
|
|
368
|
+
propagateKeys = false;
|
|
369
|
+
info( 'query-from-many', [ toMany.location, query ], { art: toMany }, {
|
|
370
|
+
std: 'Key properties are not propagated because a to-many association $(ART) is selected',
|
|
371
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
372
|
+
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
373
|
+
} );
|
|
374
|
+
}
|
|
375
375
|
for (const name in query.elements) {
|
|
376
376
|
const elem = query.elements[name];
|
|
377
377
|
|
|
@@ -383,6 +383,14 @@ function resolve( model ) {
|
|
|
383
383
|
}
|
|
384
384
|
return propagateKeys;
|
|
385
385
|
|
|
386
|
+
function pushNonProjected( nav ) {
|
|
387
|
+
if (nav.$duplicates)
|
|
388
|
+
return;
|
|
389
|
+
const { key } = nav._origin;
|
|
390
|
+
if (key?.val && !nav._projections?.length)
|
|
391
|
+
pushToDict( notProjected, nav._parent.name.id, nav.name.id );
|
|
392
|
+
}
|
|
393
|
+
|
|
386
394
|
function selectTest( expr, user ) {
|
|
387
395
|
const art = withAssociation( expr, targetMaxNotOne );
|
|
388
396
|
if (art) {
|
|
@@ -1004,11 +1012,16 @@ function resolve( model ) {
|
|
|
1004
1012
|
forEachGeneric( query, 'elements', resolveRefs );
|
|
1005
1013
|
if (query.from)
|
|
1006
1014
|
resolveJoinOn( query.from );
|
|
1007
|
-
if (query.where)
|
|
1008
|
-
|
|
1015
|
+
if (query.where) {
|
|
1016
|
+
const user = query.where._block ? query.where : query;
|
|
1017
|
+
resolveExpr( query.where, 'where', user );
|
|
1018
|
+
}
|
|
1009
1019
|
if (query.groupBy)
|
|
1010
1020
|
resolveBy( query.groupBy, 'groupBy', 'groupBy' );
|
|
1011
|
-
|
|
1021
|
+
if (query.having) {
|
|
1022
|
+
const user = query.having._block ? query.having : query;
|
|
1023
|
+
resolveExpr( query.having, 'having', user );
|
|
1024
|
+
}
|
|
1012
1025
|
if (query.$orderBy) // ORDER BY from UNION:
|
|
1013
1026
|
// TODO clarify: can I access the tab alias of outer queries? If not:
|
|
1014
1027
|
// 4th arg query._main instead query._parent.
|
|
@@ -1051,8 +1064,10 @@ function resolve( model ) {
|
|
|
1051
1064
|
*/
|
|
1052
1065
|
function resolveBy( array, refMode, exprMode ) {
|
|
1053
1066
|
for (const value of array ) {
|
|
1054
|
-
if (value)
|
|
1055
|
-
|
|
1067
|
+
if (value) {
|
|
1068
|
+
const user = value._block ? value : query;
|
|
1069
|
+
resolveExpr( value, (value.path ? refMode : exprMode), user );
|
|
1070
|
+
}
|
|
1056
1071
|
}
|
|
1057
1072
|
}
|
|
1058
1073
|
|
|
@@ -1566,6 +1581,8 @@ function resolve( model ) {
|
|
|
1566
1581
|
// No traversal into query, art.$queries set in define.js
|
|
1567
1582
|
// No subqueries for type projections, nor in annotation expressions.
|
|
1568
1583
|
const { query } = expr;
|
|
1584
|
+
if (!query.from && !query.args) // parser error
|
|
1585
|
+
return type; // ...see also TODO in initQueryExpression()
|
|
1569
1586
|
if (query._main?.kind === 'type' ||
|
|
1570
1587
|
(!query.kind && !query._leadingQuery)) { // UNION has _leadingQuery
|
|
1571
1588
|
error( 'expr-no-subquery', [ expr.location, user ], {
|