@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.
- package/CHANGELOG.md +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +30 -63
- package/lib/api/options.js +5 -5
- 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 +52 -2
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +46 -12
- 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 +33 -14
- 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 +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- 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 +7 -6
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- 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/braceExpression.js +4 -2
- 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 +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- 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} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +94 -801
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- 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 +47 -33
- package/lib/transform/translateAssocsToJoins.js +10 -27
- 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/file.js +2 -1
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2340
- package/lib/compiler/resolver.js +0 -2988
- package/lib/transform/universalCsnEnricher.js +0 -67
package/lib/model/enrichCsn.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
113
|
-
|
|
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
|
|
116
|
-
throw new Error( '
|
|
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: […]
|
|
146
|
-
parent._origin = refLocation( getOrigin( parent
|
|
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
|
|
154
|
-
|
|
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
|
-
|
|
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
|
@@ -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-
|
|
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-
|
|
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;
|
|
@@ -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 =
|
|
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
|
-
|
|
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;
|