@sap/cds-compiler 4.0.2 → 4.2.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 (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- /**
3
+ /*
4
4
  * Module for general (partial) CSN looper functions, respecting dictionaries and allowing
5
5
  * to pass custom callbacks for certain properties like "ref".
6
6
  *
@@ -10,6 +10,8 @@
10
10
  *
11
11
  * @module lib/transform/db/applyTransformations
12
12
  */
13
+
14
+
13
15
  const { setProp } = require('../../base/model');
14
16
 
15
17
 
@@ -18,11 +20,17 @@ const { setProp } = require('../../base/model');
18
20
  * @param {string} prop The property of parent to start at
19
21
  * @param {object} customTransformers Map of prop to transform and function to apply
20
22
  * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
21
- * @param {applyTransformationsOptions} [options={}]
23
+ * @param {applyTransformationsOptions} [_options={}]
22
24
  * @param {CSN.Path} path Path to parent
23
25
  * @returns {object} parent with transformations applied
24
26
  */
25
- function applyTransformationsInternal( parent, prop, customTransformers, artifactTransformers, options, path = [] ) {
27
+ function applyTransformationsInternal( parent, prop, customTransformers, artifactTransformers, _options, path = [] ) {
28
+ const options = { ..._options };
29
+ if (!options.skipStandard)
30
+ options.skipStandard = { $tableConstraints: true };
31
+ else if (options.skipStandard.$tableConstraints === undefined)
32
+ options.skipStandard = { ...options.skipStandard, ...{ $tableConstraints: true } };
33
+
26
34
  const transformers = {
27
35
  elements: dictionary,
28
36
  definitions: dictionary,
@@ -43,23 +51,24 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
43
51
  standard( parent, name, parent[name] );
44
52
  }
45
53
  else {
46
- standard(parent, prop, parent[prop]);
54
+ standard( parent, prop, parent[prop] );
47
55
  }
48
56
  return parent;
49
57
 
50
58
  /**
51
- * Default transformer for things that are not dictionaries, like "type" or "keys".
59
+ * Default transformer for things that are not dictionaries or dictionary entries,
60
+ * such as "type" or "keys".
52
61
  * The customTransformers are applied here (and only here).
53
62
  *
54
63
  * @param {object | Array} _parent the thing that has _prop
55
- * @param {string|number} _prop the name of the current property
64
+ * @param {string|number} _prop the name of the current property or index
56
65
  * @param {object} node The value of node[_prop]
57
66
  */
58
67
  function standard( _parent, _prop, node ) {
59
68
  if (!node || typeof node !== 'object' ||
60
69
  !{}.propertyIsEnumerable.call( _parent, _prop ) ||
61
70
  (typeof _prop === 'string' && _prop.startsWith('@')) ||
62
- (options.skipIgnore && node._ignore) ||
71
+ (options.skipIgnore && node.$ignore) ||
63
72
  options.skipStandard?.[_prop]
64
73
  )
65
74
  return;
@@ -75,13 +84,35 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
75
84
  const trans = transformers[name] || standard;
76
85
  if (customTransformers[name])
77
86
  customTransformers[name](node, name, node[name], csnPath, _parent, _prop);
78
-
79
87
  trans( node, name, node[name], csnPath );
80
88
  }
81
89
  }
82
90
  csnPath.pop();
83
91
  }
84
92
 
93
+ /**
94
+ * Transformer for dictionary entries. Similar to standard(), but does not filter
95
+ * based on the given name. Otherwise, `options.skipStandards` could accidentally skip
96
+ * dictionary entries (e.g. entity called `@Name`).
97
+ *
98
+ * @param {object | Array} dict the thing that has _prop
99
+ * @param {string|number} entryName the name of the current property
100
+ * @param {object} node The value of node[_prop]
101
+ */
102
+ function dictEntry( dict, entryName, node ) {
103
+ if (!node || typeof node !== 'object' || (options.skipIgnore && node.$ignore))
104
+ return;
105
+
106
+ csnPath.push( entryName );
107
+ for (const name of Object.getOwnPropertyNames( node )) {
108
+ const trans = transformers[name] || standard;
109
+ if (customTransformers[name])
110
+ customTransformers[name](node, name, node[name], csnPath, dict);
111
+ trans( node, name, node[name], csnPath );
112
+ }
113
+ csnPath.pop();
114
+ }
115
+
85
116
  /**
86
117
  * Transformer for things that are dictionaries - like "elements".
87
118
  *
@@ -90,12 +121,12 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
90
121
  * @param {object} dict The value of node[_prop]
91
122
  */
92
123
  function dictionary( node, _prop, dict ) {
93
- // Allow skipping dicts like actions in forRelationalDB
94
- if (options.skipDict && options.skipDict[_prop] || dict === null || dict === undefined) // with universal CSN, dicts might be null
124
+ // Allow skipping dictionaries like actions in forRelationalDB
125
+ if (options.skipDict?.[_prop] || dict == null) // with universal CSN, dictionaries might be null
95
126
  return;
96
127
  csnPath.push( _prop );
97
128
  for (const name of Object.getOwnPropertyNames( dict ))
98
- standard( dict, name, dict[name] );
129
+ dictEntry( dict, name, dict[name] );
99
130
 
100
131
  if (!Object.prototype.propertyIsEnumerable.call( node, _prop ))
101
132
  setProp(node, `$${_prop}`, dict);
@@ -112,13 +143,9 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
112
143
  function definitions( node, _prop, dict ) {
113
144
  csnPath.push( _prop );
114
145
  for (const name of Object.getOwnPropertyNames( dict )) {
115
- const skip = (options && options.allowArtifact && !options.allowArtifact(dict[name], name)) ||
116
- (options && options.skipArtifact && options.skipArtifact(dict[name], name)) ||
117
- (options && options.skip && options.skip.includes(dict[name].kind)) ||
118
- false;
119
- if (!skip) {
146
+ if (!isArtifactSkipped( dict, name )) {
120
147
  artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
121
- standard( dict, name, dict[name] );
148
+ dictEntry( dict, name, dict[name] );
122
149
  }
123
150
  }
124
151
  if (!Object.prototype.propertyIsEnumerable.call( node, _prop ))
@@ -126,6 +153,20 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
126
153
  csnPath.pop();
127
154
  }
128
155
 
156
+ /**
157
+ * Whether the given artifact `dict[name]` is skipped via options.
158
+ *
159
+ * @param {object} dict
160
+ * @param {string} name
161
+ * @returns {boolean}
162
+ */
163
+ function isArtifactSkipped( dict, name ) {
164
+ return options && ((options.allowArtifact && !options.allowArtifact(dict[name], name)) ||
165
+ (options.skipArtifact && options.skipArtifact(dict[name], name)) ||
166
+ (options.skip?.includes(dict[name].kind))) ||
167
+ false;
168
+ }
169
+
129
170
  /**
130
171
  * Keep looping through the pathRef - because in a .ref we can have .args and .where
131
172
  *
@@ -173,7 +214,7 @@ function applyTransformations( csn, customTransformers = {}, artifactTransformer
173
214
  if (options.skipIgnore === undefined)
174
215
  options.skipIgnore = true;
175
216
 
176
- if (csn && csn.definitions)
217
+ if (csn?.definitions)
177
218
  return applyTransformationsInternal(csn, 'definitions', customTransformers, artifactTransformers, options);
178
219
  return csn;
179
220
  }
@@ -230,12 +271,12 @@ module.exports = {
230
271
 
231
272
  /**
232
273
  * @typedef {object} applyTransformationsOptions
233
- * @property {(artifact, name) => boolean} [allowArtifact] to only allow certain artifacts
234
- * @property {(artifact, name) => boolean} [skipArtifact] to skip certain artifacts
274
+ * @property {(artifact: CSN.Artifact, name: string) => boolean} [allowArtifact] to only allow certain artifacts
275
+ * @property {(artifact: CSN.Artifact, name: string) => boolean} [skipArtifact] to skip certain artifacts
235
276
  * @property {boolean} [drillRef] whether to drill into infix/args
236
277
  * @property {string[]} [skip] skip definitions from certain kind
237
278
  * @property {object} [skipStandard] stop drill-down on certain "standard" props
238
279
  * @property {object} [skipDict] stop drill-down on certain "dictionary" props
239
- * @property {boolean} [skipIgnore=true] Whether to skip _ignore elements or not
280
+ * @property {boolean} [skipIgnore=true] Whether to skip $ignore elements or not
240
281
  * @property {boolean} [directDict=false] Implicitly set via applyTransformationsOnDictionary
241
282
  */
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { forEachDefinition, hasAnnotationValue } = require('../../model/csnUtils');
4
- const { getTransformers } = require('../transformUtilsNew');
5
- const { setProp } = require('../../base/model');
4
+ const { getTransformers } = require('../transformUtils');
6
5
  const { pathName } = require('../../compiler/utils');
7
6
 
8
7
 
@@ -97,7 +96,7 @@ function processAssertUnique( csn, options, error, info ) {
97
96
  }
98
97
  // 10) Store remaining paths (if any) in constraint dictionary
99
98
  if (flattenedPathObjects.length)
100
- constraintDict[constraintName.join('.')] = flattenedPathObjects;
99
+ constraintDict[constraintName.join('.')] = { paths: flattenedPathObjects, parentTable: artifactName };
101
100
  }
102
101
  }
103
102
 
@@ -110,9 +109,9 @@ function processAssertUnique( csn, options, error, info ) {
110
109
  // preserve dictionary in '$tableConstraints' on the artifact for path rewriting and rendering
111
110
  if (Object.keys(constraintDict).length) {
112
111
  if (!('$tableConstraints' in artifact))
113
- setProp(artifact, '$tableConstraints', Object.create(null));
112
+ artifact.$tableConstraints = Object.create(null);
114
113
 
115
- setProp(artifact.$tableConstraints, 'unique', constraintDict);
114
+ artifact.$tableConstraints.unique = constraintDict;
116
115
  }
117
116
 
118
117
  /**
@@ -286,7 +285,7 @@ function rewriteUniqueConstraints( csn, options, pathDelimiter ) {
286
285
  }
287
286
  for (const uniqueConstraint in artifact.$tableConstraints.unique) {
288
287
  // iterate over each constraint
289
- const c = uniqueConstraints[uniqueConstraint];
288
+ const c = uniqueConstraints[uniqueConstraint].paths;
290
289
  const rewrittenPaths = [];
291
290
  // and inspect each path of the constraint
292
291
  c.forEach((cpath) => {
@@ -319,7 +318,7 @@ function rewriteUniqueConstraints( csn, options, pathDelimiter ) {
319
318
  }
320
319
  });
321
320
  // preserve the rewritten and filtered paths for toSql
322
- uniqueConstraints[uniqueConstraint] = rewrittenPaths;
321
+ uniqueConstraints[uniqueConstraint] = { paths: rewrittenPaths, parentTable: uniqueConstraints[uniqueConstraint].parentTable };
323
322
 
324
323
  // now add the index for HANA CDS
325
324
  if (options.transformation === 'hdbcds') {
@@ -335,7 +334,7 @@ function rewriteUniqueConstraints( csn, options, pathDelimiter ) {
335
334
  artifact.technicalConfig.hana.indexes[uniqueConstraint] = index;
336
335
  }
337
336
  }
338
- setProp(artifact.$tableConstraints, 'unique', uniqueConstraints);
337
+ artifact.$tableConstraints.unique = uniqueConstraints;
339
338
  }
340
339
  }
341
340
  }
@@ -48,8 +48,8 @@ function attachOnConditions( csn, csnUtils, pathDelimiter ) {
48
48
  // Assemble an ON-condition with the foreign keys created in earlier steps
49
49
  const onCondParts = [];
50
50
  let joinWithAnd = false;
51
- if (elem.keys.length === 0) { // TODO: really kill instead of _ignore?
52
- elem._ignore = true;
51
+ if (elem.keys.length === 0) { // TODO: really kill instead of $ignore?
52
+ elem.$ignore = true;
53
53
  }
54
54
  else {
55
55
  for (const foreignKey of elem.keys) {
@@ -3,13 +3,13 @@
3
3
  const {
4
4
  forEachGeneric, forEachMemberRecursively, hasAnnotationValue, isPersistedOnDatabase,
5
5
  } = require('../../model/csnUtils');
6
- const transformUtils = require('../transformUtilsNew');
6
+ const transformUtils = require('../transformUtils');
7
7
 
8
8
  const exists = '@cds.persistence.exists';
9
9
 
10
10
  /**
11
11
  * Return a callback function for forEachDefinition that marks artifacts that are abstract or @cds.persistence.exists/skip
12
- * with _ignore.
12
+ * with $ignore.
13
13
  *
14
14
  * @returns {(artifact: CSN.Artifact, artifactName) => void} Callback function for forEachDefinition
15
15
  */
@@ -24,12 +24,12 @@ function getAnnoProcessor() {
24
24
  hasAnnotationValue(artifact, '@cds.persistence.skip') ||
25
25
  hasAnnotationValue(artifact, exists));
26
26
  if (ignoreArtifact)
27
- artifact._ignore = true;
27
+ artifact.$ignore = true;
28
28
  }
29
29
  }
30
30
 
31
31
  /**
32
- * Return a callback function for forEachDefinition that marks associations with _ignore
32
+ * Return a callback function for forEachDefinition that marks associations with $ignore
33
33
  * if their target does not reach the database, i.e. marked with @cds.persistence.skip or is abstract
34
34
  *
35
35
  * @param {CSN.Model} csn
@@ -72,7 +72,7 @@ function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
72
72
  }
73
73
 
74
74
  /**
75
- * Mark the given member with _ignore if it is an association/composition and it's target is unreachable.
75
+ * Mark the given member with $ignore if it is an association/composition and its target is unreachable.
76
76
  *
77
77
  * @param {CSN.Element} member
78
78
  * @param {string} memberName
@@ -81,13 +81,13 @@ function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
81
81
  */
82
82
  function ignore( member, memberName, prop, path ) {
83
83
  if (options.sqlDialect === 'hana' &&
84
- !member._ignore && member.target &&
84
+ !member.$ignore && member.target &&
85
85
  isAssocOrComposition(member) &&
86
86
  !isPersistedOnDatabase(csn.definitions[member.target])) {
87
87
  info(null, path,
88
88
  { target: member.target, anno: '@cds.persistence.skip' },
89
- 'Association has been removed as it\'s target $(TARGET) is annotated with $(ANNO)');
90
- member._ignore = true;
89
+ 'Association has been removed, as its target $(TARGET) is annotated with $(ANNO) and can\'t be rendered in SAP HANA SQL');
90
+ member.$ignore = true;
91
91
  }
92
92
  }
93
93
  }
@@ -124,7 +124,7 @@ function getPersistenceTableProcessor( csn, options, messageFunctions ) {
124
124
 
125
125
  recurseElements(artifact, [ 'definitions', artifactName ], (member, path) => {
126
126
  // All elements must have a type for this to work
127
- if (!member._ignore && !member.kind && !member.type) {
127
+ if (!member.$ignore && !member.kind && !member.type) {
128
128
  error(null, path, { anno: '@cds.persistence.table' },
129
129
  'Expecting element to have a type if view is annotated with $(ANNO)');
130
130
  }
@@ -56,6 +56,25 @@ function createReferentialConstraints( csn, options ) {
56
56
  },
57
57
  });
58
58
  }
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`) {
61
+ const { on } = element;
62
+ const target = csn.definitions[element.target];
63
+ // `texts` entities have a key named "locale"
64
+ const targetSideHasLocaleKey = target.elements.locale?.key;
65
+ if (targetSideHasLocaleKey && !skipConstraintGeneration(parent, target, { /* there is no assoc */ })) {
66
+ const sourceElements = Array.from(elementsOfSourceSide(on, elements));
67
+ const targetElements = Array.from(elementsOfTargetSide(on, target.elements));
68
+ // `texts` entities have all the keys the original entity has
69
+ const allElementsAreKeysAndHaveTheSameName = targetElements.length &&
70
+ targetElements.every(
71
+ ([ targetKey, e ]) => e.key &&
72
+ sourceElements.some(([ sourceKey, sourceElement ]) => sourceElement.key && targetKey === sourceKey )
73
+ );
74
+ if (allElementsAreKeysAndHaveTheSameName)
75
+ attachConstraintsToDependentKeys(targetElements, sourceElements, path[path.length - 1], 'texts', { texts: true });
76
+ }
77
+ }
59
78
  }
60
79
  },
61
80
  }, [], { skipIgnore: false, skipArtifact: a => a.query || a.kind !== 'entity' });
@@ -134,7 +153,7 @@ function createReferentialConstraints( csn, options ) {
134
153
  * @param {Array} parentKeys array holding parent keys in the format [['key1', 'value1'], [...], ...]
135
154
  * @param {CSN.PathSegment} parentTable the sql-table where the foreign key constraints will be pointing to
136
155
  * @param {CSN.PathSegment} sourceAssociation the name of the association from which the constraint originates
137
- * @param {CSN.PathSegment} upLinkFor the name of the composition which used this association in a `$self = <comp>.<up_>` comparison
156
+ * @param {CSN.PathSegment | object} upLinkFor the name of the composition which used this association in a `$self = <comp>.<up_>` comparison
138
157
  * it is used for a comment in the constraint, which is only printed out in test-mode
139
158
  */
140
159
  function attachConstraintsToDependentKeys( dependentKeys, parentKeys, parentTable, sourceAssociation, upLinkFor = null ) {
@@ -440,13 +459,16 @@ function createReferentialConstraints( csn, options ) {
440
459
  */
441
460
  function elementsOfTargetSide( on, targetElements ) {
442
461
  const elements = new Map();
443
- on.filter(element => typeof element === 'object' &&
444
- element.ref.length > 1 &&
445
- targetElements[element.ref[element.ref.length - 1]])
446
- .forEach((element) => {
447
- elements.set(element.ref[element.ref.length - 1], targetElements[element.ref[element.ref.length - 1]]);
448
- });
449
-
462
+ const findElements = (tokenStream) => {
463
+ tokenStream
464
+ .forEach((element) => {
465
+ if (typeof element === 'object' && element.ref?.length > 1 && targetElements[element.ref[element.ref.length - 1]])
466
+ elements.set(element.ref[element.ref.length - 1], targetElements[element.ref[element.ref.length - 1]]);
467
+ else if (element.xpr)
468
+ findElements(element.xpr);
469
+ });
470
+ };
471
+ findElements(on);
450
472
  return elements;
451
473
  }
452
474
 
@@ -459,12 +481,16 @@ function createReferentialConstraints( csn, options ) {
459
481
  */
460
482
  function elementsOfSourceSide( on, sourceElements ) {
461
483
  const elements = new Map();
462
- on.filter(element => typeof element === 'object' &&
463
- element.ref.length === 1 &&
464
- sourceElements[element.ref[0]])
465
- .forEach((element) => {
466
- elements.set(element.ref[0], sourceElements[element.ref[0]]);
467
- });
484
+ const findElements = (tokenStream) => {
485
+ tokenStream
486
+ .forEach((element) => {
487
+ if (typeof element === 'object' && element.ref?.length === 1 && sourceElements[element.ref[0]])
488
+ elements.set(element.ref[0], sourceElements[element.ref[0]]);
489
+ else if (element.xpr)
490
+ findElements(element.xpr);
491
+ });
492
+ };
493
+ findElements(on);
468
494
  return elements;
469
495
  }
470
496
 
@@ -507,12 +533,16 @@ function createReferentialConstraints( csn, options ) {
507
533
  dependentKey.push(foreignKeyName);
508
534
  onDeleteRules.add($foreignKeyConstraintCopy.onDelete);
509
535
  });
510
- // onDelete Rule is the "weakest" rule applicable. Precedence: RESTRICT > SET NULL > CASCADE
536
+ // onDelete Rule is the "weakest" rule applicable. Precedence: RESTRICT > CASCADE
511
537
  const onDelete = onDeleteRules.has('RESTRICT') ? 'RESTRICT' : 'CASCADE';
512
538
  let onDeleteRemark = null;
513
539
  // comments in sqlite files are causing the JDBC driver to throw an error on deployment
514
- if (options.testMode && onDelete === 'CASCADE')
515
- onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
540
+ if (options.testMode && onDelete === 'CASCADE') {
541
+ if ($foreignKeyConstraint.upLinkFor?.texts)
542
+ onDeleteRemark = `Constraint originates from localized composition ”${$foreignKeyConstraint.parentTable}:texts“`;
543
+ else
544
+ onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
545
+ }
516
546
  // constraint identifier usually start with `c__` to avoid name clashes
517
547
  let identifier = options.pre2134ReferentialConstraintNames ? '' : 'c__';
518
548
  identifier += `${getResultingName(csn, options.sqlMapping, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`;