@sap/cds-compiler 3.6.2 → 3.8.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 +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
package/lib/render/toRename.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
const { makeMessageFunction } = require('../base/messages');
|
|
5
5
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
6
|
-
const {
|
|
6
|
+
const { forEachDefinition } = require('../model/csnUtils');
|
|
7
7
|
const { optionProcessor } = require('../optionProcessor');
|
|
8
8
|
const { isBetaEnabled } = require('../base/model');
|
|
9
9
|
const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
|
|
10
|
+
const { getIdentifierUtils } = require('./utils/sql');
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -46,6 +47,9 @@ function toRename( inputCsn, options ) {
|
|
|
46
47
|
|
|
47
48
|
// FIXME: Currently, 'toRename' implies transformation for HANA (transferring the options to forRelationalDB)
|
|
48
49
|
const csn = transformForRelationalDBWithCsn(inputCsn, options, 'to.rename');
|
|
50
|
+
const hdbcdsOrQuotedIdentifiers = getIdentifierUtils(csn, options);
|
|
51
|
+
const plainIdentifiers = getIdentifierUtils(csn, { sqlDialect: 'hana', sqlMapping: 'plain' });
|
|
52
|
+
|
|
49
53
|
// forRelationalDB looses empty contexts and services, add them again so that toRename can calculate the namespaces
|
|
50
54
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
51
55
|
if ((artifact.kind === 'context' || artifact.kind === 'service') && csn.definitions[artifactName] === undefined)
|
|
@@ -82,10 +86,12 @@ function toRename( inputCsn, options ) {
|
|
|
82
86
|
function renameTableAndColumns( artifactName, art ) {
|
|
83
87
|
let resultStr = '';
|
|
84
88
|
if (art.kind === 'entity' && !art.query) {
|
|
85
|
-
const beforeTableName =
|
|
86
|
-
const afterTableName =
|
|
89
|
+
const beforeTableName = hdbcdsOrQuotedIdentifiers.renderArtifactName(artifactName);
|
|
90
|
+
const afterTableName = plainIdentifiers.renderArtifactName(artifactName);
|
|
87
91
|
|
|
88
|
-
if (beforeTableName
|
|
92
|
+
if (beforeTableName.toUpperCase() === `"${afterTableName}"`)
|
|
93
|
+
resultStr += ` --EXEC 'RENAME TABLE ${beforeTableName} TO ${afterTableName}';\n`;
|
|
94
|
+
else if (beforeTableName !== afterTableName)
|
|
89
95
|
resultStr += ` EXEC 'RENAME TABLE ${beforeTableName} TO ${afterTableName}';\n`;
|
|
90
96
|
|
|
91
97
|
|
|
@@ -93,13 +99,14 @@ function toRename( inputCsn, options ) {
|
|
|
93
99
|
const e = art.elements[name];
|
|
94
100
|
let str = '';
|
|
95
101
|
|
|
96
|
-
const beforeColumnName = quoteSqlId(name);
|
|
97
|
-
const afterColumnName =
|
|
102
|
+
const beforeColumnName = hdbcdsOrQuotedIdentifiers.quoteSqlId(name);
|
|
103
|
+
const afterColumnName = plainIdentifiers.quoteSqlId(name);
|
|
98
104
|
|
|
99
105
|
if (!e._ignore) {
|
|
100
106
|
if (e.target)
|
|
101
107
|
str = ` EXEC 'ALTER TABLE ${afterTableName} DROP ASSOCIATION ${beforeColumnName}';\n`;
|
|
102
|
-
|
|
108
|
+
else if (beforeColumnName.toUpperCase() === `"${afterColumnName}"` ) // Basically a no-op - render commented out
|
|
109
|
+
str = ` --EXEC 'RENAME COLUMN ${afterTableName}.${beforeColumnName} TO ${afterColumnName}';\n`;
|
|
103
110
|
else if (beforeColumnName !== afterColumnName)
|
|
104
111
|
str = ` EXEC 'RENAME COLUMN ${afterTableName}.${beforeColumnName} TO ${afterColumnName}';\n`;
|
|
105
112
|
}
|
|
@@ -108,50 +115,6 @@ function toRename( inputCsn, options ) {
|
|
|
108
115
|
}
|
|
109
116
|
return resultStr;
|
|
110
117
|
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Return 'name' in the form of an absolute CDS name - for the 'hdbcds' naming convention,
|
|
114
|
-
* this means converting '.' to '::' on the border between namespace and top-level artifact.
|
|
115
|
-
* For all other naming conventions, this is a no-op.
|
|
116
|
-
*
|
|
117
|
-
* @param {string} name Name to absolutify
|
|
118
|
-
* @returns {string} Absolute name
|
|
119
|
-
*/
|
|
120
|
-
function absoluteCdsName( name ) {
|
|
121
|
-
if (options.sqlMapping !== 'hdbcds')
|
|
122
|
-
return name;
|
|
123
|
-
|
|
124
|
-
const namespaceName = getNamespace(inputCsn, name);
|
|
125
|
-
if (namespaceName)
|
|
126
|
-
return `${namespaceName}::${name.substring(namespaceName.length + 1)}`;
|
|
127
|
-
|
|
128
|
-
return name;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Return 'name' with appropriate "-quotes, also replacing '::' by '.' if 'options.sqlMapping'
|
|
133
|
-
* is 'quoted'
|
|
134
|
-
*
|
|
135
|
-
* @param {string} name Name to quote
|
|
136
|
-
* @returns {string} Quoted string
|
|
137
|
-
*/
|
|
138
|
-
function quoteSqlId( name ) {
|
|
139
|
-
if (options.sqlMapping === 'quoted')
|
|
140
|
-
name = name.replace(/::/g, '.');
|
|
141
|
-
|
|
142
|
-
return `"${name.replace(/"/g, '""')}"`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Return 'name' with uppercasing and appropriate "-quotes, also replacing '::' and '.' by '_'
|
|
147
|
-
* (to be used by 'plain' naming convention).
|
|
148
|
-
*
|
|
149
|
-
* @param {string} name Name to turn into a plain identifier
|
|
150
|
-
* @returns {string} A plain SQL identifier
|
|
151
|
-
*/
|
|
152
|
-
function plainSqlId( name ) {
|
|
153
|
-
return `"${name.toUpperCase().replace(/(::|\.)/g, '_').replace(/"/g, '""')}"`;
|
|
154
|
-
}
|
|
155
118
|
}
|
|
156
119
|
|
|
157
120
|
module.exports = {
|
package/lib/render/toSql.js
CHANGED
|
@@ -93,10 +93,11 @@ function toSqlDdl( csn, options ) {
|
|
|
93
93
|
return `CAST(${this.renderExpr(withoutCast(x))} AS ${typeRef})`;
|
|
94
94
|
},
|
|
95
95
|
val: renderExpressionLiteral,
|
|
96
|
-
enum
|
|
97
|
-
// TODO:
|
|
96
|
+
enum(x) {
|
|
97
|
+
// TODO: better location; callers should set env.$path
|
|
98
98
|
// FIXME: We can't do enums yet because they are not resolved (and we don't bother finding their value by hand)
|
|
99
|
-
|
|
99
|
+
const loc = x.$location || this.env?._artifact?.$location;
|
|
100
|
+
error('expr-unexpected-enum', [ loc, null ], 'Enum values are not yet supported for conversion to SQL');
|
|
100
101
|
return '';
|
|
101
102
|
},
|
|
102
103
|
ref: renderExpressionRef,
|
|
@@ -54,10 +54,10 @@ function funcWithoutParen( node, dialect ) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Process already rendered expression parts by joining them nicely
|
|
58
|
-
*
|
|
59
|
-
* @param {Array} tokens Array of expression tokens
|
|
57
|
+
* Process already rendered expression parts by joining them nicely.
|
|
58
|
+
* For example, it adds spaces around operators such as `+`, but not around `.`.
|
|
60
59
|
*
|
|
60
|
+
* @param {any[]} tokens Array of expression tokens
|
|
61
61
|
* @returns {string} The rendered xpr
|
|
62
62
|
*/
|
|
63
63
|
function beautifyExprArray( tokens ) {
|
|
@@ -65,8 +65,12 @@ function beautifyExprArray( tokens ) {
|
|
|
65
65
|
let result = '';
|
|
66
66
|
for (let i = 0; i < tokens.length; i++) {
|
|
67
67
|
result += tokens[i];
|
|
68
|
-
// No space after last token, after opening parentheses, before closing parentheses, before comma
|
|
69
|
-
if (i !== tokens.length - 1 &&
|
|
68
|
+
// No space after last token, after opening parentheses, before closing parentheses, before comma, before and after dot
|
|
69
|
+
if (i !== tokens.length - 1 &&
|
|
70
|
+
// current token
|
|
71
|
+
tokens[i] !== '(' && tokens[i] !== '.' &&
|
|
72
|
+
// next token
|
|
73
|
+
tokens[i + 1] !== ')' && tokens[i + 1] !== '.' && tokens[i + 1] !== ',')
|
|
70
74
|
result += ' ';
|
|
71
75
|
}
|
|
72
76
|
return result;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// Currently unused, but may become useful again if HDBCDS -> HDBTABLE
|
|
2
|
+
// handover becomes more prominent. Historically used with the no longer
|
|
3
|
+
// existent option `--compatibility`.
|
|
4
|
+
// If necessary, more complex expressions could be parsed with parseExpr.js
|
|
5
|
+
// and then stringified with parentheses again.
|
|
6
|
+
|
|
1
7
|
'use strict';
|
|
2
8
|
|
|
3
9
|
function isAlreadyBraced(expression, start, end){
|
|
@@ -19,11 +19,12 @@ const { pathName } = require('../../compiler/utils');
|
|
|
19
19
|
* @param {CSN.Options} options Options
|
|
20
20
|
* @param {Function} error Message function for errors
|
|
21
21
|
* @param {Function} info Message function for info
|
|
22
|
+
* @returns {Function} forEachDefinition callback
|
|
22
23
|
*/
|
|
23
24
|
function processAssertUnique( csn, options, error, info ) {
|
|
24
25
|
const { resolvePath, flattenPath } = getTransformers(csn, options);
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
return handleAssertUnique;
|
|
27
28
|
/**
|
|
28
29
|
* The detailed processing - see comment above for what is going on here
|
|
29
30
|
*
|
|
@@ -361,6 +361,8 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
361
361
|
expanded.push(Object.assign({}, current, { as: currentAlias.join(pathDelimiter) } ));
|
|
362
362
|
}
|
|
363
363
|
else { // preserve stuff like .cast for redirection
|
|
364
|
+
if (base[currentAlias[currentAlias.length - 1]]?.value)
|
|
365
|
+
error('query-unsupported-calc', col.$path, { '#': 'inside' });
|
|
364
366
|
expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
|
|
365
367
|
}
|
|
366
368
|
}
|
|
@@ -293,7 +293,7 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
293
293
|
// TODO: use $ignore - _ is for links
|
|
294
294
|
element._ignore = true;
|
|
295
295
|
|
|
296
|
-
const branches = getBranches(element, elementName);
|
|
296
|
+
const branches = getBranches(element, elementName, effectiveType, pathDelimiter);
|
|
297
297
|
const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
|
|
298
298
|
|
|
299
299
|
for (const flatElemName in flatElems) {
|
|
@@ -308,7 +308,7 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
308
308
|
const flatElement = flatElems[flatElemName];
|
|
309
309
|
|
|
310
310
|
// Check if we have a valid notNull chain
|
|
311
|
-
const branch = branches[flatElemName];
|
|
311
|
+
const branch = branches[flatElemName].steps;
|
|
312
312
|
if (flatElement.notNull !== false && !branch.some(s => !s.notNull))
|
|
313
313
|
flatElement.notNull = true;
|
|
314
314
|
|
|
@@ -357,49 +357,49 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
357
357
|
return elements;
|
|
358
358
|
}, Object.create(null));
|
|
359
359
|
}
|
|
360
|
+
}
|
|
360
361
|
|
|
361
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Get not just the leaves, but all branches of a structured element.
|
|
364
|
+
*
|
|
365
|
+
* @param {object} element Structured element
|
|
366
|
+
* @param {string} elementName Name of the structured element
|
|
367
|
+
* @param {Function} effectiveType
|
|
368
|
+
* @param {string} pathDelimiter
|
|
369
|
+
* @returns {object} Returns a dictionary, where the key is the flat name of the branch and the value is an array of element-steps.
|
|
370
|
+
*/
|
|
371
|
+
function getBranches( element, elementName, effectiveType, pathDelimiter ) {
|
|
372
|
+
const branches = {};
|
|
373
|
+
const subbranchNames = [];
|
|
374
|
+
const subbranchElements = [];
|
|
375
|
+
walkElements(element, elementName);
|
|
362
376
|
/**
|
|
363
|
-
*
|
|
377
|
+
* Walk the element chain
|
|
364
378
|
*
|
|
365
|
-
* @param {object}
|
|
366
|
-
* @param {string}
|
|
367
|
-
* @returns {object} Returns a dictionary, where the key is the flat name of the branch and the value is an array of element-steps.
|
|
379
|
+
* @param {object} e
|
|
380
|
+
* @param {string} name
|
|
368
381
|
*/
|
|
369
|
-
function
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
function walkElements( e, name ) {
|
|
383
|
+
if (isBuiltinType(e.type)) {
|
|
384
|
+
branches[subbranchNames.concat(name).join(pathDelimiter)] = { steps: subbranchElements.concat(e), ref: subbranchNames.concat(name) };
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const subelements = e.elements || effectiveType(e).elements;
|
|
388
|
+
if (subelements) {
|
|
389
|
+
subbranchElements.push(e);
|
|
390
|
+
subbranchNames.push(name);
|
|
391
|
+
for (const [ subelementName, subelement ] of Object.entries(subelements))
|
|
392
|
+
walkElements(subelement, subelementName);
|
|
393
|
+
|
|
394
|
+
subbranchNames.pop();
|
|
395
|
+
subbranchElements.pop();
|
|
383
396
|
}
|
|
384
397
|
else {
|
|
385
|
-
|
|
386
|
-
const subelements = e.elements || eType.elements;
|
|
387
|
-
if (subelements) {
|
|
388
|
-
subbranchElements.push(e);
|
|
389
|
-
subbranchNames.push(name);
|
|
390
|
-
for (const [ subelementName, subelement ] of Object.entries(subelements))
|
|
391
|
-
walkElements(subelement, subelementName);
|
|
392
|
-
|
|
393
|
-
subbranchNames.pop();
|
|
394
|
-
subbranchElements.pop();
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
branches[subbranchNames.concat(name).join(pathDelimiter)] = subbranchElements.concat(e);
|
|
398
|
-
}
|
|
398
|
+
branches[subbranchNames.concat(name).join(pathDelimiter)] = { steps: subbranchElements.concat(e), ref: subbranchNames.concat(name) };
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
|
-
return branches;
|
|
402
401
|
}
|
|
402
|
+
return branches;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
/**
|
|
@@ -792,4 +792,5 @@ module.exports = {
|
|
|
792
792
|
flattenElements,
|
|
793
793
|
removeLeadingSelf,
|
|
794
794
|
handleManagedAssociationsAndCreateForeignKeys,
|
|
795
|
+
getBranches,
|
|
795
796
|
};
|