@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/model/csnUtils.js
CHANGED
|
@@ -63,7 +63,6 @@ function getUtils( model, universalReady ) {
|
|
|
63
63
|
addStringAnnotationTo,
|
|
64
64
|
getServiceName,
|
|
65
65
|
hasAnnotationValue,
|
|
66
|
-
cloneWithTransformations,
|
|
67
66
|
getFinalBaseTypeWithProps,
|
|
68
67
|
get$combined,
|
|
69
68
|
getQueryPrimarySource,
|
|
@@ -81,7 +80,7 @@ function getUtils( model, universalReady ) {
|
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
/**
|
|
84
|
-
* Get the union of all elements from the from clause
|
|
83
|
+
* Get the union of all elements from the "from" clause
|
|
85
84
|
* - descend into unions, following the lead query
|
|
86
85
|
* - merge all queries in case of joins
|
|
87
86
|
* - follow subqueries
|
|
@@ -93,7 +92,7 @@ function getUtils( model, universalReady ) {
|
|
|
93
92
|
function getSources( query, isSubquery = false ) {
|
|
94
93
|
// Remark CW: better just a while along query.SET.args[0]
|
|
95
94
|
if (query.SET) {
|
|
96
|
-
if (query.SET.args[0].SELECT
|
|
95
|
+
if (query.SET.args[0].SELECT?.elements)
|
|
97
96
|
return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
|
|
98
97
|
|
|
99
98
|
return getSources(query.SET.args[0], isSubquery);
|
|
@@ -111,9 +110,12 @@ function getUtils( model, universalReady ) {
|
|
|
111
110
|
if (isSubquery && !query.SELECT.elements)
|
|
112
111
|
throw new ModelError('Expected subquery to have .elements');
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
const elements = isSubquery ? query.SELECT.elements : art.elements;
|
|
114
|
+
// sub-queries also have an alias that is reachable by outer queries, in contrast to `from.as`.
|
|
115
|
+
const parent = query.as || query.SELECT.from.as || implicitAs(query.SELECT.from.ref);
|
|
116
|
+
// for better error messages, we refer to the actual reference name first
|
|
117
|
+
const errorParent = implicitAs(query.SELECT.from.ref);
|
|
118
|
+
return mergeElementsIntoMap(Object.create(null), elements, art.$location, parent, errorParent);
|
|
117
119
|
}
|
|
118
120
|
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
119
121
|
return getSources(query.SELECT.from, true);
|
|
@@ -131,7 +133,7 @@ function getUtils( model, universalReady ) {
|
|
|
131
133
|
}
|
|
132
134
|
else if (arg.ref) {
|
|
133
135
|
const art = artifactRef(arg);
|
|
134
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref
|
|
136
|
+
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
|
|
135
137
|
}
|
|
136
138
|
else if (arg.SELECT || arg.SET) {
|
|
137
139
|
elements = mergeElementMaps(elements, getSources(arg));
|
|
@@ -176,27 +178,13 @@ function getUtils( model, universalReady ) {
|
|
|
176
178
|
existingMap[elementName] = [];
|
|
177
179
|
|
|
178
180
|
existingMap[elementName].push({
|
|
179
|
-
element, name: elementName, source: $location, parent
|
|
181
|
+
element, name: elementName, source: $location, parent, errorParent,
|
|
180
182
|
});
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
return existingMap;
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
/**
|
|
187
|
-
* Return the name part of the artifact name - no namespace etc.
|
|
188
|
-
* @param {string|object} name Absolute name of the artifact
|
|
189
|
-
*/
|
|
190
|
-
function getBaseName( name ) {
|
|
191
|
-
if (!name)
|
|
192
|
-
return name;
|
|
193
|
-
|
|
194
|
-
if (name.id)
|
|
195
|
-
return name.id.substring( name.id.lastIndexOf('.') + 1 );
|
|
196
|
-
|
|
197
|
-
return name.substring( name.lastIndexOf('.') + 1 );
|
|
198
|
-
}
|
|
199
|
-
|
|
200
188
|
/**
|
|
201
189
|
* Return the left-most, primary source of the given query.
|
|
202
190
|
* @param {*} query Definition's query object
|
|
@@ -341,57 +329,6 @@ function getUtils( model, universalReady ) {
|
|
|
341
329
|
}
|
|
342
330
|
}
|
|
343
331
|
|
|
344
|
-
/**
|
|
345
|
-
* Clone 'node', transforming nodes therein recursively. Object 'transformers' is expected
|
|
346
|
-
* to contain a mapping of property 'key' names to transformer functions. The node's properties
|
|
347
|
-
* are walked recursively, calling each transformer function on its corresponding property
|
|
348
|
-
* 'key' of 'node', replacing 'value' in 'resultNode' with the function's return value
|
|
349
|
-
* (returning 'undefined' will delete the property).
|
|
350
|
-
* If no transformation function is found for 'key', the first letter of 'key' is tried
|
|
351
|
-
* instead (this seems to be intended for handling annotations that start with '@' ?)
|
|
352
|
-
*
|
|
353
|
-
* Regardless of their names, transformers are never applied to dictionary elements.
|
|
354
|
-
*
|
|
355
|
-
* The transformer functions are called with the following signature:
|
|
356
|
-
* transformer(value, node, resultNode, key)
|
|
357
|
-
*
|
|
358
|
-
* @param {any} rootNode Node to transform
|
|
359
|
-
* @param {any} transformers Object defining transformer functions
|
|
360
|
-
* @returns {object}
|
|
361
|
-
*/
|
|
362
|
-
function cloneWithTransformations( rootNode, transformers ) {
|
|
363
|
-
return transformNode(rootNode);
|
|
364
|
-
|
|
365
|
-
// This general transformation function will be applied to each node recursively
|
|
366
|
-
function transformNode( node ) {
|
|
367
|
-
// Return primitive values and null unchanged, but let objects and dictionaries through
|
|
368
|
-
// (Note that 'node instanceof Object' would be false for dictionaries).
|
|
369
|
-
if (node === null || typeof node !== 'object')
|
|
370
|
-
return node;
|
|
371
|
-
|
|
372
|
-
// Simply return if node is to be ignored
|
|
373
|
-
if (node === undefined || node._ignore)
|
|
374
|
-
return undefined;
|
|
375
|
-
// Transform arrays element-wise
|
|
376
|
-
if (Array.isArray(node))
|
|
377
|
-
return node.map(transformNode);
|
|
378
|
-
|
|
379
|
-
// Things not having 'proto' are dictionaries
|
|
380
|
-
const proto = Object.getPrototypeOf(node);
|
|
381
|
-
// Iterate own properties of 'node' and transform them into 'resultNode'
|
|
382
|
-
const resultNode = Object.create(proto);
|
|
383
|
-
for (const key of Object.keys(node)) {
|
|
384
|
-
// Dictionary always use transformNode(), other objects their transformer according to key
|
|
385
|
-
const transformer = (proto === null || proto === undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
|
|
386
|
-
// Apply transformer, or use transformNode() if there is none
|
|
387
|
-
const resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
|
|
388
|
-
if (resultValue !== undefined)
|
|
389
|
-
resultNode[key] = resultValue;
|
|
390
|
-
}
|
|
391
|
-
return resultNode;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
332
|
/**
|
|
396
333
|
* Resolve to the final type of a type, that means follow type chains, references, etc.
|
|
397
334
|
* Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
@@ -423,6 +360,7 @@ function getUtils( model, universalReady ) {
|
|
|
423
360
|
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\', '\\'] })
|
|
424
361
|
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\2:\\\\'] })
|
|
425
362
|
const resolvedKey = (typeof type === 'object')
|
|
363
|
+
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
|
426
364
|
? `ref[${ type.ref.length }]:${ type.ref.map((val, i) => `${ i }:${ val }`).join('\\') }`
|
|
427
365
|
: `type:${ type }`;
|
|
428
366
|
|
|
@@ -930,7 +868,7 @@ function getUnderscoredName( startIndex, parts, csn ) {
|
|
|
930
868
|
for (let i = startIndex; i < parts.length; i++) {
|
|
931
869
|
const namePart = parts.slice(0, i).join('.');
|
|
932
870
|
const art = csn.definitions[namePart];
|
|
933
|
-
if (art && !(art.kind === '
|
|
871
|
+
if (art && !(art.kind === 'context' || art.kind === 'service')) {
|
|
934
872
|
const prefix = parts.slice(0, i - 1).join('.');
|
|
935
873
|
const suffix = parts.slice(i - 1).join('_');
|
|
936
874
|
const result = [];
|
|
@@ -61,6 +61,7 @@ function tableAliasAsLink( art, parent, name ) {
|
|
|
61
61
|
* @param {string} [nameOrPath]
|
|
62
62
|
*/
|
|
63
63
|
function revealInternalProperties( model, nameOrPath ) {
|
|
64
|
+
// return model;
|
|
64
65
|
const transformers = {
|
|
65
66
|
messages: m => m,
|
|
66
67
|
name: shortenName,
|
|
@@ -71,6 +72,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
71
72
|
artifacts: artifactDictionary,
|
|
72
73
|
definitions: artifactDictionary,
|
|
73
74
|
vocabularies: dictionary,
|
|
75
|
+
$lateExtensions: dictionary,
|
|
74
76
|
elements,
|
|
75
77
|
columns,
|
|
76
78
|
expand: columns,
|
|
@@ -95,6 +97,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
95
97
|
$compositionTargets: d => d, // dictionary( boolean )
|
|
96
98
|
_extend: reveal,
|
|
97
99
|
_annotate: reveal,
|
|
100
|
+
_annotateS: artifactIdentifier,
|
|
98
101
|
_deps: dependencyInfo,
|
|
99
102
|
_status: primOrString, // is a string anyway
|
|
100
103
|
$annotations: as => as.map( $annotation ),
|
|
@@ -170,8 +173,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
170
173
|
if (!Array.isArray(deps))
|
|
171
174
|
return primOrString( deps );
|
|
172
175
|
return deps
|
|
173
|
-
.
|
|
174
|
-
.map( d => artifactIdentifier( d.art ) );
|
|
176
|
+
.map( d => `${ d.location ? '' : '-' }${ artifactIdentifier( d.art ) }`);
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
function layerExtends( dict ) {
|
|
@@ -120,6 +120,7 @@ function getArtifactComparator(otherModel, options, addedEntities, deletedEntiti
|
|
|
120
120
|
// Arguments are interchanged in this case: `artifact` from beforeModel and `otherArtifact` from afterModel.
|
|
121
121
|
if (isPersisted && !isPersistedOther) {
|
|
122
122
|
deletedEntities[name] = artifact;
|
|
123
|
+
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
|
123
124
|
} else if(isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
|
|
124
125
|
deletedEntities[name] = artifact;
|
|
125
126
|
}
|
|
@@ -254,7 +255,7 @@ module.exports = {
|
|
|
254
255
|
/**
|
|
255
256
|
* A ModelDiff encapsulates the changes between two models ("before" and "after"). It contains information
|
|
256
257
|
* about changes to .elements and removed artifacts.
|
|
257
|
-
*
|
|
258
|
+
*
|
|
258
259
|
* @typedef {object} ModelDiff
|
|
259
260
|
* @property {CSN.Definitions} definitions The artifacts present in the "after" model
|
|
260
261
|
* @property {CSN.Definitions} deletions The artifacts present in the "before", but not in the "after"
|
package/lib/optionProcessor.js
CHANGED
|
@@ -226,6 +226,7 @@ optionProcessor.command('O, toOdata')
|
|
|
226
226
|
.option(' --odata-x-service-refs')
|
|
227
227
|
.option(' --odata-foreign-keys')
|
|
228
228
|
.option(' --odata-v2-partial-constr')
|
|
229
|
+
.option(' --odata-voc-refs <list>')
|
|
229
230
|
.option('-c, --csn')
|
|
230
231
|
.option('-f, --odata-format <format>', ['flat', 'structured'])
|
|
231
232
|
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
|
|
@@ -254,6 +255,8 @@ optionProcessor.command('O, toOdata')
|
|
|
254
255
|
--odata-foreign-keys Render foreign keys in structured format (V4 only)
|
|
255
256
|
--odata-v2-partial-constr Render referential constraints also for partial principal key tuple
|
|
256
257
|
(Not spec compliant and V2 only)
|
|
258
|
+
--odata-voc-refs <list> JSON array of adhoc vocabulary definitions
|
|
259
|
+
[ { alias, ns, uri }, ... ]
|
|
257
260
|
-n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
|
|
258
261
|
the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
|
|
259
262
|
plain : (default) Names in uppercase and flattened with underscores
|
|
@@ -23,12 +23,15 @@ const { sortCsn } = require('../json/to-csn');
|
|
|
23
23
|
* @param {CSN.Options} options
|
|
24
24
|
*/
|
|
25
25
|
function alterConstraintsWithCsn( csn, options ) {
|
|
26
|
-
const { error } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
26
|
+
const { error, warning } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
27
27
|
|
|
28
28
|
const {
|
|
29
|
-
drop, alter, src, violations,
|
|
29
|
+
drop, alter, src, violations, sqlDialect,
|
|
30
30
|
} = options || {};
|
|
31
31
|
|
|
32
|
+
if (!sqlDialect || sqlDialect === 'h2' || sqlDialect === 'plain')
|
|
33
|
+
warning(null, null, { prop: sqlDialect || 'plain' }, 'Referential Constraints are not available for sql dialect $(PROP)');
|
|
34
|
+
|
|
32
35
|
if (drop && alter)
|
|
33
36
|
error(null, null, 'Option “--drop” can\'t be combined with “--alter”');
|
|
34
37
|
|