@sap/cds-compiler 2.12.0 → 2.15.2
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 +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- 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 +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
|
-
const { getUtils,
|
|
5
|
-
forEachMemberRecursively,
|
|
4
|
+
const { getUtils, cloneCsnNonDict,
|
|
5
|
+
forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
|
|
6
6
|
getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
|
|
7
7
|
isAspect, walkCsnPath,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
@@ -12,23 +12,24 @@ const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
|
12
12
|
const { csnRefs, pathId } = require('../model/csnRefs');
|
|
13
13
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
14
14
|
const validate = require('../checks/validator');
|
|
15
|
-
const {
|
|
15
|
+
const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
|
|
16
16
|
const { addLocalizationViewsWithJoins, addLocalizationViews } = require('../transform/localized');
|
|
17
17
|
const { timetrace } = require('../utils/timetrace');
|
|
18
18
|
const { createReferentialConstraints, assertConstraintIdentifierUniqueness } = require('./db/constraints');
|
|
19
|
-
const { createDict } = require('../utils/objectUtils');
|
|
19
|
+
const { createDict, forEach } = require('../utils/objectUtils');
|
|
20
20
|
const handleExists = require('./db/transformExists');
|
|
21
21
|
const replaceAssociationsInGroupByOrderBy = require('./db/groupByOrderBy');
|
|
22
22
|
const _forEachDefinition = require('../model/csnUtils').forEachDefinition;
|
|
23
23
|
const flattening = require('./db/flattening');
|
|
24
24
|
const expansion = require('./db/expansion');
|
|
25
25
|
const assertUnique = require('./db/assertUnique');
|
|
26
|
-
const generateDrafts = require('./db
|
|
27
|
-
const enrichUniversalCsn = require('./universalCsnEnricher');
|
|
26
|
+
const generateDrafts = require('./draft/db');
|
|
27
|
+
const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
|
|
28
28
|
const { getViewTransformer } = require('./db/views');
|
|
29
29
|
const cdsPersistence = require('./db/cdsPersistence');
|
|
30
30
|
const temporal = require('./db/temporal');
|
|
31
31
|
const associations = require('./db/associations')
|
|
32
|
+
const { ModelError } = require("../base/error");
|
|
32
33
|
|
|
33
34
|
// By default: Do not process non-entities/views
|
|
34
35
|
function forEachDefinition(csn, cb) {
|
|
@@ -105,7 +106,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
105
106
|
// copy the model as we don't want to change the input model
|
|
106
107
|
timetrace.start('HANA transformation');
|
|
107
108
|
/** @type {CSN.Model} */
|
|
108
|
-
let csn =
|
|
109
|
+
let csn = cloneCsnNonDict(inputModel, options);
|
|
109
110
|
|
|
110
111
|
|
|
111
112
|
|
|
@@ -113,16 +114,16 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
113
114
|
|
|
114
115
|
const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
|
|
115
116
|
|
|
116
|
-
let error, warning, info; // message functions
|
|
117
|
+
let message, error, warning, info; // message functions
|
|
117
118
|
/** @type {() => void} */
|
|
118
|
-
let
|
|
119
|
-
let artifactRef, inspectRef, effectiveType,
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
let throwWithAnyError;
|
|
120
|
+
let artifactRef, inspectRef, effectiveType, get$combined,
|
|
121
|
+
getFinalBaseType, // csnUtils (csnRefs)
|
|
122
|
+
addDefaultTypeFacets, expandStructsInExpression; // transformUtils
|
|
122
123
|
|
|
123
124
|
bindCsnReference();
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
throwWithAnyError(); // reclassify and throw in case of non-configurable errors
|
|
126
127
|
|
|
127
128
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
|
|
128
129
|
enrichUniversalCsn(csn, options);
|
|
@@ -136,7 +137,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
136
137
|
|
|
137
138
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
138
139
|
const cleanup = validate.forHana(csn, {
|
|
139
|
-
error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
|
|
140
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
|
|
140
141
|
});
|
|
141
142
|
|
|
142
143
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
@@ -145,13 +146,13 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
145
146
|
// Check if structured elements and managed associations are compared in an expression
|
|
146
147
|
// and expand these structured elements. This tuple expansion allows all other
|
|
147
148
|
// subsequent procession steps (especially a2j) to see plain paths in expressions.
|
|
148
|
-
// If errors are detected,
|
|
149
|
+
// If errors are detected, throwWithAnyError() will return from further processing
|
|
149
150
|
|
|
150
151
|
// If this function is ever undefined, we have a bug in our logic.
|
|
151
152
|
// @ts-ignore
|
|
152
153
|
expandStructsInExpression(csn, { drillRef: true });
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
throwWithAnyError();
|
|
155
156
|
|
|
156
157
|
// FIXME: This does something very similar to cloneWithTransformations -> refactor?
|
|
157
158
|
const transformCsn = transformUtils.transformModel;
|
|
@@ -166,7 +167,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
166
167
|
|
|
167
168
|
if(doA2J) {
|
|
168
169
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
169
|
-
expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info,
|
|
170
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithAnyError});
|
|
170
171
|
bindCsnReference();
|
|
171
172
|
}
|
|
172
173
|
|
|
@@ -232,34 +233,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
232
233
|
|
|
233
234
|
// (000) Rename primitive types, make UUID a String
|
|
234
235
|
transformCsn(csn, {
|
|
235
|
-
type: (val, node, key
|
|
236
|
-
// Resolve type-of chains
|
|
237
|
-
function fn() {
|
|
238
|
-
// val can be undefined: books as myBooks : redirected to Model.MyBooks
|
|
239
|
-
if (val && val.ref) {
|
|
240
|
-
const { art } = inspectRef(path);
|
|
241
|
-
if (art && art.type) {
|
|
242
|
-
val = art.type;
|
|
243
|
-
// This is somehow needed to update the ref so that inspectRef sees it
|
|
244
|
-
node[key] = val;
|
|
245
|
-
if (val.ref)
|
|
246
|
-
fn();
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
// Doesn't seem to ever ocurr
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
fn();
|
|
236
|
+
type: (val, node, key) => {
|
|
254
237
|
renamePrimitiveTypesAndUuid(val, node, key);
|
|
255
238
|
addDefaultTypeFacets(node);
|
|
256
239
|
},
|
|
257
|
-
cast: (val) => {
|
|
258
|
-
if (options.forHana.names === 'plain' || options.toSql )
|
|
259
|
-
toFinalBaseType(val);
|
|
260
|
-
renamePrimitiveTypesAndUuid(val.type, val, 'type');
|
|
261
|
-
addDefaultTypeFacets(val);
|
|
262
|
-
},
|
|
263
240
|
// HANA/SQLite do not support array-of - turn into CLOB/Text
|
|
264
241
|
items: (val, node) => {
|
|
265
242
|
node.type = 'cds.LargeString';
|
|
@@ -270,24 +247,19 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
270
247
|
// (040) Ignore entities and views that are abstract or implemented
|
|
271
248
|
// or carry the annotation cds.persistence.skip/exists
|
|
272
249
|
// These entities are not removed from the csn, but flagged as "to be ignored"
|
|
273
|
-
forEachDefinition(csn, cdsPersistence.getAnnoProcessor(
|
|
250
|
+
forEachDefinition(csn, cdsPersistence.getAnnoProcessor());
|
|
274
251
|
|
|
275
252
|
|
|
276
253
|
// (050) Check @cds.valid.from/to only on entity
|
|
277
254
|
// Views are checked in (001), unbalanced valid.from/to's or mismatching origins
|
|
278
|
-
// Temporal only in beta-mode
|
|
279
255
|
forEachDefinition(csn, temporal.getAnnotationHandler(csn, options, pathDelimiter, {error}));
|
|
280
256
|
|
|
281
|
-
|
|
257
|
+
// eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
|
|
258
|
+
doA2J && flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, error, pathDelimiter, true, { allowArtifact: artifact => (artifact.kind === 'entity') });
|
|
282
259
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
forEachDefinition(csn, flattenIndexes);
|
|
289
|
-
// Basic handling of associations in views and entities
|
|
290
|
-
forEachDefinition(csn, associations.getManagedAssociationTransformer(csn, options, pathDelimiter));
|
|
260
|
+
doA2J && forEachDefinition(csn, flattenIndexes);
|
|
261
|
+
// Managed associations get an on-condition - in views and entities
|
|
262
|
+
doA2J && associations.attachOnConditions(csn, pathDelimiter);
|
|
291
263
|
|
|
292
264
|
// (045) Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
|
|
293
265
|
// and make them entities
|
|
@@ -296,10 +268,11 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
296
268
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
297
269
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
298
270
|
// are part of the elements
|
|
299
|
-
|
|
271
|
+
if (doA2J)
|
|
272
|
+
forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter));
|
|
300
273
|
|
|
301
274
|
// Create convenience views for localized entities/views.
|
|
302
|
-
// To be done after
|
|
275
|
+
// To be done after getManagedAssocStepsInOnConditionFinalizer because associations are
|
|
303
276
|
// handled and before handleDBChecks which removes the localized attribute.
|
|
304
277
|
// Association elements of localized convenience views do not have hidden properties
|
|
305
278
|
// like $managed set, so we cannot do this earlier on.
|
|
@@ -308,11 +281,11 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
308
281
|
else
|
|
309
282
|
addLocalizationViews(csn, options);
|
|
310
283
|
|
|
311
|
-
forEachDefinition(csn, (definition, artName, prop, path) => {
|
|
284
|
+
!doA2J && forEachDefinition(csn, (definition, artName, prop, path) => {
|
|
312
285
|
if (definition.query) {
|
|
313
286
|
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
314
287
|
const that = { csnUtils: getUtils(csn), options, error };
|
|
315
|
-
|
|
288
|
+
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
|
|
316
289
|
}
|
|
317
290
|
});
|
|
318
291
|
|
|
@@ -336,18 +309,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
336
309
|
// because otherwise we would produce wrong ON-conditions for the keys involved. Sigh ...
|
|
337
310
|
forEachDefinition(csn, transformSelfInBacklinks);
|
|
338
311
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
if(validOptionsForConstraint())
|
|
349
|
-
createReferentialConstraints(csn, options);
|
|
350
|
-
}
|
|
312
|
+
/**
|
|
313
|
+
* Referential Constraints are only supported for sql-dialect "hana" and "sqlite".
|
|
314
|
+
* For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
|
|
315
|
+
* hence we do not generate the referential constraints for them.
|
|
316
|
+
*/
|
|
317
|
+
if((options.sqlDialect === 'hana' || options.sqlDialect === 'sqlite') && doA2J)
|
|
318
|
+
createReferentialConstraints(csn, options);
|
|
319
|
+
|
|
351
320
|
// no constraints for drafts
|
|
352
321
|
generateDrafts(csn, options, pathDelimiter, { info, warning, error });
|
|
353
322
|
|
|
@@ -370,30 +339,12 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
370
339
|
const checkConstraintIdentifiers = (artifact, artifactName, prop, path) => {
|
|
371
340
|
assertConstraintIdentifierUniqueness(artifact, artifactName, path, error);
|
|
372
341
|
};
|
|
373
|
-
const removeNamespaces = (artifact, artifactName) => {
|
|
374
|
-
if (artifact.kind === 'namespace')
|
|
375
|
-
delete csn.definitions[artifactName];
|
|
376
|
-
};
|
|
377
|
-
const ignoreNonPersistedArtifactsWithAnonymousAspectComposition = (artifact) => {
|
|
378
|
-
if(artifact.kind === 'type' || artifact.kind === 'aspect' || artifact.kind === 'entity' && artifact.abstract){
|
|
379
|
-
if(artifact.elements && Object.keys(artifact.elements).some((elementName) => {
|
|
380
|
-
const element = artifact.elements[elementName];
|
|
381
|
-
return !element.target && element.targetAspect && typeof element.targetAspect !== 'string';
|
|
382
|
-
})) {
|
|
383
|
-
artifact._ignore = true;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
342
|
|
|
388
343
|
forEachDefinition(csn, [
|
|
389
344
|
/* assert that there will be no conflicting unique- and foreign key constraint identifiers */
|
|
390
345
|
checkConstraintIdentifiers,
|
|
391
|
-
/* (250) Remove all namespaces from definitions */
|
|
392
|
-
removeNamespaces,
|
|
393
346
|
/* Check Type Parameters (precision, scale, length ...) */
|
|
394
347
|
checkTypeParameters,
|
|
395
|
-
/* Filter out aspects/types/abstract entities containing managed compositions of anonymous aspects */
|
|
396
|
-
ignoreNonPersistedArtifactsWithAnonymousAspectComposition,
|
|
397
348
|
// (200) Strip 'key' property from type elements
|
|
398
349
|
removeKeyPropInType,
|
|
399
350
|
]);
|
|
@@ -402,7 +353,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
402
353
|
if(doA2J)
|
|
403
354
|
flattening.removeLeadingSelf(csn);
|
|
404
355
|
|
|
405
|
-
|
|
356
|
+
throwWithAnyError();
|
|
406
357
|
|
|
407
358
|
timetrace.stop();
|
|
408
359
|
|
|
@@ -430,10 +381,25 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
430
381
|
// Set when we turn UUID into String, checked during generateDraftForHana
|
|
431
382
|
'$renamed': killProp,
|
|
432
383
|
// Set when we remove .key from temporal things, used in localized.js
|
|
433
|
-
'$key': killProp
|
|
384
|
+
'$key': killProp,
|
|
385
|
+
// We need .elements easily for rendering - otherwise we have to compute it then
|
|
386
|
+
// Does not fit in the "killers" theme - TODO: Find a better place
|
|
387
|
+
SET: (parent, prop, SET) => {
|
|
388
|
+
if(!SET.elements) {
|
|
389
|
+
const stack = [parent];
|
|
390
|
+
while(stack.length > 0) {
|
|
391
|
+
const query = stack.pop();
|
|
392
|
+
|
|
393
|
+
if(query.SET)
|
|
394
|
+
stack.push(query.SET.args[0]);
|
|
395
|
+
else if(query.SELECT)
|
|
396
|
+
setProp(SET, 'elements', query.SELECT.elements);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
434
400
|
}
|
|
435
401
|
|
|
436
|
-
applyTransformations(csn, killers, [], false);
|
|
402
|
+
applyTransformations(csn, killers, [], { skipIgnore: false});
|
|
437
403
|
|
|
438
404
|
redoProjections.forEach(fn => fn());
|
|
439
405
|
|
|
@@ -442,10 +408,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
442
408
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
443
409
|
|
|
444
410
|
function bindCsnReference(){
|
|
445
|
-
({ error, warning, info,
|
|
446
|
-
({ artifactRef, inspectRef, effectiveType } =
|
|
447
|
-
({
|
|
448
|
-
({ addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
411
|
+
({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
|
|
412
|
+
({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
|
|
413
|
+
({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
449
414
|
}
|
|
450
415
|
|
|
451
416
|
function bindCsnReferenceOnly(){
|
|
@@ -472,8 +437,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
472
437
|
}
|
|
473
438
|
})
|
|
474
439
|
}
|
|
475
|
-
}
|
|
476
|
-
|
|
440
|
+
}, [ 'definitions', artifactName, 'query' ]);
|
|
441
|
+
|
|
477
442
|
function getResolvedMixinOnCondition(csn, mixinAssociation, query, assocName, path){
|
|
478
443
|
const { inspectRef } = csnRefs(csn);
|
|
479
444
|
const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
|
|
@@ -498,7 +463,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
498
463
|
// get$combined also includes elements which are part of "excluding {}"
|
|
499
464
|
// this shouldn't be an issue here, as such references get rejected
|
|
500
465
|
const elementsOfQuerySources = get$combined(query);
|
|
501
|
-
|
|
466
|
+
forEach(elementsOfQuerySources, (id, element) => {
|
|
502
467
|
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
503
468
|
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
504
469
|
if(id === columnToReplace)
|
|
@@ -508,7 +473,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
508
473
|
|
|
509
474
|
// No implicit CAST in on-condition
|
|
510
475
|
if(replaceWith && replaceWith.cast) {
|
|
511
|
-
const clone =
|
|
476
|
+
const clone = cloneCsnNonDict(replaceWith, options);
|
|
512
477
|
delete clone.cast;
|
|
513
478
|
return clone;
|
|
514
479
|
}
|
|
@@ -524,11 +489,15 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
524
489
|
function transformViews(artifact, artifactName) {
|
|
525
490
|
if (!artifact._ignore) {
|
|
526
491
|
// Do things specific for entities and views (pass 2)
|
|
527
|
-
if ((artifact.kind === 'entity'
|
|
528
|
-
|
|
529
|
-
transformEntityOrViewPass2(
|
|
530
|
-
replaceAssociationsInGroupByOrderBy(
|
|
531
|
-
|
|
492
|
+
if ((artifact.kind === 'entity') && artifact.query) {
|
|
493
|
+
const process = (parent, prop, query, path) => {
|
|
494
|
+
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
|
|
495
|
+
replaceAssociationsInGroupByOrderBy(parent, options, inspectRef, error, path.concat(prop));
|
|
496
|
+
return query;
|
|
497
|
+
}
|
|
498
|
+
applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
|
|
499
|
+
SELECT: process
|
|
500
|
+
}, {}, [ 'definitions']);
|
|
532
501
|
}
|
|
533
502
|
}
|
|
534
503
|
}
|
|
@@ -539,7 +508,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
539
508
|
*/
|
|
540
509
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
541
510
|
if (!artifact._ignore) {
|
|
542
|
-
if (
|
|
511
|
+
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
543
512
|
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
|
|
544
513
|
|
|
545
514
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
@@ -558,9 +527,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
558
527
|
* @param {string} artifactName
|
|
559
528
|
*/
|
|
560
529
|
function removeKeyPropInType(artifact, artifactName) {
|
|
561
|
-
if (!artifact._ignore) {
|
|
530
|
+
if (!artifact._ignore && artifact.kind === 'type') {
|
|
562
531
|
forEachMemberRecursively(artifact, (member) => {
|
|
563
|
-
if (
|
|
532
|
+
if (member.key)
|
|
564
533
|
delete member.key;
|
|
565
534
|
}, [ 'definitions', artifactName ]);
|
|
566
535
|
}
|
|
@@ -609,25 +578,27 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
609
578
|
if (artifact.params) {
|
|
610
579
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
611
580
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
612
|
-
|
|
581
|
+
message('def-unexpected-paramview-assoc', path, { '#': 'view' });
|
|
613
582
|
}
|
|
614
583
|
else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
|
|
615
584
|
// UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
|
|
616
|
-
|
|
585
|
+
const anno = artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
586
|
+
message('def-unexpected-calcview-assoc', path, { '#': 'entity-persistence', anno });
|
|
617
587
|
}
|
|
618
588
|
if (csn.definitions[member.target].params) {
|
|
619
589
|
// HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
|
|
620
590
|
// SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
|
|
621
|
-
|
|
591
|
+
message('def-unexpected-paramview-assoc', path, { '#': 'target' });
|
|
622
592
|
}
|
|
623
|
-
else if(csn.definitions[member.target]['@cds.persistence.udf'] ||
|
|
593
|
+
else if(csn.definitions[member.target]['@cds.persistence.udf'] || csn.definitions[member.target]['@cds.persistence.calcview']) {
|
|
624
594
|
// HANA won't check the assoc target but when querying an association with target UDF, this is the error:
|
|
625
595
|
// SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
|
|
626
596
|
// CREATE TABLE F (id INTEGER NOT NULL);
|
|
627
597
|
// CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
|
|
628
598
|
// CREATE TABLE Y ( id INTEGER NOT NULL, toUDF_id INTEGER) WITH ASSOCIATIONS (MANY TO ONE JOIN UDF AS toUDF ON (toUDF.id = toUDF_id));
|
|
629
599
|
// CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
|
|
630
|
-
|
|
600
|
+
const anno = csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
601
|
+
message('def-unexpected-calcview-assoc', path, { '#': 'target-persistence', anno });
|
|
631
602
|
}
|
|
632
603
|
}
|
|
633
604
|
}, [ 'definitions', artifactName ]);
|
|
@@ -638,7 +609,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
638
609
|
* @param {string} artifactName
|
|
639
610
|
*/
|
|
640
611
|
function handleChecksForWithParameters(artifact, artifactName) {
|
|
641
|
-
if (!artifact._ignore && artifact.params && (artifact.kind === 'entity'
|
|
612
|
+
if (!artifact._ignore && artifact.params && (artifact.kind === 'entity')) {
|
|
642
613
|
if (!artifact.query) { // table entity with params
|
|
643
614
|
// Allow with plain
|
|
644
615
|
error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
|
|
@@ -666,7 +637,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
666
637
|
|
|
667
638
|
function handleAssocToJoins() {
|
|
668
639
|
// With flattening errors, it makes little sense to continue.
|
|
669
|
-
|
|
640
|
+
throwWithAnyError();
|
|
670
641
|
// the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
|
|
671
642
|
// simply make it invisible and copy it over to the result csn
|
|
672
643
|
forEachDefinition(csn, art => art.technicalConfig && setProp(art, 'technicalConfig', art.technicalConfig));
|
|
@@ -908,7 +879,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
908
879
|
else if (assoc.on)
|
|
909
880
|
return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName);
|
|
910
881
|
|
|
911
|
-
throw new
|
|
882
|
+
throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
|
|
912
883
|
}
|
|
913
884
|
|
|
914
885
|
// For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
|
|
@@ -963,22 +934,23 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
963
934
|
const newOnCond = cloneWithTransformations(assoc.on, {
|
|
964
935
|
ref: (value) => cloneWithTransformations(value, {}),
|
|
965
936
|
});
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
937
|
+
applyTransformationsOnNonDictionary({on: newOnCond}, 'on', {
|
|
938
|
+
ref: (parent, prop, ref) => {
|
|
939
|
+
if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
|
|
940
|
+
{
|
|
941
|
+
ref.shift();
|
|
942
|
+
} else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
|
|
943
|
+
// We could also have a $self infront of the assoc name - so we would need to shift twice
|
|
944
|
+
ref.shift();
|
|
945
|
+
ref.shift();
|
|
946
|
+
}
|
|
947
|
+
else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
|
|
948
|
+
ref.unshift(elemName);
|
|
949
|
+
// if there was a $self identifier in the forwarding association onCond
|
|
950
|
+
// we do not need it any more, as we prepended in the previous step the back association's id
|
|
951
|
+
if (ref[1] === '$self')
|
|
952
|
+
ref.splice(1, 1);
|
|
953
|
+
}
|
|
982
954
|
}
|
|
983
955
|
});
|
|
984
956
|
return newOnCond;
|
|
@@ -1045,24 +1017,22 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1045
1017
|
// (which can currently only be 'positiveInteger') and (optional) the value is in a given range
|
|
1046
1018
|
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
1047
1019
|
const paramValue = node[paramName];
|
|
1048
|
-
if (paramValue
|
|
1020
|
+
if (paramValue === undefined || paramValue === null) {
|
|
1049
1021
|
if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
|
|
1050
1022
|
return true;
|
|
1051
1023
|
} else {
|
|
1052
|
-
return error('missing-
|
|
1024
|
+
return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
|
|
1053
1025
|
}
|
|
1054
1026
|
}
|
|
1055
1027
|
if (range) {
|
|
1056
1028
|
if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
|
|
1057
|
-
error(
|
|
1058
|
-
{ prop: paramName, type: node.type, number: range.max, $reviewed: false }
|
|
1059
|
-
'Expecting parameter $(PROP) for type $(TYPE) to not exceed $(NUMBER)');
|
|
1029
|
+
error('type-unexpected-argument', path,
|
|
1030
|
+
{ '#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false });
|
|
1060
1031
|
return false;
|
|
1061
1032
|
}
|
|
1062
1033
|
if (range.min && paramValue < range.min) {
|
|
1063
|
-
error(
|
|
1064
|
-
{ prop: paramName, type: node.type, number: range.min, $reviewed: false }
|
|
1065
|
-
'Expecting parameter $(PROP) for type $(TYPE) to be greater than or equal to $(NUMBER)');
|
|
1034
|
+
error('type-unexpected-argument', path,
|
|
1035
|
+
{ '#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false });
|
|
1066
1036
|
return false;
|
|
1067
1037
|
}
|
|
1068
1038
|
}
|
|
@@ -1090,7 +1060,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1090
1060
|
function flattenIndexes(art, artName) {
|
|
1091
1061
|
// Flatten structs in indexes (unless explicitly asked to keep structs)
|
|
1092
1062
|
const tc = art.technicalConfig;
|
|
1093
|
-
if (
|
|
1063
|
+
if (art.kind === 'entity') {
|
|
1094
1064
|
if (tc && tc[dialect]) {
|
|
1095
1065
|
// Secondary and fulltext indexes
|
|
1096
1066
|
for (const name in tc[dialect].indexes) {
|
|
@@ -1144,75 +1114,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1144
1114
|
}
|
|
1145
1115
|
}
|
|
1146
1116
|
}
|
|
1147
|
-
|
|
1148
|
-
/**
|
|
1149
|
-
* Loop over all elements and for all unmanaged associations translate
|
|
1150
|
-
* <assoc base>.<managed assoc>.<fk> to <assoc base>.<managed assoc>_<fk>
|
|
1151
|
-
*
|
|
1152
|
-
* Or in other words: Allow using the foreign keys of managed associations in on-conditions
|
|
1153
|
-
*
|
|
1154
|
-
* @param {CSN.Artifact} artifact Artifact to check
|
|
1155
|
-
* @param {string} artifactName Name of the artifact
|
|
1156
|
-
*/
|
|
1157
|
-
function handleManagedAssocStepsInOnCondition(artifact, artifactName) {
|
|
1158
|
-
for (const elemName in artifact.elements) {
|
|
1159
|
-
const elem = artifact.elements[elemName];
|
|
1160
|
-
if (doA2J) {
|
|
1161
|
-
// The association is an unmanaged on
|
|
1162
|
-
if (!elem.keys && elem.target && elem.on) {
|
|
1163
|
-
forEachRef(elem.on, (ref, refOwner, path) => {
|
|
1164
|
-
// [<assoc base>.]<managed assoc>.<field>
|
|
1165
|
-
if (ref.length > 1) {
|
|
1166
|
-
const { links } = inspectRef(path);
|
|
1167
|
-
if (links) {
|
|
1168
|
-
// eslint-disable-next-line for-direction
|
|
1169
|
-
for (let i = links.length - 1; i >= 0; i--) {
|
|
1170
|
-
const link = links[i];
|
|
1171
|
-
// We found the latest managed assoc path step
|
|
1172
|
-
if (link.art && link.art.target && link.art.keys) {
|
|
1173
|
-
// Doesn't work when ref-target (filter condition) or similar is used
|
|
1174
|
-
if (!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
|
|
1175
|
-
// We join the managed assoc with everything following it
|
|
1176
|
-
const sourceElementName = ref.slice(i).join(pathDelimiter);
|
|
1177
|
-
const source = findSource(links, i - 1) || artifact;
|
|
1178
|
-
// allow specifying managed assoc on the source side
|
|
1179
|
-
const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
|
|
1180
|
-
if(fks && fks.length >= 1){
|
|
1181
|
-
const fk = fks[0];
|
|
1182
|
-
const managedAssocStepName = refOwner.ref[i];
|
|
1183
|
-
const fkName = `${ managedAssocStepName }${ pathDelimiter }${ fk.as }`;
|
|
1184
|
-
if(source && source.elements[fkName])
|
|
1185
|
-
refOwner.ref = [ ...ref.slice(0, i), fkName ];
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
}, [ 'definitions', artifactName, 'elements', elemName, 'on' ]);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
/**
|
|
1198
|
-
* Find out where the managed association is
|
|
1199
|
-
*
|
|
1200
|
-
* @param {Array} links
|
|
1201
|
-
* @param {Number} startIndex
|
|
1202
|
-
* @returns {Object| undefined} CSN definition of the source of the managed association
|
|
1203
|
-
*
|
|
1204
|
-
*/
|
|
1205
|
-
function findSource(links, startIndex) {
|
|
1206
|
-
for (let i = startIndex; i >= 0; i--) {
|
|
1207
|
-
const link = links[i];
|
|
1208
|
-
// We found the latest assoc step - now check where that points to
|
|
1209
|
-
if (link.art && link.art.target)
|
|
1210
|
-
return csn.definitions[link.art.target];
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
return undefined;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
1117
|
}
|
|
1217
1118
|
|
|
1218
1119
|
|