@sap/cds-compiler 2.10.2 → 2.11.4
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 +90 -5
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +3 -1
- package/bin/cdsc.js +49 -25
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +8 -36
- package/lib/api/options.js +15 -6
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +34 -10
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +57 -23
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +9 -2
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +66 -108
- package/lib/compiler/index.js +29 -29
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +225 -58
- package/lib/compiler/shared.js +53 -229
- package/lib/compiler/utils.js +184 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +34 -38
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +228 -47
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +73 -14
- package/lib/language/language.g4 +79 -3
- package/lib/main.d.ts +215 -18
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +117 -33
- package/lib/model/csnUtils.js +65 -133
- package/lib/model/enrichCsn.js +62 -37
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +15 -8
- package/lib/render/toHdbcds.js +26 -49
- package/lib/render/toSql.js +61 -39
- package/lib/render/utils/common.js +1 -1
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +19 -3
- package/lib/transform/db/transformExists.js +102 -9
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +93 -448
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/structuralPath.js +1 -5
- package/lib/transform/transformUtilsNew.js +22 -8
- package/lib/transform/translateAssocsToJoins.js +7 -15
- package/lib/utils/file.js +11 -5
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/render/toHdbcds.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getParentNameOf, getLastPartOf, getLastPartOfRef,
|
|
5
5
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
6
|
-
getRootArtifactName, getResultingName, getNamespace, forEachMember,
|
|
6
|
+
getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement,
|
|
7
7
|
} = require('../model/csnUtils');
|
|
8
8
|
const keywords = require('../base/keywords');
|
|
9
9
|
const {
|
|
@@ -17,7 +17,7 @@ const DuplicateChecker = require('./DuplicateChecker');
|
|
|
17
17
|
const { isDeprecatedEnabled, forEachDefinition } = require('../base/model');
|
|
18
18
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
19
19
|
const { makeMessageFunction } = require('../base/messages');
|
|
20
|
-
const timetrace = require('../utils/timetrace');
|
|
20
|
+
const { timetrace } = require('../utils/timetrace');
|
|
21
21
|
|
|
22
22
|
const { smartId, delimitedId } = require('../sql-identifier');
|
|
23
23
|
|
|
@@ -558,7 +558,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
558
558
|
result += env.indent + (elm.key && !isSubElement ? 'key ' : '') +
|
|
559
559
|
(elm.masked ? 'masked ' : '') +
|
|
560
560
|
quotedElementName + (omitColon ? ' ' : ' : ') +
|
|
561
|
-
renderTypeReference(elm, env
|
|
561
|
+
renderTypeReference(elm, env) +
|
|
562
562
|
renderNullability(elm);
|
|
563
563
|
if (elm.default)
|
|
564
564
|
result += ` default ${renderExpr(elm.default, env)}`;
|
|
@@ -630,7 +630,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
630
630
|
|
|
631
631
|
// Even the first step might have parameters and/or a filter
|
|
632
632
|
if (path.ref[0].args)
|
|
633
|
-
result += `(${renderArgs(path.ref[0]
|
|
633
|
+
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
634
634
|
|
|
635
635
|
if (path.ref[0].where)
|
|
636
636
|
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
|
|
@@ -680,7 +680,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
680
680
|
*/
|
|
681
681
|
function renderViewColumn(col, env, element) {
|
|
682
682
|
// Annotations and column
|
|
683
|
-
let result =
|
|
683
|
+
let result = '';
|
|
684
684
|
|
|
685
685
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1];
|
|
686
686
|
// Render 'null as <alias>' only for database and if element is virtual
|
|
@@ -926,7 +926,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
926
926
|
*/
|
|
927
927
|
function renderParameter(parName, par, env) {
|
|
928
928
|
if (par.notNull === true || par.notNull === false)
|
|
929
|
-
info(
|
|
929
|
+
info('query-ignoring-param-nullability', env.path.concat([ 'params', parName ]), { '#': 'std' });
|
|
930
930
|
return `${env.indent + formatParamIdentifier(parName, env.path.concat([ 'params', parName ]))} : ${renderTypeReference(par, env)}`;
|
|
931
931
|
}
|
|
932
932
|
|
|
@@ -959,7 +959,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
959
959
|
}
|
|
960
960
|
else {
|
|
961
961
|
// Derived type or annotation with non-anonymous type
|
|
962
|
-
result += ` : ${renderTypeReference(art, env
|
|
962
|
+
result += ` : ${renderTypeReference(art, env)};\n`;
|
|
963
963
|
}
|
|
964
964
|
return result;
|
|
965
965
|
}
|
|
@@ -970,10 +970,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
970
970
|
*
|
|
971
971
|
* @param {CSN.Element} elm Element using the type reference
|
|
972
972
|
* @param {CdlRenderEnvironment} env Environment
|
|
973
|
-
* @param {boolean} [noEnum=false] If true, do not render enums
|
|
974
973
|
* @returns {string} Rendered type reference
|
|
975
974
|
*/
|
|
976
|
-
function renderTypeReference(elm, env
|
|
975
|
+
function renderTypeReference(elm, env) {
|
|
977
976
|
let result = '';
|
|
978
977
|
|
|
979
978
|
// Array type: Render items instead
|
|
@@ -1011,11 +1010,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1011
1010
|
// Reference to another element
|
|
1012
1011
|
if (elm.type.ref) {
|
|
1013
1012
|
// For HANA CDS, we need a 'type of'
|
|
1014
|
-
|
|
1015
|
-
if (elm.enum)
|
|
1016
|
-
result += renderEnum(elm.enum, env);
|
|
1017
|
-
|
|
1018
|
-
return result;
|
|
1013
|
+
return `type of ${renderAbsolutePath(elm.type, env)}`;
|
|
1019
1014
|
}
|
|
1020
1015
|
|
|
1021
1016
|
// If we get here, it must be a named type
|
|
@@ -1027,8 +1022,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1027
1022
|
// Type names are never flattened (derived types are unraveled in HANA)
|
|
1028
1023
|
result += renderAbsoluteNameWithQuotes(elm.type, env);
|
|
1029
1024
|
}
|
|
1030
|
-
if (elm.enum && !noEnum)
|
|
1031
|
-
result += renderEnum(elm.enum, env);
|
|
1032
1025
|
|
|
1033
1026
|
return result;
|
|
1034
1027
|
}
|
|
@@ -1094,27 +1087,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1094
1087
|
return elm.type.replace(/^cds\./, '') + renderTypeParameters(elm);
|
|
1095
1088
|
}
|
|
1096
1089
|
|
|
1097
|
-
/**
|
|
1098
|
-
* Render the 'enum { ... } part of a type declaration
|
|
1099
|
-
*
|
|
1100
|
-
* @param {CSN.EnumElements} enumPart Enum part of a type declaration
|
|
1101
|
-
* @param {CdlRenderEnvironment} env Environment
|
|
1102
|
-
* @returns {string} Rendered enum
|
|
1103
|
-
*/
|
|
1104
|
-
function renderEnum(enumPart, env) {
|
|
1105
|
-
let result = ' enum {\n';
|
|
1106
|
-
const childEnv = increaseIndent(env);
|
|
1107
|
-
for (const name in enumPart) {
|
|
1108
|
-
const enumConst = enumPart[name];
|
|
1109
|
-
result += childEnv.indent + quoteId(name);
|
|
1110
|
-
if (enumConst.val !== undefined)
|
|
1111
|
-
result += ` = ${renderExpr(enumConst, childEnv)}`;
|
|
1112
|
-
result += ';\n';
|
|
1113
|
-
}
|
|
1114
|
-
result += `${env.indent}}`;
|
|
1115
|
-
return result;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1090
|
/**
|
|
1119
1091
|
* Render an expression (including paths and values) or condition 'x'.
|
|
1120
1092
|
* (no trailing LF, don't indent if inline)
|
|
@@ -1229,7 +1201,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
1229
1201
|
*/
|
|
1230
1202
|
function renderExpressionRef(x) {
|
|
1231
1203
|
if (!x.param && !x.global) {
|
|
1204
|
+
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1232
1205
|
if (x.ref[0] === '$user') {
|
|
1206
|
+
if (magicReplacement !== null)
|
|
1207
|
+
return `'${magicReplacement}'`;
|
|
1208
|
+
|
|
1209
|
+
// Keep old way of solving this to remain backwards compatible
|
|
1233
1210
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1234
1211
|
if (x.ref[1] === 'id') {
|
|
1235
1212
|
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
@@ -1251,6 +1228,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1251
1228
|
else if (x.ref[1] === 'to')
|
|
1252
1229
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1253
1230
|
}
|
|
1231
|
+
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1232
|
+
return `'${magicReplacement}'`;
|
|
1233
|
+
}
|
|
1254
1234
|
}
|
|
1255
1235
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref)).join('.')}`;
|
|
1256
1236
|
}
|
|
@@ -1262,7 +1242,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1262
1242
|
* @returns {string} Rendered cast()
|
|
1263
1243
|
*/
|
|
1264
1244
|
function renderExplicitTypeCast(value) {
|
|
1265
|
-
let typeRef = renderTypeReference(x.cast, env
|
|
1245
|
+
let typeRef = renderTypeReference(x.cast, env);
|
|
1266
1246
|
|
|
1267
1247
|
// inside a cast expression, the cds and hana cds types need to be mapped to hana sql types
|
|
1268
1248
|
const hanaSqlType = cdsToSqlTypes.hana[x.cast.type] || cdsToSqlTypes.standard[x.cast.type];
|
|
@@ -1288,7 +1268,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1288
1268
|
// (see FIXME at renderArtifact)
|
|
1289
1269
|
if (idx === 0 && s === $SELF) {
|
|
1290
1270
|
// do not produce USING for $projection
|
|
1291
|
-
if (env.currentArtifactName === $PROJECTION
|
|
1271
|
+
if (env.currentArtifactName === $PROJECTION)
|
|
1292
1272
|
return env.currentArtifactName;
|
|
1293
1273
|
|
|
1294
1274
|
return plainNames ? renderAbsoluteNamePlain(env.currentArtifactName, env)
|
|
@@ -1321,13 +1301,13 @@ function toHdbcdsSource(csn, options) {
|
|
|
1321
1301
|
|
|
1322
1302
|
// Not really a path step but an object-like function call
|
|
1323
1303
|
if (s.func)
|
|
1324
|
-
return `${s.func}(${renderArgs(s
|
|
1304
|
+
return `${s.func}(${renderArgs(s, '=>', env)})`;
|
|
1325
1305
|
|
|
1326
1306
|
// Path step, possibly with view parameters and/or filters
|
|
1327
1307
|
let result = `${formatIdentifier(s.id)}`;
|
|
1328
1308
|
if (s.args) {
|
|
1329
1309
|
// View parameters
|
|
1330
|
-
result += `(${renderArgs(s
|
|
1310
|
+
result += `(${renderArgs(s, ':', env)})`;
|
|
1331
1311
|
}
|
|
1332
1312
|
if (s.where) {
|
|
1333
1313
|
// Filter, possibly with cardinality
|
|
@@ -1344,19 +1324,21 @@ function toHdbcdsSource(csn, options) {
|
|
|
1344
1324
|
* Render function arguments or view parameters (positional if array, named if object/dict),
|
|
1345
1325
|
* using 'sep' as separator for positional parameters
|
|
1346
1326
|
*
|
|
1347
|
-
* @param {object
|
|
1327
|
+
* @param {object} node with `args` to render
|
|
1348
1328
|
* @param {string} sep Seperator between arguments
|
|
1349
1329
|
* @param {CdlRenderEnvironment} env Environment
|
|
1350
1330
|
* @returns {string} Rendered arguments
|
|
1351
1331
|
*/
|
|
1352
|
-
function renderArgs(
|
|
1332
|
+
function renderArgs(node, sep, env) {
|
|
1333
|
+
const args = node.args ? node.args : {};
|
|
1353
1334
|
// Positional arguments
|
|
1354
1335
|
if (Array.isArray(args))
|
|
1355
1336
|
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
1356
1337
|
|
|
1357
1338
|
// Named arguments (object/dict)
|
|
1358
1339
|
else if (typeof args === 'object')
|
|
1359
|
-
|
|
1340
|
+
// if this is a function param which is not a reference to the model, we must not quote it
|
|
1341
|
+
return Object.keys(args).map(key => `${node.func ? key : formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1360
1342
|
|
|
1361
1343
|
|
|
1362
1344
|
throw new Error(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1673,11 +1655,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1673
1655
|
if (id.indexOf('.') !== -1)
|
|
1674
1656
|
throw new Error(id);
|
|
1675
1657
|
|
|
1676
|
-
// FIXME: Somewhat arbitrary magic: Do not quote $projection (because HANA CDS doesn't recognize it otherwise). Similar for $self.
|
|
1677
|
-
// FIXME: The test should not be on the name, but by checking the _artifact.
|
|
1678
|
-
if (id === $PROJECTION || id === $SELF)
|
|
1679
|
-
return id;
|
|
1680
|
-
|
|
1681
1658
|
|
|
1682
1659
|
switch (options.forHana.names) {
|
|
1683
1660
|
case 'plain':
|
package/lib/render/toSql.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
const {
|
|
5
5
|
getLastPartOf, getLastPartOfRef,
|
|
6
6
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
7
|
-
forEachDefinition, getResultingName,
|
|
7
|
+
forEachDefinition, getResultingName, getVariableReplacement,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const {
|
|
10
10
|
renderFunc, beautifyExprArray, cdsToSqlTypes, getHanaComment, hasHanaComment,
|
|
@@ -15,10 +15,11 @@ const {
|
|
|
15
15
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
16
16
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
17
17
|
const { makeMessageFunction } = require('../base/messages');
|
|
18
|
-
const timetrace = require('../utils/timetrace');
|
|
18
|
+
const { timetrace } = require('../utils/timetrace');
|
|
19
19
|
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
|
|
20
20
|
const { smartFuncId } = require('../sql-identifier');
|
|
21
21
|
const { sortCsn } = require('../json/to-csn');
|
|
22
|
+
const { manageConstraints } = require('./manageConstraints');
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -221,7 +222,6 @@ function toSqlDdl(csn, options) {
|
|
|
221
222
|
for (const artifactName in csn.deletions)
|
|
222
223
|
renderArtifactDeletionInto(artifactName, csn.deletions[artifactName], resultObj);
|
|
223
224
|
|
|
224
|
-
|
|
225
225
|
// Render each artifact extension
|
|
226
226
|
// Only HANA SQL is currently supported.
|
|
227
227
|
// Note that extensions may contain new elements referenced in migrations, thus should be compiled first.
|
|
@@ -273,7 +273,6 @@ function toSqlDdl(csn, options) {
|
|
|
273
273
|
// Hack: Other than in 'hdbtable' files, in HANA SQL COLUMN is not mandatory but default.
|
|
274
274
|
if (options.toSql.dialect === 'hana' && hdbKind === 'hdbtable' && sourceString.startsWith('COLUMN '))
|
|
275
275
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
276
|
-
|
|
277
276
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
278
277
|
}
|
|
279
278
|
else if (!options.testMode) {
|
|
@@ -284,6 +283,15 @@ function toSqlDdl(csn, options) {
|
|
|
284
283
|
delete resultObj[hdbKind];
|
|
285
284
|
}
|
|
286
285
|
|
|
286
|
+
// add `ALTER TABLE ADD CONSTRAINT` statements if requested
|
|
287
|
+
if (options.sqlDialect !== 'sqlite' && options.constraintsAsAlter) {
|
|
288
|
+
const alterStmts = manageConstraints(csn, options);
|
|
289
|
+
|
|
290
|
+
for ( const constraintName of Object.keys(alterStmts))
|
|
291
|
+
sql[constraintName] = `${options.testMode ? '' : sqlVersionLine}${alterStmts[constraintName]}`;
|
|
292
|
+
resultObj.sql = sql;
|
|
293
|
+
}
|
|
294
|
+
|
|
287
295
|
if (options.toSql.src === 'sql')
|
|
288
296
|
resultObj.sql = sql;
|
|
289
297
|
|
|
@@ -385,7 +393,7 @@ function toSqlDdl(csn, options) {
|
|
|
385
393
|
function reducesTypeSize(def) {
|
|
386
394
|
// HANA does not allow decreasing the value of any of those type parameters.
|
|
387
395
|
return def.old.type === def.new.type &&
|
|
388
|
-
|
|
396
|
+
[ 'length', 'precision', 'scale' ].some(param => def.new[param] < def.old[param]);
|
|
389
397
|
}
|
|
390
398
|
function getEltStr(defVariant, eltName) {
|
|
391
399
|
return defVariant.target
|
|
@@ -500,13 +508,9 @@ function toSqlDdl(csn, options) {
|
|
|
500
508
|
result += `TABLE ${tableName}`;
|
|
501
509
|
result += ' (\n';
|
|
502
510
|
const elements = Object.keys(art.elements).map(eltName => renderElement(artifactName, eltName, art.elements[eltName], definitionsDuplicateChecker, getFzIndex(eltName, hanaTc), childEnv)).filter(s => s !== '').join(',\n');
|
|
503
|
-
if (elements !== '')
|
|
511
|
+
if (elements !== '')
|
|
504
512
|
result += elements;
|
|
505
|
-
|
|
506
|
-
else {
|
|
507
|
-
// TODO: Already be handled by 'empty-entity' reclassification; better location
|
|
508
|
-
error(null, [ 'definitions', artifactName ], 'Entities must have at least one element that is non-virtual');
|
|
509
|
-
}
|
|
513
|
+
|
|
510
514
|
const uniqueFields = Object.keys(art.elements).filter(name => art.elements[name].unique && !art.elements[name].virtual)
|
|
511
515
|
.map(name => quoteSqlId(name))
|
|
512
516
|
.join(', ');
|
|
@@ -517,7 +521,8 @@ function toSqlDdl(csn, options) {
|
|
|
517
521
|
if (primaryKeys !== '')
|
|
518
522
|
result += `,\n${childEnv.indent}${primaryKeys}`;
|
|
519
523
|
|
|
520
|
-
|
|
524
|
+
const constraintsAsAlter = options.constraintsAsAlter && options.sqlDialect !== 'sqlite';
|
|
525
|
+
if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
|
|
521
526
|
const renderReferentialConstraintsAsHdbconstraint = options.toSql.src === 'hdi';
|
|
522
527
|
const referentialConstraints = {};
|
|
523
528
|
Object.entries(art.$tableConstraints.referential)
|
|
@@ -525,12 +530,12 @@ function toSqlDdl(csn, options) {
|
|
|
525
530
|
referentialConstraints[fileName] = renderReferentialConstraint(referentialConstraint, childEnv.indent, false, csn, options);
|
|
526
531
|
});
|
|
527
532
|
if (renderReferentialConstraintsAsHdbconstraint) {
|
|
528
|
-
Object.entries(referentialConstraints).forEach(
|
|
533
|
+
Object.entries(referentialConstraints).forEach(([ fileName, constraint ]) => {
|
|
529
534
|
resultObj.hdbconstraint[fileName] = constraint;
|
|
530
535
|
});
|
|
531
536
|
}
|
|
532
537
|
else {
|
|
533
|
-
Object.values(referentialConstraints).forEach(
|
|
538
|
+
Object.values(referentialConstraints).forEach((constraint) => {
|
|
534
539
|
result += `,\n${constraint}`;
|
|
535
540
|
});
|
|
536
541
|
}
|
|
@@ -546,8 +551,7 @@ function toSqlDdl(csn, options) {
|
|
|
546
551
|
= `UNIQUE INVERTED INDEX ${renderArtifactName(`${artifactName}_${cn}`)} ON ${tableName} (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
|
|
547
552
|
}
|
|
548
553
|
else {
|
|
549
|
-
result += `,\n${childEnv.indent}CONSTRAINT ${renderArtifactName(`${artifactName}_${cn}`)} UNIQUE (${
|
|
550
|
-
c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
|
|
554
|
+
result += `,\n${childEnv.indent}CONSTRAINT ${renderArtifactName(`${artifactName}_${cn}`)} UNIQUE (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
|
|
551
555
|
}
|
|
552
556
|
}
|
|
553
557
|
result += `${env.indent}\n)`;
|
|
@@ -568,7 +572,7 @@ function toSqlDdl(csn, options) {
|
|
|
568
572
|
if (options.toSql.dialect === 'hana')
|
|
569
573
|
renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
|
|
570
574
|
|
|
571
|
-
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options
|
|
575
|
+
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
|
|
572
576
|
result += ` COMMENT '${getHanaComment(art)}'`;
|
|
573
577
|
|
|
574
578
|
resultObj.hdbtable[artifactName] = result;
|
|
@@ -659,8 +663,7 @@ function toSqlDdl(csn, options) {
|
|
|
659
663
|
if (duplicateChecker)
|
|
660
664
|
duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
|
|
661
665
|
|
|
662
|
-
let result = `${env.indent + quotedElementName} ${
|
|
663
|
-
renderTypeReference(artifactName, elementName, elm)
|
|
666
|
+
let result = `${env.indent + quotedElementName} ${renderTypeReference(artifactName, elementName, elm)
|
|
664
667
|
}${renderNullability(elm, true)}`;
|
|
665
668
|
if (elm.default)
|
|
666
669
|
result += ` DEFAULT ${renderExpr(elm.default, env)}`;
|
|
@@ -956,7 +959,7 @@ function toSqlDdl(csn, options) {
|
|
|
956
959
|
// An empty actual parameter list is rendered as `()`.
|
|
957
960
|
const ref = csn.definitions[path.ref[0].id] || csn.definitions[path.ref[0]];
|
|
958
961
|
if (ref && ref.params) {
|
|
959
|
-
result += `(${renderArgs(path.ref[0]
|
|
962
|
+
result += `(${renderArgs(path.ref[0] || {}, '=>', env, syntax)})`;
|
|
960
963
|
}
|
|
961
964
|
else if ([ 'udf' ].includes(syntax)) {
|
|
962
965
|
// if syntax is user defined function, render empty argument list
|
|
@@ -978,21 +981,23 @@ function toSqlDdl(csn, options) {
|
|
|
978
981
|
* Render function arguments or view parameters (positional if array, named if object/dict),
|
|
979
982
|
* using 'sep' as separator for positional parameters
|
|
980
983
|
*
|
|
981
|
-
* @param {
|
|
984
|
+
* @param {object} node with `args` to render
|
|
982
985
|
* @param {string} sep Separator between args
|
|
983
986
|
* @param {object} env Render environment
|
|
984
987
|
* @param {string} syntax Some magic A2J paramter - for calcview parameter rendering
|
|
985
988
|
* @returns {string} Rendered arguments
|
|
986
989
|
* @throws Throws if args is not an array or object.
|
|
987
990
|
*/
|
|
988
|
-
function renderArgs(
|
|
991
|
+
function renderArgs(node, sep, env, syntax) {
|
|
992
|
+
const args = node.args ? node.args : {};
|
|
989
993
|
// Positional arguments
|
|
990
994
|
if (Array.isArray(args))
|
|
991
995
|
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
992
996
|
|
|
993
997
|
// Named arguments (object/dict)
|
|
994
998
|
else if (typeof args === 'object')
|
|
995
|
-
|
|
999
|
+
// if this is a function param which is not a reference to the model, we must not quote it
|
|
1000
|
+
return Object.keys(args).map(key => `${node.func ? key : decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
996
1001
|
|
|
997
1002
|
|
|
998
1003
|
throw new Error(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1054,7 +1059,7 @@ function toSqlDdl(csn, options) {
|
|
|
1054
1059
|
definitionsDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
|
|
1055
1060
|
let result = `VIEW ${viewName}`;
|
|
1056
1061
|
|
|
1057
|
-
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options
|
|
1062
|
+
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
|
|
1058
1063
|
result += ` COMMENT '${getHanaComment(art)}'`;
|
|
1059
1064
|
|
|
1060
1065
|
result += renderParameterDefinitions(artifactName, art.params);
|
|
@@ -1087,7 +1092,7 @@ function toSqlDdl(csn, options) {
|
|
|
1087
1092
|
for (const pn in params) {
|
|
1088
1093
|
const p = params[pn];
|
|
1089
1094
|
if (p.notNull === true || p.notNull === false)
|
|
1090
|
-
info(
|
|
1095
|
+
info('query-ignoring-param-nullability', [ 'definitions', artifactName, 'params', pn ], { '#': 'sql' });
|
|
1091
1096
|
// do not quote parameter identifiers for naming mode "quoted" / "hdbcds"
|
|
1092
1097
|
// this would be an incompatible change, as non-uppercased, quoted identifiers
|
|
1093
1098
|
// are rejected by the HANA compiler.
|
|
@@ -1151,12 +1156,11 @@ function toSqlDdl(csn, options) {
|
|
|
1151
1156
|
const childEnv = increaseIndent(env);
|
|
1152
1157
|
result += `SELECT${select.distinct ? ' DISTINCT' : ''}`;
|
|
1153
1158
|
// FIXME: We probably also need to consider `excluding` here ?
|
|
1154
|
-
result += `\n${
|
|
1155
|
-
(select.
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
.join(',\n')}\n`;
|
|
1159
|
+
result += `\n${(select.columns || [ '*' ])
|
|
1160
|
+
.filter(col => !(select.mixin || Object.create(null))[firstPathStepId(col.ref)]) // No mixin columns
|
|
1161
|
+
.map(col => renderViewColumn(col, childEnv))
|
|
1162
|
+
.filter(s => s !== '')
|
|
1163
|
+
.join(',\n')}\n`;
|
|
1160
1164
|
result += `${env.indent}FROM ${renderViewSource(artifactName, select.from, env)}`;
|
|
1161
1165
|
if (select.where)
|
|
1162
1166
|
result += `\n${env.indent}WHERE ${renderExpr(select.where, env, true, true)}`;
|
|
@@ -1384,6 +1388,8 @@ function toSqlDdl(csn, options) {
|
|
|
1384
1388
|
// Function call, possibly with args (use '=>' for named args)
|
|
1385
1389
|
else if (x.func) {
|
|
1386
1390
|
const funcName = smartFuncId(prepareIdentifier(x.func), options.toSql.dialect);
|
|
1391
|
+
if (x.xpr)
|
|
1392
|
+
return renderWindowFunction(funcName, x, env);
|
|
1387
1393
|
return renderFunc(funcName, x, options.toSql.dialect, a => renderArgs(a, '=>', env, null));
|
|
1388
1394
|
}
|
|
1389
1395
|
// Nested expression
|
|
@@ -1406,29 +1412,36 @@ function toSqlDdl(csn, options) {
|
|
|
1406
1412
|
throw new Error(`Unknown expression: ${JSON.stringify(x)}`);
|
|
1407
1413
|
}
|
|
1408
1414
|
|
|
1415
|
+
function renderWindowFunction(funcName, node, env) {
|
|
1416
|
+
const suffix = node.xpr.shift(); // OVER
|
|
1417
|
+
let r = `${funcName}(${renderArgs(node, '=>', env, null)})`;
|
|
1418
|
+
r += ` ${suffix} (${renderExpr(node.xpr, env)})`;
|
|
1419
|
+
return r;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1409
1422
|
function renderExpressionLiteral(x) {
|
|
1410
1423
|
// Literal value, possibly with explicit 'literal' property
|
|
1411
1424
|
switch (x.literal || typeof x.val) {
|
|
1412
1425
|
case 'number':
|
|
1413
1426
|
case 'boolean':
|
|
1414
1427
|
case 'null':
|
|
1415
|
-
|
|
1428
|
+
// 17.42, NULL, TRUE
|
|
1416
1429
|
return String(x.val).toUpperCase();
|
|
1417
1430
|
case 'x':
|
|
1418
|
-
|
|
1431
|
+
// x'f000'
|
|
1419
1432
|
return `${x.literal}'${x.val}'`;
|
|
1420
1433
|
case 'date':
|
|
1421
1434
|
case 'time':
|
|
1422
1435
|
case 'timestamp':
|
|
1423
1436
|
if (options.toSql.dialect === 'sqlite') {
|
|
1424
|
-
|
|
1437
|
+
// simple string literal '2017-11-02'
|
|
1425
1438
|
return `'${x.val}'`;
|
|
1426
1439
|
}
|
|
1427
1440
|
// date'2017-11-02'
|
|
1428
1441
|
return `${x.literal}'${x.val}'`;
|
|
1429
1442
|
|
|
1430
1443
|
case 'string':
|
|
1431
|
-
|
|
1444
|
+
// 'foo', with proper escaping
|
|
1432
1445
|
return `'${x.val.replace(/'/g, '\'\'')}'`;
|
|
1433
1446
|
case 'object':
|
|
1434
1447
|
if (x.val === null)
|
|
@@ -1442,7 +1455,12 @@ function toSqlDdl(csn, options) {
|
|
|
1442
1455
|
|
|
1443
1456
|
function renderExpressionRef(x) {
|
|
1444
1457
|
if (!x.param && !x.global) {
|
|
1458
|
+
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1459
|
+
|
|
1445
1460
|
if (x.ref[0] === '$user') {
|
|
1461
|
+
if (magicReplacement !== null)
|
|
1462
|
+
return `'${magicReplacement}'`;
|
|
1463
|
+
|
|
1446
1464
|
const result = render$user(x);
|
|
1447
1465
|
// Invalid second path step doesn't cause a return
|
|
1448
1466
|
if (result)
|
|
@@ -1454,6 +1472,9 @@ function toSqlDdl(csn, options) {
|
|
|
1454
1472
|
if (result)
|
|
1455
1473
|
return result;
|
|
1456
1474
|
}
|
|
1475
|
+
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1476
|
+
return `'${magicReplacement}'`;
|
|
1477
|
+
}
|
|
1457
1478
|
}
|
|
1458
1479
|
// FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
|
|
1459
1480
|
// assume that it was not if the path has length 2 (
|
|
@@ -1476,6 +1497,7 @@ function toSqlDdl(csn, options) {
|
|
|
1476
1497
|
function render$user(x) {
|
|
1477
1498
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1478
1499
|
if (x.ref[1] === 'id') {
|
|
1500
|
+
// Keep the old-style for compatibilty with magicVars.id - instead of magicVars.user.id...
|
|
1479
1501
|
if (options.toSql.user && typeof options.toSql.user === 'string' || options.toSql.user instanceof String)
|
|
1480
1502
|
return `'${options.toSql.user}'`;
|
|
1481
1503
|
|
|
@@ -1483,7 +1505,7 @@ function toSqlDdl(csn, options) {
|
|
|
1483
1505
|
return `'${options.toSql.user.id}'`;
|
|
1484
1506
|
|
|
1485
1507
|
if (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain') {
|
|
1486
|
-
warning(null, null, 'The "$user" variable is not supported. Use
|
|
1508
|
+
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1487
1509
|
return '\'$user.id\'';
|
|
1488
1510
|
}
|
|
1489
1511
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
@@ -1495,7 +1517,7 @@ function toSqlDdl(csn, options) {
|
|
|
1495
1517
|
}
|
|
1496
1518
|
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1497
1519
|
}
|
|
1498
|
-
// Basically: Second path step was invalid, do nothing
|
|
1520
|
+
// Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
|
|
1499
1521
|
return null;
|
|
1500
1522
|
}
|
|
1501
1523
|
/**
|
|
@@ -1593,13 +1615,13 @@ function toSqlDdl(csn, options) {
|
|
|
1593
1615
|
|
|
1594
1616
|
// Not really a path step but an object-like function call
|
|
1595
1617
|
if (s.func)
|
|
1596
|
-
return `${s.func}(${renderArgs(s
|
|
1618
|
+
return `${s.func}(${renderArgs(s, '=>', env, null)})`;
|
|
1597
1619
|
|
|
1598
1620
|
// Path step, possibly with view parameters and/or filters
|
|
1599
1621
|
let result = `${quoteSqlId(s.id)}`;
|
|
1600
1622
|
if (s.args) {
|
|
1601
1623
|
// View parameters
|
|
1602
|
-
result += `(${renderArgs(s
|
|
1624
|
+
result += `(${renderArgs(s, '=>', env, null)})`;
|
|
1603
1625
|
}
|
|
1604
1626
|
if (s.where) {
|
|
1605
1627
|
// Filter, possibly with cardinality
|
|
@@ -33,7 +33,7 @@ const { implicitAs } = require('../../model/csnRefs');
|
|
|
33
33
|
function renderFunc( funcName, node, dialect, renderArgs) {
|
|
34
34
|
if (funcWithoutParen( node, dialect ))
|
|
35
35
|
return funcName;
|
|
36
|
-
return `${funcName}(${renderArgs( node
|
|
36
|
+
return `${funcName}(${renderArgs( node )})`;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|