@sap/cds-compiler 6.1.0 → 6.2.2
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 +43 -0
- package/bin/cdsc.js +6 -2
- package/bin/cdsse.js +1 -1
- package/bin/cdsv2m.js +1 -1
- package/lib/api/main.js +29 -7
- package/lib/base/builtins.js +9 -0
- package/lib/base/keywords.js +1 -1
- package/lib/base/message-registry.js +5 -3
- package/lib/base/messages.js +2 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +7 -2
- package/lib/checks/featureFlags.js +4 -1
- package/lib/compiler/assert-consistency.js +3 -1
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +38 -21
- package/lib/compiler/define.js +24 -5
- package/lib/compiler/extend.js +1 -1
- package/lib/compiler/finalize-parse-cdl.js +9 -1
- package/lib/compiler/generate.js +4 -4
- package/lib/compiler/lsp-api.js +2 -0
- package/lib/compiler/populate.js +8 -8
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +15 -14
- package/lib/compiler/shared.js +6 -6
- package/lib/compiler/tweak-assocs.js +6 -6
- package/lib/compiler/utils.js +9 -16
- package/lib/compiler/xpr-rewrite.js +2 -2
- package/lib/gen/BaseParser.js +35 -29
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1423 -1432
- package/lib/gen/Dictionary.json +1 -0
- package/lib/gen/cdlKeywords.json +26 -0
- package/lib/inspect/inspectPropagation.js +1 -1
- package/lib/json/from-csn.js +2 -2
- package/lib/json/to-csn.js +1 -1
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/model/cloneCsn.js +1 -0
- package/lib/optionProcessor.js +8 -7
- package/lib/parsers/AstBuildingParser.js +24 -21
- package/lib/parsers/identifiers.js +2 -30
- package/lib/render/toCdl.js +63 -9
- package/lib/render/toSql.js +127 -108
- package/lib/render/utils/sql.js +67 -0
- package/lib/transform/addTenantFields.js +4 -4
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +20 -2
- package/lib/transform/forOdata.js +91 -2
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/odata/flattening.js +1 -1
- package/lib/transform/translateAssocsToJoins.js +2 -26
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
|
@@ -29,6 +29,7 @@ const { addLocalizationViews } = require('./localized');
|
|
|
29
29
|
const { cloneFullCsn } = require('../model/cloneCsn');
|
|
30
30
|
const { csnRefs } = require('../model/csnRefs');
|
|
31
31
|
const replaceForeignKeyRefsInExpressionAnnotations = require('./odata/foreignKeyRefsInXprAnnos');
|
|
32
|
+
const { isAnnotationExpression, xprInAnnoProperties } = require('../base/builtins');
|
|
32
33
|
|
|
33
34
|
// Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.
|
|
34
35
|
// The result should be suitable for consumption by EDMX processors (annotations and metadata)
|
|
@@ -293,6 +294,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
293
294
|
|| memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
|
|
294
295
|
}
|
|
295
296
|
|
|
297
|
+
processDynamicFieldControlAnnotations(member);
|
|
298
|
+
|
|
296
299
|
// Mark fields with @odata.on.insert/update as @Core.Computed
|
|
297
300
|
annotateCoreComputed(member);
|
|
298
301
|
|
|
@@ -338,6 +341,90 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
338
341
|
//--------------------------------------------------------------------
|
|
339
342
|
// HELPER SECTION STARTS HERE
|
|
340
343
|
|
|
344
|
+
// Transform @readonly/@mandatory/@disabled into @Common.FieldControl annotation
|
|
345
|
+
// with a when/then/else expression consisting of the input from the annotations.
|
|
346
|
+
function processDynamicFieldControlAnnotations(node) {
|
|
347
|
+
if (node['@Common.FieldControl']) return;
|
|
348
|
+
// TODO (SO): factor this out into a constant so we don't create a fresh array all the time?
|
|
349
|
+
if (['@readonly', '@mandatory', '@disabled'].some(key => typeof node[key] === 'boolean')) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const definedAnnotations = ['@disabled', '@readonly', '@mandatory']
|
|
354
|
+
.filter(key => node[key] && isAnnotationExpression(node[key]));
|
|
355
|
+
|
|
356
|
+
if (definedAnnotations.length === 0) return;
|
|
357
|
+
|
|
358
|
+
const values = {
|
|
359
|
+
'@disabled': { val: 0 },
|
|
360
|
+
'@readonly': { val: 1 },
|
|
361
|
+
'@mandatory': { val: 7 },
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const fieldControl = {
|
|
365
|
+
'=': true,
|
|
366
|
+
xpr: createFieldControlExpression(definedAnnotations),
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
setAnnotation(node, '@Common.FieldControl', fieldControl);
|
|
370
|
+
|
|
371
|
+
function createFieldControlExpression(annotations) {
|
|
372
|
+
let nestedExpression = null;
|
|
373
|
+
|
|
374
|
+
for (let i = annotations.length - 1; i >= 0; i--) {
|
|
375
|
+
const annotation = annotations[i];
|
|
376
|
+
const xprInAnnoValue = getXprFromAnno(node[annotation]);
|
|
377
|
+
const annotationVal = values[annotation];
|
|
378
|
+
|
|
379
|
+
// Build the current annotation's expression
|
|
380
|
+
const currentExpression = [
|
|
381
|
+
'case',
|
|
382
|
+
'when',
|
|
383
|
+
...(Array.isArray(xprInAnnoValue) ? xprInAnnoValue : [xprInAnnoValue]),
|
|
384
|
+
'then',
|
|
385
|
+
annotationVal,
|
|
386
|
+
'else',
|
|
387
|
+
// Use the previous nested expression or default value. Note that annotations
|
|
388
|
+
// are looped backwards
|
|
389
|
+
nestedExpression ? { xpr: nestedExpression } : { val: 3 },
|
|
390
|
+
'end',
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
// Update the nested expression
|
|
394
|
+
nestedExpression = currentExpression;
|
|
395
|
+
}
|
|
396
|
+
return nestedExpression;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function getXprFromAnno(anno) {
|
|
400
|
+
const xprProp = xprInAnnoProperties.find(prop => anno[prop] !== undefined);
|
|
401
|
+
const constructResult = {
|
|
402
|
+
'ref': () => {
|
|
403
|
+
const result = { ref: anno.ref };
|
|
404
|
+
if (anno.cast)
|
|
405
|
+
result.cast = anno.cast;
|
|
406
|
+
return result;
|
|
407
|
+
},
|
|
408
|
+
'xpr': () => anno.xpr,
|
|
409
|
+
'list': () => ({ list: anno.list }),
|
|
410
|
+
'literal': () => constructResult['val'](),
|
|
411
|
+
'val': () => {
|
|
412
|
+
const result = { val: anno.val };
|
|
413
|
+
if (anno.literal)
|
|
414
|
+
result.literal = anno.literal;
|
|
415
|
+
return result;
|
|
416
|
+
},
|
|
417
|
+
'#': () => ({ '#': anno['#'] }),
|
|
418
|
+
'func': () => ({ func: anno.func }),
|
|
419
|
+
'args': () => ({ args: anno.args }),
|
|
420
|
+
'SELECT': () => ({ SELECT: anno.SELECT }),
|
|
421
|
+
'SET': () => ({ SET: anno.SET }),
|
|
422
|
+
'cast': () => constructResult['ref']()
|
|
423
|
+
}
|
|
424
|
+
return constructResult[xprProp]();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
341
428
|
// Mark elements that are annotated with @odata.on.insert/update with the annotation @Core.Computed.
|
|
342
429
|
function annotateCoreComputed(node) {
|
|
343
430
|
// If @Core.Computed is explicitly set, don't overwrite it!
|
|
@@ -403,7 +490,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
403
490
|
setAnnotation(node, `@Capabilities.DeleteRestrictions${ qualifier ? '#' + qualifier : ''}.Deletable`, false);
|
|
404
491
|
setAnnotation(node, `@Capabilities.InsertRestrictions${ qualifier ? '#' + qualifier : ''}.Insertable`, false);
|
|
405
492
|
setAnnotation(node, `@Capabilities.UpdateRestrictions${ qualifier ? '#' + qualifier : ''}.Updatable`, false);
|
|
406
|
-
} else {
|
|
493
|
+
} else if (!isAnnotationExpression(node['@readonly'])) {
|
|
494
|
+
// add @Core.Computed only for non-xpr values of @readonly
|
|
407
495
|
setAnnotation(node, '@Core.Computed', true);
|
|
408
496
|
}
|
|
409
497
|
};
|
|
@@ -426,7 +514,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
426
514
|
|
|
427
515
|
// Only on element level
|
|
428
516
|
if(node.kind == null) {
|
|
429
|
-
if (node['@mandatory'] && !
|
|
517
|
+
if (node['@mandatory'] && !isAnnotationExpression(node['@mandatory'])
|
|
518
|
+
&& !Object.entries(node).some(([k,v]) => k === '@Common.FieldControl' || k.startsWith('@Common.FieldControl.') && v != null)) {
|
|
430
519
|
setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
431
520
|
}
|
|
432
521
|
if (node['@assert.range'] != null)
|
|
@@ -360,7 +360,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
360
360
|
messageFunctions.throwWithError();
|
|
361
361
|
|
|
362
362
|
// TODO: Might have to do this earlier if we want special rendering for projections?
|
|
363
|
-
const findAndMarkSqlServiceArtifacts = options.sqlDialect === 'hana' && options.src === 'hdi' && (csn.meta?.[featureFlags]?.$sqlService || csn.meta?.[featureFlags]?.$dummyService) ? processSqlServices(csn, options): () => {}
|
|
363
|
+
const findAndMarkSqlServiceArtifacts = options.sqlDialect === 'hana' && options.src === 'hdi' && (csn.meta?.[featureFlags]?.$sqlService || csn.meta?.[featureFlags]?.$dummyService || csn.meta?.[featureFlags]?.$dataProductService) ? processSqlServices(csn, options): () => {}
|
|
364
364
|
|
|
365
365
|
// Apply view-specific transformations
|
|
366
366
|
// (160) Projections now finally become views
|
|
@@ -472,7 +472,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, getFinalTy
|
|
|
472
472
|
generatedForeignKeysForAssoc.forEach(gfk => gfk[1]['@odata.foreignKey4'] = flatEltName);
|
|
473
473
|
// reassign the generated foreign keys for current assoc in order to assign
|
|
474
474
|
// correct values for $generatedFieldName later on during flattenManagedAssocsAsKeys();
|
|
475
|
-
// eslint-disable-next-line @stylistic/
|
|
475
|
+
// eslint-disable-next-line @stylistic/max-statements-per-line
|
|
476
476
|
setProp(flatElt, '$generatedForeignKeys', generatedForeignKeysForAssoc.map(gfk => { return { name: gfk[0] }}));
|
|
477
477
|
}
|
|
478
478
|
}
|
|
@@ -18,8 +18,6 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
18
18
|
const compileOptions = { ...options, $skipNameCheck: true };
|
|
19
19
|
delete compileOptions.csnFlavor;
|
|
20
20
|
|
|
21
|
-
//require('fs').writeFileSync('./csninput_a2j.json', JSON.stringify(csn, null,2))
|
|
22
|
-
//console.log('CSN passed by A2J to compiler:',JSON.stringify(csn,null,2))
|
|
23
21
|
const model = recompileX(csn, compileOptions);
|
|
24
22
|
timetrace.stop('A2J: Recompiling model');
|
|
25
23
|
timetrace.start('A2J: Translating associations to joins');
|
|
@@ -45,11 +43,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
45
43
|
deduplicateMessages( options.messages );
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
const newCsn = compactModel(model, compileOptions);
|
|
50
|
-
//require('fs').writeFileSync('./csnoutput_a2j.json', JSON.stringify(newCsn, null,2))
|
|
51
|
-
//console.log('CSN returned by A2J:',JSON.stringify(newCsn,null,2))
|
|
52
|
-
return newCsn;
|
|
46
|
+
return compactModel(model, compileOptions);
|
|
53
47
|
}
|
|
54
48
|
|
|
55
49
|
function translateAssocsToJoins(model, inputOptions = {})
|
|
@@ -355,12 +349,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
355
349
|
const leafArt = tail[tail.length-1]._artifact;
|
|
356
350
|
const tailPath = tail.map(p=>p.id).join(pathDelimiter);
|
|
357
351
|
const fk = tail[i]._artifact.$flatSrcFKs.find(f => f._artifact === leafArt && f.acc.startsWith(tailPath));
|
|
358
|
-
if(!fk)
|
|
359
|
-
// const revealInternalProperties = require('../model/revealInternalProperties.js');
|
|
360
|
-
// console.log('++++++++ Path tail: ', revealInternalProperties(tail[tail.length-1]._artifact));
|
|
361
|
-
// console.log('******** Flat FKs\n', tail[i]._artifact.$flatSrcFKs.map(f => revealInternalProperties(f._artifact)));
|
|
352
|
+
if (!fk)
|
|
362
353
|
throw new CompilerAssertion('Debug me: No flat FK found for FK rewriting');
|
|
363
|
-
}
|
|
364
354
|
// replace tail path with flattened foreign key including prefix
|
|
365
355
|
tail.splice(i, tail.length, fk);
|
|
366
356
|
}
|
|
@@ -1707,17 +1697,6 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1707
1697
|
return p.map(p => delim + p.id + delim).join('.');
|
|
1708
1698
|
}
|
|
1709
1699
|
|
|
1710
|
-
// for debugging only
|
|
1711
|
-
// eslint-disable-next-line no-unused-vars
|
|
1712
|
-
function printPath(pathDict, env)
|
|
1713
|
-
{
|
|
1714
|
-
const alias = (pathDict.name && pathDict.name.id) || '<undefined>'
|
|
1715
|
-
const path = pathDict.path;
|
|
1716
|
-
const s = pathAsStr(path, '"');
|
|
1717
|
-
const me = env.lead && (env.lead.name.id || env.lead.op);
|
|
1718
|
-
console.log(me + ': ' + env.location + ': ' + s + ' alias: ' + alias);
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
1700
|
function clone(obj) {
|
|
1722
1701
|
let newObj;
|
|
1723
1702
|
if (typeof obj !== 'object' || obj === null) // return primitive type, note that typeof null === 'object'
|
|
@@ -1941,9 +1920,6 @@ function walkPath(node, env)
|
|
|
1941
1920
|
// else if(path) {
|
|
1942
1921
|
// var util = require('util');
|
|
1943
1922
|
// var { reveal } = require('../model/revealInternalProperties');
|
|
1944
|
-
|
|
1945
|
-
// console.log('Path not resolved, can\'t find leaf _artifact: ' + path.map(p=>p.id).join('.') + '\n' +
|
|
1946
|
-
// util.inspect( reveal( node, false ), false, null ));
|
|
1947
1923
|
// }
|
|
1948
1924
|
return path;
|
|
1949
1925
|
}
|
|
@@ -580,7 +580,7 @@ function checkFileCase( dep, realpath, nativeRealpath, messageFunctions ) {
|
|
|
580
580
|
for (const using of dep.usingFroms) {
|
|
581
581
|
const { warning } = messageFunctions;
|
|
582
582
|
warning('file-unexpected-case-mismatch', [ using.location, using ], {},
|
|
583
|
-
// eslint-disable-next-line @stylistic/
|
|
583
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
584
584
|
'The imported filename differs on the filesystem; ensure that capitalization matches the actual file\'s name');
|
|
585
585
|
}
|
|
586
586
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.2",
|
|
4
4
|
"description": "CDS (Core Data Services) compiler and backends",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"download": "exit 0",
|
|
18
18
|
"gen": "npm run rdpg",
|
|
19
|
-
"rdpg": "node ./redepage/bin/redepage --compile lib/gen/CdlParser.js --copy-base-parser lib/parsers/CdlGrammar.g4 && node scripts/genGrammarChecksum.js",
|
|
19
|
+
"rdpg": "node ./redepage/bin/redepage --compile lib/gen/CdlParser.js --copy-base-parser lib/parsers/CdlGrammar.g4 && node ./scripts/createCdlKeywordList.js && node scripts/genGrammarChecksum.js",
|
|
20
20
|
"xmakeAfterInstall": "npm run gen",
|
|
21
21
|
"xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
|
|
22
22
|
"test": "node scripts/xmakeTestDispatcher.js",
|