@sap/cds-compiler 2.13.8 → 3.0.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 +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
package/lib/api/main.js
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const prepareOptions =
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const sortViews =
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
5
|
+
const prepareOptions = lazyload('./options');
|
|
6
|
+
const baseModel = lazyload('../base/model');
|
|
7
|
+
const location = lazyload('../base/location');
|
|
8
|
+
const messages = lazyload('../base/messages');
|
|
9
|
+
const compiler = lazyload('../compiler/index');
|
|
10
|
+
const toCsn = lazyload('../json/to-csn');
|
|
11
|
+
const forOdataNew = lazyload('../transform/forOdataNew.js');
|
|
12
|
+
const toSql = lazyload('../render/toSql');
|
|
13
|
+
const toCdl = require('../render/toCdl');
|
|
14
|
+
const modelCompare = lazyload('../modelCompare/compare');
|
|
15
|
+
const sortViews = lazyload('../model/sortViews');
|
|
16
|
+
const csnUtils = lazyload('../model/csnUtils');
|
|
17
|
+
const timetrace = lazyload('../utils/timetrace');
|
|
18
|
+
const forHanaNew = lazyload('../transform/forHanaNew');
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Return the artifact name for use for the hdbresult object
|
|
@@ -26,16 +26,15 @@ const { transformForHanaWithCsn } = require('../transform/forHanaNew');
|
|
|
26
26
|
* @returns {string} Name with . replaced as _ in some places
|
|
27
27
|
*/
|
|
28
28
|
function getFileName(artifactName, csn) {
|
|
29
|
-
return getResultingName(csn, 'quoted', artifactName);
|
|
29
|
+
return csnUtils.getResultingName(csn, 'quoted', artifactName);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
odata: 'toOdata',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const { cloneCsn } = require('../model/csnUtils');
|
|
32
|
+
const { cloneCsnNonDict } = require('../model/csnUtils');
|
|
37
33
|
const { toHdbcdsSource } = require('../render/toHdbcds');
|
|
38
34
|
const { ModelError } = require('../base/error');
|
|
35
|
+
const { forEach, forEachKey } = require('../utils/objectUtils');
|
|
36
|
+
const { checkRemovedDeprecatedFlags } = require('../base/model');
|
|
37
|
+
const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
|
|
39
38
|
|
|
40
39
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
41
40
|
const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
|
|
@@ -53,15 +52,14 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
|
|
|
53
52
|
function attachTransformerCharacteristics(csn, transformation, options,
|
|
54
53
|
relevantOptionNames, optionalOptionNames = []) {
|
|
55
54
|
const relevant = {};
|
|
56
|
-
const propName = propertyToCheck[transformation];
|
|
57
55
|
for (const name of relevantOptionNames ) {
|
|
58
|
-
if (options[
|
|
59
|
-
relevant[name] = options[
|
|
56
|
+
if (options[name] !== undefined)
|
|
57
|
+
relevant[name] = options[name];
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
for (const name of optionalOptionNames ) {
|
|
63
|
-
if (options[
|
|
64
|
-
relevant[name] = options[
|
|
61
|
+
if (options[name] !== undefined)
|
|
62
|
+
relevant[name] = options[name];
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
for (const name of relevantGeneralOptions ) {
|
|
@@ -69,10 +67,10 @@ function attachTransformerCharacteristics(csn, transformation, options,
|
|
|
69
67
|
relevant[name] = options[name];
|
|
70
68
|
}
|
|
71
69
|
if (!csn.meta)
|
|
72
|
-
setProp(csn, 'meta', {});
|
|
70
|
+
baseModel.setProp(csn, 'meta', {});
|
|
73
71
|
|
|
74
|
-
setProp(csn.meta, 'options', relevant);
|
|
75
|
-
setProp(csn.meta, 'transformation', transformation);
|
|
72
|
+
baseModel.setProp(csn.meta, 'options', relevant);
|
|
73
|
+
baseModel.setProp(csn.meta, 'transformation', transformation);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
/**
|
|
@@ -91,7 +89,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
91
89
|
// Not able to check
|
|
92
90
|
return;
|
|
93
91
|
}
|
|
94
|
-
const { error, warning,
|
|
92
|
+
const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
|
|
95
93
|
|
|
96
94
|
for (const name of relevantOptionNames ) {
|
|
97
95
|
if (options[name] !== csn.meta.options[name])
|
|
@@ -103,7 +101,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
103
101
|
warning('options-mismatch-pretransformed-csn', null, `Expected pre-processed CSN to have option "${ name }" set to "${ options[name] }". Found: "${ csn.meta.options[name] }"`);
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
|
|
104
|
+
throwWithAnyError();
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
/**
|
|
@@ -127,7 +125,7 @@ function isPreTransformed(csn, transformation) {
|
|
|
127
125
|
* @returns {object} Return an oData-pre-processed CSN
|
|
128
126
|
*/
|
|
129
127
|
function odataInternal(csn, internalOptions) {
|
|
130
|
-
const oDataCsn = transform4odataWithCsn(csn, internalOptions);
|
|
128
|
+
const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
|
|
131
129
|
attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
|
|
132
130
|
return oDataCsn;
|
|
133
131
|
}
|
|
@@ -149,13 +147,13 @@ function odata(csn, options = {}) {
|
|
|
149
147
|
*
|
|
150
148
|
* @param {object} csn CSN to process
|
|
151
149
|
* @param {object} [externalOptions={}] Options
|
|
152
|
-
* @returns {
|
|
150
|
+
* @returns {object} { model: string, namespace: string, unappliedExtensions: string }
|
|
153
151
|
*/
|
|
154
152
|
function cdl(csn, externalOptions = {}) {
|
|
155
153
|
const internalOptions = prepareOptions.to.cdl(externalOptions);
|
|
156
|
-
|
|
157
|
-
return result;
|
|
154
|
+
return toCdl.csnToCdl(cloneCsnNonDict(csn, internalOptions), internalOptions);
|
|
158
155
|
}
|
|
156
|
+
|
|
159
157
|
/**
|
|
160
158
|
* Transform a CSN like to.sql
|
|
161
159
|
*
|
|
@@ -167,8 +165,9 @@ function cdl(csn, externalOptions = {}) {
|
|
|
167
165
|
function forSql(csn, options = {}) {
|
|
168
166
|
const internalOptions = prepareOptions.to.sql(options);
|
|
169
167
|
internalOptions.transformation = 'sql';
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.sql');
|
|
169
|
+
|
|
170
|
+
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
172
171
|
}
|
|
173
172
|
/**
|
|
174
173
|
* Transform a CSN like to.hdi
|
|
@@ -180,8 +179,10 @@ function forSql(csn, options = {}) {
|
|
|
180
179
|
*/
|
|
181
180
|
function forHdi(csn, options = {}) {
|
|
182
181
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
183
|
-
internalOptions.
|
|
184
|
-
|
|
182
|
+
internalOptions.transformation = 'sql';
|
|
183
|
+
const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdi');
|
|
184
|
+
|
|
185
|
+
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
185
186
|
}
|
|
186
187
|
/**
|
|
187
188
|
* Transform a CSN like to.hdbcds
|
|
@@ -195,9 +196,9 @@ function forHdbcds(csn, options = {}) {
|
|
|
195
196
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
196
197
|
internalOptions.transformation = 'hdbcds';
|
|
197
198
|
|
|
198
|
-
const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
199
|
+
const hanaCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
199
200
|
|
|
200
|
-
return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
201
|
+
return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
/**
|
|
@@ -211,12 +212,16 @@ function sql(csn, options = {}) {
|
|
|
211
212
|
const internalOptions = prepareOptions.to.sql(options);
|
|
212
213
|
internalOptions.transformation = 'sql';
|
|
213
214
|
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
216
|
+
|
|
217
|
+
if (internalOptions.sqlDialect === 'postgres' && !baseModel.isBetaEnabled(internalOptions, 'postgres'))
|
|
218
|
+
error(null, null, 'sqlDialect: \'postgres\' is only supported with beta-flag \'postgres\'');
|
|
216
219
|
|
|
217
|
-
|
|
220
|
+
// we need the CSN for view sorting
|
|
221
|
+
const transformedCsn = forSql(csn, options);
|
|
222
|
+
const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
|
|
218
223
|
|
|
219
|
-
const result = sortViews(
|
|
224
|
+
const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
|
|
220
225
|
|
|
221
226
|
return result.map(obj => obj.sql).filter(create => create);
|
|
222
227
|
}
|
|
@@ -232,12 +237,8 @@ function hdi(csn, options = {}) {
|
|
|
232
237
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
233
238
|
|
|
234
239
|
// we need the CSN for view sorting
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
|
|
238
|
-
|
|
239
|
-
const sqlCSN = intermediateResult.csn;
|
|
240
|
-
delete intermediateResult.csn;
|
|
240
|
+
const sqlCSN = forHdi(csn, options);
|
|
241
|
+
const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
|
|
241
242
|
|
|
242
243
|
if (internalOptions.testMode) {
|
|
243
244
|
// All this mapping is needed because sortViews crossmatches
|
|
@@ -245,13 +246,13 @@ function hdi(csn, options = {}) {
|
|
|
245
246
|
// But we also need to return it with the correct file ending in the end
|
|
246
247
|
// so remember and do lot's of mapping here.
|
|
247
248
|
|
|
248
|
-
const flat = flattenResultStructure(
|
|
249
|
+
const flat = flattenResultStructure(sqls);
|
|
249
250
|
|
|
250
251
|
const nameMapping = Object.create(null);
|
|
251
252
|
const sqlArtifactsWithCSNNamesToSort = Object.create(null);
|
|
252
253
|
const sqlArtifactsNotToSort = Object.create(null);
|
|
253
254
|
|
|
254
|
-
|
|
255
|
+
forEach(flat, (key) => {
|
|
255
256
|
const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');
|
|
256
257
|
nameMapping[artifactNameLikeInCsn] = key;
|
|
257
258
|
if (key.endsWith('.hdbtable') || key.endsWith('.hdbview'))
|
|
@@ -269,14 +270,14 @@ function hdi(csn, options = {}) {
|
|
|
269
270
|
}, Object.create(null));
|
|
270
271
|
|
|
271
272
|
// now add the not-sorted stuff, like indices
|
|
272
|
-
|
|
273
|
+
forEach(sqlArtifactsNotToSort, (key) => {
|
|
273
274
|
sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];
|
|
274
275
|
});
|
|
275
276
|
|
|
276
277
|
return sorted;
|
|
277
278
|
}
|
|
278
279
|
|
|
279
|
-
return remapNames(flattenResultStructure(
|
|
280
|
+
return remapNames(flattenResultStructure(sqls), sqlCSN, k => !k.endsWith('.hdbindex'));
|
|
280
281
|
}
|
|
281
282
|
/**
|
|
282
283
|
* Remap names so that they stay consistent between v1 and v2
|
|
@@ -291,10 +292,10 @@ function hdi(csn, options = {}) {
|
|
|
291
292
|
function remapNames(dict, csn, filter) {
|
|
292
293
|
const result = Object.create(null);
|
|
293
294
|
|
|
294
|
-
|
|
295
|
+
forEach(dict, (key, value) => {
|
|
295
296
|
const name = remapName(key, csn, filter);
|
|
296
297
|
result[name] = value;
|
|
297
|
-
}
|
|
298
|
+
});
|
|
298
299
|
|
|
299
300
|
return result;
|
|
300
301
|
}
|
|
@@ -342,45 +343,17 @@ function remapName(key, csn, filter = () => true) {
|
|
|
342
343
|
* consists of a column drop and add).
|
|
343
344
|
*/
|
|
344
345
|
function hdiMigration(csn, options, beforeImage) {
|
|
345
|
-
/**
|
|
346
|
-
* Swap arguments in case of inverted argument order.
|
|
347
|
-
* This is for backward compatibility with @sap/cds@4.5.(2…3).
|
|
348
|
-
*
|
|
349
|
-
* @todo Remove in cds-compiler@2.x
|
|
350
|
-
* @param {HdiOptions|CSN.Model} inputOptions Options or CSN image
|
|
351
|
-
* @param {HdiOptions|CSN.Model} inputBeforeImage CSN image or options
|
|
352
|
-
* @returns {Array} Array where the real options come first
|
|
353
|
-
*/
|
|
354
|
-
function backwardCompatible(inputOptions, inputBeforeImage) {
|
|
355
|
-
/**
|
|
356
|
-
* Check whether the given argument is a CSN
|
|
357
|
-
*
|
|
358
|
-
* @param {object} arg Argument to verify
|
|
359
|
-
* @returns {boolean} True if it is a CSN
|
|
360
|
-
*/
|
|
361
|
-
function isBeforeImage(arg) {
|
|
362
|
-
return arg === null || [ 'definitions', 'meta', '$version' ].some(key => key in arg);
|
|
363
|
-
}
|
|
364
|
-
return isBeforeImage(inputBeforeImage)
|
|
365
|
-
? [ inputOptions, inputBeforeImage ]
|
|
366
|
-
: [ inputBeforeImage, inputOptions ];
|
|
367
|
-
}
|
|
368
|
-
[ options, beforeImage ] = backwardCompatible(options, beforeImage);
|
|
369
|
-
|
|
370
346
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
371
|
-
internalOptions.toSql.csn = true;
|
|
372
347
|
|
|
373
348
|
// Prepare after-image.
|
|
374
|
-
|
|
375
|
-
// cloneCsnMessages(csn, options, internalOptions);
|
|
376
|
-
const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
|
|
349
|
+
const afterImage = forHdi(csn, options);
|
|
377
350
|
|
|
378
351
|
// Compare both images.
|
|
379
|
-
const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
352
|
+
const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
380
353
|
|
|
381
354
|
// Convert the diff to SQL.
|
|
382
355
|
internalOptions.forHana = true; // Make it pass the SQL rendering
|
|
383
|
-
const { deletions, migrations, ...hdbkinds } = toSqlDdl(diff, internalOptions);
|
|
356
|
+
const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
|
|
384
357
|
|
|
385
358
|
return {
|
|
386
359
|
afterImage,
|
|
@@ -396,15 +369,15 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
396
369
|
*/
|
|
397
370
|
function createDefinitions() {
|
|
398
371
|
const result = [];
|
|
399
|
-
|
|
372
|
+
forEach(hdbkinds, (kind, artifacts) => {
|
|
400
373
|
const suffix = `.${ kind }`;
|
|
401
|
-
|
|
374
|
+
forEach(artifacts, (name, sqlStatement) => {
|
|
402
375
|
if ( kind !== 'hdbindex' )
|
|
403
376
|
result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
|
|
404
377
|
else
|
|
405
378
|
result.push({ name, suffix, sql: sqlStatement });
|
|
406
|
-
}
|
|
407
|
-
}
|
|
379
|
+
});
|
|
380
|
+
});
|
|
408
381
|
return result;
|
|
409
382
|
}
|
|
410
383
|
/**
|
|
@@ -414,9 +387,7 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
414
387
|
*/
|
|
415
388
|
function createDeletions() {
|
|
416
389
|
const result = [];
|
|
417
|
-
|
|
418
|
-
result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' });
|
|
419
|
-
|
|
390
|
+
forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
|
|
420
391
|
return result;
|
|
421
392
|
}
|
|
422
393
|
/**
|
|
@@ -426,9 +397,7 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
426
397
|
*/
|
|
427
398
|
function createMigrations() {
|
|
428
399
|
const result = [];
|
|
429
|
-
|
|
430
|
-
result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset });
|
|
431
|
-
|
|
400
|
+
forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
|
|
432
401
|
return result;
|
|
433
402
|
}
|
|
434
403
|
}
|
|
@@ -443,14 +412,14 @@ hdi.migration = hdiMigration;
|
|
|
443
412
|
* @returns {HDBCDS} { <filename>:<content>, ...}
|
|
444
413
|
*/
|
|
445
414
|
function hdbcds(csn, options = {}) {
|
|
446
|
-
timetrace.start('to.hdbcds');
|
|
415
|
+
timetrace.timetrace.start('to.hdbcds');
|
|
447
416
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
448
417
|
internalOptions.transformation = 'hdbcds';
|
|
449
418
|
|
|
450
419
|
const hanaCsn = forHdbcds(csn, internalOptions);
|
|
451
420
|
|
|
452
421
|
const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
|
|
453
|
-
timetrace.stop();
|
|
422
|
+
timetrace.timetrace.stop();
|
|
454
423
|
return result;
|
|
455
424
|
}
|
|
456
425
|
/**
|
|
@@ -472,11 +441,11 @@ function edm(csn, options = {}) {
|
|
|
472
441
|
let servicesEdmj;
|
|
473
442
|
if (isPreTransformed(csn, 'odata')) {
|
|
474
443
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
475
|
-
servicesEdmj =
|
|
444
|
+
servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
|
|
476
445
|
}
|
|
477
446
|
else {
|
|
478
447
|
const oDataCsn = odataInternal(csn, internalOptions);
|
|
479
|
-
servicesEdmj =
|
|
448
|
+
servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
|
|
480
449
|
}
|
|
481
450
|
return servicesEdmj.edmj;
|
|
482
451
|
}
|
|
@@ -492,9 +461,9 @@ edm.all = edmall;
|
|
|
492
461
|
*/
|
|
493
462
|
function edmall(csn, options = {}) {
|
|
494
463
|
const internalOptions = prepareOptions.to.edm(options);
|
|
495
|
-
const { error } = makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
464
|
+
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
496
465
|
|
|
497
|
-
if (internalOptions.
|
|
466
|
+
if (internalOptions.odataVersion === 'v2')
|
|
498
467
|
error(null, null, 'OData JSON output is not available for OData V2');
|
|
499
468
|
|
|
500
469
|
const result = {};
|
|
@@ -506,13 +475,11 @@ function edmall(csn, options = {}) {
|
|
|
506
475
|
else
|
|
507
476
|
oDataCsn = odataInternal(csn, internalOptions);
|
|
508
477
|
|
|
509
|
-
const servicesJson =
|
|
478
|
+
const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
|
|
510
479
|
const services = servicesJson.edmj;
|
|
511
|
-
for (const serviceName in services)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
result[serviceName] = lEdm;
|
|
515
|
-
}
|
|
480
|
+
for (const serviceName in services)
|
|
481
|
+
result[serviceName] = services[serviceName];
|
|
482
|
+
|
|
516
483
|
return result;
|
|
517
484
|
}
|
|
518
485
|
/**
|
|
@@ -534,11 +501,11 @@ function edmx(csn, options = {}) {
|
|
|
534
501
|
let services;
|
|
535
502
|
if (isPreTransformed(csn, 'odata')) {
|
|
536
503
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
537
|
-
services =
|
|
504
|
+
services = preparedCsnToEdmx(csn, service, internalOptions);
|
|
538
505
|
}
|
|
539
506
|
else {
|
|
540
507
|
const oDataCsn = odataInternal(csn, internalOptions);
|
|
541
|
-
services =
|
|
508
|
+
services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
|
|
542
509
|
}
|
|
543
510
|
|
|
544
511
|
return services.edmx;
|
|
@@ -565,7 +532,7 @@ function edmxall(csn, options = {}) {
|
|
|
565
532
|
else
|
|
566
533
|
oDataCsn = odataInternal(csn, internalOptions);
|
|
567
534
|
|
|
568
|
-
const servicesEdmx =
|
|
535
|
+
const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
|
|
569
536
|
const services = servicesEdmx.edmx;
|
|
570
537
|
// Create annotations and metadata once per service
|
|
571
538
|
for (const serviceName in services) {
|
|
@@ -576,6 +543,79 @@ function edmxall(csn, options = {}) {
|
|
|
576
543
|
return result;
|
|
577
544
|
}
|
|
578
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
548
|
+
* using 'options'
|
|
549
|
+
*
|
|
550
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
551
|
+
* @param {string} service Service name to use. If you want all services, use preparedCsnToEdmxAll()
|
|
552
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
553
|
+
* @returns {object} Rendered EDMX string for the given service.
|
|
554
|
+
*/
|
|
555
|
+
function preparedCsnToEdmx(csn, service, options) {
|
|
556
|
+
const e = csn2edm(csn, service, options);
|
|
557
|
+
return {
|
|
558
|
+
edmx: (e ? e.toXML('all') : undefined),
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
564
|
+
* using 'options'.
|
|
565
|
+
*
|
|
566
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
567
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
568
|
+
* @returns {object} Dictionary of rendered EDMX strings for each service.
|
|
569
|
+
*/
|
|
570
|
+
function preparedCsnToEdmxAll(csn, options) {
|
|
571
|
+
const edmxResult = csn2edmAll(csn, options);
|
|
572
|
+
for (const service in edmxResult)
|
|
573
|
+
edmxResult[service] = edmxResult[service].toXML('all');
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
edmx: edmxResult,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
582
|
+
* using 'options'
|
|
583
|
+
*
|
|
584
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
585
|
+
* @param {string} service Service name for which EDMX should be rendered.
|
|
586
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
587
|
+
* @returns {object} Rendered EDM JSON object for of the given service.
|
|
588
|
+
*/
|
|
589
|
+
function preparedCsnToEdm(csn, service, options) {
|
|
590
|
+
// Override OData version as edm json is always v4
|
|
591
|
+
options.odataVersion = 'v4';
|
|
592
|
+
const e = csn2edm(csn, service, options);
|
|
593
|
+
return {
|
|
594
|
+
edmj: (e ? e.toJSON() : undefined),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
600
|
+
* using 'options'
|
|
601
|
+
*
|
|
602
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
603
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
604
|
+
* @returns {object} Dictionary of rendered EDM JSON objects for each service.
|
|
605
|
+
*/
|
|
606
|
+
function preparedCsnToEdmAll(csn, options) {
|
|
607
|
+
// Override OData version as edm json is always v4
|
|
608
|
+
options.odataVersion = 'v4';
|
|
609
|
+
const edmj = csn2edmAll(csn, options);
|
|
610
|
+
for (const service in edmj)
|
|
611
|
+
edmj[service] = edmj[service].toJSON();
|
|
612
|
+
|
|
613
|
+
return {
|
|
614
|
+
edmj,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
|
|
579
619
|
/**
|
|
580
620
|
* Flatten the result structure to a flat map.
|
|
581
621
|
*
|
|
@@ -586,12 +626,13 @@ function edmxall(csn, options = {}) {
|
|
|
586
626
|
*/
|
|
587
627
|
function flattenResultStructure(toProcess) {
|
|
588
628
|
const result = {};
|
|
589
|
-
|
|
629
|
+
forEach(toProcess, (fileType, artifacts) => {
|
|
590
630
|
if (fileType === 'messages')
|
|
591
|
-
|
|
592
|
-
|
|
631
|
+
return;
|
|
632
|
+
forEach(artifacts, (filename) => {
|
|
593
633
|
result[`${ filename }.${ fileType }`] = artifacts[filename];
|
|
594
|
-
|
|
634
|
+
});
|
|
635
|
+
});
|
|
595
636
|
|
|
596
637
|
return result;
|
|
597
638
|
}
|
|
@@ -608,7 +649,9 @@ module.exports = {
|
|
|
608
649
|
for_sql: publishCsnProcessor(forSql, 'for.sql'),
|
|
609
650
|
for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
|
|
610
651
|
for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
|
|
611
|
-
/** */
|
|
652
|
+
/** Deprecated, will be removed in cds-compiler@v4 */
|
|
653
|
+
preparedCsnToEdmx,
|
|
654
|
+
preparedCsnToEdm,
|
|
612
655
|
};
|
|
613
656
|
|
|
614
657
|
|
|
@@ -641,29 +684,101 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
641
684
|
*/
|
|
642
685
|
function api( csn, options = {}, ...args ) {
|
|
643
686
|
try {
|
|
687
|
+
if (options.deprecated) {
|
|
688
|
+
const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
|
|
689
|
+
checkRemovedDeprecatedFlags( options, messageFunctions );
|
|
690
|
+
}
|
|
691
|
+
checkOutdatedOptions( options );
|
|
644
692
|
return processor( csn, options, ...args );
|
|
645
693
|
}
|
|
646
694
|
catch (err) {
|
|
647
|
-
if (err instanceof CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
|
|
695
|
+
if (err instanceof messages.CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
|
|
648
696
|
throw err;
|
|
649
697
|
|
|
650
698
|
if (options.testMode && !(err instanceof TypeError) &&
|
|
651
699
|
!(err instanceof ModelError))
|
|
652
700
|
throw err;
|
|
653
701
|
|
|
654
|
-
const { info } = makeMessageFunction( csn, options, 'compile' );
|
|
655
|
-
const msg = info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
|
|
702
|
+
const { info } = messages.makeMessageFunction( csn, options, 'compile' );
|
|
703
|
+
const msg = info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
|
|
656
704
|
if (options.internalMsg)
|
|
657
705
|
msg.error = err; // Attach original error
|
|
658
706
|
|
|
659
707
|
// next line to be replaced by CSN parser call which reads the CSN object
|
|
660
|
-
const xsn = recompileX(csn, options);
|
|
661
|
-
const recompiledCsn = compactModel(xsn);
|
|
708
|
+
const xsn = compiler.recompileX(csn, options);
|
|
709
|
+
const recompiledCsn = toCsn.compactModel(xsn);
|
|
662
710
|
return processor( recompiledCsn, options, ...args );
|
|
663
711
|
}
|
|
664
712
|
}
|
|
665
713
|
}
|
|
666
714
|
|
|
715
|
+
// Note: No toCsn, because @sap/cds may still use it (2022-06-15)
|
|
716
|
+
const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
|
|
717
|
+
/**
|
|
718
|
+
* Checks if outdated options are used and if so, throw a compiler error.
|
|
719
|
+
* These include:
|
|
720
|
+
* - magicVars (now variableReplacements)
|
|
721
|
+
* - toOdata/toSql/toHana/forHana -> now flat options
|
|
722
|
+
*
|
|
723
|
+
* @param {CSN.Options} options Backend options
|
|
724
|
+
*/
|
|
725
|
+
function checkOutdatedOptions(options) {
|
|
726
|
+
const { error, throwWithError } = messages.makeMessageFunction(null, options, 'api');
|
|
727
|
+
|
|
728
|
+
// This error has been emitted once, we don't need to emit it again.
|
|
729
|
+
if (options.messages?.some(m => m.messageId === 'api-invalid-option')) {
|
|
730
|
+
throwWithError();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
for (const name of oldBackendOptionNames) {
|
|
735
|
+
if (typeof options[name] === 'object') // may be a boolean due to internal options
|
|
736
|
+
error('api-invalid-option', null, { '#': 'std', name });
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (options.magicVars)
|
|
740
|
+
error('api-invalid-option', null, { '#': 'magicVars' });
|
|
741
|
+
|
|
742
|
+
// Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
|
|
743
|
+
// forgot about user -> $user and locale -> $user.locale
|
|
744
|
+
if (options.variableReplacements?.user)
|
|
745
|
+
error('api-invalid-option', null, { '#': 'user' });
|
|
746
|
+
if (options.variableReplacements?.locale)
|
|
747
|
+
error('api-invalid-option', null, { '#': 'locale' });
|
|
748
|
+
|
|
749
|
+
forEachKey(options.variableReplacements || {}, (name) => {
|
|
750
|
+
if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
|
|
751
|
+
error('api-invalid-option', null, { '#': 'noDollar', name });
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
throwWithError();
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Load the module on-demand and not immediately.
|
|
759
|
+
*
|
|
760
|
+
* @param {string} moduleName Name of the module to load - like with require
|
|
761
|
+
* @returns {object} A Proxy that handles the on-demand loading
|
|
762
|
+
*/
|
|
763
|
+
function lazyload(moduleName) {
|
|
764
|
+
let module;
|
|
765
|
+
return new Proxy(((...args) => {
|
|
766
|
+
if (!module) // eslint-disable-next-line global-require
|
|
767
|
+
module = require(moduleName);
|
|
768
|
+
|
|
769
|
+
if (module.apply && typeof module.apply === 'function')
|
|
770
|
+
return module.apply(this, args);
|
|
771
|
+
return module; // for destructured calls
|
|
772
|
+
}), {
|
|
773
|
+
get(target, name) {
|
|
774
|
+
if (!module) // eslint-disable-next-line global-require
|
|
775
|
+
module = require(moduleName);
|
|
776
|
+
|
|
777
|
+
return module[name];
|
|
778
|
+
},
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
667
782
|
|
|
668
783
|
/**
|
|
669
784
|
* Option format used by the old API, where they are grouped thematically.
|