@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- 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 +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- 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 +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/render/toSql.js
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
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,
|
|
11
|
+
getSqlSnippets,
|
|
11
12
|
} = require('./utils/common');
|
|
12
13
|
const {
|
|
13
14
|
renderReferentialConstraint, getIdentifierUtils,
|
|
@@ -15,10 +16,11 @@ const {
|
|
|
15
16
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
16
17
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
17
18
|
const { makeMessageFunction } = require('../base/messages');
|
|
18
|
-
const timetrace = require('../utils/timetrace');
|
|
19
|
+
const { timetrace } = require('../utils/timetrace');
|
|
19
20
|
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
|
|
20
21
|
const { smartFuncId } = require('../sql-identifier');
|
|
21
22
|
const { sortCsn } = require('../json/to-csn');
|
|
23
|
+
const { manageConstraints } = require('./manageConstraints');
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -185,10 +187,10 @@ function toSqlDdl(csn, options) {
|
|
|
185
187
|
checkCSNVersion(csn, options);
|
|
186
188
|
|
|
187
189
|
// The final result in hdb-kind-specific form, without leading CREATE, without trailing newlines
|
|
188
|
-
// (note that the order here is relevant for transmission into '
|
|
190
|
+
// (note that the order here is relevant for transmission into 'mainResultObj.sql' below and that
|
|
189
191
|
// the attribute names must be the HDI plugin names for --src hdi)
|
|
190
192
|
// The result object may have a `sql` dictionary for `toSql`.
|
|
191
|
-
const
|
|
193
|
+
const mainResultObj = {
|
|
192
194
|
hdbtabletype: Object.create(null),
|
|
193
195
|
hdbtable: Object.create(null),
|
|
194
196
|
hdbindex: Object.create(null),
|
|
@@ -214,13 +216,12 @@ function toSqlDdl(csn, options) {
|
|
|
214
216
|
// Current indentation string
|
|
215
217
|
indent: '',
|
|
216
218
|
};
|
|
217
|
-
renderArtifactInto(artifactName, artifact,
|
|
219
|
+
renderArtifactInto(artifactName, artifact, mainResultObj, env);
|
|
218
220
|
});
|
|
219
221
|
|
|
220
222
|
// Render each deleted artifact
|
|
221
223
|
for (const artifactName in csn.deletions)
|
|
222
|
-
renderArtifactDeletionInto(artifactName, csn.deletions[artifactName],
|
|
223
|
-
|
|
224
|
+
renderArtifactDeletionInto(artifactName, csn.deletions[artifactName], mainResultObj);
|
|
224
225
|
|
|
225
226
|
// Render each artifact extension
|
|
226
227
|
// Only HANA SQL is currently supported.
|
|
@@ -231,7 +232,7 @@ function toSqlDdl(csn, options) {
|
|
|
231
232
|
const artifactName = extension.extend;
|
|
232
233
|
const _artifact = csn.definitions[artifactName];
|
|
233
234
|
const env = { indent: '', _artifact };
|
|
234
|
-
renderArtifactExtensionInto(artifactName, _artifact, extension,
|
|
235
|
+
renderArtifactExtensionInto(artifactName, _artifact, extension, mainResultObj, env);
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
}
|
|
@@ -244,7 +245,7 @@ function toSqlDdl(csn, options) {
|
|
|
244
245
|
const artifactName = migration.migrate;
|
|
245
246
|
const _artifact = csn.definitions[artifactName];
|
|
246
247
|
const env = { indent: '', _artifact };
|
|
247
|
-
renderArtifactMigrationInto(artifactName, migration,
|
|
248
|
+
renderArtifactMigrationInto(artifactName, migration, mainResultObj, env);
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
}
|
|
@@ -265,34 +266,42 @@ function toSqlDdl(csn, options) {
|
|
|
265
266
|
|
|
266
267
|
// Handle hdbKinds separately from alterTable case
|
|
267
268
|
// eslint-disable-next-line no-unused-vars
|
|
268
|
-
const { deletions, migrations, ...hdbKinds } =
|
|
269
|
+
const { deletions, migrations: _, ...hdbKinds } = mainResultObj;
|
|
269
270
|
for (const hdbKind of Object.keys(hdbKinds)) {
|
|
270
|
-
for (const name in
|
|
271
|
+
for (const name in mainResultObj[hdbKind]) {
|
|
271
272
|
if (options.toSql.src === 'sql') {
|
|
272
|
-
let sourceString =
|
|
273
|
+
let sourceString = mainResultObj[hdbKind][name];
|
|
273
274
|
// Hack: Other than in 'hdbtable' files, in HANA SQL COLUMN is not mandatory but default.
|
|
274
275
|
if (options.toSql.dialect === 'hana' && hdbKind === 'hdbtable' && sourceString.startsWith('COLUMN '))
|
|
275
276
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
276
|
-
|
|
277
277
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
278
278
|
}
|
|
279
279
|
else if (!options.testMode) {
|
|
280
|
-
|
|
280
|
+
mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
if (options.toSql.src === 'sql')
|
|
284
|
-
delete
|
|
284
|
+
delete mainResultObj[hdbKind];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// add `ALTER TABLE ADD CONSTRAINT` statements if requested
|
|
288
|
+
if (options.sqlDialect !== 'sqlite' && options.constraintsAsAlter) {
|
|
289
|
+
const alterStmts = manageConstraints(csn, options);
|
|
290
|
+
|
|
291
|
+
for ( const constraintName of Object.keys(alterStmts))
|
|
292
|
+
sql[constraintName] = `${options.testMode ? '' : sqlVersionLine}${alterStmts[constraintName]}`;
|
|
293
|
+
mainResultObj.sql = sql;
|
|
285
294
|
}
|
|
286
295
|
|
|
287
296
|
if (options.toSql.src === 'sql')
|
|
288
|
-
|
|
297
|
+
mainResultObj.sql = sql;
|
|
289
298
|
|
|
290
299
|
for (const name in deletions)
|
|
291
300
|
deletions[name] = `${options.testMode ? '' : sqlVersionLine}${deletions[name]}`;
|
|
292
301
|
|
|
293
302
|
|
|
294
303
|
timetrace.stop();
|
|
295
|
-
return
|
|
304
|
+
return mainResultObj;
|
|
296
305
|
|
|
297
306
|
/**
|
|
298
307
|
* Render an artifact into the appropriate dictionary of 'resultObj'.
|
|
@@ -483,7 +492,9 @@ function toSqlDdl(csn, options) {
|
|
|
483
492
|
env._artifact = art;
|
|
484
493
|
const childEnv = increaseIndent(env);
|
|
485
494
|
const hanaTc = art.technicalConfig && art.technicalConfig.hana;
|
|
486
|
-
|
|
495
|
+
// tables can have @sql.prepend and @sql.append
|
|
496
|
+
const { front, back } = getSqlSnippets(options, art);
|
|
497
|
+
let result = front;
|
|
487
498
|
// Only HANA has row/column tables
|
|
488
499
|
if (options.toSql.dialect === 'hana') {
|
|
489
500
|
if (hanaTc && hanaTc.storeType) {
|
|
@@ -500,13 +511,9 @@ function toSqlDdl(csn, options) {
|
|
|
500
511
|
result += `TABLE ${tableName}`;
|
|
501
512
|
result += ' (\n';
|
|
502
513
|
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 !== '')
|
|
514
|
+
if (elements !== '')
|
|
504
515
|
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
|
-
}
|
|
516
|
+
|
|
510
517
|
const uniqueFields = Object.keys(art.elements).filter(name => art.elements[name].unique && !art.elements[name].virtual)
|
|
511
518
|
.map(name => quoteSqlId(name))
|
|
512
519
|
.join(', ');
|
|
@@ -517,7 +524,8 @@ function toSqlDdl(csn, options) {
|
|
|
517
524
|
if (primaryKeys !== '')
|
|
518
525
|
result += `,\n${childEnv.indent}${primaryKeys}`;
|
|
519
526
|
|
|
520
|
-
|
|
527
|
+
const constraintsAsAlter = options.constraintsAsAlter && options.sqlDialect !== 'sqlite';
|
|
528
|
+
if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
|
|
521
529
|
const renderReferentialConstraintsAsHdbconstraint = options.toSql.src === 'hdi';
|
|
522
530
|
const referentialConstraints = {};
|
|
523
531
|
Object.entries(art.$tableConstraints.referential)
|
|
@@ -567,9 +575,12 @@ function toSqlDdl(csn, options) {
|
|
|
567
575
|
if (options.toSql.dialect === 'hana')
|
|
568
576
|
renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
|
|
569
577
|
|
|
570
|
-
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options
|
|
578
|
+
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
|
|
571
579
|
result += ` COMMENT '${getHanaComment(art)}'`;
|
|
572
580
|
|
|
581
|
+
if (back)
|
|
582
|
+
result += back;
|
|
583
|
+
|
|
573
584
|
resultObj.hdbtable[artifactName] = result;
|
|
574
585
|
}
|
|
575
586
|
|
|
@@ -605,12 +616,12 @@ function toSqlDdl(csn, options) {
|
|
|
605
616
|
if (!(artifactName in resultObj.migrations))
|
|
606
617
|
resultObj.migrations[artifactName] = [];
|
|
607
618
|
|
|
608
|
-
const migrations = sqlArray.map(
|
|
619
|
+
const migrations = sqlArray.map(migrationSql => ({ drop, sql: migrationSql }));
|
|
609
620
|
resultObj.migrations[artifactName].push(...migrations);
|
|
610
621
|
}
|
|
611
622
|
|
|
612
|
-
function addDeletion(resultObj, artifactName,
|
|
613
|
-
resultObj.deletions[artifactName] =
|
|
623
|
+
function addDeletion(resultObj, artifactName, deletionSql) {
|
|
624
|
+
resultObj.deletions[artifactName] = deletionSql;
|
|
614
625
|
}
|
|
615
626
|
|
|
616
627
|
/**
|
|
@@ -667,9 +678,15 @@ function toSqlDdl(csn, options) {
|
|
|
667
678
|
if (fzindex && options.toSql.dialect === 'hana')
|
|
668
679
|
result += ` ${renderExpr(fzindex, env)}`;
|
|
669
680
|
|
|
670
|
-
if (options.toSql.dialect === 'hana' && hasHanaComment(elm, options
|
|
681
|
+
if (options.toSql.dialect === 'hana' && hasHanaComment(elm, options))
|
|
671
682
|
result += ` COMMENT '${getHanaComment(elm)}'`;
|
|
672
683
|
|
|
684
|
+
// (table) elements can only have a @sql.append
|
|
685
|
+
const { back } = getSqlSnippets(options, elm);
|
|
686
|
+
|
|
687
|
+
if (back !== '')
|
|
688
|
+
result += back;
|
|
689
|
+
|
|
673
690
|
return result;
|
|
674
691
|
}
|
|
675
692
|
|
|
@@ -979,7 +996,7 @@ function toSqlDdl(csn, options) {
|
|
|
979
996
|
* @param {object} node with `args` to render
|
|
980
997
|
* @param {string} sep Separator between args
|
|
981
998
|
* @param {object} env Render environment
|
|
982
|
-
* @param {string} syntax Some magic A2J
|
|
999
|
+
* @param {string|null} syntax Some magic A2J parameter - for calcview parameter rendering
|
|
983
1000
|
* @returns {string} Rendered arguments
|
|
984
1001
|
* @throws Throws if args is not an array or object.
|
|
985
1002
|
*/
|
|
@@ -1002,14 +1019,13 @@ function toSqlDdl(csn, options) {
|
|
|
1002
1019
|
* Render the given argument/parameter correctly.
|
|
1003
1020
|
*
|
|
1004
1021
|
* @param {string} arg Argument to render
|
|
1005
|
-
* @param {string}
|
|
1022
|
+
* @param {string|null} parameterSyntax Some magic A2J parameter - for calcview parameter rendering
|
|
1006
1023
|
* @returns {string} Rendered argument
|
|
1007
1024
|
*/
|
|
1008
|
-
function decorateParameter(arg,
|
|
1009
|
-
if (
|
|
1025
|
+
function decorateParameter(arg, parameterSyntax) {
|
|
1026
|
+
if (parameterSyntax === 'calcview')
|
|
1010
1027
|
return `PLACEHOLDER."$$${arg}$$"`;
|
|
1011
1028
|
|
|
1012
|
-
|
|
1013
1029
|
return quoteSqlId(arg);
|
|
1014
1030
|
}
|
|
1015
1031
|
}
|
|
@@ -1054,7 +1070,7 @@ function toSqlDdl(csn, options) {
|
|
|
1054
1070
|
definitionsDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
|
|
1055
1071
|
let result = `VIEW ${viewName}`;
|
|
1056
1072
|
|
|
1057
|
-
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options
|
|
1073
|
+
if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
|
|
1058
1074
|
result += ` COMMENT '${getHanaComment(art)}'`;
|
|
1059
1075
|
|
|
1060
1076
|
result += renderParameterDefinitions(artifactName, art.params);
|
|
@@ -1070,6 +1086,11 @@ function toSqlDdl(csn, options) {
|
|
|
1070
1086
|
result += `${env.indent})`;
|
|
1071
1087
|
}
|
|
1072
1088
|
|
|
1089
|
+
// views can only have a @sql.append
|
|
1090
|
+
const { back } = getSqlSnippets(options, art);
|
|
1091
|
+
if (back)
|
|
1092
|
+
result += back;
|
|
1093
|
+
|
|
1073
1094
|
return result;
|
|
1074
1095
|
}
|
|
1075
1096
|
|
|
@@ -1077,7 +1098,7 @@ function toSqlDdl(csn, options) {
|
|
|
1077
1098
|
* Render the parameter definition of a view if any. Return the parameters in parentheses, or an empty string
|
|
1078
1099
|
*
|
|
1079
1100
|
* @param {string} artifactName Name of the view
|
|
1080
|
-
* @param {
|
|
1101
|
+
* @param {Object} params Dictionary of parameters
|
|
1081
1102
|
* @returns {string} Rendered parameters
|
|
1082
1103
|
*/
|
|
1083
1104
|
function renderParameterDefinitions(artifactName, params) {
|
|
@@ -1087,7 +1108,7 @@ function toSqlDdl(csn, options) {
|
|
|
1087
1108
|
for (const pn in params) {
|
|
1088
1109
|
const p = params[pn];
|
|
1089
1110
|
if (p.notNull === true || p.notNull === false)
|
|
1090
|
-
info(
|
|
1111
|
+
info('query-ignoring-param-nullability', [ 'definitions', artifactName, 'params', pn ], { '#': 'sql' });
|
|
1091
1112
|
// do not quote parameter identifiers for naming mode "quoted" / "hdbcds"
|
|
1092
1113
|
// this would be an incompatible change, as non-uppercased, quoted identifiers
|
|
1093
1114
|
// are rejected by the HANA compiler.
|
|
@@ -1333,27 +1354,27 @@ function toSqlDdl(csn, options) {
|
|
|
1333
1354
|
* (no trailing LF, don't indent if inline)
|
|
1334
1355
|
*
|
|
1335
1356
|
* @todo Reuse this with toCdl
|
|
1336
|
-
* @param {Array|object|string}
|
|
1357
|
+
* @param {Array|object|string} expr Expression to render
|
|
1337
1358
|
* @param {object} env Render environment
|
|
1338
1359
|
* @param {boolean} inline Wether to render the expression inline
|
|
1339
1360
|
* @param {boolean} nestedExpr Wether to treat the expression as nested
|
|
1340
1361
|
* @returns {string} Rendered expression
|
|
1341
1362
|
*/
|
|
1342
|
-
function renderExpr(
|
|
1363
|
+
function renderExpr(expr, env, inline = true, nestedExpr = false) {
|
|
1343
1364
|
// Compound expression
|
|
1344
|
-
if (Array.isArray(
|
|
1345
|
-
const tokens =
|
|
1365
|
+
if (Array.isArray(expr)) {
|
|
1366
|
+
const tokens = expr.map(item => renderExpr(item, env, inline, nestedExpr));
|
|
1346
1367
|
return beautifyExprArray(tokens);
|
|
1347
1368
|
}
|
|
1348
|
-
else if (typeof
|
|
1349
|
-
if (nestedExpr &&
|
|
1350
|
-
return renderExplicitTypeCast(renderExprObject());
|
|
1351
|
-
return renderExprObject();
|
|
1369
|
+
else if (typeof expr === 'object' && expr !== null) {
|
|
1370
|
+
if (nestedExpr && expr.cast && expr.cast.type)
|
|
1371
|
+
return renderExplicitTypeCast(expr, renderExprObject(expr));
|
|
1372
|
+
return renderExprObject(expr);
|
|
1352
1373
|
}
|
|
1353
1374
|
// Not a literal value but part of an operator, function etc - just leave as it is
|
|
1354
1375
|
// FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
|
|
1355
1376
|
|
|
1356
|
-
return String(
|
|
1377
|
+
return String(expr).toUpperCase();
|
|
1357
1378
|
|
|
1358
1379
|
|
|
1359
1380
|
/**
|
|
@@ -1361,7 +1382,7 @@ function toSqlDdl(csn, options) {
|
|
|
1361
1382
|
*
|
|
1362
1383
|
* @returns {string} String representation of the expression
|
|
1363
1384
|
*/
|
|
1364
|
-
function renderExprObject() {
|
|
1385
|
+
function renderExprObject(x) {
|
|
1365
1386
|
if (x.list) {
|
|
1366
1387
|
return `(${x.list.map(item => renderExpr(item)).join(', ')})`;
|
|
1367
1388
|
}
|
|
@@ -1383,6 +1404,8 @@ function toSqlDdl(csn, options) {
|
|
|
1383
1404
|
// Function call, possibly with args (use '=>' for named args)
|
|
1384
1405
|
else if (x.func) {
|
|
1385
1406
|
const funcName = smartFuncId(prepareIdentifier(x.func), options.toSql.dialect);
|
|
1407
|
+
if (x.xpr)
|
|
1408
|
+
return renderWindowFunction(funcName, x, env);
|
|
1386
1409
|
return renderFunc(funcName, x, options.toSql.dialect, a => renderArgs(a, '=>', env, null));
|
|
1387
1410
|
}
|
|
1388
1411
|
// Nested expression
|
|
@@ -1405,6 +1428,13 @@ function toSqlDdl(csn, options) {
|
|
|
1405
1428
|
throw new Error(`Unknown expression: ${JSON.stringify(x)}`);
|
|
1406
1429
|
}
|
|
1407
1430
|
|
|
1431
|
+
function renderWindowFunction(funcName, node, fctEnv) {
|
|
1432
|
+
const suffix = node.xpr[0]; // OVER
|
|
1433
|
+
let r = `${funcName}(${renderArgs(node, '=>', fctEnv, null)})`;
|
|
1434
|
+
r += ` ${suffix} (${renderExpr(node.xpr.slice(1), fctEnv)})`; // do not pass suffix in renderExpr
|
|
1435
|
+
return r;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1408
1438
|
function renderExpressionLiteral(x) {
|
|
1409
1439
|
// Literal value, possibly with explicit 'literal' property
|
|
1410
1440
|
switch (x.literal || typeof x.val) {
|
|
@@ -1441,7 +1471,12 @@ function toSqlDdl(csn, options) {
|
|
|
1441
1471
|
|
|
1442
1472
|
function renderExpressionRef(x) {
|
|
1443
1473
|
if (!x.param && !x.global) {
|
|
1474
|
+
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1475
|
+
|
|
1444
1476
|
if (x.ref[0] === '$user') {
|
|
1477
|
+
if (magicReplacement !== null)
|
|
1478
|
+
return `'${magicReplacement}'`;
|
|
1479
|
+
|
|
1445
1480
|
const result = render$user(x);
|
|
1446
1481
|
// Invalid second path step doesn't cause a return
|
|
1447
1482
|
if (result)
|
|
@@ -1453,6 +1488,9 @@ function toSqlDdl(csn, options) {
|
|
|
1453
1488
|
if (result)
|
|
1454
1489
|
return result;
|
|
1455
1490
|
}
|
|
1491
|
+
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1492
|
+
return `'${magicReplacement}'`;
|
|
1493
|
+
}
|
|
1456
1494
|
}
|
|
1457
1495
|
// FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
|
|
1458
1496
|
// assume that it was not if the path has length 2 (
|
|
@@ -1475,6 +1513,7 @@ function toSqlDdl(csn, options) {
|
|
|
1475
1513
|
function render$user(x) {
|
|
1476
1514
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1477
1515
|
if (x.ref[1] === 'id') {
|
|
1516
|
+
// Keep the old-style for compatibilty with magicVars.id - instead of magicVars.user.id...
|
|
1478
1517
|
if (options.toSql.user && typeof options.toSql.user === 'string' || options.toSql.user instanceof String)
|
|
1479
1518
|
return `'${options.toSql.user}'`;
|
|
1480
1519
|
|
|
@@ -1482,7 +1521,7 @@ function toSqlDdl(csn, options) {
|
|
|
1482
1521
|
return `'${options.toSql.user.id}'`;
|
|
1483
1522
|
|
|
1484
1523
|
if (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain') {
|
|
1485
|
-
warning(null, null, 'The "$user" variable is not supported. Use
|
|
1524
|
+
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1486
1525
|
return '\'$user.id\'';
|
|
1487
1526
|
}
|
|
1488
1527
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
@@ -1494,7 +1533,7 @@ function toSqlDdl(csn, options) {
|
|
|
1494
1533
|
}
|
|
1495
1534
|
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1496
1535
|
}
|
|
1497
|
-
// Basically: Second path step was invalid, do nothing
|
|
1536
|
+
// Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
|
|
1498
1537
|
return null;
|
|
1499
1538
|
}
|
|
1500
1539
|
/**
|
|
@@ -1547,10 +1586,11 @@ function toSqlDdl(csn, options) {
|
|
|
1547
1586
|
/**
|
|
1548
1587
|
* Renders an explicit `cast()` inside an 'xpr'.
|
|
1549
1588
|
*
|
|
1589
|
+
* @param {object} x Expression with cast
|
|
1550
1590
|
* @param {string} value Value to cast
|
|
1551
1591
|
* @returns {string} CAST statement
|
|
1552
1592
|
*/
|
|
1553
|
-
function renderExplicitTypeCast(value) {
|
|
1593
|
+
function renderExplicitTypeCast(x, value) {
|
|
1554
1594
|
const typeRef = renderBuiltinType(x.cast.type) + renderTypeParameters(x.cast);
|
|
1555
1595
|
return `CAST(${value} AS ${typeRef})`;
|
|
1556
1596
|
}
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
} = require('../../model/csnUtils');
|
|
20
20
|
|
|
21
21
|
const { implicitAs } = require('../../model/csnRefs');
|
|
22
|
+
const { isBetaEnabled } = require('../../base/model');
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -205,12 +206,11 @@ function addMissingChildContexts(csn, artifactName, killList) {
|
|
|
205
206
|
addPossibleGaps(name.slice(artifactName.length + 1).split('.'), artifactName);
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
function addPossibleGaps(possibleGaps,
|
|
209
|
-
let possibleGap = artifactName;
|
|
209
|
+
function addPossibleGaps(possibleGaps, gapArtifactName) {
|
|
210
210
|
for (const gap of possibleGaps) {
|
|
211
|
-
|
|
212
|
-
if (!csn.definitions[
|
|
213
|
-
const contextName =
|
|
211
|
+
gapArtifactName += `.${gap}`;
|
|
212
|
+
if (!csn.definitions[gapArtifactName]) {
|
|
213
|
+
const contextName = gapArtifactName;
|
|
214
214
|
csn.definitions[contextName] = {
|
|
215
215
|
kind: 'context',
|
|
216
216
|
};
|
|
@@ -355,6 +355,24 @@ function getHanaComment(obj) {
|
|
|
355
355
|
return obj.doc.split('\n\n')[0].trim().replace(/'/g, "''");
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Get the @sql.prepend/append if set - already add a space after/before.
|
|
360
|
+
* If no value is set, use '';
|
|
361
|
+
*
|
|
362
|
+
* @param {CSN.Options} options
|
|
363
|
+
* @param {object} obj
|
|
364
|
+
* @returns {object} object with .front and .back
|
|
365
|
+
*/
|
|
366
|
+
function getSqlSnippets(options, obj) {
|
|
367
|
+
if (isBetaEnabled(options, 'sqlSnippets')) {
|
|
368
|
+
const front = obj['@sql.prepend'] ? `${obj['@sql.prepend']} ` : '';
|
|
369
|
+
const back = obj['@sql.append'] ? ` ${obj['@sql.append']}` : '';
|
|
370
|
+
|
|
371
|
+
return { front, back };
|
|
372
|
+
}
|
|
373
|
+
return { front: '', back: '' };
|
|
374
|
+
}
|
|
375
|
+
|
|
358
376
|
/**
|
|
359
377
|
* @typedef CdlRenderEnvironment Rendering environment used throughout the render process.
|
|
360
378
|
*
|
|
@@ -383,4 +401,5 @@ module.exports = {
|
|
|
383
401
|
getHanaComment,
|
|
384
402
|
findElement,
|
|
385
403
|
funcWithoutParen,
|
|
404
|
+
getSqlSnippets,
|
|
386
405
|
};
|
package/lib/render/utils/sql.js
CHANGED
|
@@ -13,11 +13,11 @@ const { smartId, delimitedId } = require('../../sql-identifier');
|
|
|
13
13
|
* @param {boolean} toUpperCase Wether to uppercase the identifier
|
|
14
14
|
* @param {CSN.Model} csn CSN
|
|
15
15
|
* @param {CSN.Options} options is needed for the naming mode and the sql dialect
|
|
16
|
-
* @param {boolean} alterConstraint whether the constraint should be rendered as part of an ALTER TABLE statement
|
|
16
|
+
* @param {boolean} [alterConstraint=false] whether the constraint should be rendered as part of an ALTER TABLE statement
|
|
17
17
|
*
|
|
18
18
|
* @returns {string} SQL statement which can be used to create the referential constraint on the db.
|
|
19
19
|
*/
|
|
20
|
-
function renderReferentialConstraint(constraint, indent, toUpperCase, csn, options, alterConstraint) {
|
|
20
|
+
function renderReferentialConstraint(constraint, indent, toUpperCase, csn, options, alterConstraint = false) {
|
|
21
21
|
let quoteId;
|
|
22
22
|
// for to.hana we can't utilize the sql identifier utils
|
|
23
23
|
if (options.transformation === 'hdbcds') {
|
|
@@ -51,14 +51,16 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
|
|
|
51
51
|
if (!alterConstraint) {
|
|
52
52
|
result += `${indent}FOREIGN KEY(${constraint.foreignKey.map(quoteId).join(', ')})\n`;
|
|
53
53
|
result += `${indent}REFERENCES ${quoteId(getResultingName(csn, names, constraint.parentTable))}(${constraint.parentKey.map(quoteId).join(', ')})\n`;
|
|
54
|
+
const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
|
|
55
|
+
|
|
54
56
|
// omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
|
|
55
57
|
if (forSqlite) {
|
|
56
58
|
if (constraint.onDelete === 'CASCADE' )
|
|
57
|
-
result += `${indent}ON DELETE ${constraint.onDelete}${
|
|
59
|
+
result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
62
|
result += `${indent}ON UPDATE RESTRICT\n`;
|
|
61
|
-
result += `${indent}ON DELETE ${constraint.onDelete}${
|
|
63
|
+
result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
// constraint enforcement / validation must be switched off using sqlite pragma statement
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
function isAlreadyBraced(expression, start, end){
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
return start - 1 > -1 &&
|
|
5
|
+
end + 1 < expression.length &&
|
|
6
|
+
expression[start-1] === '(' &&
|
|
7
|
+
expression[end+1] === ')';
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
function binarycomparison(expression, token, index){
|