@sap/cds-compiler 3.4.0 → 3.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +61 -38
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +83 -55
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/propagator.js +22 -29
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- 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 +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +33 -22
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +2 -3
- package/lib/modelCompare/filter.js +0 -83
package/lib/render/toHdbcds.js
CHANGED
|
@@ -7,9 +7,9 @@ const {
|
|
|
7
7
|
} = require('../model/csnUtils');
|
|
8
8
|
const keywords = require('../base/keywords');
|
|
9
9
|
const {
|
|
10
|
-
renderFunc,
|
|
10
|
+
renderFunc, createExpressionRenderer, getRealName, addContextMarkers, addIntermediateContexts,
|
|
11
11
|
hasHanaComment, getHanaComment, funcWithoutParen, getSqlSnippets,
|
|
12
|
-
cdsToSqlTypes, cdsToHdbcdsTypes,
|
|
12
|
+
cdsToSqlTypes, cdsToHdbcdsTypes, withoutCast,
|
|
13
13
|
} = require('./utils/common');
|
|
14
14
|
const {
|
|
15
15
|
renderReferentialConstraint,
|
|
@@ -70,37 +70,41 @@ function toHdbcdsSource(csn, options) {
|
|
|
70
70
|
info, warning, error, throwWithAnyError,
|
|
71
71
|
} = makeMessageFunction(csn, options, 'to.hdbcds');
|
|
72
72
|
|
|
73
|
-
const
|
|
73
|
+
const exprRenderer = createExpressionRenderer({
|
|
74
74
|
finalize: x => x,
|
|
75
|
-
|
|
76
|
-
let typeRef = renderTypeReference(x.cast, env);
|
|
77
|
-
|
|
75
|
+
typeCast(x) {
|
|
76
|
+
let typeRef = renderTypeReference(x.cast, this.env);
|
|
78
77
|
// inside a cast expression, the cds and hana cds types need to be mapped to hana sql types
|
|
79
78
|
const hanaSqlType = cdsToSqlTypes.hana[x.cast.type] || cdsToSqlTypes.standard[x.cast.type];
|
|
80
79
|
if (hanaSqlType) {
|
|
81
80
|
const typeRefWithoutParams = typeRef.substring(0, typeRef.indexOf('(')) || typeRef;
|
|
82
81
|
typeRef = typeRef.replace(typeRefWithoutParams, hanaSqlType);
|
|
83
82
|
}
|
|
84
|
-
return `CAST(${renderExpr(x
|
|
83
|
+
return `CAST(${this.renderExpr(withoutCast(x))} AS ${typeRef})`;
|
|
85
84
|
},
|
|
86
85
|
val: renderExpressionLiteral,
|
|
87
86
|
enum: x => `#${x['#']}`,
|
|
88
87
|
ref: renderExpressionRef,
|
|
89
|
-
aliasOnly
|
|
90
|
-
return x.as;
|
|
91
|
-
},
|
|
88
|
+
aliasOnly: x => x.as,
|
|
92
89
|
windowFunction: renderExpressionFunc,
|
|
93
90
|
func: renderExpressionFunc,
|
|
94
|
-
xpr(x
|
|
95
|
-
if (this.
|
|
96
|
-
return `(${
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
xpr(x) {
|
|
92
|
+
if (this.isNestedXpr && !x.cast)
|
|
93
|
+
return `(${this.renderSubExpr(x.xpr)})`;
|
|
94
|
+
return this.renderSubExpr(x.xpr);
|
|
95
|
+
},
|
|
96
|
+
SELECT(x) {
|
|
97
|
+
return `(${renderQuery(x, false, increaseIndent(this.env))})`;
|
|
98
|
+
},
|
|
99
|
+
SET(x) {
|
|
100
|
+
return `${renderQuery(x, false, increaseIndent(this.env))}`;
|
|
99
101
|
},
|
|
100
|
-
SELECT: (x, env) => `(${renderQuery(x, false, increaseIndent(env))})`,
|
|
101
|
-
SET: (x, env) => `${renderQuery(x, false, increaseIndent(env))}`,
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
function renderExpr(x, env) {
|
|
105
|
+
return exprRenderer.renderExpr(x, env);
|
|
106
|
+
}
|
|
107
|
+
|
|
104
108
|
checkCSNVersion(csn, options);
|
|
105
109
|
|
|
106
110
|
const hdbcdsResult = Object.create(null);
|
|
@@ -647,7 +651,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
647
651
|
result = `(${result} ${source.join} `;
|
|
648
652
|
result += `join ${renderViewSource(source.args[i], env)}`;
|
|
649
653
|
if (source.on)
|
|
650
|
-
result += ` on ${renderExpr(source.on, env
|
|
654
|
+
result += ` on ${renderExpr(source.on, env)}`;
|
|
651
655
|
|
|
652
656
|
result += ')';
|
|
653
657
|
}
|
|
@@ -688,7 +692,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
688
692
|
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
689
693
|
|
|
690
694
|
if (path.ref[0].where)
|
|
691
|
-
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env
|
|
695
|
+
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env)}]`;
|
|
692
696
|
|
|
693
697
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
694
698
|
if (path.ref.length > 1)
|
|
@@ -761,7 +765,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
761
765
|
error(null, env.path, { tokensymbol: 'key', $reviewed: true }, 'Unexpected $(TOKENSYMBOL) in subquery');
|
|
762
766
|
|
|
763
767
|
const key = (!env.skipKeys && (col.key || (element && element.key)) ? 'key ' : '');
|
|
764
|
-
result += key + renderExpr(col, env
|
|
768
|
+
result += key + renderExpr(withoutCast(col), env);
|
|
765
769
|
let alias = col.as || col.func;
|
|
766
770
|
// HANA requires an alias for 'key' columns just for syntactical reasons
|
|
767
771
|
// FIXME: This will not complain for non-refs (but that should be checked in forRelationalDB)
|
|
@@ -779,7 +783,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
779
783
|
// Redirections are never flattened (don't exist in HANA)
|
|
780
784
|
result += ` : redirected to ${renderAbsoluteNameWithQuotes(col.cast.target, env)}`;
|
|
781
785
|
if (col.cast.on)
|
|
782
|
-
result += ` on ${renderExpr(col.cast.on, env
|
|
786
|
+
result += ` on ${renderExpr(col.cast.on, env)}`;
|
|
783
787
|
}
|
|
784
788
|
|
|
785
789
|
return result;
|
|
@@ -910,13 +914,13 @@ function toHdbcdsSource(csn, options) {
|
|
|
910
914
|
*/
|
|
911
915
|
function renderSelectProperties(select, alreadyRendered, env) {
|
|
912
916
|
if (select.where)
|
|
913
|
-
alreadyRendered += `${continueIndent(alreadyRendered, env)}where ${renderExpr(select.where, env
|
|
917
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}where ${renderExpr(select.where, env)}`;
|
|
914
918
|
|
|
915
919
|
if (select.groupBy)
|
|
916
|
-
alreadyRendered += `${continueIndent(alreadyRendered, env)}group by ${select.groupBy.map(expr => renderExpr(expr, env
|
|
920
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}group by ${select.groupBy.map(expr => renderExpr(expr, env)).join(', ')}`;
|
|
917
921
|
|
|
918
922
|
if (select.having)
|
|
919
|
-
alreadyRendered += `${continueIndent(alreadyRendered, env)}having ${renderExpr(select.having, env
|
|
923
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}having ${renderExpr(select.having, env)}`;
|
|
920
924
|
|
|
921
925
|
if (select.orderBy)
|
|
922
926
|
alreadyRendered += `${continueIndent(alreadyRendered, env)}order by ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
@@ -970,7 +974,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
970
974
|
* @returns {string} Rendered order by
|
|
971
975
|
*/
|
|
972
976
|
function renderOrderByEntry(entry, env) {
|
|
973
|
-
let result = renderExpr(entry, env
|
|
977
|
+
let result = renderExpr(entry, env);
|
|
974
978
|
if (entry.sort)
|
|
975
979
|
result += ` ${entry.sort}`;
|
|
976
980
|
|
|
@@ -1118,7 +1122,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1118
1122
|
|
|
1119
1123
|
// ON-condition (if any)
|
|
1120
1124
|
if (elm.on) {
|
|
1121
|
-
result += ` on ${renderExpr(elm.on, env
|
|
1125
|
+
result += ` on ${renderExpr(elm.on, env)}`;
|
|
1122
1126
|
}
|
|
1123
1127
|
else if (elm.targetAspect && elm.targetAspect.elements) { // anonymous aspect
|
|
1124
1128
|
const childEnv = increaseIndent(env);
|
|
@@ -1159,7 +1163,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1159
1163
|
* @param {number} idx Path position
|
|
1160
1164
|
* @returns {string} Rendered path step
|
|
1161
1165
|
*/
|
|
1162
|
-
function renderPathStep(s, idx, ref, env
|
|
1166
|
+
function renderPathStep(s, idx, ref, env) {
|
|
1163
1167
|
// Simple id or absolute name
|
|
1164
1168
|
if (typeof s === 'string') {
|
|
1165
1169
|
// HANA-specific extra magic (should actually be in forRelationalDB)
|
|
@@ -1210,7 +1214,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1210
1214
|
}
|
|
1211
1215
|
if (s.where) {
|
|
1212
1216
|
// Filter, possibly with cardinality
|
|
1213
|
-
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env
|
|
1217
|
+
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env)}]`;
|
|
1214
1218
|
}
|
|
1215
1219
|
return result;
|
|
1216
1220
|
}
|
|
@@ -1250,16 +1254,15 @@ function toHdbcdsSource(csn, options) {
|
|
|
1250
1254
|
* Render the given expression x - which has a .func property
|
|
1251
1255
|
*
|
|
1252
1256
|
* @param {object} x
|
|
1253
|
-
* @param {CdlRenderEnvironment} env
|
|
1254
1257
|
* @returns {string}
|
|
1255
1258
|
*/
|
|
1256
|
-
function renderExpressionFunc(x
|
|
1259
|
+
function renderExpressionFunc(x) {
|
|
1257
1260
|
const regex = RegExp(/^[a-zA-Z][\w#$]*$/, 'g');
|
|
1258
1261
|
const funcName = regex.test(x.func) ? x.func : quoteId(x.func);
|
|
1259
1262
|
// we can't quote functions with parens, issue warning if it is a reserved keyword
|
|
1260
1263
|
if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
|
|
1261
1264
|
warning(null, x.$location, { id: uppercaseAndUnderscore(funcName) }, 'The identifier $(ID) is a SAP HANA keyword');
|
|
1262
|
-
return renderFunc(funcName, x, 'hana', a => renderArgs(a, '=>', env));
|
|
1265
|
+
return renderFunc(funcName, x, 'hana', a => renderArgs(a, '=>', this.env));
|
|
1263
1266
|
}
|
|
1264
1267
|
|
|
1265
1268
|
/**
|
|
@@ -1267,7 +1270,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1267
1270
|
* @returns {string} Rendered expression
|
|
1268
1271
|
* @todo no extra magic with x.param or x.global
|
|
1269
1272
|
*/
|
|
1270
|
-
function renderExpressionRef(x
|
|
1273
|
+
function renderExpressionRef(x) {
|
|
1271
1274
|
if (!x.param && !x.global) {
|
|
1272
1275
|
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1273
1276
|
if (x.ref[0] === '$user') {
|
|
@@ -1294,7 +1297,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1294
1297
|
return renderStringForHdbcds(magicReplacement);
|
|
1295
1298
|
}
|
|
1296
1299
|
}
|
|
1297
|
-
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref,
|
|
1300
|
+
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref, this.env)).join('.')}`;
|
|
1298
1301
|
}
|
|
1299
1302
|
|
|
1300
1303
|
/**
|
|
@@ -1310,12 +1313,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
1310
1313
|
const args = node.args ? node.args : {};
|
|
1311
1314
|
// Positional arguments
|
|
1312
1315
|
if (Array.isArray(args))
|
|
1313
|
-
return args.map(arg => renderExpr(arg, env
|
|
1316
|
+
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
1314
1317
|
|
|
1315
1318
|
// Named arguments (object/dict)
|
|
1316
1319
|
else if (typeof args === 'object')
|
|
1317
1320
|
// if this is a function param which is not a reference to the model, we must not quote it
|
|
1318
|
-
return Object.keys(args).map(key => `${node.func ? key : formatIdentifier(key)} ${sep} ${renderExpr(args[key], env
|
|
1321
|
+
return Object.keys(args).map(key => `${node.func ? key : formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1319
1322
|
|
|
1320
1323
|
|
|
1321
1324
|
throw new ModelError(`Unknown args: ${JSON.stringify(args)}`);
|
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');
|
|
@@ -80,11 +83,13 @@ function toSqlDdl(csn, options) {
|
|
|
80
83
|
error, warning, info, throwWithAnyError,
|
|
81
84
|
} = makeMessageFunction(csn, options, 'to.sql');
|
|
82
85
|
const { quoteSqlId, prepareIdentifier } = getIdentifierUtils(options);
|
|
83
|
-
|
|
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
|
-
},
|
|
139
|
-
},
|
|
140
|
-
/*
|
|
141
|
-
Render association additions as HANA SQL.
|
|
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});`) : [];
|
|
149
|
-
},
|
|
150
|
-
/*
|
|
151
|
-
Render key addition as HANA SQL.
|
|
152
|
-
*/
|
|
153
|
-
addKey(tableName, elementsObj) {
|
|
154
|
-
return [ `ALTER TABLE ${tableName} ADD ${render.primaryKey(elementsObj)}` ];
|
|
155
|
-
},
|
|
156
|
-
/*
|
|
157
|
-
Render column removals as HANA SQL.
|
|
158
|
-
*/
|
|
159
|
-
dropColumns(tableName, sqlIds) {
|
|
160
|
-
return [ `ALTER TABLE ${tableName} DROP ${options.sqlDialect === 'hana' ? '(' : ''}${sqlIds.join(', ')}${options.sqlDialect === 'hana' ? ')' : ''};` ];
|
|
161
|
-
},
|
|
162
|
-
/*
|
|
163
|
-
Render association removals as HANA SQL.
|
|
164
|
-
*/
|
|
165
|
-
dropAssociation(tableName, sqlId) {
|
|
166
|
-
return options.sqlDialect === 'hana' ? [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ] : [];
|
|
167
|
-
},
|
|
168
|
-
/*
|
|
169
|
-
Render primary-key removals as HANA SQL.
|
|
170
|
-
*/
|
|
171
|
-
dropKey(tableName) {
|
|
172
|
-
return [ `ALTER TABLE ${tableName} DROP PRIMARY KEY;` ];
|
|
102
|
+
aliasOnly: x => x.as,
|
|
103
|
+
windowFunction( x) {
|
|
104
|
+
return renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, this.env);
|
|
173
105
|
},
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
*/
|
|
177
|
-
alterColumns(tableName, definitionsStr) {
|
|
178
|
-
return [ `ALTER TABLE ${tableName} ALTER (${definitionsStr});` ];
|
|
106
|
+
func(x) {
|
|
107
|
+
return renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, options.sqlDialect, a => renderArgs(a, '=>', this.env, null));
|
|
179
108
|
},
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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})`;
|
|
109
|
+
xpr(x) {
|
|
110
|
+
if (this.isNestedXpr && !x.cast)
|
|
111
|
+
return `(${this.renderSubExpr(x.xpr)})`;
|
|
112
|
+
return this.renderSubExpr(x.xpr);
|
|
190
113
|
},
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
*/
|
|
194
|
-
alterEntityComment(tableName, comment) {
|
|
195
|
-
return [ `COMMENT ON TABLE ${tableName} IS ${render.comment(comment)};` ];
|
|
114
|
+
SELECT( x) {
|
|
115
|
+
return `(${renderQuery('<subselect>', x, increaseIndent(this.env))})`;
|
|
196
116
|
},
|
|
197
|
-
|
|
198
|
-
|
|
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};` ];
|
|
117
|
+
SET( x) {
|
|
118
|
+
return `(${renderQuery('<union>', x, increaseIndent(this.env))})`;
|
|
214
119
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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'))
|
|
@@ -435,10 +348,10 @@ function toSqlDdl(csn, options) {
|
|
|
435
348
|
return def.old.type === def.new.type &&
|
|
436
349
|
[ 'length', 'precision', 'scale' ].some(param => def.new[param] < def.old[param]);
|
|
437
350
|
}
|
|
438
|
-
function getEltStr(defVariant, eltName) {
|
|
351
|
+
function getEltStr(defVariant, eltName, changeType = 'extension') {
|
|
439
352
|
return defVariant.target
|
|
440
353
|
? renderAssociationElement(eltName, defVariant, env)
|
|
441
|
-
: renderElement(artifactName, eltName, defVariant, null, null, activateAlterMode(env));
|
|
354
|
+
: renderElement(artifactName, eltName, defVariant, null, null, activateAlterMode(env, changeType));
|
|
442
355
|
}
|
|
443
356
|
function getEltStrNoProps(defVariant, eltName, ...props) {
|
|
444
357
|
const defNoProps = Object.assign({}, defVariant);
|
|
@@ -466,7 +379,7 @@ function toSqlDdl(csn, options) {
|
|
|
466
379
|
if (migration.properties) {
|
|
467
380
|
for (const [ prop, def ] of Object.entries(migration.properties)) {
|
|
468
381
|
if (prop === 'doc' && !options.disableHanaComments) { // def.new may be `null`
|
|
469
|
-
const alterComment = render.alterEntityComment(
|
|
382
|
+
const alterComment = render.alterEntityComment(artifactName, def.new);
|
|
470
383
|
addMigration(resultObj, artifactName, false, alterComment);
|
|
471
384
|
}
|
|
472
385
|
else if (sqlSnippetAnnos.includes(prop)) { // NOTE: @sql.replace may be supported in the future
|
|
@@ -475,7 +388,7 @@ function toSqlDdl(csn, options) {
|
|
|
475
388
|
addMigration(resultObj, artifactName, false, null, getUnknownSqlReason(prop, artifactName, def.old, def.new));
|
|
476
389
|
}
|
|
477
390
|
else {
|
|
478
|
-
addMigration(resultObj, artifactName, false, render.alterEntitySqlSnippet(
|
|
391
|
+
addMigration(resultObj, artifactName, false, render.alterEntitySqlSnippet(artifactName, def.new));
|
|
479
392
|
}
|
|
480
393
|
}
|
|
481
394
|
}
|
|
@@ -493,10 +406,10 @@ function toSqlDdl(csn, options) {
|
|
|
493
406
|
|
|
494
407
|
// Remove columns.
|
|
495
408
|
if (removeCols.length)
|
|
496
|
-
addMigration(resultObj, artifactName, true, render.dropColumns(
|
|
409
|
+
addMigration(resultObj, artifactName, true, render.dropColumns(artifactName, removeCols));
|
|
497
410
|
|
|
498
411
|
// Remove associations.
|
|
499
|
-
removeAssocs.forEach(assoc => addMigration(resultObj, artifactName, true, render.dropAssociation(
|
|
412
|
+
removeAssocs.forEach(assoc => addMigration(resultObj, artifactName, true, render.dropAssociation(artifactName, assoc)));
|
|
500
413
|
}
|
|
501
414
|
}
|
|
502
415
|
|
|
@@ -507,8 +420,8 @@ function toSqlDdl(csn, options) {
|
|
|
507
420
|
const sqlId = quoteSqlId(eltName);
|
|
508
421
|
changeElementsDuplicateChecker.addElement(sqlId, undefined, eltName);
|
|
509
422
|
|
|
510
|
-
const eltStrOld = getEltStr(def.old, eltName);
|
|
511
|
-
const eltStrNew = getEltStr(def.new, eltName);
|
|
423
|
+
const eltStrOld = getEltStr(def.old, eltName, 'migration');
|
|
424
|
+
const eltStrNew = getEltStr(def.new, eltName, 'migration');
|
|
512
425
|
if (eltStrNew === eltStrOld)
|
|
513
426
|
return; // Prevent spurious migrations, where the column DDL does not change.
|
|
514
427
|
|
|
@@ -535,7 +448,7 @@ function toSqlDdl(csn, options) {
|
|
|
535
448
|
const eltStrOldNoDoc = getEltStrNoProps(def.old, eltName, 'doc');
|
|
536
449
|
const eltStrNewNoDoc = getEltStrNoProps(def.new, eltName, 'doc');
|
|
537
450
|
if (eltStrOldNoDoc === eltStrNewNoDoc) { // only `doc` changed
|
|
538
|
-
const alterComment = render.alterColumnComment(
|
|
451
|
+
const alterComment = render.alterColumnComment(artifactName, sqlId, def.new.doc);
|
|
539
452
|
addMigration(resultObj, artifactName, false, alterComment);
|
|
540
453
|
continue;
|
|
541
454
|
}
|
|
@@ -545,16 +458,16 @@ function toSqlDdl(csn, options) {
|
|
|
545
458
|
// Lossy change because either an association is removed and/or added, or the type size is reduced.
|
|
546
459
|
// Drop old element and re-add it in its new shape.
|
|
547
460
|
const drop = def.old.target
|
|
548
|
-
? render.dropAssociation(
|
|
549
|
-
: render.dropColumns(
|
|
461
|
+
? render.dropAssociation(artifactName, sqlId)
|
|
462
|
+
: render.dropColumns(artifactName, [ sqlId ]);
|
|
550
463
|
const add = def.new.target
|
|
551
|
-
? render.addAssociations(artifactName,
|
|
552
|
-
: render.
|
|
464
|
+
? render.addAssociations(artifactName, { [eltName]: def.new }, env)
|
|
465
|
+
: render.addColumnsFromElementsObj(artifactName, { [eltName]: def.new }, env);
|
|
553
466
|
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add));
|
|
554
467
|
}
|
|
555
468
|
else {
|
|
556
469
|
// Lossless change: no associations directly affected, no size reduction.
|
|
557
|
-
addMigration(resultObj, artifactName, false, render.alterColumns(
|
|
470
|
+
addMigration(resultObj, artifactName, false, render.alterColumns(artifactName, sqlId, def, eltStrNew));
|
|
558
471
|
}
|
|
559
472
|
}
|
|
560
473
|
}
|
|
@@ -682,14 +595,14 @@ function toSqlDdl(csn, options) {
|
|
|
682
595
|
const tableName = renderArtifactName(artifactName);
|
|
683
596
|
if (duplicateChecker)
|
|
684
597
|
duplicateChecker.addArtifact(tableName, undefined, artifactName);
|
|
685
|
-
const elements = render.
|
|
686
|
-
const associations = render.addAssociations(artifactName,
|
|
598
|
+
const elements = render.addColumnsFromElementsObj(artifactName, extElements, env, duplicateChecker);
|
|
599
|
+
const associations = render.addAssociations(artifactName, extElements, env);
|
|
687
600
|
if (elements.length + associations.length > 0)
|
|
688
601
|
addMigration(resultObj, artifactName, false, [ ...elements, ...associations ]);
|
|
689
602
|
|
|
690
603
|
if (Object.values(extElements).some(elt => elt.key)) {
|
|
691
|
-
const drop = render.dropKey(
|
|
692
|
-
const add = render.addKey(
|
|
604
|
+
const drop = render.dropKey(artifactName);
|
|
605
|
+
const add = render.addKey(artifactName, artifactElements);
|
|
693
606
|
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add));
|
|
694
607
|
}
|
|
695
608
|
}
|
|
@@ -752,11 +665,12 @@ function toSqlDdl(csn, options) {
|
|
|
752
665
|
if (elm.virtual || elm.target)
|
|
753
666
|
return '';
|
|
754
667
|
|
|
668
|
+
const isPostgresAlterColumn = env.alterMode && env.changeType === 'migration' && options.sqlDialect === 'postgres';
|
|
755
669
|
const quotedElementName = quoteSqlId(elementName);
|
|
756
670
|
if (duplicateChecker)
|
|
757
671
|
duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
|
|
758
672
|
|
|
759
|
-
let result = `${env.indent + quotedElementName} ${renderTypeReference(artifactName, elementName, elm)
|
|
673
|
+
let result = `${env.indent + quotedElementName}${isPostgresAlterColumn ? ' TYPE' : ''} ${renderTypeReference(artifactName, elementName, elm)
|
|
760
674
|
}${renderNullability(elm, true, env.alterMode)}`;
|
|
761
675
|
if (elm.default)
|
|
762
676
|
result += ` DEFAULT ${renderExpr(elm.default, env)}`;
|
|
@@ -811,7 +725,7 @@ function toSqlDdl(csn, options) {
|
|
|
811
725
|
}
|
|
812
726
|
result += ' JOIN ';
|
|
813
727
|
result += `${renderArtifactName(elm.target)} AS ${quoteSqlId(elementName)} ON (`;
|
|
814
|
-
result += `${renderExpr(elm.on, env
|
|
728
|
+
result += `${renderExpr(elm.on, env)})`;
|
|
815
729
|
}
|
|
816
730
|
return result;
|
|
817
731
|
}
|
|
@@ -953,7 +867,7 @@ function toSqlDdl(csn, options) {
|
|
|
953
867
|
result += renderJoinCardinality(source.cardinality);
|
|
954
868
|
result += `JOIN ${renderViewSource(artifactName, source.args[i], env)}`;
|
|
955
869
|
if (source.on)
|
|
956
|
-
result += ` ON ${renderExpr(source.on, env
|
|
870
|
+
result += ` ON ${renderExpr(source.on, env)}`;
|
|
957
871
|
|
|
958
872
|
result += ')';
|
|
959
873
|
}
|
|
@@ -1066,7 +980,7 @@ function toSqlDdl(csn, options) {
|
|
|
1066
980
|
result += '()';
|
|
1067
981
|
}
|
|
1068
982
|
if (path.ref[0].where)
|
|
1069
|
-
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env
|
|
983
|
+
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env)}]`;
|
|
1070
984
|
|
|
1071
985
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
1072
986
|
if (path.ref.length > 1)
|
|
@@ -1091,12 +1005,12 @@ function toSqlDdl(csn, options) {
|
|
|
1091
1005
|
const args = node.args ? node.args : {};
|
|
1092
1006
|
// Positional arguments
|
|
1093
1007
|
if (Array.isArray(args))
|
|
1094
|
-
return args.map(arg => renderExpr(arg, env
|
|
1008
|
+
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
1095
1009
|
|
|
1096
1010
|
// Named arguments (object/dict)
|
|
1097
1011
|
else if (typeof args === 'object')
|
|
1098
1012
|
// 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
|
|
1013
|
+
return Object.keys(args).map(key => `${node.func ? key : decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1100
1014
|
|
|
1101
1015
|
|
|
1102
1016
|
throw new ModelError(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1135,7 +1049,7 @@ function toSqlDdl(csn, options) {
|
|
|
1135
1049
|
result += `${env.indent}NULL AS ${quoteSqlId(col.as || leaf)}`;
|
|
1136
1050
|
}
|
|
1137
1051
|
else {
|
|
1138
|
-
result = env.indent + renderExpr(col, env
|
|
1052
|
+
result = env.indent + renderExpr(withoutCast(col), env);
|
|
1139
1053
|
if (col.as)
|
|
1140
1054
|
result += ` AS ${quoteSqlId(col.as)}`;
|
|
1141
1055
|
else if (col.func)
|
|
@@ -1207,7 +1121,7 @@ function toSqlDdl(csn, options) {
|
|
|
1207
1121
|
pIdentifier = quoteSqlId(pn);
|
|
1208
1122
|
let pstr = `IN ${pIdentifier} ${renderTypeReference(artifactName, pn, p)}`;
|
|
1209
1123
|
if (p.default)
|
|
1210
|
-
pstr += ` DEFAULT ${renderExpr(p.default)}`;
|
|
1124
|
+
pstr += ` DEFAULT ${renderExpr(p.default, { indent: '' })}`;
|
|
1211
1125
|
|
|
1212
1126
|
parray.push(pstr);
|
|
1213
1127
|
}
|
|
@@ -1262,19 +1176,19 @@ function toSqlDdl(csn, options) {
|
|
|
1262
1176
|
result += `SELECT${select.distinct ? ' DISTINCT' : ''}`;
|
|
1263
1177
|
// FIXME: We probably also need to consider `excluding` here ?
|
|
1264
1178
|
result += `\n${(select.columns || [ '*' ])
|
|
1265
|
-
.filter(col => !
|
|
1179
|
+
.filter(col => !select.mixin?.[firstPathStepId(col.ref)]) // No mixin columns
|
|
1266
1180
|
.map(col => renderViewColumn(col, elements || select.elements, childEnv))
|
|
1267
1181
|
.filter(s => s !== '')
|
|
1268
1182
|
.join(',\n')}\n`;
|
|
1269
1183
|
result += `${env.indent}FROM ${renderViewSource(artifactName, select.from, env)}`;
|
|
1270
1184
|
if (select.where)
|
|
1271
|
-
result += `\n${env.indent}WHERE ${renderExpr(select.where, env
|
|
1185
|
+
result += `\n${env.indent}WHERE ${renderExpr(select.where, env)}`;
|
|
1272
1186
|
|
|
1273
1187
|
if (select.groupBy)
|
|
1274
|
-
result += `\n${env.indent}GROUP BY ${select.groupBy.map(expr => renderExpr(expr, env
|
|
1188
|
+
result += `\n${env.indent}GROUP BY ${select.groupBy.map(expr => renderExpr(expr, env)).join(', ')}`;
|
|
1275
1189
|
|
|
1276
1190
|
if (select.having)
|
|
1277
|
-
result += `\n${env.indent}HAVING ${renderExpr(select.having, env
|
|
1191
|
+
result += `\n${env.indent}HAVING ${renderExpr(select.having, env)}`;
|
|
1278
1192
|
|
|
1279
1193
|
if (select.orderBy)
|
|
1280
1194
|
result += `\n${env.indent}ORDER BY ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
@@ -1322,7 +1236,7 @@ function toSqlDdl(csn, options) {
|
|
|
1322
1236
|
* @returns {string} Rendered ORDER BY entry
|
|
1323
1237
|
*/
|
|
1324
1238
|
function renderOrderByEntry(entry, env) {
|
|
1325
|
-
let result = renderExpr(entry, env
|
|
1239
|
+
let result = renderExpr(entry, env);
|
|
1326
1240
|
if (entry.sort)
|
|
1327
1241
|
result += ` ${entry.sort.toUpperCase()}`;
|
|
1328
1242
|
|
|
@@ -1650,7 +1564,7 @@ function toSqlDdl(csn, options) {
|
|
|
1650
1564
|
if (s.where) {
|
|
1651
1565
|
// Filter, possibly with cardinality
|
|
1652
1566
|
// FIXME: Does SQL understand filter cardinalities?
|
|
1653
|
-
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env
|
|
1567
|
+
result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env)}]`;
|
|
1654
1568
|
}
|
|
1655
1569
|
return result;
|
|
1656
1570
|
}
|
|
@@ -1660,9 +1574,8 @@ function toSqlDdl(csn, options) {
|
|
|
1660
1574
|
}
|
|
1661
1575
|
|
|
1662
1576
|
function renderWindowFunction(funcName, node, fctEnv) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
r += ` ${suffix} (${renderExpr(node.xpr.slice(1), fctEnv)})`; // do not pass suffix in renderExpr
|
|
1577
|
+
let r = `${funcName}(${renderArgs(node, '=>', fctEnv, null)}) `;
|
|
1578
|
+
r += renderExpr(node.xpr, fctEnv); // xpr[0] is 'over'
|
|
1666
1579
|
return r;
|
|
1667
1580
|
}
|
|
1668
1581
|
|
|
@@ -1679,10 +1592,11 @@ function toSqlDdl(csn, options) {
|
|
|
1679
1592
|
* Returns a copy of 'env' with alterMode set to true
|
|
1680
1593
|
*
|
|
1681
1594
|
* @param {object} env Render environment
|
|
1595
|
+
* @param {string} changeType 'extension' or 'migration'
|
|
1682
1596
|
* @returns {object} Render environment with alterMode
|
|
1683
1597
|
*/
|
|
1684
|
-
function activateAlterMode(env) {
|
|
1685
|
-
return Object.assign({ alterMode: true }, env);
|
|
1598
|
+
function activateAlterMode(env, changeType) {
|
|
1599
|
+
return Object.assign({ alterMode: true, changeType }, env);
|
|
1686
1600
|
}
|
|
1687
1601
|
}
|
|
1688
1602
|
|