@sap/cds-compiler 2.12.0 → 2.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
4
|
const { getUtils, cloneCsn,
|
|
5
|
-
forEachMemberRecursively,
|
|
5
|
+
forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
|
|
6
6
|
getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
|
|
7
7
|
isAspect, walkCsnPath,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
@@ -12,7 +12,7 @@ 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');
|
|
@@ -23,12 +23,13 @@ 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) {
|
|
@@ -113,12 +114,12 @@ 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
119
|
let throwWithError;
|
|
119
|
-
let artifactRef, inspectRef, effectiveType,
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
let artifactRef, inspectRef, effectiveType, get$combined,
|
|
121
|
+
getFinalBaseType, // csnUtils (csnRefs)
|
|
122
|
+
addDefaultTypeFacets, expandStructsInExpression; // transformUtils
|
|
122
123
|
|
|
123
124
|
bindCsnReference();
|
|
124
125
|
|
|
@@ -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
|
|
@@ -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
|
]);
|
|
@@ -433,7 +384,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
433
384
|
'$key': killProp
|
|
434
385
|
}
|
|
435
386
|
|
|
436
|
-
applyTransformations(csn, killers, [], false);
|
|
387
|
+
applyTransformations(csn, killers, [], { skipIgnore: false});
|
|
437
388
|
|
|
438
389
|
redoProjections.forEach(fn => fn());
|
|
439
390
|
|
|
@@ -442,10 +393,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
442
393
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
443
394
|
|
|
444
395
|
function bindCsnReference(){
|
|
445
|
-
({ error, warning, info, throwWithError } = makeMessageFunction(csn, options, moduleName));
|
|
446
|
-
({ artifactRef, inspectRef, effectiveType } =
|
|
447
|
-
({
|
|
448
|
-
({ addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
396
|
+
({ error, warning, info, message, throwWithError } = makeMessageFunction(csn, options, moduleName));
|
|
397
|
+
({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
|
|
398
|
+
({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
449
399
|
}
|
|
450
400
|
|
|
451
401
|
function bindCsnReferenceOnly(){
|
|
@@ -472,8 +422,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
472
422
|
}
|
|
473
423
|
})
|
|
474
424
|
}
|
|
475
|
-
}
|
|
476
|
-
|
|
425
|
+
}, [ 'definitions', artifactName, 'query' ]);
|
|
426
|
+
|
|
477
427
|
function getResolvedMixinOnCondition(csn, mixinAssociation, query, assocName, path){
|
|
478
428
|
const { inspectRef } = csnRefs(csn);
|
|
479
429
|
const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
|
|
@@ -524,7 +474,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
524
474
|
function transformViews(artifact, artifactName) {
|
|
525
475
|
if (!artifact._ignore) {
|
|
526
476
|
// Do things specific for entities and views (pass 2)
|
|
527
|
-
if ((artifact.kind === 'entity'
|
|
477
|
+
if ((artifact.kind === 'entity') && artifact.query) {
|
|
528
478
|
forAllQueries(artifact.query, (q, p) => {
|
|
529
479
|
transformEntityOrViewPass2(q, artifact, artifactName, p)
|
|
530
480
|
replaceAssociationsInGroupByOrderBy(q, options, inspectRef, error, p);
|
|
@@ -539,7 +489,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
539
489
|
*/
|
|
540
490
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
541
491
|
if (!artifact._ignore) {
|
|
542
|
-
if (
|
|
492
|
+
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
543
493
|
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
|
|
544
494
|
|
|
545
495
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
@@ -558,9 +508,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
558
508
|
* @param {string} artifactName
|
|
559
509
|
*/
|
|
560
510
|
function removeKeyPropInType(artifact, artifactName) {
|
|
561
|
-
if (!artifact._ignore) {
|
|
511
|
+
if (!artifact._ignore && artifact.kind === 'type') {
|
|
562
512
|
forEachMemberRecursively(artifact, (member) => {
|
|
563
|
-
if (
|
|
513
|
+
if (member.key)
|
|
564
514
|
delete member.key;
|
|
565
515
|
}, [ 'definitions', artifactName ]);
|
|
566
516
|
}
|
|
@@ -609,25 +559,27 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
609
559
|
if (artifact.params) {
|
|
610
560
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
611
561
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
612
|
-
|
|
562
|
+
message('def-unexpected-paramview-assoc', path, { '#': 'view' });
|
|
613
563
|
}
|
|
614
564
|
else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
|
|
615
565
|
// UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
|
|
616
|
-
|
|
566
|
+
const anno = artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
567
|
+
message('def-unexpected-calcview-assoc', path, { '#': 'entity-persistence', anno });
|
|
617
568
|
}
|
|
618
569
|
if (csn.definitions[member.target].params) {
|
|
619
570
|
// HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
|
|
620
571
|
// SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
|
|
621
|
-
|
|
572
|
+
message('def-unexpected-paramview-assoc', path, { '#': 'target' });
|
|
622
573
|
}
|
|
623
|
-
else if(csn.definitions[member.target]['@cds.persistence.udf'] ||
|
|
574
|
+
else if(csn.definitions[member.target]['@cds.persistence.udf'] || csn.definitions[member.target]['@cds.persistence.calcview']) {
|
|
624
575
|
// HANA won't check the assoc target but when querying an association with target UDF, this is the error:
|
|
625
576
|
// SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
|
|
626
577
|
// CREATE TABLE F (id INTEGER NOT NULL);
|
|
627
578
|
// CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
|
|
628
579
|
// 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
580
|
// CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
|
|
630
|
-
|
|
581
|
+
const anno = csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
582
|
+
message('def-unexpected-calcview-assoc', path, { '#': 'target-persistence', anno });
|
|
631
583
|
}
|
|
632
584
|
}
|
|
633
585
|
}, [ 'definitions', artifactName ]);
|
|
@@ -638,7 +590,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
638
590
|
* @param {string} artifactName
|
|
639
591
|
*/
|
|
640
592
|
function handleChecksForWithParameters(artifact, artifactName) {
|
|
641
|
-
if (!artifact._ignore && artifact.params && (artifact.kind === 'entity'
|
|
593
|
+
if (!artifact._ignore && artifact.params && (artifact.kind === 'entity')) {
|
|
642
594
|
if (!artifact.query) { // table entity with params
|
|
643
595
|
// Allow with plain
|
|
644
596
|
error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
|
|
@@ -908,7 +860,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
908
860
|
else if (assoc.on)
|
|
909
861
|
return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName);
|
|
910
862
|
|
|
911
|
-
throw new
|
|
863
|
+
throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
|
|
912
864
|
}
|
|
913
865
|
|
|
914
866
|
// For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
|
|
@@ -963,22 +915,23 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
963
915
|
const newOnCond = cloneWithTransformations(assoc.on, {
|
|
964
916
|
ref: (value) => cloneWithTransformations(value, {}),
|
|
965
917
|
});
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
918
|
+
applyTransformationsOnNonDictionary({on: newOnCond}, 'on', {
|
|
919
|
+
ref: (parent, prop, ref) => {
|
|
920
|
+
if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
|
|
921
|
+
{
|
|
922
|
+
ref.shift();
|
|
923
|
+
} else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
|
|
924
|
+
// We could also have a $self infront of the assoc name - so we would need to shift twice
|
|
925
|
+
ref.shift();
|
|
926
|
+
ref.shift();
|
|
927
|
+
}
|
|
928
|
+
else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
|
|
929
|
+
ref.unshift(elemName);
|
|
930
|
+
// if there was a $self identifier in the forwarding association onCond
|
|
931
|
+
// we do not need it any more, as we prepended in the previous step the back association's id
|
|
932
|
+
if (ref[1] === '$self')
|
|
933
|
+
ref.splice(1, 1);
|
|
934
|
+
}
|
|
982
935
|
}
|
|
983
936
|
});
|
|
984
937
|
return newOnCond;
|
|
@@ -1090,7 +1043,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1090
1043
|
function flattenIndexes(art, artName) {
|
|
1091
1044
|
// Flatten structs in indexes (unless explicitly asked to keep structs)
|
|
1092
1045
|
const tc = art.technicalConfig;
|
|
1093
|
-
if (
|
|
1046
|
+
if (art.kind === 'entity') {
|
|
1094
1047
|
if (tc && tc[dialect]) {
|
|
1095
1048
|
// Secondary and fulltext indexes
|
|
1096
1049
|
for (const name in tc[dialect].indexes) {
|
|
@@ -1144,75 +1097,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1144
1097
|
}
|
|
1145
1098
|
}
|
|
1146
1099
|
}
|
|
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
1100
|
}
|
|
1217
1101
|
|
|
1218
1102
|
|