@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.
- package/CHANGELOG.md +47 -2
- package/bin/cdsc.js +15 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +5 -1
- package/lib/api/main.js +61 -23
- package/lib/api/options.js +40 -0
- package/lib/base/builtins.js +89 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +50 -33
- package/lib/base/messages.js +71 -16
- package/lib/base/model.js +0 -2
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +6 -22
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +61 -11
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +2 -2
- package/lib/compiler/index.js +9 -9
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +3 -0
- package/lib/compiler/resolve.js +6 -20
- package/lib/compiler/shared.js +1 -1
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +113 -55
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +3 -3
- package/lib/edm/edmInboundChecks.js +24 -5
- package/lib/edm/edmPreprocessor.js +46 -20
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +9 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1941 -1850
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +8 -7
- package/lib/json/to-csn.js +12 -7
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +9 -10
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/main.js +8 -1
- package/lib/model/cloneCsn.js +15 -6
- package/lib/model/csnRefs.js +141 -35
- package/lib/model/csnUtils.js +1 -4
- package/lib/model/enrichCsn.js +1 -1
- package/lib/modelCompare/compare.js +106 -92
- package/lib/optionProcessor.js +23 -1
- package/lib/render/toCdl.js +3 -2
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/draft/odata.js +16 -17
- package/lib/transform/effective/main.js +6 -3
- package/lib/transform/effective/misc.js +18 -8
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +8 -7
- package/lib/transform/forRelationalDB.js +103 -112
- package/lib/transform/odata/flattening.js +82 -44
- package/lib/transform/odata/toFinalBaseType.js +9 -25
- package/lib/transform/odata/typesExposure.js +28 -15
- package/lib/transform/parseExpr.js +0 -3
- package/lib/transform/transformUtils.js +12 -8
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
package/lib/model/enrichCsn.js
CHANGED
|
@@ -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('../
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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, {
|
|
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
|
|
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
|
-
|
|
146
|
-
|
|
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,
|
|
152
|
-
}
|
|
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
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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
|
|
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
|
-
}
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
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' &&
|
|
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
|
/**
|
package/lib/optionProcessor.js
CHANGED
|
@@ -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
|
|
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 = {
|
package/lib/render/toCdl.js
CHANGED
|
@@ -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
|
|
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]*$/;
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
getLastPartOf, getLastPartOfRef,
|
|
5
|
-
hasValidSkipOrExists,
|
|
5
|
+
hasValidSkipOrExists, generatedByCompilerVersion, getNormalizedQuery,
|
|
6
6
|
getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement, hasAnnotationValue,
|
|
7
|
-
pathName,
|
|
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 =
|
|
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') {
|
package/lib/render/toSql.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
const {
|
|
5
5
|
getLastPartOf, getLastPartOfRef,
|
|
6
|
-
hasValidSkipOrExists,
|
|
6
|
+
hasValidSkipOrExists, generatedByCompilerVersion, getNormalizedQuery,
|
|
7
7
|
forEachDefinition, getResultingName,
|
|
8
|
-
getVariableReplacement,
|
|
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
|
|
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
|