@sap/cds-compiler 2.11.2 → 2.13.6

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 (140) hide show
  1. package/CHANGELOG.md +175 -2
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +23 -17
  5. package/bin/cdsse.js +2 -2
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +25 -6
  9. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  10. package/doc/NameResolution.md +21 -16
  11. package/lib/api/main.js +32 -79
  12. package/lib/api/options.js +3 -2
  13. package/lib/api/validate.js +2 -1
  14. package/lib/backends.js +16 -26
  15. package/lib/base/dictionaries.js +0 -8
  16. package/lib/base/error.js +26 -0
  17. package/lib/base/keywords.js +10 -19
  18. package/lib/base/location.js +9 -4
  19. package/lib/base/message-registry.js +75 -9
  20. package/lib/base/messages.js +31 -35
  21. package/lib/base/model.js +2 -62
  22. package/lib/base/optionProcessorHelper.js +246 -183
  23. package/lib/checks/.eslintrc.json +2 -0
  24. package/lib/checks/actionsFunctions.js +2 -1
  25. package/lib/checks/annotationsOData.js +1 -1
  26. package/lib/checks/cdsPersistence.js +2 -1
  27. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  28. package/lib/checks/enricher.js +17 -1
  29. package/lib/checks/foreignKeys.js +4 -4
  30. package/lib/checks/invalidTarget.js +3 -1
  31. package/lib/checks/managedInType.js +4 -4
  32. package/lib/checks/managedWithoutKeys.js +3 -1
  33. package/lib/checks/queryNoDbArtifacts.js +1 -3
  34. package/lib/checks/selectItems.js +4 -4
  35. package/lib/checks/sql-snippets.js +94 -0
  36. package/lib/checks/types.js +1 -1
  37. package/lib/checks/unknownMagic.js +1 -1
  38. package/lib/checks/validator.js +12 -7
  39. package/lib/compiler/assert-consistency.js +12 -8
  40. package/lib/compiler/base.js +0 -1
  41. package/lib/compiler/builtins.js +42 -21
  42. package/lib/compiler/checks.js +46 -12
  43. package/lib/compiler/cycle-detector.js +1 -1
  44. package/lib/compiler/define.js +1103 -0
  45. package/lib/compiler/extend.js +983 -0
  46. package/lib/compiler/finalize-parse-cdl.js +231 -0
  47. package/lib/compiler/index.js +46 -39
  48. package/lib/compiler/kick-start.js +190 -0
  49. package/lib/compiler/moduleLayers.js +4 -4
  50. package/lib/compiler/populate.js +1226 -0
  51. package/lib/compiler/propagator.js +113 -47
  52. package/lib/compiler/resolve.js +1433 -0
  53. package/lib/compiler/shared.js +100 -65
  54. package/lib/compiler/tweak-assocs.js +529 -0
  55. package/lib/compiler/utils.js +215 -33
  56. package/lib/edm/.eslintrc.json +5 -0
  57. package/lib/edm/annotations/genericTranslation.js +38 -25
  58. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  59. package/lib/edm/csn2edm.js +10 -9
  60. package/lib/edm/edm.js +19 -20
  61. package/lib/edm/edmPreprocessor.js +166 -95
  62. package/lib/edm/edmUtils.js +127 -34
  63. package/lib/gen/Dictionary.json +92 -43
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +11 -1
  66. package/lib/gen/language.tokens +86 -82
  67. package/lib/gen/languageLexer.interp +18 -1
  68. package/lib/gen/languageLexer.js +925 -847
  69. package/lib/gen/languageLexer.tokens +78 -74
  70. package/lib/gen/languageParser.js +5434 -4298
  71. package/lib/json/from-csn.js +59 -17
  72. package/lib/json/to-csn.js +189 -71
  73. package/lib/language/antlrParser.js +3 -3
  74. package/lib/language/docCommentParser.js +3 -3
  75. package/lib/language/errorStrategy.js +26 -8
  76. package/lib/language/genericAntlrParser.js +144 -53
  77. package/lib/language/language.g4 +424 -200
  78. package/lib/language/multiLineStringParser.js +536 -0
  79. package/lib/main.d.ts +550 -61
  80. package/lib/main.js +38 -11
  81. package/lib/model/api.js +3 -1
  82. package/lib/model/csnRefs.js +322 -198
  83. package/lib/model/csnUtils.js +226 -370
  84. package/lib/model/enrichCsn.js +124 -69
  85. package/lib/model/revealInternalProperties.js +29 -7
  86. package/lib/model/sortViews.js +10 -2
  87. package/lib/modelCompare/compare.js +17 -12
  88. package/lib/optionProcessor.js +8 -3
  89. package/lib/render/.eslintrc.json +1 -2
  90. package/lib/render/DuplicateChecker.js +1 -1
  91. package/lib/render/manageConstraints.js +36 -33
  92. package/lib/render/toCdl.js +174 -275
  93. package/lib/render/toHdbcds.js +203 -122
  94. package/lib/render/toRename.js +7 -10
  95. package/lib/render/toSql.js +161 -82
  96. package/lib/render/utils/common.js +22 -8
  97. package/lib/render/utils/sql.js +10 -7
  98. package/lib/render/utils/stringEscapes.js +111 -0
  99. package/lib/sql-identifier.js +1 -1
  100. package/lib/transform/.eslintrc.json +5 -0
  101. package/lib/transform/braceExpression.js +4 -2
  102. package/lib/transform/db/.eslintrc.json +2 -0
  103. package/lib/transform/db/applyTransformations.js +212 -0
  104. package/lib/transform/db/assertUnique.js +1 -1
  105. package/lib/transform/db/associations.js +187 -0
  106. package/lib/transform/db/cdsPersistence.js +150 -0
  107. package/lib/transform/db/constraints.js +61 -56
  108. package/lib/transform/db/expansion.js +50 -29
  109. package/lib/transform/db/flattening.js +556 -106
  110. package/lib/transform/db/groupByOrderBy.js +3 -1
  111. package/lib/transform/db/temporal.js +236 -0
  112. package/lib/transform/db/transformExists.js +103 -28
  113. package/lib/transform/db/views.js +92 -44
  114. package/lib/transform/draft/.eslintrc.json +38 -0
  115. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  116. package/lib/transform/draft/odata.js +227 -0
  117. package/lib/transform/forHanaNew.js +98 -783
  118. package/lib/transform/forOdataNew.js +22 -175
  119. package/lib/transform/localized.js +36 -32
  120. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  121. package/lib/transform/odata/referenceFlattener.js +95 -89
  122. package/lib/transform/odata/structureFlattener.js +1 -1
  123. package/lib/transform/odata/toFinalBaseType.js +86 -12
  124. package/lib/transform/odata/typesExposure.js +5 -5
  125. package/lib/transform/odata/utils.js +2 -2
  126. package/lib/transform/transformUtilsNew.js +47 -33
  127. package/lib/transform/translateAssocsToJoins.js +13 -30
  128. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  129. package/lib/transform/universalCsn/coreComputed.js +170 -0
  130. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  131. package/lib/transform/universalCsn/utils.js +63 -0
  132. package/lib/utils/file.js +8 -3
  133. package/lib/utils/objectUtils.js +30 -0
  134. package/lib/utils/timetrace.js +8 -2
  135. package/package.json +1 -1
  136. package/share/messages/README.md +26 -0
  137. package/lib/compiler/definer.js +0 -2349
  138. package/lib/compiler/resolver.js +0 -2922
  139. package/lib/transform/db/helpers.js +0 -58
  140. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -7,23 +7,35 @@
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:
16
16
 
17
- // * `$env` for the non-enumerable `$env` property in the original CSN.
18
- // * `$elements` for a non-enumerable `elements` property for sub queries.
17
+ // * `$parens`: the number of parentheses provided by the user around an expression
18
+ // or query if the number is different to the usual (mostly 0, sometimes 1).
19
+ // * `$elements` (in client-style CSN only) for a non-enumerable `elements` property
20
+ // for sub queries.
19
21
 
20
22
  // The following properties in the JSON represent the result of the CSN API
21
23
  // functions:
22
24
 
23
- // * `_type`, `_includes` and `_targets` have as values the `$locations` of the
25
+ // * `_type`, `_includes` and `_targets` have as values the `$location`s of the
24
26
  // referred artifacts which are returned by function `artifactRef`.
25
- // * `_links`, `_art` and `_scope` as sibling properties of `ref` have as values
26
- // the `$locations` of the artifacts/members returned by function `inspectRef`.
27
+ // * `_links` and `_art` as sibling properties of `ref` have as values the
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`).
30
+ // * `_scope` and `_env` as sibling properties of `ref` have (string) values,
31
+ // returned by function `inspectRef`, giving add/ info about the “ref base”.
32
+ // * `_origin` (in Universal CSN only) has as value the `$location` of the
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›`.
27
39
 
28
40
  'use strict';
29
41
 
@@ -32,7 +44,6 @@ const { locationString } = require('../base/location');
32
44
 
33
45
  function enrichCsn( csn, options = {} ) {
34
46
  const transformers = {
35
- // $env: reveal,
36
47
  elements: dictionary,
37
48
  definitions: dictionary,
38
49
  actions: dictionary,
@@ -50,10 +61,14 @@ function enrichCsn( csn, options = {} ) {
50
61
  // TODO: excluding
51
62
  '@': () => { /* ignore annotations */ },
52
63
  }
53
- setLocations( csn, false, null );
54
- const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
55
- csnRefs( csn );
64
+ // options.enrichCsn = 'DEBUG';
56
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
+
57
72
  const csnPath = [];
58
73
  if (csn.definitions)
59
74
  dictionary( csn, 'definitions', csn.definitions );
@@ -84,9 +99,10 @@ function enrichCsn( csn, options = {} ) {
84
99
  }
85
100
 
86
101
  function definition( parent, prop, obj ) {
87
- const origin = getOrigin( obj ); // before standard for implicit protos inside
102
+ // call getOrigin() before standard() to set implicit protos inside standard():
103
+ const origin = handleError( err => err ? err.toString() : getOrigin( obj ) );
88
104
  standard( parent, prop, obj );
89
- if (obj.$origin === undefined && origin != null)
105
+ if (obj.$origin === undefined && !obj.type && origin != null)
90
106
  obj._origin = refLocation( origin );
91
107
  }
92
108
 
@@ -95,6 +111,8 @@ function enrichCsn( csn, options = {} ) {
95
111
  return;
96
112
  csnPath.push( prop );
97
113
  for (let name of Object.getOwnPropertyNames( dict )) {
114
+ if (prop === 'definitions')
115
+ initDefinition( dict[name] );
98
116
  definition( dict, name, dict[name] );
99
117
  }
100
118
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
@@ -103,11 +121,19 @@ function enrichCsn( csn, options = {} ) {
103
121
  }
104
122
 
105
123
  function refLocation( art ) {
106
- if (art)
107
- 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
+
108
134
  if (!options.testMode)
109
- return '<illegal link>';
110
- throw new Error( 'Undefined reference' );
135
+ return `<${Object.keys( art ).join('+')}+!$location>`;
136
+ throw new Error( 'Reference to object without $location' );
111
137
  }
112
138
 
113
139
  function simpleRef( parent, prop, ref ) {
@@ -133,37 +159,21 @@ function enrichCsn( csn, options = {} ) {
133
159
  }
134
160
 
135
161
  function $origin( parent, prop, ref ) {
136
- if (options.testMode) {
137
- if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
138
- parent._origin = refLocation( getOrigin( parent, true ) );
139
- else if ( ref )
140
- standard( parent, prop, ref )
141
- }
142
- else {
143
- try {
144
- if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
145
- parent._origin = refLocation( getOrigin( parent, true ) );
146
- else if ( ref )
147
- standard( parent, prop, ref )
148
- } catch (e) {
149
- parent._origin = e.toString();
150
- }
151
- }
162
+ handleError( err => {
163
+ if (err)
164
+ parent._origin = err.toString();
165
+ else if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […] / "short-ref"
166
+ parent._origin = refLocation( getOrigin( parent ) );
167
+ else if ( ref ) // $origin: {…}
168
+ standard( parent, prop, ref );
169
+ } );
152
170
  }
153
171
 
154
- function pathRef( parent, prop, path ) {
155
- const { links, art, scope, $env } = (() => {
156
- if (options.testMode)
157
- return inspectRef( csnPath );
158
- else {
159
- try {
160
- return inspectRef( csnPath );
161
- }
162
- catch (e) {
163
- return { scope: e.toString() };
164
- }
165
- }
166
- } )();
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;
167
177
  if (links)
168
178
  parent._links = links.map( l => refLocation( l.art ) );
169
179
  if (links && links[links.length-1].art !== art)
@@ -172,6 +182,15 @@ function enrichCsn( csn, options = {} ) {
172
182
  if ($env)
173
183
  parent._env = $env;
174
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
+
175
194
  csnPath.push( prop );
176
195
  path.forEach( function step( s, i ) {
177
196
  if (s && typeof s === 'object') {
@@ -186,6 +205,16 @@ function enrichCsn( csn, options = {} ) {
186
205
  csnPath.pop();
187
206
  }
188
207
 
208
+ function handleError( callback ) {
209
+ if (options.testMode)
210
+ return callback();
211
+ try {
212
+ return callback();
213
+ } catch (err) {
214
+ return callback( err );
215
+ }
216
+ }
217
+
189
218
  function _cache_debug( obj, subCache ) {
190
219
  if (options.enrichCsn !== 'DEBUG')
191
220
  return;
@@ -227,6 +256,10 @@ function enrichCsn( csn, options = {} ) {
227
256
  obj.$$cacheObject[name] = sub;
228
257
  }
229
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
+ }
230
263
  else if (Array.isArray( val )) {
231
264
  obj.$$cacheObject[name] = val.map( item => {
232
265
  if (!item.$$objectNumber)
@@ -242,8 +275,53 @@ function enrichCsn( csn, options = {} ) {
242
275
  }
243
276
  }
244
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
+ }
245
322
  }
246
323
 
324
+
247
325
  function reveal( node, prop, value ) {
248
326
  Object.defineProperty( node, prop, {
249
327
  value,
@@ -253,27 +331,4 @@ function reveal( node, prop, value ) {
253
331
  } );
254
332
  }
255
333
 
256
- function setLocations( node, prop, loc ) {
257
- if (!node || typeof node !== 'object')
258
- return;
259
- const isMember = artifactProperties.includes( prop );
260
- if (!isMember && node.$location) {
261
- const value = locationString( node.$location, true );
262
- reveal( node, '$location', value );
263
- loc = value + '-';
264
- }
265
- else if (prop === true) {
266
- loc += '1';
267
- node.$location = loc;
268
- }
269
- if (Array.isArray( node )) {
270
- for (const item of node)
271
- setLocations( item, isMember, loc );
272
- }
273
- else {
274
- for (const name of Object.getOwnPropertyNames( node ))
275
- setLocations( node[name], isMember || name, loc );
276
- }
277
- }
278
-
279
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
  }
@@ -271,12 +287,15 @@ function artifactIdentifier( node, parent ) {
271
287
  Object.defineProperty( node, '__unique_id__', { value: ++unique_id } );
272
288
  let outer = unique_id ? '##' + node.__unique_id__ : '';
273
289
  if (node._outer) {
274
- outer = (node._outer.items === node) ? '/items'
275
- : (node._outer.returns === node) ? '/returns' : '/returns/items';
290
+ if (node.$inferred === 'REDIRECTED')
291
+ outer = '/redirected' + outer;
292
+ else
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;
276
297
  node = node._outer;
277
298
  }
278
- else if (node.$inferred === 'REDIRECTED')
279
- outer = '/redirected';
280
299
  if (node === parent)
281
300
  return 'this';
282
301
  if (node.kind === 'source')
@@ -304,7 +323,10 @@ function artifactIdentifier( node, parent ) {
304
323
  return 'source:' + quoted( node.location && node.location.file ) +
305
324
  '/using:' + quoted( node.name.id )
306
325
  default: {
307
- 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;
308
330
  }
309
331
  }
310
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
@@ -79,7 +80,7 @@ function sortTopologically(csn, _dependents, _dependencies){
79
80
  /**
80
81
  * Sort the given sql statements so that they can be deployed sequentially.
81
82
  * For ordering, only the FROM clause of views is checked - this requires A2J to
82
- * be run beforehand to resovle association usages.
83
+ * be run beforehand to resolve association usages.
83
84
  *
84
85
  * @param {object} sql Map of <object name>: "CREATE STATEMENT"
85
86
  *
@@ -91,10 +92,17 @@ 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
98
99
  layers.forEach(layer => layer.forEach(objName => result.push({name: objName, sql: sql[objName]})));
100
+ // attach sql artifacts which are not considered during the view sorting algorithm
101
+ // --> this is the case for "ALTER TABLE ADD CONSTRAINT" statements,
102
+ // because their identifiers are not part of the csn.definitions
103
+ Object.entries(sql).forEach(([ name, sqlString ]) => {
104
+ if (!result.some( o => o.name === name )) // not in result but in incoming sql
105
+ result.push({ name, sql: sqlString })
106
+ });
99
107
  return result;
100
108
  }
@@ -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,7 +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' ])
33
+ .option(' --assert-integrity-type <type>', [ 'RT', 'DB' ], { ignoreCase: true })
34
+ .option(' --constraints-in-create-table')
34
35
  .option(' --deprecated <list>')
35
36
  .option(' --hana-flavor')
36
37
  .option(' --direct-backend')
@@ -103,14 +104,17 @@ optionProcessor
103
104
  --integrity-not-validated If this option is supplied, referential constraints are NOT VALIDATED.
104
105
  This option is also applied to result of "cdsc manageConstraints"
105
106
  --assert-integrity <mode> Turn DB constraints on/off:
106
- true : Constraints will be generated for all associations if
107
+ true : (default) Constraints will be generated for all associations if
107
108
  the assert-integrity-type is set to DB
108
109
  false : No constraints will be generated
109
110
  individual : Constraints will be generated for selected associations
110
111
  --assert-integrity-type <type> Specifies how the referential integrity checks should be performed:
111
112
  RT : (default) No database constraint for an association
112
113
  if not explicitly demanded via annotation
113
- DB : Create database constraints for associations
114
+ DB : Create database constraints for associations
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
114
118
  --deprecated <list> Comma separated list of deprecated options.
115
119
  Valid values are:
116
120
  noElementsExpansion
@@ -120,6 +124,7 @@ optionProcessor
120
124
  renderVirtualElements
121
125
  unmanagedUpInComponent
122
126
  createLocalizedViews
127
+ redirectInSubQueries
123
128
  --hana-flavor Compile with backward compatibility for HANA CDS (incomplete)
124
129
  --parse-only Stop compilation after parsing and write result to <stdout>
125
130
  --fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
@@ -13,8 +13,7 @@
13
13
  // Who cares - just very whiny and in the way
14
14
  "complexity": "off",
15
15
  "max-len": "off",
16
- // We should enable this
17
- "no-shadow": "off"
16
+ "no-shadow": "warn"
18
17
  },
19
18
  "env": {
20
19
  "es6": true
@@ -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
  */