@sap/cds-compiler 4.7.4 → 4.8.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 (94) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/draft/odata.js +16 -17
  67. package/lib/transform/effective/main.js +6 -3
  68. package/lib/transform/effective/misc.js +18 -8
  69. package/lib/transform/effective/types.js +4 -3
  70. package/lib/transform/forOdata.js +8 -7
  71. package/lib/transform/forRelationalDB.js +103 -112
  72. package/lib/transform/odata/flattening.js +82 -44
  73. package/lib/transform/odata/toFinalBaseType.js +9 -25
  74. package/lib/transform/odata/typesExposure.js +28 -15
  75. package/lib/transform/parseExpr.js +0 -3
  76. package/lib/transform/transformUtils.js +12 -8
  77. package/lib/transform/translateAssocsToJoins.js +2 -2
  78. package/lib/transform/universalCsn/coreComputed.js +2 -1
  79. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  80. package/package.json +2 -2
  81. package/share/messages/README.md +4 -0
  82. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  83. package/share/messages/check-proper-type-of.md +1 -1
  84. package/share/messages/def-duplicate-autoexposed.md +1 -1
  85. package/share/messages/extend-repeated-intralayer.md +3 -16
  86. package/share/messages/extend-unrelated-layer.md +1 -1
  87. package/share/messages/message-explanations.json +1 -0
  88. package/share/messages/redirected-to-ambiguous.md +1 -1
  89. package/share/messages/redirected-to-complex.md +1 -1
  90. package/share/messages/redirected-to-unrelated.md +1 -1
  91. package/share/messages/rewrite-not-supported.md +1 -1
  92. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  93. package/share/messages/type-missing-enum-value.md +59 -0
  94. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -42,7 +42,7 @@
42
42
  const { csnRefs, artifactProperties } = require('./csnRefs');
43
43
  const { locationString } = require('../base/location');
44
44
  const { CompilerAssertion } = require('../base/error');
45
- const { isAnnotationExpression } = require('../compiler/builtins');
45
+ const { isAnnotationExpression } = require('../base/builtins');
46
46
  const shuffleGen = require('../base/shuffle');
47
47
 
48
48
  function enrichCsn( csn, options = {} ) {
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  const { makeMessageFunction } = require('../base/messages');
4
+ const { setProp } = require('../base/model');
4
5
  const {
5
6
  forEachDefinition,
6
7
  forEachMember,
7
8
  isPersistedAsTable,
8
- isPersistedAsView
9
+ isPersistedAsView,
9
10
  } = require('../model/csnUtils');
10
11
  const { forEachKey, forEach } = require('../utils/objectUtils');
11
12
 
@@ -23,7 +24,7 @@ const isChanged = Symbol('Marks a view as changed');
23
24
  */
24
25
  function compareModels(beforeModel, afterModel, options) {
25
26
  // @ts-ignore
26
- if(!(options && options.testMode)) // no $version with testMode
27
+ if (!(options && options.testMode)) // no $version with testMode
27
28
  validateCsnVersions(beforeModel, afterModel, options);
28
29
 
29
30
  const returnObj = Object.create(null);
@@ -45,8 +46,8 @@ function compareModels(beforeModel, afterModel, options) {
45
46
  function validateCsnVersions(beforeModel, afterModel, options) {
46
47
  const beforeVersion = beforeModel.$version;
47
48
  const afterVersion = afterModel.$version;
48
- let beforeVersionParts = beforeVersion && beforeVersion.split('.');
49
- let afterVersionParts = afterVersion && afterVersion.split('.');
49
+ const beforeVersionParts = beforeVersion && beforeVersion.split('.');
50
+ const afterVersionParts = afterVersion && afterVersion.split('.');
50
51
 
51
52
  if (!beforeVersionParts || beforeVersionParts.length < 2) {
52
53
  const { error, throwWithAnyError } = makeMessageFunction(beforeModel, options, 'modelCompare');
@@ -60,21 +61,25 @@ function validateCsnVersions(beforeModel, afterModel, options) {
60
61
  }
61
62
  if (beforeVersionParts[0] > afterVersionParts[0] && !(options && options.allowCsnDowngrade)) {
62
63
  const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
63
- error(null, null, { value: afterVersion, othervalue: beforeVersion, version: require('../../package.json').version },
64
- 'Incompatible CSN versions: $(VALUE) is a major downgrade from $(OTHERVALUE). Is @sap/cds-compiler version $(VERSION) outdated?');
64
+ // eslint-disable-next-line global-require
65
+ const { version } = require('../../package.json');
66
+ error(null, null, { value: afterVersion, othervalue: beforeVersion, version },
67
+ 'Incompatible CSN versions: $(VALUE) is a major downgrade from $(OTHERVALUE). Is @sap/cds-compiler version $(VERSION) outdated?');
65
68
  throwWithAnyError();
66
69
  }
67
70
  }
68
71
 
69
72
  /**
70
73
  * Calculate extensions, migrations and unchangedConstraints
71
- *
72
- * @param {CSN.Model} beforeModel
73
- * @param {CSN.Options} options
74
- * @param {object} returnObj
74
+ *
75
+ * @param {CSN.Model} beforeModel
76
+ * @param {CSN.Options} options
77
+ * @param {object} returnObj
75
78
  * @returns {function}
76
79
  */
77
- function getExtensionAndMigrations(beforeModel, options, { extensions, migrations, unchangedConstraints, changedPrimaryKeys }) {
80
+ function getExtensionAndMigrations(beforeModel, options, {
81
+ extensions, migrations, unchangedConstraints, changedPrimaryKeys,
82
+ }) {
78
83
  return function compareArtifacts(artifact, name) {
79
84
  let hasPrimaryKeyChange = false;
80
85
 
@@ -83,30 +88,31 @@ function getExtensionAndMigrations(beforeModel, options, { extensions, migration
83
88
  const keysNow = [];
84
89
  forEachMember(artifact, (element, eName) => {
85
90
  getElementComparator(otherArtifact, elements)(element, eName);
86
- if(element.key)
91
+ if (element.key)
87
92
  keysNow.push(eName);
88
93
  }, [ 'definitions', name ], true, { elementsOnly: true });
89
94
 
90
95
  // Only do this check for to.hdi.migration - the order only "bites" us when doing .hdbmigrationtable as the end-check against the intended
91
96
  // create-table will fail. TODO: Does a mismatched order of the primary key hurt us for postgres and others?
92
- if(!hasPrimaryKeyChange && options.sqlDialect === 'hana' && options.src === 'hdi') {
97
+ if (!hasPrimaryKeyChange && options.sqlDialect === 'hana' && options.src === 'hdi') {
93
98
  const keysOther = [];
94
99
  forEachMember(otherArtifact, (element, eName) => {
95
- if(element.key)
100
+ if (element.key)
96
101
  keysOther.push(eName);
97
102
  }, [ 'definitions', name ], true, { elementsOnly: true });
98
103
 
99
- if(keysNow.join(',') !== keysOther.join(','))
104
+ if (keysNow.join(',') !== keysOther.join(','))
100
105
  hasPrimaryKeyChange = true;
101
106
  }
102
107
 
103
108
  if (Object.keys(elements).length > 0) {
104
109
  const added = addedElements(name, elements);
105
- if(!hasPrimaryKeyChange)
110
+ if (!hasPrimaryKeyChange) {
106
111
  forEach(added.elements, (_name, element) => {
107
- if(element.key && !element.target)
112
+ if (element.key && !element.target)
108
113
  hasPrimaryKeyChange = true;
109
- });
114
+ });
115
+ }
110
116
  extensions.push(added);
111
117
  }
112
118
  }
@@ -118,90 +124,92 @@ function getExtensionAndMigrations(beforeModel, options, { extensions, migration
118
124
 
119
125
  const migration = { migrate: name };
120
126
 
121
- Object.keys(relevantProperties).forEach(prop => {
122
- if (artifact[prop] !== otherArtifact[prop]) {
127
+ Object.keys(relevantProperties).forEach((prop) => {
128
+ if (artifact[prop] !== otherArtifact[prop])
123
129
  changedProperties[prop] = changedElement(artifact[prop], otherArtifact[prop] || null);
124
- }
125
130
  });
126
131
 
127
132
  const removedConstraints = {};
128
133
 
129
134
  // HDI (src === 'hdi) does handle table constraints via separate files and therefore no delta handling needed
130
- if(options.src === 'sql' && (artifact.$tableConstraints || otherArtifact.$tableConstraints)) {
135
+ if (options.src === 'sql' && (artifact.$tableConstraints || otherArtifact.$tableConstraints)) {
131
136
  const current = artifact.$tableConstraints || {};
132
137
  const old = otherArtifact.$tableConstraints || {};
133
138
  let changes = false;
134
- const constraintTypes = ['unique', 'referential'];
135
- constraintTypes.forEach(constraintType => {
139
+ const constraintTypes = [ 'unique', 'referential' ];
140
+ constraintTypes.forEach((constraintType) => {
136
141
  // We only render/handle referential constraints for specific cases
137
- if(hasReferentialConstraints(options) || constraintType !== 'referential')
138
- if(current[constraintType] || old[constraintType]) {
142
+ if (hasReferentialConstraints(options) || constraintType !== 'referential') {
143
+ if (current[constraintType] || old[constraintType]) {
139
144
  removedConstraints[constraintType] = Object.create(null);
140
145
 
141
146
  const cnew = current[constraintType] || Object.create(null);
142
147
  const cold = old[constraintType] || Object.create(null);
143
148
  forEachKey(cnew, (constraintName) => {
144
- if(cold[constraintName]) {
145
- // constraint changed - add it to "removedConstraints" to drop-create it.
146
- // Quick-and-dirty compare with JSON.stringify - false-positive will just be a drop-create
147
- if(JSON.stringify(cold[constraintName]) !== JSON.stringify(cnew[constraintName])) {
149
+ if (cold[constraintName]) {
150
+ // constraint changed - add it to "removedConstraints" to drop-create it.
151
+ // Quick-and-dirty compare with JSON.stringify - false-positive will just be a drop-create
152
+ if (JSON.stringify(cold[constraintName]) !== JSON.stringify(cnew[constraintName])) {
148
153
  changes = true;
149
154
  removedConstraints[constraintType][constraintName] = cold[constraintName];
150
- if(options.constraintsInCreateTable || constraintType !== 'referential') // schedule an "ADD"
151
- extensions.push(addedConstraint(name, cnew[constraintName], constraintName, constraintType));
152
- } else {
155
+ if (options.constraintsInCreateTable || constraintType !== 'referential') // schedule an "ADD"
156
+ extensions.push(addedConstraint(name, cnew[constraintName], constraintName, constraintType));
157
+ }
158
+ else {
153
159
  unchangedConstraints.add(constraintName);
154
160
  }
155
- } else if(options.constraintsInCreateTable || constraintType !== 'referential'){
156
- // Sometimes referential constraints are anyway added via ALTER - no need to add them as explicit extensions then
157
- extensions.push(addedConstraint(name, cnew[constraintName], constraintName, constraintType));
161
+ }
162
+ else if (options.constraintsInCreateTable || constraintType !== 'referential') {
163
+ // Sometimes referential constraints are anyway added via ALTER - no need to add them as explicit extensions then
164
+ extensions.push(addedConstraint(name, cnew[constraintName], constraintName, constraintType));
158
165
  }
159
166
  });
160
167
 
161
168
  forEachKey(cold, (constraintName) => {
162
- if(!cnew[constraintName]) {
169
+ if (!cnew[constraintName]) {
163
170
  changes = true;
164
171
  removedConstraints[constraintType][constraintName] = cold[constraintName];
165
172
  }
166
- })
173
+ });
167
174
  }
175
+ }
168
176
  });
169
177
 
170
- if(changes)
178
+ if (changes)
171
179
  migration.removeConstraints = removedConstraints;
172
180
  }
173
181
 
174
- if (Object.keys(changedProperties).length > 0) {
182
+ if (Object.keys(changedProperties).length > 0)
175
183
  migration.properties = changedProperties;
176
- }
184
+
177
185
 
178
186
  forEachMember(otherArtifact, getElementComparator(artifact, removedElements), [ 'definitions', name ], true, { elementsOnly: true });
179
187
  if (Object.keys(removedElements).length > 0) {
180
188
  migration.remove = removedElements;
181
- if(!hasPrimaryKeyChange)
189
+ if (!hasPrimaryKeyChange) {
182
190
  forEach(removedElements, (_name, change) => {
183
- if(change.key && !change.target)
191
+ if (change.key && !change.target)
184
192
  hasPrimaryKeyChange = true;
185
193
  });
194
+ }
186
195
  }
187
196
 
188
197
  forEachMember(artifact, getElementComparator(otherArtifact, null, changedElements), [ 'definitions', name ], true, { elementsOnly: true });
189
198
  if (Object.keys(changedElements).length > 0) {
190
199
  migration.change = changedElements;
191
- if(!hasPrimaryKeyChange)
200
+ if (!hasPrimaryKeyChange) {
192
201
  forEach(changedElements, (_name, change) => {
193
- if(!change.onlyDoc && (change.old.key || change.new.key) && !change.new.target && !change.old.target) {
194
- // For to.hdi.migration: Just drop-create (commented out), for to.sql.migration: Handle case where we add/remove "key" keyword, no drop-create otherwise
195
- if(options.sqlDialect === 'hana' && options.src === 'hdi' || (!change.old.key || !change.new.key)) {
202
+ if (!change.onlyDoc && (change.old.key || change.new.key) && !change.new.target && !change.old.target) {
203
+ // For to.hdi.migration: Just drop-create (commented out), for to.sql.migration: Handle case where we add/remove "key" keyword, no drop-create otherwise
204
+ if (options.sqlDialect === 'hana' && options.src === 'hdi' || (!change.old.key || !change.new.key))
196
205
  hasPrimaryKeyChange = true;
197
- }
198
206
  }
199
207
  });
208
+ }
200
209
  }
201
210
 
202
- if (migration.properties || migration.remove || migration.change || migration.removeConstraints) {
211
+ if (migration.properties || migration.remove || migration.change || migration.removeConstraints)
203
212
  migrations.push(migration);
204
- }
205
213
  }
206
214
 
207
215
  const otherArtifact = beforeModel.definitions[name];
@@ -209,7 +217,7 @@ function getExtensionAndMigrations(beforeModel, options, { extensions, migration
209
217
  const isPersistedOther = otherArtifact && isPersistedAsTable(otherArtifact);
210
218
 
211
219
  // to make it easier to know which views to drop-create
212
- if(isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
220
+ if (isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
213
221
  // TODO: Check only on artifact.query/projection BUT: Need to manually check for sql-snippets then!
214
222
  artifact[isChanged] = JSON.stringify(artifact) !== JSON.stringify(otherArtifact);
215
223
  }
@@ -229,17 +237,16 @@ function getExtensionAndMigrations(beforeModel, options, { extensions, migration
229
237
 
230
238
  addElements();
231
239
  changePropsOrRemoveOrChangeElements();
232
- if(hasPrimaryKeyChange) {
240
+ if (hasPrimaryKeyChange)
233
241
  changedPrimaryKeys.push(name);
234
- }
235
242
  };
236
243
  }
237
244
  /**
238
245
  * Calculate all deleted entities.
239
- *
240
- * @param {CSN.Model} afterModel
241
- * @param {CSN.Options} options
242
- * @param {object} returnObj
246
+ *
247
+ * @param {CSN.Model} afterModel
248
+ * @param {CSN.Options} options
249
+ * @param {object} returnObj
243
250
  * @returns {function}
244
251
  */
245
252
  function getDeletions(afterModel, options, { deletions }) {
@@ -251,8 +258,9 @@ function getDeletions(afterModel, options, { deletions }) {
251
258
  // Looking for deleted entities only.
252
259
  if (isPersisted && !isPersistedOther) {
253
260
  deletions[name] = artifact;
254
- // eslint-disable-next-line sonarjs/no-duplicated-branches
255
- } else if (isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
261
+ }
262
+ // eslint-disable-next-line sonarjs/no-duplicated-branches
263
+ else if (isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
256
264
  deletions[name] = artifact;
257
265
  }
258
266
  };
@@ -260,37 +268,40 @@ function getDeletions(afterModel, options, { deletions }) {
260
268
 
261
269
  function getElementComparator(otherArtifact, addedElementsDict = null, changedElementsDict = null) {
262
270
  return function compareElements(element, name) {
263
- if (element.$ignore) {
271
+ if (element.$ignore)
264
272
  return;
265
- }
273
+
266
274
 
267
275
  const otherElement = otherArtifact.elements[name];
268
276
  if (otherElement && !otherElement.$ignore) {
269
277
  // Element type changed?
270
- if (!changedElementsDict) {
278
+ if (!changedElementsDict)
271
279
  return;
272
- }
280
+
273
281
  if (relevantTypeChange(element.type, otherElement.type) || typeParametersChanged(element, otherElement)) {
274
282
  // Type or parameters, e.g. association target, changed.
275
- if(otherElement.notNull && element.notNull === undefined) {
276
- element.$notNull = false; // Explicitly set notNull to the implicit default so we render the correct ALTER
277
- }
283
+ if (otherElement.notNull && element.notNull === undefined && !element.key || otherElement.key && !element.key && !element.notNull)
284
+ setProp(element, '$notNull', false); // Explicitly set notNull to the implicit default so we render the correct ALTER
285
+
286
+ if (otherElement.default && element.default === undefined)
287
+ setProp(element, '$default', { val: null }); // Explicitly set default to the implicit default "null" so we render the correct ALTER
288
+
278
289
  changedElementsDict[name] = changedElement(element, otherElement);
279
- } else if(docCommentChanged(element, otherElement)) {
290
+ }
291
+ else if (docCommentChanged(element, otherElement)) {
280
292
  changedElementsDict[name] = { ...changedElement(element, otherElement), onlyDoc: true };
281
293
  }
282
294
 
283
295
  return;
284
296
  }
285
297
 
286
- if (addedElementsDict) {
298
+ if (addedElementsDict)
287
299
  addedElementsDict[name] = element;
288
- }
289
- }
300
+ };
290
301
  }
291
302
 
292
303
  function relevantTypeChange(type, otherType) {
293
- return otherType !== type && !isSuperflousHanaTypeChange(otherType, type) && ![type, otherType].every(t => ['cds.Association', 'cds.Composition'].includes(t));
304
+ return otherType !== type && !isSuperflousHanaTypeChange(otherType, type) && ![ type, otherType ].every(t => [ 'cds.Association', 'cds.Composition' ].includes(t));
294
305
  }
295
306
 
296
307
  const superflousTypeChanges = {
@@ -301,7 +312,7 @@ const superflousTypeChanges = {
301
312
  'cds.UTCDateTime': 'cds.DateTime',
302
313
  'cds.UTCTimestamp': 'cds.Timestamp',
303
314
  'cds.LocalDate': 'cds.Date',
304
- 'cds.LocalTime': 'cds.Time' ,
315
+ 'cds.LocalTime': 'cds.Time',
305
316
  };
306
317
 
307
318
  /**
@@ -319,11 +330,11 @@ function isSuperflousHanaTypeChange( before, after ) {
319
330
  /**
320
331
  * If the element has one of the superflous types, do the change so we don't accidentally
321
332
  * pass such an old type into the SQL renderer.
322
- * @param {CSN.Element} element
333
+ * @param {CSN.Element} element
323
334
  * @returns {CSN.Element}
324
335
  */
325
336
  function remapType( element ) {
326
- if(element?.type && superflousTypeChanges[element.type])
337
+ if (element?.type && superflousTypeChanges[element.type])
327
338
  element.type = superflousTypeChanges[element.type];
328
339
  return element;
329
340
  }
@@ -351,11 +362,12 @@ function deepEqual(a, b, include = () => true, depth = 0) {
351
362
  return Object.keys(a).reduce((prev, key) => prev && (!include(key, depth) || deepEqual(a[key], b[key], include, depth + 1)), true);
352
363
  }
353
364
 
354
- return isObject(a)
355
- ? isObject(b)
356
- ? samePropertyCount() && allPropertiesEqual()
357
- : false
358
- : a === b;
365
+ if (isObject(a)) {
366
+ return isObject(b)
367
+ ? samePropertyCount() && allPropertiesEqual()
368
+ : false;
369
+ }
370
+ return a === b;
359
371
  }
360
372
 
361
373
  function docCommentChanged(element, otherElement) {
@@ -363,9 +375,9 @@ function docCommentChanged(element, otherElement) {
363
375
  }
364
376
 
365
377
  const relevantProperties = {
366
- 'doc': true,
378
+ doc: true,
367
379
  '@sql.prepend': true,
368
- '@sql.append': true
380
+ '@sql.append': true,
369
381
  };
370
382
 
371
383
  /**
@@ -378,18 +390,20 @@ const relevantProperties = {
378
390
  function typeParametersChanged(element, otherElement) {
379
391
  const checked = new Set();
380
392
  for (const key in element) {
381
- if (Object.prototype.hasOwnProperty.call(element, key))
382
- if((!key.startsWith('@') || relevantProperties[key]) && key !== 'type' && key !== 'doc') {
393
+ if (Object.prototype.hasOwnProperty.call(element, key)) {
394
+ if ((!key.startsWith('@') || relevantProperties[key]) && key !== 'type' && key !== 'doc') {
383
395
  checked.add(key);
384
- if(!deepEqual(element[key], otherElement[key]))
396
+ if (!deepEqual(element[key], otherElement[key]))
385
397
  return true;
386
- }
398
+ }
399
+ }
387
400
  }
388
401
 
389
402
  for (const key in otherElement) {
390
- if (Object.prototype.hasOwnProperty.call(otherElement, key))
391
- if((!key.startsWith('@') || relevantProperties[key]) && key !== 'type' && key !== 'doc' && !checked.has(key))
403
+ if (Object.prototype.hasOwnProperty.call(otherElement, key)) {
404
+ if ((!key.startsWith('@') || relevantProperties[key]) && key !== 'type' && key !== 'doc' && !checked.has(key))
392
405
  return true;
406
+ }
393
407
  }
394
408
 
395
409
  return false;
@@ -398,7 +412,7 @@ function typeParametersChanged(element, otherElement) {
398
412
  function addedElements(entity, elements) {
399
413
  return {
400
414
  extend: entity,
401
- elements
415
+ elements,
402
416
  };
403
417
  }
404
418
 
@@ -408,24 +422,24 @@ function addedConstraint(entity, constraint, constraintName, constraintType) {
408
422
  constraint,
409
423
  constraintName,
410
424
  constraintType,
411
- }
425
+ };
412
426
  }
413
427
 
414
428
  function changedElement(element, otherElement) {
415
429
  return {
416
430
  old: remapType(otherElement),
417
- new: element
431
+ new: element,
418
432
  };
419
433
  }
420
434
 
421
435
  function hasReferentialConstraints(options) {
422
- return options.src === 'sql' && (options.sqlDialect === 'postgres' || options.sqlDialect === 'hana' || options.sqlDialect === 'sqlite')
436
+ return options.src === 'sql' && (options.sqlDialect === 'postgres' || options.sqlDialect === 'hana' || options.sqlDialect === 'sqlite');
423
437
  }
424
438
 
425
439
  module.exports = {
426
440
  compareModels,
427
441
  deepEqual,
428
- isChanged
442
+ isChanged,
429
443
  };
430
444
 
431
445
  /**
@@ -145,6 +145,7 @@ optionProcessor
145
145
  O, toOdata [options] <files...> Generate ODATA metadata and annotations
146
146
  C, toCdl <files...> Generate CDS source files
147
147
  Q, toSql [options] <files...> Generate SQL DDL statements
148
+ J, forJava [options] <files...> Generate CSN for the Java Runtime
148
149
  toCsn [options] <files...> (default) Generate original model as CSN
149
150
  parseCdl [options] <file> Generate a CSN that is close to the CDL source.
150
151
  explain <message-id> Explain a compiler message.
@@ -224,6 +225,7 @@ optionProcessor.command('O, toOdata')
224
225
  .option(' --odata-containment')
225
226
  .option(' --odata-capabilities-pullup')
226
227
  .option(' --odata-openapi-hints')
228
+ .option(' --edm4openapi', { optionName: 'edm4OpenAPI' })
227
229
  .option(' --odata-proxies')
228
230
  .option(' --odata-x-service-refs')
229
231
  .option(' --odata-foreign-keys')
@@ -254,6 +256,7 @@ optionProcessor.command('O, toOdata')
254
256
  --odata-containment Generate Containment Navigation Properties for compositions (V4 only)
255
257
  --odata-capabilities-pullup Rewrite @Capabilities annotations (V4 containment only).
256
258
  --odata-openapi-hints Add various annotations to JSON API as input for OpenAPI generation.
259
+ --edm4openapi Downgrade some errors to warnings for OpenAPI generation.
257
260
  --odata-proxies Generate Proxies for out-of-service navigation targets (V4 only).
258
261
  --odata-x-service-refs Generate schema references (V4 only).
259
262
  --odata-foreign-keys Render foreign keys in structured format (V4 only)
@@ -274,6 +277,17 @@ optionProcessor.command('O, toOdata')
274
277
  those views, that only have an association to a localized entity/view.
275
278
  `);
276
279
 
280
+ optionProcessor.command('J, forJava')
281
+ .option('-h, --help')
282
+ .help(`
283
+ Usage: cdsc forJava [options] <files...>
284
+
285
+ Generate CSN (structured) for Java Runtime.
286
+
287
+ Options
288
+ -h, --help Show this help text
289
+ `);
290
+
277
291
  optionProcessor.command('C, toCdl')
278
292
  .option('-h, --help')
279
293
  .help(`
@@ -530,6 +544,8 @@ optionProcessor.command('inspect')
530
544
 
531
545
  optionProcessor.command('toEffectiveCsn')
532
546
  .option('-h, --help')
547
+ .option('--resolve-simple-types <val>', { valid: ['true', 'false'] } )
548
+ .option('--resolve-projections <val>', { valid: ['true', 'false'] } )
533
549
  .positionalArgument('<files...>')
534
550
  .help(`
535
551
  Usage: cdsc toEffectiveCsn [options] <files...>
@@ -539,7 +555,13 @@ optionProcessor.command('toEffectiveCsn')
539
555
  Beta mode is required.
540
556
 
541
557
  Options
542
- -h, --help Show this help text
558
+ -h, --help Show this help text
559
+ --resolve-simple-types <val> Resolve simple types:
560
+ true: (default) resolve simple type references to their simple base type
561
+ false: do not resolve simple type references
562
+ --resolve-projections <val> Resolve projections:
563
+ true: (default) resolve projections to ordinary views with SELECT
564
+ false: leave them as real projections
543
565
  `);
544
566
 
545
567
  module.exports = {
@@ -9,13 +9,14 @@ const { forEachDefinition, normalizeTypeRef } = require('../model/csnUtils');
9
9
  const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
10
10
  const { isBetaEnabled } = require('../base/model');
11
11
  const { ModelError } = require('../base/error');
12
- const { typeParameters, specialFunctions, isAnnotationExpression } = require('../compiler/builtins');
12
+ const { typeParameters, specialFunctions } = require('../compiler/builtins');
13
+ const { isAnnotationExpression } = require('../base/builtins');
13
14
  const { forEach } = require('../utils/objectUtils');
14
15
  const {
15
- isBuiltinType,
16
16
  generatedByCompilerVersion,
17
17
  getNormalizedQuery,
18
18
  } = require('../model/csnUtils');
19
+ const { isBuiltinType } = require('../base/builtins');
19
20
  const { cloneFullCsn } = require('../model/cloneCsn');
20
21
 
21
22
  const identifierRegex = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
@@ -2,10 +2,11 @@
2
2
 
3
3
  const {
4
4
  getLastPartOf, getLastPartOfRef,
5
- hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
5
+ hasValidSkipOrExists, generatedByCompilerVersion, getNormalizedQuery,
6
6
  getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement, hasAnnotationValue,
7
- pathName, isMagicVariable,
7
+ pathName,
8
8
  } = require('../model/csnUtils');
9
+ const { isBuiltinType, isMagicVariable } = require('../base/builtins');
9
10
  const keywords = require('../base/keywords');
10
11
  const {
11
12
  renderFunc, createExpressionRenderer, getRealName, addContextMarkers, addIntermediateContexts,
@@ -315,49 +316,6 @@ function toHdbcdsSource( csn, options, messageFunctions ) {
315
316
  return false;
316
317
  }
317
318
 
318
- /* FIXME: Not yet required
319
- // Returns the artifact or element that constitutes the final type of
320
- // construct 'node', i.e. the object in which we would find type properties for
321
- // 'node'. Note that this may well be 'node' itself.
322
- function getFinalTypeOf(node) {
323
- if (node && node.type) {
324
- if (isBuiltinType(node.type)) {
325
- return node;
326
- }
327
- return getFinalTypeOf(node.type);
328
- }
329
- return node;
330
- }
331
-
332
- // Resolve path array 'ref' against artifact 'base' (or against 'csn.definitions'
333
- // if no 'base' given).
334
- // Return the resulting artifact or element (or 'undefined' if not found).
335
- function resolveRef(ref, base) {
336
- let result = base;
337
- for (let i = 0; i < ref.length; i++) {
338
- let pathStep = ref[i].id || ref[i];
339
- // Only first path step may be looked up in 'definitions'
340
- if (i === 0 && !base) {
341
- result = csn.definitions[pathStep];
342
- continue;
343
- }
344
- // Structured type
345
- else if (result && result.elements) {
346
- result = getFinalTypeOf(result.elements[pathStep]);
347
- }
348
- // Association
349
- else if (result && result.target) {
350
- result = resolveRef([pathStep], csn.definitions[result.target]);
351
- }
352
- // Not resolvable
353
- else {
354
- return undefined;
355
- }
356
- }
357
- return result;
358
- }
359
- */
360
-
361
319
  /**
362
320
  * Render a context or service. Return the resulting source string.
363
321
  *
@@ -1155,9 +1113,7 @@ function toHdbcdsSource( csn, options, messageFunctions ) {
1155
1113
  */
1156
1114
  function renderAssociationType( elm, env ) {
1157
1115
  // Type, cardinality and target
1158
- let result = 'association';
1159
-
1160
- result += `${renderCardinality(elm.cardinality)} to `;
1116
+ let result = `association${renderCardinality(elm.cardinality)} to `;
1161
1117
 
1162
1118
  // normal target or named aspect
1163
1119
  if (elm.target || elm.targetAspect && typeof elm.targetAspect === 'string') {
@@ -3,10 +3,11 @@
3
3
 
4
4
  const {
5
5
  getLastPartOf, getLastPartOfRef,
6
- hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
6
+ hasValidSkipOrExists, generatedByCompilerVersion, getNormalizedQuery,
7
7
  forEachDefinition, getResultingName,
8
- getVariableReplacement, isMagicVariable, pathName,
8
+ getVariableReplacement, pathName,
9
9
  } = require('../model/csnUtils');
10
+ const { isBuiltinType, isMagicVariable } = require('../base/builtins');
10
11
  const { forEach, forEachValue, forEachKey } = require('../utils/objectUtils');
11
12
  const {
12
13
  renderFunc, cdsToSqlTypes, getHanaComment, hasHanaComment,
@@ -740,7 +741,9 @@ function toSqlDdl( csn, options, messageFunctions ) {
740
741
  let result = `${env.indent + quotedElementName}${isPostgresAlterColumn ? ' TYPE' : ''} ${renderTypeReference(elm, env)
741
742
  }${renderNullability(elm, true, env.alterMode)}`;
742
743
  // calculated elements (on write) can't have a default; ignore it
743
- if (elm.default && !elm.value)
744
+ if (elm.$default && env.alterMode && !elm.value && options.sqlDialect !== 'postgres')
745
+ result += ` DEFAULT ${renderExpr(elm.$default, env.withSubPath([ '$default' ]))}`;
746
+ else if (elm.default && !elm.value)
744
747
  result += ` DEFAULT ${renderExpr(elm.default, env.withSubPath([ 'default' ]))}`;
745
748
 
746
749
  // Only SAP HANA has fuzzy indices