@sap/cds-compiler 5.8.0 → 5.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 +37 -0
- package/bin/cds_remove_invalid_whitespace.js +5 -3
- package/bin/cds_update_identifiers.js +9 -6
- package/bin/cdsc.js +79 -59
- package/bin/cdsse.js +14 -10
- package/bin/cdsv2m.js +3 -1
- package/lib/api/options.js +28 -6
- package/lib/base/message-registry.js +15 -4
- package/lib/checks/validator.js +3 -0
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/checks.js +70 -50
- package/lib/compiler/extend.js +1 -1
- package/lib/compiler/generate.js +8 -2
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/lsp-api.js +1 -1
- package/lib/compiler/propagator.js +2 -2
- package/lib/compiler/resolve.js +78 -31
- package/lib/compiler/shared.js +3 -3
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +10 -0
- package/lib/compiler/xpr-rewrite.js +1 -1
- package/lib/edm/annotations/edmJson.js +42 -39
- package/lib/edm/annotations/genericTranslation.js +55 -55
- package/lib/edm/annotations/preprocessAnnotations.js +5 -5
- package/lib/edm/csn2edm.js +21 -16
- package/lib/edm/edm.js +62 -62
- package/lib/edm/edmAnnoPreprocessor.js +2 -2
- package/lib/edm/edmInboundChecks.js +1 -1
- package/lib/edm/edmPreprocessor.js +32 -32
- package/lib/edm/edmUtils.js +8 -8
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +77 -81
- package/lib/gen/Dictionary.json +3062 -3072
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1238 -1236
- package/lib/json/from-csn.js +1 -1
- package/lib/json/to-csn.js +30 -3
- package/lib/language/genericAntlrParser.js +16 -0
- package/lib/main.d.ts +79 -1
- package/lib/model/csnRefs.js +12 -5
- package/lib/model/xprAsTree.js +71 -0
- package/lib/modelCompare/utils/filter.js +1 -1
- package/lib/optionProcessor.js +46 -32
- package/lib/parsers/CdlGrammar.g4 +33 -28
- package/lib/parsers/Lexer.js +1 -1
- package/lib/parsers/XprTree.js +25 -16
- package/lib/render/toCdl.js +902 -414
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +8 -0
- package/lib/render/utils/common.js +2 -2
- package/lib/render/utils/operators.js +160 -0
- package/lib/render/utils/pretty.js +337 -0
- package/lib/sql-identifier.js +7 -9
- package/lib/transform/addTenantFields.js +39 -41
- package/lib/transform/db/applyTransformations.js +4 -4
- package/lib/transform/db/assertUnique.js +6 -5
- package/lib/transform/db/associations.js +3 -3
- package/lib/transform/db/assocsToQueries/transformExists.js +13 -13
- package/lib/transform/db/assocsToQueries/utils.js +8 -0
- package/lib/transform/db/backlinks.js +19 -14
- package/lib/transform/db/constraints.js +6 -6
- package/lib/transform/db/expansion.js +1 -1
- package/lib/transform/db/flattening.js +2 -2
- package/lib/transform/db/groupByOrderBy.js +1 -1
- package/lib/transform/db/processSqlServices.js +3 -3
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/temporal.js +7 -9
- package/lib/transform/db/views.js +6 -6
- package/lib/transform/draft/odata.js +2 -0
- package/lib/transform/effective/annotations.js +1 -1
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/main.js +1 -0
- package/lib/transform/effective/service.js +2 -2
- package/lib/transform/forRelationalDB.js +11 -5
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/adaptAnnotationRefs.js +10 -9
- package/lib/transform/odata/createForeignKeys.js +1 -1
- package/lib/transform/odata/flattening.js +2 -1
- package/lib/transform/parseExpr.js +2 -2
- package/lib/transform/transformUtils.js +9 -7
- package/lib/transform/translateAssocsToJoins.js +0 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -2
- package/lib/utils/moduleResolve.js +7 -5
- package/package.json +1 -1
- package/share/messages/def-upcoming-virtual-change.md +55 -0
- package/share/messages/file-unexpected-case-mismatch.md +61 -0
- package/share/messages/message-explanations.json +2 -0
- package/lib/transform/braceExpression.js +0 -77
|
@@ -57,6 +57,8 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
57
57
|
initDefinition,
|
|
58
58
|
artifactRef,
|
|
59
59
|
effectiveType,
|
|
60
|
+
queryForElements,
|
|
61
|
+
$getQueries,
|
|
60
62
|
msgLocations,
|
|
61
63
|
} = csnRefs( csn, true );
|
|
62
64
|
|
|
@@ -100,7 +102,10 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
100
102
|
// the cache of csnRefs):
|
|
101
103
|
for (const name in definitions) {
|
|
102
104
|
const art = definitions[name];
|
|
103
|
-
addTenantFieldToArt(art, options)
|
|
105
|
+
if (addTenantFieldToArt( art, options ) && (art.query || art.projection)) {
|
|
106
|
+
for (const qcache of $getQueries( art ).slice( 1 ))
|
|
107
|
+
addTenantFieldToArt( qcache._select, options, true );
|
|
108
|
+
}
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
(csn.extensions || []).forEach( ( ext, idx ) => {
|
|
@@ -152,25 +157,36 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
152
157
|
|
|
153
158
|
// Queries --------------------------------------------------------------------
|
|
154
159
|
|
|
155
|
-
function handleQuery( query ) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
function handleQuery( query, fromSelect, parentQuery ) {
|
|
161
|
+
const select = query.SELECT || query.projection;
|
|
162
|
+
if (select)
|
|
163
|
+
csnPath[0] = query;
|
|
164
|
+
if (!projection || query.ref && handleQuerySource( query, fromSelect ))
|
|
159
165
|
return;
|
|
160
166
|
|
|
161
|
-
if (query !== projection && !independent
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
167
|
+
if (query !== projection && !independent &&
|
|
168
|
+
(fromSelect && !fromSelect.from.ref || !parentQuery?.SET)) {
|
|
169
|
+
// If a sub query would be supported in ORDER BY or LIMIT, the test above
|
|
170
|
+
// would not be enough
|
|
171
|
+
error( 'tenant-unsupported-query', msgLocations( csnPath ),
|
|
172
|
+
{ '#': (fromSelect?.from?.join ? 'join' : 'subquery') },
|
|
173
|
+
{
|
|
174
|
+
std: 'Can\'t have tenant-dependent non-simple query entities',
|
|
175
|
+
join: 'Can\'t use a join in a tenant-dependent entity',
|
|
176
|
+
subquery: 'Can\'t use a sub query in a tenant-dependent entity',
|
|
177
|
+
} );
|
|
178
|
+
projection = null; // no further error
|
|
165
179
|
return;
|
|
166
180
|
}
|
|
167
181
|
|
|
168
|
-
if (!
|
|
182
|
+
if (!select)
|
|
169
183
|
return; // query.SET or query.join
|
|
170
|
-
csnPath[0] = query;
|
|
171
184
|
csnPath.push( query.SELECT ? 'SELECT' : 'projection' );
|
|
172
185
|
|
|
173
|
-
const
|
|
186
|
+
const qcache = queryForElements( query );
|
|
187
|
+
if (qcache.$queryNumber > 1)
|
|
188
|
+
handleElements( qcache );
|
|
189
|
+
|
|
174
190
|
if (select.mixin)
|
|
175
191
|
checkMixins( select.mixin );
|
|
176
192
|
if (!independent) {
|
|
@@ -178,19 +194,12 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
178
194
|
checkExcluding( select.excluding );
|
|
179
195
|
if (select.columns)
|
|
180
196
|
handleColumns( select.columns );
|
|
181
|
-
// TODO: when we allow subqueries, we must also check for published in redirected assocs
|
|
182
|
-
// TODO: for subqueries, we might need to adapt the inferred elements
|
|
183
|
-
// TODO: where exists ref -
|
|
184
|
-
// TODO: select and query clauses, especially with aggregation functions
|
|
185
197
|
handleGroupBy( select );
|
|
186
198
|
}
|
|
187
|
-
else if (query !== projection && select.columns) {
|
|
188
|
-
checkColumnCasts( select.columns );
|
|
189
|
-
}
|
|
190
199
|
csnPath.length = 1;
|
|
191
200
|
}
|
|
192
201
|
|
|
193
|
-
function handleQuerySource( query ) {
|
|
202
|
+
function handleQuerySource( query, fromSelect ) {
|
|
194
203
|
if (independent) {
|
|
195
204
|
const art = pathId(query.ref[0]); // yes, the base
|
|
196
205
|
if (csn.definitions[art][annoTenantIndep])
|
|
@@ -202,8 +211,8 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
202
211
|
} );
|
|
203
212
|
return true;
|
|
204
213
|
}
|
|
205
|
-
if (
|
|
206
|
-
return false;
|
|
214
|
+
if (fromSelect && fromSelect.from !== query) // with JOIN
|
|
215
|
+
return false; // issue better error later
|
|
207
216
|
if ((query.as || implicitAs( query.ref )) === tenantName) {
|
|
208
217
|
error( 'tenant-invalid-alias-name', msgLocations( csnPath ),
|
|
209
218
|
{ name: tenantName, '#': (query.as ? 'std' : 'implicit') } );
|
|
@@ -265,20 +274,6 @@ function addTenantFields( csn, options, messageFunctions ) {
|
|
|
265
274
|
: { ref: [ tenantName ] } );
|
|
266
275
|
}
|
|
267
276
|
|
|
268
|
-
function checkColumnCasts( columns, prop = 'columns' ) {
|
|
269
|
-
csnPath.push( prop, -1 );
|
|
270
|
-
for (const col of columns) {
|
|
271
|
-
++csnPath[csnPath.length - 1];
|
|
272
|
-
if (col.cast?.target)
|
|
273
|
-
handleAssociations( col.cast, null );
|
|
274
|
-
else if (col.expand)
|
|
275
|
-
checkColumnCasts( col.expand, 'expand' );
|
|
276
|
-
else if (col.inline)
|
|
277
|
-
checkColumnCasts( col.inline, 'inline' );
|
|
278
|
-
}
|
|
279
|
-
csnPath.length -= 2;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
277
|
// Associations ---------------------------------------------------------------
|
|
283
278
|
|
|
284
279
|
function handleAssociations( elem, afterRecursion ) {
|
|
@@ -419,12 +414,15 @@ function isTenantDepEntity( art ) {
|
|
|
419
414
|
return art?.kind === 'entity' && !art[annoTenantIndep];
|
|
420
415
|
}
|
|
421
416
|
|
|
422
|
-
function addTenantFieldToArt( art, options ) {
|
|
417
|
+
function addTenantFieldToArt( art, options, isQuery = false ) {
|
|
418
|
+
if (!isQuery && !isTenantDepEntity( art ))
|
|
419
|
+
return false;
|
|
423
420
|
const tenantName = options.tenantDiscriminator === true ? 'tenant' : options.tenantDiscriminator;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
421
|
+
const elements = Object.getOwnPropertyDescriptor( art, 'elements' );
|
|
422
|
+
// `query.elements` is usually non-enumerable
|
|
423
|
+
elements.value = { [tenantName]: { ...tenantDef }, ...elements.value };
|
|
424
|
+
Object.defineProperty( art, 'elements', elements );
|
|
425
|
+
return true;
|
|
428
426
|
}
|
|
429
427
|
|
|
430
428
|
module.exports = {
|
|
@@ -131,14 +131,14 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
131
131
|
if (options.skipDict?.[_prop] || dict == null) // with universal CSN, dictionaries might be null
|
|
132
132
|
return;
|
|
133
133
|
csnPath.push( _prop );
|
|
134
|
-
context[`$in_${_prop}`] = true;
|
|
134
|
+
context[`$in_${ _prop }`] = true;
|
|
135
135
|
for (const name of Object.getOwnPropertyNames( dict ))
|
|
136
136
|
dictEntry( dict, name, dict[name] );
|
|
137
137
|
|
|
138
138
|
if (!Object.prototype.propertyIsEnumerable.call( node, _prop ))
|
|
139
|
-
setProp(node, `$${_prop}`, dict);
|
|
139
|
+
setProp(node, `$${ _prop }`, dict);
|
|
140
140
|
csnPath.pop();
|
|
141
|
-
context[`$in_${_prop}`] = undefined;
|
|
141
|
+
context[`$in_${ _prop }`] = undefined;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
@@ -188,7 +188,7 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
if (!Object.prototype.propertyIsEnumerable.call( node, _prop ))
|
|
191
|
-
setProp(node, `$${_prop}`, dict);
|
|
191
|
+
setProp(node, `$${ _prop }`, dict);
|
|
192
192
|
csnPath.pop();
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -322,16 +322,17 @@ function rewriteUniqueConstraints( csn, options, pathDelimiter ) {
|
|
|
322
322
|
|
|
323
323
|
// now add the index for HANA CDS
|
|
324
324
|
if (options.transformation === 'hdbcds') {
|
|
325
|
-
const
|
|
325
|
+
const cond = [];
|
|
326
326
|
let i = 0;
|
|
327
327
|
for (const constraint of rewrittenPaths) {
|
|
328
328
|
if (i > 0)
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
cond.push(',');
|
|
330
|
+
cond.push(constraint);
|
|
331
331
|
i++;
|
|
332
332
|
}
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
artifact.technicalConfig.hana.indexes[uniqueConstraint] = [
|
|
334
|
+
'unique', 'index', { ref: [ uniqueConstraint ] }, 'on', { xpr: cond },
|
|
335
|
+
];
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
338
|
artifact.$tableConstraints.unique = uniqueConstraints;
|
|
@@ -62,7 +62,7 @@ function attachOnConditions( csn, csnUtils, pathDelimiter, iterateOptions = {},
|
|
|
62
62
|
...foreignKey.ref,
|
|
63
63
|
],
|
|
64
64
|
};
|
|
65
|
-
const fkName = `${elemName}${pathDelimiter}${foreignKey.as || implicitAs(foreignKey.ref)}`;
|
|
65
|
+
const fkName = `${ elemName }${ pathDelimiter }${ foreignKey.as || implicitAs(foreignKey.ref) }`;
|
|
66
66
|
const fKeyArg = {
|
|
67
67
|
ref: [
|
|
68
68
|
...fkName.startsWith('$') ? [ '$self' ] : [],
|
|
@@ -189,14 +189,14 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter, processOnInQueries
|
|
|
189
189
|
// Doesn't work when ref-target (filter condition) or similar is used
|
|
190
190
|
!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
|
|
191
191
|
const fkRef = ref[i + 1];
|
|
192
|
-
const fkName = (!fkAlias ? fkRef : `${fkRef}${pathDelimiter}${fkAlias}`);
|
|
192
|
+
const fkName = (!fkAlias ? fkRef : `${ fkRef }${ pathDelimiter }${ fkAlias }`);
|
|
193
193
|
const fks = link.art.keys.filter(key => key.ref[0] === fkName);
|
|
194
194
|
|
|
195
195
|
if (fks.length >= 1) { // after flattening, at most one FK will remain.
|
|
196
196
|
// `.as` is set for SQL, but not for OData -> fall back to implicit alias
|
|
197
197
|
fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
|
|
198
198
|
const managedAssocStepName = ref[i];
|
|
199
|
-
const newFkName = `${managedAssocStepName}${pathDelimiter}${fkAlias}`;
|
|
199
|
+
const newFkName = `${ managedAssocStepName }${ pathDelimiter }${ fkAlias }`;
|
|
200
200
|
if (isColumns) {
|
|
201
201
|
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
|
|
202
202
|
if (!refOwner.as)
|
|
@@ -283,23 +283,23 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
// TODO: parentheses around sub expressions are to be represented by inner `xpr`
|
|
287
|
-
if (extension.length > 3)
|
|
288
|
-
subselect.SELECT.where.push('('); // add braces around the on-condition part to ensure precedence is kept
|
|
289
|
-
|
|
290
286
|
// TODO: add tenant comparison here ?
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
if (extension.length > 3) {
|
|
288
|
+
// make on-condition part sub-xpr to ensure precedence is kept
|
|
289
|
+
subselect.SELECT.where.push({ xpr: extension });
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
subselect.SELECT.where.push(...extension);
|
|
293
|
+
}
|
|
295
294
|
|
|
296
295
|
newExpr.push('exists');
|
|
297
|
-
if (ref
|
|
296
|
+
if (ref?.where) {
|
|
298
297
|
const remappedWhere = remapExistingWhere(target, ref.where, exprPath, current);
|
|
298
|
+
subselect.SELECT.where.push('and');
|
|
299
299
|
if (remappedWhere.length > 3)
|
|
300
|
-
subselect.SELECT.where.push(
|
|
300
|
+
subselect.SELECT.where.push( { xpr: remappedWhere } );
|
|
301
301
|
else
|
|
302
|
-
subselect.SELECT.where.push(
|
|
302
|
+
subselect.SELECT.where.push( ...remappedWhere );
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
newExpr.push(subselect);
|
|
@@ -332,10 +332,10 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
|
|
|
332
332
|
* @returns {CSN.Query}
|
|
333
333
|
*/
|
|
334
334
|
function getSubselect( target, assocRef, _sources ) {
|
|
335
|
-
let subselectAlias = `_${assocRef.id ? assocRef.id : assocRef}_exists`;
|
|
335
|
+
let subselectAlias = `_${ assocRef.id ? assocRef.id : assocRef }_exists`;
|
|
336
336
|
|
|
337
337
|
while (_sources[subselectAlias])
|
|
338
|
-
subselectAlias = `_${subselectAlias}`;
|
|
338
|
+
subselectAlias = `_${ subselectAlias }`;
|
|
339
339
|
|
|
340
340
|
const subselect = {
|
|
341
341
|
SELECT: {
|
|
@@ -445,3 +445,11 @@ function getHelpers( csn, inspectRef, error ) {
|
|
|
445
445
|
|
|
446
446
|
|
|
447
447
|
module.exports = { getHelpers };
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @typedef {Token[]} TokenStream Array of tokens.
|
|
451
|
+
*/
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @typedef {string|object} Token Could be an object or a string - strings are usually operators.
|
|
455
|
+
*/
|
|
@@ -69,7 +69,8 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
69
69
|
*/
|
|
70
70
|
function processBacklinkAssoc( elem, elemName, art, artName, pathToOn ) {
|
|
71
71
|
// Don't add braces if it is a single expression (ignoring superfluous braces)
|
|
72
|
-
|
|
72
|
+
// TODO: This check is too simplistic and probably adds superfluous parentheses.
|
|
73
|
+
const multipleExprs = elem.on.length > 3;
|
|
73
74
|
elem.on = processExpressionArgs(elem.on, pathToOn);
|
|
74
75
|
const column = csnUtils.getColumn(elem);
|
|
75
76
|
if (column?.cast?.on) // avoid difference between column and element
|
|
@@ -92,26 +93,30 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
92
93
|
// (if so, replace all three tokens with the condition generated from the other side, in parentheses)
|
|
93
94
|
if (isDollarSelfOrProjectionOperand(xprArgs[i]) && isAssociationOperand(xprArgs[i + 2], path.concat([ i + 2 ]), csnUtils.inspectRef)) {
|
|
94
95
|
const assoc = csnUtils.inspectRef(path.concat([ i + 2 ])).art;
|
|
95
|
-
if (multipleExprs)
|
|
96
|
-
result.push('(');
|
|
97
96
|
const backlinkName = xprArgs[i + 2].ref[xprArgs[i + 2].ref.length - 1];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
const comparison = transformDollarSelfComparison(
|
|
98
|
+
xprArgs[i + 2], assoc, backlinkName,
|
|
99
|
+
elem, elemName, art, artName, path.concat([ i ])
|
|
100
|
+
);
|
|
101
|
+
|
|
102
102
|
if (multipleExprs)
|
|
103
|
-
result.push(
|
|
103
|
+
result.push({ xpr: comparison });
|
|
104
|
+
else
|
|
105
|
+
result.push(...comparison);
|
|
106
|
+
|
|
104
107
|
i += 3;
|
|
105
108
|
attachBacklinkInformation(backlinkName);
|
|
106
109
|
}
|
|
107
110
|
else if (isDollarSelfOrProjectionOperand(xprArgs[i + 2]) && isAssociationOperand(xprArgs[i], path.concat([ i ]), csnUtils.inspectRef)) {
|
|
108
111
|
const assoc = csnUtils.inspectRef(path.concat([ i ])).art;
|
|
109
|
-
if (multipleExprs)
|
|
110
|
-
result.push('(');
|
|
111
112
|
const backlinkName = xprArgs[i].ref[xprArgs[i].ref.length - 1];
|
|
112
|
-
|
|
113
|
+
const comparison = transformDollarSelfComparison(xprArgs[i], assoc, backlinkName, elem, elemName, art, artName, path.concat([ i + 2 ]));
|
|
114
|
+
|
|
113
115
|
if (multipleExprs)
|
|
114
|
-
result.push(
|
|
116
|
+
result.push({ xpr: comparison });
|
|
117
|
+
else
|
|
118
|
+
result.push(...comparison);
|
|
119
|
+
|
|
115
120
|
i += 3;
|
|
116
121
|
attachBacklinkInformation(backlinkName);
|
|
117
122
|
}
|
|
@@ -205,7 +210,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
205
210
|
return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName, art, path);
|
|
206
211
|
}
|
|
207
212
|
|
|
208
|
-
throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${JSON.stringify(elem.on)}`);
|
|
213
|
+
throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
|
|
@@ -235,7 +240,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
235
240
|
// Depending on naming conventions, the foreign key may two path steps (hdbcds) or be a single path step with a flattened name (plain, quoted)
|
|
236
241
|
// With to.hdbcds in conjunction with hdbcds naming, we need to NOT use the alias - else we get deployment errors
|
|
237
242
|
const keyName = k.as && doA2J ? [ k.as ] : k.ref;
|
|
238
|
-
const fKeyPath = !doA2J ? [ assocName, ...keyName ] : [ `${assocName}${pathDelimiter}${keyName[0]}` ];
|
|
243
|
+
const fKeyPath = !doA2J ? [ assocName, ...keyName ] : [ `${ assocName }${ pathDelimiter }${ keyName[0] }` ];
|
|
239
244
|
// FIXME: _artifact to the args ???
|
|
240
245
|
const a = [
|
|
241
246
|
{
|
|
@@ -57,7 +57,7 @@ function createReferentialConstraints( csn, options ) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
// for `texts` compositions, we may generate foreign key constraints even w/o `up_`
|
|
60
|
-
else if (elementName === 'texts' && element.target === `${path[path.length - 1]}.texts`) {
|
|
60
|
+
else if (elementName === 'texts' && element.target === `${ path[path.length - 1] }.texts`) {
|
|
61
61
|
const { on } = element;
|
|
62
62
|
const textsEntity = csn.definitions[element.target];
|
|
63
63
|
// `texts` entities have a key named "locale"
|
|
@@ -568,14 +568,14 @@ function createReferentialConstraints( csn, options ) {
|
|
|
568
568
|
// comments in sqlite files are causing the JDBC driver to throw an error on deployment
|
|
569
569
|
if (options.testMode && onDelete === 'CASCADE') {
|
|
570
570
|
if ($foreignKeyConstraint.upLinkFor?.texts)
|
|
571
|
-
onDeleteRemark = `Constraint originates from localized composition ”${$foreignKeyConstraint.parentTable}:texts“`;
|
|
571
|
+
onDeleteRemark = `Constraint originates from localized composition ”${ $foreignKeyConstraint.parentTable }:texts“`;
|
|
572
572
|
else
|
|
573
|
-
onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
|
|
573
|
+
onDeleteRemark = `Up_ link for Composition "${ $foreignKeyConstraint.upLinkFor }" implies existential dependency`;
|
|
574
574
|
}
|
|
575
575
|
// constraint identifier usually start with `c__` to avoid name clashes
|
|
576
576
|
let identifier = options.pre2134ReferentialConstraintNames ? '' : 'c__';
|
|
577
|
-
identifier += `${getResultingName(csn, options.sqlMapping, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`;
|
|
578
|
-
referentialConstraints[`${getResultingName(csn, 'quoted', artifactName)}_${$foreignKeyConstraint.sourceAssociation}`] = {
|
|
577
|
+
identifier += `${ getResultingName(csn, options.sqlMapping, artifactName) }_${ $foreignKeyConstraint.sourceAssociation }`;
|
|
578
|
+
referentialConstraints[`${ getResultingName(csn, 'quoted', artifactName) }_${ $foreignKeyConstraint.sourceAssociation }`] = {
|
|
579
579
|
identifier,
|
|
580
580
|
foreignKey: dependentKey,
|
|
581
581
|
parentKey,
|
|
@@ -605,7 +605,7 @@ function assertConstraintIdentifierUniqueness( artifact, artifactName, path, err
|
|
|
605
605
|
return;
|
|
606
606
|
|
|
607
607
|
forEachKey(artifact.$tableConstraints.unique, (uniqueConstraintKey) => {
|
|
608
|
-
const uniqueConstraintIdentifier = `${artifactName}_${uniqueConstraintKey}`; // final unique constraint identifier will be generated in renderer likewise
|
|
608
|
+
const uniqueConstraintIdentifier = `${ artifactName }_${ uniqueConstraintKey }`; // final unique constraint identifier will be generated in renderer likewise
|
|
609
609
|
if (artifact.$tableConstraints.referential[uniqueConstraintIdentifier]) {
|
|
610
610
|
error(null, path,
|
|
611
611
|
{ name: uniqueConstraintIdentifier, art: artifactName },
|
|
@@ -99,7 +99,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
99
99
|
if (rewritten.toMany.length > 0 && !options.toOdata) {
|
|
100
100
|
markAsToDummify(artifact, path[1]);
|
|
101
101
|
rewritten.toMany.forEach(({ art }) => {
|
|
102
|
-
error( null, art.$path || [ 'definitions', path[1] ], { name: `${art.$env || path[1]}:${art.ref.map(r => r.id || r)}` }, 'Unexpected .expand with to-many association $(NAME)');
|
|
102
|
+
error( null, art.$path || [ 'definitions', path[1] ], { name: `${ art.$env || path[1] }:${ art.ref.map(r => r.id || r) }` }, 'Unexpected .expand with to-many association $(NAME)');
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
else {
|
|
@@ -751,7 +751,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
751
751
|
// we have reached a leaf element, create a foreign key
|
|
752
752
|
else if (finalElement.type == null || isBuiltinType(finalElement.type)) {
|
|
753
753
|
const newFk = Object.create(null);
|
|
754
|
-
[ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${f}`) ].forEach((prop) => {
|
|
754
|
+
[ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${ f }`) ].forEach((prop) => {
|
|
755
755
|
// copy props from original element to preserve derived types!
|
|
756
756
|
if (element[prop] !== undefined)
|
|
757
757
|
newFk[prop] = element[prop];
|
|
@@ -761,7 +761,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
761
761
|
|
|
762
762
|
fks.forEach((fk) => {
|
|
763
763
|
// prepend current prefix
|
|
764
|
-
fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
|
|
764
|
+
fk[0] = `${ prefix }${ pathDelimiter }${ fk[0] }`;
|
|
765
765
|
// if this is the entry association, decorate the final foreign keys with the association props
|
|
766
766
|
if (lvl === 0) {
|
|
767
767
|
if (options.transformation !== 'effective')
|
|
@@ -91,7 +91,7 @@ function replaceAssociationsInGroupByOrderBy( inputQuery, options, inspectRef, e
|
|
|
91
91
|
function getForeignKeyRefs( assoc ) {
|
|
92
92
|
return assoc.keys.map((fk) => {
|
|
93
93
|
if (!fk.$generatedFieldName)
|
|
94
|
-
throw new ModelError(`Expecting generated field name for foreign key: ${JSON.stringify(fk)}`);
|
|
94
|
+
throw new ModelError(`Expecting generated field name for foreign key: ${ JSON.stringify(fk) }`);
|
|
95
95
|
|
|
96
96
|
return { ref: [ fk.$generatedFieldName ] };
|
|
97
97
|
});
|
|
@@ -101,10 +101,10 @@ function createServiceDummy(artifact, artifactName, csn, { error }) {
|
|
|
101
101
|
delete dummy['@cds.persistence.exists'];
|
|
102
102
|
delete dummy.$ignore;
|
|
103
103
|
|
|
104
|
-
if (csn.definitions[`dummy.${artifactName}`])
|
|
105
|
-
error(null, [ 'definitions', artifactName ], { name: `dummy.${artifactName}` }, 'Generated artifact name $(NAME) conflicts with existing entity');
|
|
104
|
+
if (csn.definitions[`dummy.${ artifactName }`])
|
|
105
|
+
error(null, [ 'definitions', artifactName ], { name: `dummy.${ artifactName }` }, 'Generated artifact name $(NAME) conflicts with existing entity');
|
|
106
106
|
else
|
|
107
|
-
csn.definitions[`dummy.${artifactName}`] = dummy;
|
|
107
|
+
csn.definitions[`dummy.${ artifactName }`] = dummy;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
module.exports = {
|
|
@@ -384,12 +384,12 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
386
|
else {
|
|
387
|
-
throw new CompilerAssertion(`Unhandled arg type: ${JSON.stringify(arg, null, 2)}`);
|
|
387
|
+
throw new CompilerAssertion(`Unhandled arg type: ${ JSON.stringify(arg, null, 2) }`);
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
return mergedElements;
|
|
391
391
|
}
|
|
392
|
-
throw new CompilerAssertion(`Unhandled query type: ${JSON.stringify(SELECT, null, 2)}`);
|
|
392
|
+
throw new CompilerAssertion(`Unhandled query type: ${ JSON.stringify(SELECT, null, 2) }`);
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
/**
|
|
@@ -73,19 +73,17 @@ function getViewDecorator( csn, messageFunctions, csnUtils, options ) {
|
|
|
73
73
|
const validFrom = { ref: [ '$valid', 'from' ] };
|
|
74
74
|
const validTo = { ref: [ '$valid', 'to' ] };
|
|
75
75
|
|
|
76
|
-
const cond = [
|
|
77
|
-
|
|
78
|
-
if (normalizedQuery.query.SELECT.where)
|
|
79
|
-
normalizedQuery.query.SELECT.where = [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
normalizedQuery.query.SELECT.where = cond;
|
|
83
|
-
}
|
|
76
|
+
const cond = { xpr: [ fromPath, '<', validTo, 'and', toPath, '>', validFrom ] };
|
|
77
|
+
|
|
78
|
+
if (normalizedQuery.query.SELECT.where) // if there is an existing where-clause, extend it by adding 'and (temporal clause)'
|
|
79
|
+
normalizedQuery.query.SELECT.where = [ { xpr: normalizedQuery.query.SELECT.where }, 'and', cond ];
|
|
80
|
+
else
|
|
81
|
+
normalizedQuery.query.SELECT.where = [ cond ];
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
else {
|
|
87
85
|
info(null, [ 'definitions', artifactName ],
|
|
88
|
-
{ source: `${from[0].errorParent}.${from[0].name}`, target: `${to[0].errorParent}.${to[0].name}` },
|
|
86
|
+
{ source: `${ from[0].errorParent }.${ from[0].name }`, target: `${ to[0].errorParent }.${ to[0].name }` },
|
|
89
87
|
'No temporal WHERE clause added as $(SOURCE) and $(TARGET) are not of same origin');
|
|
90
88
|
}
|
|
91
89
|
}
|
|
@@ -145,10 +145,10 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
145
145
|
const matchingCombined = $combined[elemName];
|
|
146
146
|
// Internal errors - this should never happen!
|
|
147
147
|
if (matchingCombined.length > 1) { // should already be caught by compiler
|
|
148
|
-
throw new ModelError(`Ambiguous name - can't be resolved: ${elemName}. Found in: ${matchingCombined.map(o => o.parent)}`);
|
|
148
|
+
throw new ModelError(`Ambiguous name - can't be resolved: ${ elemName }. Found in: ${ matchingCombined.map(o => o.parent) }`);
|
|
149
149
|
}
|
|
150
150
|
else if (matchingCombined.length === 0) { // no clue how this could happen? Invalid CSN?
|
|
151
|
-
throw new ModelError(`No matching entry found in UNION of all elements for: ${elemName}`);
|
|
151
|
+
throw new ModelError(`No matching entry found in UNION of all elements for: ${ elemName }`);
|
|
152
152
|
}
|
|
153
153
|
alias = matchingCombined[0].parent;
|
|
154
154
|
}
|
|
@@ -177,7 +177,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
177
177
|
ref,
|
|
178
178
|
};
|
|
179
179
|
if (assocCol.as) {
|
|
180
|
-
const columnName = `${assocCol.as}${pathDelimiter}${foreignKey.as}`;
|
|
180
|
+
const columnName = `${ assocCol.as }${ pathDelimiter }${ foreignKey.as }`;
|
|
181
181
|
result.as = columnName;
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -229,9 +229,9 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
// Create an unused alias name for the MIXIN - use 3 _ to avoid collision with usings
|
|
232
|
-
let mixinElemName = `___${mixinName || elemName}`;
|
|
232
|
+
let mixinElemName = `___${ mixinName || elemName }`;
|
|
233
233
|
while (elements[mixinElemName])
|
|
234
|
-
mixinElemName = `_${mixinElemName}`;
|
|
234
|
+
mixinElemName = `_${ mixinElemName }`;
|
|
235
235
|
|
|
236
236
|
// Copy the association element to the MIXIN clause under its alias name
|
|
237
237
|
// Needs to be a deep copy, as we transform the on-condition
|
|
@@ -327,7 +327,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
327
327
|
function transformViewOrEntity( query, artifact, artName, path ) {
|
|
328
328
|
const ignoreAssociations = options.sqlDialect === 'hana' && options.withHanaAssociations === false;
|
|
329
329
|
csnUtils.initDefinition(artifact);
|
|
330
|
-
const { elements } = queryOrMain(query, artifact);
|
|
330
|
+
const { elements } = queryOrMain(query, artifact); // TODO: use queryForElements
|
|
331
331
|
// We use the elements from the leading query/main artifact - adapt the path
|
|
332
332
|
const elementsPath = elements === artifact.elements ? path.slice(0, 2).concat('elements') : path.concat('elements');
|
|
333
333
|
const queryPath = path;
|
|
@@ -188,6 +188,8 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
188
188
|
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', ['DraftMessages'] );
|
|
189
189
|
}
|
|
190
190
|
setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: ['DraftMessages'] });
|
|
191
|
+
const service = csn.definitions[getServiceOfArtifact(artifactName, services)];
|
|
192
|
+
setAnnotation(service, '@Common.AddressViaNavigationPath', true);
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
// Iterate elements
|
|
@@ -172,7 +172,7 @@ function remapODataAnnotations( csn ) {
|
|
|
172
172
|
*/
|
|
173
173
|
function remapAnnotationsOnElement( artifact, element ) {
|
|
174
174
|
if (element.elements && !element.$ignore) // We expect to only be called on flattened CSN - error if we encounter .elements!
|
|
175
|
-
throw new CompilerAssertion(`Expected a flat model. Found element with subelements: ${JSON.stringify(element)}`);
|
|
175
|
+
throw new CompilerAssertion(`Expected a flat model. Found element with subelements: ${ JSON.stringify(element) }`);
|
|
176
176
|
for (const prop in element) {
|
|
177
177
|
if (directMappings[prop])
|
|
178
178
|
directMappings[prop](csn, artifact, element, prop);
|
|
@@ -104,7 +104,7 @@ function addForeignKeyToColumns( foreignKey, columns, associationColumn, options
|
|
|
104
104
|
ref,
|
|
105
105
|
};
|
|
106
106
|
if (associationColumn.as) {
|
|
107
|
-
const columnName = `${associationColumn.as}_${foreignKey.as || implicitAs(foreignKey.ref)}`;
|
|
107
|
+
const columnName = `${ associationColumn.as }_${ foreignKey.as || implicitAs(foreignKey.ref) }`;
|
|
108
108
|
result.as = columnName;
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -34,6 +34,7 @@ const getServiceFilterFunction = require('./service');
|
|
|
34
34
|
*/
|
|
35
35
|
function effectiveCsn( model, options, messageFunctions ) {
|
|
36
36
|
const csn = cloneFullCsn(model, options);
|
|
37
|
+
delete csn.namespace; // must not be set for effective CSN
|
|
37
38
|
delete csn.vocabularies; // must not be set for effective CSN
|
|
38
39
|
messageFunctions.setModel(csn);
|
|
39
40
|
|
|
@@ -16,13 +16,13 @@ function getServiceFilterFunction(serviceName, collector) {
|
|
|
16
16
|
if (artifactName === serviceName && artifact.kind === 'service') {
|
|
17
17
|
collector.service = artifact;
|
|
18
18
|
}
|
|
19
|
-
else if (definitions[serviceName]?.kind === 'service' && artifactName.startsWith(`${serviceName }.`)) {
|
|
19
|
+
else if (definitions[serviceName]?.kind === 'service' && artifactName.startsWith(`${ serviceName }.`)) {
|
|
20
20
|
collector.containedArtifacts[artifactName] = artifact;
|
|
21
21
|
delete artifact.query;
|
|
22
22
|
delete artifact.projection;
|
|
23
23
|
if (artifact.elements) {
|
|
24
24
|
forEach(artifact.elements, (elementName, element) => {
|
|
25
|
-
if (element.target && !element.target.startsWith(`${serviceName }.`))
|
|
25
|
+
if (element.target && !element.target.startsWith(`${ serviceName }.`))
|
|
26
26
|
delete artifact.elements[elementName];
|
|
27
27
|
});
|
|
28
28
|
}
|