@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/bin/cdsc.js +3 -4
  3. package/bin/cdshi.js +19 -6
  4. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  5. package/lib/api/main.js +6 -3
  6. package/lib/base/message-registry.js +61 -38
  7. package/lib/base/messages.js +7 -3
  8. package/lib/checks/.eslintrc.json +2 -0
  9. package/lib/compiler/.eslintrc.json +4 -1
  10. package/lib/compiler/assert-consistency.js +8 -6
  11. package/lib/compiler/builtins.js +13 -13
  12. package/lib/compiler/checks.js +50 -33
  13. package/lib/compiler/define.js +9 -6
  14. package/lib/compiler/extend.js +83 -55
  15. package/lib/compiler/finalize-parse-cdl.js +3 -3
  16. package/lib/compiler/populate.js +16 -5
  17. package/lib/compiler/propagator.js +22 -29
  18. package/lib/compiler/resolve.js +2 -15
  19. package/lib/compiler/shared.js +4 -4
  20. package/lib/compiler/utils.js +14 -0
  21. package/lib/edm/annotations/genericTranslation.js +68 -56
  22. package/lib/edm/csn2edm.js +214 -174
  23. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  24. package/lib/edm/edmInboundChecks.js +2 -2
  25. package/lib/edm/edmPreprocessor.js +1 -1
  26. package/lib/edm/edmUtils.js +3 -3
  27. package/lib/gen/Dictionary.json +176 -8
  28. package/lib/gen/language.checksum +1 -1
  29. package/lib/gen/language.interp +2 -1
  30. package/lib/gen/languageParser.js +4776 -4513
  31. package/lib/json/from-csn.js +21 -16
  32. package/lib/json/to-csn.js +37 -41
  33. package/lib/language/.eslintrc.json +4 -1
  34. package/lib/language/antlrParser.js +5 -2
  35. package/lib/language/docCommentParser.js +6 -6
  36. package/lib/language/errorStrategy.js +43 -23
  37. package/lib/language/genericAntlrParser.js +54 -95
  38. package/lib/language/language.g4 +92 -66
  39. package/lib/language/multiLineStringParser.js +2 -2
  40. package/lib/language/textUtils.js +2 -2
  41. package/lib/model/csnRefs.js +5 -0
  42. package/lib/modelCompare/compare.js +2 -2
  43. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  44. package/lib/modelCompare/utils/filter.js +99 -0
  45. package/lib/render/.eslintrc.json +1 -0
  46. package/lib/render/toCdl.js +96 -127
  47. package/lib/render/toHdbcds.js +38 -35
  48. package/lib/render/toSql.js +75 -161
  49. package/lib/render/utils/common.js +133 -83
  50. package/lib/render/utils/delta.js +227 -0
  51. package/lib/transform/db/.eslintrc.json +2 -0
  52. package/lib/transform/draft/.eslintrc.json +1 -35
  53. package/lib/transform/forOdataNew.js +33 -22
  54. package/lib/transform/forRelationalDB.js +1 -1
  55. package/lib/transform/localized.js +9 -8
  56. package/lib/transform/odata/typesExposure.js +26 -4
  57. package/lib/transform/transformUtilsNew.js +15 -8
  58. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  59. package/package.json +2 -3
  60. package/lib/modelCompare/filter.js +0 -83
@@ -7,9 +7,9 @@ const {
7
7
  } = require('../model/csnUtils');
8
8
  const keywords = require('../base/keywords');
9
9
  const {
10
- renderFunc, getExpressionRenderer, getRealName, addContextMarkers, addIntermediateContexts,
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 renderExpr = getExpressionRenderer({
73
+ const exprRenderer = createExpressionRenderer({
74
74
  finalize: x => x,
75
- explicitTypeCast(x, env) {
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, env)} AS ${typeRef})`;
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(x, _env) {
90
- return x.as;
91
- },
88
+ aliasOnly: x => x.as,
92
89
  windowFunction: renderExpressionFunc,
93
90
  func: renderExpressionFunc,
94
- xpr(x, env) {
95
- if (this.nestedExpr && !x.cast)
96
- return `(${renderExpr(x.xpr, env, this.inline, true)})`;
97
-
98
- return renderExpr(x.xpr, env, this.inline, true);
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, true, true)}`;
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, true, true)}]`;
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, true);
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, true, true)}`;
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, true, true)}`;
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, true, false, true)).join(', ')}`;
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, true, true)}`;
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, true, false, true);
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, true, true)}`;
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, inline) {
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, inline, true)}]`;
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, env) {
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, env) {
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, env, this.inline)).join('.')}`;
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, true, false, true)).join(', ');
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, true, false, true)}`).join(', ');
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)}`);
@@ -8,8 +8,11 @@ const {
8
8
  } = require('../model/csnUtils');
9
9
  const {
10
10
  renderFunc, cdsToSqlTypes, getHanaComment, hasHanaComment,
11
- getSqlSnippets, getExpressionRenderer,
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
- const renderExpr = getExpressionRenderer({
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
- explicitTypeCast: (x, env) => {
90
+ typeCast(x) {
86
91
  const typeRef = renderBuiltinType(x.cast.type) + renderTypeParameters(x.cast);
87
- return `CAST(${renderExpr(x, env)} AS ${typeRef})`;
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(x, _env) {
98
- return x.as;
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
- Render column modifications as HANA SQL.
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
- Render primary keys as HANA SQL.
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})`;
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
- Render entity-comment modifications as HANA SQL.
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
- 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};` ];
117
+ SET( x) {
118
+ return `(${renderQuery('<union>', x, increaseIndent(this.env))})`;
214
119
  },
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'))
@@ -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(tableName, def.new);
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(tableName, def.new));
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(tableName, removeCols));
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(tableName, assoc)));
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(tableName, sqlId, def.new.doc);
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(tableName, sqlId)
549
- : render.dropColumns(tableName, [ sqlId ]);
461
+ ? render.dropAssociation(artifactName, sqlId)
462
+ : render.dropColumns(artifactName, [ sqlId ]);
550
463
  const add = def.new.target
551
- ? render.addAssociations(artifactName, tableName, { [eltName]: def.new }, env)
552
- : render.addColumns.fromElementsObj(artifactName, tableName, { [eltName]: def.new }, env);
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(tableName, eltStrNew));
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.addColumns.fromElementsObj(artifactName, tableName, extElements, env, duplicateChecker);
686
- const associations = render.addAssociations(artifactName, tableName, extElements, env);
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(tableName);
692
- const add = render.addKey(tableName, artifactElements);
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, true, true)})`;
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, true, true)}`;
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, true, true)}]`;
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, true, false, true)).join(', ');
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, true, false, true)}`).join(', ');
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, true);
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 => !(select.mixin || Object.create(null))[firstPathStepId(col.ref)]) // No mixin columns
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, true, true)}`;
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, true, false, true)).join(', ')}`;
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, true, true)}`;
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, true, false, true);
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, true, true)}]`;
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
- const suffix = node.xpr[0]; // OVER
1664
- let r = `${funcName}(${renderArgs(node, '=>', fctEnv, null)})`;
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