@sap/cds-compiler 3.3.2 → 3.4.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 +33 -0
- package/bin/cdsc.js +3 -1
- package/doc/CHANGELOG_BETA.md +17 -0
- package/lib/api/main.js +147 -18
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/keywords.js +104 -0
- package/lib/base/message-registry.js +137 -68
- package/lib/base/messages.js +59 -48
- package/lib/base/model.js +1 -0
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +13 -8
- package/lib/checks/defaultValues.js +3 -1
- package/lib/checks/elements.js +1 -1
- package/lib/checks/parameters.js +4 -2
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/validator.js +14 -4
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/checks.js +30 -20
- package/lib/compiler/define.js +89 -25
- package/lib/compiler/extend.js +33 -28
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/populate.js +30 -8
- package/lib/compiler/propagator.js +23 -28
- package/lib/compiler/resolve.js +11 -5
- package/lib/compiler/shared.js +66 -48
- package/lib/compiler/tweak-assocs.js +2 -3
- package/lib/compiler/utils.js +11 -0
- package/lib/edm/annotations/genericTranslation.js +7 -4
- package/lib/edm/csn2edm.js +1 -1
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3565 -3544
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +140 -158
- package/lib/json/to-csn.js +23 -5
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +7 -10
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +115 -84
- package/lib/language/language.g4 +29 -25
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.js +1 -0
- package/lib/model/csnRefs.js +4 -3
- package/lib/model/csnUtils.js +39 -7
- package/lib/model/sortViews.js +7 -3
- package/lib/modelCompare/compare.js +49 -15
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +5 -1
- package/lib/render/manageConstraints.js +9 -5
- package/lib/render/toCdl.js +120 -62
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +6 -2
- package/lib/render/utils/common.js +7 -0
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +11 -4
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +7 -1
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +7 -3
- package/lib/transform/forRelationalDB.js +12 -6
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +23 -14
- package/lib/transform/translateAssocsToJoins.js +12 -12
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { forEachDefinition, hasAnnotationValue } = require('../../model/csnUtils');
|
|
4
4
|
const { getTransformers } = require('../transformUtilsNew');
|
|
5
5
|
const { setProp } = require('../../base/model');
|
|
6
|
+
const { pathName } = require('../../compiler/utils');
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -38,13 +39,14 @@ function processAssertUnique(csn, options, error, info) {
|
|
|
38
39
|
// filter unique constraints from annotations
|
|
39
40
|
for (const propName in artifact) {
|
|
40
41
|
if (propName.startsWith('@assert.unique') && artifact[propName] !== null) {
|
|
42
|
+
const anno = propName;
|
|
41
43
|
// Constraint Name check
|
|
42
44
|
const constraintName = propName.split('.').splice(2);
|
|
43
45
|
if (constraintName.length === 0)
|
|
44
|
-
|
|
46
|
+
error(null, [ 'definitions', artifactName ], { anno }, '$(ANNO): Table constraint can\'t be anonymous');
|
|
45
47
|
if (constraintName.length > 1)
|
|
46
48
|
// Neither HANA CDS nor HANA SQL allow dots in index names
|
|
47
|
-
|
|
49
|
+
error(null, [ 'definitions', artifactName ], { anno }, '$(ANNO): Illegal character \'.\' in constraint name');
|
|
48
50
|
|
|
49
51
|
const propValue = artifact[propName];
|
|
50
52
|
// Constraint value check, returns array of path values
|
|
@@ -79,10 +81,12 @@ function processAssertUnique(csn, options, error, info) {
|
|
|
79
81
|
pathxrefs[pstr]++;
|
|
80
82
|
});
|
|
81
83
|
Object.keys(pathxrefs).forEach((k) => {
|
|
82
|
-
if (pathxrefs[k] > 1)
|
|
83
|
-
|
|
84
|
+
if (pathxrefs[k] > 1) {
|
|
85
|
+
error(null, [ 'definitions', artifactName ], { anno, id: k },
|
|
86
|
+
'$(ANNO): Final path $(ID) can only be specified once');
|
|
87
|
+
}
|
|
84
88
|
});
|
|
85
|
-
// 9) Add into constraint cross
|
|
89
|
+
// 9) Add into constraint cross-reference
|
|
86
90
|
if (constraintKey.length) {
|
|
87
91
|
if (constraintXrefs[constraintKey])
|
|
88
92
|
constraintXrefs[constraintKey].push(propName);
|
|
@@ -99,7 +103,7 @@ function processAssertUnique(csn, options, error, info) {
|
|
|
99
103
|
for (const key in constraintXrefs) {
|
|
100
104
|
const val = constraintXrefs[key];
|
|
101
105
|
if (val.length > 1)
|
|
102
|
-
|
|
106
|
+
error(null, [ 'definitions', artifactName ], { annos: val }, '$(ANNOS): Constraint can only be specified once');
|
|
103
107
|
}
|
|
104
108
|
// preserve dictionary in '$tableConstraints' on the artifact for path rewriting and rendering
|
|
105
109
|
if (Object.keys(constraintDict).length) {
|
|
@@ -119,17 +123,18 @@ function processAssertUnique(csn, options, error, info) {
|
|
|
119
123
|
*/
|
|
120
124
|
function checkVal(val, propName) {
|
|
121
125
|
const paths = [];
|
|
126
|
+
const loc = [ 'definitions', artifactName ];
|
|
122
127
|
if (!Array.isArray(val)) {
|
|
123
|
-
|
|
128
|
+
error(null, loc, { anno: propName, value: JSON.stringify(unref(val)) }, '$(ANNO): Value $(VALUE) is not an array');
|
|
124
129
|
}
|
|
125
130
|
else {
|
|
126
131
|
if (val.length === 0)
|
|
127
|
-
|
|
132
|
+
info(null, loc, { anno: propName }, '$(ANNO): Empty annotation is ignored');
|
|
128
133
|
|
|
129
134
|
val.forEach((v) => {
|
|
130
135
|
const p = v['='];
|
|
131
136
|
if (!p)
|
|
132
|
-
|
|
137
|
+
error(null, loc, { anno: propName, value: JSON.stringify(unref(v)) }, '$(ANNO): Value $(VALUE) is not a path');
|
|
133
138
|
else
|
|
134
139
|
paths.push(p);
|
|
135
140
|
});
|
|
@@ -185,61 +190,45 @@ function processAssertUnique(csn, options, error, info) {
|
|
|
185
190
|
return;
|
|
186
191
|
path.isChecked = true;
|
|
187
192
|
let foundErr = false;
|
|
193
|
+
const name = pathName(path.ref);
|
|
194
|
+
const loc = [ 'definitions', artifactName ];
|
|
188
195
|
for (let i = 0; i < path.ref.length && !foundErr; i++) {
|
|
189
196
|
const art = path.ref[i]._art;
|
|
197
|
+
const elemref = path.ref[i].id;
|
|
190
198
|
if (art) {
|
|
191
199
|
if (art.items) {
|
|
192
|
-
|
|
200
|
+
error(null, loc, { elemref, name, anno: constraintName },
|
|
201
|
+
'$(ANNO): \'Array of/many\' element $(ELEMREF) is not allowed in $(NAME)');
|
|
193
202
|
delete path._art;
|
|
194
203
|
foundErr = true;
|
|
195
204
|
}
|
|
196
205
|
if (art.target) {
|
|
197
206
|
if (art.on) {
|
|
198
|
-
|
|
207
|
+
error(null, loc, { elemref, name, anno: constraintName },
|
|
208
|
+
'$(ANNO): Unmanaged association $(ELEMREF) is not allowed in $(NAME)');
|
|
199
209
|
delete path._art;
|
|
200
210
|
foundErr = true;
|
|
201
211
|
}
|
|
202
212
|
if (art.keys && i < path.ref.length - 1) {
|
|
203
|
-
|
|
213
|
+
error(null, loc, { elemref, name, anno: constraintName },
|
|
214
|
+
'$(ANNO): Element access via managed association $(ELEMREF) is not allowed in $(NAME)');
|
|
204
215
|
delete path._art;
|
|
205
216
|
foundErr = true;
|
|
206
217
|
}
|
|
207
218
|
}
|
|
208
219
|
}
|
|
209
220
|
else {
|
|
210
|
-
|
|
221
|
+
error(null, loc, { elemref, anno: constraintName }, '$(ANNO): $(ELEMREF) has not been found');
|
|
211
222
|
foundErr = true;
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
if (!foundErr && path._art && [ 'cds.LargeBinary', 'cds.LargeString',
|
|
216
|
-
'cds.hana.CLOB', 'cds.hana.ST_POINT', 'cds.hana.ST_GEOMETRY' ].includes(path._art.type))
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @param {string} message
|
|
222
|
-
*/
|
|
223
|
-
function msg(message) {
|
|
224
|
-
err(constraintName, `${message} in "${path.ref.map(p => p.id).join('.')}"`);
|
|
227
|
+
'cds.hana.CLOB', 'cds.hana.ST_POINT', 'cds.hana.ST_GEOMETRY' ].includes(path._art.type)) {
|
|
228
|
+
error(null, loc, { type: path._art.type, name, anno: constraintName },
|
|
229
|
+
'$(ANNO): Type $(TYPE) not allowed in $(NAME)');
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
|
-
|
|
228
|
-
// message macros for unified messaging
|
|
229
|
-
/**
|
|
230
|
-
* @param {string} propName
|
|
231
|
-
* @param {string} message
|
|
232
|
-
*/
|
|
233
|
-
function err(propName, message) {
|
|
234
|
-
error(null, [ 'definitions', artifactName ], `${propName}: ${message}`);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* @param {string} propName
|
|
238
|
-
* @param {string} message
|
|
239
|
-
*/
|
|
240
|
-
function inf(propName, message) {
|
|
241
|
-
info(null, [ 'definitions', artifactName ], `${propName}: ${message}`);
|
|
242
|
-
}
|
|
243
232
|
}
|
|
244
233
|
}
|
|
245
234
|
|
|
@@ -62,7 +62,8 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
62
62
|
* For such skipped things, error for usage of assoc pointing to them and and ignore publishing of assoc pointing to them.
|
|
63
63
|
*/
|
|
64
64
|
function rewriteExpandInline() {
|
|
65
|
-
|
|
65
|
+
let cleanup;
|
|
66
|
+
let _dependents;
|
|
66
67
|
|
|
67
68
|
const entity = findAnEntity();
|
|
68
69
|
const toDummyfy = [];
|
|
@@ -76,7 +77,6 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
76
77
|
const root = get$combined({ SELECT: parent });
|
|
77
78
|
if (!hasAnnotationValue(artifact, '@cds.persistence.table')) {
|
|
78
79
|
const rewritten = rewrite(root, parent.columns, parent.excluding);
|
|
79
|
-
parent.columns = rewritten.columns;
|
|
80
80
|
/*
|
|
81
81
|
* Do not remove unexpandable many columns in OData
|
|
82
82
|
*/
|
|
@@ -96,7 +96,8 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
96
96
|
if (!options.toOdata)
|
|
97
97
|
dummyfy();
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
if (cleanup)
|
|
100
|
+
cleanup.forEach(fn => fn());
|
|
100
101
|
|
|
101
102
|
({ effectiveType, inspectRef } = csnRefs(csn));
|
|
102
103
|
|
|
@@ -104,7 +105,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
104
105
|
const publishing = [];
|
|
105
106
|
// OData must allow navigations to @cds.persistence.skip targets
|
|
106
107
|
// as valid navigations in the API
|
|
107
|
-
if (
|
|
108
|
+
if (options.transformation !== 'odata') {
|
|
108
109
|
applyTransformations(csn, {
|
|
109
110
|
target: (parent, name, target, path) => {
|
|
110
111
|
if (toDummyfy.indexOf(target) !== -1) {
|
|
@@ -207,6 +208,9 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
207
208
|
* @param {string} name
|
|
208
209
|
*/
|
|
209
210
|
function markAsToDummyfy(artifact, name) {
|
|
211
|
+
if (!_dependents && !cleanup)
|
|
212
|
+
({ cleanup, _dependents } = setDependencies(csn, csnUtils));
|
|
213
|
+
|
|
210
214
|
const stack = [ [ artifact, name ] ];
|
|
211
215
|
while (stack.length > 0) {
|
|
212
216
|
const [ a, n ] = stack.pop();
|
|
@@ -355,6 +359,9 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
355
359
|
else if (current.val !== undefined || current.func !== undefined) {
|
|
356
360
|
expanded.push(Object.assign(current, { as: currentAlias.join(pathDelimiter) }));
|
|
357
361
|
}
|
|
362
|
+
else if (current.$scope === '$magic' || current.$scope === '$self') {
|
|
363
|
+
expanded.push(Object.assign({}, current, { as: currentAlias.join(pathDelimiter) } ));
|
|
364
|
+
}
|
|
358
365
|
else { // preserve stuff like .cast for redirection
|
|
359
366
|
expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
|
|
360
367
|
}
|
|
@@ -81,7 +81,9 @@ function getViewDecorator(csn, messageFunctions) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
else {
|
|
84
|
-
info(null, [ 'definitions', artifactName ],
|
|
84
|
+
info(null, [ 'definitions', artifactName ],
|
|
85
|
+
{ source: `${from[0].errorParent}.${from[0].name}`, target: `${to[0].errorParent}.${to[0].name}` },
|
|
86
|
+
'No temporal WHERE clause added as $(SOURCE) and $(TARGET) are not of same origin');
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
else if (from.length > 0 || to.length > 0) {
|
|
@@ -428,6 +428,11 @@ function handleExists(csn, options, error) {
|
|
|
428
428
|
* @returns {object[]} The stuff to add to the where
|
|
429
429
|
*/
|
|
430
430
|
function translateManagedAssocToWhere(root, target, isPrefixedWithTableAlias, base, current) {
|
|
431
|
+
if (current.$scope === '$self') {
|
|
432
|
+
error('ref-unexpected-exists-self', current.$path, { id: current.ref[0], name: 'exists' }, 'With $(NAME), path steps must not start with $(ID)');
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
|
|
431
436
|
const whereExtension = [];
|
|
432
437
|
for (let j = 0; j < root.keys.length; j++) {
|
|
433
438
|
const lop = { ref: [ target, ...root.keys[j].ref ] }; // target side
|
|
@@ -800,7 +805,8 @@ function handleExists(csn, options, error) {
|
|
|
800
805
|
}
|
|
801
806
|
else if (part.$scope === '$self') { // source side - "absolute" scope
|
|
802
807
|
// Same message as in forRelationalDB/transformDollarSelfComparisonWithUnmanagedAssoc
|
|
803
|
-
error(null, part.$path,
|
|
808
|
+
error(null, part.$path, { name: '$self' },
|
|
809
|
+
'An association that uses $(NAME) in its ON-condition can\'t be compared to "$self"');
|
|
804
810
|
}
|
|
805
811
|
else if (partInspect.art) { // source side - with local scope
|
|
806
812
|
where.push({ ref: [ target, ...assoc.ref.slice(1, -1), ...part.ref ] });
|
|
@@ -247,14 +247,14 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
|
247
247
|
if (isUnion(queryPath) && options.transformation === 'hdbcds') {
|
|
248
248
|
if (isBetaEnabled(options, 'ignoreAssocPublishingInUnion') && doA2J) {
|
|
249
249
|
if (elem.keys)
|
|
250
|
-
info(null, queryPath,
|
|
250
|
+
info(null, queryPath, { name: elemName }, 'Managed association $(NAME), published in a UNION, will be ignored');
|
|
251
251
|
else
|
|
252
|
-
info(null, queryPath,
|
|
252
|
+
info(null, queryPath, { name: elemName }, 'Association $(NAME), published in a UNION, will be ignored');
|
|
253
253
|
|
|
254
254
|
elem._ignore = true;
|
|
255
255
|
}
|
|
256
256
|
else {
|
|
257
|
-
error(null, queryPath,
|
|
257
|
+
error(null, queryPath, { name: elemName }, 'Association $(NAME) can\'t be published in a SAP HANA CDS UNION');
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
else if (queryPath.length > 4 && options.transformation === 'hdbcds') { // path.length > 4 -> is a subquery
|
|
@@ -325,17 +325,40 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
|
325
325
|
* due to an issue with HANA - only for hdbcds-hdbcds, I assume flattening
|
|
326
326
|
* takes care of this for the other cases already
|
|
327
327
|
*
|
|
328
|
-
* @param {CSN.
|
|
328
|
+
* @param {CSN.Column} col
|
|
329
329
|
* @param {CSN.Path} path
|
|
330
330
|
*/
|
|
331
|
-
function addImplicitAliasWithAssoc(
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
331
|
+
function addImplicitAliasWithAssoc(col, path) {
|
|
332
|
+
if (!col.as && col.ref && col.ref.length > 1) {
|
|
333
|
+
const { links } = inspectRef(path);
|
|
334
|
+
if (links && links.slice(0, -1).some(({ art }) => isAssocOrComposition(art && art.type || '')))
|
|
335
|
+
col.as = getLastRefStepString(col.ref);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* If simply selecting from a param like `:param`, we need to add an implicit alias like `:param as param`
|
|
341
|
+
* due to an issue with HANA
|
|
342
|
+
*
|
|
343
|
+
* @param {CSN.Column} col
|
|
344
|
+
*/
|
|
345
|
+
function addImplicitAliasWithLonelyParam(col) {
|
|
346
|
+
if (!col.as && col.param)
|
|
347
|
+
col.as = getLastRefStepString(col.ref);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Loop over the columns and call all of the given functions with the column and the path
|
|
353
|
+
*
|
|
354
|
+
* @param {Function[]} functions
|
|
355
|
+
* @param {CSN.Column[]} columns
|
|
356
|
+
* @param {CSN.Path} path
|
|
357
|
+
*/
|
|
358
|
+
function processColumns(functions, columns, path) {
|
|
359
|
+
for (let i = 0; i < columns.length; i++) {
|
|
360
|
+
const col = columns[i];
|
|
361
|
+
functions.forEach(fn => fn(col, path.concat(i)));
|
|
339
362
|
}
|
|
340
363
|
}
|
|
341
364
|
|
|
@@ -401,8 +424,14 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
|
401
424
|
query.SELECT.columns = Object.keys(elements).filter(elem => !elements[elem]._ignore).map(key => stripLeadingSelf(columnMap[key]));
|
|
402
425
|
// If following an association, explicitly set the implicit alias
|
|
403
426
|
// due to an issue with HANA - this seems to only have an effect on ref files with hdbcds-hdbcds, so only run then
|
|
427
|
+
const columnProcessors = [];
|
|
428
|
+
if (options.transformation === 'hdbcds' || options.transformation === 'sql' && options.sqlDialect === 'hana')
|
|
429
|
+
columnProcessors.push(addImplicitAliasWithLonelyParam);
|
|
404
430
|
if (options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')
|
|
405
|
-
addImplicitAliasWithAssoc
|
|
431
|
+
columnProcessors.push(addImplicitAliasWithAssoc);
|
|
432
|
+
|
|
433
|
+
if (columnProcessors.length > 0)
|
|
434
|
+
processColumns(columnProcessors, query.SELECT.columns, path.concat('columns'));
|
|
406
435
|
|
|
407
436
|
delete query.SELECT.excluding; // just to make the output of the new transformer the same as the old
|
|
408
437
|
|
|
@@ -165,8 +165,8 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
else {
|
|
168
|
-
error(null, [ 'definitions', draftRootName ], { name: persistenceName },
|
|
169
|
-
|
|
168
|
+
error(null, [ 'definitions', draftRootName ], { name: persistenceName, alias: definingDraftRoot },
|
|
169
|
+
'Entity $(NAME) already generated by draft root $(ALIAS)');
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
return;
|
|
@@ -109,7 +109,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
109
109
|
// @ts-ignore
|
|
110
110
|
const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external']);
|
|
111
111
|
// @ts-ignore
|
|
112
|
-
const isExternalServiceMember = (
|
|
112
|
+
const isExternalServiceMember = (art, name) => {
|
|
113
|
+
return !!(externalServices.includes(getServiceName(name)) || (art && art['@cds.external']))
|
|
114
|
+
}
|
|
113
115
|
|
|
114
116
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
115
117
|
enrichUniversalCsn(csn, options);
|
|
@@ -163,10 +165,12 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
163
165
|
// Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
|
|
164
166
|
// OData doesn't resolve type chains after the first 'items'
|
|
165
167
|
flattening.resolveTypeReferences(csn, options, resolved, '_',
|
|
166
|
-
{ skip: [ 'action', 'aspect', 'event', 'function', 'type'],
|
|
168
|
+
{ skip: [ 'action', 'aspect', 'event', 'function', 'type'],
|
|
169
|
+
skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
|
|
167
170
|
// No structured elements exists anymore
|
|
168
171
|
flattening.flattenElements(csn, options, '_', error,
|
|
169
|
-
{ skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
172
|
+
{ skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
173
|
+
skipArtifact: isExternalServiceMember,
|
|
170
174
|
skipStandard: { items: true }, // don't drill further into .items
|
|
171
175
|
skipDict: { actions: true } }); // don't drill further into .actions -> bound actions, action-artifacts are handled by skip
|
|
172
176
|
}
|
|
@@ -310,7 +310,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
310
310
|
* For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
|
|
311
311
|
* hence we do not generate the referential constraints for them.
|
|
312
312
|
*/
|
|
313
|
-
if(options.sqlDialect !== 'plain' && doA2J)
|
|
313
|
+
if(options.sqlDialect !== 'plain' && options.sqlDialect !== 'h2' && doA2J)
|
|
314
314
|
createReferentialConstraints(csn, options);
|
|
315
315
|
|
|
316
316
|
// no constraints for drafts
|
|
@@ -625,12 +625,12 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
625
625
|
}
|
|
626
626
|
else if (options.sqlDialect === 'sqlite') { // view with params
|
|
627
627
|
// Allow with plain
|
|
628
|
-
error(null, [ 'definitions', artifactName ],
|
|
628
|
+
error(null, [ 'definitions', artifactName ], 'SQLite does not support entities with parameters');
|
|
629
629
|
}
|
|
630
630
|
else {
|
|
631
631
|
for (const pname in artifact.params) {
|
|
632
632
|
if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
|
|
633
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ],
|
|
633
|
+
warning(null, [ 'definitions', artifactName, 'params', pname ], 'Expecting regular SQL-Identifier');
|
|
634
634
|
}
|
|
635
635
|
else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
|
|
636
636
|
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
|
|
@@ -704,6 +704,11 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
704
704
|
node.length = 36;
|
|
705
705
|
setProp(node, '$renamed', 'cds.UUID');
|
|
706
706
|
}
|
|
707
|
+
|
|
708
|
+
if(options.sqlDialect === 'h2' && val === 'cds.Decimal' && node.scale === undefined) {
|
|
709
|
+
node[key] = 'cds.DecimalFloat'; // cds.Decimal and cds.Decimal(p) should map do DECFLOAT for h2
|
|
710
|
+
}
|
|
711
|
+
|
|
707
712
|
// Length/Precision/Scale is done in addDefaultTypeFacets
|
|
708
713
|
}
|
|
709
714
|
|
|
@@ -860,7 +865,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
860
865
|
// not to the view).
|
|
861
866
|
// FIXME: This also means that corresponding key fields should be in the select list etc ...
|
|
862
867
|
if (!art.query && !art.projection && assoc.target && assoc.target != artifactName)
|
|
863
|
-
error(null, path,
|
|
868
|
+
error(null, path, { name: '$self' }, 'Only an association that points back to this artifact can be compared to $(NAME)');
|
|
864
869
|
|
|
865
870
|
|
|
866
871
|
// Check: The forward link <assocOp> must not contain '$self' in its own ON-condition
|
|
@@ -868,7 +873,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
868
873
|
const containsDollarSelf = assoc.on.some(isDollarSelfOrProjectionOperand);
|
|
869
874
|
|
|
870
875
|
if (containsDollarSelf)
|
|
871
|
-
error(null, path,
|
|
876
|
+
error(null, path, { name: '$self' },
|
|
877
|
+
'An association that uses $(NAME) in its ON-condition can\'t be compared to $(NAME)');
|
|
872
878
|
}
|
|
873
879
|
|
|
874
880
|
// Transform comparison of $self to managed association into AND-combined foreign key comparisons
|
|
@@ -1086,7 +1092,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1086
1092
|
else if (art.elements) {
|
|
1087
1093
|
// The reference is structured
|
|
1088
1094
|
if (isFulltextIndex)
|
|
1089
|
-
error(null, path,
|
|
1095
|
+
error(null, path, { name: artName }, 'A fulltext index can\'t be defined on a structured element $(NAME)');
|
|
1090
1096
|
// First, compute the name from the path, e.g ['s', 's1', 's2' ] will result in 'S_s1_s2' ...
|
|
1091
1097
|
const refPath = flattenStructStepsInRef(val.ref, path);
|
|
1092
1098
|
// ... and take this as the prefix for all elements
|
|
@@ -73,7 +73,7 @@ const _targetFor = Symbol('_targetFor');
|
|
|
73
73
|
*/
|
|
74
74
|
function _addLocalizationViews(csn, options, useJoins, config) {
|
|
75
75
|
// Don't try to create convenience views with errors.
|
|
76
|
-
if (hasErrors(options.messages))
|
|
76
|
+
if (hasErrors(options.messages)) // TODO: this is actually wrong, consider --test-mode
|
|
77
77
|
return csn;
|
|
78
78
|
|
|
79
79
|
const messageFunctions = makeMessageFunction(csn, options);
|
|
@@ -108,7 +108,8 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
108
108
|
if (newType) {
|
|
109
109
|
// error, if it was not exposed by us
|
|
110
110
|
if (!exposedTypes[fullQualifiedNewTypeName]) {
|
|
111
|
-
error(null, path,
|
|
111
|
+
error(null, path, { type: fullQualifiedNewTypeName, name: memberName },
|
|
112
|
+
'Can\'t create artificial type $(TYPE) for $(NAME) because the name is already used');
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
115
|
}
|