@sap/cds-compiler 4.0.2 → 4.2.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -7,9 +7,9 @@ const { cloneCsnNonDict,
|
|
|
7
7
|
isAspect, walkCsnPath, isPersistedOnDatabase,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const { makeMessageFunction } = require('../base/messages');
|
|
10
|
-
const transformUtils = require('./
|
|
10
|
+
const transformUtils = require('./transformUtils');
|
|
11
11
|
const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
12
|
-
const { csnRefs, pathId, traverseQuery } = require('../model/csnRefs');
|
|
12
|
+
const { csnRefs, pathId, traverseQuery, columnAlias} = require('../model/csnRefs');
|
|
13
13
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
14
14
|
const validate = require('../checks/validator');
|
|
15
15
|
const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
|
|
@@ -26,7 +26,7 @@ const expansion = require('./db/expansion');
|
|
|
26
26
|
const assertUnique = require('./db/assertUnique');
|
|
27
27
|
const generateDrafts = require('./draft/db');
|
|
28
28
|
const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
|
|
29
|
-
const { getViewTransformer } = require('./db/views');
|
|
29
|
+
const { getViewTransformer, ensureColumnNames } = require('./db/views');
|
|
30
30
|
const cdsPersistence = require('./db/cdsPersistence');
|
|
31
31
|
const temporal = require('./db/temporal');
|
|
32
32
|
const associations = require('./db/associations')
|
|
@@ -98,17 +98,17 @@ function forEachDefinition(csn, cb) {
|
|
|
98
98
|
* - (250) Remove name space definitions again (only in forRelationalDB). Maybe we can omit inserting namespace definitions
|
|
99
99
|
* completely (TODO)
|
|
100
100
|
*
|
|
101
|
-
* @param {CSN.Model}
|
|
101
|
+
* @param {CSN.Model} csn
|
|
102
102
|
* @param {CSN.Options} options
|
|
103
103
|
* @param {string} moduleName The calling compiler module name, e.g. `to.hdi` or `to.hdbcds`.
|
|
104
104
|
*/
|
|
105
|
-
function transformForRelationalDBWithCsn(
|
|
105
|
+
function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
106
106
|
// copy the model as we don't want to change the input model
|
|
107
107
|
timetrace.start('HANA transformation');
|
|
108
108
|
|
|
109
109
|
timetrace.start('Clone CSN');
|
|
110
110
|
/** @type {CSN.Model} */
|
|
111
|
-
|
|
111
|
+
csn = cloneCsnNonDict(csn, options);
|
|
112
112
|
timetrace.stop('Clone CSN');
|
|
113
113
|
|
|
114
114
|
checkCSNVersion(csn, options);
|
|
@@ -117,20 +117,13 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
117
117
|
// There is also an explicit default length via options.defaultStringLength
|
|
118
118
|
const implicitDefaultLengths = getDefaultTypeLengths(options.sqlDialect);
|
|
119
119
|
|
|
120
|
+
/** @type {object} */
|
|
120
121
|
let csnUtils;
|
|
122
|
+
/** @type {object} */
|
|
123
|
+
let messageFunctions;
|
|
121
124
|
let message, error, warning, info; // message functions
|
|
122
125
|
/** @type {() => void} */
|
|
123
126
|
let throwWithAnyError;
|
|
124
|
-
// csnUtils
|
|
125
|
-
let artifactRef,
|
|
126
|
-
inspectRef,
|
|
127
|
-
effectiveType,
|
|
128
|
-
initDefinition,
|
|
129
|
-
dropDefinitionCache,
|
|
130
|
-
get$combined,
|
|
131
|
-
getCsnDef,
|
|
132
|
-
isAssocOrComposition,
|
|
133
|
-
addStringAnnotationTo;
|
|
134
127
|
// transformUtils
|
|
135
128
|
let addDefaultTypeFacets,
|
|
136
129
|
expandStructsInExpression,
|
|
@@ -148,6 +141,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
148
141
|
bindCsnReference();
|
|
149
142
|
}
|
|
150
143
|
|
|
144
|
+
ensureColumnNames(csn, options, csnUtils);
|
|
145
|
+
|
|
151
146
|
const dialect = options.sqlDialect;
|
|
152
147
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
153
148
|
if (!doA2J)
|
|
@@ -159,14 +154,16 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
159
154
|
timetrace.start('Validate');
|
|
160
155
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
161
156
|
const cleanup = validate.forRelationalDB(csn, {
|
|
162
|
-
|
|
157
|
+
...messageFunctions, csnUtils, ...csnUtils, csn, options, isAspect
|
|
163
158
|
});
|
|
164
159
|
timetrace.stop('Validate');
|
|
165
160
|
|
|
166
|
-
rewriteCalculatedElementsInViews(csn, options, pathDelimiter,
|
|
161
|
+
rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
|
|
167
162
|
|
|
168
163
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
169
|
-
handleExists(csn, options, error, inspectRef, initDefinition, dropDefinitionCache);
|
|
164
|
+
handleExists(csn, options, error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
|
|
165
|
+
|
|
166
|
+
doA2J && flattening.linkForeignKeyAnnotationExtensionsToAssociation(csn, options);
|
|
170
167
|
|
|
171
168
|
// Check if structured elements and managed associations are compared in an expression
|
|
172
169
|
// and expand these structured elements. This tuple expansion allows all other
|
|
@@ -179,19 +176,17 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
179
176
|
|
|
180
177
|
throwWithAnyError();
|
|
181
178
|
|
|
182
|
-
const transformCsn = transformUtils.transformModel;
|
|
183
|
-
|
|
184
179
|
forEachDefinition(csn, [
|
|
185
180
|
// (001) Add a temporal where condition to views where applicable before assoc2join
|
|
186
181
|
// assoc2join eventually rewrites the table aliases
|
|
187
|
-
temporal.getViewDecorator(csn,
|
|
182
|
+
temporal.getViewDecorator(csn, messageFunctions, csnUtils),
|
|
188
183
|
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
189
184
|
assertUnique.prepare(csn, options, error, info)
|
|
190
185
|
]);
|
|
191
186
|
|
|
192
187
|
if(doA2J) {
|
|
193
188
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
194
|
-
expansion.expandStructureReferences(csn, options, pathDelimiter,
|
|
189
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, messageFunctions, csnUtils);
|
|
195
190
|
bindCsnReference();
|
|
196
191
|
}
|
|
197
192
|
|
|
@@ -217,7 +212,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
217
212
|
flattening.flattenElements(csn, options, pathDelimiter, error);
|
|
218
213
|
} else {
|
|
219
214
|
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
220
|
-
flattening.resolveTypeReferences(csn, options,
|
|
215
|
+
flattening.resolveTypeReferences(csn, options, new WeakMap(), pathDelimiter);
|
|
221
216
|
}
|
|
222
217
|
|
|
223
218
|
// (010) If requested, translate associations to joins
|
|
@@ -227,7 +222,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
227
222
|
bindCsnReference();
|
|
228
223
|
|
|
229
224
|
const redoProjections = [];
|
|
230
|
-
// Use the "raw" forEachDefinition here to ensure that the
|
|
225
|
+
// Use the "raw" forEachDefinition here to ensure that the $ignore takes effect
|
|
231
226
|
_forEachDefinition(csn, (artifact) => {
|
|
232
227
|
if(artifact.kind === 'entity' && artifact.projection) {
|
|
233
228
|
artifact.query = { SELECT: artifact.projection };
|
|
@@ -242,8 +237,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
242
237
|
}
|
|
243
238
|
})
|
|
244
239
|
} else if(artifact.kind === 'annotation' || artifact.kind === 'action' || artifact.kind === 'function' || artifact.kind === 'event'){
|
|
245
|
-
//
|
|
246
|
-
artifact
|
|
240
|
+
// $ignore actions etc. - this loop seemed handy for this, as we can hook into an existing if
|
|
241
|
+
artifact.$ignore = true;
|
|
247
242
|
}
|
|
248
243
|
});
|
|
249
244
|
|
|
@@ -251,20 +246,30 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
251
246
|
|
|
252
247
|
timetrace.start('Transform CSN')
|
|
253
248
|
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
249
|
+
// Rename primitive types, make UUID a String; replace `items` by cds.LargeString
|
|
250
|
+
//
|
|
251
|
+
// First, gather all nodes that are arrayed: Don't replace inline, or getFinalTypeInfo()
|
|
252
|
+
// may not return `.items` for types that were already processed.
|
|
253
|
+
{
|
|
254
|
+
const removeItems = new Set();
|
|
255
|
+
applyTransformations(csn, {
|
|
256
|
+
type: (node) => {
|
|
257
|
+
if (node.items || node.type && csnUtils.getFinalTypeInfo(node.type)?.items)
|
|
258
|
+
removeItems.add(node);
|
|
259
|
+
renamePrimitiveTypesAndUuid(node.type, node, 'type');
|
|
260
|
+
addDefaultTypeFacets(node, implicitDefaultLengths);
|
|
261
|
+
},
|
|
262
|
+
items: (node) => removeItems.add(node),
|
|
263
|
+
});
|
|
260
264
|
// no support for array-of - turn into CLOB/Text
|
|
261
265
|
// must be done after A2J or compiler checks could change
|
|
262
266
|
// (e.g. annotation def checks for arrayed types)
|
|
263
|
-
|
|
267
|
+
for (const node of removeItems) {
|
|
264
268
|
node.type = 'cds.LargeString';
|
|
265
269
|
delete node.items;
|
|
266
|
-
}
|
|
267
|
-
|
|
270
|
+
}
|
|
271
|
+
removeItems.clear();
|
|
272
|
+
}
|
|
268
273
|
|
|
269
274
|
forEachDefinition(csn, [
|
|
270
275
|
// (040) Ignore entities and views that are abstract or implemented
|
|
@@ -273,7 +278,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
273
278
|
cdsPersistence.getAnnoProcessor(),
|
|
274
279
|
// (050) Check @cds.valid.from/to only on entity
|
|
275
280
|
// Views are checked in (001), unbalanced valid.from/to's or mismatching origins
|
|
276
|
-
temporal.getAnnotationHandler(csn, options, pathDelimiter,
|
|
281
|
+
temporal.getAnnotationHandler(csn, options, pathDelimiter, messageFunctions)
|
|
277
282
|
]);
|
|
278
283
|
|
|
279
284
|
// eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
|
|
@@ -285,7 +290,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
285
290
|
|
|
286
291
|
// (045) Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
|
|
287
292
|
// and make them entities
|
|
288
|
-
forEachDefinition(csn, cdsPersistence.getPersistenceTableProcessor(csn, options,
|
|
293
|
+
forEachDefinition(csn, cdsPersistence.getPersistenceTableProcessor(csn, options, messageFunctions));
|
|
289
294
|
|
|
290
295
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
291
296
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
@@ -341,19 +346,19 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
341
346
|
createReferentialConstraints(csn, options);
|
|
342
347
|
|
|
343
348
|
// no constraints for drafts
|
|
344
|
-
generateDrafts(csn, options, pathDelimiter,
|
|
349
|
+
generateDrafts(csn, options, pathDelimiter, messageFunctions);
|
|
345
350
|
|
|
346
351
|
// Set the final constraint paths and produce hana tc indexes if required
|
|
347
352
|
// See function comment for extensive information.
|
|
348
353
|
assertUnique.rewrite(csn, options, pathDelimiter);
|
|
349
354
|
|
|
350
355
|
// Associations that point to things marked with @cds.persistence.skip are removed
|
|
351
|
-
forEachDefinition(csn, cdsPersistence.getAssocToSkippedIgnorer(csn, options,
|
|
356
|
+
forEachDefinition(csn, cdsPersistence.getAssocToSkippedIgnorer(csn, options, messageFunctions, csnUtils));
|
|
352
357
|
|
|
353
358
|
// Apply view-specific transformations
|
|
354
359
|
// (160) Projections now finally become views
|
|
355
360
|
// Replace managed association in group/order by with foreign keys
|
|
356
|
-
const transformEntityOrViewPass2 = getViewTransformer(csn, options,
|
|
361
|
+
const transformEntityOrViewPass2 = getViewTransformer(csn, options, messageFunctions, transformCommon);
|
|
357
362
|
forEachDefinition(csn, transformViews);
|
|
358
363
|
|
|
359
364
|
// Recursively apply transformCommon and attach @cds.persistence.name
|
|
@@ -384,14 +389,14 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
384
389
|
|
|
385
390
|
const killers = {
|
|
386
391
|
// Used to ignore actions etc from processing and remove associations/elements
|
|
387
|
-
'
|
|
392
|
+
'$ignore': function (parent, a, b, path){
|
|
388
393
|
if(path.length > 2) {
|
|
389
394
|
const tail = path[path.length-1];
|
|
390
395
|
const parentPath = path.slice(0, -1)
|
|
391
396
|
const parentParent = walkCsnPath(csn, parentPath);
|
|
392
397
|
delete parentParent[tail];
|
|
393
398
|
} else {
|
|
394
|
-
delete parent
|
|
399
|
+
delete parent.$ignore;
|
|
395
400
|
}
|
|
396
401
|
},
|
|
397
402
|
// Still used in flattenStructuredElements - in db/flattening.js
|
|
@@ -441,7 +446,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
441
446
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
442
447
|
|
|
443
448
|
function bindCsnReference(){
|
|
444
|
-
|
|
449
|
+
messageFunctions = makeMessageFunction(csn, options, moduleName);
|
|
450
|
+
({ error, warning, info, message, throwWithAnyError } = messageFunctions);
|
|
445
451
|
|
|
446
452
|
({ flattenStructuredElement,
|
|
447
453
|
flattenStructStepsInRef,
|
|
@@ -451,94 +457,85 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
451
457
|
expandStructsInExpression,
|
|
452
458
|
csnUtils
|
|
453
459
|
} = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
454
|
-
|
|
455
|
-
({ artifactRef,
|
|
456
|
-
inspectRef,
|
|
457
|
-
effectiveType,
|
|
458
|
-
initDefinition,
|
|
459
|
-
dropDefinitionCache,
|
|
460
|
-
get$combined,
|
|
461
|
-
getCsnDef,
|
|
462
|
-
isAssocOrComposition,
|
|
463
|
-
addStringAnnotationTo,
|
|
464
|
-
} = csnUtils);
|
|
465
460
|
}
|
|
466
461
|
|
|
467
462
|
function bindCsnReferenceOnly(){
|
|
468
463
|
// invalidate caches for CSN ref API
|
|
469
|
-
|
|
464
|
+
const csnRefApi = csnRefs(csn);
|
|
465
|
+
Object.assign(csnUtils, csnRefApi);
|
|
470
466
|
}
|
|
471
467
|
|
|
468
|
+
// For non-A2J only
|
|
472
469
|
function handleMixinOnConditions(artifact, artifactName) {
|
|
473
|
-
if (!artifact.query)
|
|
470
|
+
if (!artifact.query) // projections can't have mixins
|
|
474
471
|
return;
|
|
475
472
|
forAllQueries(artifact.query, (query, path) => {
|
|
476
|
-
const { mixin } = query.SELECT
|
|
477
|
-
if(mixin) {
|
|
473
|
+
const { mixin } = query.SELECT || {};
|
|
474
|
+
if (mixin) {
|
|
478
475
|
query.SELECT.columns
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
})
|
|
476
|
+
// filter for associations which are used in the SELECT
|
|
477
|
+
.filter((c) => {
|
|
478
|
+
return c.ref && c.ref.length > 1;
|
|
479
|
+
})
|
|
480
|
+
.forEach((usedAssoc) => {
|
|
481
|
+
const assocName = pathId(usedAssoc.ref[0]);
|
|
482
|
+
const mixinAssociation = mixin[assocName];
|
|
483
|
+
if (mixinAssociation)
|
|
484
|
+
mixinAssociation.on = getResolvedMixinOnCondition(mixinAssociation, query, assocName, path.concat(['mixin', assocName]));
|
|
485
|
+
})
|
|
490
486
|
}
|
|
491
|
-
}, [
|
|
492
|
-
|
|
493
|
-
function getResolvedMixinOnCondition(csn, mixinAssociation, query, assocName, path){
|
|
494
|
-
const { inspectRef } = csnRefs(csn);
|
|
495
|
-
const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
|
|
496
|
-
return mixinAssociation.on
|
|
497
|
-
.map((onConditionPart, i) => {
|
|
498
|
-
let columnToReplace;
|
|
499
|
-
if(onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')){
|
|
500
|
-
const { links } = inspectRef(path.concat(['on', i]));
|
|
501
|
-
if(links){
|
|
502
|
-
columnToReplace = onConditionPart.ref[links.length - 1];
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
if (!columnToReplace)
|
|
506
|
-
return onConditionPart;
|
|
507
|
-
|
|
508
|
-
const replaceWith = query.SELECT.columns.find((column) =>
|
|
509
|
-
column.as && column.as === columnToReplace ||
|
|
510
|
-
column.ref && column.ref[0] === columnToReplace
|
|
511
|
-
);
|
|
512
|
-
if (!replaceWith && referencedThroughStar) {
|
|
513
|
-
// not explicitly in column list, check query sources
|
|
514
|
-
// get$combined also includes elements which are part of "excluding {}"
|
|
515
|
-
// this shouldn't be an issue here, as such references get rejected
|
|
516
|
-
const elementsOfQuerySources = get$combined(query);
|
|
517
|
-
forEach(elementsOfQuerySources, (id, element) => {
|
|
518
|
-
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
519
|
-
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
520
|
-
if(id === columnToReplace)
|
|
521
|
-
onConditionPart.ref[0] = element[0].parent;
|
|
522
|
-
});
|
|
523
|
-
}
|
|
487
|
+
}, ['definitions', artifactName, 'query']);
|
|
488
|
+
}
|
|
524
489
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
490
|
+
// For non-A2J only
|
|
491
|
+
function getResolvedMixinOnCondition(mixinAssociation, query, assocName, path) {
|
|
492
|
+
const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
|
|
493
|
+
return mixinAssociation.on.map(handeMixinOnConditionPart);
|
|
494
|
+
|
|
495
|
+
function handeMixinOnConditionPart(onConditionPart, i) {
|
|
496
|
+
let columnToReplace;
|
|
497
|
+
if (onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')){
|
|
498
|
+
const { links } = csnUtils.inspectRef(path.concat(['on', i]));
|
|
499
|
+
if (links) {
|
|
500
|
+
columnToReplace = onConditionPart.ref[links.length - 1];
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (!columnToReplace)
|
|
504
|
+
return onConditionPart;
|
|
505
|
+
|
|
506
|
+
const replaceWith = query.SELECT.columns.find(col => columnAlias(col) === columnToReplace);
|
|
507
|
+
if (!replaceWith && referencedThroughStar) {
|
|
508
|
+
// not explicitly in column list, check query sources
|
|
509
|
+
// get$combined also includes elements which are part of "excluding {}"
|
|
510
|
+
// this shouldn't be an issue here, as such references get rejected
|
|
511
|
+
const elementsOfQuerySources = csnUtils.get$combined(query);
|
|
512
|
+
forEach(elementsOfQuerySources, (id, element) => {
|
|
513
|
+
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
514
|
+
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
515
|
+
if(id === columnToReplace)
|
|
516
|
+
onConditionPart.ref[0] = element[0].parent;
|
|
517
|
+
});
|
|
518
|
+
return onConditionPart;
|
|
519
|
+
}
|
|
520
|
+
else if (replaceWith) {
|
|
521
|
+
const clone = cloneCsnNonDict(replaceWith, options);
|
|
522
|
+
delete clone.cast; // No implicit CAST in on-condition
|
|
523
|
+
delete clone.as;
|
|
524
|
+
return clone;
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
return onConditionPart
|
|
528
|
+
}
|
|
533
529
|
}
|
|
534
530
|
}
|
|
535
531
|
|
|
532
|
+
|
|
536
533
|
/**
|
|
537
534
|
* @param {CSN.Artifact} artifact
|
|
538
535
|
* @param {string} artifactName
|
|
539
536
|
*/
|
|
540
537
|
function transformViews(artifact, artifactName) {
|
|
541
|
-
if (!artifact
|
|
538
|
+
if (!artifact.$ignore) {
|
|
542
539
|
// Do things specific for entities and views (pass 2)
|
|
543
540
|
if ((artifact.kind === 'entity') && artifact.query) {
|
|
544
541
|
|
|
@@ -557,7 +554,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
557
554
|
|
|
558
555
|
const process = (parent, prop, query, path) => {
|
|
559
556
|
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
|
|
560
|
-
replaceAssociationsInGroupByOrderBy(parent, options, inspectRef, error, path.concat(prop));
|
|
557
|
+
replaceAssociationsInGroupByOrderBy(parent, options, csnUtils.inspectRef, error, path.concat(prop));
|
|
561
558
|
return query;
|
|
562
559
|
}
|
|
563
560
|
applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
|
|
@@ -572,9 +569,9 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
572
569
|
* @param {string} artifactName
|
|
573
570
|
*/
|
|
574
571
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
575
|
-
if (!artifact
|
|
572
|
+
if (!artifact.$ignore) {
|
|
576
573
|
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
577
|
-
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
574
|
+
csnUtils.addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
578
575
|
|
|
579
576
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
580
577
|
if (property === 'returns')
|
|
@@ -584,7 +581,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
584
581
|
// Virtual elements in entities and types are not annotated, as they have no DB representation.
|
|
585
582
|
// In views they are, as we generate a null expression for them (null as <colname>)
|
|
586
583
|
if ((!member.virtual || artifact.query))
|
|
587
|
-
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
584
|
+
csnUtils.addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
588
585
|
}, [ 'definitions', artifactName ]);
|
|
589
586
|
}
|
|
590
587
|
}
|
|
@@ -594,7 +591,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
594
591
|
* @param {string} artifactName
|
|
595
592
|
*/
|
|
596
593
|
function removeKeyPropInType(artifact, artifactName) {
|
|
597
|
-
if (!artifact
|
|
594
|
+
if (!artifact.$ignore && artifact.kind === 'type') {
|
|
598
595
|
forEachMemberRecursively(artifact, (member) => {
|
|
599
596
|
if (member.key)
|
|
600
597
|
delete member.key;
|
|
@@ -618,7 +615,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
618
615
|
function doit(dict, subPath) {
|
|
619
616
|
for (const elemName in dict) {
|
|
620
617
|
const elem = dict[elemName];
|
|
621
|
-
if (elem.on && isAssocOrComposition(elem))
|
|
618
|
+
if (elem.on && csnUtils.isAssocOrComposition(elem))
|
|
622
619
|
processBacklinkAssoc(elem, elemName, artifact, artifactName, subPath.concat([ elemName, 'on' ]));
|
|
623
620
|
}
|
|
624
621
|
}
|
|
@@ -641,7 +638,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
641
638
|
// For HANA: Report an error on
|
|
642
639
|
// - view with parameters that has an element of type association/composition
|
|
643
640
|
// - association that points to entity with parameters
|
|
644
|
-
if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
641
|
+
if (options.sqlDialect === 'hana' && member.target && csnUtils.isAssocOrComposition(member) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
645
642
|
if (artifact.params) {
|
|
646
643
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
647
644
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
@@ -676,7 +673,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
676
673
|
* @param {string} artifactName
|
|
677
674
|
*/
|
|
678
675
|
function handleChecksForWithParameters(artifact, artifactName) {
|
|
679
|
-
if (!artifact
|
|
676
|
+
if (!artifact.$ignore && artifact.params && (artifact.kind === 'entity')) {
|
|
680
677
|
if (!artifact.query) { // table entity with params
|
|
681
678
|
// Allow with plain
|
|
682
679
|
error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
|
|
@@ -707,19 +704,38 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
707
704
|
throwWithAnyError();
|
|
708
705
|
// the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
|
|
709
706
|
// simply make it invisible and copy it over to the result csn
|
|
710
|
-
forEachDefinition(csn,
|
|
707
|
+
forEachDefinition(csn,
|
|
708
|
+
art => art.technicalConfig && setProp(art, 'technicalConfig',
|
|
709
|
+
art.technicalConfig));
|
|
711
710
|
|
|
712
711
|
const newCsn = translateAssocsToJoinsCSN(csn, options);
|
|
713
712
|
|
|
714
713
|
// restore all (non-enumerable) properties that wouldn't survive reaugmentation/compactification into the new compact model
|
|
715
714
|
forEachDefinition(csn, (art, artName) => {
|
|
716
715
|
if(art['$tableConstraints']) {
|
|
717
|
-
|
|
716
|
+
newCsn.definitions[artName].$tableConstraints = art['$tableConstraints'];
|
|
718
717
|
}
|
|
719
718
|
if (art.technicalConfig)
|
|
720
719
|
newCsn.definitions[artName].technicalConfig = art.technicalConfig;
|
|
721
720
|
|
|
722
721
|
});
|
|
722
|
+
// restore $fkExtensions and $structRef for foreign key annotations
|
|
723
|
+
if (isBetaEnabled(options, 'annotateForeignKeys')) {
|
|
724
|
+
forEachDefinition(csn, (oldDef, artName) => {
|
|
725
|
+
const newDef = newCsn.definitions[artName];
|
|
726
|
+
if(oldDef?.elements) {
|
|
727
|
+
Object.entries(oldDef.elements).forEach(([eltName, oldElt]) => {
|
|
728
|
+
const newElt = newDef.elements[eltName];
|
|
729
|
+
if(oldElt.$fkExtensions)
|
|
730
|
+
setProp(newElt, '$fkExtensions', oldElt.$fkExtensions);
|
|
731
|
+
oldElt.keys?.forEach((fk, i) => {
|
|
732
|
+
if(fk.$structRef && newElt.keys?.[i])
|
|
733
|
+
setProp(newElt.keys[i], '$structRef', fk.$structRef);
|
|
734
|
+
})
|
|
735
|
+
})
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
}
|
|
723
739
|
csn = newCsn;
|
|
724
740
|
}
|
|
725
741
|
|
|
@@ -754,11 +770,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
754
770
|
function renamePrimitiveTypesAndUuid(val, node, key) {
|
|
755
771
|
// assert key === 'type'
|
|
756
772
|
const hanaNamesMap = createDict({
|
|
757
|
-
'cds.
|
|
758
|
-
'cds.Timestamp': 'cds.UTCTimestamp',
|
|
759
|
-
'cds.Date': 'cds.LocalDate',
|
|
760
|
-
'cds.Time': 'cds.LocalTime',
|
|
761
|
-
'cds.UUID': 'cds.String',
|
|
773
|
+
'cds.UUID': 'cds.String'
|
|
762
774
|
});
|
|
763
775
|
node[key] = hanaNamesMap[val] || val;
|
|
764
776
|
if (val === 'cds.UUID' && !node.length) {
|
|
@@ -790,7 +802,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
790
802
|
if (elem.default && elem.default['#']) {
|
|
791
803
|
let Enum = elem.enum;
|
|
792
804
|
if (!Enum && !isBuiltinType(elem.type)) {
|
|
793
|
-
const typeDef = getCsnDef(elem.type);
|
|
805
|
+
const typeDef = csnUtils.getCsnDef(elem.type);
|
|
794
806
|
Enum = typeDef && typeDef.enum;
|
|
795
807
|
}
|
|
796
808
|
if (!Enum) {
|
|
@@ -852,7 +864,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
852
864
|
// Check if one side is $self and the other an association
|
|
853
865
|
// (if so, replace all three tokens with the condition generated from the other side, in parentheses)
|
|
854
866
|
if (isDollarSelfOrProjectionOperand(xprArgs[i]) && isAssociationOperand(xprArgs[i + 2], path.concat([ i + 2 ]))) {
|
|
855
|
-
const assoc = inspectRef(path.concat([ i + 2 ])).art;
|
|
867
|
+
const assoc = csnUtils.inspectRef(path.concat([ i + 2 ])).art;
|
|
856
868
|
if (multipleExprs)
|
|
857
869
|
result.push('(');
|
|
858
870
|
const backlinkName = xprArgs[i + 2].ref[xprArgs[i + 2].ref.length - 1];
|
|
@@ -867,7 +879,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
867
879
|
attachBacklinkInformation(backlinkName);
|
|
868
880
|
}
|
|
869
881
|
else if (isDollarSelfOrProjectionOperand(xprArgs[i + 2]) && isAssociationOperand(xprArgs[i], path.concat([ i ]))) {
|
|
870
|
-
const assoc = inspectRef(path.concat([ i ])).art;
|
|
882
|
+
const assoc = csnUtils.inspectRef(path.concat([ i ])).art;
|
|
871
883
|
if (multipleExprs)
|
|
872
884
|
result.push('(');
|
|
873
885
|
const backlinkName = xprArgs[i].ref[xprArgs[i].ref.length - 1];
|
|
@@ -943,7 +955,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
943
955
|
if(assoc.keys.length)
|
|
944
956
|
return transformDollarSelfComparisonWithManagedAssoc(assocOp, assoc, assocName, elemName);
|
|
945
957
|
else {
|
|
946
|
-
elem
|
|
958
|
+
elem.$ignore = true;
|
|
947
959
|
return [];
|
|
948
960
|
}
|
|
949
961
|
}
|
|
@@ -1034,10 +1046,10 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1034
1046
|
function checkTypeParameters(artifact, artifactName) {
|
|
1035
1047
|
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
1036
1048
|
// Check type parameters (length, precision, scale ...)
|
|
1037
|
-
if (!member
|
|
1049
|
+
if (!member.$ignore && member.type)
|
|
1038
1050
|
_check(member, memberName, csn, path);
|
|
1039
1051
|
|
|
1040
|
-
if (!member
|
|
1052
|
+
if (!member.$ignore && member.items && member.items.type)
|
|
1041
1053
|
_check(member.items, memberName, csn, path.concat([ 'items' ]));
|
|
1042
1054
|
}, [ 'definitions', artifactName ]);
|
|
1043
1055
|
|
|
@@ -1079,7 +1091,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1079
1091
|
// (which can currently only be 'positiveInteger') and (optional) the value is in a given range
|
|
1080
1092
|
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
1081
1093
|
const paramValue = node[paramName];
|
|
1082
|
-
if (paramValue
|
|
1094
|
+
if (paramValue == null) {
|
|
1083
1095
|
if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
|
|
1084
1096
|
return true;
|
|
1085
1097
|
} else {
|
|
@@ -1134,7 +1146,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1134
1146
|
if (typeof val === 'object' && val.ref) {
|
|
1135
1147
|
// Replace a reference by references to it's elements, if it is structured
|
|
1136
1148
|
const path = [ 'definitions', artName, 'technicalConfig', dialect, 'indexes', name, idx ];
|
|
1137
|
-
const { art } = inspectRef(path);
|
|
1149
|
+
const { art } = csnUtils.inspectRef(path);
|
|
1138
1150
|
if (!art) {
|
|
1139
1151
|
// A reference that has no artifact (e.g. the reference to the index name itself). Just copy it over
|
|
1140
1152
|
flattenedIndex.push(val);
|
|
@@ -1176,8 +1188,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1176
1188
|
}
|
|
1177
1189
|
}
|
|
1178
1190
|
}
|
|
1179
|
-
}
|
|
1180
1191
|
|
|
1192
|
+
}
|
|
1181
1193
|
|
|
1182
1194
|
module.exports = {
|
|
1183
1195
|
transformForRelationalDBWithCsn,
|