@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.
- package/CHANGELOG.md +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/model/enrichCsn.js
CHANGED
|
@@ -61,10 +61,14 @@ function enrichCsn( csn, options = {} ) {
|
|
|
61
61
|
// TODO: excluding
|
|
62
62
|
'@': () => { /* ignore annotations */ },
|
|
63
63
|
}
|
|
64
|
-
|
|
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
|
|
119
|
-
|
|
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
|
|
122
|
-
throw new Error( '
|
|
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
|
-
|
|
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(
|
|
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'
|
|
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
|
-
|
|
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
|
}
|
package/lib/model/sortViews.js
CHANGED
|
@@ -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
|
|
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 {
|
|
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 = [
|
|
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,
|
|
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 (!
|
|
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
|
-
|
|
172
|
+
changedElementsDict[name] = changedElement(element, otherElement);
|
|
168
173
|
}
|
|
169
174
|
|
|
170
175
|
return;
|
|
171
176
|
}
|
|
172
177
|
|
|
173
|
-
if (
|
|
174
|
-
|
|
178
|
+
if (addedElementsDict) {
|
|
179
|
+
addedElementsDict[name] = element;
|
|
175
180
|
}
|
|
176
181
|
}
|
|
177
182
|
}
|
package/lib/optionProcessor.js
CHANGED
|
@@ -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-
|
|
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-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
+
forEach(artifact.$tableConstraints.referential, (identifier, referentialConstraint) => {
|
|
187
|
+
referentialConstraints[identifier] = referentialConstraint;
|
|
188
|
+
});
|
|
186
189
|
}
|
|
187
190
|
});
|
|
188
191
|
return referentialConstraints;
|