@sap/cds-compiler 3.5.4 → 3.6.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 +65 -2
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +28 -13
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +37 -40
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +32 -4
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +22 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +31 -22
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +2 -14
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +4 -6
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +13 -7
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +59 -41
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
package/lib/render/toHdbcds.js
CHANGED
|
@@ -47,14 +47,7 @@ function renderStringForHdbcds( str ) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Render the CSN model 'model' to CDS source text.
|
|
51
|
-
* top-level artifact. Return a dictionary of top-level artifacts
|
|
52
|
-
* by their names, like this:
|
|
53
|
-
* { "foo" : "using XY; context foo {...};",
|
|
54
|
-
* "bar::wiz" : "namespace bar::; entity wiz {...};"
|
|
55
|
-
* }
|
|
56
|
-
*
|
|
57
|
-
* FIXME: This comment no longer tells the whole truth
|
|
50
|
+
* Render the CSN model 'model' to CDS source text.
|
|
58
51
|
*
|
|
59
52
|
* @param {CSN.Model} csn HANA transformed CSN
|
|
60
53
|
* @param {CSN.Options} [options] Transformation options
|
|
@@ -141,12 +134,11 @@ function toHdbcdsSource( csn, options ) {
|
|
|
141
134
|
const hdbconstraint = Object.create(null);
|
|
142
135
|
forEachDefinition(csn, (art) => {
|
|
143
136
|
if (art.$tableConstraints && art.$tableConstraints.referential) {
|
|
144
|
-
const renderToUppercase = plainNames;
|
|
145
137
|
const referentialConstraints = {};
|
|
146
138
|
Object.entries(art.$tableConstraints.referential)
|
|
147
139
|
.forEach(([ fileName, referentialConstraint ]) => {
|
|
148
140
|
referentialConstraints[fileName] = renderReferentialConstraint(
|
|
149
|
-
referentialConstraint, increaseIndent(createEnv()).indent,
|
|
141
|
+
referentialConstraint, increaseIndent(createEnv()).indent, false, csn, options
|
|
150
142
|
);
|
|
151
143
|
});
|
|
152
144
|
Object.entries(referentialConstraints)
|
|
@@ -176,8 +168,8 @@ function toHdbcdsSource( csn, options ) {
|
|
|
176
168
|
function sort( obj ) {
|
|
177
169
|
const keys = Object.keys(obj).sort((a, b) => a.localeCompare(b));
|
|
178
170
|
const sortedResult = Object.create(null);
|
|
179
|
-
for (
|
|
180
|
-
sortedResult[
|
|
171
|
+
for (const key of keys)
|
|
172
|
+
sortedResult[key] = obj[key];
|
|
181
173
|
|
|
182
174
|
return sortedResult;
|
|
183
175
|
}
|
|
@@ -191,7 +183,7 @@ function toHdbcdsSource( csn, options ) {
|
|
|
191
183
|
* @returns {string} The rendered artifact
|
|
192
184
|
*/
|
|
193
185
|
function renderArtifact( artifactName, art, env ) {
|
|
194
|
-
//
|
|
186
|
+
// We're always a top-level artifact.
|
|
195
187
|
env.path = [ 'definitions', artifactName ];
|
|
196
188
|
// Ignore whole artifacts if toHana says so
|
|
197
189
|
if (art.abstract || hasValidSkipOrExists(art))
|
|
@@ -691,8 +683,10 @@ function toHdbcdsSource( csn, options ) {
|
|
|
691
683
|
if (path.ref[0].args)
|
|
692
684
|
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
693
685
|
|
|
694
|
-
if (path.ref[0].where)
|
|
695
|
-
|
|
686
|
+
if (path.ref[0].where) {
|
|
687
|
+
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
688
|
+
result += `[${cardinality}${renderExpr(path.ref[0].where, env)}]`;
|
|
689
|
+
}
|
|
696
690
|
|
|
697
691
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
698
692
|
if (path.ref.length > 1)
|
|
@@ -959,8 +953,10 @@ function toHdbcdsSource( csn, options ) {
|
|
|
959
953
|
if (limit.rows !== undefined)
|
|
960
954
|
result += `limit ${renderExpr(limit.rows, env)}`;
|
|
961
955
|
|
|
962
|
-
if (limit.offset !== undefined)
|
|
963
|
-
|
|
956
|
+
if (limit.offset !== undefined) {
|
|
957
|
+
const indent = result !== '' ? `\n${increaseIndent(env).indent}` : '';
|
|
958
|
+
result += `${indent}offset ${renderExpr(limit.offset, env)}`;
|
|
959
|
+
}
|
|
964
960
|
|
|
965
961
|
return result;
|
|
966
962
|
}
|
|
@@ -1214,7 +1210,8 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1214
1210
|
}
|
|
1215
1211
|
if (s.where) {
|
|
1216
1212
|
// Filter, possibly with cardinality
|
|
1217
|
-
|
|
1213
|
+
const cardinality = s.cardinality ? `${s.cardinality.max}: ` : '';
|
|
1214
|
+
result += `[${cardinality}${renderExpr(s.where, env)}]`;
|
|
1218
1215
|
}
|
|
1219
1216
|
return result;
|
|
1220
1217
|
}
|
|
@@ -1371,7 +1368,8 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1371
1368
|
* @returns {string} Rendered foreign key
|
|
1372
1369
|
*/
|
|
1373
1370
|
function renderForeignKey( fKey, env ) {
|
|
1374
|
-
|
|
1371
|
+
const alias = fKey.as ? (` as ${formatIdentifier(fKey.as)}`) : '';
|
|
1372
|
+
return `${renderExpr(fKey, env)}${alias}`;
|
|
1375
1373
|
}
|
|
1376
1374
|
|
|
1377
1375
|
/**
|
|
@@ -1585,7 +1583,7 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1585
1583
|
return '';
|
|
1586
1584
|
}
|
|
1587
1585
|
// The top-level artifact's parent would be the namespace (if any)
|
|
1588
|
-
const namespace = getNamespace(csn, topLevelName)
|
|
1586
|
+
const namespace = getNamespace(csn, topLevelName);
|
|
1589
1587
|
if (namespace)
|
|
1590
1588
|
return `${env.indent}namespace ${quotePathString(namespace)};\n`;
|
|
1591
1589
|
|
|
@@ -1720,8 +1718,10 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1720
1718
|
|
|
1721
1719
|
if (hdbcdsNames) {
|
|
1722
1720
|
const namespace = getNamespace(csn, absName);
|
|
1723
|
-
if (namespace)
|
|
1724
|
-
|
|
1721
|
+
if (namespace) {
|
|
1722
|
+
const id = `${namespace}::${resultingName.substring(namespace.length + 2)}`;
|
|
1723
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
1724
|
+
}
|
|
1725
1725
|
}
|
|
1726
1726
|
return `"${resultingName.replace(/"/g, '""')}"`;
|
|
1727
1727
|
}
|
package/lib/render/toRename.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const { makeMessageFunction } = require('../base/messages');
|
|
5
5
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
6
|
-
const {
|
|
6
|
+
const { getNamespace, forEachDefinition } = require('../model/csnUtils');
|
|
7
7
|
const { optionProcessor } = require('../optionProcessor');
|
|
8
8
|
const { isBetaEnabled } = require('../base/model');
|
|
9
9
|
const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
|
|
@@ -34,7 +34,7 @@ function toRename( inputCsn, options ) {
|
|
|
34
34
|
const { error, warning, throwWithError } = makeMessageFunction(inputCsn, options, 'to.rename');
|
|
35
35
|
|
|
36
36
|
// Merge options with defaults.
|
|
37
|
-
options = Object.assign({ sqlMapping: 'hdbcds' }, options);
|
|
37
|
+
options = Object.assign({ sqlMapping: 'hdbcds', sqlDialect: 'hana' }, options);
|
|
38
38
|
|
|
39
39
|
// Verify options
|
|
40
40
|
optionProcessor.verifyOptions(options, 'toRename').forEach(complaint => warning(null, null, `${complaint}`));
|
|
@@ -53,7 +53,6 @@ function toRename( inputCsn, options ) {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
const result = Object.create(null);
|
|
56
|
-
const { getNamespaceOfArtifact } = getUtils(csn, false);
|
|
57
56
|
|
|
58
57
|
// Render each artifact on its own
|
|
59
58
|
for (const artifactName in csn.definitions) {
|
|
@@ -122,7 +121,7 @@ function toRename( inputCsn, options ) {
|
|
|
122
121
|
if (options.sqlMapping !== 'hdbcds')
|
|
123
122
|
return name;
|
|
124
123
|
|
|
125
|
-
const namespaceName =
|
|
124
|
+
const namespaceName = getNamespace(inputCsn, name);
|
|
126
125
|
if (namespaceName)
|
|
127
126
|
return `${namespaceName}::${name.substring(namespaceName.length + 1)}`;
|
|
128
127
|
|
package/lib/render/toSql.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
7
7
|
forEachDefinition, getResultingName, getVariableReplacement,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
|
+
const { forEach, forEachValue, forEachKey } = require('../utils/objectUtils');
|
|
9
10
|
const {
|
|
10
11
|
renderFunc, cdsToSqlTypes, getHanaComment, hasHanaComment,
|
|
11
12
|
getSqlSnippets, createExpressionRenderer, withoutCast,
|
|
@@ -24,7 +25,7 @@ const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
|
|
|
24
25
|
const { smartFuncId } = require('../sql-identifier');
|
|
25
26
|
const { sortCsn } = require('../json/to-csn');
|
|
26
27
|
const { manageConstraints } = require('./manageConstraints');
|
|
27
|
-
const { ModelError } = require('../base/error');
|
|
28
|
+
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -135,7 +136,7 @@ function toSqlDdl( csn, options ) {
|
|
|
135
136
|
|
|
136
137
|
// FIXME: Currently requires 'options.forHana', because it can only render HANA-ish SQL dialect
|
|
137
138
|
if (!options.forHana && !isBetaEnabled(options, 'sqlExtensions'))
|
|
138
|
-
throw new
|
|
139
|
+
throw new CompilerAssertion('toSql can currently only be used with HANA preprocessing');
|
|
139
140
|
|
|
140
141
|
checkCSNVersion(csn, options);
|
|
141
142
|
|
|
@@ -229,7 +230,7 @@ function toSqlDdl( csn, options ) {
|
|
|
229
230
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
230
231
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
231
232
|
}
|
|
232
|
-
else if (!options.testMode) {
|
|
233
|
+
else if (!options.testMode && options.generatedByComment) {
|
|
233
234
|
mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
|
|
234
235
|
}
|
|
235
236
|
}
|
|
@@ -237,12 +238,14 @@ function toSqlDdl( csn, options ) {
|
|
|
237
238
|
delete mainResultObj[hdbKind];
|
|
238
239
|
}
|
|
239
240
|
|
|
240
|
-
// add `ALTER TABLE ADD CONSTRAINT` statements per default for `to.sql` w/ dialect `hana`
|
|
241
|
-
|
|
241
|
+
// add `ALTER TABLE ADD CONSTRAINT` statements per default for `to.sql` w/ dialect `hana` / `postgres`
|
|
242
|
+
// TODO `ALTER TABLE ADD CONSTRAINT` statements also for sqlite constraints
|
|
243
|
+
if (!options.constraintsInCreateTable && options.src === 'sql' && (options.sqlDialect === 'hana' || options.sqlDialect === 'postgres' /* || options.sqlDialect === 'sqlite' */)) {
|
|
242
244
|
const alterStmts = manageConstraints(csn, options);
|
|
243
245
|
|
|
244
|
-
|
|
246
|
+
forEachKey(alterStmts, (constraintName) => {
|
|
245
247
|
sql[constraintName] = `${options.testMode ? '' : sqlVersionLine}${alterStmts[constraintName]}`;
|
|
248
|
+
});
|
|
246
249
|
mainResultObj.sql = sql;
|
|
247
250
|
}
|
|
248
251
|
|
|
@@ -250,7 +253,7 @@ function toSqlDdl( csn, options ) {
|
|
|
250
253
|
mainResultObj.sql = sql;
|
|
251
254
|
|
|
252
255
|
for (const name in deletions)
|
|
253
|
-
deletions[name] = `${options.testMode ? '' : sqlVersionLine}${deletions[name]}`;
|
|
256
|
+
deletions[name] = `${options.testMode || !options.generatedByComment ? '' : sqlVersionLine}${deletions[name]}`;
|
|
254
257
|
|
|
255
258
|
timetrace.stop('SQL rendering');
|
|
256
259
|
return mainResultObj;
|
|
@@ -506,17 +509,16 @@ function toSqlDdl( csn, options ) {
|
|
|
506
509
|
if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
|
|
507
510
|
const renderReferentialConstraintsAsHdbconstraint = options.src === 'hdi';
|
|
508
511
|
const referentialConstraints = {};
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
});
|
|
512
|
+
forEach(art.$tableConstraints.referential, ( fileName, referentialConstraint ) => {
|
|
513
|
+
referentialConstraints[fileName] = renderReferentialConstraint(referentialConstraint, childEnv.indent, false, csn, options);
|
|
514
|
+
});
|
|
513
515
|
if (renderReferentialConstraintsAsHdbconstraint) {
|
|
514
|
-
|
|
516
|
+
forEach(referentialConstraints, (fileName, constraint ) => {
|
|
515
517
|
resultObj.hdbconstraint[fileName] = constraint;
|
|
516
518
|
});
|
|
517
519
|
}
|
|
518
520
|
else {
|
|
519
|
-
|
|
521
|
+
forEachValue(referentialConstraints, (constraint) => {
|
|
520
522
|
result += `,\n${constraint}`;
|
|
521
523
|
});
|
|
522
524
|
}
|
|
@@ -527,12 +529,14 @@ function toSqlDdl( csn, options ) {
|
|
|
527
529
|
const uniqueConstraints = art.$tableConstraints && art.$tableConstraints.unique;
|
|
528
530
|
for (const cn in uniqueConstraints) {
|
|
529
531
|
const c = uniqueConstraints[cn];
|
|
532
|
+
const cnName = renderArtifactName(`${artifactName}_${cn}`);
|
|
533
|
+
const refs = c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ');
|
|
530
534
|
if (options.src === 'hdi') {
|
|
531
535
|
resultObj.hdbindex[`${artifactName}.${cn}`]
|
|
532
|
-
= `UNIQUE INVERTED INDEX ${
|
|
536
|
+
= `UNIQUE INVERTED INDEX ${cnName} ON ${tableName} (${refs})`;
|
|
533
537
|
}
|
|
534
538
|
else {
|
|
535
|
-
result += `,\n${childEnv.indent}CONSTRAINT ${
|
|
539
|
+
result += `,\n${childEnv.indent}CONSTRAINT ${cnName} UNIQUE (${refs})`;
|
|
536
540
|
}
|
|
537
541
|
}
|
|
538
542
|
result += `${env.indent}\n)`;
|
|
@@ -962,8 +966,10 @@ function toSqlDdl( csn, options ) {
|
|
|
962
966
|
// CV without parameters is called as simple view
|
|
963
967
|
result += '()';
|
|
964
968
|
}
|
|
965
|
-
if (path.ref[0].where)
|
|
966
|
-
|
|
969
|
+
if (path.ref[0].where) {
|
|
970
|
+
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
971
|
+
result += `[${cardinality}${renderExpr(path.ref[0].where, env)}]`;
|
|
972
|
+
}
|
|
967
973
|
|
|
968
974
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
969
975
|
if (path.ref.length > 1)
|
|
@@ -1193,10 +1199,10 @@ function toSqlDdl( csn, options ) {
|
|
|
1193
1199
|
}
|
|
1194
1200
|
|
|
1195
1201
|
/**
|
|
1196
|
-
* Render a query's LIMIT clause, which may
|
|
1202
|
+
* Render a query's LIMIT clause, which may also have OFFSET.
|
|
1197
1203
|
*
|
|
1198
1204
|
* @param {CSN.QueryLimit} limit Limit clause
|
|
1199
|
-
* @param {object} env
|
|
1205
|
+
* @param {object} env Render environment
|
|
1200
1206
|
* @returns {string} Rendered LIMIT clause
|
|
1201
1207
|
*/
|
|
1202
1208
|
function renderLimit( limit, env ) {
|
|
@@ -1204,8 +1210,10 @@ function toSqlDdl( csn, options ) {
|
|
|
1204
1210
|
if (limit.rows !== undefined)
|
|
1205
1211
|
result += `LIMIT ${renderExpr(limit.rows, env)}`;
|
|
1206
1212
|
|
|
1207
|
-
if (limit.offset !== undefined)
|
|
1208
|
-
|
|
1213
|
+
if (limit.offset !== undefined) {
|
|
1214
|
+
const indent = result !== '' ? `\n${env.indent}` : '';
|
|
1215
|
+
result += `${indent}OFFSET ${renderExpr(limit.offset, env)}`;
|
|
1216
|
+
}
|
|
1209
1217
|
|
|
1210
1218
|
return result;
|
|
1211
1219
|
}
|
|
@@ -1547,7 +1555,8 @@ function toSqlDdl( csn, options ) {
|
|
|
1547
1555
|
if (s.where) {
|
|
1548
1556
|
// Filter, possibly with cardinality
|
|
1549
1557
|
// FIXME: Does SQL understand filter cardinalities?
|
|
1550
|
-
|
|
1558
|
+
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1559
|
+
result += `[${cardinality}${renderExpr(s.where, env)}]`;
|
|
1551
1560
|
}
|
|
1552
1561
|
return result;
|
|
1553
1562
|
}
|
|
@@ -162,7 +162,9 @@ class DeltaRendererPostgres extends DeltaRenderer {
|
|
|
162
162
|
* @todo tableName is escaped - we cannot simply add _pkey
|
|
163
163
|
*/
|
|
164
164
|
dropKey(artifactName) {
|
|
165
|
-
|
|
165
|
+
const table = this.scopedFunctions.renderArtifactName(artifactName);
|
|
166
|
+
const pkey = this.scopedFunctions.renderArtifactName(`${artifactName}_pkey`);
|
|
167
|
+
return [ `ALTER TABLE ${table} DROP CONSTRAINT ${pkey};` ];
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
/**
|
package/lib/render/utils/sql.js
CHANGED
|
@@ -19,18 +19,7 @@ const { ModelError } = require('../../base/error');
|
|
|
19
19
|
* @returns {string} SQL statement which can be used to create the referential constraint on the db.
|
|
20
20
|
*/
|
|
21
21
|
function renderReferentialConstraint( constraint, indent, toUpperCase, csn, options, alterConstraint = false ) {
|
|
22
|
-
|
|
23
|
-
// for to.hana we can't utilize the sql identifier utils
|
|
24
|
-
if (options.transformation === 'hdbcds') {
|
|
25
|
-
quoteId = (id) => {
|
|
26
|
-
if (options.sqlMapping === 'plain')
|
|
27
|
-
return id.replace(/\./g, '_');
|
|
28
|
-
return `"${id}"`;
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
quoteId = getIdentifierUtils(csn, options).quoteSqlId;
|
|
33
|
-
}
|
|
22
|
+
const quoteId = getIdentifierUtils(csn, options).quoteSqlId;
|
|
34
23
|
if (toUpperCase) {
|
|
35
24
|
constraint.identifier = constraint.identifier.toUpperCase();
|
|
36
25
|
constraint.foreignKey = constraint.foreignKey.map(fk => fk.toUpperCase());
|
|
@@ -40,8 +29,7 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
|
|
|
40
29
|
}
|
|
41
30
|
|
|
42
31
|
const renderAsHdbconstraint = options.transformation === 'hdbcds' ||
|
|
43
|
-
options.src === 'hdi'
|
|
44
|
-
(options.manageConstraints && options.manageConstraints.src === 'hdi');
|
|
32
|
+
options.src === 'hdi';
|
|
45
33
|
|
|
46
34
|
const { sqlMapping, sqlDialect } = options;
|
|
47
35
|
let result = '';
|
|
@@ -3,22 +3,21 @@
|
|
|
3
3
|
const {
|
|
4
4
|
applyTransformationsOnNonDictionary,
|
|
5
5
|
applyTransformations,
|
|
6
|
-
getUtils,
|
|
7
6
|
} = require('../../model/csnUtils');
|
|
8
7
|
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* In all .elements of entities and views (and their bound actions/functions), create the on-condition for
|
|
12
10
|
* a managed associations. This needs to happen after the .keys are expanded and the corresponding elements are created.
|
|
13
11
|
*
|
|
14
12
|
* @param {CSN.Model} csn
|
|
13
|
+
* @param {object} csnUtils
|
|
15
14
|
* @param {string} pathDelimiter
|
|
16
15
|
* @returns {CSN.Model} Return the input csn, with the transformations applied
|
|
17
16
|
*/
|
|
18
|
-
function attachOnConditions( csn, pathDelimiter ) {
|
|
17
|
+
function attachOnConditions( csn, csnUtils, pathDelimiter ) {
|
|
19
18
|
const {
|
|
20
19
|
isManagedAssociation,
|
|
21
|
-
} =
|
|
20
|
+
} = csnUtils;
|
|
22
21
|
|
|
23
22
|
const alreadyHandled = new WeakMap();
|
|
24
23
|
applyTransformations(csn, {
|
|
@@ -104,13 +103,14 @@ function attachOnConditions( csn, pathDelimiter ) {
|
|
|
104
103
|
|
|
105
104
|
/**
|
|
106
105
|
* @param {CSN.Model} csn
|
|
106
|
+
* @param {object} csnUtils
|
|
107
107
|
* @param {string} pathDelimiter
|
|
108
108
|
* @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition
|
|
109
109
|
*/
|
|
110
|
-
function getFKAccessFinalizer( csn, pathDelimiter ) {
|
|
110
|
+
function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
|
|
111
111
|
const {
|
|
112
112
|
inspectRef,
|
|
113
|
-
} =
|
|
113
|
+
} = csnUtils;
|
|
114
114
|
|
|
115
115
|
return handleManagedAssocSteps;
|
|
116
116
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
forEachGeneric, forEachMemberRecursively, hasAnnotationValue, isPersistedOnDatabase,
|
|
5
|
-
getUtils,
|
|
6
5
|
} = require('../../model/csnUtils');
|
|
7
6
|
const transformUtils = require('../transformUtilsNew');
|
|
8
7
|
|
|
@@ -37,13 +36,14 @@ function getAnnoProcessor() {
|
|
|
37
36
|
* @param {CSN.Options} options
|
|
38
37
|
* @param {object} messageFunctions
|
|
39
38
|
* @param {Function} messageFunctions.info
|
|
39
|
+
* @param {object} csnUtils
|
|
40
40
|
* @returns {(artifact: CSN.Artifact, artifactName: string, prop: string, path: CSN.Path) => void} Callback function for forEachDefinition
|
|
41
41
|
*/
|
|
42
|
-
function getAssocToSkippedIgnorer( csn, options, messageFunctions ) {
|
|
42
|
+
function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
|
|
43
43
|
const { info } = messageFunctions;
|
|
44
44
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
45
45
|
|
|
46
|
-
const { isAssocOrComposition } =
|
|
46
|
+
const { isAssocOrComposition } = csnUtils;
|
|
47
47
|
|
|
48
48
|
return ignoreAssociationToSkippedTarget;
|
|
49
49
|
/**
|
|
@@ -4,6 +4,7 @@ const { forEachDefinition } = require('../../base/model');
|
|
|
4
4
|
const { applyTransformations, hasAnnotationValue, getResultingName } = require('../../model/csnUtils');
|
|
5
5
|
const { csnRefs } = require('../../model/csnRefs');
|
|
6
6
|
const { forEach, forEachKey } = require('../../utils/objectUtils');
|
|
7
|
+
const { CompilerAssertion } = require('../../base/error');
|
|
7
8
|
|
|
8
9
|
const COMPOSITION = 'cds.Composition';
|
|
9
10
|
const ASSOCIATION = 'cds.Association';
|
|
@@ -90,7 +91,7 @@ function createReferentialConstraints( csn, options ) {
|
|
|
90
91
|
foreignKeyConstraintForAssociation(up_, [ 'definitions', composition.target, 'elements', upLinkName ], path[path.length - 1]);
|
|
91
92
|
}
|
|
92
93
|
else if (!onCondition && composition.keys.length > 0) {
|
|
93
|
-
throw new
|
|
94
|
+
throw new CompilerAssertion('Please debug me, an on-condition was expected here, but only found keys');
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -120,7 +121,7 @@ function createReferentialConstraints( csn, options ) {
|
|
|
120
121
|
attachConstraintsToDependentKeys(dependentKeys, parentKeys, association.target, path[path.length - 1], upLinkFor);
|
|
121
122
|
}
|
|
122
123
|
else if (!onCondition && association.keys.length > 0) {
|
|
123
|
-
throw new
|
|
124
|
+
throw new CompilerAssertion('Please debug me, an on-condition was expected here, but only found keys');
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
|
|
@@ -377,12 +378,9 @@ function createReferentialConstraints( csn, options ) {
|
|
|
377
378
|
* @returns {boolean}
|
|
378
379
|
*/
|
|
379
380
|
function assertForIntegrityTypeDB() {
|
|
380
|
-
|
|
381
|
-
return true;
|
|
382
|
-
return false;
|
|
381
|
+
return isAssertIntegrityAnnotationSetTo(RT);
|
|
383
382
|
}
|
|
384
383
|
|
|
385
|
-
|
|
386
384
|
/**
|
|
387
385
|
* convenience to check if value of element's @assert.integrity annotation
|
|
388
386
|
* is the same as a given value
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
hasAnnotationValue,
|
|
4
|
+
hasAnnotationValue,
|
|
5
5
|
applyTransformations,
|
|
6
6
|
setDependencies,
|
|
7
7
|
walkCsnPath,
|
|
@@ -21,10 +21,10 @@ const { forEach } = require('../../utils/objectUtils');
|
|
|
21
21
|
* @param {Function} messageFunctions.error
|
|
22
22
|
* @param {Function} messageFunctions.info
|
|
23
23
|
* @param {Function} messageFunctions.throwWithAnyError
|
|
24
|
-
* @param {object}
|
|
24
|
+
* @param {object} csnUtils
|
|
25
|
+
* @param {object} [iterateOptions]
|
|
25
26
|
*/
|
|
26
|
-
function expandStructureReferences( csn, options, pathDelimiter, { error, info, throwWithAnyError }, iterateOptions = {} ) {
|
|
27
|
-
const csnUtils = getUtils(csn);
|
|
27
|
+
function expandStructureReferences( csn, options, pathDelimiter, { error, info, throwWithAnyError }, csnUtils, iterateOptions = {} ) {
|
|
28
28
|
const {
|
|
29
29
|
isStructured, get$combined, getFinalBaseTypeWithProps,
|
|
30
30
|
} = csnUtils;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
getUtils,
|
|
5
4
|
applyTransformations, applyTransformationsOnNonDictionary,
|
|
6
5
|
isBuiltinType, cloneCsnNonDict,
|
|
7
6
|
copyAnnotations, implicitAs, isDeepEqual,
|
|
@@ -409,10 +408,11 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
409
408
|
* @param {Function} error
|
|
410
409
|
* @param {string} pathDelimiter
|
|
411
410
|
* @param {boolean} flattenKeyRefs
|
|
411
|
+
* @param {object} csnUtils
|
|
412
412
|
* @param {object} iterateOptions
|
|
413
413
|
*/
|
|
414
|
-
function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, pathDelimiter, flattenKeyRefs, iterateOptions = {} ) {
|
|
415
|
-
const { isManagedAssociation, inspectRef, isStructured } =
|
|
414
|
+
function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
|
|
415
|
+
const { isManagedAssociation, inspectRef, isStructured } = csnUtils;
|
|
416
416
|
const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
417
417
|
if (flattenKeyRefs) {
|
|
418
418
|
applyTransformations(csn, {
|
|
@@ -595,7 +595,7 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, pat
|
|
|
595
595
|
Object.entries(dict).forEach(([ elementName, element ]) => {
|
|
596
596
|
orderedElements.push([ elementName, element ]);
|
|
597
597
|
const eltPath = path.concat(prop, elementName);
|
|
598
|
-
const fks = createForeignKeys(eltPath, element, elementName, csn, options, pathDelimiter);
|
|
598
|
+
const fks = createForeignKeys(csnUtils, eltPath, element, elementName, csn, options, pathDelimiter);
|
|
599
599
|
|
|
600
600
|
// finalize the generated foreign keys
|
|
601
601
|
const refCount = fks.reduce((acc, fk) => {
|
|
@@ -662,6 +662,7 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, pat
|
|
|
662
662
|
*
|
|
663
663
|
* If element is not a managed association, an empty array is returned
|
|
664
664
|
*
|
|
665
|
+
* @param {object} csnUtils
|
|
665
666
|
* @param {Array|object} path CSN path pointing to element or the result of a previous call to inspectRef
|
|
666
667
|
* @param {CSN.Element} element
|
|
667
668
|
* @param {string} prefix Element name
|
|
@@ -671,12 +672,8 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, pat
|
|
|
671
672
|
* @param {number} lvl
|
|
672
673
|
* @returns {Array[]} First element of every sub-array is the foreign key name, second is the foreign key definition
|
|
673
674
|
*/
|
|
674
|
-
function createForeignKeys( path, element, prefix, csn, options, pathDelimiter, lvl = 0 ) {
|
|
675
|
-
const
|
|
676
|
-
effectiveType,
|
|
677
|
-
inspectRef,
|
|
678
|
-
} = getUtils(csn);
|
|
679
|
-
|
|
675
|
+
function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathDelimiter, lvl = 0 ) {
|
|
676
|
+
const special$self = !csn?.definitions?.$self && '$self';
|
|
680
677
|
const isInspectRefResult = !Array.isArray(path);
|
|
681
678
|
|
|
682
679
|
let fks = [];
|
|
@@ -686,8 +683,8 @@ function createForeignKeys( path, element, prefix, csn, options, pathDelimiter,
|
|
|
686
683
|
let finalElement = element;
|
|
687
684
|
let finalTypeName; // TODO: Find a way to not rely on $path?
|
|
688
685
|
// TODO: effectiveType's return value is 'path' for the next inspectRef
|
|
689
|
-
if (element.type && !isBuiltinType(element.type)) {
|
|
690
|
-
const tmpElt = effectiveType(element);
|
|
686
|
+
if (element.type && !isBuiltinType(element.type) && element.type !== special$self) {
|
|
687
|
+
const tmpElt = csnUtils.effectiveType(element);
|
|
691
688
|
// effective type resolves to structs and enums only but not scalars
|
|
692
689
|
if (Object.keys(tmpElt).length) {
|
|
693
690
|
finalElement = tmpElt;
|
|
@@ -717,8 +714,8 @@ function createForeignKeys( path, element, prefix, csn, options, pathDelimiter,
|
|
|
717
714
|
finalElement.keys.forEach((key, keyIndex) => {
|
|
718
715
|
const continuePath = getContinuePath([ 'keys', keyIndex ]);
|
|
719
716
|
const alias = key.as || implicitAs(key.ref);
|
|
720
|
-
const result = inspectRef(continuePath);
|
|
721
|
-
fks = fks.concat(createForeignKeys(result, result.art, alias, csn, options, pathDelimiter, lvl + 1));
|
|
717
|
+
const result = csnUtils.inspectRef(continuePath);
|
|
718
|
+
fks = fks.concat(createForeignKeys(csnUtils, result, result.art, alias, csn, options, pathDelimiter, lvl + 1));
|
|
722
719
|
});
|
|
723
720
|
if (!hasKeys)
|
|
724
721
|
delete finalElement.keys;
|
|
@@ -742,7 +739,7 @@ function createForeignKeys( path, element, prefix, csn, options, pathDelimiter,
|
|
|
742
739
|
// Skip already produced foreign keys
|
|
743
740
|
if (!elem['@odata.foreignKey4']) {
|
|
744
741
|
const continuePath = getContinuePath([ 'elements', elemName ]);
|
|
745
|
-
fks = fks.concat(createForeignKeys(continuePath, elem, elemName, csn, options, pathDelimiter, lvl + 1));
|
|
742
|
+
fks = fks.concat(createForeignKeys(csnUtils, continuePath, elem, elemName, csn, options, pathDelimiter, lvl + 1));
|
|
746
743
|
}
|
|
747
744
|
});
|
|
748
745
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
|
|
4
|
+
getNormalizedQuery, hasAnnotationValue, forEachMember,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
const { implicitAs } = require('../../model/csnRefs');
|
|
7
7
|
const { setProp } = require('../../base/model');
|
|
@@ -20,11 +20,12 @@ const validFromString = '@cds.valid.from';
|
|
|
20
20
|
* @param {CSN.Model} csn
|
|
21
21
|
* @param {object} messageFunctions
|
|
22
22
|
* @param {Function} messageFunctions.info
|
|
23
|
+
* @param {object} csnUtils
|
|
23
24
|
* @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition applying the where-condition to views.
|
|
24
25
|
*/
|
|
25
|
-
function getViewDecorator( csn, messageFunctions ) {
|
|
26
|
+
function getViewDecorator( csn, messageFunctions, csnUtils ) {
|
|
26
27
|
const { info } = messageFunctions;
|
|
27
|
-
const { get$combined } =
|
|
28
|
+
const { get$combined } = csnUtils;
|
|
28
29
|
return addTemporalWhereConditionToView;
|
|
29
30
|
/**
|
|
30
31
|
* Add a where condition to views that
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { forAllQueries, forEachDefinition, walkCsnPath } = require('../../model/csnUtils');
|
|
4
4
|
const { setProp } = require('../../base/model');
|
|
5
5
|
const { getRealName } = require('../../render/utils/common');
|
|
6
|
-
const { csnRefs } = require('../../model/csnRefs');
|
|
7
6
|
const { ModelError } = require('../../base/error');
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -47,9 +46,11 @@ const { ModelError } = require('../../base/error');
|
|
|
47
46
|
* @param {CSN.Model} csn
|
|
48
47
|
* @param {CSN.Options} options
|
|
49
48
|
* @param {Function} error
|
|
49
|
+
* @param {Function} inspectRef
|
|
50
|
+
* @param {Function} initDefinition
|
|
51
|
+
* @param {Function} dropDefinitionCache
|
|
50
52
|
*/
|
|
51
|
-
function handleExists( csn, options, error ) {
|
|
52
|
-
let { inspectRef } = csnRefs(csn);
|
|
53
|
+
function handleExists( csn, options, error, inspectRef, initDefinition, dropDefinitionCache ) {
|
|
53
54
|
const generatedExists = new WeakMap();
|
|
54
55
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
55
56
|
if (artifact.projection) // do the same hack we do for the other stuff...
|
|
@@ -78,14 +79,19 @@ function handleExists( csn, options, error ) {
|
|
|
78
79
|
|
|
79
80
|
while (toProcess.length > 0) {
|
|
80
81
|
const [ queryPath, exprPath ] = toProcess.pop();
|
|
82
|
+
// Re-init caches for this artifact
|
|
83
|
+
dropDefinitionCache(artifact);
|
|
84
|
+
initDefinition(artifact);
|
|
81
85
|
// leftovers can happen with nested exists - we then need to drill down into the created SELECT
|
|
82
86
|
// to check for further exists
|
|
83
87
|
const { result, leftovers } = processExists(queryPath, exprPath);
|
|
84
88
|
walkCsnPath(csn, exprPath.slice(0, -1))[exprPath[exprPath.length - 1]] = result;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
toProcess.push(...leftovers.reverse()); // any leftovers - schedule for further processing
|
|
89
|
+
leftovers.reverse();
|
|
90
|
+
toProcess.push(...leftovers); // any leftovers - schedule for further processing
|
|
88
91
|
}
|
|
92
|
+
// Make sure we leave csnRefs usable
|
|
93
|
+
dropDefinitionCache(artifact);
|
|
94
|
+
initDefinition(artifact);
|
|
89
95
|
}
|
|
90
96
|
}, [ 'definitions', artifactName, 'query' ]);
|
|
91
97
|
}
|
|
@@ -763,7 +769,7 @@ function handleExists( csn, options, error ) {
|
|
|
763
769
|
*/
|
|
764
770
|
function remapExistingWhere( target, where ) {
|
|
765
771
|
return where.map((part) => {
|
|
766
|
-
if (part.ref) {
|
|
772
|
+
if (part.ref && part.$scope !== '$magic') {
|
|
767
773
|
part.ref = [ target, ...part.ref ];
|
|
768
774
|
return part;
|
|
769
775
|
}
|