@sap/cds-compiler 2.15.2 → 3.0.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 +66 -1590
- package/bin/cdsc.js +42 -46
- 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 +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- 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 +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- 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 +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- 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 +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- 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 +333 -259
- package/lib/language/language.g4 +600 -645
- 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 +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -57
- 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 +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- 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 +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- 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 +6 -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 +31 -21
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- 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 {
|
|
@@ -829,7 +839,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
829
839
|
* @param {object} [elements] For leading query, the elements of the artifact
|
|
830
840
|
* @returns {string} The rendered query
|
|
831
841
|
*/
|
|
832
|
-
function renderQuery(query, isLeadingQuery, env, path = [], elements) {
|
|
842
|
+
function renderQuery(query, isLeadingQuery, env, path = [], elements = null) {
|
|
833
843
|
let result = '';
|
|
834
844
|
env.skipKeys = !isLeadingQuery;
|
|
835
845
|
// Set operator, like UNION, INTERSECT, ...
|
|
@@ -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)})`;
|
|
@@ -117,6 +117,9 @@ function toSqlDdl(csn, options) {
|
|
|
117
117
|
*/
|
|
118
118
|
addColumns: {
|
|
119
119
|
fromElementStrings(tableName, eltStrings) {
|
|
120
|
+
if (options.sqlDialect === 'sqlite') // SQLite can only alter one column at a time
|
|
121
|
+
return eltStrings.map(eltString => `ALTER TABLE ${tableName} ADD ${eltString};`);
|
|
122
|
+
|
|
120
123
|
const elts = options.sqlDialect === 'hana' ? `(${eltStrings.join(', ')})` : `${eltStrings.join(', ')}`;
|
|
121
124
|
return [ `ALTER TABLE ${tableName} ADD ${elts};` ];
|
|
122
125
|
},
|
|
@@ -138,10 +141,10 @@ function toSqlDdl(csn, options) {
|
|
|
138
141
|
TODO duplicity check
|
|
139
142
|
*/
|
|
140
143
|
addAssociations(artifactName, tableName, elementsObj, env) {
|
|
141
|
-
return Object.entries(elementsObj)
|
|
144
|
+
return options.sqlDialect === 'hana' ? Object.entries(elementsObj)
|
|
142
145
|
.map(([ name, elt ]) => renderAssociationElement(name, elt, env))
|
|
143
146
|
.filter(s => s !== '')
|
|
144
|
-
.map(eltStr => `ALTER TABLE ${tableName} ADD ASSOCIATION (${eltStr});`);
|
|
147
|
+
.map(eltStr => `ALTER TABLE ${tableName} ADD ASSOCIATION (${eltStr});`) : [];
|
|
145
148
|
},
|
|
146
149
|
/*
|
|
147
150
|
Render key addition as HANA SQL.
|
|
@@ -159,7 +162,7 @@ function toSqlDdl(csn, options) {
|
|
|
159
162
|
Render association removals as HANA SQL.
|
|
160
163
|
*/
|
|
161
164
|
dropAssociation(tableName, sqlId) {
|
|
162
|
-
return [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ];
|
|
165
|
+
return options.sqlDialect === 'hana' ? [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ] : [];
|
|
163
166
|
},
|
|
164
167
|
/*
|
|
165
168
|
Render primary-key removals as HANA SQL.
|
|
@@ -238,7 +241,7 @@ function toSqlDdl(csn, options) {
|
|
|
238
241
|
};
|
|
239
242
|
|
|
240
243
|
// Registries for artifact and element names per CSN section
|
|
241
|
-
const definitionsDuplicateChecker = new DuplicateChecker(options.
|
|
244
|
+
const definitionsDuplicateChecker = new DuplicateChecker(options.sqlMapping);
|
|
242
245
|
const deletionsDuplicateChecker = new DuplicateChecker();
|
|
243
246
|
const extensionsDuplicateChecker = new DuplicateChecker();
|
|
244
247
|
const removeElementsDuplicateChecker = new DuplicateChecker();
|
|
@@ -262,7 +265,7 @@ function toSqlDdl(csn, options) {
|
|
|
262
265
|
// Render each artifact extension
|
|
263
266
|
// Only HANA SQL is currently supported.
|
|
264
267
|
// Note that extensions may contain new elements referenced in migrations, thus should be compiled first.
|
|
265
|
-
if (csn.extensions && (options.
|
|
268
|
+
if (csn.extensions && (options.sqlDialect === 'hana' || isBetaEnabled(options, 'sqlExtensions'))) {
|
|
266
269
|
for (const extension of options && options.testMode ? sortCsn(csn.extensions) : csn.extensions) {
|
|
267
270
|
if (extension.extend) {
|
|
268
271
|
const artifactName = extension.extend;
|
|
@@ -275,7 +278,7 @@ function toSqlDdl(csn, options) {
|
|
|
275
278
|
|
|
276
279
|
// Render each artifact change
|
|
277
280
|
// Only HANA SQL is currently supported.
|
|
278
|
-
if (csn.migrations && (options.
|
|
281
|
+
if (csn.migrations && (options.sqlDialect === 'hana' || isBetaEnabled(options, 'sqlExtensions'))) {
|
|
279
282
|
for (const migration of options && options.testMode ? sortCsn(csn.migrations) : csn.migrations) {
|
|
280
283
|
if (migration.migrate) {
|
|
281
284
|
const artifactName = migration.migrate;
|
|
@@ -294,7 +297,7 @@ function toSqlDdl(csn, options) {
|
|
|
294
297
|
// Throw exception in case of errors
|
|
295
298
|
throwWithAnyError();
|
|
296
299
|
|
|
297
|
-
// Transfer results from hdb-specific dictionaries into 'sql' dictionary in proper order if
|
|
300
|
+
// Transfer results from hdb-specific dictionaries into 'sql' dictionary in proper order if src === 'sql'
|
|
298
301
|
// (relying on the order of dictionaries above)
|
|
299
302
|
// FIXME: Should consider inter-view dependencies, too
|
|
300
303
|
const sql = Object.create(null);
|
|
@@ -305,10 +308,10 @@ function toSqlDdl(csn, options) {
|
|
|
305
308
|
const { deletions, migrations: _, ...hdbKinds } = mainResultObj;
|
|
306
309
|
for (const hdbKind of Object.keys(hdbKinds)) {
|
|
307
310
|
for (const name in mainResultObj[hdbKind]) {
|
|
308
|
-
if (options.
|
|
311
|
+
if (options.src === 'sql') {
|
|
309
312
|
let sourceString = mainResultObj[hdbKind][name];
|
|
310
313
|
// Hack: Other than in 'hdbtable' files, in HANA SQL COLUMN is not mandatory but default.
|
|
311
|
-
if (options.
|
|
314
|
+
if (options.sqlDialect === 'hana' && hdbKind === 'hdbtable' && sourceString.startsWith('COLUMN '))
|
|
312
315
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
313
316
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
314
317
|
}
|
|
@@ -316,7 +319,7 @@ function toSqlDdl(csn, options) {
|
|
|
316
319
|
mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
|
|
317
320
|
}
|
|
318
321
|
}
|
|
319
|
-
if (options.
|
|
322
|
+
if (options.src === 'sql')
|
|
320
323
|
delete mainResultObj[hdbKind];
|
|
321
324
|
}
|
|
322
325
|
|
|
@@ -329,7 +332,7 @@ function toSqlDdl(csn, options) {
|
|
|
329
332
|
mainResultObj.sql = sql;
|
|
330
333
|
}
|
|
331
334
|
|
|
332
|
-
if (options.
|
|
335
|
+
if (options.src === 'sql')
|
|
333
336
|
mainResultObj.sql = sql;
|
|
334
337
|
|
|
335
338
|
for (const name in deletions)
|
|
@@ -420,7 +423,7 @@ function toSqlDdl(csn, options) {
|
|
|
420
423
|
* @returns {string} Artifact name
|
|
421
424
|
*/
|
|
422
425
|
function renderArtifactName(artifactName) {
|
|
423
|
-
return quoteSqlId(getResultingName(csn, options.
|
|
426
|
+
return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName));
|
|
424
427
|
}
|
|
425
428
|
|
|
426
429
|
// Render an artifact migration into the appropriate dictionary of 'resultObj'.
|
|
@@ -573,7 +576,7 @@ function toSqlDdl(csn, options) {
|
|
|
573
576
|
const { front, back } = getSqlSnippets(options, art);
|
|
574
577
|
let result = front;
|
|
575
578
|
// Only HANA has row/column tables
|
|
576
|
-
if (options.
|
|
579
|
+
if (options.sqlDialect === 'hana') {
|
|
577
580
|
if (hanaTc && hanaTc.storeType) {
|
|
578
581
|
// Explicitly specified
|
|
579
582
|
result += `${art.technicalConfig.hana.storeType.toUpperCase()} `;
|
|
@@ -604,7 +607,7 @@ function toSqlDdl(csn, options) {
|
|
|
604
607
|
// for `to.sql` w/ dialect `hana` the constraints will be part of the
|
|
605
608
|
const constraintsAsAlter = !options.constraintsInCreateTable && options.src === 'sql' && options.sqlDialect === 'hana';
|
|
606
609
|
if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
|
|
607
|
-
const renderReferentialConstraintsAsHdbconstraint = options.
|
|
610
|
+
const renderReferentialConstraintsAsHdbconstraint = options.src === 'hdi';
|
|
608
611
|
const referentialConstraints = {};
|
|
609
612
|
Object.entries(art.$tableConstraints.referential)
|
|
610
613
|
.forEach(([ fileName, referentialConstraint ]) => {
|
|
@@ -627,7 +630,7 @@ function toSqlDdl(csn, options) {
|
|
|
627
630
|
const uniqueConstraints = art.$tableConstraints && art.$tableConstraints.unique;
|
|
628
631
|
for (const cn in uniqueConstraints) {
|
|
629
632
|
const c = uniqueConstraints[cn];
|
|
630
|
-
if (options.
|
|
633
|
+
if (options.src === 'hdi') {
|
|
631
634
|
resultObj.hdbindex[`${artifactName}.${cn}`]
|
|
632
635
|
= `UNIQUE INVERTED INDEX ${renderArtifactName(`${artifactName}_${cn}`)} ON ${tableName} (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
|
|
633
636
|
}
|
|
@@ -637,20 +640,20 @@ function toSqlDdl(csn, options) {
|
|
|
637
640
|
}
|
|
638
641
|
result += `${env.indent}\n)`;
|
|
639
642
|
|
|
640
|
-
if (options.
|
|
643
|
+
if (options.sqlDialect === 'hana')
|
|
641
644
|
result += renderTechnicalConfiguration(art.technicalConfig, childEnv);
|
|
642
645
|
|
|
643
646
|
|
|
644
647
|
const associations = Object.keys(art.elements).map(name => renderAssociationElement(name, art.elements[name], childEnv))
|
|
645
648
|
.filter(s => s !== '')
|
|
646
649
|
.join(',\n');
|
|
647
|
-
if (associations !== '' && options.
|
|
650
|
+
if (associations !== '' && options.sqlDialect === 'hana') {
|
|
648
651
|
result += `${env.indent} WITH ASSOCIATIONS (\n${associations}\n`;
|
|
649
652
|
result += `${env.indent})`;
|
|
650
653
|
}
|
|
651
654
|
// Only HANA has indices
|
|
652
655
|
// FIXME: Really? We should provide a DB-agnostic way to specify that
|
|
653
|
-
if (options.
|
|
656
|
+
if (options.sqlDialect === 'hana')
|
|
654
657
|
renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
|
|
655
658
|
|
|
656
659
|
if (options.sqlDialect === 'hana' && hasHanaComment(art, options))
|
|
@@ -758,7 +761,7 @@ function toSqlDdl(csn, options) {
|
|
|
758
761
|
result += ` DEFAULT ${renderExpr(elm.default, env)}`;
|
|
759
762
|
|
|
760
763
|
// Only HANA has fuzzy indices
|
|
761
|
-
if (fzindex && options.
|
|
764
|
+
if (fzindex && options.sqlDialect === 'hana')
|
|
762
765
|
result += ` ${renderExpr(fzindex, env)}`;
|
|
763
766
|
|
|
764
767
|
// (table) elements can only have a @sql.append
|
|
@@ -906,7 +909,7 @@ function toSqlDdl(csn, options) {
|
|
|
906
909
|
throw new ModelError(`Unexpected form of index: "${index}"`);
|
|
907
910
|
|
|
908
911
|
let indexName = renderArtifactName(`${artifactName}.${index[i + 1].ref}`);
|
|
909
|
-
if (options.
|
|
912
|
+
if (options.sqlMapping === 'plain')
|
|
910
913
|
indexName = indexName.replace(/(\.|::)/g, '_');
|
|
911
914
|
|
|
912
915
|
const result = index.slice(0, i + 1); // CREATE UNIQUE INDEX
|
|
@@ -945,7 +948,7 @@ function toSqlDdl(csn, options) {
|
|
|
945
948
|
let result = `${renderViewSource(artifactName, source.args[0], env)}`;
|
|
946
949
|
for (let i = 1; i < source.args.length; i++) {
|
|
947
950
|
result = `(${result} ${source.join.toUpperCase()} `;
|
|
948
|
-
if (options.
|
|
951
|
+
if (options.sqlDialect === 'hana')
|
|
949
952
|
result += renderJoinCardinality(source.cardinality);
|
|
950
953
|
result += `JOIN ${renderViewSource(artifactName, source.args[i], env)}`;
|
|
951
954
|
if (source.on)
|
|
@@ -1008,7 +1011,7 @@ function toSqlDdl(csn, options) {
|
|
|
1008
1011
|
let result = renderAbsolutePath(path, ':', env);
|
|
1009
1012
|
|
|
1010
1013
|
// Take care of aliases
|
|
1011
|
-
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.
|
|
1014
|
+
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
|
|
1012
1015
|
if (path.as) {
|
|
1013
1016
|
// Source had an alias - render it
|
|
1014
1017
|
result += ` AS ${quoteSqlId(path.as)}`;
|
|
@@ -1126,7 +1129,7 @@ function toSqlDdl(csn, options) {
|
|
|
1126
1129
|
let result = '';
|
|
1127
1130
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1] || col.func;
|
|
1128
1131
|
if (leaf && elements[leaf] && elements[leaf].virtual) {
|
|
1129
|
-
if (isDeprecatedEnabled(options, '
|
|
1132
|
+
if (isDeprecatedEnabled(options, '_renderVirtualElements'))
|
|
1130
1133
|
// render a virtual column 'null as <alias>'
|
|
1131
1134
|
result += `${env.indent}NULL AS ${quoteSqlId(col.as || leaf)}`;
|
|
1132
1135
|
}
|
|
@@ -1165,7 +1168,7 @@ function toSqlDdl(csn, options) {
|
|
|
1165
1168
|
.map(name => renderAssociationElement(name, art.elements[name], childEnv))
|
|
1166
1169
|
.filter(s => s !== '')
|
|
1167
1170
|
.join(',\n');
|
|
1168
|
-
if (associations !== '' && options.
|
|
1171
|
+
if (associations !== '' && options.sqlDialect === 'hana') {
|
|
1169
1172
|
result += `${env.indent}\nWITH ASSOCIATIONS (\n${associations}\n`;
|
|
1170
1173
|
result += `${env.indent})`;
|
|
1171
1174
|
}
|
|
@@ -1197,7 +1200,7 @@ function toSqlDdl(csn, options) {
|
|
|
1197
1200
|
// this would be an incompatible change, as non-uppercased, quoted identifiers
|
|
1198
1201
|
// are rejected by the HANA compiler.
|
|
1199
1202
|
let pIdentifier;
|
|
1200
|
-
if (options.
|
|
1203
|
+
if (options.sqlMapping === 'quoted' || options.sqlMapping === 'hdbcds')
|
|
1201
1204
|
pIdentifier = prepareIdentifier(pn);
|
|
1202
1205
|
else
|
|
1203
1206
|
pIdentifier = quoteSqlId(pn);
|
|
@@ -1386,7 +1389,7 @@ function toSqlDdl(csn, options) {
|
|
|
1386
1389
|
'cds.LocalTime': 'cds.Time',
|
|
1387
1390
|
};
|
|
1388
1391
|
const tName = forHanaRenamesToEarly[typeName] || typeName;
|
|
1389
|
-
const types = cdsToSqlTypes[options.
|
|
1392
|
+
const types = cdsToSqlTypes[options.sqlDialect];
|
|
1390
1393
|
return types && types[tName] || cdsToSqlTypes.standard[tName] || 'CHAR';
|
|
1391
1394
|
}
|
|
1392
1395
|
|
|
@@ -1426,7 +1429,7 @@ function toSqlDdl(csn, options) {
|
|
|
1426
1429
|
|
|
1427
1430
|
if (elm.srid !== undefined) {
|
|
1428
1431
|
// SAP HANA Geometry types translate into CHAR in plain/sqlite (give them the default length of 2000)
|
|
1429
|
-
if (options.
|
|
1432
|
+
if (options.sqlDialect !== 'hana')
|
|
1430
1433
|
params.push(2000);
|
|
1431
1434
|
else
|
|
1432
1435
|
params.push(elm.srid);
|
|
@@ -1448,7 +1451,7 @@ function toSqlDdl(csn, options) {
|
|
|
1448
1451
|
case 'date':
|
|
1449
1452
|
case 'time':
|
|
1450
1453
|
case 'timestamp':
|
|
1451
|
-
if (options.
|
|
1454
|
+
if (options.sqlDialect === 'sqlite') {
|
|
1452
1455
|
// simple string literal '2017-11-02'
|
|
1453
1456
|
return `'${x.val}'`;
|
|
1454
1457
|
}
|
|
@@ -1474,7 +1477,7 @@ function toSqlDdl(csn, options) {
|
|
|
1474
1477
|
|
|
1475
1478
|
if (x.ref[0] === '$user') {
|
|
1476
1479
|
if (magicReplacement !== null)
|
|
1477
|
-
return
|
|
1480
|
+
return renderStringForSql(magicReplacement, options.sqlDialect);
|
|
1478
1481
|
|
|
1479
1482
|
const result = render$user();
|
|
1480
1483
|
// Invalid second path step doesn't cause a return
|
|
@@ -1488,7 +1491,19 @@ function toSqlDdl(csn, options) {
|
|
|
1488
1491
|
return result;
|
|
1489
1492
|
}
|
|
1490
1493
|
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1491
|
-
return
|
|
1494
|
+
return renderStringForSql(magicReplacement, options.sqlDialect);
|
|
1495
|
+
}
|
|
1496
|
+
else if (x.ref[0] === '$now') { // TODO: Can there be cases where $now is followed by something?
|
|
1497
|
+
switch (options.sqlDialect) {
|
|
1498
|
+
case 'plain':
|
|
1499
|
+
case 'sqlite':
|
|
1500
|
+
case 'hana':
|
|
1501
|
+
return 'CURRENT_TIMESTAMP';
|
|
1502
|
+
case 'postgres':
|
|
1503
|
+
return 'current_timestamp';
|
|
1504
|
+
default:
|
|
1505
|
+
return quoteSqlId(x.ref[0]);
|
|
1506
|
+
}
|
|
1492
1507
|
}
|
|
1493
1508
|
}
|
|
1494
1509
|
// FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
|
|
@@ -1508,27 +1523,17 @@ function toSqlDdl(csn, options) {
|
|
|
1508
1523
|
* @returns {string|null} Null in case of an invalid second path step
|
|
1509
1524
|
*/
|
|
1510
1525
|
function render$user() {
|
|
1511
|
-
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1526
|
+
// FIXME: this is all not enough: we might need an explicit select item alias (?)
|
|
1512
1527
|
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\')';
|
|
1528
|
+
if (options.sqlDialect === 'hana')
|
|
1529
|
+
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1530
|
+
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1531
|
+
return '\'$user.id\'';
|
|
1525
1532
|
}
|
|
1526
1533
|
else if (x.ref[1] === 'locale') {
|
|
1527
|
-
if (options.
|
|
1528
|
-
return (
|
|
1529
|
-
|
|
1530
|
-
}
|
|
1531
|
-
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1534
|
+
if (options.sqlDialect === 'hana')
|
|
1535
|
+
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1536
|
+
return '\'en\''; // default language
|
|
1532
1537
|
}
|
|
1533
1538
|
// Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
|
|
1534
1539
|
return null;
|
|
@@ -1547,13 +1552,14 @@ function toSqlDdl(csn, options) {
|
|
|
1547
1552
|
*/
|
|
1548
1553
|
function render$at() {
|
|
1549
1554
|
if (x.ref[1] === 'from') {
|
|
1550
|
-
switch (options.
|
|
1555
|
+
switch (options.sqlDialect) {
|
|
1551
1556
|
case 'sqlite': {
|
|
1552
1557
|
const dateFromFormat = '%Y-%m-%dT%H:%M:%S.000Z';
|
|
1553
1558
|
return `strftime('${dateFromFormat}', 'now')`;
|
|
1554
1559
|
}
|
|
1555
1560
|
case 'hana':
|
|
1556
1561
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-FROM\'))';
|
|
1562
|
+
case 'postgres':
|
|
1557
1563
|
case 'plain':
|
|
1558
1564
|
return 'current_timestamp';
|
|
1559
1565
|
default:
|
|
@@ -1562,7 +1568,7 @@ function toSqlDdl(csn, options) {
|
|
|
1562
1568
|
}
|
|
1563
1569
|
|
|
1564
1570
|
if (x.ref[1] === 'to') {
|
|
1565
|
-
switch (options.
|
|
1571
|
+
switch (options.sqlDialect) {
|
|
1566
1572
|
case 'sqlite': {
|
|
1567
1573
|
// + 1ms compared to $at.from
|
|
1568
1574
|
const dateToFormat = '%Y-%m-%dT%H:%M:%S.001Z';
|
|
@@ -1570,6 +1576,7 @@ function toSqlDdl(csn, options) {
|
|
|
1570
1576
|
}
|
|
1571
1577
|
case 'hana':
|
|
1572
1578
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1579
|
+
case 'postgres':
|
|
1573
1580
|
case 'plain':
|
|
1574
1581
|
return 'current_timestamp';
|
|
1575
1582
|
default:
|
|
@@ -1591,7 +1598,6 @@ function toSqlDdl(csn, options) {
|
|
|
1591
1598
|
if (typeof (s) === 'string') {
|
|
1592
1599
|
// TODO: When is this actually executed and not handled already in renderExpr?
|
|
1593
1600
|
const magicForHana = {
|
|
1594
|
-
$now: 'CURRENT_TIMESTAMP',
|
|
1595
1601
|
'$user.id': 'SESSION_CONTEXT(\'APPLICATIONUSER\')',
|
|
1596
1602
|
'$user.locale': 'SESSION_CONTEXT(\'LOCALE\')',
|
|
1597
1603
|
};
|
|
@@ -1599,7 +1605,7 @@ function toSqlDdl(csn, options) {
|
|
|
1599
1605
|
if (idx === 0) {
|
|
1600
1606
|
// HANA-specific translation of '$now' and '$user'
|
|
1601
1607
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1602
|
-
if (magicForHana[s])
|
|
1608
|
+
if (options.sqlDialect === 'hana' && magicForHana[s])
|
|
1603
1609
|
return magicForHana[s];
|
|
1604
1610
|
|
|
1605
1611
|
// 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
|
/**
|