@sap/cds-compiler 2.12.0 → 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 (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -61,10 +61,14 @@ function enrichCsn( csn, options = {} ) {
61
61
  // TODO: excluding
62
62
  '@': () => { /* ignore annotations */ },
63
63
  }
64
- setLocations( csn, false, null );
65
- const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
66
- csnRefs( csn );
64
+ // options.enrichCsn = 'DEBUG';
67
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
+
68
72
  const csnPath = [];
69
73
  if (csn.definitions)
70
74
  dictionary( csn, 'definitions', csn.definitions );
@@ -107,6 +111,8 @@ function enrichCsn( csn, options = {} ) {
107
111
  return;
108
112
  csnPath.push( prop );
109
113
  for (let name of Object.getOwnPropertyNames( dict )) {
114
+ if (prop === 'definitions')
115
+ initDefinition( dict[name] );
110
116
  definition( dict, name, dict[name] );
111
117
  }
112
118
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
@@ -115,11 +121,19 @@ function enrichCsn( csn, options = {} ) {
115
121
  }
116
122
 
117
123
  function refLocation( art ) {
118
- if (art && typeof art === 'object')
119
- 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
+
120
134
  if (!options.testMode)
121
- return art || '<illegal link>';
122
- throw new Error( 'Undefined reference' );
135
+ return `<${Object.keys( art ).join('+')}+!$location>`;
136
+ throw new Error( 'Reference to object without $location' );
123
137
  }
124
138
 
125
139
  function simpleRef( parent, prop, ref ) {
@@ -242,6 +256,10 @@ function enrichCsn( csn, options = {} ) {
242
256
  obj.$$cacheObject[name] = sub;
243
257
  }
244
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
+ }
245
263
  else if (Array.isArray( val )) {
246
264
  obj.$$cacheObject[name] = val.map( item => {
247
265
  if (!item.$$objectNumber)
@@ -257,8 +275,53 @@ function enrichCsn( csn, options = {} ) {
257
275
  }
258
276
  }
259
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
+ }
260
322
  }
261
323
 
324
+
262
325
  function reveal( node, prop, value ) {
263
326
  Object.defineProperty( node, prop, {
264
327
  value,
@@ -268,26 +331,4 @@ function reveal( node, prop, value ) {
268
331
  } );
269
332
  }
270
333
 
271
- function setLocations( node, prop, loc ) {
272
- if (!node || typeof node !== 'object')
273
- return;
274
- const isMember = artifactProperties.includes( prop );
275
- if (!isMember && node.$location) {
276
- loc = locationString( node.$location, true );
277
- reveal( node, '$location', loc );
278
- }
279
- else if (prop === true) {
280
- loc += '^';
281
- node.$location = loc;
282
- }
283
- if (Array.isArray( node )) {
284
- for (const item of node)
285
- setLocations( item, isMember, loc );
286
- }
287
- else {
288
- for (const name of Object.getOwnPropertyNames( node ))
289
- setLocations( node[name], isMember || name, loc );
290
- }
291
- }
292
-
293
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
  }
@@ -31,7 +31,7 @@ optionProcessor
31
31
  .option(' --integrity-not-enforced')
32
32
  .option(' --assert-integrity <mode>', [ 'true', 'false', 'individual' ])
33
33
  .option(' --assert-integrity-type <type>', [ 'RT', 'DB' ], { ignoreCase: true })
34
- .option(' --constraints-as-alter <boolean>')
34
+ .option(' --constraints-in-create-table')
35
35
  .option(' --deprecated <list>')
36
36
  .option(' --hana-flavor')
37
37
  .option(' --direct-backend')
@@ -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
@@ -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;
@@ -71,19 +71,23 @@ function listReferentialIntegrityViolations(csn, options) {
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;