@sap/cds-compiler 2.11.4 → 2.13.8

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 (133) hide show
  1. package/CHANGELOG.md +159 -1
  2. package/bin/cds_update_identifiers.js +7 -7
  3. package/bin/cdsc.js +22 -23
  4. package/bin/cdsse.js +2 -2
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +25 -6
  7. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  8. package/doc/NameResolution.md +21 -16
  9. package/lib/api/main.js +30 -63
  10. package/lib/api/options.js +5 -5
  11. package/lib/api/validate.js +0 -5
  12. package/lib/backends.js +15 -23
  13. package/lib/base/dictionaries.js +0 -8
  14. package/lib/base/error.js +26 -0
  15. package/lib/base/keywords.js +7 -17
  16. package/lib/base/location.js +9 -4
  17. package/lib/base/message-registry.js +52 -2
  18. package/lib/base/messages.js +16 -26
  19. package/lib/base/model.js +2 -62
  20. package/lib/base/optionProcessorHelper.js +246 -183
  21. package/lib/checks/.eslintrc.json +2 -0
  22. package/lib/checks/actionsFunctions.js +2 -1
  23. package/lib/checks/annotationsOData.js +1 -1
  24. package/lib/checks/cdsPersistence.js +2 -1
  25. package/lib/checks/enricher.js +17 -1
  26. package/lib/checks/foreignKeys.js +4 -4
  27. package/lib/checks/invalidTarget.js +3 -1
  28. package/lib/checks/managedInType.js +4 -4
  29. package/lib/checks/managedWithoutKeys.js +3 -1
  30. package/lib/checks/queryNoDbArtifacts.js +1 -3
  31. package/lib/checks/selectItems.js +4 -4
  32. package/lib/checks/sql-snippets.js +94 -0
  33. package/lib/checks/types.js +1 -1
  34. package/lib/checks/validator.js +12 -7
  35. package/lib/compiler/assert-consistency.js +10 -6
  36. package/lib/compiler/base.js +0 -1
  37. package/lib/compiler/builtins.js +8 -6
  38. package/lib/compiler/checks.js +46 -12
  39. package/lib/compiler/cycle-detector.js +1 -1
  40. package/lib/compiler/define.js +1103 -0
  41. package/lib/compiler/extend.js +983 -0
  42. package/lib/compiler/finalize-parse-cdl.js +231 -0
  43. package/lib/compiler/index.js +33 -14
  44. package/lib/compiler/kick-start.js +190 -0
  45. package/lib/compiler/moduleLayers.js +4 -4
  46. package/lib/compiler/populate.js +1226 -0
  47. package/lib/compiler/propagator.js +113 -47
  48. package/lib/compiler/resolve.js +1433 -0
  49. package/lib/compiler/shared.js +76 -38
  50. package/lib/compiler/tweak-assocs.js +529 -0
  51. package/lib/compiler/utils.js +204 -33
  52. package/lib/edm/.eslintrc.json +5 -0
  53. package/lib/edm/annotations/genericTranslation.js +38 -25
  54. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  55. package/lib/edm/csn2edm.js +10 -9
  56. package/lib/edm/edm.js +19 -20
  57. package/lib/edm/edmPreprocessor.js +166 -95
  58. package/lib/edm/edmUtils.js +127 -34
  59. package/lib/gen/Dictionary.json +92 -43
  60. package/lib/gen/language.checksum +1 -1
  61. package/lib/gen/language.interp +11 -1
  62. package/lib/gen/language.tokens +86 -82
  63. package/lib/gen/languageLexer.interp +18 -1
  64. package/lib/gen/languageLexer.js +925 -847
  65. package/lib/gen/languageLexer.tokens +78 -74
  66. package/lib/gen/languageParser.js +5434 -4298
  67. package/lib/json/from-csn.js +59 -17
  68. package/lib/json/to-csn.js +143 -71
  69. package/lib/language/antlrParser.js +3 -3
  70. package/lib/language/docCommentParser.js +3 -3
  71. package/lib/language/genericAntlrParser.js +144 -54
  72. package/lib/language/language.g4 +424 -203
  73. package/lib/language/multiLineStringParser.js +536 -0
  74. package/lib/main.d.ts +472 -61
  75. package/lib/main.js +38 -11
  76. package/lib/model/api.js +3 -1
  77. package/lib/model/csnRefs.js +321 -204
  78. package/lib/model/csnUtils.js +224 -263
  79. package/lib/model/enrichCsn.js +97 -40
  80. package/lib/model/revealInternalProperties.js +27 -6
  81. package/lib/model/sortViews.js +2 -1
  82. package/lib/modelCompare/compare.js +17 -12
  83. package/lib/optionProcessor.js +7 -6
  84. package/lib/render/DuplicateChecker.js +1 -1
  85. package/lib/render/manageConstraints.js +36 -33
  86. package/lib/render/toCdl.js +174 -275
  87. package/lib/render/toHdbcds.js +201 -115
  88. package/lib/render/toRename.js +7 -10
  89. package/lib/render/toSql.js +149 -75
  90. package/lib/render/utils/common.js +22 -8
  91. package/lib/render/utils/sql.js +10 -7
  92. package/lib/render/utils/stringEscapes.js +111 -0
  93. package/lib/sql-identifier.js +1 -1
  94. package/lib/transform/.eslintrc.json +5 -0
  95. package/lib/transform/braceExpression.js +4 -2
  96. package/lib/transform/db/.eslintrc.json +2 -0
  97. package/lib/transform/db/applyTransformations.js +35 -12
  98. package/lib/transform/db/assertUnique.js +1 -1
  99. package/lib/transform/db/associations.js +187 -0
  100. package/lib/transform/db/cdsPersistence.js +150 -0
  101. package/lib/transform/db/constraints.js +61 -56
  102. package/lib/transform/db/expansion.js +50 -29
  103. package/lib/transform/db/flattening.js +552 -105
  104. package/lib/transform/db/groupByOrderBy.js +3 -1
  105. package/lib/transform/db/temporal.js +236 -0
  106. package/lib/transform/db/transformExists.js +94 -28
  107. package/lib/transform/db/views.js +5 -4
  108. package/lib/transform/draft/.eslintrc.json +38 -0
  109. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  110. package/lib/transform/draft/odata.js +227 -0
  111. package/lib/transform/forHanaNew.js +94 -801
  112. package/lib/transform/forOdataNew.js +22 -175
  113. package/lib/transform/localized.js +36 -32
  114. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  115. package/lib/transform/odata/referenceFlattener.js +95 -89
  116. package/lib/transform/odata/structureFlattener.js +1 -1
  117. package/lib/transform/odata/toFinalBaseType.js +86 -12
  118. package/lib/transform/odata/typesExposure.js +5 -5
  119. package/lib/transform/odata/utils.js +2 -2
  120. package/lib/transform/transformUtilsNew.js +47 -33
  121. package/lib/transform/translateAssocsToJoins.js +10 -27
  122. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  123. package/lib/transform/universalCsn/coreComputed.js +170 -0
  124. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  125. package/lib/transform/universalCsn/utils.js +63 -0
  126. package/lib/utils/file.js +2 -1
  127. package/lib/utils/objectUtils.js +30 -0
  128. package/lib/utils/timetrace.js +8 -2
  129. package/package.json +1 -1
  130. package/share/messages/README.md +26 -0
  131. package/lib/compiler/definer.js +0 -2340
  132. package/lib/compiler/resolver.js +0 -2988
  133. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -7,9 +7,9 @@
7
7
 
8
8
  // * `File.cds:3:5` if the original CSN has a non-enumerable `$location` property
9
9
  // with value `{file: "File.cds", line: 3, col: 5}`.
10
- // * `File.cds:3:5-1` if the original CSN has _no_ `$location` property, for an
10
+ // * `File.cds:3:5^` if the original CSN has _no_ `$location` property, for an
11
11
  // inferred member of a main artifact or member with `$location: `File.cds:3:5`;
12
- // the number of digits in the `-1` suffix is the member depth.
12
+ // the number of `^`s in the suffix is the member depth.
13
13
 
14
14
  // Other enumerable properties in the JSON for non-enumerable properties in the
15
15
  // original CSN:
@@ -25,11 +25,17 @@
25
25
  // * `_type`, `_includes` and `_targets` have as values the `$location`s of the
26
26
  // referred artifacts which are returned by function `artifactRef`.
27
27
  // * `_links` and `_art` as sibling properties of `ref` have as values the
28
- // `$locations` of the artifacts/members returned by function `inspectRef`.
28
+ // `$locations` of the artifacts/members returned by function `inspectRef` (`_art`
29
+ // for ref in `from` only, where it is different to the last item of `_links`).
29
30
  // * `_scope` and `_env` as sibling properties of `ref` have (string) values,
30
31
  // returned by function `inspectRef`, giving add/ info about the “ref base”.
31
32
  // * `_origin` (in Universal CSN only) has as value the `$location` of the
32
33
  // prototype returned by function getOrigin().
34
+ // * `_test.inspect.csnpath` as sibling property of `ref` has an object value
35
+ // with properties `_links` and `_scope` of a further `inspectRef` call;
36
+ // it is only called, with `[‹art›, ...‹csnpath›]` as argument, if `‹art›`,
37
+ // which is the referred artifact returned by the first `inspectRef` call,
38
+ // has an annotation `@$test.inspect.csnpath` with array value `‹csnpath›`.
33
39
 
34
40
  'use strict';
35
41
 
@@ -55,10 +61,14 @@ function enrichCsn( csn, options = {} ) {
55
61
  // TODO: excluding
56
62
  '@': () => { /* ignore annotations */ },
57
63
  }
58
- setLocations( csn, false, null );
59
- const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
60
- csnRefs( csn );
64
+ // options.enrichCsn = 'DEBUG';
61
65
  let $$cacheObjectNumber = 0; // for debugging
66
+ const debugLocationInfo = options.enrichCsn === 'DEBUG' && Object.create(null);
67
+
68
+ setLocations( csn, false, null );
69
+ const { inspectRef, artifactRef, getOrigin, initDefinition, __getCache_forEnrichCsnDebugging } =
70
+ csnRefs( csn, true );
71
+
62
72
  const csnPath = [];
63
73
  if (csn.definitions)
64
74
  dictionary( csn, 'definitions', csn.definitions );
@@ -92,7 +102,7 @@ function enrichCsn( csn, options = {} ) {
92
102
  // call getOrigin() before standard() to set implicit protos inside standard():
93
103
  const origin = handleError( err => err ? err.toString() : getOrigin( obj ) );
94
104
  standard( parent, prop, obj );
95
- if (obj.$origin === undefined && origin != null)
105
+ if (obj.$origin === undefined && !obj.type && origin != null)
96
106
  obj._origin = refLocation( origin );
97
107
  }
98
108
 
@@ -101,6 +111,8 @@ function enrichCsn( csn, options = {} ) {
101
111
  return;
102
112
  csnPath.push( prop );
103
113
  for (let name of Object.getOwnPropertyNames( dict )) {
114
+ if (prop === 'definitions')
115
+ initDefinition( dict[name] );
104
116
  definition( dict, name, dict[name] );
105
117
  }
106
118
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
@@ -109,11 +121,19 @@ function enrichCsn( csn, options = {} ) {
109
121
  }
110
122
 
111
123
  function refLocation( art ) {
112
- if (art && typeof art === 'object')
113
- return art.$location || '<no location>';
124
+ if (!art || typeof art !== 'object' || Array.isArray( art )) {
125
+ if (!options.testMode)
126
+ return (typeof art === 'string')
127
+ ? `<illegal ref = ${art}>`
128
+ : `<illegal ref: ${typeof art}>`;
129
+ throw new Error( 'Illegal reference' );
130
+ }
131
+ else if (art.$location)
132
+ return art.$location;
133
+
114
134
  if (!options.testMode)
115
- return art || '<illegal link>';
116
- throw new Error( 'Undefined reference' );
135
+ return `<${Object.keys( art ).join('+')}+!$location>`;
136
+ throw new Error( 'Reference to object without $location' );
117
137
  }
118
138
 
119
139
  function simpleRef( parent, prop, ref ) {
@@ -142,16 +162,18 @@ function enrichCsn( csn, options = {} ) {
142
162
  handleError( err => {
143
163
  if (err)
144
164
  parent._origin = err.toString();
145
- else if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
146
- parent._origin = refLocation( getOrigin( parent, true ) );
147
- else if ( ref )
165
+ else if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […] / "short-ref"
166
+ parent._origin = refLocation( getOrigin( parent ) );
167
+ else if ( ref ) // $origin: {…}
148
168
  standard( parent, prop, ref );
149
169
  } );
150
170
  }
151
171
 
152
- function pathRef( parent, prop, path ) {
153
- const { links, art, scope, $env }
154
- = handleError( err => (err) ? { scope: err.toString() } : inspectRef( csnPath ) );
172
+ function pathRef( parent, prop, path, inspectionPath = csnPath ) {
173
+ const inspection = handleError( (err) => {
174
+ return (err) ? { scope: err.toString() } : inspectRef( inspectionPath );
175
+ });
176
+ const { links, art, scope, $env } = inspection;
155
177
  if (links)
156
178
  parent._links = links.map( l => refLocation( l.art ) );
157
179
  if (links && links[links.length-1].art !== art)
@@ -160,6 +182,15 @@ function enrichCsn( csn, options = {} ) {
160
182
  if ($env)
161
183
  parent._env = $env;
162
184
 
185
+ if (!prop) // recursive call for @$test.inspect.csnpath
186
+ return;
187
+ const testPath = art && art['@$test.inspect.csnpath'];
188
+ if (testPath && parent.ref) {
189
+ const further = {};
190
+ pathRef( further, null, null, [ inspection, ...testPath ] );
191
+ parent['_test.inspect.csnpath'] = further;
192
+ }
193
+
163
194
  csnPath.push( prop );
164
195
  path.forEach( function step( s, i ) {
165
196
  if (s && typeof s === 'object') {
@@ -225,6 +256,10 @@ function enrichCsn( csn, options = {} ) {
225
256
  obj.$$cacheObject[name] = sub;
226
257
  }
227
258
  }
259
+ else if (name === '$origin$step') { // string value handled above
260
+ const kind = Object.keys( val )[0];
261
+ obj.$$cacheObject[name] = `${kind}: ${ val[kind] }`;
262
+ }
228
263
  else if (Array.isArray( val )) {
229
264
  obj.$$cacheObject[name] = val.map( item => {
230
265
  if (!item.$$objectNumber)
@@ -240,8 +275,53 @@ function enrichCsn( csn, options = {} ) {
240
275
  }
241
276
  }
242
277
  }
278
+
279
+ function debugLocation( loc, userProvided ) {
280
+ if (debugLocationInfo && !userProvided) {
281
+ loc = loc.replace( /\([0-9]+\)\^/, '^' );
282
+ debugLocationInfo[loc] = (debugLocationInfo[loc] || 0) + 1;
283
+ loc = `${loc}(${debugLocationInfo[loc]})`;
284
+ }
285
+ return loc;
286
+ }
287
+
288
+ function setLocations( node, prop, loc ) {
289
+ if (!node || typeof node !== 'object')
290
+ return;
291
+ const isMember = artifactProperties.includes( prop );
292
+ if (!isMember && node.$location) {
293
+ if (typeof node.$location === 'string') // already set for nested 'items'
294
+ return;
295
+ loc = locationString( node.$location, true );
296
+ if (!node.SELECT) // compatibility: $location of query both inside and as sibling of SELECT
297
+ reveal( node, '$location', debugLocation( loc, !node.$generated ) );
298
+ }
299
+ else if (prop === true || prop === 'returns') { // in dictionary or returns
300
+ loc = debugLocation( loc + '^' );
301
+ node.$location = loc;
302
+ }
303
+ else if (prop === 'items') {
304
+ let iloc = loc + '[]';
305
+ let obj = node;
306
+ while (obj) {
307
+ // should not appear in --enrich-csn, only for _origin info
308
+ Object.defineProperty( obj, '$location', { value: iloc, enumerable: false } );
309
+ obj = obj.items;
310
+ iloc += '[]';
311
+ }
312
+ }
313
+ if (Array.isArray( node )) {
314
+ for (const item of node)
315
+ setLocations( item, isMember, loc );
316
+ }
317
+ else {
318
+ for (const name of Object.getOwnPropertyNames( node ))
319
+ setLocations( node[name], isMember || name, loc );
320
+ }
321
+ }
243
322
  }
244
323
 
324
+
245
325
  function reveal( node, prop, value ) {
246
326
  Object.defineProperty( node, prop, {
247
327
  value,
@@ -251,27 +331,4 @@ function reveal( node, prop, value ) {
251
331
  } );
252
332
  }
253
333
 
254
- function setLocations( node, prop, loc ) {
255
- if (!node || typeof node !== 'object')
256
- return;
257
- const isMember = artifactProperties.includes( prop );
258
- if (!isMember && node.$location) {
259
- const value = locationString( node.$location, true );
260
- reveal( node, '$location', value );
261
- loc = value + '-';
262
- }
263
- else if (prop === true) {
264
- loc += '1';
265
- node.$location = loc;
266
- }
267
- if (Array.isArray( node )) {
268
- for (const item of node)
269
- setLocations( item, isMember, loc );
270
- }
271
- else {
272
- for (const name of Object.getOwnPropertyNames( node ))
273
- setLocations( node[name], isMember || name, loc );
274
- }
275
- }
276
-
277
334
  module.exports = enrichCsn;
@@ -50,7 +50,16 @@ function tableAliasAsLink( art, parent, name ) {
50
50
  parent === art._parent.$tableAliases[name].$duplicates);
51
51
  }
52
52
 
53
- function revealInternalProperties( model, name ) {
53
+ /**
54
+ * Reveal internal properties of `model` for the given artifact name (or path).
55
+ * `path` could be a definition name or a `/`-separated XSN path such as
56
+ * `name.space/S/E/elements/a/type/scope/`.
57
+ *
58
+ * @param {XSN.Model} model
59
+ * @param {string} [nameOrPath]
60
+ * @returns {string}
61
+ */
62
+ function revealInternalProperties( model, nameOrPath ) {
54
63
  const transformers = {
55
64
  messages: m => m,
56
65
  name: shortenName,
@@ -76,6 +85,7 @@ function revealInternalProperties( model, name ) {
76
85
  $tableAliases: dictionary,
77
86
  $duplicates: duplicates,
78
87
  $keysNavigation: dictionary,
88
+ targetAspect,
79
89
  $layerNumber: n => n,
80
90
  $extra: e => e,
81
91
  _layerRepresentative: s => s.realname,
@@ -89,7 +99,7 @@ function revealInternalProperties( model, name ) {
89
99
  $messageFunctions: () => '‹some functions›',
90
100
  }
91
101
  unique_id = 1;
92
- return revealXsnPath(name, model);
102
+ return revealXsnPath(nameOrPath, model);
93
103
 
94
104
  // Returns the desired artifact/dictionary in the XSN.
95
105
  //
@@ -259,6 +269,12 @@ function revealInternalProperties( model, name ) {
259
269
  return r;
260
270
  }
261
271
 
272
+ function targetAspect( node, parent ) {
273
+ if (node.elements && unique_id && node.__unique_id__ == null)
274
+ Object.defineProperty( node, '__unique_id__', { value: ++unique_id } );
275
+ return reveal( node, parent );
276
+ }
277
+
262
278
  function duplicates( node, parent ) {
263
279
  return reveal( node, parent, parent.name && parent.name.id );
264
280
  }
@@ -272,10 +288,12 @@ function artifactIdentifier( node, parent ) {
272
288
  let outer = unique_id ? '##' + node.__unique_id__ : '';
273
289
  if (node._outer) {
274
290
  if (node.$inferred === 'REDIRECTED')
275
- outer = '/redirected';
291
+ outer = '/redirected' + outer;
276
292
  else
277
- outer = (node._outer.items === node) ? '/items'
278
- : (node._outer.returns === node) ? '/returns' : '/returns/items';
293
+ outer = (node._outer.items === node) ? '/items' + outer
294
+ : (node._outer.returns === node) ? '/returns' + outer
295
+ : (node._outer.targetAspect === node) ? '/target' + outer
296
+ : '/returns/items' + outer;
279
297
  node = node._outer;
280
298
  }
281
299
  if (node === parent)
@@ -305,7 +323,10 @@ function artifactIdentifier( node, parent ) {
305
323
  return 'source:' + quoted( node.location && node.location.file ) +
306
324
  '/using:' + quoted( node.name.id )
307
325
  default: {
308
- return ((node._main || node).kind || '<kind>') + ':' + msg.artName( node ) + outer;
326
+ let main = node._main;
327
+ while (main && main._outer) // anonymous aspect
328
+ main = main._outer._main;
329
+ return ((main || node).kind || '<kind>') + ':' + msg.artName( node ) + outer;
309
330
  }
310
331
  }
311
332
  }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
  const {setDependencies} = require('./csnUtils');
3
+ const { ModelError } = require("../base/error");
3
4
 
4
5
  /**
5
6
  * @typedef {Object} Layers
@@ -91,7 +92,7 @@ module.exports = function({sql, csn}){
91
92
  const { layers, leftover } = sortTopologically(csn, _dependents, _dependencies);
92
93
  cleanup.forEach(fn => fn());
93
94
  if(leftover.length > 0)
94
- throw new Error('Unable to build a correct dependency graph! Are there cycles?');
95
+ throw new ModelError('Unable to build a correct dependency graph! Are there cycles?');
95
96
 
96
97
  const result = [];
97
98
  // keep the "artifact name" - needed for to.hdi sorting
@@ -6,13 +6,14 @@ const {
6
6
  forEachMember,
7
7
  hasAnnotationValue
8
8
  } = require('../model/csnUtils');
9
+ const { isBetaEnabled } = require('../base/model');
9
10
 
10
11
  /**
11
12
  * Compares two models, in HANA-transformed CSN format, to each other.
12
13
  *
13
14
  * @param beforeModel the before-model
14
15
  * @param afterModel the after-model
15
- * @param {import('../api/main.js').hdiOptions|false} options
16
+ * @param {HdiOptions|false} options
16
17
  * @returns {object} the sets of deletions, extensions, and migrations of entities necessary to transform the before-model
17
18
  * to the after-model, together with all the definitions of the after-model
18
19
  */
@@ -26,8 +27,8 @@ function compareModels(beforeModel, afterModel, options) {
26
27
  const migrations = []; // element changes/removals or changes of entity properties
27
28
 
28
29
  // There is currently no use in knowing the added entities only. If this changes, hand in `addedEntities` to `getArtifactComparator` below.
29
- forEachDefinition(afterModel, getArtifactComparator(beforeModel, null, null, elementAdditions, migrations));
30
- forEachDefinition(beforeModel, getArtifactComparator(afterModel, null, deletedEntities, null, null));
30
+ forEachDefinition(afterModel, getArtifactComparator(beforeModel, options, null, null, elementAdditions, migrations));
31
+ forEachDefinition(beforeModel, getArtifactComparator(afterModel, options, null, deletedEntities, null, null));
31
32
 
32
33
  const returnObj = Object.create(null);
33
34
  returnObj.definitions = afterModel.definitions;
@@ -60,7 +61,7 @@ function validateCsnVersions(beforeModel, afterModel, options) {
60
61
  }
61
62
  }
62
63
 
63
- function getArtifactComparator(otherModel, addedEntities, deletedEntities, elementAdditions, migrations) {
64
+ function getArtifactComparator(otherModel, options, addedEntities, deletedEntities, elementAdditions, migrations) {
64
65
  return function compareArtifacts(artifact, name) {
65
66
  function addElements() {
66
67
  const elements = {};
@@ -70,7 +71,11 @@ function getArtifactComparator(otherModel, addedEntities, deletedEntities, eleme
70
71
  }
71
72
  }
72
73
  function changePropsOrRemoveOrChangeElements() {
73
- const relevantProperties = ['doc'];
74
+ const relevantProperties = [
75
+ { name: 'doc' },
76
+ { name: '@sql.prepend' },
77
+ { name: '@sql.append' },
78
+ ];
74
79
  const changedProperties = {};
75
80
 
76
81
  const removedElements = {};
@@ -79,8 +84,8 @@ function getArtifactComparator(otherModel, addedEntities, deletedEntities, eleme
79
84
  const migration = { migrate: name };
80
85
 
81
86
  relevantProperties.forEach(prop => {
82
- if (artifact[prop] !== otherArtifact[prop]) {
83
- changedProperties[prop] = changedElement(artifact[prop], otherArtifact[prop] || null);
87
+ if (artifact[prop.name] !== otherArtifact[prop.name] && (!prop.beta || isBetaEnabled(options, prop.beta))) {
88
+ changedProperties[prop.name] = changedElement(artifact[prop.name], otherArtifact[prop.name] || null);
84
89
  }
85
90
  });
86
91
  if (Object.keys(changedProperties).length > 0) {
@@ -150,7 +155,7 @@ function isPersistedAsTable(artifact) {
150
155
  && !hasAnnotationValue(artifact, '@cds.persistence.exists');
151
156
  }
152
157
 
153
- function getElementComparator(otherArtifact, addedElements = null, changedElements = null) {
158
+ function getElementComparator(otherArtifact, addedElementsDict = null, changedElementsDict = null) {
154
159
  return function compareElements(element, name) {
155
160
  if (element._ignore) {
156
161
  return;
@@ -159,19 +164,19 @@ function getElementComparator(otherArtifact, addedElements = null, changedElemen
159
164
  const otherElement = otherArtifact.elements[name];
160
165
  if (otherElement && !otherElement._ignore) {
161
166
  // Element type changed?
162
- if (!changedElements) {
167
+ if (!changedElementsDict) {
163
168
  return;
164
169
  }
165
170
  if (relevantTypeChange(element.type, otherElement.type) || typeParametersChanged(element, otherElement)) {
166
171
  // Type or parameters, e.g. association target, changed.
167
- changedElements[name] = changedElement(element, otherElement);
172
+ changedElementsDict[name] = changedElement(element, otherElement);
168
173
  }
169
174
 
170
175
  return;
171
176
  }
172
177
 
173
- if (addedElements) {
174
- addedElements[name] = element;
178
+ if (addedElementsDict) {
179
+ addedElementsDict[name] = element;
175
180
  }
176
181
  }
177
182
  }
@@ -30,8 +30,8 @@ optionProcessor
30
30
  .option(' --integrity-not-validated')
31
31
  .option(' --integrity-not-enforced')
32
32
  .option(' --assert-integrity <mode>', [ 'true', 'false', 'individual' ])
33
- .option(' --assert-integrity-type <type>', [ 'RT', 'DB' ])
34
- .option(' --constraints-as-alter <boolean>')
33
+ .option(' --assert-integrity-type <type>', [ 'RT', 'DB' ], { ignoreCase: true })
34
+ .option(' --constraints-in-create-table')
35
35
  .option(' --deprecated <list>')
36
36
  .option(' --hana-flavor')
37
37
  .option(' --direct-backend')
@@ -104,7 +104,7 @@ optionProcessor
104
104
  --integrity-not-validated If this option is supplied, referential constraints are NOT VALIDATED.
105
105
  This option is also applied to result of "cdsc manageConstraints"
106
106
  --assert-integrity <mode> Turn DB constraints on/off:
107
- true : Constraints will be generated for all associations if
107
+ true : (default) Constraints will be generated for all associations if
108
108
  the assert-integrity-type is set to DB
109
109
  false : No constraints will be generated
110
110
  individual : Constraints will be generated for selected associations
@@ -112,9 +112,9 @@ optionProcessor
112
112
  RT : (default) No database constraint for an association
113
113
  if not explicitly demanded via annotation
114
114
  DB : Create database constraints for associations
115
- --constraints-as-alter <boolean> If set to 'true', the foreign key constraints will be rendered as
116
- "ALTER TABLE ADD CONSTRAINT" statement rather than being part of the
117
- "CREATE TABLE" statement
115
+ --constraints-in-create-table If set, the foreign key constraints will be rendered as
116
+ part of the "CREATE TABLE" statements rather than as separate
117
+ "ALTER TABLE ADD CONSTRAINT" statements
118
118
  --deprecated <list> Comma separated list of deprecated options.
119
119
  Valid values are:
120
120
  noElementsExpansion
@@ -124,6 +124,7 @@ optionProcessor
124
124
  renderVirtualElements
125
125
  unmanagedUpInComponent
126
126
  createLocalizedViews
127
+ redirectInSubQueries
127
128
  --hana-flavor Compile with backward compatibility for HANA CDS (incomplete)
128
129
  --parse-only Stop compilation after parsing and write result to <stdout>
129
130
  --fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
@@ -62,7 +62,7 @@ class DuplicateChecker {
62
62
  * Add an element to the "seen"-list
63
63
  *
64
64
  * @param {string} name Rendered element name
65
- * @param {CSN.Location} location
65
+ * @param {CSN.Location|CSN.Path} location
66
66
  * @param {string} modelName CSN element name
67
67
  *
68
68
  */
@@ -5,6 +5,7 @@ const {
5
5
  forEachDefinition,
6
6
  getResultingName,
7
7
  } = require('../model/csnUtils');
8
+ const { forEach } = require('../utils/objectUtils');
8
9
 
9
10
  const {
10
11
  renderReferentialConstraint, getIdentifierUtils,
@@ -13,7 +14,7 @@ const {
13
14
  /**
14
15
  * This render middleware can be used to generate SQL DDL ALTER TABLE <table> ALTER / ADD / DROP CONSTRAINT <constraint> statements for a given CDL model.
15
16
  * Moreover, it can be used to generate .hdbconstraint artifacts.
16
- * Depending on the options.manageConstraints provided,the VALIDATED / ENFORCED flag of the constraints can be adjusted.
17
+ * Depending on the options.manageConstraints provided, the VALIDATED / ENFORCED flag of the constraints can be adjusted.
17
18
  *
18
19
  * @param {CSN.Model} csn
19
20
  * @param {CSN.Options} options
@@ -29,25 +30,24 @@ function manageConstraints(csn, options) {
29
30
  const { quoteSqlId } = getIdentifierUtils(options);
30
31
  forEachDefinition(csn, (artifact) => {
31
32
  if (artifact.$tableConstraints && artifact.$tableConstraints.referential) {
32
- Object.entries(artifact.$tableConstraints.referential)
33
- .forEach(([ fileName, constraint ]) => {
34
- const renderAlterConstraintStatement = alter && src !== 'hdi';
35
- const renderedConstraint = renderReferentialConstraint(constraint, indent, false, csn, options, renderAlterConstraintStatement);
36
- if (src === 'hdi') {
37
- resultArtifacts[fileName] = renderedConstraint;
38
- return;
39
- }
40
- let alterTableStatement = '';
41
- alterTableStatement += `${indent}ALTER TABLE ${quoteSqlId(getResultingName(csn, options.toSql.names, constraint.dependentTable))}`;
42
- if (renderAlterConstraintStatement)
43
- alterTableStatement += `\n${indent}ALTER ${renderedConstraint};`;
44
- else if (drop)
45
- alterTableStatement += `${indent} DROP CONSTRAINT ${quoteSqlId(constraint.identifier)};`;
46
- else
47
- alterTableStatement += `\n${indent}ADD ${renderedConstraint};`;
48
-
49
- resultArtifacts[fileName] = alterTableStatement;
50
- });
33
+ forEach(artifact.$tableConstraints.referential, (fileName, constraint) => {
34
+ const renderAlterConstraintStatement = alter && src !== 'hdi';
35
+ const renderedConstraint = renderReferentialConstraint(constraint, indent, false, csn, options, renderAlterConstraintStatement);
36
+ if (src === 'hdi') {
37
+ resultArtifacts[fileName] = renderedConstraint;
38
+ return;
39
+ }
40
+ let alterTableStatement = '';
41
+ alterTableStatement += `${indent}ALTER TABLE ${quoteSqlId(getResultingName(csn, options.toSql.names, constraint.dependentTable))}`;
42
+ if (renderAlterConstraintStatement)
43
+ alterTableStatement += `\n${indent}ALTER ${renderedConstraint};`;
44
+ else if (drop)
45
+ alterTableStatement += `${indent} DROP CONSTRAINT ${quoteSqlId(constraint.identifier)};`;
46
+ else
47
+ alterTableStatement += `\n${indent}ADD ${renderedConstraint};`;
48
+
49
+ resultArtifacts[fileName] = alterTableStatement;
50
+ });
51
51
  }
52
52
  });
53
53
  return resultArtifacts;
@@ -65,25 +65,29 @@ function listReferentialIntegrityViolations(csn, options) {
65
65
  const referentialConstraints = getListOfAllConstraints(csn);
66
66
  const resultArtifacts = {};
67
67
  const indent = ' ';
68
- const increaseIndent = indent => `${indent} `;
68
+ const increaseIndent = str => ` ${str}`;
69
69
  // helper function to reduce parent key / foreign key array to a comma separated string which can be used in a select clause
70
70
  const keyStringReducer = prefix => (prev, curr, index) => (index > 0 ? `${prev},\n${curr} AS "${prefix}:${curr}"` : prev);
71
71
  // helper function to reduce the parent key / foreign key arrays of a referential constraint to a join list which can be used in a where clause
72
72
  const joinPkWithFkReducer = (constraint, subQueryAlias, mainQueryAlias) => (prev, curr, index) => (index > 0
73
73
  ? `${prev} AND
74
- ${increaseIndent(indent)}${mainQueryAlias}.${quoteSqlId(constraint.foreignKey[index])} = ${subQueryAlias}.${quoteSqlId(constraint.parentKey[index])}`
74
+ ${increaseIndent(indent)}"${mainQueryAlias}".${quoteSqlId(constraint.foreignKey[index])} = ${subQueryAlias}.${quoteSqlId(constraint.parentKey[index])}`
75
75
  : increaseIndent(increaseIndent(indent)) + prev);
76
76
 
77
- Object.entries(referentialConstraints).forEach(([ identifier, constraint ]) => {
77
+ Object.entries(referentialConstraints).forEach(([ identifier, constraint ], index) => {
78
78
  let selectViolations = 'SELECT\n';
79
+ // this column indicates which SELECT revealed the integrity violation
80
+ // and helps to identify the corrupted table
81
+ selectViolations += `${index} as "SELECT-ID",\n`;
79
82
  // SELECT <primary_key>,
80
83
  const primaryKeyList = selectPrimaryKeyColumns(constraint);
81
84
  if (primaryKeyList)
82
85
  selectViolations += `${primaryKeyList},\n`;
83
86
  // ... <foreign_key>
84
87
  selectViolations += selectForeignKeyColumns(constraint);
85
- // ... FROM <dependent table> AS "MAIN"
86
- selectViolations += `\nFROM ${quoteAndGetResultingName(constraint.dependentTable)} AS "MAIN"\n`;
88
+ const mainQueryAlias = `MAIN_${index}`;
89
+ // ... FROM <dependent table> AS "${index}"
90
+ selectViolations += `\nFROM ${quoteAndGetResultingName(constraint.dependentTable)} AS "${mainQueryAlias}"\n`;
87
91
  // ... WHERE NOT (<(part of) foreign key is null>)
88
92
  selectViolations += whereNotForeignKeyIsNull(constraint);
89
93
  /*
@@ -91,7 +95,7 @@ function listReferentialIntegrityViolations(csn, options) {
91
95
  SELECT * FROM <parent_table> WHERE <dependent_table>.<foreign_key> = <parent_table>.<parent_key>
92
96
  )
93
97
  */
94
- selectViolations += andNoMatchingPrimaryKeyExists(constraint);
98
+ selectViolations += andNoMatchingPrimaryKeyExists(constraint, mainQueryAlias);
95
99
  resultArtifacts[identifier] = selectViolations;
96
100
  });
97
101
 
@@ -147,21 +151,21 @@ function listReferentialIntegrityViolations(csn, options) {
147
151
  * Generate SQL sub-SELECT, listing all rows of the parent table where no matching primary key column for the respective foreign key is found.
148
152
  *
149
153
  * @param {CSN.ReferentialConstraint} constraint
154
+ * @param {string} mainQueryAlias
150
155
  * @returns AND NOT EXISTS ( SELECT * FROM <parent_table> WHERE <dependent_table>.<foreign_key> = <parent_table>.<parent_key> ) statement
151
156
  */
152
- function andNoMatchingPrimaryKeyExists(constraint) {
157
+ function andNoMatchingPrimaryKeyExists(constraint, mainQueryAlias) {
153
158
  let andNotExists = `\n${indent}AND NOT EXISTS (\n`;
154
159
  andNotExists += `${increaseIndent(indent)}SELECT * FROM ${quoteAndGetResultingName(constraint.parentTable)}`;
155
160
  // add an alias to both queries so that they can be distinguished at all times
156
161
  const subQueryAlias = '"SUB"';
157
- const mainQueryAlias = '"MAIN"';
158
162
  andNotExists += ` AS ${subQueryAlias}`;
159
163
  andNotExists += '\n';
160
164
  const joinListReducer = joinPkWithFkReducer(constraint, subQueryAlias, mainQueryAlias);
161
165
  andNotExists += `${increaseIndent(indent)}WHERE (\n`;
162
166
  andNotExists += constraint.foreignKey
163
167
  .reduce(joinListReducer,
164
- `${mainQueryAlias}.${quoteSqlId(constraint.foreignKey[0])} = ${subQueryAlias}.${quoteSqlId(constraint.parentKey[0])}`);
168
+ `"${mainQueryAlias}".${quoteSqlId(constraint.foreignKey[0])} = ${subQueryAlias}.${quoteSqlId(constraint.parentKey[0])}`);
165
169
  andNotExists += `\n${increaseIndent(indent)})`;
166
170
  andNotExists += `\n${indent});`;
167
171
  return andNotExists;
@@ -179,10 +183,9 @@ function getListOfAllConstraints(csn) {
179
183
  const referentialConstraints = {};
180
184
  forEachDefinition(csn, (artifact) => {
181
185
  if (artifact.$tableConstraints && artifact.$tableConstraints.referential) {
182
- Object.entries(artifact.$tableConstraints.referential)
183
- .forEach(([ identifier, referentialConstraint ]) => {
184
- referentialConstraints[identifier] = referentialConstraint;
185
- });
186
+ forEach(artifact.$tableConstraints.referential, (identifier, referentialConstraint) => {
187
+ referentialConstraints[identifier] = referentialConstraint;
188
+ });
186
189
  }
187
190
  });
188
191
  return referentialConstraints;