@sap/cds-compiler 3.9.2 → 4.0.0
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 +98 -0
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +144 -65
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +24 -6
- package/lib/edm/csn2edm.js +47 -45
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +159 -114
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +5 -3
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- package/share/messages/message-explanations.json +1 -1
|
@@ -139,6 +139,7 @@ function getParentContextName( csn, artifactName ) {
|
|
|
139
139
|
*
|
|
140
140
|
* Context A.B will be created by addIntermediateContexts
|
|
141
141
|
*
|
|
142
|
+
* @param {CSN.Model} csn
|
|
142
143
|
* @param {Function[]} killList Array to add cleanup functions to
|
|
143
144
|
*/
|
|
144
145
|
function addContextMarkers( csn, killList ) {
|
|
@@ -171,7 +172,7 @@ function addContextMarkers( csn, killList ) {
|
|
|
171
172
|
*
|
|
172
173
|
* A and A.B.C.D -> A.B and A.B.C are possible candidates
|
|
173
174
|
*
|
|
174
|
-
*
|
|
175
|
+
* @param {CSN.Model} csn
|
|
175
176
|
* @param {string} parentName Name of the parent context
|
|
176
177
|
* @param {string} artifactName Name of the current context
|
|
177
178
|
* @returns {string[]} All possible context names inbetween
|
|
@@ -311,6 +312,25 @@ const cdsToHdbcdsTypes = {
|
|
|
311
312
|
'cds.Int64': 'cds.Integer64',
|
|
312
313
|
};
|
|
313
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Default lengths for CDS types.
|
|
317
|
+
*/
|
|
318
|
+
const sqlDefaultLengths = {
|
|
319
|
+
hana: {
|
|
320
|
+
'cds.String': 5000,
|
|
321
|
+
},
|
|
322
|
+
default: {
|
|
323
|
+
'cds.String': 255,
|
|
324
|
+
'cds.Binary': 5000,
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
function getDefaultTypeLengths( sqlDialect ) {
|
|
329
|
+
if (!sqlDefaultLengths[sqlDialect])
|
|
330
|
+
return sqlDefaultLengths.default;
|
|
331
|
+
return { ...sqlDefaultLengths.default, ...sqlDefaultLengths[sqlDialect] };
|
|
332
|
+
}
|
|
333
|
+
|
|
314
334
|
/**
|
|
315
335
|
* Get the element matching the column
|
|
316
336
|
*
|
|
@@ -467,9 +487,10 @@ function withoutCast( xpr ) {
|
|
|
467
487
|
* (no trailing LF, don't indent if inline)
|
|
468
488
|
*
|
|
469
489
|
* @param {ExpressionConfiguration} rendererBase
|
|
490
|
+
* @param {boolean} [adaptPath] If true, `env.path` will be adapted for lists and subExpr.
|
|
470
491
|
* @returns {ExpressionRenderer} Expression rendering utility
|
|
471
492
|
*/
|
|
472
|
-
function createExpressionRenderer( rendererBase ) {
|
|
493
|
+
function createExpressionRenderer( rendererBase, adaptPath = false ) {
|
|
473
494
|
const renderer = Object.create(rendererBase);
|
|
474
495
|
renderer.visitExpr = visitExpr;
|
|
475
496
|
/**
|
|
@@ -484,6 +505,7 @@ function createExpressionRenderer( rendererBase ) {
|
|
|
484
505
|
// are nested. This information is used for adding parentheses around
|
|
485
506
|
// expressions (see `this.xpr()`).
|
|
486
507
|
renderObj.isNestedXpr = false;
|
|
508
|
+
renderObj.adaptPath = adaptPath;
|
|
487
509
|
return renderObj.visitExpr(x);
|
|
488
510
|
};
|
|
489
511
|
/**
|
|
@@ -495,6 +517,7 @@ function createExpressionRenderer( rendererBase ) {
|
|
|
495
517
|
const renderObj = Object.create(renderer);
|
|
496
518
|
renderObj.env = env || this?.env;
|
|
497
519
|
renderObj.isNestedXpr = true;
|
|
520
|
+
renderObj.adaptPath = adaptPath;
|
|
498
521
|
return renderObj.visitExpr(x);
|
|
499
522
|
};
|
|
500
523
|
|
|
@@ -519,7 +542,14 @@ function visitExpr( x ) {
|
|
|
519
542
|
// Compound expression, e.g. for on- or where-conditions.
|
|
520
543
|
// If xpr is part of an array, it's always a nested xpr,
|
|
521
544
|
// e.g. CSN for `(1=1 or 2=2) and 3=3`.
|
|
522
|
-
const tokens = x.map(item =>
|
|
545
|
+
const tokens = x.map((item, i) => {
|
|
546
|
+
if (this.adaptPath) {
|
|
547
|
+
// We want to keep the prototype of the original env.
|
|
548
|
+
const env = Object.assign(Object.create(Object.getPrototypeOf(this.env || {})), this.env, { path: [ ...this.env.path, i ] });
|
|
549
|
+
return this.renderSubExpr(item, env);
|
|
550
|
+
}
|
|
551
|
+
return this.renderSubExpr(item);
|
|
552
|
+
});
|
|
523
553
|
return beautifyExprArray(tokens);
|
|
524
554
|
}
|
|
525
555
|
else if (typeof x !== 'object' || x === null) {
|
|
@@ -531,7 +561,14 @@ function visitExpr( x ) {
|
|
|
531
561
|
}
|
|
532
562
|
else if (x.list) {
|
|
533
563
|
// Render as non-nested expr.
|
|
534
|
-
return `(${x.list.map(
|
|
564
|
+
return `(${x.list.map((item, i) => {
|
|
565
|
+
if (this.adaptPath) {
|
|
566
|
+
// We want to keep the prototype of the original env.
|
|
567
|
+
const env = Object.assign(Object.create(Object.getPrototypeOf(this.env || {})), this.env, { path: [ ...this.env.path, 'list', i ] });
|
|
568
|
+
return this.renderExpr(item, env);
|
|
569
|
+
}
|
|
570
|
+
return this.renderExpr(item);
|
|
571
|
+
}).join(', ')})`;
|
|
535
572
|
}
|
|
536
573
|
else if (x.val !== undefined) {
|
|
537
574
|
return this.val(x);
|
|
@@ -581,4 +618,5 @@ module.exports = {
|
|
|
581
618
|
funcWithoutParen,
|
|
582
619
|
getSqlSnippets,
|
|
583
620
|
withoutCast,
|
|
621
|
+
getDefaultTypeLengths,
|
|
584
622
|
};
|
|
@@ -24,7 +24,7 @@ class DeltaRenderer {
|
|
|
24
24
|
// TODO: May also include 'RENAME' at a later stage
|
|
25
25
|
const alterEnv = this.scopedFunctions.activateAlterMode(env);
|
|
26
26
|
const elements = Object.entries(elementsObj)
|
|
27
|
-
.map(([ name, elt ]) => this.scopedFunctions.renderElement(
|
|
27
|
+
.map(([ name, elt ]) => this.scopedFunctions.renderElement(name, elt, duplicateChecker, null, alterEnv))
|
|
28
28
|
.filter(s => s !== '');
|
|
29
29
|
|
|
30
30
|
if (elements.length)
|
package/lib/render/utils/sql.js
CHANGED
|
@@ -42,7 +42,7 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
|
|
|
42
42
|
const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
|
|
43
43
|
|
|
44
44
|
// omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
|
|
45
|
-
if (sqlDialect === 'sqlite') {
|
|
45
|
+
if (sqlDialect === 'sqlite' || sqlDialect === 'postgres') {
|
|
46
46
|
if (constraint.onDelete === 'CASCADE' )
|
|
47
47
|
result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
|
|
48
48
|
}
|
|
@@ -57,8 +57,8 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
|
|
|
57
57
|
result += `${indent}${!constraint.validated ? 'NOT ' : ''}VALIDATED\n`;
|
|
58
58
|
result += `${indent}${!constraint.enforced ? 'NOT ' : ''}ENFORCED\n`;
|
|
59
59
|
}
|
|
60
|
-
// for sqlite, the DEFERRABLE keyword is required
|
|
61
|
-
result += `${indent}${sqlDialect === 'sqlite' ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
|
|
60
|
+
// for sqlite and postgreSQL, the DEFERRABLE keyword is required
|
|
61
|
+
result += `${indent}${sqlDialect === 'sqlite' || sqlDialect === 'postgres' ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
|
|
62
62
|
return result;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -22,7 +22,7 @@ function binarycomparison(expression, token, index){
|
|
|
22
22
|
return index + 3;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function
|
|
25
|
+
function between(expression, token, index){
|
|
26
26
|
let start = index-1, end = index+4;
|
|
27
27
|
if(expression[index-1] === 'not'){
|
|
28
28
|
start -= 1;
|
|
@@ -57,7 +57,7 @@ const bracers = {
|
|
|
57
57
|
'>=': binarycomparison,
|
|
58
58
|
'<=': binarycomparison,
|
|
59
59
|
'!=': binarycomparison,
|
|
60
|
-
'between':
|
|
60
|
+
'between': between,
|
|
61
61
|
'like': like
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"root": true,
|
|
3
3
|
"plugins": ["sonarjs", "jsdoc"],
|
|
4
|
-
"extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"
|
|
4
|
+
"extends": ["plugin:jsdoc/recommended", "../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
|
|
5
5
|
"rules": {
|
|
6
6
|
"prefer-const": "error",
|
|
7
7
|
"quotes": ["error", "single", "avoid-escape"],
|
|
@@ -60,7 +60,7 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
60
60
|
!{}.propertyIsEnumerable.call( _parent, _prop ) ||
|
|
61
61
|
(typeof _prop === 'string' && _prop.startsWith('@')) ||
|
|
62
62
|
(options.skipIgnore && node._ignore) ||
|
|
63
|
-
|
|
63
|
+
options.skipStandard?.[_prop]
|
|
64
64
|
)
|
|
65
65
|
return;
|
|
66
66
|
|
|
@@ -137,17 +137,17 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
137
137
|
csnPath.push( _prop );
|
|
138
138
|
_path.forEach( ( s, i ) => {
|
|
139
139
|
if (s && typeof s === 'object') {
|
|
140
|
-
csnPath.push( i );
|
|
141
140
|
if (options.drillRef) {
|
|
142
141
|
standard(_path, i, s);
|
|
143
142
|
}
|
|
144
143
|
else {
|
|
144
|
+
csnPath.push( i );
|
|
145
145
|
if (s.args)
|
|
146
146
|
standard( s, 'args', s.args );
|
|
147
147
|
if (s.where)
|
|
148
148
|
standard( s, 'where', s.where );
|
|
149
|
+
csnPath.pop();
|
|
149
150
|
}
|
|
150
|
-
csnPath.pop();
|
|
151
151
|
}
|
|
152
152
|
} );
|
|
153
153
|
csnPath.pop();
|
|
@@ -118,7 +118,10 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
|
|
|
118
118
|
* Loop over all elements and for all unmanaged associations translate
|
|
119
119
|
* <assoc base>.<managed assoc>.<fk> to <assoc base>.<managed assoc>_<fk>
|
|
120
120
|
*
|
|
121
|
-
* Or in other words: Allow using the foreign keys of managed associations in
|
|
121
|
+
* Or in other words: Allow using the foreign keys of managed associations in
|
|
122
|
+
* on-conditions / calculated elements on-write.
|
|
123
|
+
*
|
|
124
|
+
* Expects that flattening has already been performed.
|
|
122
125
|
*
|
|
123
126
|
* @param {CSN.Artifact} artifact Artifact to check
|
|
124
127
|
* @param {string} artifactName Name of the artifact
|
|
@@ -130,6 +133,7 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
|
|
|
130
133
|
if (ref.length > 1) {
|
|
131
134
|
const { links } = inspectRef(path);
|
|
132
135
|
if (links) {
|
|
136
|
+
let fkAlias = '';
|
|
133
137
|
// eslint-disable-next-line for-direction
|
|
134
138
|
for (let i = links.length - 1; i >= 0; i--) {
|
|
135
139
|
const link = links[i];
|
|
@@ -137,19 +141,25 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
|
|
|
137
141
|
if (link.art && link.art.target && link.art.keys &&
|
|
138
142
|
// Doesn't work when ref-target (filter condition) or similar is used
|
|
139
143
|
!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
const fkRef = ref[i + 1];
|
|
145
|
+
const fkName = (!fkAlias ? fkRef : `${fkRef}${pathDelimiter}${fkAlias}`);
|
|
146
|
+
const fks = link.art.keys.filter(key => key.ref[0] === fkName);
|
|
147
|
+
|
|
148
|
+
if (fks.length >= 1) { // after flattening, at most one FK will remain.
|
|
149
|
+
// `.as` is set for SQL, but not for OData -> fall back to implicit alias
|
|
150
|
+
fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
|
|
151
|
+
const source = findSource(links, i - 1) || artifact;
|
|
152
|
+
const managedAssocStepName = ref[i];
|
|
153
|
+
const newFkName = `${managedAssocStepName}${pathDelimiter}${fkAlias}`;
|
|
154
|
+
if (source?.elements[newFkName])
|
|
155
|
+
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
|
|
151
156
|
}
|
|
152
157
|
}
|
|
158
|
+
else {
|
|
159
|
+
fkAlias = '';
|
|
160
|
+
// Ignore last path step and unmanaged associations.
|
|
161
|
+
// Structures should have been already flattened.
|
|
162
|
+
}
|
|
153
163
|
}
|
|
154
164
|
}
|
|
155
165
|
}
|
|
@@ -160,6 +170,8 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
|
|
|
160
170
|
// The association is an unmanaged one
|
|
161
171
|
if (!elem.keys && elem.target && elem.on)
|
|
162
172
|
applyTransformationsOnNonDictionary(elem, 'on', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
|
|
173
|
+
else if (elem.value?.stored)
|
|
174
|
+
applyTransformationsOnNonDictionary(elem, 'value', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
if (artifact.query || artifact.projection) {
|
|
@@ -59,14 +59,14 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Turn .expand/.inline into normal refs. @cds.persistence.skip .expand with to-many (and all transitive views).
|
|
62
|
-
* For such skipped things, error for usage of assoc pointing to them and
|
|
62
|
+
* For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
|
|
63
63
|
*/
|
|
64
64
|
function rewriteExpandInline() {
|
|
65
65
|
let cleanup = [];
|
|
66
66
|
let _dependents;
|
|
67
67
|
|
|
68
68
|
const entity = findAnEntity();
|
|
69
|
-
const
|
|
69
|
+
const toDummify = [];
|
|
70
70
|
|
|
71
71
|
applyTransformations(csn, {
|
|
72
72
|
columns: (parent, name, columns, path) => {
|
|
@@ -110,7 +110,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
110
110
|
if (options.transformation !== 'odata') {
|
|
111
111
|
applyTransformations(csn, {
|
|
112
112
|
target: (parent, name, target, path) => {
|
|
113
|
-
if (
|
|
113
|
+
if (toDummify.indexOf(target) !== -1) {
|
|
114
114
|
publishing.push({
|
|
115
115
|
parent, name, target, path: [ ...path ],
|
|
116
116
|
});
|
|
@@ -161,7 +161,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
161
161
|
|
|
162
162
|
const pathStep = obj.ref[j].id ? obj.ref[j].id : obj.ref[j];
|
|
163
163
|
const target = art.target ? art.target : pathStep;
|
|
164
|
-
if (
|
|
164
|
+
if (toDummify.indexOf(target) !== -1) {
|
|
165
165
|
error( null, obj.$path, {
|
|
166
166
|
id: pathStep, elemref: obj, name,
|
|
167
167
|
}, 'Unexpected “@cds.persistence.skip” annotation on Association target $(NAME) of $(ID) in path $(ELEMREF) was skipped because of .expand in conjunction with to-many');
|
|
@@ -174,7 +174,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
174
174
|
if (art) {
|
|
175
175
|
const pathStep = obj.ref[obj.ref.length - 1].id ? obj.ref[obj.ref.length - 1].id : obj.ref[obj.ref.length - 1];
|
|
176
176
|
const target = art.target ? art.target : pathStep;
|
|
177
|
-
if (
|
|
177
|
+
if (toDummify.indexOf(target) !== -1)
|
|
178
178
|
kill.push(i);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
@@ -221,7 +221,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
221
221
|
stack.push([ dependent, dependentName ]);
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
|
-
|
|
224
|
+
toDummify.push(n);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -229,7 +229,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
229
229
|
* Replace the artifacts in `toDummify` with simple dummy views as produced by createDummyView.
|
|
230
230
|
*/
|
|
231
231
|
function dummyfy() {
|
|
232
|
-
for (const artifactName of [ ...new Set(
|
|
232
|
+
for (const artifactName of [ ...new Set(toDummify) ])
|
|
233
233
|
csn.definitions[artifactName] = createDummyView(entity);
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -366,7 +366,8 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
366
366
|
}
|
|
367
367
|
else { // preserve stuff like .cast for redirection
|
|
368
368
|
const thing = base[currentAlias[currentAlias.length - 1]];
|
|
369
|
-
|
|
369
|
+
const value = current?._art?.value || thing?.value;
|
|
370
|
+
if (value && !value.stored)
|
|
370
371
|
error('query-unsupported-calc', current.$path || col.$path, { '#': 'inside' });
|
|
371
372
|
expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
|
|
372
373
|
}
|
|
@@ -396,7 +397,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
396
397
|
* With a .cast.on or .on in a .expand/.inline, we need to change the references,
|
|
397
398
|
* since we change the overall scope of things (by "heaving" them up into "normal refs").
|
|
398
399
|
*
|
|
399
|
-
* So anything that does not have a $self/$projection
|
|
400
|
+
* So anything that does not have a $self/$projection infront gets the so-far-traveled alias,
|
|
400
401
|
* since after the transformation it will basically be in "top-level".
|
|
401
402
|
*
|
|
402
403
|
* @param {object} parent
|
|
@@ -529,7 +530,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
529
530
|
if (col.ref && col.$scope !== '$magic') {
|
|
530
531
|
const _art = col._art || inspectRef(path.concat(i)).art;
|
|
531
532
|
if (_art && isStructured(_art))
|
|
532
|
-
newThing.push(...expandRef(_art, col
|
|
533
|
+
newThing.push(...expandRef(_art, col, withAlias));
|
|
533
534
|
|
|
534
535
|
else
|
|
535
536
|
newThing.push(col);
|
|
@@ -552,16 +553,14 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
552
553
|
* Iterative, to not run into stack overflow.
|
|
553
554
|
*
|
|
554
555
|
* @param {CSN.Element} art
|
|
555
|
-
* @param {
|
|
556
|
-
* @param {Array} alias
|
|
557
|
-
* @param {boolean} isKey True if the ref obj has property key: true
|
|
556
|
+
* @param {object} root Column, ref in order by, etc.
|
|
558
557
|
* @param {boolean} withAlias
|
|
559
558
|
* @returns {Array}
|
|
560
559
|
*/
|
|
561
|
-
function expandRef( art,
|
|
560
|
+
function expandRef( art, root, withAlias ) {
|
|
562
561
|
const expanded = [];
|
|
563
562
|
/** @type {Array<[CSN.Element, any[], any[]]>} */
|
|
564
|
-
const stack = [ [ art, ref, [
|
|
563
|
+
const stack = [ [ art, root.ref, [ root.as || implicitAs(root.ref) ] ] ];
|
|
565
564
|
while (stack.length > 0) {
|
|
566
565
|
const [ current, currentRef, currentAlias ] = stack.pop();
|
|
567
566
|
if (isStructured(current)) {
|
|
@@ -569,16 +568,16 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
569
568
|
stack.push([ e, currentRef.concat(n), currentAlias.concat(n) ]);
|
|
570
569
|
}
|
|
571
570
|
else {
|
|
572
|
-
const obj = { ref: currentRef };
|
|
571
|
+
const obj = { ...root, ...{ ref: currentRef } };
|
|
573
572
|
if (withAlias) {
|
|
574
573
|
const newAlias = currentAlias.join(pathDelimiter);
|
|
575
574
|
// if (alias !== undefined) // explicit alias
|
|
576
575
|
obj.as = newAlias;
|
|
577
576
|
// alias was implicit - to later distinguish expanded s -> s.a from explicitly written s.a
|
|
578
|
-
if (
|
|
577
|
+
if (root.as === undefined)
|
|
579
578
|
setProp(obj, '$implicitAlias', true);
|
|
580
579
|
}
|
|
581
|
-
if (
|
|
580
|
+
if (root.key)
|
|
582
581
|
obj.key = true;
|
|
583
582
|
expanded.push(obj);
|
|
584
583
|
}
|
|
@@ -90,38 +90,31 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
|
|
|
90
90
|
return;
|
|
91
91
|
if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
|
|
92
92
|
toFinalBaseType(parent, resolved, true);
|
|
93
|
+
|
|
94
|
+
if (parent.items) // items could have unresolved types
|
|
95
|
+
toFinalBaseType(parent.items, resolved, true);
|
|
96
|
+
|
|
93
97
|
// structured types might not have the child-types replaced.
|
|
94
98
|
// Drill down to ensure this.
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
let nextElements = parent.elements || parent.items?.elements;
|
|
100
|
+
if (nextElements) {
|
|
101
|
+
const stack = [ nextElements ];
|
|
97
102
|
while (stack.length > 0) {
|
|
98
103
|
const elements = stack.pop();
|
|
99
104
|
for (const e of Object.values(elements)) {
|
|
100
105
|
if (e.type && !isBuiltinType(e.type))
|
|
101
106
|
toFinalBaseType(e, resolved, true);
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
stack.push(
|
|
107
|
+
nextElements = e.elements || e.items?.elements;
|
|
108
|
+
if (nextElements)
|
|
109
|
+
stack.push(nextElements);
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
}
|
|
113
|
+
|
|
108
114
|
const directLocalized = parent.localized || false;
|
|
109
115
|
if (!directLocalized && !options.toOdata)
|
|
110
116
|
removeLocalized(parent);
|
|
111
117
|
}
|
|
112
|
-
// HANA/SQLite do not support array-of - turn into CLOB/Text
|
|
113
|
-
if (parent.items && !options.toOdata) {
|
|
114
|
-
parent.type = 'cds.LargeString';
|
|
115
|
-
delete parent.items;
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
// HANA/SQLite do not support array-of - turn into CLOB/Text
|
|
119
|
-
items: (parent) => {
|
|
120
|
-
// OData has no LargeString substitution and doesn't expand types under items
|
|
121
|
-
if (!options.toOdata) {
|
|
122
|
-
parent.type = 'cds.LargeString';
|
|
123
|
-
delete parent.items;
|
|
124
|
-
}
|
|
125
118
|
},
|
|
126
119
|
}, [ (definitions, artifactName, artifact) => {
|
|
127
120
|
// Replace events, actions and functions with simple dummies - they don't have effect on forRelationalDB stuff
|
|
@@ -520,7 +513,7 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, war
|
|
|
520
513
|
setProp(assoc.keys[i], inferredAlias, true);
|
|
521
514
|
if (!(options.toOdata && assoc.keys[i].ref.length === 1))
|
|
522
515
|
// In OData backend there are no aliases assigned when the same as the ref
|
|
523
|
-
// TODO: remove the if after the new flattening in OData has been
|
|
516
|
+
// TODO: remove the if after the new flattening in OData has been completed
|
|
524
517
|
assoc.keys[i].as = assoc.keys[i].ref[assoc.keys[i].ref.length - 1];
|
|
525
518
|
collector.push(assoc.keys[i]);
|
|
526
519
|
}
|
|
@@ -699,13 +692,16 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
699
692
|
}
|
|
700
693
|
else {
|
|
701
694
|
// unwind a derived type chain to a scalar type
|
|
702
|
-
while (finalElement
|
|
695
|
+
while (finalElement?.type && !isBuiltinType(finalElement?.type)) {
|
|
703
696
|
finalTypeName = finalElement.type;
|
|
704
697
|
finalElement = csn.definitions[finalElement.type];
|
|
705
698
|
}
|
|
706
699
|
}
|
|
707
700
|
}
|
|
708
701
|
|
|
702
|
+
if (!finalElement)
|
|
703
|
+
return [];
|
|
704
|
+
|
|
709
705
|
if (finalElement.target && !finalElement.on) {
|
|
710
706
|
const hasKeys = !!finalElement.keys;
|
|
711
707
|
if (!hasKeys) {
|
|
@@ -741,7 +737,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
741
737
|
});
|
|
742
738
|
}
|
|
743
739
|
// we have reached a leaf element, create a foreign key
|
|
744
|
-
else if (finalElement
|
|
740
|
+
else if (finalElement.type == null || isBuiltinType(finalElement.type)) {
|
|
745
741
|
const newFk = Object.create(null);
|
|
746
742
|
for (const prop of [ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type' ]) {
|
|
747
743
|
// copy props from original element to preserve derived types!
|