@sap/cds-compiler 3.4.2 → 3.5.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 +80 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +15 -16
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +2 -2
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +61 -59
- package/lib/api/options.js +4 -2
- package/lib/api/validate.js +2 -2
- package/lib/base/cleanSymbols.js +2 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +6 -6
- package/lib/base/location.js +11 -12
- package/lib/base/message-registry.js +177 -58
- package/lib/base/messages.js +252 -180
- package/lib/base/model.js +14 -11
- package/lib/base/node-helpers.js +9 -10
- package/lib/base/optionProcessorHelper.js +138 -129
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +5 -5
- package/lib/checks/annotationsOData.js +4 -4
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +3 -3
- package/lib/checks/defaultValues.js +3 -3
- package/lib/checks/elements.js +7 -7
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +4 -4
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/managedWithoutKeys.js +1 -1
- package/lib/checks/nonexpandableStructured.js +5 -3
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +5 -6
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -2
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +4 -4
- package/lib/checks/types.js +7 -7
- package/lib/checks/utils.js +4 -4
- package/lib/checks/validator.js +16 -13
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/builtins.js +14 -14
- package/lib/compiler/checks.js +123 -48
- package/lib/compiler/define.js +12 -13
- package/lib/compiler/extend.js +266 -60
- package/lib/compiler/finalize-parse-cdl.js +10 -5
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +14 -6
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +27 -16
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +20 -0
- package/lib/edm/annotations/genericTranslation.js +604 -358
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +275 -222
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +6 -6
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +107 -77
- package/lib/edm/edmUtils.js +44 -5
- package/lib/gen/Dictionary.json +210 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +67 -63
- package/lib/gen/language.tokens +81 -81
- package/lib/gen/languageLexer.interp +4 -10
- package/lib/gen/languageLexer.js +854 -869
- package/lib/gen/languageLexer.tokens +79 -81
- package/lib/gen/languageParser.js +14309 -13832
- package/lib/inspect/inspectModelStatistics.js +2 -2
- package/lib/inspect/inspectPropagation.js +6 -6
- package/lib/inspect/inspectUtils.js +2 -2
- package/lib/json/from-csn.js +102 -55
- package/lib/json/to-csn.js +119 -198
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +113 -133
- package/lib/language/language.g4 +1550 -1506
- package/lib/language/multiLineStringParser.js +3 -3
- package/lib/language/textUtils.js +2 -2
- package/lib/main.js +3 -3
- package/lib/model/csnRefs.js +5 -0
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +100 -0
- package/lib/optionProcessor.js +5 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +311 -276
- package/lib/render/toHdbcds.js +97 -94
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +127 -223
- package/lib/render/utils/common.js +141 -108
- package/lib/render/utils/delta.js +227 -0
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/assertUnique.js +13 -12
- package/lib/transform/db/associations.js +5 -5
- package/lib/transform/db/cdsPersistence.js +10 -8
- package/lib/transform/db/constraints.js +14 -14
- package/lib/transform/db/expansion.js +20 -22
- package/lib/transform/db/flattening.js +24 -42
- package/lib/transform/db/groupByOrderBy.js +3 -3
- package/lib/transform/db/temporal.js +6 -6
- package/lib/transform/db/transformExists.js +23 -23
- package/lib/transform/db/views.js +16 -16
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +8 -29
- package/lib/transform/forRelationalDB.js +16 -6
- package/lib/transform/localized.js +11 -10
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +113 -47
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +17 -10
- package/lib/transform/translateAssocsToJoins.js +24 -19
- package/lib/transform/universalCsn/coreComputed.js +10 -10
- package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
- package/lib/transform/universalCsn/utils.js +3 -3
- package/lib/utils/file.js +5 -5
- package/lib/utils/moduleResolve.js +13 -13
- package/lib/utils/objectUtils.js +6 -6
- package/lib/utils/term.js +5 -2
- package/lib/utils/timetrace.js +51 -24
- package/package.json +5 -8
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/redirected-to-complex.md +4 -4
- package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
- package/lib/modelCompare/filter.js +0 -83
package/lib/render/toSql.js
CHANGED
|
@@ -8,8 +8,11 @@ const {
|
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const {
|
|
10
10
|
renderFunc, cdsToSqlTypes, getHanaComment, hasHanaComment,
|
|
11
|
-
getSqlSnippets,
|
|
11
|
+
getSqlSnippets, createExpressionRenderer, withoutCast,
|
|
12
12
|
} = require('./utils/common');
|
|
13
|
+
const {
|
|
14
|
+
getDeltaRenderer,
|
|
15
|
+
} = require('./utils/delta');
|
|
13
16
|
const {
|
|
14
17
|
renderReferentialConstraint, getIdentifierUtils,
|
|
15
18
|
} = require('./utils/sql');
|
|
@@ -74,17 +77,19 @@ const { ModelError } = require('../base/error');
|
|
|
74
77
|
* @param {CSN.Options} options Transformation options
|
|
75
78
|
* @returns {object} Dictionary of artifact-type:artifacts, where artifacts is a dictionary of name:content
|
|
76
79
|
*/
|
|
77
|
-
function toSqlDdl(csn, options) {
|
|
80
|
+
function toSqlDdl( csn, options ) {
|
|
78
81
|
timetrace.start('SQL rendering');
|
|
79
82
|
const {
|
|
80
83
|
error, warning, info, throwWithAnyError,
|
|
81
84
|
} = makeMessageFunction(csn, options, 'to.sql');
|
|
82
|
-
const { quoteSqlId, prepareIdentifier } = getIdentifierUtils(options);
|
|
83
|
-
|
|
85
|
+
const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options);
|
|
86
|
+
|
|
87
|
+
const exprRenderer = createExpressionRenderer({
|
|
88
|
+
// FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
|
|
84
89
|
finalize: x => String(x).toUpperCase(),
|
|
85
|
-
|
|
90
|
+
typeCast(x) {
|
|
86
91
|
const typeRef = renderBuiltinType(x.cast.type) + renderTypeParameters(x.cast);
|
|
87
|
-
return `CAST(${renderExpr(x
|
|
92
|
+
return `CAST(${this.renderExpr(withoutCast(x))} AS ${typeRef})`;
|
|
88
93
|
},
|
|
89
94
|
val: renderExpressionLiteral,
|
|
90
95
|
enum: (x) => {
|
|
@@ -94,131 +99,39 @@ function toSqlDdl(csn, options) {
|
|
|
94
99
|
return '';
|
|
95
100
|
},
|
|
96
101
|
ref: renderExpressionRef,
|
|
97
|
-
aliasOnly
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
windowFunction: (x, env) => renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, env),
|
|
101
|
-
func: (x, env) => renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, options.sqlDialect, a => renderArgs(a, '=>', env, null)),
|
|
102
|
-
xpr(x, env) {
|
|
103
|
-
if (this.nestedExpr && !x.cast)
|
|
104
|
-
return `(${renderExpr(x.xpr, env, this.inline, true)})`;
|
|
105
|
-
|
|
106
|
-
return renderExpr(x.xpr, env, this.inline, true);
|
|
107
|
-
},
|
|
108
|
-
SELECT: (x, env) => `(${renderQuery('<subselect>', x, increaseIndent(env))})`,
|
|
109
|
-
SET: (x, env) => `(${renderQuery('<union>', x, increaseIndent(env))})`,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Utils to render SQL statements.
|
|
113
|
-
const render = {
|
|
114
|
-
/*
|
|
115
|
-
Render column additions as HANA SQL. Checks for duplicate elements.
|
|
116
|
-
Only HANA SQL is currently supported.
|
|
117
|
-
*/
|
|
118
|
-
addColumns: {
|
|
119
|
-
fromElementStrings(tableName, eltStrings) {
|
|
120
|
-
if (options.sqlDialect === 'sqlite') // SQLite can only alter one column at a time
|
|
121
|
-
return eltStrings.map(eltString => `ALTER TABLE ${tableName} ADD ${eltString};`);
|
|
122
|
-
|
|
123
|
-
const elts = options.sqlDialect === 'hana' ? `(${eltStrings.join(', ')})` : `${eltStrings.join(', ')}`;
|
|
124
|
-
return [ `ALTER TABLE ${tableName} ADD ${elts};` ];
|
|
125
|
-
},
|
|
126
|
-
fromElementsObj(artifactName, tableName, elementsObj, env, duplicateChecker) {
|
|
127
|
-
// Only extend with 'ADD' for elements/associations
|
|
128
|
-
// TODO: May also include 'RENAME' at a later stage
|
|
129
|
-
const alterEnv = activateAlterMode(env);
|
|
130
|
-
const elements = Object.entries(elementsObj)
|
|
131
|
-
.map(([ name, elt ]) => renderElement(artifactName, name, elt, duplicateChecker, null, alterEnv))
|
|
132
|
-
.filter(s => s !== '');
|
|
133
|
-
|
|
134
|
-
if (elements.length)
|
|
135
|
-
return render.addColumns.fromElementStrings(tableName, elements);
|
|
136
|
-
|
|
137
|
-
return [];
|
|
138
|
-
},
|
|
102
|
+
aliasOnly: x => x.as,
|
|
103
|
+
windowFunction( x) {
|
|
104
|
+
return renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, this.env);
|
|
139
105
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
TODO duplicity check
|
|
143
|
-
*/
|
|
144
|
-
addAssociations(artifactName, tableName, elementsObj, env) {
|
|
145
|
-
return options.sqlDialect === 'hana' ? Object.entries(elementsObj)
|
|
146
|
-
.map(([ name, elt ]) => renderAssociationElement(name, elt, env))
|
|
147
|
-
.filter(s => s !== '')
|
|
148
|
-
.map(eltStr => `ALTER TABLE ${tableName} ADD ASSOCIATION (${eltStr});`) : [];
|
|
106
|
+
func(x) {
|
|
107
|
+
return renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, options.sqlDialect, a => renderArgs(a, '=>', this.env, null));
|
|
149
108
|
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return [ `ALTER TABLE ${tableName} ADD ${render.primaryKey(elementsObj)}` ];
|
|
109
|
+
xpr(x) {
|
|
110
|
+
if (this.isNestedXpr && !x.cast)
|
|
111
|
+
return `(${this.renderSubExpr(x.xpr)})`;
|
|
112
|
+
return this.renderSubExpr(x.xpr);
|
|
155
113
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
*/
|
|
159
|
-
dropColumns(tableName, sqlIds) {
|
|
160
|
-
return [ `ALTER TABLE ${tableName} DROP ${options.sqlDialect === 'hana' ? '(' : ''}${sqlIds.join(', ')}${options.sqlDialect === 'hana' ? ')' : ''};` ];
|
|
114
|
+
SELECT( x) {
|
|
115
|
+
return `(${renderQuery('<subselect>', x, increaseIndent(this.env))})`;
|
|
161
116
|
},
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
*/
|
|
165
|
-
dropAssociation(tableName, sqlId) {
|
|
166
|
-
return options.sqlDialect === 'hana' ? [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ] : [];
|
|
117
|
+
SET( x) {
|
|
118
|
+
return `(${renderQuery('<union>', x, increaseIndent(this.env))})`;
|
|
167
119
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
primaryKey(elementsObj) {
|
|
184
|
-
const primaryKeys = Object.keys(elementsObj)
|
|
185
|
-
.filter(name => elementsObj[name].key)
|
|
186
|
-
.filter(name => !elementsObj[name].virtual)
|
|
187
|
-
.map(name => quoteSqlId(name))
|
|
188
|
-
.join(', ');
|
|
189
|
-
return primaryKeys && `PRIMARY KEY(${primaryKeys})`;
|
|
190
|
-
},
|
|
191
|
-
/*
|
|
192
|
-
Render entity-comment modifications as HANA SQL.
|
|
193
|
-
*/
|
|
194
|
-
alterEntityComment(tableName, comment) {
|
|
195
|
-
return [ `COMMENT ON TABLE ${tableName} IS ${render.comment(comment)};` ];
|
|
196
|
-
},
|
|
197
|
-
/*
|
|
198
|
-
Render column-comment modifications as HANA SQL.
|
|
199
|
-
*/
|
|
200
|
-
alterColumnComment(tableName, columnName, comment) {
|
|
201
|
-
return [ `COMMENT ON COLUMN ${tableName}.${columnName} IS ${render.comment(comment)};` ];
|
|
202
|
-
},
|
|
203
|
-
/*
|
|
204
|
-
Render comment string.
|
|
205
|
-
*/
|
|
206
|
-
comment(comment) {
|
|
207
|
-
return comment && renderStringForSql(getHanaComment({ doc: comment }), options.sqlDialect) || 'NULL';
|
|
208
|
-
},
|
|
209
|
-
/*
|
|
210
|
-
Alter SQL snippet for entity.
|
|
211
|
-
*/
|
|
212
|
-
alterEntitySqlSnippet(tableName, snippet) {
|
|
213
|
-
return [ `ALTER TABLE ${tableName} ${snippet};` ];
|
|
214
|
-
},
|
|
215
|
-
/*
|
|
216
|
-
Concatenate multiple statements which are to be treated as one by the API caller.
|
|
217
|
-
*/
|
|
218
|
-
concat(...statements) {
|
|
219
|
-
return [ statements.join('\n') ];
|
|
220
|
-
},
|
|
221
|
-
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
function renderExpr( x, env ) {
|
|
123
|
+
return exprRenderer.renderExpr(x, env);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const render = getDeltaRenderer(options, {
|
|
127
|
+
renderElement,
|
|
128
|
+
renderArtifactName,
|
|
129
|
+
renderAssociationElement,
|
|
130
|
+
quoteSqlId,
|
|
131
|
+
renderStringForSql,
|
|
132
|
+
activateAlterMode,
|
|
133
|
+
getHanaComment,
|
|
134
|
+
});
|
|
222
135
|
|
|
223
136
|
// FIXME: Currently requires 'options.forHana', because it can only render HANA-ish SQL dialect
|
|
224
137
|
if (!options.forHana && !isBetaEnabled(options, 'sqlExtensions'))
|
|
@@ -339,8 +252,7 @@ function toSqlDdl(csn, options) {
|
|
|
339
252
|
for (const name in deletions)
|
|
340
253
|
deletions[name] = `${options.testMode ? '' : sqlVersionLine}${deletions[name]}`;
|
|
341
254
|
|
|
342
|
-
|
|
343
|
-
timetrace.stop();
|
|
255
|
+
timetrace.stop('SQL rendering');
|
|
344
256
|
return mainResultObj;
|
|
345
257
|
|
|
346
258
|
/**
|
|
@@ -351,7 +263,7 @@ function toSqlDdl(csn, options) {
|
|
|
351
263
|
* @param {object} resultObj Result collector
|
|
352
264
|
* @param {object} env Render environment
|
|
353
265
|
*/
|
|
354
|
-
function renderArtifactInto(artifactName, art, resultObj, env) {
|
|
266
|
+
function renderArtifactInto( artifactName, art, resultObj, env ) {
|
|
355
267
|
// Ignore whole artifacts if forRelationalDB says so
|
|
356
268
|
if (art.abstract || hasValidSkipOrExists(art))
|
|
357
269
|
return;
|
|
@@ -393,7 +305,7 @@ function toSqlDdl(csn, options) {
|
|
|
393
305
|
* @param {object} resultObj Result collector
|
|
394
306
|
* @param {object} env Render environment
|
|
395
307
|
*/
|
|
396
|
-
function renderArtifactExtensionInto(artifactName, artifact, ext, resultObj, env) {
|
|
308
|
+
function renderArtifactExtensionInto( artifactName, artifact, ext, resultObj, env ) {
|
|
397
309
|
// Property kind is always omitted for elements and can be omitted for
|
|
398
310
|
// top-level type definitions, it does not exist for extensions.
|
|
399
311
|
if (artifactName && !ext.query)
|
|
@@ -404,52 +316,36 @@ function toSqlDdl(csn, options) {
|
|
|
404
316
|
}
|
|
405
317
|
|
|
406
318
|
// Render an artifact deletion into the appropriate dictionary of 'resultObj'.
|
|
407
|
-
function renderArtifactDeletionInto(artifactName, art, resultObj) {
|
|
319
|
+
function renderArtifactDeletionInto( artifactName, art, resultObj ) {
|
|
408
320
|
const tableName = renderArtifactName(artifactName);
|
|
409
321
|
deletionsDuplicateChecker.addArtifact(tableName, art.$location, artifactName);
|
|
410
322
|
|
|
411
323
|
addDeletion(resultObj, artifactName, `DROP TABLE ${tableName}`);
|
|
412
324
|
}
|
|
413
325
|
|
|
414
|
-
/**
|
|
415
|
-
* Given the following artifact name: namespace.prefix.entity.with.dot, render the following,
|
|
416
|
-
* depending on the naming mode:
|
|
417
|
-
* - plain: NAMESPACE_PREFIX_ENTITY_WITH_DOT
|
|
418
|
-
* - quoted: namespace.prefix.entity_with_dot
|
|
419
|
-
* - hdbcds: namespace::prefix.entity_with_dot
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* @param {string} artifactName Artifact name to render
|
|
423
|
-
*
|
|
424
|
-
* @returns {string} Artifact name
|
|
425
|
-
*/
|
|
426
|
-
function renderArtifactName(artifactName) {
|
|
427
|
-
return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName));
|
|
428
|
-
}
|
|
429
|
-
|
|
430
326
|
// Render an artifact migration into the appropriate dictionary of 'resultObj'.
|
|
431
327
|
// Only HANA SQL is currently supported.
|
|
432
|
-
function renderArtifactMigrationInto(artifactName, migration, resultObj, env) {
|
|
433
|
-
function reducesTypeSize(def) {
|
|
328
|
+
function renderArtifactMigrationInto( artifactName, migration, resultObj, env ) {
|
|
329
|
+
function reducesTypeSize( def ) {
|
|
434
330
|
// HANA does not allow decreasing the value of any of those type parameters.
|
|
435
331
|
return def.old.type === def.new.type &&
|
|
436
332
|
[ 'length', 'precision', 'scale' ].some(param => def.new[param] < def.old[param]);
|
|
437
333
|
}
|
|
438
|
-
function getEltStr(defVariant, eltName) {
|
|
334
|
+
function getEltStr( defVariant, eltName, changeType = 'extension' ) {
|
|
439
335
|
return defVariant.target
|
|
440
336
|
? renderAssociationElement(eltName, defVariant, env)
|
|
441
|
-
: renderElement(artifactName, eltName, defVariant, null, null, activateAlterMode(env));
|
|
337
|
+
: renderElement(artifactName, eltName, defVariant, null, null, activateAlterMode(env, changeType));
|
|
442
338
|
}
|
|
443
|
-
function getEltStrNoProps(defVariant, eltName, ...props) {
|
|
339
|
+
function getEltStrNoProps( defVariant, eltName, ...props ) {
|
|
444
340
|
const defNoProps = Object.assign({}, defVariant);
|
|
445
341
|
for (const prop of props)
|
|
446
342
|
delete defNoProps[prop];
|
|
447
343
|
return getEltStr(defNoProps, eltName);
|
|
448
344
|
}
|
|
449
|
-
function oldAnnoChangedIncompatibly(defOld, defNew) {
|
|
345
|
+
function oldAnnoChangedIncompatibly( defOld, defNew ) {
|
|
450
346
|
return typeof defOld === 'string' && defOld.trim().length && !(typeof defNew === 'string' && defNew.trim().startsWith(`${defOld.trim()} `));
|
|
451
347
|
}
|
|
452
|
-
function getUnknownSqlReason(anno, artName, defOld, defNew, eltName) {
|
|
348
|
+
function getUnknownSqlReason( anno, artName, defOld, defNew, eltName ) {
|
|
453
349
|
const changeKind = defNew === undefined
|
|
454
350
|
? `removed (previous value: ${JSON.stringify(defOld)})`
|
|
455
351
|
: `changed from ${JSON.stringify(defOld)} to ${JSON.stringify(defNew)}`;
|
|
@@ -466,7 +362,7 @@ function toSqlDdl(csn, options) {
|
|
|
466
362
|
if (migration.properties) {
|
|
467
363
|
for (const [ prop, def ] of Object.entries(migration.properties)) {
|
|
468
364
|
if (prop === 'doc' && !options.disableHanaComments) { // def.new may be `null`
|
|
469
|
-
const alterComment = render.alterEntityComment(
|
|
365
|
+
const alterComment = render.alterEntityComment(artifactName, def.new);
|
|
470
366
|
addMigration(resultObj, artifactName, false, alterComment);
|
|
471
367
|
}
|
|
472
368
|
else if (sqlSnippetAnnos.includes(prop)) { // NOTE: @sql.replace may be supported in the future
|
|
@@ -475,7 +371,7 @@ function toSqlDdl(csn, options) {
|
|
|
475
371
|
addMigration(resultObj, artifactName, false, null, getUnknownSqlReason(prop, artifactName, def.old, def.new));
|
|
476
372
|
}
|
|
477
373
|
else {
|
|
478
|
-
addMigration(resultObj, artifactName, false, render.alterEntitySqlSnippet(
|
|
374
|
+
addMigration(resultObj, artifactName, false, render.alterEntitySqlSnippet(artifactName, def.new));
|
|
479
375
|
}
|
|
480
376
|
}
|
|
481
377
|
}
|
|
@@ -493,10 +389,10 @@ function toSqlDdl(csn, options) {
|
|
|
493
389
|
|
|
494
390
|
// Remove columns.
|
|
495
391
|
if (removeCols.length)
|
|
496
|
-
addMigration(resultObj, artifactName, true, render.dropColumns(
|
|
392
|
+
addMigration(resultObj, artifactName, true, render.dropColumns(artifactName, removeCols));
|
|
497
393
|
|
|
498
394
|
// Remove associations.
|
|
499
|
-
removeAssocs.forEach(assoc => addMigration(resultObj, artifactName, true, render.dropAssociation(
|
|
395
|
+
removeAssocs.forEach(assoc => addMigration(resultObj, artifactName, true, render.dropAssociation(artifactName, assoc)));
|
|
500
396
|
}
|
|
501
397
|
}
|
|
502
398
|
|
|
@@ -507,8 +403,8 @@ function toSqlDdl(csn, options) {
|
|
|
507
403
|
const sqlId = quoteSqlId(eltName);
|
|
508
404
|
changeElementsDuplicateChecker.addElement(sqlId, undefined, eltName);
|
|
509
405
|
|
|
510
|
-
const eltStrOld = getEltStr(def.old, eltName);
|
|
511
|
-
const eltStrNew = getEltStr(def.new, eltName);
|
|
406
|
+
const eltStrOld = getEltStr(def.old, eltName, 'migration');
|
|
407
|
+
const eltStrNew = getEltStr(def.new, eltName, 'migration');
|
|
512
408
|
if (eltStrNew === eltStrOld)
|
|
513
409
|
return; // Prevent spurious migrations, where the column DDL does not change.
|
|
514
410
|
|
|
@@ -535,7 +431,7 @@ function toSqlDdl(csn, options) {
|
|
|
535
431
|
const eltStrOldNoDoc = getEltStrNoProps(def.old, eltName, 'doc');
|
|
536
432
|
const eltStrNewNoDoc = getEltStrNoProps(def.new, eltName, 'doc');
|
|
537
433
|
if (eltStrOldNoDoc === eltStrNewNoDoc) { // only `doc` changed
|
|
538
|
-
const alterComment = render.alterColumnComment(
|
|
434
|
+
const alterComment = render.alterColumnComment(artifactName, sqlId, def.new.doc);
|
|
539
435
|
addMigration(resultObj, artifactName, false, alterComment);
|
|
540
436
|
continue;
|
|
541
437
|
}
|
|
@@ -545,16 +441,16 @@ function toSqlDdl(csn, options) {
|
|
|
545
441
|
// Lossy change because either an association is removed and/or added, or the type size is reduced.
|
|
546
442
|
// Drop old element and re-add it in its new shape.
|
|
547
443
|
const drop = def.old.target
|
|
548
|
-
? render.dropAssociation(
|
|
549
|
-
: render.dropColumns(
|
|
444
|
+
? render.dropAssociation(artifactName, sqlId)
|
|
445
|
+
: render.dropColumns(artifactName, [ sqlId ]);
|
|
550
446
|
const add = def.new.target
|
|
551
|
-
? render.addAssociations(artifactName,
|
|
552
|
-
: render.
|
|
447
|
+
? render.addAssociations(artifactName, { [eltName]: def.new }, env)
|
|
448
|
+
: render.addColumnsFromElementsObj(artifactName, { [eltName]: def.new }, env);
|
|
553
449
|
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add));
|
|
554
450
|
}
|
|
555
451
|
else {
|
|
556
452
|
// Lossless change: no associations directly affected, no size reduction.
|
|
557
|
-
addMigration(resultObj, artifactName, false, render.alterColumns(
|
|
453
|
+
addMigration(resultObj, artifactName, false, render.alterColumns(artifactName, sqlId, def, eltStrNew));
|
|
558
454
|
}
|
|
559
455
|
}
|
|
560
456
|
}
|
|
@@ -569,7 +465,7 @@ function toSqlDdl(csn, options) {
|
|
|
569
465
|
* @param {object} resultObj Result collector
|
|
570
466
|
* @param {object} env Render environment
|
|
571
467
|
*/
|
|
572
|
-
function renderEntityInto(artifactName, art, resultObj, env) {
|
|
468
|
+
function renderEntityInto( artifactName, art, resultObj, env ) {
|
|
573
469
|
env._artifact = art;
|
|
574
470
|
const childEnv = increaseIndent(env);
|
|
575
471
|
const hanaTc = art.technicalConfig && art.technicalConfig.hana;
|
|
@@ -678,23 +574,23 @@ function toSqlDdl(csn, options) {
|
|
|
678
574
|
* @param {object} env Render environment
|
|
679
575
|
* @param {DuplicateChecker} duplicateChecker
|
|
680
576
|
*/
|
|
681
|
-
function renderExtendInto(artifactName, artifactElements, extElements, resultObj, env, duplicateChecker) {
|
|
577
|
+
function renderExtendInto( artifactName, artifactElements, extElements, resultObj, env, duplicateChecker ) {
|
|
682
578
|
const tableName = renderArtifactName(artifactName);
|
|
683
579
|
if (duplicateChecker)
|
|
684
580
|
duplicateChecker.addArtifact(tableName, undefined, artifactName);
|
|
685
|
-
const elements = render.
|
|
686
|
-
const associations = render.addAssociations(artifactName,
|
|
581
|
+
const elements = render.addColumnsFromElementsObj(artifactName, extElements, env, duplicateChecker);
|
|
582
|
+
const associations = render.addAssociations(artifactName, extElements, env);
|
|
687
583
|
if (elements.length + associations.length > 0)
|
|
688
584
|
addMigration(resultObj, artifactName, false, [ ...elements, ...associations ]);
|
|
689
585
|
|
|
690
586
|
if (Object.values(extElements).some(elt => elt.key)) {
|
|
691
|
-
const drop = render.dropKey(
|
|
692
|
-
const add = render.addKey(
|
|
587
|
+
const drop = render.dropKey(artifactName);
|
|
588
|
+
const add = render.addKey(artifactName, artifactElements);
|
|
693
589
|
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add));
|
|
694
590
|
}
|
|
695
591
|
}
|
|
696
592
|
|
|
697
|
-
function addMigration(resultObj, artifactName, drop, sqlArray, description) {
|
|
593
|
+
function addMigration( resultObj, artifactName, drop, sqlArray, description ) {
|
|
698
594
|
if (!(artifactName in resultObj.migrations))
|
|
699
595
|
resultObj.migrations[artifactName] = [];
|
|
700
596
|
|
|
@@ -707,7 +603,7 @@ function toSqlDdl(csn, options) {
|
|
|
707
603
|
resultObj.migrations[artifactName].push(...migrations);
|
|
708
604
|
}
|
|
709
605
|
|
|
710
|
-
function addDeletion(resultObj, artifactName, deletionSql) {
|
|
606
|
+
function addDeletion( resultObj, artifactName, deletionSql ) {
|
|
711
607
|
resultObj.deletions[artifactName] = deletionSql;
|
|
712
608
|
}
|
|
713
609
|
|
|
@@ -718,7 +614,7 @@ function toSqlDdl(csn, options) {
|
|
|
718
614
|
* @param {object} hanaTc Technical configuration object
|
|
719
615
|
* @returns {object} fzindex for the element
|
|
720
616
|
*/
|
|
721
|
-
function getFzIndex(elemName, hanaTc) {
|
|
617
|
+
function getFzIndex( elemName, hanaTc ) {
|
|
722
618
|
if (!hanaTc || !hanaTc.fzindexes || !hanaTc.fzindexes[elemName])
|
|
723
619
|
return undefined;
|
|
724
620
|
|
|
@@ -748,15 +644,16 @@ function toSqlDdl(csn, options) {
|
|
|
748
644
|
* @param {object} env Render environment
|
|
749
645
|
* @returns {string} Rendered element
|
|
750
646
|
*/
|
|
751
|
-
function renderElement(artifactName, elementName, elm, duplicateChecker, fzindex, env) {
|
|
647
|
+
function renderElement( artifactName, elementName, elm, duplicateChecker, fzindex, env ) {
|
|
752
648
|
if (elm.virtual || elm.target)
|
|
753
649
|
return '';
|
|
754
650
|
|
|
651
|
+
const isPostgresAlterColumn = env.alterMode && env.changeType === 'migration' && options.sqlDialect === 'postgres';
|
|
755
652
|
const quotedElementName = quoteSqlId(elementName);
|
|
756
653
|
if (duplicateChecker)
|
|
757
654
|
duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
|
|
758
655
|
|
|
759
|
-
let result = `${env.indent + quotedElementName} ${renderTypeReference(artifactName, elementName, elm)
|
|
656
|
+
let result = `${env.indent + quotedElementName}${isPostgresAlterColumn ? ' TYPE' : ''} ${renderTypeReference(artifactName, elementName, elm)
|
|
760
657
|
}${renderNullability(elm, true, env.alterMode)}`;
|
|
761
658
|
if (elm.default)
|
|
762
659
|
result += ` DEFAULT ${renderExpr(elm.default, env)}`;
|
|
@@ -791,7 +688,7 @@ function toSqlDdl(csn, options) {
|
|
|
791
688
|
* @param {object} env Render environment
|
|
792
689
|
* @returns {string} Rendered association element
|
|
793
690
|
*/
|
|
794
|
-
function renderAssociationElement(elementName, elm, env) {
|
|
691
|
+
function renderAssociationElement( elementName, elm, env ) {
|
|
795
692
|
let result = '';
|
|
796
693
|
if (elm.target) {
|
|
797
694
|
result += env.indent;
|
|
@@ -811,7 +708,7 @@ function toSqlDdl(csn, options) {
|
|
|
811
708
|
}
|
|
812
709
|
result += ' JOIN ';
|
|
813
710
|
result += `${renderArtifactName(elm.target)} AS ${quoteSqlId(elementName)} ON (`;
|
|
814
|
-
result += `${renderExpr(elm.on, env
|
|
711
|
+
result += `${renderExpr(elm.on, env)})`;
|
|
815
712
|
}
|
|
816
713
|
return result;
|
|
817
714
|
}
|
|
@@ -827,7 +724,7 @@ function toSqlDdl(csn, options) {
|
|
|
827
724
|
* @param {object} env Render environment
|
|
828
725
|
* @returns {string} Rendered technical configuration
|
|
829
726
|
*/
|
|
830
|
-
function renderTechnicalConfiguration(tc, env) {
|
|
727
|
+
function renderTechnicalConfiguration( tc, env ) {
|
|
831
728
|
let result = '';
|
|
832
729
|
|
|
833
730
|
if (!tc)
|
|
@@ -871,7 +768,7 @@ function toSqlDdl(csn, options) {
|
|
|
871
768
|
* @param {object} resultObj Result collector
|
|
872
769
|
* @param {object} env Render environment
|
|
873
770
|
*/
|
|
874
|
-
function renderIndexesInto(indexes, artifactName, resultObj, env) {
|
|
771
|
+
function renderIndexesInto( indexes, artifactName, resultObj, env ) {
|
|
875
772
|
// Indices and full-text indices
|
|
876
773
|
for (const idxName in indexes || {}) {
|
|
877
774
|
let result = '';
|
|
@@ -903,7 +800,7 @@ function toSqlDdl(csn, options) {
|
|
|
903
800
|
* @param {Array} index Index definition
|
|
904
801
|
* @returns {Array} Index with artifact name inserted
|
|
905
802
|
*/
|
|
906
|
-
function insertTableName(index) {
|
|
803
|
+
function insertTableName( index ) {
|
|
907
804
|
const i = index.indexOf('index');
|
|
908
805
|
const j = index.indexOf('(');
|
|
909
806
|
if (i > index.length - 2 || !index[i + 1].ref || j < i || j > index.length - 2)
|
|
@@ -934,7 +831,7 @@ function toSqlDdl(csn, options) {
|
|
|
934
831
|
* @param {object} env Render environment
|
|
935
832
|
* @returns {string} Rendered view source
|
|
936
833
|
*/
|
|
937
|
-
function renderViewSource(artifactName, source, env) {
|
|
834
|
+
function renderViewSource( artifactName, source, env ) {
|
|
938
835
|
// Sub-SELECT
|
|
939
836
|
if (source.SELECT || source.SET) {
|
|
940
837
|
let result = `(${renderQuery(artifactName, source, increaseIndent(env))})`;
|
|
@@ -953,7 +850,7 @@ function toSqlDdl(csn, options) {
|
|
|
953
850
|
result += renderJoinCardinality(source.cardinality);
|
|
954
851
|
result += `JOIN ${renderViewSource(artifactName, source.args[i], env)}`;
|
|
955
852
|
if (source.on)
|
|
956
|
-
result += ` ON ${renderExpr(source.on, env
|
|
853
|
+
result += ` ON ${renderExpr(source.on, env)}`;
|
|
957
854
|
|
|
958
855
|
result += ')';
|
|
959
856
|
}
|
|
@@ -974,7 +871,7 @@ function toSqlDdl(csn, options) {
|
|
|
974
871
|
* @param {object} card CSN cardinality representation
|
|
975
872
|
* @returns {string} Rendered cardinality
|
|
976
873
|
*/
|
|
977
|
-
function renderJoinCardinality(card) {
|
|
874
|
+
function renderJoinCardinality( card ) {
|
|
978
875
|
let result = '';
|
|
979
876
|
if (card) {
|
|
980
877
|
if (card.srcmin && card.srcmin === 1)
|
|
@@ -1002,7 +899,7 @@ function toSqlDdl(csn, options) {
|
|
|
1002
899
|
* @param {object} env Render environment
|
|
1003
900
|
* @returns {string} Rendered path
|
|
1004
901
|
*/
|
|
1005
|
-
function renderAbsolutePathWithAlias(artifactName, path, env) {
|
|
902
|
+
function renderAbsolutePathWithAlias( artifactName, path, env ) {
|
|
1006
903
|
// This actually can't happen anymore because assoc2joins should have taken care of it
|
|
1007
904
|
if (path.ref[0].where)
|
|
1008
905
|
throw new ModelError(`"${artifactName}": Filters in FROM are not supported for conversion to SQL`);
|
|
@@ -1040,7 +937,7 @@ function toSqlDdl(csn, options) {
|
|
|
1040
937
|
* @param {object} env Render environment
|
|
1041
938
|
* @returns {string} Rendered path
|
|
1042
939
|
*/
|
|
1043
|
-
function renderAbsolutePath(path, sep, env) {
|
|
940
|
+
function renderAbsolutePath( path, sep, env ) {
|
|
1044
941
|
// Sanity checks
|
|
1045
942
|
if (!path.ref)
|
|
1046
943
|
throw new ModelError(`Expecting ref in path: ${JSON.stringify(path)}`);
|
|
@@ -1066,7 +963,7 @@ function toSqlDdl(csn, options) {
|
|
|
1066
963
|
result += '()';
|
|
1067
964
|
}
|
|
1068
965
|
if (path.ref[0].where)
|
|
1069
|
-
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env
|
|
966
|
+
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env)}]`;
|
|
1070
967
|
|
|
1071
968
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
1072
969
|
if (path.ref.length > 1)
|
|
@@ -1087,16 +984,16 @@ function toSqlDdl(csn, options) {
|
|
|
1087
984
|
* @returns {string} Rendered arguments
|
|
1088
985
|
* @throws Throws if args is not an array or object.
|
|
1089
986
|
*/
|
|
1090
|
-
function renderArgs(node, sep, env, syntax) {
|
|
987
|
+
function renderArgs( node, sep, env, syntax ) {
|
|
1091
988
|
const args = node.args ? node.args : {};
|
|
1092
989
|
// Positional arguments
|
|
1093
990
|
if (Array.isArray(args))
|
|
1094
|
-
return args.map(arg => renderExpr(arg, env
|
|
991
|
+
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
1095
992
|
|
|
1096
993
|
// Named arguments (object/dict)
|
|
1097
994
|
else if (typeof args === 'object')
|
|
1098
995
|
// if this is a function param which is not a reference to the model, we must not quote it
|
|
1099
|
-
return Object.keys(args).map(key => `${node.func ? key : decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env
|
|
996
|
+
return Object.keys(args).map(key => `${node.func ? key : decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1100
997
|
|
|
1101
998
|
|
|
1102
999
|
throw new ModelError(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1109,7 +1006,7 @@ function toSqlDdl(csn, options) {
|
|
|
1109
1006
|
* @param {string|null} parameterSyntax Some magic A2J parameter - for calcview parameter rendering
|
|
1110
1007
|
* @returns {string} Rendered argument
|
|
1111
1008
|
*/
|
|
1112
|
-
function decorateParameter(arg, parameterSyntax) {
|
|
1009
|
+
function decorateParameter( arg, parameterSyntax ) {
|
|
1113
1010
|
if (parameterSyntax === 'calcview')
|
|
1114
1011
|
return `PLACEHOLDER."$$${arg}$$"`;
|
|
1115
1012
|
|
|
@@ -1126,7 +1023,7 @@ function toSqlDdl(csn, options) {
|
|
|
1126
1023
|
* @param {object} env Render environment
|
|
1127
1024
|
* @returns {string} Rendered column
|
|
1128
1025
|
*/
|
|
1129
|
-
function renderViewColumn(col, elements, env) {
|
|
1026
|
+
function renderViewColumn( col, elements, env ) {
|
|
1130
1027
|
let result = '';
|
|
1131
1028
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1] || col.func;
|
|
1132
1029
|
if (leaf && elements[leaf] && elements[leaf].virtual) {
|
|
@@ -1135,7 +1032,7 @@ function toSqlDdl(csn, options) {
|
|
|
1135
1032
|
result += `${env.indent}NULL AS ${quoteSqlId(col.as || leaf)}`;
|
|
1136
1033
|
}
|
|
1137
1034
|
else {
|
|
1138
|
-
result = env.indent + renderExpr(col, env
|
|
1035
|
+
result = env.indent + renderExpr(withoutCast(col), env);
|
|
1139
1036
|
if (col.as)
|
|
1140
1037
|
result += ` AS ${quoteSqlId(col.as)}`;
|
|
1141
1038
|
else if (col.func)
|
|
@@ -1152,7 +1049,7 @@ function toSqlDdl(csn, options) {
|
|
|
1152
1049
|
* @param {object} env Render environment
|
|
1153
1050
|
* @returns {string} Rendered view
|
|
1154
1051
|
*/
|
|
1155
|
-
function renderView(artifactName, art, env) {
|
|
1052
|
+
function renderView( artifactName, art, env ) {
|
|
1156
1053
|
env._artifact = art;
|
|
1157
1054
|
const viewName = renderArtifactName(artifactName);
|
|
1158
1055
|
definitionsDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
|
|
@@ -1189,7 +1086,7 @@ function toSqlDdl(csn, options) {
|
|
|
1189
1086
|
* @param {Object} params Dictionary of parameters
|
|
1190
1087
|
* @returns {string} Rendered parameters
|
|
1191
1088
|
*/
|
|
1192
|
-
function renderParameterDefinitions(artifactName, params) {
|
|
1089
|
+
function renderParameterDefinitions( artifactName, params ) {
|
|
1193
1090
|
let result = '';
|
|
1194
1091
|
if (params) {
|
|
1195
1092
|
const parray = [];
|
|
@@ -1207,7 +1104,7 @@ function toSqlDdl(csn, options) {
|
|
|
1207
1104
|
pIdentifier = quoteSqlId(pn);
|
|
1208
1105
|
let pstr = `IN ${pIdentifier} ${renderTypeReference(artifactName, pn, p)}`;
|
|
1209
1106
|
if (p.default)
|
|
1210
|
-
pstr += ` DEFAULT ${renderExpr(p.default)}`;
|
|
1107
|
+
pstr += ` DEFAULT ${renderExpr(p.default, { indent: '' })}`;
|
|
1211
1108
|
|
|
1212
1109
|
parray.push(pstr);
|
|
1213
1110
|
}
|
|
@@ -1225,7 +1122,7 @@ function toSqlDdl(csn, options) {
|
|
|
1225
1122
|
* @param {CSN.Elements} [elements] to override direct query elements - e.g. leading union should win
|
|
1226
1123
|
* @returns {string} Rendered query
|
|
1227
1124
|
*/
|
|
1228
|
-
function renderQuery(artifactName, query, env, elements = null) {
|
|
1125
|
+
function renderQuery( artifactName, query, env, elements = null ) {
|
|
1229
1126
|
let result = '';
|
|
1230
1127
|
// Set operator, like UNION, INTERSECT, ...
|
|
1231
1128
|
if (query.SET) {
|
|
@@ -1262,19 +1159,19 @@ function toSqlDdl(csn, options) {
|
|
|
1262
1159
|
result += `SELECT${select.distinct ? ' DISTINCT' : ''}`;
|
|
1263
1160
|
// FIXME: We probably also need to consider `excluding` here ?
|
|
1264
1161
|
result += `\n${(select.columns || [ '*' ])
|
|
1265
|
-
.filter(col => !
|
|
1162
|
+
.filter(col => !select.mixin?.[firstPathStepId(col.ref)]) // No mixin columns
|
|
1266
1163
|
.map(col => renderViewColumn(col, elements || select.elements, childEnv))
|
|
1267
1164
|
.filter(s => s !== '')
|
|
1268
1165
|
.join(',\n')}\n`;
|
|
1269
1166
|
result += `${env.indent}FROM ${renderViewSource(artifactName, select.from, env)}`;
|
|
1270
1167
|
if (select.where)
|
|
1271
|
-
result += `\n${env.indent}WHERE ${renderExpr(select.where, env
|
|
1168
|
+
result += `\n${env.indent}WHERE ${renderExpr(select.where, env)}`;
|
|
1272
1169
|
|
|
1273
1170
|
if (select.groupBy)
|
|
1274
|
-
result += `\n${env.indent}GROUP BY ${select.groupBy.map(expr => renderExpr(expr, env
|
|
1171
|
+
result += `\n${env.indent}GROUP BY ${select.groupBy.map(expr => renderExpr(expr, env)).join(', ')}`;
|
|
1275
1172
|
|
|
1276
1173
|
if (select.having)
|
|
1277
|
-
result += `\n${env.indent}HAVING ${renderExpr(select.having, env
|
|
1174
|
+
result += `\n${env.indent}HAVING ${renderExpr(select.having, env)}`;
|
|
1278
1175
|
|
|
1279
1176
|
if (select.orderBy)
|
|
1280
1177
|
result += `\n${env.indent}ORDER BY ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
@@ -1291,7 +1188,7 @@ function toSqlDdl(csn, options) {
|
|
|
1291
1188
|
* @param {Array} ref Array of refs
|
|
1292
1189
|
* @returns {string|undefined} Id of first path step
|
|
1293
1190
|
*/
|
|
1294
|
-
function firstPathStepId(ref) {
|
|
1191
|
+
function firstPathStepId( ref ) {
|
|
1295
1192
|
return ref && ref[0] && (ref[0].id || ref[0]);
|
|
1296
1193
|
}
|
|
1297
1194
|
|
|
@@ -1302,7 +1199,7 @@ function toSqlDdl(csn, options) {
|
|
|
1302
1199
|
* @param {object} env Renderenvironment
|
|
1303
1200
|
* @returns {string} Rendered LIMIT clause
|
|
1304
1201
|
*/
|
|
1305
|
-
function renderLimit(limit, env) {
|
|
1202
|
+
function renderLimit( limit, env ) {
|
|
1306
1203
|
let result = '';
|
|
1307
1204
|
if (limit.rows !== undefined)
|
|
1308
1205
|
result += `LIMIT ${renderExpr(limit.rows, env)}`;
|
|
@@ -1321,8 +1218,8 @@ function toSqlDdl(csn, options) {
|
|
|
1321
1218
|
* @param {object} env Render environment
|
|
1322
1219
|
* @returns {string} Rendered ORDER BY entry
|
|
1323
1220
|
*/
|
|
1324
|
-
function renderOrderByEntry(entry, env) {
|
|
1325
|
-
let result = renderExpr(entry, env
|
|
1221
|
+
function renderOrderByEntry( entry, env ) {
|
|
1222
|
+
let result = renderExpr(entry, env);
|
|
1326
1223
|
if (entry.sort)
|
|
1327
1224
|
result += ` ${entry.sort.toUpperCase()}`;
|
|
1328
1225
|
|
|
@@ -1340,7 +1237,7 @@ function toSqlDdl(csn, options) {
|
|
|
1340
1237
|
* @param {CSN.Element} elm CSN element
|
|
1341
1238
|
* @returns {string} Rendered type reference
|
|
1342
1239
|
*/
|
|
1343
|
-
function renderTypeReference(artifactName, elementName, elm) {
|
|
1240
|
+
function renderTypeReference( artifactName, elementName, elm ) {
|
|
1344
1241
|
let result = '';
|
|
1345
1242
|
|
|
1346
1243
|
// Anonymous structured type: Not supported with SQL (but shouldn't happen anyway after forHana flattened them)
|
|
@@ -1382,7 +1279,7 @@ function toSqlDdl(csn, options) {
|
|
|
1382
1279
|
* @param {string} typeName Name of the type
|
|
1383
1280
|
* @returns {string} Rendered type
|
|
1384
1281
|
*/
|
|
1385
|
-
function renderBuiltinType(typeName) {
|
|
1282
|
+
function renderBuiltinType( typeName ) {
|
|
1386
1283
|
const forHanaRenamesToEarly = {
|
|
1387
1284
|
'cds.UTCDateTime': 'cds.DateTime',
|
|
1388
1285
|
'cds.UTCTimestamp': 'cds.Timestamp',
|
|
@@ -1402,7 +1299,7 @@ function toSqlDdl(csn, options) {
|
|
|
1402
1299
|
* @param {boolean} deltaMode Look for a $notNull and use that with precedence over notNull
|
|
1403
1300
|
* @returns {string} NULL/NOT NULL or ''
|
|
1404
1301
|
*/
|
|
1405
|
-
function renderNullability(obj, treatKeyAsNotNull = false, deltaMode = false) {
|
|
1302
|
+
function renderNullability( obj, treatKeyAsNotNull = false, deltaMode = false ) {
|
|
1406
1303
|
if (deltaMode && obj.$notNull !== undefined) { // can be set via compare.js if it goes from "not null" to implicit "null"
|
|
1407
1304
|
return obj.$notNull ? ' NOT NULL' : ' NULL';
|
|
1408
1305
|
}
|
|
@@ -1422,7 +1319,7 @@ function toSqlDdl(csn, options) {
|
|
|
1422
1319
|
* @param {CSN.Element} elm CSN element
|
|
1423
1320
|
* @returns {string} Rendered type parameters
|
|
1424
1321
|
*/
|
|
1425
|
-
function renderTypeParameters(elm) {
|
|
1322
|
+
function renderTypeParameters( elm ) {
|
|
1426
1323
|
const params = [];
|
|
1427
1324
|
// Length, precision and scale (even if incomplete)
|
|
1428
1325
|
if (elm.length !== undefined)
|
|
@@ -1444,7 +1341,7 @@ function toSqlDdl(csn, options) {
|
|
|
1444
1341
|
return params.length === 0 ? '' : `(${params.join(', ')})`;
|
|
1445
1342
|
}
|
|
1446
1343
|
|
|
1447
|
-
function renderExpressionLiteral(x) {
|
|
1344
|
+
function renderExpressionLiteral( x ) {
|
|
1448
1345
|
// Literal value, possibly with explicit 'literal' property
|
|
1449
1346
|
switch (x.literal || typeof x.val) {
|
|
1450
1347
|
case 'number':
|
|
@@ -1478,7 +1375,7 @@ function toSqlDdl(csn, options) {
|
|
|
1478
1375
|
}
|
|
1479
1376
|
}
|
|
1480
1377
|
|
|
1481
|
-
function renderExpressionRef(x, env) {
|
|
1378
|
+
function renderExpressionRef( x, env ) {
|
|
1482
1379
|
if (!x.param && !x.global) {
|
|
1483
1380
|
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1484
1381
|
|
|
@@ -1610,7 +1507,7 @@ function toSqlDdl(csn, options) {
|
|
|
1610
1507
|
* @param {number} idx index of the path step in the overall path
|
|
1611
1508
|
* @returns {string} Rendered path step
|
|
1612
1509
|
*/
|
|
1613
|
-
function renderPathStep(s, idx) {
|
|
1510
|
+
function renderPathStep( s, idx ) {
|
|
1614
1511
|
// Simple id or absolute name
|
|
1615
1512
|
if (typeof (s) === 'string') {
|
|
1616
1513
|
// TODO: When is this actually executed and not handled already in renderExpr?
|
|
@@ -1650,7 +1547,7 @@ function toSqlDdl(csn, options) {
|
|
|
1650
1547
|
if (s.where) {
|
|
1651
1548
|
// Filter, possibly with cardinality
|
|
1652
1549
|
// FIXME: Does SQL understand filter cardinalities?
|
|
1653
|
-
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env
|
|
1550
|
+
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env)}]`;
|
|
1654
1551
|
}
|
|
1655
1552
|
return result;
|
|
1656
1553
|
}
|
|
@@ -1659,10 +1556,9 @@ function toSqlDdl(csn, options) {
|
|
|
1659
1556
|
}
|
|
1660
1557
|
}
|
|
1661
1558
|
|
|
1662
|
-
function renderWindowFunction(funcName, node, fctEnv) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
r += ` ${suffix} (${renderExpr(node.xpr.slice(1), fctEnv)})`; // do not pass suffix in renderExpr
|
|
1559
|
+
function renderWindowFunction( funcName, node, fctEnv ) {
|
|
1560
|
+
let r = `${funcName}(${renderArgs(node, '=>', fctEnv, null)}) `;
|
|
1561
|
+
r += renderExpr(node.xpr, fctEnv); // xpr[0] is 'over'
|
|
1666
1562
|
return r;
|
|
1667
1563
|
}
|
|
1668
1564
|
|
|
@@ -1672,17 +1568,18 @@ function toSqlDdl(csn, options) {
|
|
|
1672
1568
|
* @param {object} env Render environment
|
|
1673
1569
|
* @returns {object} Render environment with increased indent
|
|
1674
1570
|
*/
|
|
1675
|
-
function increaseIndent(env) {
|
|
1571
|
+
function increaseIndent( env ) {
|
|
1676
1572
|
return Object.assign({}, env, { indent: `${env.indent} ` });
|
|
1677
1573
|
}
|
|
1678
1574
|
/**
|
|
1679
1575
|
* Returns a copy of 'env' with alterMode set to true
|
|
1680
1576
|
*
|
|
1681
1577
|
* @param {object} env Render environment
|
|
1578
|
+
* @param {string} changeType 'extension' or 'migration'
|
|
1682
1579
|
* @returns {object} Render environment with alterMode
|
|
1683
1580
|
*/
|
|
1684
|
-
function activateAlterMode(env) {
|
|
1685
|
-
return Object.assign({ alterMode: true }, env);
|
|
1581
|
+
function activateAlterMode( env, changeType ) {
|
|
1582
|
+
return Object.assign({ alterMode: true, changeType }, env);
|
|
1686
1583
|
}
|
|
1687
1584
|
}
|
|
1688
1585
|
|
|
@@ -1693,8 +1590,8 @@ function toSqlDdl(csn, options) {
|
|
|
1693
1590
|
* @param {string} sqlDialect
|
|
1694
1591
|
* @return {string}
|
|
1695
1592
|
*/
|
|
1696
|
-
function renderStringForSql(str, sqlDialect) {
|
|
1697
|
-
if (sqlDialect === 'hana' || sqlDialect === 'sqlite') {
|
|
1593
|
+
function renderStringForSql( str, sqlDialect ) {
|
|
1594
|
+
if (sqlDialect === 'hana' || sqlDialect === 'sqlite' || sqlDialect === 'h2') {
|
|
1698
1595
|
// SQLite
|
|
1699
1596
|
// ======
|
|
1700
1597
|
// SQLite's tokenizer available at
|
|
@@ -1704,6 +1601,13 @@ function renderStringForSql(str, sqlDialect) {
|
|
|
1704
1601
|
// <https://sqlite.org/nulinstr.html>.
|
|
1705
1602
|
//
|
|
1706
1603
|
//
|
|
1604
|
+
// H2
|
|
1605
|
+
// ======
|
|
1606
|
+
// H2's tokenizer available at
|
|
1607
|
+
// <https://github.com/h2database/h2database/blob/master/h2/src/main/org/h2/command/Tokenizer.java>.
|
|
1608
|
+
// For strings, see method "readCharacterString()".
|
|
1609
|
+
//
|
|
1610
|
+
//
|
|
1707
1611
|
// HANA
|
|
1708
1612
|
// ====
|
|
1709
1613
|
// Respects the specification available at
|