@sap/cds-compiler 6.1.0 → 6.3.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/bin/cdsc.js +17 -6
  3. package/bin/cdsse.js +1 -1
  4. package/bin/cdsv2m.js +1 -1
  5. package/lib/api/main.js +29 -7
  6. package/lib/api/options.js +1 -1
  7. package/lib/base/builtins.js +9 -0
  8. package/lib/base/keywords.js +1 -1
  9. package/lib/base/message-registry.js +41 -10
  10. package/lib/base/messages.js +13 -6
  11. package/lib/base/model.js +1 -1
  12. package/lib/base/optionProcessorHelper.js +7 -2
  13. package/lib/checks/assocOutsideService.js +17 -30
  14. package/lib/checks/checkForTypes.js +0 -18
  15. package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
  16. package/lib/checks/featureFlags.js +4 -1
  17. package/lib/checks/onConditions.js +2 -2
  18. package/lib/checks/queryNoDbArtifacts.js +16 -15
  19. package/lib/checks/types.js +1 -1
  20. package/lib/checks/utils.js +30 -6
  21. package/lib/checks/validator.js +4 -5
  22. package/lib/compiler/assert-consistency.js +3 -1
  23. package/lib/compiler/base.js +1 -1
  24. package/lib/compiler/builtins.js +1 -1
  25. package/lib/compiler/checks.js +85 -39
  26. package/lib/compiler/define.js +24 -5
  27. package/lib/compiler/extend.js +1 -1
  28. package/lib/compiler/finalize-parse-cdl.js +9 -1
  29. package/lib/compiler/generate.js +4 -4
  30. package/lib/compiler/index.js +88 -6
  31. package/lib/compiler/lsp-api.js +2 -0
  32. package/lib/compiler/populate.js +8 -8
  33. package/lib/compiler/propagator.js +1 -1
  34. package/lib/compiler/resolve.js +22 -21
  35. package/lib/compiler/shared.js +6 -6
  36. package/lib/compiler/tweak-assocs.js +53 -31
  37. package/lib/compiler/utils.js +9 -16
  38. package/lib/compiler/xpr-rewrite.js +2 -2
  39. package/lib/gen/BaseParser.js +35 -29
  40. package/lib/gen/CdlGrammar.checksum +1 -1
  41. package/lib/gen/CdlParser.js +1424 -1430
  42. package/lib/gen/Dictionary.json +1 -2
  43. package/lib/gen/cdlKeywords.json +26 -0
  44. package/lib/inspect/inspectPropagation.js +1 -1
  45. package/lib/json/from-csn.js +2 -2
  46. package/lib/json/to-csn.js +1 -1
  47. package/lib/language/multiLineStringParser.js +1 -1
  48. package/lib/model/cloneCsn.js +1 -0
  49. package/lib/model/csnRefs.js +9 -4
  50. package/lib/model/csnUtils.js +67 -2
  51. package/lib/optionProcessor.js +9 -9
  52. package/lib/parsers/AstBuildingParser.js +28 -26
  53. package/lib/parsers/identifiers.js +2 -30
  54. package/lib/render/toCdl.js +73 -13
  55. package/lib/render/toSql.js +127 -108
  56. package/lib/render/utils/common.js +4 -2
  57. package/lib/render/utils/sql.js +67 -0
  58. package/lib/transform/addTenantFields.js +4 -4
  59. package/lib/transform/db/assertUnique.js +2 -1
  60. package/lib/transform/db/associations.js +37 -1
  61. package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
  62. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  63. package/lib/transform/db/cdsPersistence.js +1 -1
  64. package/lib/transform/db/expansion.js +37 -36
  65. package/lib/transform/db/killAnnotations.js +1 -0
  66. package/lib/transform/db/processSqlServices.js +20 -2
  67. package/lib/transform/draft/db.js +20 -20
  68. package/lib/transform/draft/odata.js +38 -40
  69. package/lib/transform/effective/associations.js +1 -1
  70. package/lib/transform/effective/flattening.js +40 -47
  71. package/lib/transform/effective/main.js +6 -4
  72. package/lib/transform/forOdata.js +201 -92
  73. package/lib/transform/forRelationalDB.js +151 -142
  74. package/lib/transform/localized.js +116 -109
  75. package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
  76. package/lib/transform/odata/createForeignKeys.js +73 -70
  77. package/lib/transform/odata/flattening.js +216 -200
  78. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
  79. package/lib/transform/odata/toFinalBaseType.js +40 -39
  80. package/lib/transform/odata/typesExposure.js +151 -133
  81. package/lib/transform/odata/utils.js +7 -6
  82. package/lib/transform/parseExpr.js +165 -162
  83. package/lib/transform/transformUtils.js +184 -551
  84. package/lib/transform/translateAssocsToJoins.js +511 -596
  85. package/lib/transform/tupleExpansion.js +495 -0
  86. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  87. package/lib/utils/moduleResolve.js +1 -1
  88. package/package.json +2 -2
  89. package/lib/base/cleanSymbols.js +0 -17
  90. package/lib/checks/nonexpandableStructured.js +0 -39
@@ -0,0 +1,495 @@
1
+ 'use strict';
2
+
3
+ const { setProp } = require('../base/model');
4
+ const { applyTransformations, isDollarSelfOrProjectionOperand, implicitAs } = require('../model/csnUtils');
5
+ const { isBuiltinType } = require('../base/builtins');
6
+ const { condAsTree } = require('../model/xprAsTree');
7
+ const { pathToMessageString } = require('../base/messages');
8
+ const { pathId } = require('../model/csnRefs');
9
+ const { cloneCsnNonDict } = require('../model/cloneCsn');
10
+
11
+ /**
12
+ * All relational operators supported by tuple expansion.
13
+ * Also includes `is not`, which is actually two tokens.
14
+ *
15
+ * @type {string[]}
16
+ */
17
+ const RelationalOperators = [ '=', '<>', '==', '!=', 'is', 'is not' /* , 'like' */ ];
18
+
19
+ /**
20
+ * Operators that to be used to combine expanded expressions by keeping logical relations.
21
+ */
22
+ const OperatorCombinator = {
23
+ __proto__: null,
24
+ '=': 'and',
25
+ '==': 'and',
26
+ '<>': 'or',
27
+ '!=': 'or',
28
+ is: 'and',
29
+ 'is not': 'or',
30
+ };
31
+
32
+
33
+ /**
34
+ * Get transformation functions for "tuple expansion", i.e. for expanding
35
+ * structure references in expressions.
36
+ *
37
+ * @param {CSN.Model} csn
38
+ * @param {object} csnUtils
39
+ * @param {object} msgFunctions
40
+ *
41
+ * @returns {object} Object containing functions `expandStructsInExpression` and `flattenPath`
42
+ */
43
+ function tupleExpansion(csn, csnUtils, msgFunctions) {
44
+ const { message, error, info } = msgFunctions;
45
+ const { inspectRef, effectiveType, resolvePath } = csnUtils;
46
+
47
+ return {
48
+ expandStructsInExpression,
49
+ flattenPath,
50
+ };
51
+
52
+ /**
53
+ * Expand structured expression arguments to flat reference paths.
54
+ * Structured elements are real sub element lists and managed associations.
55
+ * All unmanaged association definitions are rewritten if applicable (elements/mixins).
56
+ * Also, HAVING and WHERE clauses are rewritten.
57
+ * We also check for infix filters and `.xpr` in columns.
58
+ *
59
+ * @param {object} [traversalOptions={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts
60
+ */
61
+ function expandStructsInExpression( traversalOptions = {} ) {
62
+ applyTransformations(csn, {
63
+ on: expandExpr,
64
+ having: expandExpr,
65
+ where: expandExpr,
66
+ xpr: expandExpr,
67
+ args: (parent, name, args, path) => {
68
+ if (!parent.id && !parent.func)
69
+ return; // ensure we're not in JOIN
70
+
71
+ if (Array.isArray(parent.args)) {
72
+ expandExpr(parent, name, args, path);
73
+ return;
74
+ }
75
+
76
+ for (const argName in parent.args) // named arguments
77
+ rejectAnyDirectStructureReference([ parent.args[argName] ], path.concat(name, argName));
78
+ },
79
+ }, [], traversalOptions);
80
+ }
81
+
82
+ function expandExpr(parent, name, _xpr, path) {
83
+ // We need to structurize the _model_, as error locations may otherwise point to non-existent paths.
84
+ // Note: structurizer does not go into nested `xpr`.
85
+ parent[name] = condAsTree(parent[name]);
86
+ parent[name] = expandStructurizedExpr(parent[name], path.concat(name));
87
+ parent[name] = parent[name].flat(Infinity); // tokenize again
88
+ }
89
+
90
+ function expandStructurizedExpr(expr, location) {
91
+ if (!Array.isArray(expr))
92
+ return expr; // don't traverse strings, etc.
93
+
94
+ expr = expr.map((e, i) => expandStructurizedExpr(e, location.concat(i)));
95
+
96
+ if (expr.length === 3 && typeof expr[1] === 'string') // also includes `<lhs> is null`
97
+ return expandBinaryOp(expr[0], [ expr[1] ], expr[2], location) ?? expr;
98
+
99
+ if (expr.length === 4 && expr[1] === 'is' && expr[2] === 'not' && expr[3] === 'null')
100
+ return expandBinaryOp(expr[0], [ expr[1], expr[2] ], expr[3], location) ?? expr;
101
+
102
+ // expr.length is either <3 or >=4, in which case we reject all structure references
103
+ // in the array, but still traverse sub-arrays (the expression is structurized, after all).
104
+ rejectAnyDirectStructureReference(expr, location);
105
+
106
+ return expr;
107
+ }
108
+
109
+ /**
110
+ * Expands the binary operation `lhs <op> rhs`, where `op` may be one or more tokens,
111
+ * e.g. `is not` for `is not null`.
112
+ *
113
+ * @param {object} lhs
114
+ * @param {string[]} operators
115
+ * @param {object} rhs
116
+ * @param location
117
+ * @returns {object[]|null} In case of errors, returns `null`.
118
+ */
119
+ function expandBinaryOp(lhs, operators, rhs, location) {
120
+ const lhsArt = lhs._art || lhs.ref && enrichRef(lhs, location.concat(0));
121
+ const rhsArt = rhs._art || rhs.ref && enrichRef(rhs, location.concat(2));
122
+
123
+ if (!lhsArt && !rhsArt || !isExpandable(lhsArt) && !isExpandable(rhsArt))
124
+ return null; // no structure to expand
125
+
126
+ if (isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs)) {
127
+ // if either side is bare `$self`, the compiler has handled it already
128
+ return null;
129
+ }
130
+
131
+ // At least one side is expandable. That means, if we can't expand a structural reference
132
+ // starting here, we _must_ emit an error.
133
+
134
+ const opStr = operators.join(' ');
135
+ if (!RelationalOperators.includes(opStr)) {
136
+ message('expr-unexpected-operator', location, { op: opStr, elemref: (lhsArt && lhs) || rhs },
137
+ 'Unexpected operator $(OP) in structural comparison with $(ELEMREF)');
138
+ return null;
139
+ }
140
+
141
+ const lhsIsVal = isVal(lhs);
142
+ const rhsIsVal = isVal(rhs);
143
+
144
+ const lhsPaths = lhsIsVal ? [] : flattenPath(lhs, false, true );
145
+ const rhsPaths = rhsIsVal ? [] : flattenPath(rhs, false, true );
146
+
147
+ const msgCode = () => `${ xprForMessage( lhs ) } ${ opStr } ${ xprForMessage( rhs ) }`;
148
+
149
+ if (xprOperationRejected())
150
+ return null;
151
+ if (scalarOperationRejected())
152
+ return null;
153
+
154
+ if (lhsPaths.length === 0 && rhsPaths.length === 0) {
155
+ error('expr-invalid-expansion-empty', location, {
156
+ code: msgCode(),
157
+ }, 'Neither side of the expression $(CODE) expands to anything');
158
+ return null;
159
+ }
160
+
161
+ const xref = createCrossReferences(lhsPaths, rhsPaths, lhs, rhs);
162
+
163
+ for (const xn in xref) {
164
+ const x = xref[xn];
165
+
166
+ // Each entry of the left side must have a matching entry on the right.
167
+ if (!x.lhs || !x.rhs) {
168
+ error('expr-invalid-expansion', location, {
169
+ '#': 'path-mismatch',
170
+ value: msgCode(),
171
+ name: xn,
172
+ alias: pathToMessageString(x.rhs ? lhs : rhs),
173
+ } );
174
+ return null;
175
+ }
176
+
177
+ if (rejectNonScalarRef(lhs, x.lhs, location) || rejectNonScalarRef(rhs, x.rhs, location)) {
178
+ // one side was expanded to a non-scalar reference
179
+ return null;
180
+ }
181
+
182
+ // info about type incompatibility if no other errors occurred
183
+ if (!lhsIsVal && !rhsIsVal && x.lhs && x.rhs) {
184
+ if (getType(x.lhs._art) !== getType(x.rhs._art)) {
185
+ info('expr-ignoring-type-mismatch', location, {
186
+ code: msgCode(),
187
+ name: xn,
188
+ }, 'Types of sub path $(NAME) differ when expanding structure in $(CODE)');
189
+ }
190
+ }
191
+ }
192
+
193
+ // We're not adding another `xpr`, because `op` binds stronger than the combinators
194
+ // in all cases we care about.
195
+ const xpr = Object.values(xref).map((x) => {
196
+ delete x.lhs.comparisonRef;
197
+ delete x.rhs.comparisonRef;
198
+ return [ x.lhs, ...operators, x.rhs ];
199
+ });
200
+ // insert the combinator between each value
201
+ for (let i = 1; i < xpr.length; i += 2)
202
+ xpr.splice(i, 0, OperatorCombinator[opStr]);
203
+
204
+ return xpr.length > 1 ? { xpr } : xpr;
205
+
206
+ function rejectNonScalarRef(e, x, location) {
207
+ if ( !x || isVal(e) || isScalarOrNoType(x))
208
+ return false;
209
+ // error: one side was expanded to a non-scalar, e.g. unmanaged association
210
+ error('expr-invalid-expansion', location, {
211
+ '#': 'non-scalar',
212
+ value: msgCode(),
213
+ name: pathToMessageString( e ),
214
+ });
215
+ return true;
216
+ }
217
+
218
+ /**
219
+ * Check for and reject invalid expressions with complex operands.
220
+ *
221
+ * @returns {boolean} If true, expression is invalid.
222
+ */
223
+ function xprOperationRejected() {
224
+ if (Array.isArray(lhs) || Array.isArray(rhs)) {
225
+ error('ref-unexpected-structured', location, { '#': 'complexExpr', elemref: lhs.ref ? lhs : rhs, value: msgCode() });
226
+ return true;
227
+ }
228
+
229
+ return false;
230
+ }
231
+
232
+ /**
233
+ * Check for and reject invalid expressions with scalar values/references.
234
+ *
235
+ * @returns {boolean} If true, expression is invalid.
236
+ */
237
+ function scalarOperationRejected() {
238
+ if ((opStr === 'is' || opStr === 'is not') && (rhs === 'null' || lhs === 'null'))
239
+ return false; // `is [not] null` works even with multiple elements
240
+
241
+ if ((lhsIsVal || isScalarOrNoType(lhs)) && rhsPaths.length !== 1) {
242
+ const variant = rhs._art?.target && 'assoc-expr' || 'struct-expr';
243
+ error('ref-unexpected-structured', location, { '#': variant, elemref: rhs });
244
+ return true;
245
+ }
246
+ if ((rhsIsVal || isScalarOrNoType(rhs)) && lhsPaths.length !== 1) {
247
+ const variant = lhs._art?.target && 'assoc-expr' || 'struct-expr';
248
+ error('ref-unexpected-structured', location, { '#': variant, elemref: lhs });
249
+ return true;
250
+ }
251
+
252
+ if (lhs.ref && isScalarOrNoType(lhs)) {
253
+ error('expr-unsupported-expansion', location, { '#': 'scalarRef', elemref: lhs, value: msgCode() });
254
+ return true;
255
+ }
256
+ if (rhs.ref && isScalarOrNoType(rhs)) {
257
+ error('expr-unsupported-expansion', location, { '#': 'scalarRef', elemref: rhs, value: msgCode() });
258
+ return true;
259
+ }
260
+
261
+ return false;
262
+ }
263
+ }
264
+
265
+ function rejectAnyDirectStructureReference(expr, location) {
266
+ if (expr[0] === 'exists') {
267
+ // we ignore WHERE EXISTS clauses; they are not relevant for OData,
268
+ // and in SQL it was handled before
269
+ return;
270
+ }
271
+
272
+ for (const x of expr) {
273
+ const art = x?._art || x?.ref && !x.$scope && inspectRef(location.concat(0)).art;
274
+ if (art && isExpandable(art)) {
275
+ const variant = art.target && 'assoc' || 'std';
276
+ error('ref-unexpected-structured', location, { '#': variant, elemref: x });
277
+ }
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Pairs left and right sub-paths into a common structure.
283
+ *
284
+ * @param {Array} lhsPaths
285
+ * @param {Array} rhsPaths
286
+ * @param {object} lhs
287
+ * @param {object} rhs
288
+ * @returns {Record<string, object>}
289
+ */
290
+ function createCrossReferences(lhsPaths, rhsPaths, lhs, rhs) {
291
+ const xRef = Object.create(null);
292
+
293
+ const rhsIsVal = isVal(rhs);
294
+ for (const p of lhsPaths) {
295
+ const name = p.comparisonRef.slice(lhs.ref.length).map(pathId).join('.');
296
+ xRef[name] ??= { name };
297
+ xRef[name].lhs = p;
298
+ if (rhsIsVal)
299
+ xRef[name].rhs = rhs;
300
+ }
301
+
302
+ const lhsIsVal = isVal(lhs);
303
+ for (const p of rhsPaths) {
304
+ const name = p.comparisonRef.slice(rhs.ref.length).map(pathId).join('.');
305
+ xRef[name] ??= { name };
306
+ xRef[name].rhs = p;
307
+ if (lhsIsVal)
308
+ xRef[name].lhs = lhs;
309
+ }
310
+
311
+ return xRef;
312
+ }
313
+
314
+ /**
315
+ * Flatten structured leaf types and return an array of paths.
316
+ *
317
+ * Argument 'path' must be an object of the form
318
+ * `{ _art: <leaf_artifact>, ref: [...] }`
319
+ * with `_art` identifying `ref[ref.length-1]`
320
+ *
321
+ * A produced path has the form `{ _art: <ref>, ref: [ <id> (, <id>)* ] }`
322
+ *
323
+ * Flattening stops on all non-structured elements, if followMgdAssoc=false.
324
+ *
325
+ * If fullRef is true, a path step is produced as `{ id: <id>, _art: <link> }`
326
+ */
327
+ function flattenPath(path, fullRef = false, followMgdAssoc = false) {
328
+ let art = path._art;
329
+ if (!art)
330
+ return [ path ];
331
+
332
+ if (!art.elements) {
333
+ if (followMgdAssoc && art.target && art.keys) {
334
+ const rc = [];
335
+ for (const k of art.keys) {
336
+ const nps = {
337
+ ref: k.ref.map(p => (fullRef ? { id: p } : p) ),
338
+ comparisonRef: [ k.as || implicitAs(k.ref) ],
339
+ };
340
+ setProp(nps, '_art', k._art);
341
+ const paths = flattenPath( nps, fullRef, followMgdAssoc );
342
+ // prepend prefix path
343
+ paths.forEach((p) => {
344
+ p.comparisonRef.unshift(...clonePath(path.comparisonRef || path.ref));
345
+ p.ref.unshift(...clonePath(path.ref));
346
+ if (path.$scope !== undefined)
347
+ setProp(p, '$scope', path.$scope);
348
+ });
349
+ rc.push(...paths);
350
+ }
351
+ return rc;
352
+ }
353
+
354
+ if (art.type?.ref)
355
+ art = resolvePath(art.type);
356
+ else if (art.type && !isBuiltinType(art.type))
357
+ art = csn.definitions[art.type];
358
+ }
359
+
360
+ const { elements } = art;
361
+ if (!elements) {
362
+ setProp(path, '_art', art);
363
+ return [ path ];
364
+ }
365
+
366
+ const rc = [];
367
+ Object.entries(elements).forEach(([ en, elt ]) => {
368
+ const step = (fullRef ? { id: en, _art: elt } : en );
369
+ const nps = {
370
+ ref: [ step ],
371
+ comparisonRef: [ step ],
372
+ };
373
+ setProp(nps, '_art', elt);
374
+ const paths = flattenPath( nps, fullRef, followMgdAssoc );
375
+ // prepend prefix path
376
+ paths.forEach((p) => {
377
+ p.comparisonRef.unshift(...clonePath(path.comparisonRef || path.ref));
378
+ p.ref.splice(0, 0, ...clonePath(path.ref));
379
+ if (path.$scope !== undefined)
380
+ setProp(p, '$scope', path.$scope);
381
+ });
382
+ rc.push(...paths);
383
+ });
384
+ return rc;
385
+ }
386
+
387
+ function getType(art) {
388
+ const effArt = art && effectiveType(art);
389
+ return Object.keys(effArt).length ? effArt : art.type;
390
+ }
391
+
392
+ function isExpandable(art) {
393
+ art = art && effectiveType(art);
394
+ if (!art)
395
+ return false;
396
+
397
+ // items in ON conditions are illegal but this should be checked elsewhere
398
+ const elements = art.elements || art.items?.elements;
399
+ return !!(elements || art.target && art.keys);
400
+ }
401
+
402
+ /**
403
+ * Returns true if the given artifact or artifact reference has a scalar type
404
+ * or no type at all, e.g. for references to elements of type `cds.String`.
405
+ *
406
+ * @param art
407
+ * @returns {boolean}
408
+ */
409
+ function isScalarOrNoType(art) {
410
+ art = art?._art ?? art;
411
+ if (isTypeScalarBuiltin(art.type))
412
+ return true;
413
+
414
+ art = art && effectiveType(art); // builtins are resolved to `{}`
415
+ if (!art)
416
+ return false;
417
+
418
+ const type = art.type || art.items?.type;
419
+ const elements = art.elements || art.items?.elements;
420
+ const target = art.target || art.items?.target;
421
+ if (!elements && !type && !target)
422
+ return true;
423
+
424
+ return isTypeScalarBuiltin(type);
425
+ }
426
+
427
+ function isTypeScalarBuiltin(type) {
428
+ // "cds.Map" can't be used for tuple expansion, even though it is a structured type.
429
+ return (type && isBuiltinType(type) &&
430
+ type !== 'cds.Association' && type !== 'cds.Composition' && type !== 'cds.Map');
431
+ }
432
+
433
+ /**
434
+ * Enrich a reference node with properties that are also set by our enricher.
435
+ * There are paths (generated by "where-exists") that are missing these properties, hence
436
+ * we add them here.
437
+ * Since the result of tuple expansion is not exposed in any CSN, we don't need to care about
438
+ * cleanup afterwards.
439
+ *
440
+ * TODO: Can we move this to the enricher?
441
+ *
442
+ * @param node Object with `ref`
443
+ * @param {CSN.Path} loc Path to the node.
444
+ * @returns {*}
445
+ */
446
+ function enrichRef(node, loc) {
447
+ if (!node.ref)
448
+ return node;
449
+ const inspected = inspectRef(loc);
450
+
451
+ if (inspected.links)
452
+ setProp(node, '_links', inspected.links);
453
+ if (inspected.art)
454
+ setProp(node, '_art', inspected.art );
455
+ if (inspected.$env)
456
+ setProp(node, '$env', inspected.$env );
457
+
458
+ setProp(node, '$scope', inspected.scope);
459
+ setProp(node, '$path', [ ...loc ]);
460
+
461
+ return node._art;
462
+ }
463
+ }
464
+
465
+ function isVal(valOrRef) {
466
+ return (valOrRef === 'null' || valOrRef.val !== undefined);
467
+ }
468
+
469
+ function xprForMessage(xpr) {
470
+ if (isVal(xpr))
471
+ return xpr.val ?? xpr;
472
+ if (xpr.ref)
473
+ return pathToMessageString( xpr );
474
+ return '…';
475
+ }
476
+
477
+ /**
478
+ * Clone a ref. Otherwise, it may happen that two paths with filters are transformed twice
479
+ * during flattening, due to object equality.
480
+ *
481
+ * @param {any[]} ref
482
+ * @returns {any[]}
483
+ */
484
+ function clonePath(ref) {
485
+ return ref.map((step) => {
486
+ if (typeof step === 'string')
487
+ return step;
488
+ return cloneCsnNonDict(step);
489
+ });
490
+ }
491
+
492
+ module.exports = {
493
+ tupleExpansion,
494
+ RelationalOperators,
495
+ };
@@ -48,6 +48,7 @@ module.exports = (csn, options) => {
48
48
  params: skip, // TODO: (comment is from propagator.js) actually only with parent action
49
49
  returns: always,
50
50
  notNull: always,
51
+ default: always,
51
52
  keys: always,
52
53
  };
53
54
 
@@ -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/js/max-len
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.1.0",
3
+ "version": "6.3.0",
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",
@@ -1,17 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Remove the given symbols from the object.
5
- * Does NOT do so recursively, only directly on the object.
6
- *
7
- * @param {object} obj
8
- * @param {...any} symbols
9
- */
10
- function cleanSymbols( obj, ...symbols ) {
11
- for (const symbol of symbols)
12
- delete obj[symbol];
13
- }
14
-
15
- module.exports = {
16
- cleanSymbols,
17
- };
@@ -1,39 +0,0 @@
1
- 'use strict';
2
-
3
- const { otherSideIsExpandableStructure, resolveArtifactType } = require('./utils');
4
-
5
- /**
6
- * Check the given expression for non-expandable structure usage
7
- *
8
- * @param {object} parent Object with the expression as a property
9
- * @param {string} name Name of the expression property on parent
10
- * @param {Array} expression Expression to check - .on .xpr .having and .where
11
- */
12
- function nonexpandableStructuredInExpression( parent, name, expression ) {
13
- for (let i = 0; i < expression.length; i++) {
14
- if (expression[i].ref) {
15
- const { ref } = expression[i];
16
- // eslint-disable-next-line prefer-const
17
- let { _art, $scope } = expression[i];
18
- if (!_art)
19
- continue;
20
- const validStructuredElement = otherSideIsExpandableStructure.call(this, expression, i);
21
- if (_art) {
22
- _art = resolveArtifactType.call(this, _art);
23
- // Paths of an expression may end on a structured element only if both operands in the expression end on a structured element
24
- if ((_art?.elements || _art?.keys && (i === 0 || expression[i - 1] !== 'exists')) && !validStructuredElement && ($scope !== '$self' || $scope === '$self' && ref.length > 1)) { // TODO: Use $self to navigate to struct
25
- this.error('ref-unexpected-structured',
26
- name === 'on' ? [ ...parent.$path, name, i ] : expression[i].$path,
27
- { '#': 'std', elemref: { ref } } );
28
- }
29
- }
30
- }
31
- }
32
- }
33
-
34
- module.exports = {
35
- on: nonexpandableStructuredInExpression,
36
- having: nonexpandableStructuredInExpression,
37
- where: nonexpandableStructuredInExpression,
38
- xpr: nonexpandableStructuredInExpression,
39
- };