@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.
Files changed (143) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +15 -16
  5. package/bin/cdshi.js +19 -6
  6. package/doc/CHANGELOG_ARCHIVE.md +2 -2
  7. package/doc/CHANGELOG_BETA.md +9 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  9. package/lib/api/main.js +61 -59
  10. package/lib/api/options.js +4 -2
  11. package/lib/api/validate.js +2 -2
  12. package/lib/base/cleanSymbols.js +2 -3
  13. package/lib/base/dictionaries.js +6 -6
  14. package/lib/base/error.js +2 -2
  15. package/lib/base/keywords.js +6 -6
  16. package/lib/base/location.js +11 -12
  17. package/lib/base/message-registry.js +177 -58
  18. package/lib/base/messages.js +252 -180
  19. package/lib/base/model.js +14 -11
  20. package/lib/base/node-helpers.js +9 -10
  21. package/lib/base/optionProcessorHelper.js +138 -129
  22. package/lib/checks/.eslintrc.json +2 -0
  23. package/lib/checks/actionsFunctions.js +5 -5
  24. package/lib/checks/annotationsOData.js +4 -4
  25. package/lib/checks/arrayOfs.js +1 -1
  26. package/lib/checks/cdsPersistence.js +1 -1
  27. package/lib/checks/checkForTypes.js +3 -3
  28. package/lib/checks/defaultValues.js +3 -3
  29. package/lib/checks/elements.js +7 -7
  30. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  31. package/lib/checks/foreignKeys.js +1 -1
  32. package/lib/checks/invalidTarget.js +4 -4
  33. package/lib/checks/managedInType.js +1 -1
  34. package/lib/checks/managedWithoutKeys.js +1 -1
  35. package/lib/checks/nonexpandableStructured.js +5 -3
  36. package/lib/checks/nullableKeys.js +1 -1
  37. package/lib/checks/onConditions.js +5 -6
  38. package/lib/checks/parameters.js +1 -1
  39. package/lib/checks/queryNoDbArtifacts.js +2 -2
  40. package/lib/checks/selectItems.js +4 -4
  41. package/lib/checks/sql-snippets.js +4 -4
  42. package/lib/checks/types.js +7 -7
  43. package/lib/checks/utils.js +4 -4
  44. package/lib/checks/validator.js +16 -13
  45. package/lib/compiler/.eslintrc.json +4 -1
  46. package/lib/compiler/assert-consistency.js +8 -7
  47. package/lib/compiler/builtins.js +14 -14
  48. package/lib/compiler/checks.js +123 -48
  49. package/lib/compiler/define.js +12 -13
  50. package/lib/compiler/extend.js +266 -60
  51. package/lib/compiler/finalize-parse-cdl.js +10 -5
  52. package/lib/compiler/index.js +17 -14
  53. package/lib/compiler/populate.js +14 -6
  54. package/lib/compiler/propagator.js +2 -0
  55. package/lib/compiler/resolve.js +2 -15
  56. package/lib/compiler/shared.js +27 -16
  57. package/lib/compiler/tweak-assocs.js +5 -6
  58. package/lib/compiler/utils.js +20 -0
  59. package/lib/edm/annotations/genericTranslation.js +604 -358
  60. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  61. package/lib/edm/csn2edm.js +275 -222
  62. package/lib/edm/edm.js +17 -3
  63. package/lib/edm/edmAnnoPreprocessor.js +6 -6
  64. package/lib/edm/edmInboundChecks.js +2 -2
  65. package/lib/edm/edmPreprocessor.js +107 -77
  66. package/lib/edm/edmUtils.js +44 -5
  67. package/lib/gen/Dictionary.json +210 -8
  68. package/lib/gen/language.checksum +1 -1
  69. package/lib/gen/language.interp +67 -63
  70. package/lib/gen/language.tokens +81 -81
  71. package/lib/gen/languageLexer.interp +4 -10
  72. package/lib/gen/languageLexer.js +854 -869
  73. package/lib/gen/languageLexer.tokens +79 -81
  74. package/lib/gen/languageParser.js +14309 -13832
  75. package/lib/inspect/inspectModelStatistics.js +2 -2
  76. package/lib/inspect/inspectPropagation.js +6 -6
  77. package/lib/inspect/inspectUtils.js +2 -2
  78. package/lib/json/from-csn.js +102 -55
  79. package/lib/json/to-csn.js +119 -198
  80. package/lib/language/antlrParser.js +5 -2
  81. package/lib/language/docCommentParser.js +6 -6
  82. package/lib/language/errorStrategy.js +43 -23
  83. package/lib/language/genericAntlrParser.js +113 -133
  84. package/lib/language/language.g4 +1550 -1506
  85. package/lib/language/multiLineStringParser.js +3 -3
  86. package/lib/language/textUtils.js +2 -2
  87. package/lib/main.js +3 -3
  88. package/lib/model/csnRefs.js +5 -0
  89. package/lib/model/csnUtils.js +130 -122
  90. package/lib/model/revealInternalProperties.js +1 -1
  91. package/lib/model/sortViews.js +4 -6
  92. package/lib/modelCompare/compare.js +2 -2
  93. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  94. package/lib/modelCompare/utils/filter.js +100 -0
  95. package/lib/optionProcessor.js +5 -0
  96. package/lib/render/.eslintrc.json +1 -0
  97. package/lib/render/DuplicateChecker.js +1 -1
  98. package/lib/render/manageConstraints.js +12 -12
  99. package/lib/render/toCdl.js +311 -276
  100. package/lib/render/toHdbcds.js +97 -94
  101. package/lib/render/toRename.js +5 -5
  102. package/lib/render/toSql.js +127 -223
  103. package/lib/render/utils/common.js +141 -108
  104. package/lib/render/utils/delta.js +227 -0
  105. package/lib/render/utils/sql.js +22 -6
  106. package/lib/render/utils/stringEscapes.js +3 -3
  107. package/lib/transform/db/.eslintrc.json +2 -0
  108. package/lib/transform/db/applyTransformations.js +3 -3
  109. package/lib/transform/db/assertUnique.js +13 -12
  110. package/lib/transform/db/associations.js +5 -5
  111. package/lib/transform/db/cdsPersistence.js +10 -8
  112. package/lib/transform/db/constraints.js +14 -14
  113. package/lib/transform/db/expansion.js +20 -22
  114. package/lib/transform/db/flattening.js +24 -42
  115. package/lib/transform/db/groupByOrderBy.js +3 -3
  116. package/lib/transform/db/temporal.js +6 -6
  117. package/lib/transform/db/transformExists.js +23 -23
  118. package/lib/transform/db/views.js +16 -16
  119. package/lib/transform/draft/.eslintrc.json +1 -35
  120. package/lib/transform/draft/db.js +10 -10
  121. package/lib/transform/draft/odata.js +2 -2
  122. package/lib/transform/forOdataNew.js +8 -29
  123. package/lib/transform/forRelationalDB.js +16 -6
  124. package/lib/transform/localized.js +11 -10
  125. package/lib/transform/odata/toFinalBaseType.js +41 -27
  126. package/lib/transform/odata/typesExposure.js +113 -47
  127. package/lib/transform/parseExpr.js +209 -106
  128. package/lib/transform/transformUtilsNew.js +17 -10
  129. package/lib/transform/translateAssocsToJoins.js +24 -19
  130. package/lib/transform/universalCsn/coreComputed.js +10 -10
  131. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  132. package/lib/transform/universalCsn/utils.js +3 -3
  133. package/lib/utils/file.js +5 -5
  134. package/lib/utils/moduleResolve.js +13 -13
  135. package/lib/utils/objectUtils.js +6 -6
  136. package/lib/utils/term.js +5 -2
  137. package/lib/utils/timetrace.js +51 -24
  138. package/package.json +5 -8
  139. package/share/messages/check-proper-type-of.md +1 -1
  140. package/share/messages/message-explanations.json +1 -1
  141. package/share/messages/redirected-to-complex.md +4 -4
  142. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
  143. package/lib/modelCompare/filter.js +0 -83
@@ -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');
@@ -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
- const renderExpr = getExpressionRenderer({
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
- 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
- },
102
+ aliasOnly: x => x.as,
103
+ windowFunction( x) {
104
+ return renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, this.env);
139
105
  },
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});`) : [];
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
- Render key addition as HANA SQL.
152
- */
153
- addKey(tableName, elementsObj) {
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
- 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' ? ')' : ''};` ];
114
+ SELECT( x) {
115
+ return `(${renderQuery('<subselect>', x, increaseIndent(this.env))})`;
161
116
  },
162
- /*
163
- Render association removals as HANA SQL.
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
- Render primary-key removals as HANA SQL.
170
- */
171
- dropKey(tableName) {
172
- return [ `ALTER TABLE ${tableName} DROP PRIMARY KEY;` ];
173
- },
174
- /*
175
- Render column modifications as HANA SQL.
176
- */
177
- alterColumns(tableName, definitionsStr) {
178
- return [ `ALTER TABLE ${tableName} ALTER (${definitionsStr});` ];
179
- },
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})`;
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(tableName, def.new);
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(tableName, def.new));
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(tableName, removeCols));
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(tableName, assoc)));
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(tableName, sqlId, def.new.doc);
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(tableName, sqlId)
549
- : render.dropColumns(tableName, [ sqlId ]);
444
+ ? render.dropAssociation(artifactName, sqlId)
445
+ : render.dropColumns(artifactName, [ sqlId ]);
550
446
  const add = def.new.target
551
- ? render.addAssociations(artifactName, tableName, { [eltName]: def.new }, env)
552
- : render.addColumns.fromElementsObj(artifactName, tableName, { [eltName]: def.new }, env);
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(tableName, eltStrNew));
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.addColumns.fromElementsObj(artifactName, tableName, extElements, env, duplicateChecker);
686
- const associations = render.addAssociations(artifactName, tableName, extElements, env);
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(tableName);
692
- const add = render.addKey(tableName, artifactElements);
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, true, true)})`;
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, true, true)}`;
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, true, true)}]`;
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, true, false, true)).join(', ');
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, true, false, true)}`).join(', ');
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, true);
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 => !(select.mixin || Object.create(null))[firstPathStepId(col.ref)]) // No mixin columns
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, true, true)}`;
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, true, false, true)).join(', ')}`;
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, true, true)}`;
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, true, false, true);
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, true, true)}]`;
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
- 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
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