@sap/cds-compiler 2.5.0 → 2.10.4
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 +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
|
@@ -6,7 +6,7 @@ const { getUtils, cloneCsn, forEachGeneric,
|
|
|
6
6
|
forEachMemberRecursively, forEachRef,
|
|
7
7
|
forAllQueries, forAllElements, hasAnnotationValue, getArtifactDatabaseNameOf,
|
|
8
8
|
getElementDatabaseNameOf, isBuiltinType, applyTransformations,
|
|
9
|
-
isPersistedOnDatabase, getNormalizedQuery, isAspect,
|
|
9
|
+
isPersistedOnDatabase, getNormalizedQuery, isAspect, walkCsnPath,
|
|
10
10
|
} = require('../model/csnUtils');
|
|
11
11
|
const { makeMessageFunction } = require('../base/messages');
|
|
12
12
|
const transformUtils = require('./transformUtilsNew');
|
|
@@ -22,8 +22,11 @@ const handleExists = require('./db/transformExists');
|
|
|
22
22
|
const { usesMixinAssociation, getMixinAssocOfQueryIfPublished } = require('./db/helpers');
|
|
23
23
|
const replaceAssociationsInGroupByOrderBy = require('./db/groupByOrderBy');
|
|
24
24
|
const _forEachDefinition = require('../model/csnUtils').forEachDefinition;
|
|
25
|
+
const flattening = require('./db/flattening');
|
|
26
|
+
const expansion = require('./db/expansion');
|
|
25
27
|
const assertUnique = require('./db/assertUnique');
|
|
26
28
|
const generateDrafts = require('./db/draft');
|
|
29
|
+
const enrichUniversalCsn = require('./universalCsnEnricher');
|
|
27
30
|
|
|
28
31
|
// By default: Do not process non-entities/views
|
|
29
32
|
function forEachDefinition(csn, cb) {
|
|
@@ -36,12 +39,6 @@ function forEachDefinition(csn, cb) {
|
|
|
36
39
|
* The behavior is controlled by the following options:
|
|
37
40
|
* options = {
|
|
38
41
|
* forHana.names // See the behavior of 'names' in toHana, toSql and toRename
|
|
39
|
-
* forHana.keepNamespaces // Do not transform namespaces to contexts (to be used for
|
|
40
|
-
* // producing HANA-CDS compatible names with 'toHana', 'toSql' ...)
|
|
41
|
-
* forHana.keepStructsAssocs // Do not flatten structs, do not convert managed assocs to
|
|
42
|
-
* // unmanaged ones, do not convert assocs to joins (to be used
|
|
43
|
-
* // for rendering strictly HANA-CDS compatible CDS source with
|
|
44
|
-
* // 'toHana')
|
|
45
42
|
* forHana.alwaysResolveDerivedTypes // Always resolve derived type chains (by default, this is only
|
|
46
43
|
* // done for 'quoted' names). FIXME: Should always be done in general.
|
|
47
44
|
* }
|
|
@@ -50,12 +47,11 @@ function forEachDefinition(csn, cb) {
|
|
|
50
47
|
* - (000) Some primitive type names are mapped to HANA type names (e.g. DateTime => UTCDateTime,
|
|
51
48
|
* Date => LocalDate, ...).The primitive type 'UUID' is renamed to 'String' (see also 060 below).
|
|
52
49
|
* - (001) Add a temporal where condition to views where applicable before assoc2join
|
|
53
|
-
* - (010) (not for
|
|
50
|
+
* - (010) (not for to.hdbcds with hdbcds names): Transform associations to joins
|
|
54
51
|
* - (015) Draft shadow entities are generated for entities/views annotated with '@odata.draft.enabled'.
|
|
55
52
|
* - (020) Check: in "plain" mode, quoted ids are not allowed.
|
|
56
53
|
* (a) check in namespace declarations
|
|
57
54
|
* (b) check in artifact/element definitions.
|
|
58
|
-
* - (030) For all elements, derived types are replaced by their final base type.
|
|
59
55
|
* - (040) Abstract entities and entities 'implemented in' something are ignored, as well
|
|
60
56
|
* as entities annotated with '@cds.persistence.skip' or '@cds.persistence.exists'.
|
|
61
57
|
* - (050) Checks on the hierarchical model (pre-flattening)
|
|
@@ -69,10 +65,9 @@ function forEachDefinition(csn, cb) {
|
|
|
69
65
|
* - (100) 'masked' is ignored (a), and attribute 'localized' is removed (b)
|
|
70
66
|
* - (110) Actions and functions (bound or unbound) are ignored.
|
|
71
67
|
* - (120) (a) Services become contexts.
|
|
72
|
-
*
|
|
73
|
-
* - (130) (not for 'keepStructsAssocs'): Elements having structured types are flattened into
|
|
68
|
+
* - (130) (not for to.hdbcds with hdbcds names): Elements having structured types are flattened into
|
|
74
69
|
* multiple elements (using '_' or '.' as name separator, depending on 'forHana.names').
|
|
75
|
-
* - (140) (not for
|
|
70
|
+
* - (140) (not for to.hdbcds with hdbcds names): Managed associations get explicit ON-conditions, with
|
|
76
71
|
* generated foreign key elements (also using '_' or '.' as name separator, depending on 'forHana.names').
|
|
77
72
|
* - (150) (a) Elements from inherited (included) entities are copied into the receiving entity
|
|
78
73
|
* (b) The 'include' property is removed from entities.
|
|
@@ -86,10 +81,10 @@ function forEachDefinition(csn, cb) {
|
|
|
86
81
|
* (a) enum constants in defaults are replaced by their values (assuming a matching enum as element type)
|
|
87
82
|
* (b) the enum-ness is stripped off (i.e. the enum type is replaced by its final base type).
|
|
88
83
|
* - (200) The 'key' property is removed from all elements of types.
|
|
89
|
-
* - (210) (not for
|
|
84
|
+
* - (210) (not for to.hdbcds with hdbcds names): Managed associations in GROUP BY and ORDER BY are
|
|
90
85
|
* replaced by by their foreign key fields.
|
|
91
86
|
* - (220) Contexts that contain no artifacts or only ignored artifacts are ignored.
|
|
92
|
-
* - (230) (only for
|
|
87
|
+
* - (230) (only for to.hdbcds with hdbcds names): The following are rejected in views
|
|
93
88
|
* (a) Structured elements
|
|
94
89
|
* (b) Managed association elements
|
|
95
90
|
* (c) Managed association entries in GROUP BY
|
|
@@ -121,14 +116,20 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
121
116
|
/** @type {() => void} */
|
|
122
117
|
let throwWithError;
|
|
123
118
|
let artifactRef, inspectRef, queryOrMain, effectiveType, // csnRefs
|
|
124
|
-
addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType, getFinalBaseType
|
|
119
|
+
addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType, getFinalBaseType, // transformUtils
|
|
120
|
+
get$combined; // csnUtils
|
|
125
121
|
|
|
126
122
|
bindCsnReference();
|
|
127
123
|
|
|
128
124
|
throwWithError(); // reclassify and throw in case of non-configurable errors
|
|
125
|
+
|
|
126
|
+
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
|
|
127
|
+
enrichUniversalCsn(csn, options);
|
|
128
|
+
bindCsnReference();
|
|
129
|
+
}
|
|
129
130
|
|
|
130
131
|
const dialect = options.forHana && options.forHana.dialect || options.toSql && options.toSql.dialect;
|
|
131
|
-
const doA2J = !options.
|
|
132
|
+
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
132
133
|
if (!doA2J)
|
|
133
134
|
forEachDefinition(csn, handleMixinOnConditions);
|
|
134
135
|
|
|
@@ -142,25 +143,48 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
142
143
|
// subsequent procession steps (especially a2j) to see plain paths in expressions.
|
|
143
144
|
// If errors are detected, throwWithError() will return from further processing
|
|
144
145
|
|
|
145
|
-
|
|
146
|
+
expandStructsInExpression(csn, { drillRef: true });
|
|
146
147
|
|
|
147
148
|
throwWithError();
|
|
148
149
|
|
|
149
150
|
// FIXME: This does something very similar to cloneWithTransformations -> refactor?
|
|
150
151
|
const transformCsn = transformUtils.transformModel;
|
|
151
152
|
|
|
152
|
-
handleExists(csn, error);
|
|
153
|
+
handleExists(csn, options, error);
|
|
153
154
|
|
|
154
155
|
// (001) Add a temporal where condition to views where applicable before assoc2join
|
|
155
156
|
// assoc2join eventually rewrites the table aliases
|
|
156
157
|
forEachDefinition(csn, addTemporalWhereConditionToView);
|
|
157
158
|
|
|
158
|
-
|
|
159
|
+
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
159
160
|
assertUnique.prepare(csn, options, error, info);
|
|
160
161
|
|
|
162
|
+
if(doA2J) {
|
|
163
|
+
// Expand a structured thing in: keys, columns, order by, group by
|
|
164
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithError});
|
|
165
|
+
bindCsnReference();
|
|
166
|
+
}
|
|
167
|
+
|
|
161
168
|
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
162
169
|
cleanup();
|
|
163
170
|
|
|
171
|
+
bindCsnReferenceOnly();
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if(doA2J) {
|
|
175
|
+
const resolved = new WeakMap();
|
|
176
|
+
// No refs with struct-steps exist anymore
|
|
177
|
+
flattening.flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter);
|
|
178
|
+
// No type references exist anymore
|
|
179
|
+
// Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
|
|
180
|
+
flattening.resolveTypeReferences(csn, options, resolved, pathDelimiter);
|
|
181
|
+
// No structured elements exists anymore
|
|
182
|
+
flattening.flattenElements(csn, options, pathDelimiter, error);
|
|
183
|
+
} else {
|
|
184
|
+
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
185
|
+
flattening.resolveTypeReferences(csn, options, undefined, pathDelimiter);
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
// (010) If requested, translate associations to joins
|
|
165
189
|
if (doA2J)
|
|
166
190
|
handleAssocToJoins();
|
|
@@ -188,6 +212,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
188
212
|
}
|
|
189
213
|
});
|
|
190
214
|
|
|
215
|
+
// Must happen after A2J, as A2J needs $self to correctly resolve stuff
|
|
216
|
+
if(doA2J)
|
|
217
|
+
flattening.removeLeadingSelf(csn);
|
|
218
|
+
|
|
191
219
|
const {
|
|
192
220
|
flattenStructuredElement,
|
|
193
221
|
flattenStructStepsInRef, getForeignKeyArtifact,
|
|
@@ -242,13 +270,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
242
270
|
},
|
|
243
271
|
}, true);
|
|
244
272
|
|
|
245
|
-
// (030) - For all elements, replace derived types by final base type
|
|
246
|
-
forEachDefinition(csn, (artifact) => {
|
|
247
|
-
forEachMemberRecursively(artifact, (member) => {
|
|
248
|
-
toFinalBaseType(member);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
273
|
// (040) Ignore entities and views that are abstract or implemented
|
|
253
274
|
// or carry the annotation cds.persistence.skip/exists
|
|
254
275
|
// These entities are not removed from the csn, but flagged as "to be ignored"
|
|
@@ -260,57 +281,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
260
281
|
// Temporal only in beta-mode
|
|
261
282
|
forEachDefinition(csn, handleTemporalAnnotations);
|
|
262
283
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// the following step can rely on the rest of the model having
|
|
266
|
-
// a certain structure, i.e. structs unfolded
|
|
267
|
-
if(!options.forHana.keepStructsAssocs) {
|
|
268
|
-
const flatteningOfStructured = [];
|
|
269
|
-
const adaptRefs = [];
|
|
270
|
-
// A2J resolves paths in queries to their flattened version,
|
|
271
|
-
// that is the foreign key of a managed assoc that will be generated in a later step
|
|
272
|
-
// we need to adapt the refs after the foreign keys are generated
|
|
273
|
-
const adaptQueryRefLater = [];
|
|
274
|
-
|
|
275
|
-
const fkRefs = new WeakMap();
|
|
276
|
-
|
|
277
|
-
applyTransformations(csn, {
|
|
278
|
-
keys: (parent, prop, keys) => {
|
|
279
|
-
keys.forEach(key => fkRefs.set(key.ref, true));
|
|
280
|
-
},
|
|
281
|
-
ref: (parent, prop, ref, path) => {
|
|
282
|
-
// Do not process fk refs - no need to adapt
|
|
283
|
-
if(fkRefs.has(ref))
|
|
284
|
-
return;
|
|
285
|
-
|
|
286
|
-
setProp(parent, '$path', [...path]);
|
|
287
|
-
const lastRef = ref[ref.length-1];
|
|
288
|
-
const fn = () => {
|
|
289
|
-
const scopedPath = [...parent.$path];
|
|
290
|
-
parent.ref = flattenStructStepsInRef(ref, scopedPath);
|
|
291
|
-
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
292
|
-
if (parent.ref[ref.length - 1] != lastRef && insideColumns(scopedPath) && !parent.as)
|
|
293
|
-
parent.as = lastRef;
|
|
294
|
-
};
|
|
295
|
-
// adapt queries later
|
|
296
|
-
const enclosingArtifact = csn.definitions[path[1]];
|
|
297
|
-
if(enclosingArtifact.query)
|
|
298
|
-
adaptQueryRefLater.push(fn);
|
|
299
|
-
else
|
|
300
|
-
adaptRefs.push(fn);
|
|
301
|
-
}
|
|
302
|
-
}, [(definitions, artifactName, artifact) => flatteningOfStructured.push(() => flattenStructuredElements(artifact, artifactName))]);
|
|
303
|
-
|
|
304
|
-
adaptRefs.forEach(fn => fn());
|
|
305
|
-
flatteningOfStructured.forEach(fn => fn());
|
|
306
|
-
handleManagedAssociationsAndCreateForeignKeys();
|
|
307
|
-
// now the foreign key references in queries are resolvable
|
|
308
|
-
bindCsnReferenceOnly();
|
|
309
|
-
adaptQueryRefLater.forEach(fn => fn());
|
|
310
|
-
}else {
|
|
311
|
-
handleManagedAssociationsAndCreateForeignKeys();
|
|
312
|
-
}
|
|
313
|
-
|
|
284
|
+
handleManagedAssociationsAndCreateForeignKeys();
|
|
285
|
+
|
|
314
286
|
function handleManagedAssociationsAndCreateForeignKeys() {
|
|
315
287
|
forEachDefinition(csn, (art, artName) => handleManagedAssociationFKs(art, artName));
|
|
316
288
|
forEachDefinition(csn, (art, artName) => createForeignKeyElements(art, artName));
|
|
@@ -366,8 +338,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
366
338
|
* hence we do not generate the referential constraints for them.
|
|
367
339
|
*/
|
|
368
340
|
const validOptionsForConstraint = () => {
|
|
369
|
-
return (options.forHana.dialect === 'sqlite' || options.forHana.dialect === 'hana') &&
|
|
370
|
-
!(options.toHana && options.toHana.names === 'hdbcds')
|
|
341
|
+
return (options.forHana.dialect === 'sqlite' || options.forHana.dialect === 'hana') && doA2J;
|
|
371
342
|
}
|
|
372
343
|
if(validOptionsForConstraint())
|
|
373
344
|
createReferentialConstraints(csn, options);
|
|
@@ -390,12 +361,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
390
361
|
// Recursively apply transformCommon and attach @cds.persistence.name
|
|
391
362
|
forEachDefinition(csn, recursivelyApplyCommon);
|
|
392
363
|
|
|
393
|
-
// (20 a) If we keep associations as they are (hdbcds naming convention), we cannot have structured
|
|
394
|
-
// view elements (we could enumerate the elements but we can't give them the names one would expect)
|
|
395
|
-
// Check foreign keys of redirected associations
|
|
396
|
-
// (200) Strip 'key' property from type elements
|
|
397
|
-
forEachDefinition(csn, recursiveChecks);
|
|
398
|
-
|
|
399
364
|
const checkConstraintIdentifiers = (artifact, artifactName, prop, path) => {
|
|
400
365
|
assertConstraintIdentifierUniqueness(artifact, artifactName, path, error);
|
|
401
366
|
};
|
|
@@ -424,7 +389,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
424
389
|
/* Check Type Parameters (precision, scale, length ...) */
|
|
425
390
|
checkTypeParameters,
|
|
426
391
|
/* Filter out aspects/types/abstract entities containing managed compositions of anonymous aspects */
|
|
427
|
-
ignoreNonPersistedArtifactsWithAnonymousAspectComposition
|
|
392
|
+
ignoreNonPersistedArtifactsWithAnonymousAspectComposition,
|
|
393
|
+
// (200) Strip 'key' property from type elements
|
|
394
|
+
removeKeyPropInType,
|
|
428
395
|
]);
|
|
429
396
|
|
|
430
397
|
throwWithError();
|
|
@@ -439,7 +406,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
439
406
|
'_ignore': function (parent, a, b, path){
|
|
440
407
|
if(path.length > 2) {
|
|
441
408
|
const tail = path[path.length-1];
|
|
442
|
-
const
|
|
409
|
+
const parentPath = path.slice(0, -1)
|
|
410
|
+
const parentParent = walkCsnPath(csn, parentPath);
|
|
443
411
|
delete parentParent[tail];
|
|
444
412
|
} else {
|
|
445
413
|
delete parent._ignore;
|
|
@@ -478,7 +446,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
478
446
|
* @param {string} artName
|
|
479
447
|
*/
|
|
480
448
|
function createForeignKeyElements(art, artName) {
|
|
481
|
-
if ((art.kind === 'entity' || art.kind === 'view') &&
|
|
449
|
+
if ((art.kind === 'entity' || art.kind === 'view') && doA2J) {
|
|
482
450
|
forAllElements(art, artName, (parent, elements, pathToElements) => {
|
|
483
451
|
const elementsArray = [];
|
|
484
452
|
forEachGeneric(parent, 'elements', (element, elemName) => {
|
|
@@ -529,7 +497,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
529
497
|
function bindCsnReference(){
|
|
530
498
|
({ error, warning, info, throwWithError } = makeMessageFunction(csn, options, moduleName));
|
|
531
499
|
({ artifactRef, inspectRef, queryOrMain, effectiveType } = csnRefs(csn));
|
|
532
|
-
({ getFinalBaseType } = getUtils(csn));
|
|
500
|
+
({ getFinalBaseType, get$combined } = getUtils(csn));
|
|
533
501
|
({ addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
534
502
|
}
|
|
535
503
|
|
|
@@ -568,12 +536,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
568
536
|
if(onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')){
|
|
569
537
|
const { links } = inspectRef(path.concat(['on', i]));
|
|
570
538
|
if(links){
|
|
571
|
-
// no assocs in $projection / $self paths #5050
|
|
572
|
-
links.forEach((element, j) => {
|
|
573
|
-
const { art } = links[j];
|
|
574
|
-
if(art && art.target) // Fine for plain
|
|
575
|
-
error(null, path.concat(['on', j]), `Step "${onConditionPart.ref[j]}" in path "${onConditionPart.ref.join('.')}" must not be an association`);
|
|
576
|
-
});
|
|
577
539
|
columnToReplace = onConditionPart.ref[links.length - 1];
|
|
578
540
|
}
|
|
579
541
|
}
|
|
@@ -648,16 +610,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
648
610
|
* @param {CSN.Artifact} artifact
|
|
649
611
|
* @param {string} artifactName
|
|
650
612
|
*/
|
|
651
|
-
function
|
|
613
|
+
function removeKeyPropInType(artifact, artifactName) {
|
|
652
614
|
if (!artifact._ignore) {
|
|
653
|
-
forEachMemberRecursively(artifact, (member
|
|
654
|
-
if (options.forHana.keepStructsAssocs &&
|
|
655
|
-
artifact.query &&
|
|
656
|
-
isStructured(member)) {
|
|
657
|
-
error(null, path, `With "hdbcds" naming, structured elements can't be used in a view`);
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
|
|
615
|
+
forEachMemberRecursively(artifact, (member) => {
|
|
661
616
|
if (artifact.kind === 'type' && member.key)
|
|
662
617
|
delete member.key;
|
|
663
618
|
}, [ 'definitions', artifactName ]);
|
|
@@ -720,7 +675,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
720
675
|
if (artifact.params) {
|
|
721
676
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
722
677
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
723
|
-
error(null, path,
|
|
678
|
+
error(null, path, 'Unexpected association in parameterized view');
|
|
724
679
|
}
|
|
725
680
|
else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
|
|
726
681
|
// UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
|
|
@@ -729,7 +684,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
729
684
|
if (csn.definitions[member.target].params) {
|
|
730
685
|
// HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
|
|
731
686
|
// SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
|
|
732
|
-
error(null, path,
|
|
687
|
+
error(null, path, 'Unexpected parameterized association target');
|
|
733
688
|
}
|
|
734
689
|
else if(csn.definitions[member.target]['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
|
|
735
690
|
// HANA won't check the assoc target but when querying an association with target UDF, this is the error:
|
|
@@ -760,7 +715,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
760
715
|
const elem = elements[elemName];
|
|
761
716
|
// (140) Generate foreign key elements and ON-condition for managed associations
|
|
762
717
|
// (unless explicitly asked to keep assocs unchanged)
|
|
763
|
-
if (
|
|
718
|
+
if (doA2J) {
|
|
764
719
|
if (isManagedAssociationElement(elem))
|
|
765
720
|
transformManagedAssociation(parent, artifactName, elem, elemName);
|
|
766
721
|
}
|
|
@@ -771,7 +726,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
771
726
|
|
|
772
727
|
function fixBorkedElementsOfLocalized(elements, pathToElements){
|
|
773
728
|
const pathToNonLocalized = ['definitions', pathToElements[1].replace('localized.',''), ...pathToElements.slice(2)];
|
|
774
|
-
const nonLocalizedElements = walkCsnPath(pathToNonLocalized);
|
|
729
|
+
const nonLocalizedElements = walkCsnPath(csn, pathToNonLocalized);
|
|
775
730
|
|
|
776
731
|
|
|
777
732
|
for(const elementName in elements){
|
|
@@ -818,7 +773,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
818
773
|
else {
|
|
819
774
|
for (const pname in artifact.params) {
|
|
820
775
|
if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
|
|
821
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ], `
|
|
776
|
+
warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
|
|
822
777
|
}
|
|
823
778
|
else if (options.forHana.names !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
|
|
824
779
|
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.forHana.names },
|
|
@@ -872,9 +827,12 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
872
827
|
}
|
|
873
828
|
|
|
874
829
|
function handleAssocToJoins() {
|
|
830
|
+
// With flattening errors, it makes little sense to continue.
|
|
831
|
+
throwWithError();
|
|
875
832
|
// the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
|
|
876
833
|
// simply make it invisible and copy it over to the result csn
|
|
877
834
|
forEachDefinition(csn, art => art.technicalConfig && setProp(art, 'technicalConfig', art.technicalConfig));
|
|
835
|
+
|
|
878
836
|
const newCsn = translateAssocsToJoinsCSN(csn, options);
|
|
879
837
|
|
|
880
838
|
// restore all (non-enumerable) properties that wouldn't survive reaugmentation/compactification into the new compact model
|
|
@@ -896,7 +854,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
896
854
|
const mixinElement = q.SELECT.mixin[mixinName];
|
|
897
855
|
if (mixinElement._ignore && options.toSql) {
|
|
898
856
|
columnClearer.push(() => {
|
|
899
|
-
const query = walkCsnPath(p);
|
|
857
|
+
const query = walkCsnPath(csn, p);
|
|
900
858
|
for(let i = query.columns.length-1; i > -1; i--){
|
|
901
859
|
const col = query.columns[i];
|
|
902
860
|
if(col && col.ref && col.ref[0] === mixinName){
|
|
@@ -1056,127 +1014,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1056
1014
|
}
|
|
1057
1015
|
}
|
|
1058
1016
|
|
|
1059
|
-
|
|
1060
|
-
/**
|
|
1061
|
-
* Compute and return $combined for the given query.
|
|
1062
|
-
*
|
|
1063
|
-
* @param {CSN.Query} query
|
|
1064
|
-
* @returns {object}
|
|
1065
|
-
*/
|
|
1066
|
-
function get$combined(query) {
|
|
1067
|
-
const sources = getSources(query);
|
|
1068
|
-
return sources;
|
|
1069
|
-
|
|
1070
|
-
/**
|
|
1071
|
-
* Get the union of all elements from the from clause
|
|
1072
|
-
* - descend into unions, following the lead query
|
|
1073
|
-
* - merge all queries in case of joins
|
|
1074
|
-
* - follow subqueries
|
|
1075
|
-
*
|
|
1076
|
-
* @param {CSN.Query} query Query to check
|
|
1077
|
-
* @returns {object} Map of sources
|
|
1078
|
-
*/
|
|
1079
|
-
function getSources(query) {
|
|
1080
|
-
// Remark CW: better just a while along query.SET.args[0]
|
|
1081
|
-
if (query.SET) {
|
|
1082
|
-
if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
|
|
1083
|
-
return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
|
|
1084
|
-
|
|
1085
|
-
return getSources(query.SET.args[0]);
|
|
1086
|
-
}
|
|
1087
|
-
else if (query.SELECT) {
|
|
1088
|
-
if (query.SELECT.from.args) {
|
|
1089
|
-
return walkArgs(query.SELECT.from.args);
|
|
1090
|
-
}
|
|
1091
|
-
else if (query.SELECT.from.ref) {
|
|
1092
|
-
const art = artifactRef(query.SELECT.from);
|
|
1093
|
-
return mergeElementsIntoMap(Object.create(null), art.elements, art.$location,
|
|
1094
|
-
query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
|
|
1095
|
-
query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
|
|
1096
|
-
}
|
|
1097
|
-
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
1098
|
-
return getSources(query.SELECT.from);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
function walkArgs(args) {
|
|
1103
|
-
let elements = Object.create(null);
|
|
1104
|
-
for (const arg of args) {
|
|
1105
|
-
if (arg.args) {
|
|
1106
|
-
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
1107
|
-
}
|
|
1108
|
-
else if (arg.ref) {
|
|
1109
|
-
const art = artifactRef(arg);
|
|
1110
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
1111
|
-
}
|
|
1112
|
-
else if (arg.SELECT || arg.SET) {
|
|
1113
|
-
elements = mergeElementMaps(elements, getSources(arg));
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
return elements;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
return {};
|
|
1121
|
-
|
|
1122
|
-
/**
|
|
1123
|
-
* Merge two maps of elements together
|
|
1124
|
-
*
|
|
1125
|
-
* @param {object} mapA Map a - will be returned
|
|
1126
|
-
* @param {object} mapB Map b - will not be returned
|
|
1127
|
-
* @returns {object} mapA
|
|
1128
|
-
*/
|
|
1129
|
-
function mergeElementMaps(mapA, mapB) {
|
|
1130
|
-
for (const elementName in mapB) {
|
|
1131
|
-
if (!mapA[elementName])
|
|
1132
|
-
mapA[elementName] = [];
|
|
1133
|
-
|
|
1134
|
-
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
return mapA;
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
/**
|
|
1141
|
-
* Merge elements into an existing map
|
|
1142
|
-
*
|
|
1143
|
-
* @param {any} existingMap map to merge into - will be returned
|
|
1144
|
-
* @param {object} elements elements to merge into the map
|
|
1145
|
-
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
1146
|
-
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
1147
|
-
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
1148
|
-
* @returns {object} existingMap
|
|
1149
|
-
*/
|
|
1150
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
1151
|
-
for (const elementName in elements) {
|
|
1152
|
-
const element = elements[elementName];
|
|
1153
|
-
if (!existingMap[elementName])
|
|
1154
|
-
existingMap[elementName] = [];
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
existingMap[elementName].push({
|
|
1158
|
-
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
return existingMap;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Return the name part of the artifact name - no namespace etc.
|
|
1168
|
-
* @param {string|object} name Absolute name of the artifact
|
|
1169
|
-
*/
|
|
1170
|
-
function getBaseName(name) {
|
|
1171
|
-
if (!name)
|
|
1172
|
-
return name;
|
|
1173
|
-
|
|
1174
|
-
if (name.id)
|
|
1175
|
-
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
1176
|
-
|
|
1177
|
-
return name.substring( name.lastIndexOf('.')+1 )
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
1017
|
/**
|
|
1181
1018
|
* Get all elements tagged with @cds.valid.from/to from the union of all entities of the from-clause.
|
|
1182
1019
|
*
|
|
@@ -1261,16 +1098,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1261
1098
|
replaceEnumSymbolsByValues(obj, path);
|
|
1262
1099
|
}
|
|
1263
1100
|
|
|
1264
|
-
function walkCsnPath(path) {
|
|
1265
|
-
/** @type {object} */
|
|
1266
|
-
let obj = csn;
|
|
1267
|
-
for(let i = 0; i < path.length; i++){
|
|
1268
|
-
obj = obj[path[i]];
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
return obj;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
1101
|
// Change the names of those builtin types that have different names in HANA.
|
|
1275
1102
|
// (do that directly in the csn where the builtin types are defined, so that
|
|
1276
1103
|
// all users of the types benefit from it). Also add the type parameter 'length'
|
|
@@ -1357,7 +1184,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1357
1184
|
});
|
|
1358
1185
|
}
|
|
1359
1186
|
}
|
|
1360
|
-
if (query && options.
|
|
1187
|
+
if (query && options.transformation === 'hdbcds') {
|
|
1361
1188
|
// check all queries/subqueries for mixin publishing inside of unions -> forbidden in hdbcds
|
|
1362
1189
|
if (query.SELECT && query.SELECT.mixin && path.indexOf('SET') !== -1) {
|
|
1363
1190
|
for (const elementName in elements) {
|
|
@@ -1425,7 +1252,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1425
1252
|
|
|
1426
1253
|
// For associations - make sure that the foreign keys have the same "style"
|
|
1427
1254
|
// If A.assoc => A.assoc_id, else if assoc => assoc_id or assoc as Assoc => Assoc_id
|
|
1428
|
-
if (elem.keys &&
|
|
1255
|
+
if (elem.keys && doA2J) {
|
|
1429
1256
|
const assoc_col = columnMap[elemName];
|
|
1430
1257
|
if (assoc_col && assoc_col.ref) {
|
|
1431
1258
|
elem.keys.forEach((key) => {
|
|
@@ -1449,7 +1276,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1449
1276
|
}
|
|
1450
1277
|
// Add flattened structured things preserving aliases and refs with/without table alias
|
|
1451
1278
|
// If we add them when we get to them in "elements", we cannot know what table alias was used...
|
|
1452
|
-
if (isStructured(elem) &&
|
|
1279
|
+
if (isStructured(elem) && doA2J) {
|
|
1453
1280
|
const col = columnMap[elemName];
|
|
1454
1281
|
const originalName = col.ref[col.ref.length - 1];
|
|
1455
1282
|
const flatElements = flattenStructuredElement(elem, originalName, [], path);
|
|
@@ -1474,21 +1301,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1474
1301
|
if (!elem.on && !elem._ignore)
|
|
1475
1302
|
hasNonAssocElements = true;
|
|
1476
1303
|
|
|
1477
|
-
|
|
1478
|
-
// (230 b) If we keep associations as they are (hdbcds naming convention), we cannot have managed associations
|
|
1479
|
-
// as view elements (their foreign keys cannot be addressed in the view)
|
|
1480
|
-
if (options.forHana.keepStructsAssocs &&
|
|
1481
|
-
query &&
|
|
1482
|
-
isAssocOrComposition(elem.type) &&
|
|
1483
|
-
!elem.on) {
|
|
1484
|
-
error(null, [ 'definitions', artName, 'elements', elemName ], `With "hdbcds" naming, managed association elements can't be used in a view`);
|
|
1485
|
-
continue;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1304
|
// (180 b) Create MIXINs for association elements in projections or views (those that are not mixins by themselves)
|
|
1489
1305
|
// CDXCORE-585: Allow mixin associations to be used and published in parallel
|
|
1490
1306
|
if (query !== undefined && elem.target) {
|
|
1491
|
-
if(isUnion(path) && options.
|
|
1307
|
+
if(isUnion(path) && options.transformation === 'hdbcds'){
|
|
1492
1308
|
if(isBetaEnabled(options, 'ignoreAssocPublishingInUnion') && doA2J){
|
|
1493
1309
|
if(elem.keys) {
|
|
1494
1310
|
info(null, path, `Managed association "${elemName}", published in a UNION, will be ignored`)
|
|
@@ -1500,7 +1316,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1500
1316
|
else {
|
|
1501
1317
|
error(null, path, `Association "${elemName}" can't be published in a SAP HANA CDS UNION`)
|
|
1502
1318
|
}
|
|
1503
|
-
} else if(path.length > 4 && options.
|
|
1319
|
+
} else if(path.length > 4 && options.transformation === 'hdbcds'){ // path.length > 4 -> is a subquery
|
|
1504
1320
|
error(null, path, { name: elemName },
|
|
1505
1321
|
'Association $(NAME) can\'t be published in a subquery')
|
|
1506
1322
|
} else {
|
|
@@ -1713,23 +1529,41 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1713
1529
|
const assoc = inspectRef(path.concat([ i + 2 ])).art;
|
|
1714
1530
|
if (multipleExprs)
|
|
1715
1531
|
result.push('(');
|
|
1532
|
+
const backlinkName = xprArgs[i + 2].ref[xprArgs[i + 2].ref.length - 1];
|
|
1716
1533
|
result.push(...transformDollarSelfComparison(xprArgs[i + 2],
|
|
1717
1534
|
assoc,
|
|
1718
|
-
|
|
1535
|
+
backlinkName,
|
|
1719
1536
|
elem, elemName, art, artName, path.concat([ i ])
|
|
1720
1537
|
));
|
|
1721
1538
|
if (multipleExprs)
|
|
1722
1539
|
result.push(')');
|
|
1723
1540
|
i += 3;
|
|
1541
|
+
// remember name of backlink, important for foreign key constraints
|
|
1542
|
+
if(elem.$selfOnCondition)
|
|
1543
|
+
elem.$selfOnCondition.backlinkName += `_${ backlinkName }`;
|
|
1544
|
+
else {
|
|
1545
|
+
setProp(elem, '$selfOnCondition', {
|
|
1546
|
+
backlinkName
|
|
1547
|
+
}) // important for the foreign key constraints
|
|
1548
|
+
}
|
|
1724
1549
|
}
|
|
1725
1550
|
else if (isDollarSelfOrProjectionOperand(xprArgs[i + 2]) && isAssociationOperand(xprArgs[i], path.concat([ i ]))) {
|
|
1726
1551
|
const assoc = inspectRef(path.concat([ i ])).art;
|
|
1727
1552
|
if (multipleExprs)
|
|
1728
1553
|
result.push('(');
|
|
1729
|
-
|
|
1554
|
+
const backlinkName = xprArgs[i].ref[xprArgs[i].ref.length - 1];
|
|
1555
|
+
result.push(...transformDollarSelfComparison(xprArgs[i], assoc, backlinkName, elem, elemName, art, artName, path.concat([ i + 2 ])));
|
|
1730
1556
|
if (multipleExprs)
|
|
1731
1557
|
result.push(')');
|
|
1732
1558
|
i += 3;
|
|
1559
|
+
// remember name of backlink, important for foreign key constraints
|
|
1560
|
+
if(elem.$selfOnCondition)
|
|
1561
|
+
elem.$selfOnCondition.backlinkName += `_${ backlinkName }`;
|
|
1562
|
+
else {
|
|
1563
|
+
setProp(elem, '$selfOnCondition', {
|
|
1564
|
+
backlinkName
|
|
1565
|
+
}) // important for the foreign key constraints
|
|
1566
|
+
}
|
|
1733
1567
|
}
|
|
1734
1568
|
// Otherwise take one (!) token unchanged
|
|
1735
1569
|
else {
|
|
@@ -1806,8 +1640,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1806
1640
|
assoc.keys.forEach((k) => {
|
|
1807
1641
|
// Depending on naming conventions, the foreign key may two path steps (hdbcds) or be a single path step with a flattened name (plain, quoted)
|
|
1808
1642
|
// With to.hdbcds in conjunction with hdbcds naming, we need to NOT use the alias - else we get deployment errors
|
|
1809
|
-
const keyName = k.as &&
|
|
1810
|
-
const fKeyPath =
|
|
1643
|
+
const keyName = k.as && doA2J ? [k.as] : k.ref;
|
|
1644
|
+
const fKeyPath = !doA2J ? [ assocName, ...keyName ] : [ `${ assocName }${ pathDelimiter }${ keyName[0] }` ];
|
|
1811
1645
|
// FIXME: _artifact to the args ???
|
|
1812
1646
|
const a = [
|
|
1813
1647
|
{
|
|
@@ -1882,10 +1716,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1882
1716
|
return true;
|
|
1883
1717
|
}
|
|
1884
1718
|
|
|
1885
|
-
function insideColumns(path) {
|
|
1886
|
-
return path.length >= 3 && path[path.length - 3] === 'SELECT' && path[path.length - 2] === 'columns';
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
1719
|
/**
|
|
1890
1720
|
* @param {CSN.Artifact} artifact
|
|
1891
1721
|
* @param {string} artifactName
|
|
@@ -1982,119 +1812,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1982
1812
|
return !(options.toSql && type === 'cds.String' && (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain'));
|
|
1983
1813
|
}
|
|
1984
1814
|
|
|
1985
|
-
/**
|
|
1986
|
-
* Get not just the leafs, but all the branches of a structured element
|
|
1987
|
-
*
|
|
1988
|
-
* @param {object} element Structured element
|
|
1989
|
-
* @param {string} elementName Name of the structured element
|
|
1990
|
-
* @returns {object} Returns a dictionary, where the key is the flat name of the branch and the value is an array of element-steps.
|
|
1991
|
-
*/
|
|
1992
|
-
function getBranches(element, elementName){
|
|
1993
|
-
const branches = {};
|
|
1994
|
-
const subbranchNames = [];
|
|
1995
|
-
const subbranchElements = [];
|
|
1996
|
-
walkElements(element, elementName);
|
|
1997
|
-
function walkElements(e, name){
|
|
1998
|
-
if(isBuiltinType(e)){
|
|
1999
|
-
branches[subbranchNames.concat(name).join(pathDelimiter)] = subbranchElements.concat(e);
|
|
2000
|
-
} else {
|
|
2001
|
-
const eType = effectiveType(e)
|
|
2002
|
-
const subelements = e.elements || eType.elements;
|
|
2003
|
-
if(subelements){
|
|
2004
|
-
subbranchElements.push(e);
|
|
2005
|
-
subbranchNames.push(name);
|
|
2006
|
-
for(let [subelementName, subelement] of Object.entries(subelements)){
|
|
2007
|
-
walkElements(subelement, subelementName);
|
|
2008
|
-
}
|
|
2009
|
-
subbranchNames.pop();
|
|
2010
|
-
subbranchElements.pop();
|
|
2011
|
-
} else {
|
|
2012
|
-
branches[subbranchNames.concat(name).join(pathDelimiter)] = subbranchElements.concat(e);
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
return branches;
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
/**
|
|
2021
|
-
* Flatten structures
|
|
2022
|
-
*
|
|
2023
|
-
* @param {CSN.Artifact} art Artifact
|
|
2024
|
-
* @param {string} artName Artifact Name
|
|
2025
|
-
*/
|
|
2026
|
-
function flattenStructuredElements(art, artName) {
|
|
2027
|
-
if ((art.kind === 'entity' || art.kind === 'view') && !options.forHana.keepStructsAssocs) {
|
|
2028
|
-
forAllElements(art, artName, (parent, elements, pathToElements) => {
|
|
2029
|
-
const elementsArray = [];
|
|
2030
|
-
for (const elemName in elements) {
|
|
2031
|
-
const pathToElement = pathToElements.concat([elemName])
|
|
2032
|
-
const elem = parent.elements[elemName];
|
|
2033
|
-
elementsArray.push([elemName, elem]);
|
|
2034
|
-
if (isStructured(elem)) {
|
|
2035
|
-
// Ignore the structured element, replace it by its flattened form
|
|
2036
|
-
// TODO: use $ignore - _ is for links
|
|
2037
|
-
elem._ignore = true;
|
|
2038
|
-
|
|
2039
|
-
const branches = getBranches(elem, elemName);
|
|
2040
|
-
const flatElems = flattenStructuredElement(elem, elemName, [], pathToElement);
|
|
2041
|
-
|
|
2042
|
-
for (const flatElemName in flatElems) {
|
|
2043
|
-
if (parent.elements[flatElemName])
|
|
2044
|
-
error(null, pathToElement, `"${ artName }.${ elemName }": Flattened struct element name conflicts with existing element: "${ flatElemName }"`);
|
|
2045
|
-
|
|
2046
|
-
const flatElement = flatElems[flatElemName];
|
|
2047
|
-
|
|
2048
|
-
// Check if we have a valid notNull chain
|
|
2049
|
-
const branch = branches[flatElemName];
|
|
2050
|
-
if(flatElement.notNull !== false && !branch.some(s => !s.notNull)){
|
|
2051
|
-
flatElement.notNull = true;
|
|
2052
|
-
}
|
|
2053
|
-
|
|
2054
|
-
if(flatElement.type && isAssocOrComposition(flatElement.type) && flatElement.on){
|
|
2055
|
-
// Make refs resolvable by fixing the first ref step
|
|
2056
|
-
for (let i = 0; i < flatElement.on.length; i++) {
|
|
2057
|
-
const onPart = flatElement.on[i];
|
|
2058
|
-
if (onPart.ref) {
|
|
2059
|
-
const firstRef = flatElement.on[i].ref[0];
|
|
2060
|
-
|
|
2061
|
-
/*
|
|
2062
|
-
when element is defined in the current name resolution scope, like
|
|
2063
|
-
entity E {
|
|
2064
|
-
key x: Integer;
|
|
2065
|
-
s : {
|
|
2066
|
-
y : Integer;
|
|
2067
|
-
a3 : association to E on a3.x = y;
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
|
|
2071
|
-
*/
|
|
2072
|
-
const prefix = flatElement._flatElementNameWithDots.split('.').slice(0,-1).join(pathDelimiter);
|
|
2073
|
-
const possibleFlatName = prefix + pathDelimiter + firstRef;
|
|
2074
|
-
|
|
2075
|
-
if (flatElems[possibleFlatName])
|
|
2076
|
-
flatElement.on[i].ref[0] = possibleFlatName;
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
elementsArray.push([flatElemName, flatElement]);
|
|
2081
|
-
// Still add them - otherwise we might not detect collisions between generated elements.
|
|
2082
|
-
parent.elements[flatElemName] = flatElement;
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
// Don't fake consistency of the model by adding empty elements {}
|
|
2087
|
-
if(elementsArray.length === 0)
|
|
2088
|
-
return;
|
|
2089
|
-
|
|
2090
|
-
parent.elements = elementsArray.reduce((previous, [name, element]) => {
|
|
2091
|
-
previous[name] = element;
|
|
2092
|
-
return previous;
|
|
2093
|
-
}, Object.create(null));
|
|
2094
|
-
});
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
1815
|
/**
|
|
2099
1816
|
* Flatten and create the foreign key elements of managed associaitons
|
|
2100
1817
|
*
|
|
@@ -2102,7 +1819,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
2102
1819
|
* @param {string} artName
|
|
2103
1820
|
*/
|
|
2104
1821
|
function handleManagedAssociationFKs(art, artName) {
|
|
2105
|
-
if ((art.kind === 'entity' || art.kind === 'view') &&
|
|
1822
|
+
if ((art.kind === 'entity' || art.kind === 'view') && doA2J) {
|
|
2106
1823
|
forAllElements(art, artName, (parent, elements, pathToElements) => {
|
|
2107
1824
|
if(artName.startsWith('localized.') && pathToElements.length > 3) {
|
|
2108
1825
|
// In subqueries, the elements of localized views are missing all the important bits and pieces...
|
|
@@ -2246,7 +1963,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
2246
1963
|
function flattenIndexes(art, artName) {
|
|
2247
1964
|
// Flatten structs in indexes (unless explicitly asked to keep structs)
|
|
2248
1965
|
const tc = art.technicalConfig;
|
|
2249
|
-
if ((art.kind === 'entity' || art.kind === 'view') &&
|
|
1966
|
+
if ((art.kind === 'entity' || art.kind === 'view') && doA2J) {
|
|
2250
1967
|
if (tc && tc[dialect]) {
|
|
2251
1968
|
// Secondary and fulltext indexes
|
|
2252
1969
|
for (const name in tc[dialect].indexes) {
|
|
@@ -2313,7 +2030,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
2313
2030
|
function handleManagedAssocStepsInOnCondition(artifact, artifactName) {
|
|
2314
2031
|
for (const elemName in artifact.elements) {
|
|
2315
2032
|
const elem = artifact.elements[elemName];
|
|
2316
|
-
if (
|
|
2033
|
+
if (doA2J) {
|
|
2317
2034
|
// The association is an unmanaged on
|
|
2318
2035
|
if (!elem.keys && elem.target && elem.on) {
|
|
2319
2036
|
forEachRef(elem.on, (ref, refOwner, path) => {
|