@sap/cds-compiler 2.15.4 → 3.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 +33 -1590
- package/bin/cdsc.js +36 -33
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +220 -103
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +60 -20
- package/lib/base/messages.js +65 -24
- package/lib/base/model.js +44 -2
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +87 -9
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/extend.js +59 -11
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +13 -13
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +193 -218
- package/lib/compiler/shared.js +47 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/csn2edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +25 -30
- package/lib/edm/edmUtils.js +10 -24
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20632 -22313
- package/lib/json/from-csn.js +56 -49
- package/lib/json/to-csn.js +10 -8
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +303 -229
- package/lib/language/language.g4 +573 -629
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +170 -283
- package/lib/model/revealInternalProperties.js +28 -8
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +12 -21
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +19 -15
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +53 -51
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +10 -12
- package/lib/transform/localized.js +22 -16
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +3 -3
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/render/toHdbcds.js
CHANGED
|
@@ -35,6 +35,16 @@ function getEscapedHanaComment(obj) {
|
|
|
35
35
|
return getHanaComment(obj).replace(/\n/g, '\\n').replace(/'/g, "''");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Render a string for HDBCDS, i.e. put it in quotes and escape single quotes.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} str
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function renderStringForHdbcds(str) {
|
|
45
|
+
return `'${str.replace(/'/g, '\'\'')}'`;
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
/**
|
|
39
49
|
* Render the CSN model 'model' to CDS source text. One source is created per
|
|
40
50
|
* top-level artifact. Return a dictionary of top-level artifacts
|
|
@@ -731,7 +741,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
731
741
|
|
|
732
742
|
// Render 'null as <alias>' only for database and if element is virtual
|
|
733
743
|
if (element && element.virtual) {
|
|
734
|
-
if (isDeprecatedEnabled(options, '
|
|
744
|
+
if (isDeprecatedEnabled(options, '_renderVirtualElements'))
|
|
735
745
|
return `${result}${env.indent}null as ${formatIdentifier(leaf)}`;
|
|
736
746
|
}
|
|
737
747
|
else {
|
|
@@ -1223,7 +1233,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1223
1233
|
case 'timestamp':
|
|
1224
1234
|
return `${x.literal}'${x.val}'`;
|
|
1225
1235
|
case 'string':
|
|
1226
|
-
return
|
|
1236
|
+
return renderStringForHdbcds(x.val);
|
|
1227
1237
|
case 'object':
|
|
1228
1238
|
if (x.val === null)
|
|
1229
1239
|
return 'null';
|
|
@@ -1260,22 +1270,16 @@ function toHdbcdsSource(csn, options) {
|
|
|
1260
1270
|
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1261
1271
|
if (x.ref[0] === '$user') {
|
|
1262
1272
|
if (magicReplacement !== null)
|
|
1263
|
-
return
|
|
1264
|
-
|
|
1265
|
-
// Keep old way of solving this to remain backwards compatible
|
|
1266
|
-
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1267
|
-
if (x.ref[1] === 'id') {
|
|
1268
|
-
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
1269
|
-
return `'${options.magicVars.user}'`;
|
|
1273
|
+
return renderStringForHdbcds(magicReplacement);
|
|
1270
1274
|
|
|
1271
|
-
|
|
1272
|
-
return `'${options.magicVars.user.id}'`;
|
|
1275
|
+
// Note: The compiler already transforms $user into $user.id.
|
|
1273
1276
|
|
|
1277
|
+
// FIXME: this is all not enough: we might need an explicit select item alias (?)
|
|
1278
|
+
if (x.ref[1] === 'id')
|
|
1274
1279
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1275
|
-
|
|
1276
|
-
else if (x.ref[1] === 'locale')
|
|
1280
|
+
|
|
1281
|
+
else if (x.ref[1] === 'locale')
|
|
1277
1282
|
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1278
|
-
}
|
|
1279
1283
|
}
|
|
1280
1284
|
else if (x.ref[0] === '$at') {
|
|
1281
1285
|
if (x.ref[1] === 'from')
|
|
@@ -1285,7 +1289,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1285
1289
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1286
1290
|
}
|
|
1287
1291
|
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1288
|
-
return
|
|
1292
|
+
return renderStringForHdbcds(magicReplacement);
|
|
1289
1293
|
}
|
|
1290
1294
|
}
|
|
1291
1295
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref, env, this.inline)).join('.')}`;
|
package/lib/render/toRename.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { makeMessageFunction } = require('../base/messages');
|
|
5
5
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
6
|
-
const { getUtils } = require('../model/csnUtils');
|
|
6
|
+
const { getUtils, forEachDefinition } = require('../model/csnUtils');
|
|
7
|
+
const { optionProcessor } = require('../optionProcessor');
|
|
8
|
+
const { isBetaEnabled } = require('../base/model');
|
|
9
|
+
const { transformForHanaWithCsn } = require('../transform/forHanaNew');
|
|
7
10
|
|
|
8
|
-
// FIXME: This is not up-to-date in regards to the changes to hdbcds/sql quoting etc.
|
|
9
11
|
|
|
10
12
|
/**
|
|
13
|
+
* FIXME: Not yet supported, only in beta mode
|
|
14
|
+
* FIXME: This is not up-to-date in regards to the changes to hdbcds/sql quoting etc.
|
|
11
15
|
*
|
|
12
|
-
*
|
|
16
|
+
* Generate SQL DDL rename statements for a migration, renaming existing tables and their
|
|
13
17
|
* columns so that they match the result of "toHana" or "toSql" with the 'plain' option for names.
|
|
14
18
|
* Expects the naming convention of the existing tables to be either 'quoted' or 'hdbcds' (default).
|
|
15
|
-
* The following options control what is actually generated:
|
|
19
|
+
* The following options control what is actually generated (see help above):
|
|
16
20
|
* options : {
|
|
17
|
-
*
|
|
21
|
+
* sqlMapping : existing names, either 'quoted' or 'hdbcds' (default)
|
|
18
22
|
* }
|
|
19
23
|
* Return a dictionary of top-level artifacts by their names, like this:
|
|
20
24
|
* { "foo" : "RENAME TABLE \"foo\" ...",
|
|
@@ -22,34 +26,52 @@ const { getUtils } = require('../model/csnUtils');
|
|
|
22
26
|
* }
|
|
23
27
|
*
|
|
24
28
|
* @todo clarify input parameters
|
|
25
|
-
* @param {CSN.Model}
|
|
29
|
+
* @param {CSN.Model} inputCsn CSN?
|
|
26
30
|
* @param {CSN.Options} options Transformation options
|
|
27
31
|
* @returns {object} A dictionary of name: rename statement
|
|
28
32
|
*/
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
function toRename(inputCsn, options) {
|
|
34
|
+
const { error, warning, throwWithError } = makeMessageFunction(inputCsn, options, 'to.rename');
|
|
35
|
+
|
|
36
|
+
// Merge options with defaults.
|
|
37
|
+
options = Object.assign({ sqlMapping: 'hdbcds' }, options);
|
|
38
|
+
|
|
39
|
+
// Verify options
|
|
40
|
+
optionProcessor.verifyOptions(options, 'toRename').forEach(complaint => warning(null, null, `${complaint}`));
|
|
41
|
+
checkCSNVersion(inputCsn, options);
|
|
42
|
+
|
|
43
|
+
// Requires beta mode
|
|
44
|
+
if (!isBetaEnabled(options, 'toRename'))
|
|
45
|
+
error(null, null, 'Generation of SQL rename statements is not supported yet (only in beta mode)');
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
// FIXME: Currently, 'toRename' implies transformation for HANA (transferring the options to forHana)
|
|
48
|
+
const csn = transformForHanaWithCsn(inputCsn, options, 'to.rename');
|
|
49
|
+
// forHanaCsn looses empty contexts and services, add them again so that toRename can calculate the namespaces
|
|
50
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
51
|
+
if ((artifact.kind === 'context' || artifact.kind === 'service') && csn.definitions[artifactName] === undefined)
|
|
52
|
+
csn.definitions[artifactName] = artifact;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const result = Object.create(null);
|
|
56
|
+
const { getNamespaceOfArtifact } = getUtils(csn, false);
|
|
34
57
|
|
|
35
|
-
const { getNamespaceOfArtifact } = getUtils(csn);
|
|
36
58
|
// Render each artifact on its own
|
|
37
59
|
for (const artifactName in csn.definitions) {
|
|
38
60
|
const sourceStr = renameTableAndColumns(artifactName, csn.definitions[artifactName]);
|
|
39
|
-
|
|
40
61
|
if (sourceStr !== '')
|
|
41
62
|
result[artifactName] = sourceStr;
|
|
42
63
|
}
|
|
43
|
-
// Throw exception in case of errors
|
|
44
|
-
if (hasErrors(options.messages))
|
|
45
|
-
throw new CompilationError(options.messages);
|
|
46
64
|
|
|
47
|
-
|
|
65
|
+
throwWithError();
|
|
48
66
|
|
|
67
|
+
return {
|
|
68
|
+
rename: result,
|
|
69
|
+
options,
|
|
70
|
+
};
|
|
49
71
|
|
|
50
72
|
/**
|
|
51
73
|
* If 'art' is a non-view entity, generate SQL statements to rename the corresponding
|
|
52
|
-
* table and its columns from the naming conventions given in 'options.
|
|
74
|
+
* table and its columns from the naming conventions given in 'options.sqlMapping'
|
|
53
75
|
* (either 'quoted' or 'hdbcds') to 'plain'. In addition, drop any existing associations
|
|
54
76
|
* from the columns (they would likely become invalid anyway).
|
|
55
77
|
* Do not rename anything if the names are identical.
|
|
@@ -97,7 +119,7 @@ function toRenameDdl(csn, options) {
|
|
|
97
119
|
* @returns {string} Absolute name
|
|
98
120
|
*/
|
|
99
121
|
function absoluteCdsName(name) {
|
|
100
|
-
if (options.
|
|
122
|
+
if (options.sqlMapping !== 'hdbcds')
|
|
101
123
|
return name;
|
|
102
124
|
|
|
103
125
|
const namespaceName = getNamespaceOfArtifact(name);
|
|
@@ -108,14 +130,14 @@ function toRenameDdl(csn, options) {
|
|
|
108
130
|
}
|
|
109
131
|
|
|
110
132
|
/**
|
|
111
|
-
* Return 'name' with appropriate "-quotes, also replacing '::' by '.' if 'options.
|
|
133
|
+
* Return 'name' with appropriate "-quotes, also replacing '::' by '.' if 'options.sqlMapping'
|
|
112
134
|
* is 'quoted'
|
|
113
135
|
*
|
|
114
136
|
* @param {string} name Name to quote
|
|
115
137
|
* @returns {string} Quoted string
|
|
116
138
|
*/
|
|
117
139
|
function quoteSqlId(name) {
|
|
118
|
-
if (options.
|
|
140
|
+
if (options.sqlMapping === 'quoted')
|
|
119
141
|
name = name.replace(/::/g, '.');
|
|
120
142
|
|
|
121
143
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -134,5 +156,5 @@ function toRenameDdl(csn, options) {
|
|
|
134
156
|
}
|
|
135
157
|
|
|
136
158
|
module.exports = {
|
|
137
|
-
|
|
159
|
+
toRename,
|
|
138
160
|
};
|
package/lib/render/toSql.js
CHANGED
|
@@ -97,8 +97,8 @@ function toSqlDdl(csn, options) {
|
|
|
97
97
|
aliasOnly(x, _env) {
|
|
98
98
|
return x.as;
|
|
99
99
|
},
|
|
100
|
-
windowFunction: (x, env) => renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.
|
|
101
|
-
func: (x, env) => renderFunc(smartFuncId(prepareIdentifier(x.func), options.
|
|
100
|
+
windowFunction: (x, env) => renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, env),
|
|
101
|
+
func: (x, env) => renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, options.sqlDialect, a => renderArgs(a, '=>', env, null)),
|
|
102
102
|
xpr(x, env) {
|
|
103
103
|
if (this.nestedExpr && !x.cast)
|
|
104
104
|
return `(${renderExpr(x.xpr, env, this.inline, true)})`;
|
|
@@ -238,7 +238,7 @@ function toSqlDdl(csn, options) {
|
|
|
238
238
|
};
|
|
239
239
|
|
|
240
240
|
// Registries for artifact and element names per CSN section
|
|
241
|
-
const definitionsDuplicateChecker = new DuplicateChecker(options.
|
|
241
|
+
const definitionsDuplicateChecker = new DuplicateChecker(options.sqlMapping);
|
|
242
242
|
const deletionsDuplicateChecker = new DuplicateChecker();
|
|
243
243
|
const extensionsDuplicateChecker = new DuplicateChecker();
|
|
244
244
|
const removeElementsDuplicateChecker = new DuplicateChecker();
|
|
@@ -262,7 +262,7 @@ function toSqlDdl(csn, options) {
|
|
|
262
262
|
// Render each artifact extension
|
|
263
263
|
// Only HANA SQL is currently supported.
|
|
264
264
|
// Note that extensions may contain new elements referenced in migrations, thus should be compiled first.
|
|
265
|
-
if (csn.extensions && (options.
|
|
265
|
+
if (csn.extensions && (options.sqlDialect === 'hana' || isBetaEnabled(options, 'sqlExtensions'))) {
|
|
266
266
|
for (const extension of options && options.testMode ? sortCsn(csn.extensions) : csn.extensions) {
|
|
267
267
|
if (extension.extend) {
|
|
268
268
|
const artifactName = extension.extend;
|
|
@@ -275,7 +275,7 @@ function toSqlDdl(csn, options) {
|
|
|
275
275
|
|
|
276
276
|
// Render each artifact change
|
|
277
277
|
// Only HANA SQL is currently supported.
|
|
278
|
-
if (csn.migrations && (options.
|
|
278
|
+
if (csn.migrations && (options.sqlDialect === 'hana' || isBetaEnabled(options, 'sqlExtensions'))) {
|
|
279
279
|
for (const migration of options && options.testMode ? sortCsn(csn.migrations) : csn.migrations) {
|
|
280
280
|
if (migration.migrate) {
|
|
281
281
|
const artifactName = migration.migrate;
|
|
@@ -294,7 +294,7 @@ function toSqlDdl(csn, options) {
|
|
|
294
294
|
// Throw exception in case of errors
|
|
295
295
|
throwWithAnyError();
|
|
296
296
|
|
|
297
|
-
// Transfer results from hdb-specific dictionaries into 'sql' dictionary in proper order if
|
|
297
|
+
// Transfer results from hdb-specific dictionaries into 'sql' dictionary in proper order if src === 'sql'
|
|
298
298
|
// (relying on the order of dictionaries above)
|
|
299
299
|
// FIXME: Should consider inter-view dependencies, too
|
|
300
300
|
const sql = Object.create(null);
|
|
@@ -305,10 +305,10 @@ function toSqlDdl(csn, options) {
|
|
|
305
305
|
const { deletions, migrations: _, ...hdbKinds } = mainResultObj;
|
|
306
306
|
for (const hdbKind of Object.keys(hdbKinds)) {
|
|
307
307
|
for (const name in mainResultObj[hdbKind]) {
|
|
308
|
-
if (options.
|
|
308
|
+
if (options.src === 'sql') {
|
|
309
309
|
let sourceString = mainResultObj[hdbKind][name];
|
|
310
310
|
// Hack: Other than in 'hdbtable' files, in HANA SQL COLUMN is not mandatory but default.
|
|
311
|
-
if (options.
|
|
311
|
+
if (options.sqlDialect === 'hana' && hdbKind === 'hdbtable' && sourceString.startsWith('COLUMN '))
|
|
312
312
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
313
313
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
314
314
|
}
|
|
@@ -316,7 +316,7 @@ function toSqlDdl(csn, options) {
|
|
|
316
316
|
mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
|
-
if (options.
|
|
319
|
+
if (options.src === 'sql')
|
|
320
320
|
delete mainResultObj[hdbKind];
|
|
321
321
|
}
|
|
322
322
|
|
|
@@ -329,7 +329,7 @@ function toSqlDdl(csn, options) {
|
|
|
329
329
|
mainResultObj.sql = sql;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
if (options.
|
|
332
|
+
if (options.src === 'sql')
|
|
333
333
|
mainResultObj.sql = sql;
|
|
334
334
|
|
|
335
335
|
for (const name in deletions)
|
|
@@ -420,7 +420,7 @@ function toSqlDdl(csn, options) {
|
|
|
420
420
|
* @returns {string} Artifact name
|
|
421
421
|
*/
|
|
422
422
|
function renderArtifactName(artifactName) {
|
|
423
|
-
return quoteSqlId(getResultingName(csn, options.
|
|
423
|
+
return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName));
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
// Render an artifact migration into the appropriate dictionary of 'resultObj'.
|
|
@@ -573,7 +573,7 @@ function toSqlDdl(csn, options) {
|
|
|
573
573
|
const { front, back } = getSqlSnippets(options, art);
|
|
574
574
|
let result = front;
|
|
575
575
|
// Only HANA has row/column tables
|
|
576
|
-
if (options.
|
|
576
|
+
if (options.sqlDialect === 'hana') {
|
|
577
577
|
if (hanaTc && hanaTc.storeType) {
|
|
578
578
|
// Explicitly specified
|
|
579
579
|
result += `${art.technicalConfig.hana.storeType.toUpperCase()} `;
|
|
@@ -604,7 +604,7 @@ function toSqlDdl(csn, options) {
|
|
|
604
604
|
// for `to.sql` w/ dialect `hana` the constraints will be part of the
|
|
605
605
|
const constraintsAsAlter = !options.constraintsInCreateTable && options.src === 'sql' && options.sqlDialect === 'hana';
|
|
606
606
|
if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
|
|
607
|
-
const renderReferentialConstraintsAsHdbconstraint = options.
|
|
607
|
+
const renderReferentialConstraintsAsHdbconstraint = options.src === 'hdi';
|
|
608
608
|
const referentialConstraints = {};
|
|
609
609
|
Object.entries(art.$tableConstraints.referential)
|
|
610
610
|
.forEach(([ fileName, referentialConstraint ]) => {
|
|
@@ -627,7 +627,7 @@ function toSqlDdl(csn, options) {
|
|
|
627
627
|
const uniqueConstraints = art.$tableConstraints && art.$tableConstraints.unique;
|
|
628
628
|
for (const cn in uniqueConstraints) {
|
|
629
629
|
const c = uniqueConstraints[cn];
|
|
630
|
-
if (options.
|
|
630
|
+
if (options.src === 'hdi') {
|
|
631
631
|
resultObj.hdbindex[`${artifactName}.${cn}`]
|
|
632
632
|
= `UNIQUE INVERTED INDEX ${renderArtifactName(`${artifactName}_${cn}`)} ON ${tableName} (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
|
|
633
633
|
}
|
|
@@ -637,20 +637,20 @@ function toSqlDdl(csn, options) {
|
|
|
637
637
|
}
|
|
638
638
|
result += `${env.indent}\n)`;
|
|
639
639
|
|
|
640
|
-
if (options.
|
|
640
|
+
if (options.sqlDialect === 'hana')
|
|
641
641
|
result += renderTechnicalConfiguration(art.technicalConfig, childEnv);
|
|
642
642
|
|
|
643
643
|
|
|
644
644
|
const associations = Object.keys(art.elements).map(name => renderAssociationElement(name, art.elements[name], childEnv))
|
|
645
645
|
.filter(s => s !== '')
|
|
646
646
|
.join(',\n');
|
|
647
|
-
if (associations !== '' && options.
|
|
647
|
+
if (associations !== '' && options.sqlDialect === 'hana') {
|
|
648
648
|
result += `${env.indent} WITH ASSOCIATIONS (\n${associations}\n`;
|
|
649
649
|
result += `${env.indent})`;
|
|
650
650
|
}
|
|
651
651
|
// Only HANA has indices
|
|
652
652
|
// FIXME: Really? We should provide a DB-agnostic way to specify that
|
|
653
|
-
if (options.
|
|
653
|
+
if (options.sqlDialect === 'hana')
|
|
654
654
|
renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
|
|
655
655
|
|
|
656
656
|
if (options.sqlDialect === 'hana' && hasHanaComment(art, options))
|
|
@@ -758,7 +758,7 @@ function toSqlDdl(csn, options) {
|
|
|
758
758
|
result += ` DEFAULT ${renderExpr(elm.default, env)}`;
|
|
759
759
|
|
|
760
760
|
// Only HANA has fuzzy indices
|
|
761
|
-
if (fzindex && options.
|
|
761
|
+
if (fzindex && options.sqlDialect === 'hana')
|
|
762
762
|
result += ` ${renderExpr(fzindex, env)}`;
|
|
763
763
|
|
|
764
764
|
// (table) elements can only have a @sql.append
|
|
@@ -906,7 +906,7 @@ function toSqlDdl(csn, options) {
|
|
|
906
906
|
throw new ModelError(`Unexpected form of index: "${index}"`);
|
|
907
907
|
|
|
908
908
|
let indexName = renderArtifactName(`${artifactName}.${index[i + 1].ref}`);
|
|
909
|
-
if (options.
|
|
909
|
+
if (options.sqlMapping === 'plain')
|
|
910
910
|
indexName = indexName.replace(/(\.|::)/g, '_');
|
|
911
911
|
|
|
912
912
|
const result = index.slice(0, i + 1); // CREATE UNIQUE INDEX
|
|
@@ -945,7 +945,7 @@ function toSqlDdl(csn, options) {
|
|
|
945
945
|
let result = `${renderViewSource(artifactName, source.args[0], env)}`;
|
|
946
946
|
for (let i = 1; i < source.args.length; i++) {
|
|
947
947
|
result = `(${result} ${source.join.toUpperCase()} `;
|
|
948
|
-
if (options.
|
|
948
|
+
if (options.sqlDialect === 'hana')
|
|
949
949
|
result += renderJoinCardinality(source.cardinality);
|
|
950
950
|
result += `JOIN ${renderViewSource(artifactName, source.args[i], env)}`;
|
|
951
951
|
if (source.on)
|
|
@@ -1008,7 +1008,7 @@ function toSqlDdl(csn, options) {
|
|
|
1008
1008
|
let result = renderAbsolutePath(path, ':', env);
|
|
1009
1009
|
|
|
1010
1010
|
// Take care of aliases
|
|
1011
|
-
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.
|
|
1011
|
+
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
|
|
1012
1012
|
if (path.as) {
|
|
1013
1013
|
// Source had an alias - render it
|
|
1014
1014
|
result += ` AS ${quoteSqlId(path.as)}`;
|
|
@@ -1126,7 +1126,7 @@ function toSqlDdl(csn, options) {
|
|
|
1126
1126
|
let result = '';
|
|
1127
1127
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1] || col.func;
|
|
1128
1128
|
if (leaf && elements[leaf] && elements[leaf].virtual) {
|
|
1129
|
-
if (isDeprecatedEnabled(options, '
|
|
1129
|
+
if (isDeprecatedEnabled(options, '_renderVirtualElements'))
|
|
1130
1130
|
// render a virtual column 'null as <alias>'
|
|
1131
1131
|
result += `${env.indent}NULL AS ${quoteSqlId(col.as || leaf)}`;
|
|
1132
1132
|
}
|
|
@@ -1165,7 +1165,7 @@ function toSqlDdl(csn, options) {
|
|
|
1165
1165
|
.map(name => renderAssociationElement(name, art.elements[name], childEnv))
|
|
1166
1166
|
.filter(s => s !== '')
|
|
1167
1167
|
.join(',\n');
|
|
1168
|
-
if (associations !== '' && options.
|
|
1168
|
+
if (associations !== '' && options.sqlDialect === 'hana') {
|
|
1169
1169
|
result += `${env.indent}\nWITH ASSOCIATIONS (\n${associations}\n`;
|
|
1170
1170
|
result += `${env.indent})`;
|
|
1171
1171
|
}
|
|
@@ -1197,7 +1197,7 @@ function toSqlDdl(csn, options) {
|
|
|
1197
1197
|
// this would be an incompatible change, as non-uppercased, quoted identifiers
|
|
1198
1198
|
// are rejected by the HANA compiler.
|
|
1199
1199
|
let pIdentifier;
|
|
1200
|
-
if (options.
|
|
1200
|
+
if (options.sqlMapping === 'quoted' || options.sqlMapping === 'hdbcds')
|
|
1201
1201
|
pIdentifier = prepareIdentifier(pn);
|
|
1202
1202
|
else
|
|
1203
1203
|
pIdentifier = quoteSqlId(pn);
|
|
@@ -1386,7 +1386,7 @@ function toSqlDdl(csn, options) {
|
|
|
1386
1386
|
'cds.LocalTime': 'cds.Time',
|
|
1387
1387
|
};
|
|
1388
1388
|
const tName = forHanaRenamesToEarly[typeName] || typeName;
|
|
1389
|
-
const types = cdsToSqlTypes[options.
|
|
1389
|
+
const types = cdsToSqlTypes[options.sqlDialect];
|
|
1390
1390
|
return types && types[tName] || cdsToSqlTypes.standard[tName] || 'CHAR';
|
|
1391
1391
|
}
|
|
1392
1392
|
|
|
@@ -1426,7 +1426,7 @@ function toSqlDdl(csn, options) {
|
|
|
1426
1426
|
|
|
1427
1427
|
if (elm.srid !== undefined) {
|
|
1428
1428
|
// SAP HANA Geometry types translate into CHAR in plain/sqlite (give them the default length of 2000)
|
|
1429
|
-
if (options.
|
|
1429
|
+
if (options.sqlDialect !== 'hana')
|
|
1430
1430
|
params.push(2000);
|
|
1431
1431
|
else
|
|
1432
1432
|
params.push(elm.srid);
|
|
@@ -1448,7 +1448,7 @@ function toSqlDdl(csn, options) {
|
|
|
1448
1448
|
case 'date':
|
|
1449
1449
|
case 'time':
|
|
1450
1450
|
case 'timestamp':
|
|
1451
|
-
if (options.
|
|
1451
|
+
if (options.sqlDialect === 'sqlite') {
|
|
1452
1452
|
// simple string literal '2017-11-02'
|
|
1453
1453
|
return `'${x.val}'`;
|
|
1454
1454
|
}
|
|
@@ -1474,7 +1474,7 @@ function toSqlDdl(csn, options) {
|
|
|
1474
1474
|
|
|
1475
1475
|
if (x.ref[0] === '$user') {
|
|
1476
1476
|
if (magicReplacement !== null)
|
|
1477
|
-
return
|
|
1477
|
+
return renderStringForSql(magicReplacement, options.sqlDialect);
|
|
1478
1478
|
|
|
1479
1479
|
const result = render$user();
|
|
1480
1480
|
// Invalid second path step doesn't cause a return
|
|
@@ -1488,7 +1488,18 @@ function toSqlDdl(csn, options) {
|
|
|
1488
1488
|
return result;
|
|
1489
1489
|
}
|
|
1490
1490
|
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1491
|
-
return
|
|
1491
|
+
return renderStringForSql(magicReplacement, options.sqlDialect);
|
|
1492
|
+
}
|
|
1493
|
+
else if (x.ref[0] === '$now') { // TODO: Can there be cases where $now is followed by something?
|
|
1494
|
+
switch (options.sqlDialect) {
|
|
1495
|
+
case 'sqlite':
|
|
1496
|
+
case 'hana':
|
|
1497
|
+
return 'CURRENT_TIMESTAMP';
|
|
1498
|
+
case 'postgres':
|
|
1499
|
+
return 'current_timestamp';
|
|
1500
|
+
default:
|
|
1501
|
+
return quoteSqlId(x.ref[0]);
|
|
1502
|
+
}
|
|
1492
1503
|
}
|
|
1493
1504
|
}
|
|
1494
1505
|
// FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
|
|
@@ -1508,27 +1519,17 @@ function toSqlDdl(csn, options) {
|
|
|
1508
1519
|
* @returns {string|null} Null in case of an invalid second path step
|
|
1509
1520
|
*/
|
|
1510
1521
|
function render$user() {
|
|
1511
|
-
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1522
|
+
// FIXME: this is all not enough: we might need an explicit select item alias (?)
|
|
1512
1523
|
if (x.ref[1] === 'id') {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
else if ((options.toSql.user && options.toSql.user.id) && (typeof options.toSql.user.id === 'string' || options.toSql.user.id instanceof String))
|
|
1518
|
-
return `'${options.toSql.user.id}'`;
|
|
1519
|
-
|
|
1520
|
-
if (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain') {
|
|
1521
|
-
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1522
|
-
return '\'$user.id\'';
|
|
1523
|
-
}
|
|
1524
|
-
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1524
|
+
if (options.sqlDialect === 'hana')
|
|
1525
|
+
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1526
|
+
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1527
|
+
return '\'$user.id\'';
|
|
1525
1528
|
}
|
|
1526
1529
|
else if (x.ref[1] === 'locale') {
|
|
1527
|
-
if (options.
|
|
1528
|
-
return (
|
|
1529
|
-
|
|
1530
|
-
}
|
|
1531
|
-
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1530
|
+
if (options.sqlDialect === 'hana')
|
|
1531
|
+
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1532
|
+
return '\'en\''; // default language
|
|
1532
1533
|
}
|
|
1533
1534
|
// Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
|
|
1534
1535
|
return null;
|
|
@@ -1547,13 +1548,14 @@ function toSqlDdl(csn, options) {
|
|
|
1547
1548
|
*/
|
|
1548
1549
|
function render$at() {
|
|
1549
1550
|
if (x.ref[1] === 'from') {
|
|
1550
|
-
switch (options.
|
|
1551
|
+
switch (options.sqlDialect) {
|
|
1551
1552
|
case 'sqlite': {
|
|
1552
1553
|
const dateFromFormat = '%Y-%m-%dT%H:%M:%S.000Z';
|
|
1553
1554
|
return `strftime('${dateFromFormat}', 'now')`;
|
|
1554
1555
|
}
|
|
1555
1556
|
case 'hana':
|
|
1556
1557
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-FROM\'))';
|
|
1558
|
+
case 'postgres':
|
|
1557
1559
|
case 'plain':
|
|
1558
1560
|
return 'current_timestamp';
|
|
1559
1561
|
default:
|
|
@@ -1562,7 +1564,7 @@ function toSqlDdl(csn, options) {
|
|
|
1562
1564
|
}
|
|
1563
1565
|
|
|
1564
1566
|
if (x.ref[1] === 'to') {
|
|
1565
|
-
switch (options.
|
|
1567
|
+
switch (options.sqlDialect) {
|
|
1566
1568
|
case 'sqlite': {
|
|
1567
1569
|
// + 1ms compared to $at.from
|
|
1568
1570
|
const dateToFormat = '%Y-%m-%dT%H:%M:%S.001Z';
|
|
@@ -1570,6 +1572,7 @@ function toSqlDdl(csn, options) {
|
|
|
1570
1572
|
}
|
|
1571
1573
|
case 'hana':
|
|
1572
1574
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1575
|
+
case 'postgres':
|
|
1573
1576
|
case 'plain':
|
|
1574
1577
|
return 'current_timestamp';
|
|
1575
1578
|
default:
|
|
@@ -1591,7 +1594,6 @@ function toSqlDdl(csn, options) {
|
|
|
1591
1594
|
if (typeof (s) === 'string') {
|
|
1592
1595
|
// TODO: When is this actually executed and not handled already in renderExpr?
|
|
1593
1596
|
const magicForHana = {
|
|
1594
|
-
$now: 'CURRENT_TIMESTAMP',
|
|
1595
1597
|
'$user.id': 'SESSION_CONTEXT(\'APPLICATIONUSER\')',
|
|
1596
1598
|
'$user.locale': 'SESSION_CONTEXT(\'LOCALE\')',
|
|
1597
1599
|
};
|
|
@@ -1599,7 +1601,7 @@ function toSqlDdl(csn, options) {
|
|
|
1599
1601
|
if (idx === 0) {
|
|
1600
1602
|
// HANA-specific translation of '$now' and '$user'
|
|
1601
1603
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1602
|
-
if (magicForHana[s])
|
|
1604
|
+
if (options.sqlDialect === 'hana' && magicForHana[s])
|
|
1603
1605
|
return magicForHana[s];
|
|
1604
1606
|
|
|
1605
1607
|
// Ignore initial $projection and initial $self
|
|
@@ -279,6 +279,18 @@ const cdsToSqlTypes = {
|
|
|
279
279
|
'cds.hana.BINARY': 'BINARY',
|
|
280
280
|
'cds.hana.SMALLDECIMAL': 'DECIMAL',
|
|
281
281
|
},
|
|
282
|
+
postgres: {
|
|
283
|
+
// TODO: Type mapping for binary types is not correct, yet.
|
|
284
|
+
// We can't use text types for binary on PostgreSQL due to NUL!
|
|
285
|
+
'cds.String': 'VARCHAR',
|
|
286
|
+
'cds.LargeString': 'text',
|
|
287
|
+
'cds.hana.CLOB': 'text',
|
|
288
|
+
'cds.LargeBinary': 'bytea',
|
|
289
|
+
'cds.Binary': 'VARCHAR',
|
|
290
|
+
'cds.hana.BINARY': 'VARCHAR',
|
|
291
|
+
'cds.Double': 'double precision',
|
|
292
|
+
'cds.hana.TINYINT': 'INTEGER',
|
|
293
|
+
},
|
|
282
294
|
};
|
|
283
295
|
|
|
284
296
|
/**
|
|
@@ -375,7 +387,7 @@ function getSqlSnippets(options, obj) {
|
|
|
375
387
|
* A function used to render a certain part of an expression object
|
|
376
388
|
*
|
|
377
389
|
* @callback renderPart
|
|
378
|
-
* @param {object
|
|
390
|
+
* @param {object|array} expression
|
|
379
391
|
* @param {CdlRenderEnvironment} env
|
|
380
392
|
* @this {{inline: Boolean, nestedExpr: Boolean}}
|
|
381
393
|
* @returns {string}
|
|
@@ -397,6 +409,8 @@ function getSqlSnippets(options, obj) {
|
|
|
397
409
|
* @property {renderPart} xpr
|
|
398
410
|
* @property {renderPart} SELECT
|
|
399
411
|
* @property {renderPart} SET
|
|
412
|
+
* @property {boolean} [inline]
|
|
413
|
+
* @property {boolean} [nestedExpr]
|
|
400
414
|
*/
|
|
401
415
|
|
|
402
416
|
/**
|