@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -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 +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- 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 +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- 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 +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- 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 +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- 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/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
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,17 +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 propertyToCheck = {
|
|
33
|
-
odata: 'toOdata',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
32
|
const { cloneCsnNonDict } = require('../model/csnUtils');
|
|
37
33
|
const { toHdbcdsSource } = require('../render/toHdbcds');
|
|
38
34
|
const { ModelError } = require('../base/error');
|
|
39
35
|
const { forEach, forEachKey } = require('../utils/objectUtils');
|
|
36
|
+
const { checkRemovedDeprecatedFlags } = require('../base/model');
|
|
37
|
+
const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
|
|
40
38
|
|
|
41
39
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
42
40
|
const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
|
|
@@ -54,15 +52,14 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
|
|
|
54
52
|
function attachTransformerCharacteristics(csn, transformation, options,
|
|
55
53
|
relevantOptionNames, optionalOptionNames = []) {
|
|
56
54
|
const relevant = {};
|
|
57
|
-
const propName = propertyToCheck[transformation];
|
|
58
55
|
for (const name of relevantOptionNames ) {
|
|
59
|
-
if (options[
|
|
60
|
-
relevant[name] = options[
|
|
56
|
+
if (options[name] !== undefined)
|
|
57
|
+
relevant[name] = options[name];
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
for (const name of optionalOptionNames ) {
|
|
64
|
-
if (options[
|
|
65
|
-
relevant[name] = options[
|
|
61
|
+
if (options[name] !== undefined)
|
|
62
|
+
relevant[name] = options[name];
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
for (const name of relevantGeneralOptions ) {
|
|
@@ -70,10 +67,10 @@ function attachTransformerCharacteristics(csn, transformation, options,
|
|
|
70
67
|
relevant[name] = options[name];
|
|
71
68
|
}
|
|
72
69
|
if (!csn.meta)
|
|
73
|
-
setProp(csn, 'meta', {});
|
|
70
|
+
baseModel.setProp(csn, 'meta', {});
|
|
74
71
|
|
|
75
|
-
setProp(csn.meta, 'options', relevant);
|
|
76
|
-
setProp(csn.meta, 'transformation', transformation);
|
|
72
|
+
baseModel.setProp(csn.meta, 'options', relevant);
|
|
73
|
+
baseModel.setProp(csn.meta, 'transformation', transformation);
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
/**
|
|
@@ -92,7 +89,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
92
89
|
// Not able to check
|
|
93
90
|
return;
|
|
94
91
|
}
|
|
95
|
-
const { error, warning, throwWithAnyError } = makeMessageFunction(csn, options, module);
|
|
92
|
+
const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
|
|
96
93
|
|
|
97
94
|
for (const name of relevantOptionNames ) {
|
|
98
95
|
if (options[name] !== csn.meta.options[name])
|
|
@@ -128,7 +125,7 @@ function isPreTransformed(csn, transformation) {
|
|
|
128
125
|
* @returns {object} Return an oData-pre-processed CSN
|
|
129
126
|
*/
|
|
130
127
|
function odataInternal(csn, internalOptions) {
|
|
131
|
-
const oDataCsn = transform4odataWithCsn(csn, internalOptions);
|
|
128
|
+
const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
|
|
132
129
|
attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
|
|
133
130
|
return oDataCsn;
|
|
134
131
|
}
|
|
@@ -150,13 +147,13 @@ function odata(csn, options = {}) {
|
|
|
150
147
|
*
|
|
151
148
|
* @param {object} csn CSN to process
|
|
152
149
|
* @param {object} [externalOptions={}] Options
|
|
153
|
-
* @returns {
|
|
150
|
+
* @returns {object} { model: string, namespace: string, unappliedExtensions: string }
|
|
154
151
|
*/
|
|
155
152
|
function cdl(csn, externalOptions = {}) {
|
|
156
153
|
const internalOptions = prepareOptions.to.cdl(externalOptions);
|
|
157
|
-
|
|
158
|
-
return result;
|
|
154
|
+
return toCdl.csnToCdl(cloneCsnNonDict(csn, internalOptions), internalOptions);
|
|
159
155
|
}
|
|
156
|
+
|
|
160
157
|
/**
|
|
161
158
|
* Transform a CSN like to.sql
|
|
162
159
|
*
|
|
@@ -168,8 +165,9 @@ function cdl(csn, externalOptions = {}) {
|
|
|
168
165
|
function forSql(csn, options = {}) {
|
|
169
166
|
const internalOptions = prepareOptions.to.sql(options);
|
|
170
167
|
internalOptions.transformation = 'sql';
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.sql');
|
|
169
|
+
|
|
170
|
+
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
173
171
|
}
|
|
174
172
|
/**
|
|
175
173
|
* Transform a CSN like to.hdi
|
|
@@ -181,8 +179,10 @@ function forSql(csn, options = {}) {
|
|
|
181
179
|
*/
|
|
182
180
|
function forHdi(csn, options = {}) {
|
|
183
181
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
184
|
-
internalOptions.
|
|
185
|
-
|
|
182
|
+
internalOptions.transformation = 'sql';
|
|
183
|
+
const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdi');
|
|
184
|
+
|
|
185
|
+
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
188
|
* Transform a CSN like to.hdbcds
|
|
@@ -196,9 +196,9 @@ function forHdbcds(csn, options = {}) {
|
|
|
196
196
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
197
197
|
internalOptions.transformation = 'hdbcds';
|
|
198
198
|
|
|
199
|
-
const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
199
|
+
const hanaCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
200
200
|
|
|
201
|
-
return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
201
|
+
return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/**
|
|
@@ -212,16 +212,64 @@ function sql(csn, options = {}) {
|
|
|
212
212
|
const internalOptions = prepareOptions.to.sql(options);
|
|
213
213
|
internalOptions.transformation = 'sql';
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
internalOptions.toSql.csn = true;
|
|
215
|
+
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
217
216
|
|
|
218
|
-
|
|
217
|
+
if (internalOptions.sqlDialect === 'postgres' && !baseModel.isBetaEnabled(internalOptions, 'postgres'))
|
|
218
|
+
error(null, null, 'sqlDialect: \'postgres\' is only supported with beta-flag \'postgres\'');
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
// we need the CSN for view sorting
|
|
221
|
+
const transformedCsn = forSql(csn, options);
|
|
222
|
+
const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
|
|
223
|
+
|
|
224
|
+
const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
|
|
221
225
|
|
|
222
226
|
return result.map(obj => obj.sql).filter(create => create);
|
|
223
227
|
}
|
|
224
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Render the given deltaCSN as SQL.
|
|
231
|
+
*
|
|
232
|
+
* @param {CSN.Model} csn A clean input CSN
|
|
233
|
+
* @param {CSN.Model} deltaCsn A CSN representing new entities and extensions
|
|
234
|
+
* @param {SqlOptions} [options={}] Options
|
|
235
|
+
* @returns {object} - definitions: An array of objects with all artifacts in the after-image. Each object specifies
|
|
236
|
+
* the artifact filename, the suffix, and the corresponding SQL statement to create
|
|
237
|
+
* the artifact.
|
|
238
|
+
* - deletions: An array of objects with the deleted artifacts. Each object specifies the artifact
|
|
239
|
+
* filename and the suffix.
|
|
240
|
+
* - migrations: An array of objects with the changed (migrated) artifacts. Each object specifies the
|
|
241
|
+
* artifact filename, the suffix, and the changeset (an array of changes, each specifying
|
|
242
|
+
* whether it incurs potential data loss, and its respective SQL statement(s), with
|
|
243
|
+
* multiple statements concatenated as a multi-line string in case the change e.g.
|
|
244
|
+
* consists of a column drop and add).
|
|
245
|
+
*/
|
|
246
|
+
function mtx(csn, deltaCsn, options = {}) {
|
|
247
|
+
if (!baseModel.isBetaEnabled(options, 'to.mtx'))
|
|
248
|
+
throw new Error('to.mtx is only available with beta flag `to.mtx`');
|
|
249
|
+
|
|
250
|
+
const internalOptions = prepareOptions.to.sql(options);
|
|
251
|
+
// TODO: Use compiler.compileSources() when this function is moved to lib/main.js
|
|
252
|
+
const merged = toCsn.compactModel(compiler.compileSourcesX({ 'base.csn': csn, 'delta.csn': deltaCsn }), options);
|
|
253
|
+
const baseSql = forSql(csn, options);
|
|
254
|
+
const mergedSql = forSql(merged, options);
|
|
255
|
+
const diff = modelCompare.compareModels(baseSql, mergedSql, options);
|
|
256
|
+
// Delete artifacts that are already present in csn
|
|
257
|
+
Object.keys(baseSql.definitions).forEach((artifactName) => {
|
|
258
|
+
if (diff.definitions[artifactName]) // don't render again, but need info for primary key extension
|
|
259
|
+
diff.definitions[artifactName]['@cds.persistence.skip'] = true;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
internalOptions.forHana = true;
|
|
263
|
+
internalOptions.beta.sqlExtensions = true;
|
|
264
|
+
const { deletions, migrations, ...additions } = toSql.toSqlDdl(diff, internalOptions);
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
additions: createSqlDefinitions(additions, mergedSql),
|
|
268
|
+
deletions: createSqlDeletions(deletions, baseSql),
|
|
269
|
+
migrations: createSqlMigrations(migrations, mergedSql),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
225
273
|
/**
|
|
226
274
|
* Process the given CSN into HDI artifacts.
|
|
227
275
|
*
|
|
@@ -233,12 +281,8 @@ function hdi(csn, options = {}) {
|
|
|
233
281
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
234
282
|
|
|
235
283
|
// we need the CSN for view sorting
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
|
|
239
|
-
|
|
240
|
-
const sqlCSN = intermediateResult.csn;
|
|
241
|
-
delete intermediateResult.csn;
|
|
284
|
+
const sqlCSN = forHdi(csn, options);
|
|
285
|
+
const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
|
|
242
286
|
|
|
243
287
|
if (internalOptions.testMode) {
|
|
244
288
|
// All this mapping is needed because sortViews crossmatches
|
|
@@ -246,7 +290,7 @@ function hdi(csn, options = {}) {
|
|
|
246
290
|
// But we also need to return it with the correct file ending in the end
|
|
247
291
|
// so remember and do lot's of mapping here.
|
|
248
292
|
|
|
249
|
-
const flat = flattenResultStructure(
|
|
293
|
+
const flat = flattenResultStructure(sqls);
|
|
250
294
|
|
|
251
295
|
const nameMapping = Object.create(null);
|
|
252
296
|
const sqlArtifactsWithCSNNamesToSort = Object.create(null);
|
|
@@ -277,7 +321,7 @@ function hdi(csn, options = {}) {
|
|
|
277
321
|
return sorted;
|
|
278
322
|
}
|
|
279
323
|
|
|
280
|
-
return remapNames(flattenResultStructure(
|
|
324
|
+
return remapNames(flattenResultStructure(sqls), sqlCSN, k => !k.endsWith('.hdbindex'));
|
|
281
325
|
}
|
|
282
326
|
/**
|
|
283
327
|
* Remap names so that they stay consistent between v1 and v2
|
|
@@ -343,91 +387,69 @@ function remapName(key, csn, filter = () => true) {
|
|
|
343
387
|
* consists of a column drop and add).
|
|
344
388
|
*/
|
|
345
389
|
function hdiMigration(csn, options, beforeImage) {
|
|
346
|
-
/**
|
|
347
|
-
* Swap arguments in case of inverted argument order.
|
|
348
|
-
* This is for backward compatibility with @sap/cds@4.5.(2…3).
|
|
349
|
-
*
|
|
350
|
-
* @todo Remove in cds-compiler@2.x
|
|
351
|
-
* @param {HdiOptions|CSN.Model} inputOptions Options or CSN image
|
|
352
|
-
* @param {HdiOptions|CSN.Model} inputBeforeImage CSN image or options
|
|
353
|
-
* @returns {Array} Array where the real options come first
|
|
354
|
-
*/
|
|
355
|
-
function backwardCompatible(inputOptions, inputBeforeImage) {
|
|
356
|
-
/**
|
|
357
|
-
* Check whether the given argument is a CSN
|
|
358
|
-
*
|
|
359
|
-
* @param {object} arg Argument to verify
|
|
360
|
-
* @returns {boolean} True if it is a CSN
|
|
361
|
-
*/
|
|
362
|
-
function isBeforeImage(arg) {
|
|
363
|
-
return arg === null || [ 'definitions', 'meta', '$version' ].some(key => key in arg);
|
|
364
|
-
}
|
|
365
|
-
return isBeforeImage(inputBeforeImage)
|
|
366
|
-
? [ inputOptions, inputBeforeImage ]
|
|
367
|
-
: [ inputBeforeImage, inputOptions ];
|
|
368
|
-
}
|
|
369
|
-
[ options, beforeImage ] = backwardCompatible(options, beforeImage);
|
|
370
|
-
|
|
371
390
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
372
|
-
internalOptions.toSql.csn = true;
|
|
373
391
|
|
|
374
392
|
// Prepare after-image.
|
|
375
|
-
|
|
376
|
-
// cloneCsnMessages(csn, options, internalOptions);
|
|
377
|
-
const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
|
|
393
|
+
const afterImage = forHdi(csn, options);
|
|
378
394
|
|
|
379
395
|
// Compare both images.
|
|
380
|
-
const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
396
|
+
const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
381
397
|
|
|
382
398
|
// Convert the diff to SQL.
|
|
383
399
|
internalOptions.forHana = true; // Make it pass the SQL rendering
|
|
384
|
-
const { deletions, migrations, ...hdbkinds } = toSqlDdl(diff, internalOptions);
|
|
400
|
+
const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
|
|
385
401
|
|
|
386
402
|
return {
|
|
387
403
|
afterImage,
|
|
388
|
-
definitions:
|
|
389
|
-
deletions:
|
|
390
|
-
migrations:
|
|
404
|
+
definitions: createSqlDefinitions(hdbkinds, afterImage),
|
|
405
|
+
deletions: createSqlDeletions(deletions, beforeImage),
|
|
406
|
+
migrations: createSqlMigrations(migrations, afterImage),
|
|
391
407
|
};
|
|
408
|
+
}
|
|
392
409
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
410
|
+
/**
|
|
411
|
+
* From the given SQLs, create the correct result structure.
|
|
412
|
+
*
|
|
413
|
+
* @param {object} hdbkinds Object of hdbkinds (such as `hdbindex`) mapped to dictionary of artifacts.
|
|
414
|
+
* @param {CSN.Model} afterImage CSN, used to create correct file names in result structure.
|
|
415
|
+
* @returns {object[]} Array of objects, each having: name, suffix and sql
|
|
416
|
+
*/
|
|
417
|
+
function createSqlDefinitions(hdbkinds, afterImage) {
|
|
418
|
+
const result = [];
|
|
419
|
+
forEach(hdbkinds, (kind, artifacts) => {
|
|
420
|
+
const suffix = `.${ kind }`;
|
|
421
|
+
forEach(artifacts, (name, sqlStatement) => {
|
|
422
|
+
if ( kind !== 'hdbindex' )
|
|
423
|
+
result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
|
|
424
|
+
else
|
|
425
|
+
result.push({ name, suffix, sql: sqlStatement });
|
|
408
426
|
});
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
427
|
+
});
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* From the given deletions, create the correct result structure.
|
|
432
|
+
*
|
|
433
|
+
* @param {object} deletions Dictionary of deletions, only keys are used.
|
|
434
|
+
* @param {CSN.Model} beforeImage CSN used to create correct file names in result structure.
|
|
435
|
+
* @returns {object[]} Array of objects, each having: name and suffix - only .hdbtable as suffix for now
|
|
436
|
+
*/
|
|
437
|
+
function createSqlDeletions(deletions, beforeImage) {
|
|
438
|
+
const result = [];
|
|
439
|
+
forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* From the given migrations, create the correct result structure.
|
|
444
|
+
*
|
|
445
|
+
* @param {object} migrations Dictionary of changesets (migrations).
|
|
446
|
+
* @param {CSN.Model} afterImage CSN used to create correct file names in result structure.
|
|
447
|
+
* @returns {object[]} Array of objects, each having: name, suffix and changeset.
|
|
448
|
+
*/
|
|
449
|
+
function createSqlMigrations(migrations, afterImage) {
|
|
450
|
+
const result = [];
|
|
451
|
+
forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
|
|
452
|
+
return result;
|
|
431
453
|
}
|
|
432
454
|
|
|
433
455
|
hdi.migration = hdiMigration;
|
|
@@ -440,14 +462,14 @@ hdi.migration = hdiMigration;
|
|
|
440
462
|
* @returns {HDBCDS} { <filename>:<content>, ...}
|
|
441
463
|
*/
|
|
442
464
|
function hdbcds(csn, options = {}) {
|
|
443
|
-
timetrace.start('to.hdbcds');
|
|
465
|
+
timetrace.timetrace.start('to.hdbcds');
|
|
444
466
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
445
467
|
internalOptions.transformation = 'hdbcds';
|
|
446
468
|
|
|
447
469
|
const hanaCsn = forHdbcds(csn, internalOptions);
|
|
448
470
|
|
|
449
471
|
const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
|
|
450
|
-
timetrace.stop();
|
|
472
|
+
timetrace.timetrace.stop();
|
|
451
473
|
return result;
|
|
452
474
|
}
|
|
453
475
|
/**
|
|
@@ -469,11 +491,11 @@ function edm(csn, options = {}) {
|
|
|
469
491
|
let servicesEdmj;
|
|
470
492
|
if (isPreTransformed(csn, 'odata')) {
|
|
471
493
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
472
|
-
servicesEdmj =
|
|
494
|
+
servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
|
|
473
495
|
}
|
|
474
496
|
else {
|
|
475
497
|
const oDataCsn = odataInternal(csn, internalOptions);
|
|
476
|
-
servicesEdmj =
|
|
498
|
+
servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
|
|
477
499
|
}
|
|
478
500
|
return servicesEdmj.edmj;
|
|
479
501
|
}
|
|
@@ -489,9 +511,9 @@ edm.all = edmall;
|
|
|
489
511
|
*/
|
|
490
512
|
function edmall(csn, options = {}) {
|
|
491
513
|
const internalOptions = prepareOptions.to.edm(options);
|
|
492
|
-
const { error } = makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
514
|
+
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
493
515
|
|
|
494
|
-
if (internalOptions.
|
|
516
|
+
if (internalOptions.odataVersion === 'v2')
|
|
495
517
|
error(null, null, 'OData JSON output is not available for OData V2');
|
|
496
518
|
|
|
497
519
|
const result = {};
|
|
@@ -503,13 +525,11 @@ function edmall(csn, options = {}) {
|
|
|
503
525
|
else
|
|
504
526
|
oDataCsn = odataInternal(csn, internalOptions);
|
|
505
527
|
|
|
506
|
-
const servicesJson =
|
|
528
|
+
const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
|
|
507
529
|
const services = servicesJson.edmj;
|
|
508
|
-
for (const serviceName in services)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
result[serviceName] = lEdm;
|
|
512
|
-
}
|
|
530
|
+
for (const serviceName in services)
|
|
531
|
+
result[serviceName] = services[serviceName];
|
|
532
|
+
|
|
513
533
|
return result;
|
|
514
534
|
}
|
|
515
535
|
/**
|
|
@@ -531,11 +551,11 @@ function edmx(csn, options = {}) {
|
|
|
531
551
|
let services;
|
|
532
552
|
if (isPreTransformed(csn, 'odata')) {
|
|
533
553
|
checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
|
|
534
|
-
services =
|
|
554
|
+
services = preparedCsnToEdmx(csn, service, internalOptions);
|
|
535
555
|
}
|
|
536
556
|
else {
|
|
537
557
|
const oDataCsn = odataInternal(csn, internalOptions);
|
|
538
|
-
services =
|
|
558
|
+
services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
|
|
539
559
|
}
|
|
540
560
|
|
|
541
561
|
return services.edmx;
|
|
@@ -562,7 +582,7 @@ function edmxall(csn, options = {}) {
|
|
|
562
582
|
else
|
|
563
583
|
oDataCsn = odataInternal(csn, internalOptions);
|
|
564
584
|
|
|
565
|
-
const servicesEdmx =
|
|
585
|
+
const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
|
|
566
586
|
const services = servicesEdmx.edmx;
|
|
567
587
|
// Create annotations and metadata once per service
|
|
568
588
|
for (const serviceName in services) {
|
|
@@ -573,6 +593,79 @@ function edmxall(csn, options = {}) {
|
|
|
573
593
|
return result;
|
|
574
594
|
}
|
|
575
595
|
|
|
596
|
+
/**
|
|
597
|
+
* Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
598
|
+
* using 'options'
|
|
599
|
+
*
|
|
600
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
601
|
+
* @param {string} service Service name to use. If you want all services, use preparedCsnToEdmxAll()
|
|
602
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
603
|
+
* @returns {object} Rendered EDMX string for the given service.
|
|
604
|
+
*/
|
|
605
|
+
function preparedCsnToEdmx(csn, service, options) {
|
|
606
|
+
const e = csn2edm(csn, service, options);
|
|
607
|
+
return {
|
|
608
|
+
edmx: (e ? e.toXML('all') : undefined),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
614
|
+
* using 'options'.
|
|
615
|
+
*
|
|
616
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
617
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
618
|
+
* @returns {object} Dictionary of rendered EDMX strings for each service.
|
|
619
|
+
*/
|
|
620
|
+
function preparedCsnToEdmxAll(csn, options) {
|
|
621
|
+
const edmxResult = csn2edmAll(csn, options);
|
|
622
|
+
for (const service in edmxResult)
|
|
623
|
+
edmxResult[service] = edmxResult[service].toXML('all');
|
|
624
|
+
|
|
625
|
+
return {
|
|
626
|
+
edmx: edmxResult,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
632
|
+
* using 'options'
|
|
633
|
+
*
|
|
634
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
635
|
+
* @param {string} service Service name for which EDMX should be rendered.
|
|
636
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
637
|
+
* @returns {object} Rendered EDM JSON object for of the given service.
|
|
638
|
+
*/
|
|
639
|
+
function preparedCsnToEdm(csn, service, options) {
|
|
640
|
+
// Override OData version as edm json is always v4
|
|
641
|
+
options.odataVersion = 'v4';
|
|
642
|
+
const e = csn2edm(csn, service, options);
|
|
643
|
+
return {
|
|
644
|
+
edmj: (e ? e.toJSON() : undefined),
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
|
|
650
|
+
* using 'options'
|
|
651
|
+
*
|
|
652
|
+
* @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
|
|
653
|
+
* @param {ODataOptions} options OData / EDMX specific options.
|
|
654
|
+
* @returns {object} Dictionary of rendered EDM JSON objects for each service.
|
|
655
|
+
*/
|
|
656
|
+
function preparedCsnToEdmAll(csn, options) {
|
|
657
|
+
// Override OData version as edm json is always v4
|
|
658
|
+
options.odataVersion = 'v4';
|
|
659
|
+
const edmj = csn2edmAll(csn, options);
|
|
660
|
+
for (const service in edmj)
|
|
661
|
+
edmj[service] = edmj[service].toJSON();
|
|
662
|
+
|
|
663
|
+
return {
|
|
664
|
+
edmj,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
|
|
576
669
|
/**
|
|
577
670
|
* Flatten the result structure to a flat map.
|
|
578
671
|
*
|
|
@@ -606,7 +699,11 @@ module.exports = {
|
|
|
606
699
|
for_sql: publishCsnProcessor(forSql, 'for.sql'),
|
|
607
700
|
for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
|
|
608
701
|
for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
|
|
609
|
-
/** */
|
|
702
|
+
/** beta - WIP */
|
|
703
|
+
mtx: publishCsnProcessor(mtx, 'to.mtx'),
|
|
704
|
+
/** Deprecated, will be removed in cds-compiler@v4 */
|
|
705
|
+
preparedCsnToEdmx,
|
|
706
|
+
preparedCsnToEdm,
|
|
610
707
|
};
|
|
611
708
|
|
|
612
709
|
|
|
@@ -639,25 +736,29 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
639
736
|
*/
|
|
640
737
|
function api( csn, options = {}, ...args ) {
|
|
641
738
|
try {
|
|
739
|
+
if (options.deprecated) {
|
|
740
|
+
const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
|
|
741
|
+
checkRemovedDeprecatedFlags( options, messageFunctions );
|
|
742
|
+
}
|
|
642
743
|
checkOutdatedOptions( options );
|
|
643
744
|
return processor( csn, options, ...args );
|
|
644
745
|
}
|
|
645
746
|
catch (err) {
|
|
646
|
-
if (err instanceof CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
|
|
747
|
+
if (err instanceof messages.CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
|
|
647
748
|
throw err;
|
|
648
749
|
|
|
649
750
|
if (options.testMode && !(err instanceof TypeError) &&
|
|
650
751
|
!(err instanceof ModelError))
|
|
651
752
|
throw err;
|
|
652
753
|
|
|
653
|
-
const { info } = makeMessageFunction( csn, options, 'compile' );
|
|
654
|
-
const msg = info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
|
|
754
|
+
const { info } = messages.makeMessageFunction( csn, options, 'compile' );
|
|
755
|
+
const msg = info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
|
|
655
756
|
if (options.internalMsg)
|
|
656
757
|
msg.error = err; // Attach original error
|
|
657
758
|
|
|
658
759
|
// next line to be replaced by CSN parser call which reads the CSN object
|
|
659
|
-
const xsn = recompileX(csn, options);
|
|
660
|
-
const recompiledCsn = compactModel(xsn);
|
|
760
|
+
const xsn = compiler.recompileX(csn, options);
|
|
761
|
+
const recompiledCsn = toCsn.compactModel(xsn);
|
|
661
762
|
return processor( recompiledCsn, options, ...args );
|
|
662
763
|
}
|
|
663
764
|
}
|
|
@@ -674,34 +775,59 @@ const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
|
|
|
674
775
|
* @param {CSN.Options} options Backend options
|
|
675
776
|
*/
|
|
676
777
|
function checkOutdatedOptions(options) {
|
|
677
|
-
|
|
678
|
-
return;
|
|
679
|
-
const { warning } = makeMessageFunction(null, options, 'api');
|
|
778
|
+
const { error, throwWithError } = messages.makeMessageFunction(null, options, 'api');
|
|
680
779
|
|
|
681
|
-
// This
|
|
682
|
-
if (options.messages
|
|
780
|
+
// This error has been emitted once, we don't need to emit it again.
|
|
781
|
+
if (options.messages?.some(m => m.messageId === 'api-invalid-option')) {
|
|
782
|
+
throwWithError();
|
|
683
783
|
return;
|
|
784
|
+
}
|
|
684
785
|
|
|
685
786
|
for (const name of oldBackendOptionNames) {
|
|
686
787
|
if (typeof options[name] === 'object') // may be a boolean due to internal options
|
|
687
|
-
|
|
788
|
+
error('api-invalid-option', null, { '#': 'std', name });
|
|
688
789
|
}
|
|
689
790
|
|
|
690
791
|
if (options.magicVars)
|
|
691
|
-
|
|
792
|
+
error('api-invalid-option', null, { '#': 'magicVars' });
|
|
692
793
|
|
|
693
794
|
// Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
|
|
694
795
|
// forgot about user -> $user and locale -> $user.locale
|
|
695
|
-
if (options.variableReplacements)
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
warning('api-invalid-option', null, { '#': 'locale' });
|
|
700
|
-
}
|
|
796
|
+
if (options.variableReplacements?.user)
|
|
797
|
+
error('api-invalid-option', null, { '#': 'user' });
|
|
798
|
+
if (options.variableReplacements?.locale)
|
|
799
|
+
error('api-invalid-option', null, { '#': 'locale' });
|
|
701
800
|
|
|
702
801
|
forEachKey(options.variableReplacements || {}, (name) => {
|
|
703
802
|
if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
|
|
704
|
-
|
|
803
|
+
error('api-invalid-option', null, { '#': 'noDollar', name });
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
throwWithError();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Load the module on-demand and not immediately.
|
|
811
|
+
*
|
|
812
|
+
* @param {string} moduleName Name of the module to load - like with require
|
|
813
|
+
* @returns {object} A Proxy that handles the on-demand loading
|
|
814
|
+
*/
|
|
815
|
+
function lazyload(moduleName) {
|
|
816
|
+
let module;
|
|
817
|
+
return new Proxy(((...args) => {
|
|
818
|
+
if (!module) // eslint-disable-next-line global-require
|
|
819
|
+
module = require(moduleName);
|
|
820
|
+
|
|
821
|
+
if (module.apply && typeof module.apply === 'function')
|
|
822
|
+
return module.apply(this, args);
|
|
823
|
+
return module; // for destructured calls
|
|
824
|
+
}), {
|
|
825
|
+
get(target, name) {
|
|
826
|
+
if (!module) // eslint-disable-next-line global-require
|
|
827
|
+
module = require(moduleName);
|
|
828
|
+
|
|
829
|
+
return module[name];
|
|
830
|
+
},
|
|
705
831
|
});
|
|
706
832
|
}
|
|
707
833
|
|