@sap/cds-compiler 3.5.2 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -1
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/model.js +33 -22
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +29 -20
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +39 -42
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +3 -3
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +23 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +29 -20
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +3 -16
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +8 -8
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +2 -1
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +53 -39
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
package/lib/base/messages.js
CHANGED
|
@@ -664,9 +664,9 @@ const quote = { // could be an option in the future
|
|
|
664
664
|
|
|
665
665
|
const paramsTransform = {
|
|
666
666
|
// simple convenience:
|
|
667
|
-
name:
|
|
668
|
-
id:
|
|
669
|
-
alias:
|
|
667
|
+
name: quote.double,
|
|
668
|
+
id: quote.double,
|
|
669
|
+
alias: quote.double,
|
|
670
670
|
anno,
|
|
671
671
|
annos: transformManyWith( anno ),
|
|
672
672
|
delimited: n => quote.single( `![${ n }]` ), // represent as delimited id (TODO: double-']')
|
|
@@ -688,6 +688,7 @@ const paramsTransform = {
|
|
|
688
688
|
line: quote.direct,
|
|
689
689
|
col: quote.direct,
|
|
690
690
|
value,
|
|
691
|
+
rawvalue: quote.single,
|
|
691
692
|
rawvalues: transformManyWith( quote.single ), // no 'double' quotes for strings
|
|
692
693
|
othervalue: value,
|
|
693
694
|
art: transformArg,
|
|
@@ -719,7 +720,7 @@ function value( val ) {
|
|
|
719
720
|
return quote.single( val );
|
|
720
721
|
}
|
|
721
722
|
case 'string': {
|
|
722
|
-
// TODO: should we also shorten the string if too long?
|
|
723
|
+
// TODO: should we also shorten the string if too long? TODO: false, true, null?
|
|
723
724
|
return (!val ||
|
|
724
725
|
Number.parseFloat( val ).toString() === val ||
|
|
725
726
|
// with quotes (TODO: use `…` with escape chars):
|
|
@@ -768,7 +769,7 @@ function transformManyWith( t, sorted ) {
|
|
|
768
769
|
function quoted( name ) {
|
|
769
770
|
if (typeof name === 'string')
|
|
770
771
|
return quote.double( name );
|
|
771
|
-
throw new CompilerAssertion( `Expecting a string, not ${ name }` );
|
|
772
|
+
throw new CompilerAssertion( `Expecting a string, not ${ typeof name } (${ JSON.stringify(name) })` );
|
|
772
773
|
}
|
|
773
774
|
|
|
774
775
|
function tokenSymbol( token ) {
|
|
@@ -882,7 +883,7 @@ function replaceInString( text, params ) {
|
|
|
882
883
|
parts.push( text.substring( start ) );
|
|
883
884
|
const remain = (params['#']) ? [] : Object.keys( params ).filter( n => !usedParams.includes(n) );
|
|
884
885
|
if (remain.length) {
|
|
885
|
-
const remains
|
|
886
|
+
const remains = remain.map( n => `${ n.toUpperCase() } = ${ params[n] }` ).join(', ');
|
|
886
887
|
return `${ parts.join('') }; ${ remains }`;
|
|
887
888
|
}
|
|
888
889
|
return parts.join('');
|
|
@@ -1197,9 +1198,15 @@ function artName( art, omit ) {
|
|
|
1197
1198
|
r.push( (art.kind === 'mixin' ? 'mixin:' : 'alias:') + quoted( name.alias ) );
|
|
1198
1199
|
if (name.param != null && omit !== 'param')
|
|
1199
1200
|
r.push( name.param ? `param:${ quoted( name.param ) }` : 'returns' ); // TODO: join
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1201
|
+
|
|
1202
|
+
if (name.element && omit !== 'element') {
|
|
1203
|
+
if (name.select != null && !art.$inferred)
|
|
1204
|
+
r.push( 'column:' + quoted( name.element ) );
|
|
1205
|
+
else
|
|
1206
|
+
// r.push( `${ art.kind }: ${ quoted( name.element )}` ); or even better element:"assoc"/key:"i" same with enum
|
|
1207
|
+
r.push( (art.kind === 'enum' ? 'enum:' : 'element:') + quoted( name.element ) );
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1203
1210
|
if (art.kind === '$self')
|
|
1204
1211
|
r.push( `alias:${ quoted( name.alias ) }` ); // should be late due to $self in anonymous aspect
|
|
1205
1212
|
return r.join('/');
|
|
@@ -1237,7 +1244,7 @@ function homeName( art, absoluteOnly ) {
|
|
|
1237
1244
|
}
|
|
1238
1245
|
|
|
1239
1246
|
// The "home" for extensions is handled differently because `_artifact` is not
|
|
1240
|
-
// set for unknown extensions and we could have nested extensions.
|
|
1247
|
+
// set for unknown extensions, and we could have nested extensions.
|
|
1241
1248
|
function homeNameForExtend( art ) {
|
|
1242
1249
|
const kind = art.kind || 'extend';
|
|
1243
1250
|
// TODO: fix the following - do like in collectArtifactExtensions() or
|
|
@@ -1286,252 +1293,308 @@ function homeNameForExtend( art ) {
|
|
|
1286
1293
|
}
|
|
1287
1294
|
}
|
|
1288
1295
|
|
|
1296
|
+
/**
|
|
1297
|
+
* Construct a semantic location for the given CSN path.
|
|
1298
|
+
* Works without a CSN model, but is less precise.
|
|
1299
|
+
*
|
|
1300
|
+
* Example:
|
|
1301
|
+
* path: ["definitions", "E", "elements", "s"],
|
|
1302
|
+
* result: entity:“E”/element:“s”
|
|
1303
|
+
*
|
|
1304
|
+
* @param {CSN.Model} model
|
|
1305
|
+
* @param {CSN.Options} options
|
|
1306
|
+
* @param {CSN.Path} csnPath
|
|
1307
|
+
* @return {string|null}
|
|
1308
|
+
*/
|
|
1289
1309
|
function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
|
|
1290
1310
|
if (!model)
|
|
1291
1311
|
return null;
|
|
1292
|
-
|
|
1293
|
-
csnPath = [ ...csnPath ];
|
|
1312
|
+
let result = '';
|
|
1294
1313
|
const csnDictionaries = [
|
|
1295
1314
|
'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
|
|
1296
1315
|
];
|
|
1297
|
-
|
|
1316
|
+
// Properties that (currently) end the semantic location.
|
|
1317
|
+
const queryPropsLast = [ 'where', 'groupBy', 'having', 'orderBy', 'limit', 'offset' ];
|
|
1318
|
+
|
|
1319
|
+
let index = 0;
|
|
1320
|
+
/** @type {CSN.PathSegment} */
|
|
1321
|
+
let step = csnPath[index];
|
|
1322
|
+
let currentThing = model?.[step];
|
|
1323
|
+
|
|
1324
|
+
function next(steps = 1) {
|
|
1325
|
+
for (; steps > 0; --steps) {
|
|
1326
|
+
++index;
|
|
1327
|
+
step = csnPath[index];
|
|
1328
|
+
currentThing = currentThing && currentThing[step];
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1298
1331
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
if (ext.annotate)
|
|
1302
|
-
return `annotate:${ quoted(ext.annotate) }`;
|
|
1303
|
-
return `extend:${ quoted(ext.extend) }`;
|
|
1332
|
+
function peek(steps = 1) {
|
|
1333
|
+
return csnPath[index + steps];
|
|
1304
1334
|
}
|
|
1305
1335
|
|
|
1306
|
-
|
|
1336
|
+
// First step; always one of: -------------------------------------
|
|
1307
1337
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1338
|
+
if (step === 'definitions') {
|
|
1339
|
+
next(); // "definitions"
|
|
1340
|
+
const kind = currentThing?.kind || 'artifact';
|
|
1341
|
+
result += `${ kind }:${ quoted(step) }`;
|
|
1342
|
+
}
|
|
1343
|
+
else if (step === 'vocabularies') {
|
|
1344
|
+
next(); // dictionary name
|
|
1345
|
+
if (index < csnPath.length)
|
|
1346
|
+
result += `annotation:${ quoted(csnPath[index]) }`;
|
|
1347
|
+
else
|
|
1348
|
+
result += 'vocabularies';
|
|
1349
|
+
}
|
|
1350
|
+
else if (step === 'extensions') {
|
|
1351
|
+
next(); // "extensions"
|
|
1352
|
+
if (!currentThing) {
|
|
1353
|
+
result += `extension:${ step }`;
|
|
1354
|
+
}
|
|
1355
|
+
else {
|
|
1356
|
+
const name = currentThing.annotate || currentThing.extend;
|
|
1357
|
+
const kind = currentThing.annotate ? 'annotate' : 'extend';
|
|
1358
|
+
result += `${ kind }:${ quoted(name) }`;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1317
1361
|
|
|
1318
1362
|
if (!currentThing)
|
|
1319
1363
|
return result;
|
|
1320
1364
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
let currentAnno = null;
|
|
1337
|
-
|
|
1338
|
-
// for top level actions
|
|
1339
|
-
if (currentThing.kind === 'action')
|
|
1340
|
-
inAction = true;
|
|
1341
|
-
for (const [ index, step ] of csnPath.entries()) {
|
|
1342
|
-
currentThing = currentThing[step];
|
|
1343
|
-
if (csnDictionaries.includes(step) && !inCsnDict) {
|
|
1344
|
-
inCsnDict = true;
|
|
1345
|
-
switch (step) {
|
|
1346
|
-
case 'elements':
|
|
1347
|
-
if (!inElement) {
|
|
1348
|
-
inElement = true;
|
|
1349
|
-
// do not print intermediate items
|
|
1350
|
-
inItems = false;
|
|
1351
|
-
}
|
|
1352
|
-
break;
|
|
1353
|
-
case 'actions':
|
|
1354
|
-
inAction = true;
|
|
1355
|
-
break;
|
|
1356
|
-
case 'params':
|
|
1357
|
-
inParam = true;
|
|
1358
|
-
break;
|
|
1359
|
-
case 'enum':
|
|
1360
|
-
inElement = false;
|
|
1361
|
-
inEnum = true;
|
|
1362
|
-
break;
|
|
1363
|
-
case 'mixin':
|
|
1364
|
-
inMixin = true;
|
|
1365
|
-
inQuery = false;
|
|
1366
|
-
break;
|
|
1367
|
-
default:
|
|
1368
|
-
if (inElement) {
|
|
1369
|
-
// close element
|
|
1370
|
-
result += element();
|
|
1371
|
-
inElement = false;
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
else if ( inQuery ) {
|
|
1376
|
-
if (step === 'SELECT') {
|
|
1377
|
-
if (!csnPath[index + 1]) {
|
|
1378
|
-
result += select();
|
|
1379
|
-
}
|
|
1380
|
-
// only print last query prop for paths like
|
|
1381
|
-
// [... 'query', 'SELECT', 'from', 'SELECT', 'elements', 'struct'] -> select:2/element:"struct"
|
|
1382
|
-
// no from in the semantic location in this case
|
|
1383
|
-
else if (queryProps.includes(csnPath[index + 1]) && (!csnPath[index + 2] || query.isOnlySelect)) {
|
|
1384
|
-
const clause = csnPath[index + 1];
|
|
1385
|
-
result += select();
|
|
1386
|
-
result += `/${ clause }`;
|
|
1365
|
+
let selectDepth = (csnPath[0] !== 'extensions') ? queryDepthForMessage(csnPath, model, currentThing) : null;
|
|
1366
|
+
|
|
1367
|
+
// Artifact ref -------------------------------------
|
|
1368
|
+
|
|
1369
|
+
next();
|
|
1370
|
+
while (index < csnPath.length) {
|
|
1371
|
+
if (step === 'elements' || step === 'items') {
|
|
1372
|
+
if (index >= csnPath.length - 1)
|
|
1373
|
+
break; // last segment
|
|
1374
|
+
|
|
1375
|
+
const elementHierarchy = [];
|
|
1376
|
+
while (index < (csnPath.length - 1) && (step === 'items' || step === 'elements')) {
|
|
1377
|
+
if (step === 'elements') {
|
|
1378
|
+
next();
|
|
1379
|
+
elementHierarchy.push(step);
|
|
1387
1380
|
}
|
|
1381
|
+
next();
|
|
1388
1382
|
}
|
|
1389
|
-
|
|
1390
|
-
result +=
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
inQuery = false;
|
|
1394
|
-
}
|
|
1395
|
-
else if (inElement) {
|
|
1396
|
-
result += select();
|
|
1397
|
-
elements.push(step);
|
|
1398
|
-
inQuery = false;
|
|
1399
|
-
}
|
|
1383
|
+
if (elementHierarchy.length > 0)
|
|
1384
|
+
result += `/element:${quoted(elementHierarchy.join('.'))}`;
|
|
1385
|
+
// no trailing /elements or /items
|
|
1386
|
+
continue;
|
|
1400
1387
|
}
|
|
1401
|
-
else if (
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1388
|
+
else if (step === 'actions') {
|
|
1389
|
+
next(); // "actions"
|
|
1390
|
+
if (index < csnPath.length) {
|
|
1391
|
+
const kind = currentThing?.kind || 'action';
|
|
1392
|
+
result += `/${ kind }:${ quoted(csnPath[index]) }`;
|
|
1405
1393
|
}
|
|
1406
|
-
else {
|
|
1407
|
-
result +=
|
|
1394
|
+
else { // actions is last segment
|
|
1395
|
+
result += '/actions';
|
|
1408
1396
|
}
|
|
1409
1397
|
}
|
|
1410
|
-
else if (
|
|
1411
|
-
|
|
1398
|
+
else if (step === 'params') {
|
|
1399
|
+
dictEntry('param');
|
|
1400
|
+
}
|
|
1401
|
+
else if (step === 'enum' || step === 'mixin') {
|
|
1402
|
+
dictEntry(step);
|
|
1412
1403
|
}
|
|
1413
|
-
else if (
|
|
1414
|
-
|
|
1404
|
+
else if (step === 'cast') {
|
|
1405
|
+
// To shorten the location, only print 'cast' if it's not a
|
|
1406
|
+
// redirection target.
|
|
1407
|
+
if (csnPath[index+1] !== 'on' && csnPath[index+1] !== 'target') {
|
|
1408
|
+
result += '/cast';
|
|
1409
|
+
}
|
|
1415
1410
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
result +=
|
|
1419
|
-
|
|
1420
|
-
inKeys = true;
|
|
1411
|
+
// @ts-ignore
|
|
1412
|
+
else if (queryPropsLast.includes(step)) {
|
|
1413
|
+
result += '/' + step;
|
|
1414
|
+
break; // always last step
|
|
1421
1415
|
}
|
|
1422
|
-
else if (
|
|
1423
|
-
|
|
1424
|
-
result += `${ element() }/on`;
|
|
1425
|
-
inElement = false;
|
|
1416
|
+
else if (step === 'on') {
|
|
1417
|
+
result += '/on';
|
|
1426
1418
|
break;
|
|
1427
1419
|
}
|
|
1428
|
-
else if (
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
inElement = false;
|
|
1435
|
-
inItems = true;
|
|
1436
|
-
}
|
|
1420
|
+
else if (step === 'target') {
|
|
1421
|
+
if (currentThing)
|
|
1422
|
+
result += '/target:' + quoted(currentThing);
|
|
1423
|
+
else
|
|
1424
|
+
result += '/target';
|
|
1425
|
+
break;
|
|
1437
1426
|
}
|
|
1438
|
-
else if (
|
|
1439
|
-
//
|
|
1440
|
-
if (csnPath[index - 1] === 'elements')
|
|
1441
|
-
elements.push(step);
|
|
1427
|
+
else if (step === 'xpr' || step === 'ref' || step === 'as') {
|
|
1428
|
+
break; // don't go into xprs, refs, aliases, etc.
|
|
1442
1429
|
}
|
|
1443
|
-
else if (
|
|
1444
|
-
|
|
1445
|
-
inItems = false;
|
|
1430
|
+
else if (step === 'returns') {
|
|
1431
|
+
result += '/returns';
|
|
1446
1432
|
}
|
|
1447
|
-
else if (
|
|
1448
|
-
if (
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
else if (inRef)
|
|
1452
|
-
result += `:${ quoted(currentThing) }`;
|
|
1453
|
-
else if (currentThing.ref)
|
|
1454
|
-
result += `:${ quoted(currentThing.ref.map(r => (r.id ? r.id : r)).join('.')) }`;
|
|
1455
|
-
else
|
|
1456
|
-
return '';
|
|
1457
|
-
|
|
1458
|
-
break;
|
|
1459
|
-
}
|
|
1460
|
-
if ( step === 'ref')
|
|
1461
|
-
inRef = true;
|
|
1433
|
+
else if (step === 'query' || step === 'projection') {
|
|
1434
|
+
if (!queryPath())
|
|
1435
|
+
break; // something failed
|
|
1436
|
+
continue;
|
|
1462
1437
|
}
|
|
1463
|
-
else if (
|
|
1464
|
-
|
|
1438
|
+
else if (step === 'cardinality') {
|
|
1439
|
+
// ignore; all messages pointing to cardinality already mention "cardinality".
|
|
1440
|
+
// Also, no annotations on cardinalities.
|
|
1465
1441
|
break;
|
|
1466
1442
|
}
|
|
1467
|
-
else if (
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
result +=
|
|
1475
|
-
|
|
1476
|
-
|
|
1443
|
+
else if (step === 'type') {
|
|
1444
|
+
break; // we don't go into types
|
|
1445
|
+
}
|
|
1446
|
+
else if (step === 'keys') { // e.g. association foreign keys
|
|
1447
|
+
next(); // "keys"
|
|
1448
|
+
if (index < csnPath.length) {
|
|
1449
|
+
const key = aliasOrReference();
|
|
1450
|
+
result += `/key:${ key ? quoted(key) : step }`;
|
|
1451
|
+
}
|
|
1452
|
+
break;
|
|
1477
1453
|
}
|
|
1478
1454
|
else if (step[0] === '@') {
|
|
1479
|
-
|
|
1480
|
-
|
|
1455
|
+
// Annotations are always the last step.
|
|
1456
|
+
// Nothing comes after them, everything is user defined.
|
|
1457
|
+
result += `/${ quoted(csnPath[index]) }`;
|
|
1458
|
+
break;
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
if (options.testMode)
|
|
1462
|
+
throw new CompilerAssertion(`semantic location: Missing segment: ${ csnPath[index] } for path ${ JSON.stringify( csnPath) }`);
|
|
1463
|
+
break;
|
|
1481
1464
|
}
|
|
1465
|
+
next();
|
|
1482
1466
|
}
|
|
1483
|
-
|
|
1484
|
-
result += `${ element() }/items`;
|
|
1485
|
-
else if ( inElement )
|
|
1486
|
-
result += element();
|
|
1487
|
-
if ( currentAnno )
|
|
1488
|
-
result += `/${ quoted(currentAnno) }`;
|
|
1467
|
+
|
|
1489
1468
|
return result;
|
|
1490
1469
|
|
|
1491
|
-
function
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
}
|
|
1496
|
-
function selectAndMixin( name ) {
|
|
1497
|
-
return `${ select() }/mixin:${ quoted(name) }`;
|
|
1498
|
-
}
|
|
1499
|
-
function element() {
|
|
1500
|
-
return `/element:${ quoted(elements.join('.')) }`;
|
|
1501
|
-
}
|
|
1502
|
-
function param( name ) {
|
|
1503
|
-
return `/param:${ quoted(name) }`;
|
|
1504
|
-
}
|
|
1505
|
-
function func( name ) {
|
|
1506
|
-
if (currentThing?.kind)
|
|
1507
|
-
return `/${ currentThing.kind }:${ quoted(name) }`;
|
|
1508
|
-
return `/action:${ quoted(name) }`;
|
|
1509
|
-
}
|
|
1510
|
-
function elementAndEnum( name ) {
|
|
1511
|
-
return `${ element() }/enum:${ quoted(name) }`;
|
|
1470
|
+
function dictEntry( prefix ) {
|
|
1471
|
+
next(); // dictionary name
|
|
1472
|
+
if (index < csnPath.length)
|
|
1473
|
+
result += `/${ prefix }:${ quoted(csnPath[index]) }`;
|
|
1512
1474
|
}
|
|
1513
1475
|
|
|
1514
1476
|
/**
|
|
1515
|
-
*
|
|
1516
|
-
* check if targetQuery is only select in entity.
|
|
1477
|
+
* @return {string}
|
|
1517
1478
|
*/
|
|
1518
|
-
function
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1479
|
+
function currentRefName() {
|
|
1480
|
+
if (currentThing?.id)
|
|
1481
|
+
return currentThing?.id;
|
|
1482
|
+
else if (currentThing?.as)
|
|
1483
|
+
return currentThing?.as;
|
|
1484
|
+
else if (typeof currentThing === 'string')
|
|
1485
|
+
return currentThing;
|
|
1486
|
+
return undefined;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function aliasOrReference() {
|
|
1490
|
+
if (!currentThing?.as && currentThing?.ref) {
|
|
1491
|
+
// Create implicit alias.
|
|
1492
|
+
const { ref } = currentThing;
|
|
1493
|
+
const name = ref[ref.length - 1]?.id || ref[ref.length - 1];
|
|
1494
|
+
if (csnPath[index + 1] === 'ref' && csnPath[index + 2] === ref.length - 1) {
|
|
1495
|
+
// if the next ref points to the implicit alias, consume it to avoid duplicate names.
|
|
1496
|
+
next(2);
|
|
1497
|
+
}
|
|
1498
|
+
return name;
|
|
1499
|
+
}
|
|
1500
|
+
return currentRefName();
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function queryPath() {
|
|
1504
|
+
next();
|
|
1505
|
+
while (index < csnPath.length) {
|
|
1506
|
+
if (step === 'SELECT' || step === 'SET') {
|
|
1507
|
+
if (selectDepth > 0) {
|
|
1508
|
+
// Once inside a SELECT, go to the "last" SELECT. Only print path steps after
|
|
1509
|
+
// the last SELECT, e.g. "columns".
|
|
1510
|
+
for (let j = csnPath.length - 1; j > index; --j) {
|
|
1511
|
+
// @ts-ignore
|
|
1512
|
+
if (csnPath[j] === 'SELECT' && !csnDictionaries.includes(csnPath[j-1])) {
|
|
1513
|
+
next(j - index); // found last SELECT
|
|
1514
|
+
break;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
result += `/select:${ selectDepth }`;
|
|
1518
|
+
} else {
|
|
1519
|
+
result += '/select';
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
else if (step === 'from') {
|
|
1523
|
+
// Don't check for alias, possibly confuses users.
|
|
1524
|
+
result += '/from';
|
|
1525
|
+
}
|
|
1526
|
+
else if (step === 'columns') {
|
|
1527
|
+
next();
|
|
1528
|
+
if (index >= csnPath.length)
|
|
1529
|
+
continue; // no column name
|
|
1530
|
+
|
|
1531
|
+
let elementHierarchy = [];
|
|
1532
|
+
|
|
1533
|
+
// Concat column+expand/inline to get a name similar to elements.
|
|
1534
|
+
do {
|
|
1535
|
+
if (currentThing?.inline)
|
|
1536
|
+
elementHierarchy.push(quote.angle(step + 1));
|
|
1537
|
+
else if (currentThing === '*')
|
|
1538
|
+
elementHierarchy.push(quote.angle(currentThing));
|
|
1539
|
+
else
|
|
1540
|
+
elementHierarchy.push(aliasOrReference());
|
|
1541
|
+
|
|
1542
|
+
if (peek() === 'expand' || peek() === 'inline') {
|
|
1543
|
+
next(); // skip expand/inline
|
|
1544
|
+
next(); // go to next column
|
|
1545
|
+
} else {
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
} while (index < csnPath.length);
|
|
1549
|
+
|
|
1550
|
+
if (elementHierarchy.length > 0)
|
|
1551
|
+
result += `/column:${ quoted(elementHierarchy.join('.')) }`;
|
|
1552
|
+
}
|
|
1553
|
+
else if(step === 'args') {
|
|
1554
|
+
// Should only be reached for cases, where no SELECT in a union is picked.
|
|
1555
|
+
next(); // skip index
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
return true;
|
|
1559
|
+
}
|
|
1560
|
+
next();
|
|
1561
|
+
}
|
|
1562
|
+
return true;
|
|
1532
1563
|
}
|
|
1533
1564
|
}
|
|
1534
1565
|
|
|
1566
|
+
/**
|
|
1567
|
+
* Traverse the view's query until targetQuery is found and count the depth.
|
|
1568
|
+
* If there is only one or no query at all, returns `0` so that the semantic
|
|
1569
|
+
* location knows it does not need to print the query index.
|
|
1570
|
+
*
|
|
1571
|
+
* @param {CSN.Path} csnPath
|
|
1572
|
+
* @param {CSN.Model} model
|
|
1573
|
+
* @param {CSN.Artifact} view
|
|
1574
|
+
* @return {number}
|
|
1575
|
+
*/
|
|
1576
|
+
function queryDepthForMessage( csnPath, model, view ) {
|
|
1577
|
+
const { query: targetQuery } = analyseCsnPath(csnPath, model, false);
|
|
1578
|
+
if (!targetQuery)
|
|
1579
|
+
return 0;
|
|
1580
|
+
const rootQuery = view.query || { SELECT: view.projection };
|
|
1581
|
+
let depth = 1;
|
|
1582
|
+
let totalDepth = 0;
|
|
1583
|
+
let isFound = false;
|
|
1584
|
+
traverseQuery(rootQuery, null, null, (q, querySelect) => {
|
|
1585
|
+
if (querySelect) {
|
|
1586
|
+
totalDepth += 1;
|
|
1587
|
+
if (!isFound)
|
|
1588
|
+
depth += 1;
|
|
1589
|
+
}
|
|
1590
|
+
if (q === targetQuery)
|
|
1591
|
+
isFound = true;
|
|
1592
|
+
});
|
|
1593
|
+
if (totalDepth > 1)
|
|
1594
|
+
return depth;
|
|
1595
|
+
return 0;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1535
1598
|
/**
|
|
1536
1599
|
* Get the explanation string for the given message-id.
|
|
1537
1600
|
* Ensure to have called hasMessageExplanation() before.
|
|
@@ -1587,5 +1650,7 @@ module.exports = {
|
|
|
1587
1650
|
explainMessage,
|
|
1588
1651
|
hasMessageExplanation,
|
|
1589
1652
|
messageIdsWithExplanation,
|
|
1653
|
+
// for tests only
|
|
1590
1654
|
constructSemanticLocationFromCsnPath,
|
|
1655
|
+
homeName,
|
|
1591
1656
|
};
|
package/lib/base/model.js
CHANGED
|
@@ -37,6 +37,30 @@ const availableBetaFlags = {
|
|
|
37
37
|
nestedServices: false,
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
const availableDeprecatedFlags = {
|
|
41
|
+
// the old ones starting with _, : false
|
|
42
|
+
autoCorrectOrderBySourceRefs: true,
|
|
43
|
+
eagerPersistenceForGeneratedEntities: true,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const oldDeprecatedFlags_v2 = [
|
|
47
|
+
'createLocalizedViews',
|
|
48
|
+
'downgradableErrors',
|
|
49
|
+
'generatedEntityNameWithUnderscore',
|
|
50
|
+
'longAutoexposed',
|
|
51
|
+
'noElementsExpansion',
|
|
52
|
+
'noInheritedAutoexposeViaComposition',
|
|
53
|
+
'noScopedRedirections',
|
|
54
|
+
'oldVirtualNotNullPropagation',
|
|
55
|
+
'parensAsStrings',
|
|
56
|
+
'projectionAsQuery',
|
|
57
|
+
'redirectInSubQueries',
|
|
58
|
+
'renderVirtualElements',
|
|
59
|
+
'shortAutoexposed',
|
|
60
|
+
'unmanagedUpInComponent',
|
|
61
|
+
'v1KeysForTemporal',
|
|
62
|
+
];
|
|
63
|
+
|
|
40
64
|
/**
|
|
41
65
|
* Test for early-adaptor feature, stored in option `beta`(new-style) / `betaMode`(old-style)
|
|
42
66
|
* With that, the value of `beta` is a dictionary of feature=>Boolean.
|
|
@@ -59,7 +83,11 @@ function isBetaEnabled( options, feature ) {
|
|
|
59
83
|
/**
|
|
60
84
|
* Test for deprecated feature, stored in option `deprecated`.
|
|
61
85
|
* With that, the value of `deprecated` is a dictionary of feature=>Boolean.
|
|
62
|
-
*
|
|
86
|
+
*
|
|
87
|
+
* If no `feature` is provided, checks if any deprecated option is set
|
|
88
|
+
* which is not mentioned in availableDeprecatedFlags with value true.
|
|
89
|
+
* Useful for newer functionality which might not work with some
|
|
90
|
+
* deprecated feature turned on.
|
|
63
91
|
*
|
|
64
92
|
* Please do not move this function to the "option processor" code.
|
|
65
93
|
*
|
|
@@ -69,30 +97,13 @@ function isBetaEnabled( options, feature ) {
|
|
|
69
97
|
*/
|
|
70
98
|
function isDeprecatedEnabled( options, feature = null ) {
|
|
71
99
|
const { deprecated } = options;
|
|
72
|
-
if (!feature)
|
|
73
|
-
return !!deprecated
|
|
74
|
-
|
|
100
|
+
if (!feature) {
|
|
101
|
+
return !!deprecated && Object.keys( deprecated )
|
|
102
|
+
.some( d => !availableDeprecatedFlags[d] );
|
|
103
|
+
}
|
|
75
104
|
return deprecated && typeof deprecated === 'object' && deprecated[feature];
|
|
76
105
|
}
|
|
77
106
|
|
|
78
|
-
const oldDeprecatedFlags_v2 = [
|
|
79
|
-
'createLocalizedViews',
|
|
80
|
-
'downgradableErrors',
|
|
81
|
-
'generatedEntityNameWithUnderscore',
|
|
82
|
-
'longAutoexposed',
|
|
83
|
-
'noElementsExpansion',
|
|
84
|
-
'noInheritedAutoexposeViaComposition',
|
|
85
|
-
'noScopedRedirections',
|
|
86
|
-
'oldVirtualNotNullPropagation',
|
|
87
|
-
'parensAsStrings',
|
|
88
|
-
'projectionAsQuery',
|
|
89
|
-
'redirectInSubQueries',
|
|
90
|
-
'renderVirtualElements',
|
|
91
|
-
'shortAutoexposed',
|
|
92
|
-
'unmanagedUpInComponent',
|
|
93
|
-
'v1KeysForTemporal',
|
|
94
|
-
];
|
|
95
|
-
|
|
96
107
|
/**
|
|
97
108
|
* In cds-compiler v3, we removed old v2 deprecated flags. That can lead to silent
|
|
98
109
|
* errors such as entity/view names changing. To ensure that the user is forced
|