@sap/cds-compiler 2.10.4 → 2.12.0
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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
|
@@ -22,7 +22,7 @@ const { flattenCSN } = require('./odata/structureFlattener');
|
|
|
22
22
|
const generateForeignKeys = require('./odata/generateForeignKeyElements');
|
|
23
23
|
const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssociations');
|
|
24
24
|
const expandToFinalBaseType = require('./odata/toFinalBaseType');
|
|
25
|
-
const timetrace = require('../utils/timetrace');
|
|
25
|
+
const { timetrace } = require('../utils/timetrace');
|
|
26
26
|
const { attachPath } = require('./odata/attachPath');
|
|
27
27
|
const enrichUniversalCsn = require('./universalCsnEnricher');
|
|
28
28
|
|
|
@@ -192,6 +192,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
192
192
|
|
|
193
193
|
// Apply default type facets as set by options
|
|
194
194
|
// Flatten on-conditions in unmanaged associations
|
|
195
|
+
/* FIXME (HJB): Is this comment still correct? processOnCond only strips $self
|
|
196
|
+
We should not remove $self prefixes in structured OData to not
|
|
197
|
+
interfer with path resolution
|
|
198
|
+
*/
|
|
195
199
|
// This must be done before all the draft logic as all
|
|
196
200
|
// composition targets are annotated with @odata.draft.enabled in this step
|
|
197
201
|
forEachDefinition(csn, [ setDefaultTypeFacets, processOnCond ], { skipArtifact: isExternalServiceMember });
|
|
@@ -299,8 +303,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
299
303
|
// If @Core.Computed is explicitly set, don't overwrite it!
|
|
300
304
|
if (node['@Core.Computed'] !== undefined) return;
|
|
301
305
|
|
|
302
|
-
// For @odata.on.insert/update,
|
|
303
|
-
|
|
306
|
+
// For @odata.on.insert/update, also add @Core.Computed
|
|
307
|
+
// @odata.on is deprecated, use @cds.on {update|insert} instead
|
|
308
|
+
if(['@odata.on.insert', '@odata.on.update', '@cds.on.insert', '@cds.on.update'].some(a => node[a]))
|
|
304
309
|
node['@Core.Computed'] = true;
|
|
305
310
|
}
|
|
306
311
|
|
|
@@ -569,6 +574,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
569
574
|
// CDXCORE-481
|
|
570
575
|
// (4.5) If the member is an association whose target has @cds.odata.valuelist annotate it
|
|
571
576
|
// with @Common.ValueList.viaAssociation.
|
|
577
|
+
/*
|
|
578
|
+
FIXME (HJB): Comment outdated: Anno propagation to FKs is done in EdmPreprocessor
|
|
579
|
+
*/
|
|
572
580
|
// This must be done before foreign keys are calculated and the annotations are propagated
|
|
573
581
|
// to them. This will make sure that association and all its foreign keys are annotated with
|
|
574
582
|
// Common.ValueList in the final EDM.
|
|
@@ -5,14 +5,12 @@ const { setProp } = require('../base/model');
|
|
|
5
5
|
const { hasErrors } = require('../base/messages');
|
|
6
6
|
const { cloneCsnDictionary } = require('../model/csnUtils');
|
|
7
7
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
8
|
-
const { rejectManagedAssociationsAndStructuresForHdbcsNames } = require('../checks/selectItems');
|
|
9
8
|
const {
|
|
10
9
|
cloneCsn,
|
|
11
10
|
forEachDefinition,
|
|
12
11
|
forEachGeneric,
|
|
13
12
|
forAllQueries,
|
|
14
13
|
sortCsnDefinitionsForTests,
|
|
15
|
-
getUtils,
|
|
16
14
|
} = require('../model/csnUtils');
|
|
17
15
|
|
|
18
16
|
/**
|
|
@@ -79,7 +77,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
79
77
|
if (hasExistingLocalizationViews(csn, options))
|
|
80
78
|
return csn;
|
|
81
79
|
|
|
82
|
-
const { info
|
|
80
|
+
const { info } = makeMessageFunction(csn, options);
|
|
83
81
|
|
|
84
82
|
const noCoalesce = (options.localizedLanguageFallback === 'none' ||
|
|
85
83
|
options.localizedWithoutCoalesce);
|
|
@@ -87,16 +85,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
87
85
|
createDirectConvenienceViews(); // 1
|
|
88
86
|
createTransitiveConvenienceViews(); // 2 + 3
|
|
89
87
|
|
|
90
|
-
forEachDefinition(csn, (definition,
|
|
91
|
-
cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor)
|
|
92
|
-
if(definition.query) {
|
|
93
|
-
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
94
|
-
const that = { csnUtils: getUtils(csn), options, error };
|
|
95
|
-
rejectManagedAssociationsAndStructuresForHdbcsNames.call(that, definition, path)
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
|
|
100
89
|
|
|
101
90
|
sortCsnDefinitionsForTests(csn, options);
|
|
102
91
|
return csn;
|
|
@@ -144,7 +133,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
144
133
|
|
|
145
134
|
art[_hasLocalizedView] = viewName;
|
|
146
135
|
|
|
147
|
-
if(acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
136
|
+
if (acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
148
137
|
return;
|
|
149
138
|
|
|
150
139
|
let view;
|
|
@@ -698,6 +687,8 @@ function copyPersistenceAnnotations(target, source) {
|
|
|
698
687
|
* @param {CSN.Options} options
|
|
699
688
|
*/
|
|
700
689
|
function hasExistingLocalizationViews(csn, options) {
|
|
690
|
+
if (!csn || !csn.definitions)
|
|
691
|
+
return false;
|
|
701
692
|
const firstLocalizedView = Object.keys(csn.definitions).find(isInLocalizedNamespace);
|
|
702
693
|
if (firstLocalizedView) {
|
|
703
694
|
const { info } = makeMessageFunction(csn, options);
|
|
@@ -201,13 +201,13 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
|
|
|
201
201
|
// Transfer selected type properties from target key element
|
|
202
202
|
// FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
|
|
203
203
|
for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
|
|
204
|
-
if (fkArtifact[prop]
|
|
204
|
+
if (fkArtifact[prop] !== undefined) {
|
|
205
205
|
foreignKeyElement[prop] = fkArtifact[prop];
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
// If the association is non-fkArtifact resp. key, so should be the foreign key field
|
|
209
209
|
for (let prop of ['notNull', 'key']) {
|
|
210
|
-
if (assoc[prop]
|
|
210
|
+
if (assoc[prop] !== undefined) {
|
|
211
211
|
foreignKeyElement[prop] = assoc[prop];
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -9,7 +9,7 @@ const { setProp } = require('../base/model');
|
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
11
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsn, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
12
|
+
const { cloneCsn, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
13
13
|
|
|
14
14
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
15
15
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -80,6 +80,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
80
80
|
else if(defStrLen5k)
|
|
81
81
|
element.length = 5000;
|
|
82
82
|
}
|
|
83
|
+
if (element.type === 'cds.Binary' && element.length === undefined) {
|
|
84
|
+
if(options.defaultBinaryLength) {
|
|
85
|
+
element.length = options.defaultBinaryLength;
|
|
86
|
+
setProp(element, '$default', true);
|
|
87
|
+
}
|
|
88
|
+
else if(defStrLen5k)
|
|
89
|
+
element.length = 5000;
|
|
90
|
+
}
|
|
83
91
|
/*
|
|
84
92
|
if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
|
|
85
93
|
element.precision = options.precision;
|
|
@@ -100,7 +108,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
100
108
|
// Transfer selected type properties from target key element
|
|
101
109
|
// FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
|
|
102
110
|
for (const prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
|
|
103
|
-
if (fkArtifact[prop]
|
|
111
|
+
if (fkArtifact[prop] !== undefined) {
|
|
104
112
|
foreignKeyElement[prop] = fkArtifact[prop];
|
|
105
113
|
}
|
|
106
114
|
}
|
|
@@ -110,7 +118,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
110
118
|
|
|
111
119
|
// If the association is non-fkArtifact resp. key, so should be the foreign key field
|
|
112
120
|
for (const prop of ['notNull', 'key']) {
|
|
113
|
-
if (assoc[prop]
|
|
121
|
+
if (assoc[prop] !== undefined) {
|
|
114
122
|
foreignKeyElement[prop] = assoc[prop];
|
|
115
123
|
}
|
|
116
124
|
}
|
|
@@ -294,7 +302,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
294
302
|
* [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
|
|
295
303
|
* [ (Entity), (struct1_struct2_assoc), (elem) ]
|
|
296
304
|
*
|
|
297
|
-
* @param {string[]} ref
|
|
305
|
+
* @param {string[]} ref
|
|
298
306
|
* @param {CSN.Path} path CSN path to the ref
|
|
299
307
|
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
300
308
|
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
@@ -344,6 +352,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
344
352
|
/**
|
|
345
353
|
* Copy properties of the referenced type, but don't resolve to the final base type.
|
|
346
354
|
*
|
|
355
|
+
* Do not copy the length if it was just set via the default-value.
|
|
356
|
+
*
|
|
347
357
|
* @param {any} node Node to copy to
|
|
348
358
|
* @returns {void}
|
|
349
359
|
*/
|
|
@@ -377,7 +387,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
377
387
|
* also unravel derived enum types, i.e. take the final base type of the enum's base type.
|
|
378
388
|
* Similar with associations and compositions (we probably need a _baseType link)
|
|
379
389
|
*
|
|
380
|
-
* @param {CSN.Artifact} node
|
|
390
|
+
* @param {CSN.Artifact} node
|
|
381
391
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
382
392
|
* @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
|
|
383
393
|
* @returns {void}
|
|
@@ -396,7 +406,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
396
406
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
397
407
|
} else if(finalBaseType.items) {
|
|
398
408
|
// This changes the order - to be discussed!
|
|
399
|
-
node.items = cloneCsn(finalBaseType, options)
|
|
409
|
+
node.items = cloneCsn(finalBaseType.items, options); // copy items
|
|
400
410
|
delete node.type;
|
|
401
411
|
} else {
|
|
402
412
|
node.type=finalBaseType;
|
|
@@ -414,16 +424,16 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
414
424
|
return;
|
|
415
425
|
|
|
416
426
|
// cloneCsn only works correctly if we start "from the top"
|
|
417
|
-
const
|
|
427
|
+
const cloneTypeDef = cloneCsn(typeDef, options);
|
|
418
428
|
// With hdbcds-hdbcds, don't resolve structured types - but propagrate ".items", to turn into LargeString later on.
|
|
419
429
|
if(typeDef.items) {
|
|
420
430
|
delete node.type;
|
|
421
|
-
Object.assign(node, {items:
|
|
431
|
+
Object.assign(node, {items: cloneTypeDef.items});
|
|
422
432
|
}
|
|
423
433
|
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
424
434
|
if(!typeDef.items)
|
|
425
435
|
delete node.type;
|
|
426
|
-
Object.assign(node, {elements:
|
|
436
|
+
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
427
437
|
}
|
|
428
438
|
|
|
429
439
|
|
|
@@ -431,7 +441,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
431
441
|
}
|
|
432
442
|
// if the declared element is an enum, these values are with priority
|
|
433
443
|
if (!node.enum && typeDef.enum) {
|
|
434
|
-
const clone =
|
|
444
|
+
const clone = cloneCsnDictionary(typeDef.enum, options);
|
|
435
445
|
Object.assign(node, { enum: clone });
|
|
436
446
|
}
|
|
437
447
|
if (node.length === undefined && typeDef.length !== undefined)
|
|
@@ -855,7 +865,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
855
865
|
function checkMultipleAssignments(array, annoName, artifact, artifactName, err = true) {
|
|
856
866
|
if (array.length > 1) {
|
|
857
867
|
const loc = ['definitions', artifactName];
|
|
858
|
-
if (err
|
|
868
|
+
if (err === true) {
|
|
859
869
|
error(null, loc, { anno: annoName }, `Annotation $(ANNO) must be assigned only once`);
|
|
860
870
|
} else {
|
|
861
871
|
warning(null, loc, { anno: annoName },`Annotation $(ANNO) must be assigned only once`);
|
|
@@ -1074,7 +1084,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1074
1084
|
* Expand structured expression arguments to flat reference paths.
|
|
1075
1085
|
* Structured elements are real sub element lists and managed associations.
|
|
1076
1086
|
* All unmanaged association definitions are rewritten if applicable (elements/mixins).
|
|
1077
|
-
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1087
|
+
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1078
1088
|
* .xpr in columns.
|
|
1079
1089
|
*
|
|
1080
1090
|
* @todo Check if can be skipped for abstract entity and or cds.persistence.skip ?
|
|
@@ -1084,16 +1094,16 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1084
1094
|
function expandStructsInExpression(csn, options = {}) {
|
|
1085
1095
|
applyTransformations(csn, {
|
|
1086
1096
|
'on': (parent, name, on, path) => {
|
|
1087
|
-
parent.on = expand(parent.on, path);
|
|
1097
|
+
parent.on = expand(parent.on, path.concat(name));
|
|
1088
1098
|
},
|
|
1089
1099
|
'having': (parent, name, having, path) => {
|
|
1090
|
-
parent.having = expand(parent.having, path);
|
|
1100
|
+
parent.having = expand(parent.having, path.concat(name));
|
|
1091
1101
|
},
|
|
1092
1102
|
'where': (parent, name, where, path) => {
|
|
1093
|
-
parent.where = expand(parent.where, path);
|
|
1103
|
+
parent.where = expand(parent.where, path.concat(name));
|
|
1094
1104
|
},
|
|
1095
1105
|
'xpr': (parent, name, xpr, path) => {
|
|
1096
|
-
parent.xpr = expand(parent.xpr, path);
|
|
1106
|
+
parent.xpr = expand(parent.xpr, path.concat(name));
|
|
1097
1107
|
}
|
|
1098
1108
|
}, undefined, undefined, options);
|
|
1099
1109
|
|
|
@@ -1111,17 +1121,21 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1111
1121
|
if(i < expr.length-2)
|
|
1112
1122
|
{
|
|
1113
1123
|
const [lhs, op, rhs] = expr.slice(i);
|
|
1124
|
+
|
|
1125
|
+
// we might have to ad-hoc resolve a ref, since handleExists is run before hand and generates new refs.
|
|
1126
|
+
const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
|
|
1127
|
+
const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
|
|
1114
1128
|
// lhs & rhs must be expandable types (structures or managed associations)
|
|
1115
|
-
if(
|
|
1129
|
+
if(lhsArt && rhsArt &&
|
|
1116
1130
|
lhs.ref && rhs.ref &&
|
|
1117
|
-
isExpandable(
|
|
1131
|
+
isExpandable(lhsArt) && isExpandable(rhsArt) &&
|
|
1118
1132
|
['=', '<', '>', '>=', '<=', '!=', '<>'].includes(op) &&
|
|
1119
1133
|
!(isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs))) {
|
|
1120
1134
|
|
|
1121
1135
|
// if path is scalar and no assoc or has no type (@Core.Computed) use original expression
|
|
1122
1136
|
// only do the expansion on (managed) assocs and (items.)elements, array of check in ON cond is done elsewhere
|
|
1123
|
-
const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art:
|
|
1124
|
-
const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art:
|
|
1137
|
+
const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art: lhsArt, ref: lhs.ref }, false, true );
|
|
1138
|
+
const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art: rhsArt, ref: rhs.ref }, false, true );
|
|
1125
1139
|
|
|
1126
1140
|
// mapping dict for lhs/rhs for mismatch check
|
|
1127
1141
|
// strip lhs/rhs prefix from flattened paths to check remaining common trailing path
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
|
|
4
|
-
|
|
4
|
+
const { makeMessageFunction } = require('../base/messages');
|
|
5
5
|
const { recompileX } = require('../compiler/index');
|
|
6
|
-
|
|
6
|
+
const { linkToOrigin, pathName } = require('../compiler/utils');
|
|
7
7
|
const {compactModel, compactExpr} = require('../json/to-csn');
|
|
8
8
|
const { deduplicateMessages } = require('../base/messages');
|
|
9
|
-
const timetrace = require('../utils/timetrace');
|
|
9
|
+
const { timetrace } = require('../utils/timetrace');
|
|
10
10
|
// Paths that start with an artifact of protected kind are special
|
|
11
11
|
// either ignore them in QAT building or in path rewriting
|
|
12
12
|
const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
|
|
@@ -179,7 +179,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
179
179
|
let joinTree = query.from;
|
|
180
180
|
for(let tan in query.$tableAliases)
|
|
181
181
|
{
|
|
182
|
-
if(
|
|
182
|
+
if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
|
|
183
183
|
{
|
|
184
184
|
let ta = query.$tableAliases[tan];
|
|
185
185
|
joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
|
|
@@ -200,7 +200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
200
200
|
function createQAForFromClauseSubQuery(query, env)
|
|
201
201
|
{
|
|
202
202
|
for (let taName in query.$tableAliases) {
|
|
203
|
-
if (
|
|
203
|
+
if (query.$tableAliases[taName].kind !== '$self') {
|
|
204
204
|
let ta = query.$tableAliases[taName];
|
|
205
205
|
if(!ta.$QA) {
|
|
206
206
|
ta.$QA = createQA(env, ta._origin, taName, undefined);
|
|
@@ -241,14 +241,6 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
241
241
|
{
|
|
242
242
|
art.$QA = createQA(env, art.target._artifact, art.name.id );
|
|
243
243
|
art.$QA.mixin = true;
|
|
244
|
-
/* Mark mixin definition to be _ignored:
|
|
245
|
-
- If the mixin is used, it is now resolved into a join => definition vaporizes
|
|
246
|
-
- If the mixin is published, forHana backend must create a __copy with rewritten
|
|
247
|
-
$projection ON conditon and publish it with alias.
|
|
248
|
-
- If the mixin is neither be used nor published it shall not be visible to the database
|
|
249
|
-
(internal mixin).
|
|
250
|
-
*/
|
|
251
|
-
art.$a2j = { _ignore: true };
|
|
252
244
|
}
|
|
253
245
|
});
|
|
254
246
|
}
|
|
@@ -317,13 +309,13 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
317
309
|
let [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
|
|
318
310
|
if(!QA) {
|
|
319
311
|
error(null, pathNode.$location,
|
|
320
|
-
{ name: pathNode.path
|
|
312
|
+
{ name: pathName(pathNode.path) },
|
|
321
313
|
'Please debug me: No QA found for generic path rewriting in $(NAME)')
|
|
322
314
|
return;
|
|
323
315
|
}
|
|
324
316
|
// if the found QA is the mixin QA and if the path length is one,
|
|
325
317
|
// this indicates the publishing of a mixin assoc, don't rewrite the path
|
|
326
|
-
if(QA.mixin && tail.length
|
|
318
|
+
if(QA.mixin && tail.length === 1)
|
|
327
319
|
return;
|
|
328
320
|
let pos = tail.indexOf(ps);
|
|
329
321
|
// cut off ps if it's a join relevant association with postfix
|
|
@@ -678,7 +670,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
678
670
|
if(fwdAssoc)
|
|
679
671
|
{
|
|
680
672
|
//env.assocStack.includes(fwdAssoc) => recursion
|
|
681
|
-
if(env.assocStack.length
|
|
673
|
+
if(env.assocStack.length === 2) {
|
|
682
674
|
// reuse (ugly) error message from forHana
|
|
683
675
|
error(null, env.assocStack[0].location,
|
|
684
676
|
{ name: '$self', id: '$self' },
|
|
@@ -710,7 +702,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
710
702
|
// ON cond of the forward assoc with swapped src/tgt aliases
|
|
711
703
|
let fwdAssoc = getForwardAssociationExpr(expr);
|
|
712
704
|
if(fwdAssoc) {
|
|
713
|
-
if(env.assocStack.length
|
|
705
|
+
if(env.assocStack.length === 2) {
|
|
714
706
|
// reuse (ugly) error message from forHana
|
|
715
707
|
error(null, expr.location, 'An association that uses “$self” in its ON-condition can\'t be compared to “$self”');
|
|
716
708
|
// don't check these paths again
|
|
@@ -744,7 +736,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
744
736
|
let newSrcAlias = tgtAlias;
|
|
745
737
|
let newTgtAlias = {};
|
|
746
738
|
// first try to identify table alias for complex views or redirected associations
|
|
747
|
-
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
739
|
+
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
748
740
|
// redirected target must have a $QA
|
|
749
741
|
fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA &&
|
|
750
742
|
// $QA's artifact must either be same srcAlias artifact
|
|
@@ -920,7 +912,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
920
912
|
combined list of elements made available by the from clause.
|
|
921
913
|
*/
|
|
922
914
|
let _navigation = undefined; // don't modify original path
|
|
923
|
-
if(env.assocStack.length
|
|
915
|
+
if(env.assocStack.length === 2) {
|
|
924
916
|
// a mixin assoc cannot have a structure prefix, it's sufficient to check head
|
|
925
917
|
if(head.id === env.assocStack.id()) {
|
|
926
918
|
// source side from view point of view (target side from forward point of view)
|
|
@@ -982,7 +974,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
982
974
|
|
|
983
975
|
// Return the original association if expr is a backlink term, undefined otherwise
|
|
984
976
|
function getForwardAssociationExpr(expr) {
|
|
985
|
-
if(expr.op && expr.op.val === '=' && expr.args.length
|
|
977
|
+
if(expr.op && expr.op.val === '=' && expr.args.length === 2) {
|
|
986
978
|
return getForwardAssociation(expr.args[0].path, expr.args[1].path);
|
|
987
979
|
}
|
|
988
980
|
return undefined;
|
|
@@ -991,10 +983,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
991
983
|
function getForwardAssociation(lhs, rhs) {
|
|
992
984
|
// [alpha.]BACKLINK.[beta.]FORWARD
|
|
993
985
|
if(lhs && rhs) {
|
|
994
|
-
if(rhs.length
|
|
986
|
+
if(rhs.length === 1 && rhs[0].id === '$self' &&
|
|
995
987
|
lhs.length > 1 && hasPrefix(lhs))
|
|
996
988
|
return lhs[lhs.length-1]._artifact;
|
|
997
|
-
if(lhs.length
|
|
989
|
+
if(lhs.length === 1 && lhs[0].id === '$self' &&
|
|
998
990
|
rhs.length > 1 && hasPrefix(rhs))
|
|
999
991
|
return rhs[rhs.length-1]._artifact;
|
|
1000
992
|
}
|
|
@@ -1403,7 +1395,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1403
1395
|
|
|
1404
1396
|
let [head, ...tail] = path;
|
|
1405
1397
|
|
|
1406
|
-
if(['$projection', '$self'].includes(head.id) && tail.length) {
|
|
1398
|
+
if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
|
|
1407
1399
|
// make sure not to truncate tail
|
|
1408
1400
|
if(tail.length > 1)
|
|
1409
1401
|
[head, ...tail] = tail;
|