@sap/cds-compiler 6.4.6 → 6.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +42 -1156
  2. package/README.md +1 -10
  3. package/bin/cdsc.js +1 -1
  4. package/doc/IncompatibleChanges_v5.md +436 -0
  5. package/doc/IncompatibleChanges_v6.md +659 -0
  6. package/doc/Versioning.md +3 -7
  7. package/lib/api/main.js +1 -0
  8. package/lib/api/options.js +5 -0
  9. package/lib/api/validate.js +3 -0
  10. package/lib/base/message-registry.js +31 -8
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +3 -2
  13. package/lib/checks/actionsFunctions.js +6 -3
  14. package/lib/checks/existsInForbiddenPlaces.js +32 -0
  15. package/lib/checks/validator.js +2 -0
  16. package/lib/compiler/assert-consistency.js +3 -5
  17. package/lib/compiler/checks.js +4 -8
  18. package/lib/compiler/define.js +330 -558
  19. package/lib/compiler/extend.js +302 -18
  20. package/lib/compiler/finalize-parse-cdl.js +2 -10
  21. package/lib/compiler/generate.js +33 -5
  22. package/lib/compiler/kick-start.js +8 -7
  23. package/lib/compiler/populate.js +25 -70
  24. package/lib/compiler/propagator.js +1 -2
  25. package/lib/compiler/resolve.js +4 -13
  26. package/lib/compiler/shared.js +18 -5
  27. package/lib/compiler/tweak-assocs.js +13 -9
  28. package/lib/compiler/utils.js +98 -2
  29. package/lib/compiler/xpr-rewrite.js +2 -1
  30. package/lib/edm/annotations/edmJson.js +9 -6
  31. package/lib/edm/annotations/genericTranslation.js +8 -4
  32. package/lib/edm/csn2edm.js +3 -4
  33. package/lib/edm/edmInboundChecks.js +1 -2
  34. package/lib/edm/edmPreprocessor.js +3 -3
  35. package/lib/gen/CdlGrammar.checksum +1 -1
  36. package/lib/gen/CdlParser.js +1 -1
  37. package/lib/gen/Dictionary.json +16 -1
  38. package/lib/json/from-csn.js +4 -6
  39. package/lib/json/to-csn.js +3 -3
  40. package/lib/model/csnRefs.js +19 -5
  41. package/lib/model/enrichCsn.js +4 -2
  42. package/lib/optionProcessor.js +8 -4
  43. package/lib/render/toSql.js +0 -4
  44. package/lib/render/utils/sql.js +3 -2
  45. package/lib/transform/db/applyTransformations.js +1 -1
  46. package/lib/transform/db/assertUnique.js +3 -3
  47. package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
  48. package/lib/transform/db/assocsToQueries/transformExists.js +14 -3
  49. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  50. package/lib/transform/db/backlinks.js +4 -4
  51. package/lib/transform/db/cdsPersistence.js +4 -4
  52. package/lib/transform/db/constraints.js +4 -4
  53. package/lib/transform/db/expansion.js +5 -5
  54. package/lib/transform/db/flattening.js +4 -5
  55. package/lib/transform/db/rewriteCalculatedElements.js +3 -3
  56. package/lib/transform/db/temporal.js +11 -11
  57. package/lib/transform/draft/db.js +2 -0
  58. package/lib/transform/draft/odata.js +5 -7
  59. package/lib/transform/effective/flattening.js +1 -2
  60. package/lib/transform/forOdata.js +3 -3
  61. package/lib/transform/forRelationalDB.js +1 -1
  62. package/lib/transform/odata/createForeignKeys.js +1 -2
  63. package/lib/transform/odata/flattening.js +1 -2
  64. package/lib/transform/odata/toFinalBaseType.js +52 -55
  65. package/lib/transform/transformUtils.js +3 -4
  66. package/package.json +1 -1
  67. package/doc/CHANGELOG_BETA.md +0 -464
  68. package/doc/CHANGELOG_DEPRECATED.md +0 -235
@@ -40,7 +40,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
40
40
  const {
41
41
  info, warning, error, message,
42
42
  } = messageFunctions;
43
- const special$self = !csn?.definitions?.$self && '$self';
44
43
  const csnUtils = getUtils(csn);
45
44
 
46
45
  // proxies are merged into the final model after all proxy elements are collected
@@ -2077,7 +2076,8 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2077
2076
 
2078
2077
  function iterateParams( action, location ) {
2079
2078
  let optPns = [];
2080
- const isBP = p => (p.items?.type || p.type) === special$self;
2079
+ const isBP = p => (p.items?.type || p.type) === '$self' &&
2080
+ p === Object.values( action.params )[0];
2081
2081
 
2082
2082
  if (action.params) {
2083
2083
  Object.entries(action.params).forEach(([ pn, p ]) => {
@@ -2126,7 +2126,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2126
2126
  }
2127
2127
  else if (action.kind === 'function') {
2128
2128
  // this is a mandatory parameter, warn about all previously collected optional parameters
2129
- if (optPns.filter(op => (op.items?.type || op.type) !== special$self).length)
2129
+ if (optPns.length)
2130
2130
  error('odata-parameter-order', location.concat(pn));
2131
2131
  optPns = [];
2132
2132
  }
@@ -1 +1 @@
1
- 8e38091fda3711e1d4d2b8b87fbdd328
1
+ 2af41f8c84ca1fa40a50b8c58903dadc
@@ -3827,7 +3827,7 @@ case 645:this.s=651;{ this.attachLocation( $.expr ); }continue
3827
3827
  case 646:if(this.valuePath(_={},651)){e=_.expr; this.valuePathAstWithNew( $.expr, e ); }continue
3828
3828
  case 647:switch(this.l()){
3829
3829
  case'(':if(this.c(648))open=this.lb();continue
3830
- case'Id':if(this.valuePath(_={},651)){e=_.expr; e = this.valuePathAst( e ); e.$expected = 'exists';
3830
+ case'Id':if(this.valuePath(_={},651)){e=_.expr; e = this.valuePathAst( e ); e.$syntax = 'after-exists';
3831
3831
  $.expr.args.push( e ); this.attachLocation( $.expr ); }continue
3832
3832
  case'?':if(this.c(651)){ this.csnParseOnly( 'syntax-unsupported-param', -1, { '#': 'dynamic', code: '?' } );
3833
3833
  $.expr.args.push( { param: this.valueWithLocation(), scope: 'param' } ); this.attachLocation( $.expr ); }continue
@@ -596,7 +596,7 @@
596
596
  "Property",
597
597
  "Parameter"
598
598
  ],
599
- "Type": "Edm.String"
599
+ "Type": "Edm.PrimitiveType"
600
600
  },
601
601
  "Common.FieldControl": {
602
602
  "AppliesTo": [
@@ -910,6 +910,13 @@
910
910
  "$deprecationText": "Use terms [Aggregation.RecursiveHierarchy](https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Aggregation.V1.md#RecursiveHierarchy) and [Hierarchy.RecursiveHierarchy](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#RecursiveHierarchy) instead",
911
911
  "Type": "Common.RecursiveHierarchyType"
912
912
  },
913
+ "Common.ReferentialConstraint": {
914
+ "$experimental": true,
915
+ "AppliesTo": [
916
+ "NavigationProperty"
917
+ ],
918
+ "Type": "Collection(Common.ReferentialConstraintType)"
919
+ },
913
920
  "Common.RelatedRecursiveHierarchy": {
914
921
  "AppliesTo": [
915
922
  "Property"
@@ -3345,6 +3352,14 @@
3345
3352
  "NodeDrillStateProperty": "Edm.PropertyPath"
3346
3353
  }
3347
3354
  },
3355
+ "Common.ReferentialConstraintType": {
3356
+ "$experimental": true,
3357
+ "$kind": "ComplexType",
3358
+ "Properties": {
3359
+ "Property": "Edm.PropertyPath",
3360
+ "ReferencedProperty": "Edm.PropertyPath"
3361
+ }
3362
+ },
3348
3363
  "Common.SAPObjectNodeTypeType": {
3349
3364
  "$experimental": true,
3350
3365
  "$kind": "ComplexType",
@@ -1682,15 +1682,13 @@ function exprOrString( val, spec ) {
1682
1682
  : expr( val, spec );
1683
1683
  }
1684
1684
 
1685
- // mark path argument of 'exists' predicate with $expected:'exists'
1685
+ // mark path argument of 'exists' predicate with $syntax:'after-exists'
1686
1686
  function exprArgs( cond, spec ) {
1687
1687
  const rxsn = arrayOf( exprOrString )( cond, spec );
1688
- // TODO: do that in definer.js, neither here nor in CDL parser
1689
1688
  if (Array.isArray( rxsn )) {
1690
- for (let i = 0; i < rxsn.length - 1; i++) {
1691
- // TODO: disallow param ref - write test
1692
- if (cond[i] === 'exists' && rxsn[i + 1].path)
1693
- rxsn[++i].$expected = 'exists';
1689
+ for (let i = 0; i < rxsn.length - 1;) {
1690
+ if (cond[i++] === 'exists' && rxsn[i].path)
1691
+ rxsn[i].$syntax = 'after-exists';
1694
1692
  }
1695
1693
  }
1696
1694
  return rxsn;
@@ -1172,15 +1172,15 @@ function enumValueOrCalc( v, csn, node, prop ) {
1172
1172
  if (node.kind === 'enum') {
1173
1173
  Object.assign( csn, expression( v ) );
1174
1174
  }
1175
- else if (v === true && prop === '$calc') {
1176
- return true; // calculation has used non-projected elements
1175
+ else if (prop === '$calc') {
1176
+ return v === true || expression( v );
1177
1177
  }
1178
1178
  // In XSN, there are combined elem/column objects: do not represent column
1179
1179
  // expression when presented as element in CSN
1180
1180
 
1181
1181
  // node.$syntax set in define.el(!), but not inside an `extend`, a _parent might
1182
1182
  // not be set always for parse-only, especially with CSN input
1183
- else if (node.$syntax === 'calc' || prop === '$calc' ||
1183
+ else if (node.$syntax === 'calc' ||
1184
1184
  !node._parent || node._parent.kind === 'extend') {
1185
1185
  const stored = v.stored ? { stored: value(v.stored) } : {};
1186
1186
  return Object.assign( stored, expression( v ) );
@@ -245,9 +245,10 @@ const referenceSemantics = {
245
245
  inline: { lexical: justDollar, dynamic: 'inline' }, // ...using baseEnv
246
246
  ref_where: { lexical: justDollar, dynamic: 'ref-target' }, // ...using baseEnv
247
247
  on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
248
+ // there are also 'on_join' and 'on_mixin' with default semantics
249
+ $calc: { lexical: justDollar, dynamic: 'query' }, // calculation for draft
248
250
  annotation: { lexical: justDollar, dynamic: 'query' }, // anno top-level `ref`
249
251
  annotationExpr: { lexical: justDollar, dynamic: 'query' }, // annotation assignment
250
- // there are also 'on_join' and 'on_mixin' with default semantics
251
252
  orderBy_ref: { lexical: query => query, dynamic: 'query' },
252
253
  orderBy_expr: { lexical: query => query, dynamic: 'source' }, // ref in ORDER BY expression
253
254
  orderBy_set_ref: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
@@ -519,6 +520,8 @@ function csnRefs( csn, universalReady ) {
519
520
  * Return the object pointing to by the artifact reference (in 'type',
520
521
  * 'includes', 'target'). For `from`, use artifactRefFrom()!
521
522
  *
523
+ * Warning: do not use it for the binding parameter if the ref is `$self`.
524
+ *
522
525
  * @param {CSN.ArtifactReferencePath|string} ref
523
526
  * @param {any} [notFound] Value that is returned in case the artifact reference
524
527
  * could not be found.
@@ -612,9 +615,16 @@ function csnRefs( csn, universalReady ) {
612
615
 
613
616
  function getOriginRaw( art ) {
614
617
  if (art.type) { // TODO: make robust against "linked" = only direct
615
- return (art.type !== '$self' || csn.definitions.$self)
616
- ? artifactRef( art.type, BUILTIN_TYPE )
617
- : getCache( boundActionOrMain( art ), '_parent' );
618
+ if (art.type !== '$self')
619
+ return artifactRef( art.type, BUILTIN_TYPE );
620
+ const action = boundActionOrMain( art );
621
+ const binding = action?.params && Object.values( action.params )[0];
622
+ // binding parameter must be typed with `$self` or `many $self`:
623
+ const entity = binding && (art === binding || art === binding.items) &&
624
+ getCache( action, '_parent' );
625
+ // if name-deprecated-$self is a non-config error, this could be simplified to:
626
+ // const entity = action?.params && getCache( action, '_parent' );
627
+ return entity || artifactRef( art.type, BUILTIN_TYPE );
618
628
  }
619
629
  if (typeof art.$origin === 'object') // null, […], {…}
620
630
  return getOriginExplicit( art.$origin );
@@ -798,7 +808,7 @@ function csnRefs( csn, universalReady ) {
798
808
  }
799
809
 
800
810
  if (!qcache)
801
- throw new CompilerAssertion( `Query not in cache at: ${ locationString(query.$location) }` );
811
+ throw new CompilerAssertion( `For semantics '${ refCtx }', query not in cache at: ${ locationString(query.$location) }` );
802
812
 
803
813
  if (semantics.dynamic === 'query') {
804
814
  // TODO: for ON condition in expand, would need to use cached _element
@@ -1066,6 +1076,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1066
1076
  if (refCtx === 'annotation' && typeof obj === 'object') {
1067
1077
  // we do not know yet whether the annotation value is an expression or not →
1068
1078
  // loop over outer array and records (structure values):
1079
+ // TODO: add test → `@(A: [{target: ($user)}])` on query column
1069
1080
  if (Array.isArray( obj ) || !isAnnotationExpression( obj )) {
1070
1081
  obj = obj[prop];
1071
1082
  continue;
@@ -1155,6 +1166,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1155
1166
  else if (prop[0] === '@') {
1156
1167
  refCtx = 'annotation';
1157
1168
  }
1169
+ else if (prop === '$calc') {
1170
+ refCtx = '$calc';
1171
+ }
1158
1172
  else if (prop !== 'xpr' && prop !== 'list') {
1159
1173
  // 'xpr' and 'list' do not change the ref context, all other props do:
1160
1174
  refCtx = prop;
@@ -69,7 +69,6 @@ function enrichCsn( csn, options = {} ) {
69
69
  // options.enrichCsn = 'DEBUG';
70
70
  let $$cacheObjectNumber = 0; // for debugging
71
71
  const debugLocationInfo = options.enrichCsn === 'DEBUG' && Object.create(null);
72
- const special$self = !csn.definitions.$self && '$self';
73
72
 
74
73
  setLocations( csn, false, null );
75
74
  const {
@@ -180,7 +179,10 @@ function enrichCsn( csn, options = {} ) {
180
179
  parent[`_${ prop }`] = ref.map( r => refLocation( artifactRef( r, notFound ) ) );
181
180
  }
182
181
  else if (typeof ref === 'string') {
183
- if (!ref.startsWith( 'cds.') && ref !== special$self)
182
+ // Don't use `artifactRef( 'self' )` for binding parameter. Because it is not
183
+ // easily recognizable here, always omit `_type` property for `$self` even if
184
+ // there is a type named `$self` (which would induce a configurable error):
185
+ if (!ref.startsWith( 'cds.') && ref !== '$self')
184
186
  parent[`_${ prop }`] = refLocation( artifactRef( ref, notFound ) );
185
187
  }
186
188
  else if (!ref.elements) {
@@ -2,11 +2,13 @@
2
2
 
3
3
  /* eslint @stylistic/max-len: 0 */
4
4
 
5
- // Remarks:
6
- // - The specification is client-tool centric (bin/cdsc.js):
5
+ // Remarks (`git blame` on these remarks does not reveal the options designer)
6
+ // - The specification is client-tool centric (../bin/cdsc.js):
7
7
  // an option named `fooBar` is “produced” by `.option(' --foo-bar')`.
8
8
  // - Also list the option in the `help` text, used with `cdsc -h`.
9
- // - Specify valid values for non-boolean options in lib/api/validate.js.
9
+ // - All options must also be added to ./api/options.js.
10
+ // - Specify valid values for non-boolean options in ./api/validate.js.
11
+ // - Beta and deprecated options are specified in ./base/model.js.
10
12
 
11
13
  'use strict';
12
14
 
@@ -54,7 +56,8 @@ optionProcessor
54
56
  .option(' --add-texts-language-assoc')
55
57
  .option(' --localized-without-coalesce')
56
58
  .option(' --tenant-discriminator')
57
- .option(' --no-composition-includes')
59
+ .option(' --no-composition-includes') // TODO: missed to be removed in #13762
60
+ .option(' --no-dollar-calc') // for grep: option `noDollarCalc`
58
61
  .option(' --default-binary-length <length>')
59
62
  .option(' --default-string-length <length>')
60
63
  .option(' --struct-xpr')
@@ -157,6 +160,7 @@ optionProcessor
157
160
  to "sap.common.Languages" if it exists
158
161
  --localized-without-coalesce Omit coalesce in localized convenience views
159
162
  --no-composition-includes Do NOT add named aspects to 'includes' property of generated composition entity.
163
+ --no-dollar-calc Don't set $calc property in CSN
160
164
  --no-recompile Don't recompile in case of internal errors
161
165
  --struct-xpr Write structured expressions to the compiler CSN output (possibly then
162
166
  used as input for backends)
@@ -1294,11 +1294,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
1294
1294
 
1295
1295
  // Set operation may also have an ORDER BY and LIMIT/OFFSET (in contrast to the ones belonging to
1296
1296
  // each SELECT)
1297
- // If the whole SET has an ORDER BY/LIMIT, wrap the part before that in parentheses
1298
- // (otherwise some SQL implementations (e.g. sqlite) would interpret the ORDER BY/LIMIT as belonging
1299
- // to the last SET argument, not to the whole SET)
1300
1297
  if (query.SET.orderBy || query.SET.limit) {
1301
- result = `(${ result })`;
1302
1298
  if (query.SET.orderBy) {
1303
1299
  const orderBy = query.SET.orderBy.map(entry => renderOrderByEntry(entry, env.withSubPath([ 'orderBy' ]))).join(', ');
1304
1300
  result += `\n${ env.indent }ORDER BY ${ orderBy }`;
@@ -198,10 +198,11 @@ function _artifactIsProjectionView( csn, artifact ) {
198
198
  referencedElements[column.ref.at(-1)] = true;
199
199
  }
200
200
 
201
- // Full primary key needs to be projected according to HANA SQL spec
201
+ // Full primary key/not null chain needs to be projected according to HANA SQL spec
202
+ // https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/create-projection-view-statement-data-definition
202
203
  for (const elementName in source.elements) {
203
204
  const element = source.elements[elementName];
204
- if (element.key && !referencedElements[elementName])
205
+ if ((element.notNull || element.key) && !referencedElements[elementName])
205
206
  return false;
206
207
  }
207
208
 
@@ -211,7 +211,7 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
211
211
  *
212
212
  * @param {object | Array} node the thing that has _prop
213
213
  * @param {string|number} _prop the name of the current property
214
- * @param {any} _path The value of node[_prop]
214
+ * @param {Array} _path The value of node[_prop]
215
215
  */
216
216
  function pathRef( node, _prop, _path ) {
217
217
  csnPath.push( _prop );
@@ -122,7 +122,7 @@ function processAssertUnique( csn, options, messageFunctions ) {
122
122
  * Check strictly that annotation value is an array
123
123
  * and that the individual array entries are references
124
124
  *
125
- * @param {any} val Annotation value
125
+ * @param {object} val Annotation value
126
126
  * @param {string} propName
127
127
  * @returns {Array} Array of paths
128
128
  */
@@ -149,8 +149,8 @@ function processAssertUnique( csn, options, messageFunctions ) {
149
149
  /**
150
150
  * Convert a ref object to a path string
151
151
  *
152
- * @param {any} v
153
- * @returns {string|string[]|any}
152
+ * @param {CSN.Ref | object} v
153
+ * @returns {string|string[]|object}
154
154
  */
155
155
  function unref( v ) {
156
156
  if (Array.isArray(v))
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Normalize a queries `from` clause by moving the infix filters on the last step of a `SELECT.from.ref` path.
5
+ *
6
+ * For the given query/projection, if the terminal ref element is an object
7
+ * containing a `where` (syntactic sugar), its condition is moved into `SELECT.where`.
8
+ * - If `SELECT.where` already exists, both are combined as: { xpr: [ oldWhere ] } AND { xpr: [ leafWhere ] }.
9
+ * - Otherwise the leaf `where` becomes the `SELECT.where`.
10
+ * The ref leaf object is then replaced by its plain `id`, removing the inline filter.
11
+ *
12
+ * Mutates the provided query in place.
13
+ *
14
+ * @param {CSN.Query} query - The query to normalize.
15
+ */
16
+ function normalizeFromLeaf( query ) {
17
+ const where = query.SELECT?.from.ref?.at(-1).where;
18
+ if (!where || where.length === 0)
19
+ return;
20
+ if (query.SELECT.where)
21
+ query.SELECT.where = [ { xpr: [ ...query.SELECT.where ] }, 'AND', { xpr: [ ...where ] } ];
22
+ else
23
+ query.SELECT.where = where;
24
+
25
+ // preserve `args`
26
+ if (query.SELECT?.from.ref?.at(-1).args)
27
+ delete query.SELECT.from.ref[query.SELECT.from.ref.length - 1].where;
28
+ else
29
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] = query.SELECT.from.ref.at(-1).id;
30
+ }
31
+
32
+
33
+ module.exports = normalizeFromLeaf;
@@ -1,9 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const { forAllQueries, forEachDefinition, walkCsnPath } = require('../../../model/csnUtils');
3
+ const {
4
+ forAllQueries, forEachDefinition, walkCsnPath, transformExpression,
5
+ } = require('../../../model/csnUtils');
4
6
  const { setProp } = require('../../../base/model');
5
7
  const { getHelpers } = require('./utils');
6
8
 
9
+ const normalizeFromLeaf = require('./normalizeFrom');
10
+
7
11
  /**
8
12
  * Turn a `exists assoc[filter = 100]` into a `exists (select 1 as dummy from assoc.target where <assoc on condition> and assoc.target.filter = 100)`.
9
13
  *
@@ -62,6 +66,9 @@ function handleExists( csn, options, messageFunctions, csnUtils ) {
62
66
  forEachDefinition(csn, (artifact, artifactName) => {
63
67
  if (artifact.query || artifact.projection) {
64
68
  forAllQueries(artifact.query || { SELECT: artifact.projection }, function handleExistsQuery(query, path) {
69
+ // Normalize infix filters on the last step of a `SELECT.from.ref` path.
70
+ // By doing this before the exists transformation, we enable `SELECT * FROM Author:books[exists genre]`
71
+ normalizeFromLeaf(query);
65
72
  if (!generatedExists.has(query)) {
66
73
  const toProcess = []; // Collect all expressions we need to process here
67
74
  if (query.SELECT?.where?.length > 1)
@@ -77,8 +84,12 @@ function handleExists( csn, options, messageFunctions, csnUtils ) {
77
84
  toProcess.push([ path.slice(0, -1), path.concat([ 'from', 'on' ]) ]);
78
85
 
79
86
  for (const [ , exprPath ] of toProcess) {
80
- const expr = nestExists(exprPath);
81
- walkCsnPath(csn, exprPath.slice(0, -1))[exprPath[exprPath.length - 1]] = expr;
87
+ const token = walkCsnPath(csn, exprPath);
88
+ transformExpression(token, null, {
89
+ ref: (container, prop, ref, pathToRef) => {
90
+ nestExists(pathToRef.slice(0, -1));
91
+ },
92
+ }, exprPath);
82
93
  }
83
94
 
84
95
  while (toProcess.length > 0) {
@@ -255,7 +255,7 @@ function getHelpers( csn, inspectRef, error ) {
255
255
  /**
256
256
  * Run Object.assign on all of the passed in parameters and delete a .as and .key at the end
257
257
  *
258
- * @param {...any} args
258
+ * @param {...object} args
259
259
  * @returns {object} The merged args without an .as and .key property
260
260
  */
261
261
  function assignAndDeleteAsAndKey( ...args ) {
@@ -24,7 +24,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
24
24
  /**
25
25
  * @param {CSN.Artifact} artifact
26
26
  * @param {string} artifactName
27
- * @param {any} dummy unused Parameter
27
+ * @param {object} dummy unused Parameter
28
28
  * @param {CSN.Path} path
29
29
  */
30
30
  function transformSelfInBacklinks( artifact, artifactName, dummy, path ) {
@@ -164,7 +164,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
164
164
  * Return the condition to replace the comparison `<assocOp> = $self` in the ON-condition
165
165
  * of element <elem> of artifact 'art'. If there is anything to complain, use location <loc>
166
166
  *
167
- * @param {any} assocOp
167
+ * @param {CSN.Ref} assocOp
168
168
  * @param {CSN.Element} assoc
169
169
  * @param {string} assocName
170
170
  * @param {CSN.Element} elem
@@ -224,7 +224,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
224
224
  * keys in this artifact.
225
225
  * For example, `ON elem.ass = $self` becomes `ON elem.ass_key1 = key1 AND elem.ass_key2 = key2`
226
226
  * (assuming that `ass` has the foreign keys `key1` and `key2`)
227
- * @param {any} assocOp
227
+ * @param {CSN.Ref} assocOp
228
228
  * @param {CSN.Element} assoc
229
229
  * @param {string} originalAssocName
230
230
  * @param {string} elemName
@@ -276,7 +276,7 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
276
276
  * For example, `ON elem.ass = $self` becomes `ON a = elem.x AND b = elem.y`
277
277
  * (assuming that `ass` has the ON-condition `ON ass.a = x AND ass.b = y`)
278
278
  *
279
- * @param {any} assocOp
279
+ * @param {CSN.Ref} assocOp
280
280
  * @param {CSN.Element} assoc
281
281
  * @param {string} originalAssocName
282
282
  * @param {string} elemName
@@ -9,7 +9,7 @@ const {
9
9
  const { recurseElements } = require('../transformUtils');
10
10
 
11
11
  /**
12
- * Return a callback function for forEachDefinition that marks artifacts that are abstract or @cds.persistence.exists/skip
12
+ * Return a callback function for forEachDefinition that marks artifacts that are abstract or `@cds.persistence.exists/skip`
13
13
  * with $ignore.
14
14
  *
15
15
  * @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback function for forEachDefinition
@@ -31,7 +31,7 @@ function getAnnoProcessor() {
31
31
 
32
32
  /**
33
33
  * Return a callback function for forEachDefinition that marks associations with $ignore
34
- * if their target does not reach the database, i.e. marked with @cds.persistence.skip or is abstract
34
+ * if their target does not reach the database, i.e. marked with `@cds.persistence.skip` or is abstract
35
35
  *
36
36
  * @param {CSN.Model} csn
37
37
  * @param {CSN.Options} options
@@ -48,7 +48,7 @@ function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
48
48
 
49
49
  return ignoreAssociationToSkippedTarget;
50
50
  /**
51
- * Associations that target a @cds.persistence.skip artifact must be removed
51
+ * Associations that target a `@cds.persistence.skip` artifact must be removed
52
52
  * from the persistence model
53
53
  *
54
54
  * @param {CSN.Artifact} artifact
@@ -94,7 +94,7 @@ function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
94
94
  }
95
95
 
96
96
  /**
97
- * Return a callback function for forEachDefinition that handles artifacts marked with @cds.persistence.table.
97
+ * Return a callback function for forEachDefinition that handles artifacts marked with `@cds.persistence.table`.
98
98
  * If a .query artifact has this annotation, the .query will be deleted and it will be treated like a table.
99
99
  *
100
100
  * @param {CSN.Model} csn
@@ -239,7 +239,7 @@ function createReferentialConstraints( csn, options ) {
239
239
  * The following decision table reflects the current implementation:
240
240
  *
241
241
  * +-----------------+--------------------+-------------------+----------+
242
- * | Global Switch: | Global Check Type: | @assert.integrity | Generate |
242
+ * | Global Switch: | Global Check Type: | `@assert.integrity` | Generate |
243
243
  * |"assertIntegrity"| "assertIntegrityType"| | Constraint|
244
244
  * +-----------------+--------------------+-------------------+----------+
245
245
  * | on | RT | false | no |
@@ -345,7 +345,7 @@ function createReferentialConstraints( csn, options ) {
345
345
 
346
346
  /**
347
347
  * if global checks are 'individual' we evaluate every association,
348
- * we create db constraints if it is annotated with @assert.integrity: 'DB' (or true)
348
+ * we create db constraints if it is annotated with `@assert.integrity`: 'DB' (or true)
349
349
  *
350
350
  * @returns {boolean}
351
351
  */
@@ -388,7 +388,7 @@ function createReferentialConstraints( csn, options ) {
388
388
  /**
389
389
  * if global checks are on and global integrity check type is 'DB'
390
390
  * we create db constraints in any case except if annotated
391
- * with @assert.integrity: 'RT' (or false, but that is rejected earlier)
391
+ * with `@assert.integrity: 'RT'` (or false, but that is rejected earlier)
392
392
  *
393
393
  * @returns {boolean}
394
394
  */
@@ -397,7 +397,7 @@ function createReferentialConstraints( csn, options ) {
397
397
  }
398
398
 
399
399
  /**
400
- * Convenience to check if value of element's @assert.integrity annotation
400
+ * Convenience to check if value of element's `@assert.integrity` annotation
401
401
  * is the same as a given value. `@assert.integrity`-value checks do not use the "truthy"-semantics,
402
402
  * since string values _and_ booleans are allowed, but are treated differently.
403
403
  *
@@ -124,7 +124,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
124
124
  }
125
125
 
126
126
  /**
127
- * Turn .expand/.inline into normal refs. @cds.persistence.skip .expand with to-many (and all transitive views).
127
+ * Turn .expand/.inline into normal refs. `@cds.persistence.skip` .expand with to-many (and all transitive views).
128
128
  * For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
129
129
  */
130
130
  function rewriteExpandInline() {
@@ -269,7 +269,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
269
269
 
270
270
  /**
271
271
  * Mark the given artifact and all (transitively) dependent artifacts as `toDummify`.
272
- * This means that they will be replaced with simple dummy views in @dummify
272
+ * This means that they will be replaced with simple dummy views in `@dummify`
273
273
  *
274
274
  * @param {CSN.Artifact} artifact
275
275
  * @param {string} name
@@ -544,7 +544,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
544
544
  }
545
545
 
546
546
  /**
547
- * Create a simple dummy view marked with @cds.persistence.skip
547
+ * Create a simple dummy view marked with `@cds.persistence.skip`
548
548
  *
549
549
  * @param {string} source
550
550
  * @returns {CSN.Artifact}
@@ -646,14 +646,14 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
646
646
  * leafs `a_b` and `a_c`, if `art` has elements `b` and `c`.
647
647
  * @param {string[]} colTypeRef
648
648
  * Expanded type for the column. Basically the path to the to-be-expanded `art`.
649
- * @param {(currentRef: any[], currentAlias: string[]) => object} leafCallback
649
+ * @param {(currentRef: CSN.Ref, currentAlias: string[]) => object} leafCallback
650
650
  * Callback when leaf nodes are reached. currentRef is the type reference for the expanded
651
651
  * column. currentAlias is the columns calculated alias.
652
652
  * @returns {object[]}
653
653
  */
654
654
  function _expandStructCol( art, colName, colTypeRef, leafCallback ) {
655
655
  const expanded = [];
656
- /** @type {Array<[CSN.Element, any[], string[]]>} */
656
+ /** @type {Array<[CSN.Element, CSN.Column, string[]]>} */
657
657
  const stack = [ [ art, colTypeRef, [ colName ] ] ];
658
658
  while (stack.length > 0) {
659
659
  const [ current, currentRef, currentAlias ] = stack.pop();
@@ -468,9 +468,9 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFun
468
468
  *
469
469
  * If a structure contains an assoc, this will also be resolved and vice versa
470
470
  *
471
- * @param {*} assoc
472
- * @param {*} assocName
473
- * @param {*} path
471
+ * @param {CSN.Association} assoc
472
+ * @param {string} assocName
473
+ * @param {CSN.Path} path
474
474
  */
475
475
  function flattenFKs( assoc, assocName, path ) {
476
476
  if (!assoc.keys)
@@ -697,7 +697,6 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFun
697
697
  * @returns {Array[]} First element of every sub-array is the foreign key name, second is the foreign key definition
698
698
  */
699
699
  function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathDelimiter, lvl = 0, originalKey = { }) {
700
- const special$self = !csn?.definitions?.$self && '$self';
701
700
  const isInspectRefResult = !Array.isArray(path);
702
701
 
703
702
  let fks = [];
@@ -707,7 +706,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
707
706
  let finalElement = element;
708
707
  let finalTypeName; // TODO: Find a way to not rely on $path?
709
708
  // TODO: effectiveType's return value is 'path' for the next inspectRef
710
- if (element.type && !isBuiltinType(element.type) && element.type !== special$self) {
709
+ if (element.type && !isBuiltinType(element.type)) {
711
710
  const tmpElt = csnUtils.effectiveType(element);
712
711
  // effective type resolves to structs and enums only but not scalars
713
712
  if (Object.keys(tmpElt).length) {
@@ -141,9 +141,9 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
141
141
  /**
142
142
  * @param {object} parent
143
143
  * @param {string} prop
144
- * @param ref
145
- * @param p
146
- * @param root
144
+ * @param {CSN.Ref} ref
145
+ * @param {CSN.Path} p
146
+ * @param {CSN.Element} root
147
147
  */
148
148
  function transformRef(parent, prop, ref, p, root) {
149
149
  const {