@sap/cds-compiler 4.0.0 → 4.1.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 +115 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +60 -12
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +30 -2
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +12 -5
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/model/sortViews.js +4 -2
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +56 -42
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -101,26 +101,28 @@ function manageConstraints( csn, options ) {
|
|
|
101
101
|
forEachDefinition(csn, (artifact) => {
|
|
102
102
|
if (artifact.$tableConstraints?.referential) {
|
|
103
103
|
forEach(artifact.$tableConstraints.referential, (fileName, constraint) => {
|
|
104
|
-
|
|
105
|
-
const renderedConstraint = renderReferentialConstraint(constraint, indent, false, csn, options, renderAlterConstraintStatement);
|
|
106
|
-
if (options.src === 'hdi' && !options.drop) {
|
|
107
|
-
resultArtifacts[fileName] = renderedConstraint;
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
let alterTableStatement = '';
|
|
111
|
-
alterTableStatement += `${indent}ALTER TABLE ${quoteSqlId(getResultingName(csn, options.sqlMapping, constraint.dependentTable))}`;
|
|
112
|
-
if (renderAlterConstraintStatement)
|
|
113
|
-
alterTableStatement += `\n${indent}ALTER ${renderedConstraint};`;
|
|
114
|
-
else if (options.drop)
|
|
115
|
-
alterTableStatement += `${indent} DROP CONSTRAINT ${quoteSqlId(constraint.identifier)};`;
|
|
116
|
-
else
|
|
117
|
-
alterTableStatement += `\n${indent}ADD ${renderedConstraint};`;
|
|
118
|
-
|
|
119
|
-
resultArtifacts[fileName] = alterTableStatement;
|
|
104
|
+
resultArtifacts[fileName] = manageConstraint(constraint, csn, options, indent, quoteSqlId);
|
|
120
105
|
});
|
|
121
106
|
}
|
|
122
107
|
});
|
|
123
|
-
return
|
|
108
|
+
return resultArtifacts;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function manageConstraint( constraint, csn, options, indent, quoteSqlId ) {
|
|
112
|
+
const renderAlterConstraintStatement = options.alter && options.src !== 'hdi';
|
|
113
|
+
const renderedConstraint = renderReferentialConstraint(constraint, indent, false, csn, options, renderAlterConstraintStatement);
|
|
114
|
+
if (options.src === 'hdi' && !options.drop)
|
|
115
|
+
return renderedConstraint;
|
|
116
|
+
let alterTableStatement = '';
|
|
117
|
+
alterTableStatement += `${indent}ALTER TABLE ${quoteSqlId(getResultingName(csn, options.sqlMapping, constraint.dependentTable))}`;
|
|
118
|
+
if (renderAlterConstraintStatement)
|
|
119
|
+
alterTableStatement += `\n${indent}ALTER ${renderedConstraint};`;
|
|
120
|
+
else if (options.drop)
|
|
121
|
+
alterTableStatement += `${indent} DROP CONSTRAINT ${quoteSqlId(constraint.identifier)};`;
|
|
122
|
+
else
|
|
123
|
+
alterTableStatement += `\n${indent}ADD ${renderedConstraint};`;
|
|
124
|
+
|
|
125
|
+
return alterTableStatement;
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
/**
|
|
@@ -264,5 +266,6 @@ function getListOfAllConstraints( csn ) {
|
|
|
264
266
|
module.exports = {
|
|
265
267
|
alterConstraintsWithCsn,
|
|
266
268
|
manageConstraints,
|
|
269
|
+
manageConstraint,
|
|
267
270
|
listReferentialIntegrityViolations,
|
|
268
271
|
};
|
package/lib/render/toCdl.js
CHANGED
|
@@ -126,7 +126,7 @@ function csnToCdl( csn, options ) {
|
|
|
126
126
|
return result;
|
|
127
127
|
|
|
128
128
|
function renderVocabulariesEntry( name, anno ) {
|
|
129
|
-
if (!anno
|
|
129
|
+
if (!anno.$ignore) {
|
|
130
130
|
// This environment is passed down the call hierarchy, for dealing with
|
|
131
131
|
// indentation and name resolution issues
|
|
132
132
|
const env = createEnv({ path: [ 'vocabularies', name ] });
|
|
@@ -512,8 +512,15 @@ function csnToCdl( csn, options ) {
|
|
|
512
512
|
result += renderParameters(art, env);
|
|
513
513
|
if (art.includes)
|
|
514
514
|
result += renderIncludes(art.includes, env);
|
|
515
|
-
|
|
515
|
+
|
|
516
|
+
if (art.elements)
|
|
517
|
+
result += ` ${renderElements(art, env)}`;
|
|
518
|
+
else if (art.actions)
|
|
519
|
+
// if there are no elements, but actions, CDL syntax requires braces.
|
|
520
|
+
result += ' { }';
|
|
521
|
+
|
|
516
522
|
result += `${renderActionsAndFunctions(art, env)};\n`;
|
|
523
|
+
|
|
517
524
|
return result;
|
|
518
525
|
}
|
|
519
526
|
|
|
@@ -565,7 +572,7 @@ function csnToCdl( csn, options ) {
|
|
|
565
572
|
* Returns the resulting source string.
|
|
566
573
|
*
|
|
567
574
|
* @param {string} elementName
|
|
568
|
-
* @param {CSN.Element
|
|
575
|
+
* @param {CSN.Element} element
|
|
569
576
|
* @param {CdlRenderEnvironment} env
|
|
570
577
|
*/
|
|
571
578
|
function renderElement( elementName, element, env ) {
|
|
@@ -1037,7 +1044,7 @@ function csnToCdl( csn, options ) {
|
|
|
1037
1044
|
result += `${env.indent}}`;
|
|
1038
1045
|
}
|
|
1039
1046
|
|
|
1040
|
-
if (isLeadingQuery)
|
|
1047
|
+
if (isLeadingQuery && query.actions)
|
|
1041
1048
|
result += renderActionsAndFunctions(query, env);
|
|
1042
1049
|
|
|
1043
1050
|
if (select.where)
|
|
@@ -1148,14 +1155,14 @@ function csnToCdl( csn, options ) {
|
|
|
1148
1155
|
*/
|
|
1149
1156
|
function renderActionsAndFunctions( art, env ) {
|
|
1150
1157
|
let result = '';
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1158
|
+
if (art.actions) {
|
|
1159
|
+
const childEnv = env.withIncreasedIndent();
|
|
1160
|
+
for (const name in art.actions)
|
|
1161
|
+
result += renderActionOrFunction(name, art.actions[name], childEnv.withSubPath([ 'actions', name ]));
|
|
1162
|
+
result = (result === '')
|
|
1163
|
+
? ' actions { }'
|
|
1164
|
+
: ` actions {\n${result}${env.indent}}`;
|
|
1165
|
+
}
|
|
1159
1166
|
return result;
|
|
1160
1167
|
}
|
|
1161
1168
|
|
|
@@ -1232,10 +1239,15 @@ function csnToCdl( csn, options ) {
|
|
|
1232
1239
|
if (art.includes)
|
|
1233
1240
|
result += renderIncludes(art.includes, env);
|
|
1234
1241
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
result += ` : ${
|
|
1242
|
+
const type = renderTypeReferenceAndProps(art, env);
|
|
1243
|
+
if (type) {
|
|
1244
|
+
// For nicer output, no colon if unnamed structure is used.
|
|
1245
|
+
result += (!art.type && art.elements) ? ` ${type}` : ` : ${type}`;
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
msg.warning('syntax-missing-type', env.path, { name: artifactName },
|
|
1249
|
+
'Missing type for definition $(NAME); can\'t be represented in CDL');
|
|
1250
|
+
}
|
|
1239
1251
|
result += ';\n';
|
|
1240
1252
|
return result;
|
|
1241
1253
|
}
|
|
@@ -1310,8 +1322,9 @@ function csnToCdl( csn, options ) {
|
|
|
1310
1322
|
|
|
1311
1323
|
if (artifact.notNull !== undefined && !artifact.on) // unmanaged associations can't be followed by "not null"
|
|
1312
1324
|
result += renderNullability(artifact);
|
|
1313
|
-
// DEFAULT not possible here.
|
|
1314
1325
|
|
|
1326
|
+
if (artifact.default && !artifact.on)
|
|
1327
|
+
result += ` default ${exprRenderer.renderExpr(artifact.default, env.withSubPath([ 'default' ]))}`;
|
|
1315
1328
|
return result;
|
|
1316
1329
|
}
|
|
1317
1330
|
|
|
@@ -1814,7 +1827,7 @@ function csnToCdl( csn, options ) {
|
|
|
1814
1827
|
// Take the annotation assignment apart into <nameBeforeVariant>#<variantAndRest>
|
|
1815
1828
|
const parts = name.split('#');
|
|
1816
1829
|
const nameBeforeVariant = parts[0];
|
|
1817
|
-
const variant = parts
|
|
1830
|
+
const variant = parts.length > 1 ? parts.slice(1).join('#') : undefined;
|
|
1818
1831
|
const { parentheses } = config;
|
|
1819
1832
|
|
|
1820
1833
|
let result = `${env.indent}@`;
|
|
@@ -1822,10 +1835,11 @@ function csnToCdl( csn, options ) {
|
|
|
1822
1835
|
result += '(';
|
|
1823
1836
|
|
|
1824
1837
|
result += quoteAnnotationPathIfRequired(nameBeforeVariant, env);
|
|
1825
|
-
if (variant !== undefined)
|
|
1838
|
+
if (variant !== undefined) {
|
|
1826
1839
|
// Unfortunately, the compiler does not allow `.@` after the first variant identifier,
|
|
1827
|
-
// so we're back at simple paths.
|
|
1840
|
+
// nor multiple `#`, so we're back at simple paths that are possibly quoted.
|
|
1828
1841
|
result += `#${quotePathIfRequired(variant, env)}`;
|
|
1842
|
+
}
|
|
1829
1843
|
result += ` : ${renderAnnotationValue(anno, env)}`;
|
|
1830
1844
|
|
|
1831
1845
|
if (parentheses)
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -647,7 +647,7 @@ function toHdbcdsSource( csn, options ) {
|
|
|
647
647
|
// Because we already emit an error that calc-on-write is not supported, just ignore nullability/default.
|
|
648
648
|
if (!elm.value?.stored) {
|
|
649
649
|
result += renderNullability(elm);
|
|
650
|
-
if (elm.default)
|
|
650
|
+
if (elm.default && !elm.target)
|
|
651
651
|
result += ` default ${renderExpr(elm.default, env.withSubPath([ 'default' ]))}`;
|
|
652
652
|
}
|
|
653
653
|
|
|
@@ -798,7 +798,7 @@ function toHdbcdsSource( csn, options ) {
|
|
|
798
798
|
|
|
799
799
|
const key = (!env.skipKeys && (col.key || element?.key) ? 'key ' : '');
|
|
800
800
|
result += key + renderExpr(withoutCast(col), env);
|
|
801
|
-
let alias = col.as || col.func;
|
|
801
|
+
let alias = col.as || (!col.args && col.func); // func: e.g. CURRENT_TIMESTAMP
|
|
802
802
|
// HANA requires an alias for 'key' columns just for syntactical reasons
|
|
803
803
|
// FIXME: This will not complain for non-refs (but that should be checked in forRelationalDB)
|
|
804
804
|
// Explicit or implicit alias?
|
package/lib/render/toRename.js
CHANGED
|
@@ -5,15 +5,11 @@ const { makeMessageFunction } = require('../base/messages');
|
|
|
5
5
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
6
6
|
const { forEachDefinition } = require('../model/csnUtils');
|
|
7
7
|
const { optionProcessor } = require('../optionProcessor');
|
|
8
|
-
const { isBetaEnabled } = require('../base/model');
|
|
9
8
|
const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
|
|
10
9
|
const { getIdentifierUtils } = require('./utils/sql');
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* FIXME: Not yet supported, only in beta mode
|
|
15
|
-
* FIXME: This is not up-to-date in regards to the changes to hdbcds/sql quoting etc.
|
|
16
|
-
*
|
|
17
13
|
* Generate SQL DDL rename statements for a migration, renaming existing tables and their
|
|
18
14
|
* columns so that they match the result of "toHana" or "toSql" with the 'plain' option for names.
|
|
19
15
|
* Expects the naming convention of the existing tables to be either 'quoted' or 'hdbcds' (default).
|
|
@@ -32,7 +28,7 @@ const { getIdentifierUtils } = require('./utils/sql');
|
|
|
32
28
|
* @returns {object} A dictionary of name: rename statement
|
|
33
29
|
*/
|
|
34
30
|
function toRename( inputCsn, options ) {
|
|
35
|
-
const {
|
|
31
|
+
const { warning, throwWithError } = makeMessageFunction(inputCsn, options, 'to.rename');
|
|
36
32
|
|
|
37
33
|
// Merge options with defaults.
|
|
38
34
|
options = Object.assign({ sqlMapping: 'hdbcds', sqlDialect: 'hana' }, options);
|
|
@@ -41,9 +37,8 @@ function toRename( inputCsn, options ) {
|
|
|
41
37
|
optionProcessor.verifyOptions(options, 'toRename').forEach(complaint => warning(null, null, `${complaint}`));
|
|
42
38
|
checkCSNVersion(inputCsn, options);
|
|
43
39
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
error(null, null, 'Generation of SQL rename statements is not supported yet (only in beta mode)');
|
|
40
|
+
// Let users know that this is internal
|
|
41
|
+
warning(null, null, 'Generation of SQL rename statements is a beta feature and might change in the future');
|
|
47
42
|
|
|
48
43
|
// FIXME: Currently, 'toRename' implies transformation for HANA (transferring the options to forRelationalDB)
|
|
49
44
|
const csn = transformForRelationalDBWithCsn(inputCsn, options, 'to.rename');
|
|
@@ -102,7 +97,7 @@ function toRename( inputCsn, options ) {
|
|
|
102
97
|
const beforeColumnName = hdbcdsOrQuotedIdentifiers.quoteSqlId(name);
|
|
103
98
|
const afterColumnName = plainIdentifiers.quoteSqlId(name);
|
|
104
99
|
|
|
105
|
-
if (!e
|
|
100
|
+
if (!e.$ignore) {
|
|
106
101
|
if (e.target)
|
|
107
102
|
str = ` EXEC 'ALTER TABLE ${afterTableName} DROP ASSOCIATION ${beforeColumnName}';\n`;
|
|
108
103
|
else if (beforeColumnName.toUpperCase() === `"${afterColumnName}"` ) // Basically a no-op - render commented out
|
package/lib/render/toSql.js
CHANGED
|
@@ -24,7 +24,8 @@ const { timetrace } = require('../utils/timetrace');
|
|
|
24
24
|
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
|
|
25
25
|
const { smartFuncId } = require('../sql-identifier');
|
|
26
26
|
const { sortCsn } = require('../json/to-csn');
|
|
27
|
-
const { manageConstraints } = require('./manageConstraints');
|
|
27
|
+
const { manageConstraints, manageConstraint } = require('./manageConstraints');
|
|
28
|
+
const { renderUniqueConstraintString, renderUniqueConstraintDrop, renderUniqueConstraintAdd } = require('./utils/unique');
|
|
28
29
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
29
30
|
|
|
30
31
|
class SqlRenderEnvironment {
|
|
@@ -105,6 +106,7 @@ function toSqlDdl( csn, options ) {
|
|
|
105
106
|
error, warning, info, throwWithAnyError,
|
|
106
107
|
} = makeMessageFunction(csn, options, 'to.sql');
|
|
107
108
|
const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options);
|
|
109
|
+
let reportedMissingUserReplacement = false;
|
|
108
110
|
|
|
109
111
|
const exprRenderer = createExpressionRenderer({
|
|
110
112
|
// FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
|
|
@@ -175,6 +177,7 @@ function toSqlDdl( csn, options ) {
|
|
|
175
177
|
hdbview: Object.create(null),
|
|
176
178
|
hdbconstraint: Object.create(null),
|
|
177
179
|
deletions: Object.create(null),
|
|
180
|
+
constraintDeletions: [],
|
|
178
181
|
migrations: Object.create(null),
|
|
179
182
|
};
|
|
180
183
|
|
|
@@ -240,8 +243,10 @@ function toSqlDdl( csn, options ) {
|
|
|
240
243
|
const sqlVersionLine = `-- ${generatedByCompilerVersion()}\n`;
|
|
241
244
|
|
|
242
245
|
// Handle hdbKinds separately from alterTable case
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
const {
|
|
247
|
+
// eslint-disable-next-line no-unused-vars
|
|
248
|
+
deletions, constraintDeletions, migrations: _, ...hdbKinds
|
|
249
|
+
} = mainResultObj;
|
|
245
250
|
for (const hdbKind of Object.keys(hdbKinds)) {
|
|
246
251
|
for (const name in mainResultObj[hdbKind]) {
|
|
247
252
|
if (options.src === 'sql') {
|
|
@@ -260,14 +265,15 @@ function toSqlDdl( csn, options ) {
|
|
|
260
265
|
}
|
|
261
266
|
|
|
262
267
|
// add `ALTER TABLE ADD CONSTRAINT` statements per default for `to.sql` w/ dialect `hana` / `postgres`
|
|
263
|
-
// TODO `ALTER TABLE ADD CONSTRAINT` statements also for sqlite constraints
|
|
264
268
|
if (!options.constraintsInCreateTable && options.src === 'sql' && (options.sqlDialect === 'hana' || options.sqlDialect === 'postgres' /* || options.sqlDialect === 'sqlite' */)) {
|
|
269
|
+
const constraints = Object.create(null);
|
|
265
270
|
const alterStmts = manageConstraints(csn, options);
|
|
266
271
|
|
|
267
272
|
forEachKey(alterStmts, (constraintName) => {
|
|
268
|
-
|
|
273
|
+
if (!csn.unchangedConstraints?.has(constraintName))
|
|
274
|
+
constraints[constraintName] = `${options.testMode ? '' : sqlVersionLine}${alterStmts[constraintName]}`;
|
|
269
275
|
});
|
|
270
|
-
mainResultObj.
|
|
276
|
+
mainResultObj.constraints = constraints;
|
|
271
277
|
}
|
|
272
278
|
|
|
273
279
|
if (options.src === 'sql')
|
|
@@ -333,8 +339,12 @@ function toSqlDdl( csn, options ) {
|
|
|
333
339
|
function renderArtifactExtensionInto( artifactName, artifact, ext, resultObj, env ) {
|
|
334
340
|
// Property kind is always omitted for elements and can be omitted for
|
|
335
341
|
// top-level type definitions, it does not exist for extensions.
|
|
336
|
-
if (artifactName && !ext.query)
|
|
337
|
-
|
|
342
|
+
if (artifactName && !ext.query) {
|
|
343
|
+
if (ext.constraint)
|
|
344
|
+
renderConstraintExtendInto(artifactName, ext, resultObj);
|
|
345
|
+
else
|
|
346
|
+
renderExtendInto(artifactName, artifact.elements, ext.elements, resultObj, env, extensionsDuplicateChecker);
|
|
347
|
+
}
|
|
338
348
|
|
|
339
349
|
if (!artifactName)
|
|
340
350
|
throw new ModelError(`Undefined artifact name: ${artifactName}`);
|
|
@@ -421,6 +431,24 @@ function toSqlDdl( csn, options ) {
|
|
|
421
431
|
}
|
|
422
432
|
}
|
|
423
433
|
|
|
434
|
+
if (migration.removeConstraints) {
|
|
435
|
+
const constraintTypes = [ 'unique', 'referential' ];
|
|
436
|
+
constraintTypes.forEach((constraintType) => {
|
|
437
|
+
if (migration.removeConstraints[constraintType]) {
|
|
438
|
+
const entries = Object.entries(migration.removeConstraints[constraintType]);
|
|
439
|
+
const optionsWithDrop = { ...options, drop: true };
|
|
440
|
+
let renderer;
|
|
441
|
+
if (constraintType === 'referential')
|
|
442
|
+
renderer = constraint => manageConstraint(constraint, csn, optionsWithDrop, '', quoteSqlId);
|
|
443
|
+
else
|
|
444
|
+
renderer = (constraint, constraintName) => renderUniqueConstraintDrop(constraint, renderArtifactName(`${artifactName}_${constraintName}`), tableName, quoteSqlId);
|
|
445
|
+
entries.forEach(( [ constraintName, constraint ]) => {
|
|
446
|
+
addConstraintDeletion(resultObj, constraint.parentTable, renderer(constraint, constraintName));
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
424
452
|
// Change column types (unsupported in sqlite)
|
|
425
453
|
if (migration.change) {
|
|
426
454
|
changeElementsDuplicateChecker.addArtifact(tableName, undefined, artifactName);
|
|
@@ -550,16 +578,11 @@ function toSqlDdl( csn, options ) {
|
|
|
550
578
|
// OR create a unique index for HDI
|
|
551
579
|
const uniqueConstraints = art.$tableConstraints && art.$tableConstraints.unique;
|
|
552
580
|
for (const cn in uniqueConstraints) {
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
= `UNIQUE INVERTED INDEX ${cnName} ON ${tableName} (${refs})`;
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
result += `,\n${childEnv.indent}CONSTRAINT ${cnName} UNIQUE (${refs})`;
|
|
562
|
-
}
|
|
581
|
+
const constraint = renderUniqueConstraintString(uniqueConstraints[cn], renderArtifactName(`${artifactName}_${cn}`), tableName, quoteSqlId, options);
|
|
582
|
+
if (options.src === 'hdi')
|
|
583
|
+
resultObj.hdbindex[`${artifactName}.${cn}`] = constraint;
|
|
584
|
+
else
|
|
585
|
+
result += `,\n${childEnv.indent}${constraint}`;
|
|
563
586
|
}
|
|
564
587
|
result += `${env.indent}\n)`;
|
|
565
588
|
|
|
@@ -591,6 +614,21 @@ function toSqlDdl( csn, options ) {
|
|
|
591
614
|
resultObj.hdbtable[artifactName] = result;
|
|
592
615
|
}
|
|
593
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Render an extended entity constraint into the appropriate dictionaries of 'resultObj'.
|
|
619
|
+
* Only SAP HANA SQL is currently supported.
|
|
620
|
+
*
|
|
621
|
+
* @param {string} artifactName Name of the artifact to render
|
|
622
|
+
* @param {object} ext Constraint comprising the extension
|
|
623
|
+
* @param {object} resultObj Result collector
|
|
624
|
+
*/
|
|
625
|
+
function renderConstraintExtendInto( artifactName, { constraint, constraintName, constraintType }, resultObj ) {
|
|
626
|
+
const result = constraintType === 'unique' ? renderUniqueConstraintAdd(constraint, renderArtifactName(`${artifactName}_${constraintName}`), renderArtifactName(constraint.parentTable), quoteSqlId, options)
|
|
627
|
+
: manageConstraint(constraint, csn, options, '', quoteSqlId);
|
|
628
|
+
|
|
629
|
+
addMigration(resultObj, artifactName, false, [ result ]);
|
|
630
|
+
}
|
|
631
|
+
|
|
594
632
|
|
|
595
633
|
/**
|
|
596
634
|
* Render an extended entity into the appropriate dictionaries of 'resultObj'.
|
|
@@ -632,6 +670,9 @@ function toSqlDdl( csn, options ) {
|
|
|
632
670
|
resultObj.migrations[artifactName].push(...migrations);
|
|
633
671
|
}
|
|
634
672
|
|
|
673
|
+
function addConstraintDeletion( resultObj, artifactName, deletionSql ) {
|
|
674
|
+
resultObj.constraintDeletions.push(deletionSql);
|
|
675
|
+
}
|
|
635
676
|
function addDeletion( resultObj, artifactName, deletionSql ) {
|
|
636
677
|
resultObj.deletions[artifactName] = deletionSql;
|
|
637
678
|
}
|
|
@@ -1064,7 +1105,7 @@ function toSqlDdl( csn, options ) {
|
|
|
1064
1105
|
result = env.indent + renderExpr(withoutCast(col), env);
|
|
1065
1106
|
if (col.as)
|
|
1066
1107
|
result += ` AS ${quoteSqlId(col.as)}`;
|
|
1067
|
-
else if (col.func)
|
|
1108
|
+
else if (col.func && !col.args) // e.g. CURRENT_TIMESTAMP
|
|
1068
1109
|
result += ` AS ${quoteSqlId(col.func)}`;
|
|
1069
1110
|
}
|
|
1070
1111
|
return result;
|
|
@@ -1466,9 +1507,8 @@ function toSqlDdl( csn, options ) {
|
|
|
1466
1507
|
case 'hana':
|
|
1467
1508
|
return 'CURRENT_TIMESTAMP';
|
|
1468
1509
|
case 'h2':
|
|
1469
|
-
return 'current_timestamp';
|
|
1470
1510
|
case 'postgres':
|
|
1471
|
-
return '
|
|
1511
|
+
return 'current_timestamp';
|
|
1472
1512
|
default:
|
|
1473
1513
|
return quoteSqlId(x.ref[0]);
|
|
1474
1514
|
}
|
|
@@ -1498,19 +1538,28 @@ function toSqlDdl( csn, options ) {
|
|
|
1498
1538
|
if (options.sqlDialect === 'hana')
|
|
1499
1539
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1500
1540
|
else if (options.sqlDialect === 'postgres')
|
|
1501
|
-
return 'current_setting(\'
|
|
1541
|
+
return 'current_setting(\'cap.applicationuser\')';
|
|
1502
1542
|
else if (options.betterSqliteSessionVariables && options.sqlDialect === 'sqlite')
|
|
1503
1543
|
return 'session_context( \'$user.id\' )';
|
|
1504
|
-
|
|
1544
|
+
else if (options.sqlDialect === 'h2')
|
|
1545
|
+
return '@applicationuser';
|
|
1546
|
+
|
|
1547
|
+
if (!reportedMissingUserReplacement) {
|
|
1548
|
+
warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
|
|
1549
|
+
reportedMissingUserReplacement = true;
|
|
1550
|
+
}
|
|
1505
1551
|
return '\'$user.id\'';
|
|
1506
1552
|
}
|
|
1507
1553
|
else if (x.ref[1] === 'locale') {
|
|
1508
1554
|
if (options.sqlDialect === 'hana')
|
|
1509
1555
|
return 'SESSION_CONTEXT(\'LOCALE\')';
|
|
1510
1556
|
else if (options.sqlDialect === 'postgres')
|
|
1511
|
-
return 'current_setting(\'
|
|
1557
|
+
return 'current_setting(\'cap.locale\')';
|
|
1512
1558
|
else if (options.betterSqliteSessionVariables && options.sqlDialect === 'sqlite')
|
|
1513
1559
|
return 'session_context( \'$user.locale\' )';
|
|
1560
|
+
else if (options.sqlDialect === 'h2')
|
|
1561
|
+
return '@locale';
|
|
1562
|
+
|
|
1514
1563
|
return '\'en\''; // default language
|
|
1515
1564
|
}
|
|
1516
1565
|
// Basically: Second path step was invalid, do nothing - should not happen.
|
|
@@ -1542,8 +1591,9 @@ function toSqlDdl( csn, options ) {
|
|
|
1542
1591
|
case 'hana':
|
|
1543
1592
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-FROM\'))';
|
|
1544
1593
|
case 'postgres':
|
|
1545
|
-
return '
|
|
1594
|
+
return 'current_setting(\'cap.valid_from\')::timestamp';
|
|
1546
1595
|
case 'h2':
|
|
1596
|
+
return '@valid_from';
|
|
1547
1597
|
case 'plain':
|
|
1548
1598
|
return 'current_timestamp';
|
|
1549
1599
|
default:
|
|
@@ -1563,8 +1613,9 @@ function toSqlDdl( csn, options ) {
|
|
|
1563
1613
|
case 'hana':
|
|
1564
1614
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1565
1615
|
case 'postgres':
|
|
1566
|
-
return '
|
|
1616
|
+
return 'current_setting(\'cap.valid_to\')::timestamp';
|
|
1567
1617
|
case 'h2':
|
|
1618
|
+
return '@valid_to';
|
|
1568
1619
|
case 'plain':
|
|
1569
1620
|
return 'current_timestamp';
|
|
1570
1621
|
default:
|
|
@@ -146,7 +146,7 @@ function addContextMarkers( csn, killList ) {
|
|
|
146
146
|
const contextsToCreate = Object.create(null);
|
|
147
147
|
forEachDefinition(csn, (art, artifactName) => {
|
|
148
148
|
const namespace = getNamespace(csn, artifactName);
|
|
149
|
-
if (namespace && !(art
|
|
149
|
+
if (namespace && !(art.$ignore || hasValidSkipOrExists(art))) {
|
|
150
150
|
const parts = namespace.split('.');
|
|
151
151
|
contextsToCreate[parts[0]] = true;
|
|
152
152
|
|
|
@@ -204,7 +204,7 @@ function addMissingChildContexts( csn, artifactName, killList ) {
|
|
|
204
204
|
const possibleNames = Object.keys(csn.definitions).filter(name => name.startsWith(`${artifactName}.`)).sort((a, b) => a.length - b.length);
|
|
205
205
|
for (const name of possibleNames) {
|
|
206
206
|
const artifact = csn.definitions[name];
|
|
207
|
-
if (!artifact
|
|
207
|
+
if (!artifact.$ignore && !hasValidSkipOrExists(artifact))
|
|
208
208
|
addPossibleGaps(name.slice(artifactName.length + 1).split('.'), artifactName);
|
|
209
209
|
}
|
|
210
210
|
|
|
@@ -360,7 +360,7 @@ function findElement( elements, column ) {
|
|
|
360
360
|
function addIntermediateContexts( csn, killList ) {
|
|
361
361
|
for (const artifactName in csn.definitions) {
|
|
362
362
|
const artifact = csn.definitions[artifactName];
|
|
363
|
-
if ((artifact.kind === 'context') && !artifact
|
|
363
|
+
if ((artifact.kind === 'context') && !artifact.$ignore) {
|
|
364
364
|
// If context A.B.C and entity A exist, we still need generate context A_B.
|
|
365
365
|
// But if no entity A exists, A.B is just a namespace.
|
|
366
366
|
// For case 1 and 2, getParentContextName returns undefined - we then use the namespace as our "off-limits"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render the "CONSTRAINT <XY>;" for HDI or SQL.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} constraint The constraint to add
|
|
7
|
+
* @param {string} constraintName Name of the constraint - needs to be escaped on caller side
|
|
8
|
+
* @param {string} tableName Name of the table with the constraint - needs to be escaped on caller side
|
|
9
|
+
* @param {function} quoteSqlId Usual rendering function
|
|
10
|
+
* @param {object} options Options
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
function renderUniqueConstraintString( constraint, constraintName, tableName, quoteSqlId, options ) {
|
|
14
|
+
const c = constraint.paths;
|
|
15
|
+
const refs = c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ');
|
|
16
|
+
if (options.src === 'hdi')
|
|
17
|
+
return `UNIQUE INVERTED INDEX ${constraintName} ON ${tableName} (${refs})`;
|
|
18
|
+
|
|
19
|
+
return `CONSTRAINT ${constraintName} UNIQUE (${refs})`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Render the "ALTER TABLE XY DROP CONSTRAINT <Z>;"
|
|
23
|
+
*
|
|
24
|
+
* @param {object} constraint The constraint to drop
|
|
25
|
+
* @param {string} constraintName Name of the constraint - needs to be escaped on caller side
|
|
26
|
+
* @param {string} tableName Name of the table with the constraint - needs to be escaped on caller side
|
|
27
|
+
* @param {function} quoteSqlId Usual rendering function
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
function renderUniqueConstraintDrop( constraint, constraintName, tableName, quoteSqlId ) {
|
|
31
|
+
return `ALTER TABLE ${tableName} DROP CONSTRAINT ${quoteSqlId(constraintName)};`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Render the "ALTER TABLE XY ADD CONSTRAINT <Z>;"
|
|
36
|
+
*
|
|
37
|
+
* @param {object} constraint The constraint to add
|
|
38
|
+
* @param {string} constraintName Name of the constraint - needs to be escaped on caller side
|
|
39
|
+
* @param {string} tableName Name of the table with the constraint - needs to be escaped on caller side
|
|
40
|
+
* @param {function} quoteSqlId Usual rendering function
|
|
41
|
+
* @param {object} options Options
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function renderUniqueConstraintAdd( constraint, constraintName, tableName, quoteSqlId, options ) {
|
|
45
|
+
return `ALTER TABLE ${tableName} ADD ${renderUniqueConstraintString(constraint, constraintName, tableName, quoteSqlId, options)};`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
renderUniqueConstraintString,
|
|
50
|
+
renderUniqueConstraintDrop,
|
|
51
|
+
renderUniqueConstraintAdd,
|
|
52
|
+
};
|