@sap/cds-compiler 4.4.4 → 4.6.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 +88 -0
- package/bin/cdsc.js +18 -11
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +22 -0
- package/lib/api/main.js +306 -144
- package/lib/api/options.js +18 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +45 -10
- package/lib/base/messages.js +33 -16
- package/lib/base/model.js +4 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +8 -7
- package/lib/compiler/assert-consistency.js +40 -17
- package/lib/compiler/builtins.js +30 -53
- package/lib/compiler/checks.js +46 -14
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +35 -10
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +94 -35
- package/lib/compiler/shared.js +60 -33
- package/lib/compiler/tweak-assocs.js +188 -92
- package/lib/compiler/utils.js +11 -1
- package/lib/edm/annotations/edmJson.js +41 -66
- package/lib/edm/annotations/genericTranslation.js +27 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +28 -11
- package/lib/edm/edmInboundChecks.js +58 -15
- package/lib/edm/edmPreprocessor.js +12 -16
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +10 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +15 -2
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +6557 -5618
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +29 -4
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +28 -7
- package/lib/language/genericAntlrParser.js +118 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/main.d.ts +28 -3
- package/lib/main.js +3 -0
- package/lib/model/csnRefs.js +4 -1
- package/lib/model/csnUtils.js +20 -14
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +23 -22
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +47 -26
- package/lib/render/toHdbcds.js +63 -42
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +71 -117
- package/lib/render/utils/common.js +41 -6
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +57 -4
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +70 -71
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/draft/db.js +2 -16
- package/lib/transform/draft/odata.js +3 -3
- package/lib/transform/effective/associations.js +3 -5
- package/lib/transform/effective/main.js +6 -9
- package/lib/transform/forOdata.js +26 -55
- package/lib/transform/forRelationalDB.js +38 -18
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/transform/transformUtils.js +47 -34
- package/lib/transform/translateAssocsToJoins.js +45 -11
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
package/lib/optionProcessor.js
CHANGED
|
@@ -13,12 +13,12 @@ optionProcessor
|
|
|
13
13
|
.option('-h, --help')
|
|
14
14
|
.option('-v, --version')
|
|
15
15
|
.option(' --options <file>')
|
|
16
|
-
.option('-w, --warning <level>', ['0', '1', '2', '3'])
|
|
16
|
+
.option('-w, --warning <level>', { valid: ['0', '1', '2', '3'] })
|
|
17
17
|
.option(' --quiet')
|
|
18
18
|
.option(' --show-message-id')
|
|
19
19
|
.option(' --no-message-id')
|
|
20
20
|
.option(' --no-message-context')
|
|
21
|
-
.option(' --color <mode>', ['auto', 'always', 'never'])
|
|
21
|
+
.option(' --color <mode>', { valid: ['auto', 'always', 'never'] })
|
|
22
22
|
.option('-o, --out <dir>')
|
|
23
23
|
.option(' --cds-home <dir>')
|
|
24
24
|
.option(' --module-lookup-directories <list>')
|
|
@@ -34,13 +34,14 @@ optionProcessor
|
|
|
34
34
|
.option(' --beta <list>')
|
|
35
35
|
.option(' --deprecated <list>')
|
|
36
36
|
.option(' --direct-backend')
|
|
37
|
-
.option(' --fallback-parser <type>', ['cdl', 'csn', 'csn!'])
|
|
37
|
+
.option(' --fallback-parser <type>', { valid: ['cdl', 'csn', 'csn!'] })
|
|
38
38
|
.option(' --shuffle <seed>') // 0 | 1..4294967296
|
|
39
39
|
.option(' --test-mode')
|
|
40
40
|
.option(' --test-sort-csn')
|
|
41
41
|
.option(' --doc-comment')
|
|
42
42
|
.option(' --add-texts-language-assoc')
|
|
43
43
|
.option(' --localized-without-coalesce')
|
|
44
|
+
.option(' --tenant-as-column')
|
|
44
45
|
.option(' --default-binary-length <length>')
|
|
45
46
|
.option(' --default-string-length <length>')
|
|
46
47
|
.option(' --no-recompile')
|
|
@@ -100,6 +101,7 @@ optionProcessor
|
|
|
100
101
|
-E, --enrich-csn Show non-enumerable CSN properties and locations of references
|
|
101
102
|
-R, --raw-output <name> Write XSN for definition "name" and error output to <stdout>,
|
|
102
103
|
with name = "+", write complete XSN, long!
|
|
104
|
+
--tenant-as-column Add tenant fields to entities
|
|
103
105
|
--internal-msg Write raw messages with call stack to <stdout>/<stderr>
|
|
104
106
|
--beta-mode Enable all unsupported, incomplete (beta) features
|
|
105
107
|
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
|
|
@@ -164,7 +166,7 @@ optionProcessor
|
|
|
164
166
|
// ----------- toHana -----------
|
|
165
167
|
optionProcessor.command('H, toHana')
|
|
166
168
|
.option('-h, --help')
|
|
167
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
169
|
+
.option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: ['--names'] })
|
|
168
170
|
.option(' --render-virtual')
|
|
169
171
|
.option(' --joinfk')
|
|
170
172
|
.option('-u, --user <user>')
|
|
@@ -172,8 +174,8 @@ optionProcessor.command('H, toHana')
|
|
|
172
174
|
.option('-c, --csn')
|
|
173
175
|
.option(' --integrity-not-validated')
|
|
174
176
|
.option(' --integrity-not-enforced')
|
|
175
|
-
.option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
|
|
176
|
-
.option(' --assert-integrity-type <type>', ['RT', 'DB'],
|
|
177
|
+
.option(' --assert-integrity <mode>', { valid: ['true', 'false', 'individual'] })
|
|
178
|
+
.option(' --assert-integrity-type <type>', { valid: ['RT', 'DB'], ignoreCase: true })
|
|
177
179
|
.option(' --pre2134ReferentialConstraintNames')
|
|
178
180
|
.option(' --disable-hana-comments')
|
|
179
181
|
.help(`
|
|
@@ -216,7 +218,7 @@ optionProcessor.command('H, toHana')
|
|
|
216
218
|
|
|
217
219
|
optionProcessor.command('O, toOdata')
|
|
218
220
|
.option('-h, --help')
|
|
219
|
-
.option('-v, --odata-version <version>', ['v2', 'v4', 'v4x'],
|
|
221
|
+
.option('-v, --odata-version <version>', { valid: ['v2', 'v4', 'v4x'], aliases: ['--version'] })
|
|
220
222
|
.option('-x, --xml')
|
|
221
223
|
.option('-j, --json')
|
|
222
224
|
.option(' --odata-containment')
|
|
@@ -227,10 +229,9 @@ optionProcessor.command('O, toOdata')
|
|
|
227
229
|
.option(' --odata-foreign-keys')
|
|
228
230
|
.option(' --odata-v2-partial-constr')
|
|
229
231
|
.option(' --odata-vocabularies <list>')
|
|
230
|
-
.option(' --odata-open-type')
|
|
231
232
|
.option('-c, --csn')
|
|
232
|
-
.option('-f, --odata-format <format>', ['flat', 'structured'])
|
|
233
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
233
|
+
.option('-f, --odata-format <format>', { valid: ['flat', 'structured'] })
|
|
234
|
+
.option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: [ '--names' ] })
|
|
234
235
|
.option('-s, --service-names <list>')
|
|
235
236
|
.option(' --fewer-localized-views')
|
|
236
237
|
.help(`
|
|
@@ -260,7 +261,6 @@ optionProcessor.command('O, toOdata')
|
|
|
260
261
|
(Not spec compliant and V2 only)
|
|
261
262
|
--odata-vocabularies <list> JSON array of adhoc vocabulary definitions
|
|
262
263
|
{ prefix: { alias, ns, uri }, ... }
|
|
263
|
-
--odata-open-type Renders all structured types as OpenType=true, if not annotated otherwise.
|
|
264
264
|
-n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
|
|
265
265
|
the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
|
|
266
266
|
plain : (default) Names in uppercase and flattened with underscores
|
|
@@ -287,24 +287,25 @@ optionProcessor.command('C, toCdl')
|
|
|
287
287
|
|
|
288
288
|
optionProcessor.command('Q, toSql')
|
|
289
289
|
.option('-h, --help')
|
|
290
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
291
|
-
.option('-d, --sql-dialect <dialect>',
|
|
290
|
+
.option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: [ '--names' ] })
|
|
291
|
+
.option('-d, --sql-dialect <dialect>', { valid: ['hana', 'sqlite', 'plain', 'postgres', 'h2'], aliases: [ '--dialect' ] })
|
|
292
292
|
.option(' --render-virtual')
|
|
293
293
|
.option(' --joinfk')
|
|
294
294
|
.option('-u, --user <user>')
|
|
295
295
|
.option('-l, --locale <locale>')
|
|
296
|
-
.option('-s, --src <style>', ['sql', 'hdi'])
|
|
296
|
+
.option('-s, --src <style>', { valid: ['sql', 'hdi'] })
|
|
297
297
|
.option('-c, --csn')
|
|
298
298
|
.option(' --integrity-not-validated')
|
|
299
299
|
.option(' --integrity-not-enforced')
|
|
300
|
-
.option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
|
|
301
|
-
.option(' --assert-integrity-type <type>', ['RT', 'DB'],
|
|
300
|
+
.option(' --assert-integrity <mode>', { valid: ['true', 'false', 'individual'] })
|
|
301
|
+
.option(' --assert-integrity-type <type>', { valid: ['RT', 'DB'], ignoreCase: true })
|
|
302
302
|
.option(' --constraints-in-create-table')
|
|
303
303
|
.option(' --pre2134ReferentialConstraintNames')
|
|
304
304
|
.option(' --disable-hana-comments')
|
|
305
305
|
.option(' --generated-by-comment')
|
|
306
306
|
.option(' --better-sqlite-session-variables')
|
|
307
307
|
.option(' --fewer-localized-views')
|
|
308
|
+
.option(' --without-hana-associations')
|
|
308
309
|
.help(`
|
|
309
310
|
Usage: cdsc toSql [options] <files...>
|
|
310
311
|
|
|
@@ -360,12 +361,12 @@ optionProcessor.command('Q, toSql')
|
|
|
360
361
|
active if sqlDialect is \`sqlite\`
|
|
361
362
|
--fewer-localized-views If set, the backends will not create localized convenience views for
|
|
362
363
|
those views, that only have an association to a localized entity/view.
|
|
363
|
-
|
|
364
|
+
--without-hana-associations If set, the backend will not render a "WITH ASSOCIATIONS" for sqlDialect 'hana'
|
|
364
365
|
`);
|
|
365
366
|
|
|
366
367
|
optionProcessor.command('toRename')
|
|
367
368
|
.option('-h, --help')
|
|
368
|
-
.option('-n, --sql-mapping <style>', ['quoted', 'hdbcds'],
|
|
369
|
+
.option('-n, --sql-mapping <style>', { valid: ['quoted', 'hdbcds'], aliases: ['--names'] })
|
|
369
370
|
.help(`
|
|
370
371
|
Usage: cdsc toRename [options] <files...>
|
|
371
372
|
|
|
@@ -387,14 +388,14 @@ optionProcessor.command('toRename')
|
|
|
387
388
|
|
|
388
389
|
optionProcessor.command('manageConstraints')
|
|
389
390
|
.option('-h, --help')
|
|
390
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
391
|
-
.option('-s, --src <style>', ['sql', 'hdi'])
|
|
391
|
+
.option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: ['--names'] })
|
|
392
|
+
.option('-s, --src <style>', { valid: ['sql', 'hdi'] })
|
|
392
393
|
.option(' --drop')
|
|
393
394
|
.option(' --alter')
|
|
394
395
|
.option(' --violations')
|
|
395
396
|
.option(' --integrity-not-validated')
|
|
396
397
|
.option(' --integrity-not-enforced')
|
|
397
|
-
.option('-d, --sql-dialect <dialect>',
|
|
398
|
+
.option('-d, --sql-dialect <dialect>', { valid: ['hana', 'sqlite', 'plain', 'postgres', 'h2'], aliases: [ '--dialect' ] })
|
|
398
399
|
.help(`
|
|
399
400
|
Usage: cdsc manageConstraints [options] <files...>
|
|
400
401
|
|
|
@@ -433,7 +434,7 @@ optionProcessor.command('manageConstraints')
|
|
|
433
434
|
|
|
434
435
|
optionProcessor.command('toCsn')
|
|
435
436
|
.option('-h, --help')
|
|
436
|
-
.option('-f, --csn-flavor <flavor>', ['client', 'gensrc', 'universal'],
|
|
437
|
+
.option('-f, --csn-flavor <flavor>', { valid: ['client', 'gensrc', 'universal'], aliases: ['--flavor'] })
|
|
437
438
|
.option(' --with-localized')
|
|
438
439
|
.option(' --with-locations')
|
|
439
440
|
.option(' --struct-xpr')
|
|
@@ -6,8 +6,6 @@ const {
|
|
|
6
6
|
getResultingName,
|
|
7
7
|
} = require('../model/csnUtils');
|
|
8
8
|
const { forEach } = require('../utils/objectUtils');
|
|
9
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
10
|
-
const { optionProcessor } = require('../optionProcessor');
|
|
11
9
|
const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
|
|
12
10
|
|
|
13
11
|
const {
|
|
@@ -20,14 +18,15 @@ const { sortCsn } = require('../json/to-csn');
|
|
|
20
18
|
* Not part of our API, yet.
|
|
21
19
|
*
|
|
22
20
|
* @param {CSN.Model} csn
|
|
21
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
23
22
|
* @param {CSN.Options} options
|
|
24
23
|
*/
|
|
25
|
-
function alterConstraintsWithCsn( csn, options ) {
|
|
26
|
-
const { error, warning } =
|
|
24
|
+
function alterConstraintsWithCsn( csn, options, messageFunctions ) {
|
|
25
|
+
const { error, warning } = messageFunctions;
|
|
27
26
|
|
|
28
27
|
const {
|
|
29
28
|
drop, alter, src, violations, sqlDialect,
|
|
30
|
-
} = options
|
|
29
|
+
} = options;
|
|
31
30
|
|
|
32
31
|
if (!sqlDialect || sqlDialect === 'h2' || sqlDialect === 'plain')
|
|
33
32
|
warning(null, null, { prop: sqlDialect || 'plain' }, 'Referential Constraints are not available for sql dialect $(PROP)');
|
|
@@ -35,12 +34,11 @@ function alterConstraintsWithCsn( csn, options ) {
|
|
|
35
34
|
if (drop && alter)
|
|
36
35
|
error(null, null, 'Option “--drop” can\'t be combined with “--alter”');
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
// Of course we want the database constraints
|
|
37
|
+
// Of course, we want the database constraints
|
|
40
38
|
options.assertIntegrityType = options.assertIntegrityType || 'DB';
|
|
41
39
|
|
|
42
40
|
const transformedOptions = _transformSqlOptions(csn, options);
|
|
43
|
-
const forSqlCsn = transformForRelationalDBWithCsn(csn, transformedOptions,
|
|
41
|
+
const forSqlCsn = transformForRelationalDBWithCsn(csn, transformedOptions, messageFunctions);
|
|
44
42
|
|
|
45
43
|
if (violations && src && src !== 'sql') {
|
|
46
44
|
error(null, null, { value: '--violations', othervalue: src },
|
|
@@ -56,32 +54,18 @@ function alterConstraintsWithCsn( csn, options ) {
|
|
|
56
54
|
return intermediateResult;
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
// TODO: Remove / Move to api/options.js once alterConstraintsWithCsn is available outside bin/cdsc
|
|
58
|
+
function _transformSqlOptions( csn, options ) {
|
|
59
|
+
const { src } = options;
|
|
60
|
+
// eslint-disable-next-line global-require
|
|
61
|
+
const prepareOptions = require('../api/options');
|
|
62
|
+
options = prepareOptions.to.sql(options);
|
|
63
|
+
options.src = src;
|
|
60
64
|
// Merge options with defaults.
|
|
61
65
|
options = Object.assign({ sqlMapping: 'plain', sqlDialect: 'plain' }, options);
|
|
62
66
|
options.toSql = true;
|
|
63
|
-
|
|
64
67
|
if (!options.src && !options.csn)
|
|
65
68
|
options.src = 'sql';
|
|
66
|
-
|
|
67
|
-
const { warning, error } = makeMessageFunction(model, options, 'to.sql');
|
|
68
|
-
|
|
69
|
-
optionProcessor.verifyOptions(options, 'toSql', true)
|
|
70
|
-
// eslint-disable-next-line cds-compiler/message-template-string
|
|
71
|
-
.forEach(complaint => warning(null, null, `${complaint}`));
|
|
72
|
-
|
|
73
|
-
if (options.sqlDialect !== 'hana') {
|
|
74
|
-
// CDXCORE-465, 'quoted' and 'hdbcds' are to be used in combination with dialect 'hana' only
|
|
75
|
-
if (options.sqlMapping === 'quoted' || options.sqlMapping === 'hdbcds') {
|
|
76
|
-
error(null, null, { value: options.sqlDialect, othervalue: options.sqlMapping },
|
|
77
|
-
'Option sqlDialect: $(VALUE) can\'t be combined with sqlMapping: $(OTHERVALUE)');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// No non-HANA SQL for HDI
|
|
81
|
-
if (options.src === 'hdi')
|
|
82
|
-
error(null, null, { value: options.sqlDialect }, 'Option sqlDialect: $(VALUE) can\'t be used for SAP HANA HDI');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
69
|
return options;
|
|
86
70
|
}
|
|
87
71
|
|
package/lib/render/toCdl.js
CHANGED
|
@@ -5,13 +5,11 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
|
5
5
|
const { findElement, createExpressionRenderer, withoutCast } = require('./utils/common');
|
|
6
6
|
const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEscapes');
|
|
7
7
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
8
|
-
const { timetrace } = require('../utils/timetrace');
|
|
9
8
|
const { forEachDefinition, normalizeTypeRef } = require('../model/csnUtils');
|
|
10
9
|
const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
|
|
11
10
|
const { isBetaEnabled } = require('../base/model');
|
|
12
11
|
const { ModelError } = require('../base/error');
|
|
13
|
-
const {
|
|
14
|
-
const { typeParameters, specialFunctions } = require('../compiler/builtins');
|
|
12
|
+
const { typeParameters, specialFunctions, isAnnotationExpression } = require('../compiler/builtins');
|
|
15
13
|
const { forEach } = require('../utils/objectUtils');
|
|
16
14
|
const {
|
|
17
15
|
isBuiltinType,
|
|
@@ -30,14 +28,11 @@ const specialFunctionKeywords = Object.create(null);
|
|
|
30
28
|
* - `namespace`: Namespace statement + `using from './model.cds'.
|
|
31
29
|
*
|
|
32
30
|
* @param {CSN.Model} csn
|
|
33
|
-
* @param {CSN.Options}
|
|
31
|
+
* @param {CSN.Options} options
|
|
32
|
+
* @param {object} msg Message Functions
|
|
34
33
|
*/
|
|
35
|
-
function csnToCdl( csn, options ) {
|
|
34
|
+
function csnToCdl( csn, options, msg ) {
|
|
36
35
|
const special$self = !csn?.definitions?.$self && '$self';
|
|
37
|
-
timetrace.start('CDL rendering');
|
|
38
|
-
|
|
39
|
-
const msg = makeMessageFunction(csn, options, 'to.cdl');
|
|
40
|
-
|
|
41
36
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
|
|
42
37
|
// Since the expander modifies the CSN, we need to clone it first or
|
|
43
38
|
// toCdl can't guarantee that the input CSN is not modified.
|
|
@@ -91,8 +86,6 @@ function csnToCdl( csn, options ) {
|
|
|
91
86
|
cdlResult.model = usingsStr + cdlResult.model;
|
|
92
87
|
}
|
|
93
88
|
|
|
94
|
-
timetrace.stop('CDL rendering');
|
|
95
|
-
|
|
96
89
|
msg.throwWithError();
|
|
97
90
|
return cdlResult;
|
|
98
91
|
|
|
@@ -589,11 +582,11 @@ function csnToCdl( csn, options ) {
|
|
|
589
582
|
* @param {CdlRenderEnvironment} env
|
|
590
583
|
*/
|
|
591
584
|
function renderElement( elementName, element, env ) {
|
|
585
|
+
const isCalcElement = (element.value !== undefined);
|
|
592
586
|
let result = renderAnnotationAssignmentsAndDocComment(element, env);
|
|
593
587
|
result += env.indent;
|
|
594
588
|
result += element.virtual ? 'virtual ' : '';
|
|
595
589
|
result += element.key ? 'key ' : '';
|
|
596
|
-
// TODO(v5): Remove once deprecated flag for `masked` is removed.
|
|
597
590
|
result += element.masked ? 'masked ' : '';
|
|
598
591
|
result += quoteNonIdentifierOrKeyword(elementName, env);
|
|
599
592
|
if (element.val !== undefined) { // enum value
|
|
@@ -602,13 +595,16 @@ function csnToCdl( csn, options ) {
|
|
|
602
595
|
else if (element['#'] !== undefined) { // enum symbol reference
|
|
603
596
|
result += ` = #${element['#']}`;
|
|
604
597
|
}
|
|
605
|
-
else {
|
|
598
|
+
else if (!isCalcElement || !isDirectAssocOrComp(element.type)) {
|
|
599
|
+
// If the element is a calculated element _and_ a direct association or
|
|
600
|
+
// composition, we'd render `Association to F on (cond) = calcValue;` which
|
|
601
|
+
// would alter the ON-condition.
|
|
606
602
|
const props = renderTypeReferenceAndProps(element, env);
|
|
607
603
|
if (props !== '')
|
|
608
604
|
result += ` : ${props}`;
|
|
609
605
|
}
|
|
610
606
|
|
|
611
|
-
if (
|
|
607
|
+
if (isCalcElement) { // calculated element // @ts-ignore
|
|
612
608
|
result += ' = ';
|
|
613
609
|
if (element.value.xpr && xprContainsCondition(element.value.xpr))
|
|
614
610
|
result += exprRenderer.renderSubExpr(element.value, env.withSubPath([ 'value' ]));
|
|
@@ -873,7 +869,7 @@ function csnToCdl( csn, options ) {
|
|
|
873
869
|
// of an `annotate` statement. That may change in the future.
|
|
874
870
|
result += renderDocComment(element, env);
|
|
875
871
|
}
|
|
876
|
-
// Note: parentheses are a workaround for #9015
|
|
872
|
+
// Note: parentheses are a workaround for #9015; TODO: Check & Update
|
|
877
873
|
result += renderAnnotationAssignmentsAndDocComment(col, env, { parentheses: true });
|
|
878
874
|
result += env.indent;
|
|
879
875
|
|
|
@@ -1316,7 +1312,7 @@ function csnToCdl( csn, options ) {
|
|
|
1316
1312
|
}
|
|
1317
1313
|
|
|
1318
1314
|
// Association type
|
|
1319
|
-
if (type
|
|
1315
|
+
if (isDirectAssocOrComp(type)) {
|
|
1320
1316
|
const isComp = type === 'cds.Composition';
|
|
1321
1317
|
// Type, cardinality and target; CAPire uses CamelCase
|
|
1322
1318
|
result += isComp ? 'Composition' : 'Association';
|
|
@@ -1350,7 +1346,7 @@ function csnToCdl( csn, options ) {
|
|
|
1350
1346
|
result += renderNullability(artifact);
|
|
1351
1347
|
|
|
1352
1348
|
if (artifact.default && !artifact.on)
|
|
1353
|
-
result +=
|
|
1349
|
+
result += renderDefaultExpr(artifact, env);
|
|
1354
1350
|
return result;
|
|
1355
1351
|
}
|
|
1356
1352
|
|
|
@@ -1383,7 +1379,7 @@ function csnToCdl( csn, options ) {
|
|
|
1383
1379
|
// If there is a default value, and it's a calculated element, do not
|
|
1384
1380
|
// render the default (because it's not supported for calc elements).
|
|
1385
1381
|
if (artifact.default !== undefined && !artifact.value)
|
|
1386
|
-
result +=
|
|
1382
|
+
result += renderDefaultExpr(artifact, env);
|
|
1387
1383
|
|
|
1388
1384
|
return result;
|
|
1389
1385
|
}
|
|
@@ -1460,11 +1456,7 @@ function csnToCdl( csn, options ) {
|
|
|
1460
1456
|
* @param {CdlRenderEnvironment} env
|
|
1461
1457
|
*/
|
|
1462
1458
|
function renderAnnotationValue( annoValue, env ) {
|
|
1463
|
-
|
|
1464
|
-
// it could be `type: 'unchecked'`.
|
|
1465
|
-
const isXpr = annoValue?.['='] !== undefined && (Object.keys(annoValue).length > 1) &&
|
|
1466
|
-
isBetaEnabled(options, 'annotationExpressions');
|
|
1467
|
-
if (isXpr) {
|
|
1459
|
+
if (isAnnotationExpression(annoValue)) {
|
|
1468
1460
|
// Once inside an expression, we stay there.
|
|
1469
1461
|
const xpr = exprRenderer.renderExpr(annoValue, env);
|
|
1470
1462
|
return `( ${xpr} )`;
|
|
@@ -1758,6 +1750,17 @@ function csnToCdl( csn, options ) {
|
|
|
1758
1750
|
return result;
|
|
1759
1751
|
}
|
|
1760
1752
|
|
|
1753
|
+
function renderDefaultExpr( art, env ) {
|
|
1754
|
+
if (!art.default)
|
|
1755
|
+
return '';
|
|
1756
|
+
let result = ' default ';
|
|
1757
|
+
if ( art.default.xpr && xprContainsCondition( art.default.xpr))
|
|
1758
|
+
result += exprRenderer.renderSubExpr(withoutCast( art.default), env.withSubPath([ 'default' ]));
|
|
1759
|
+
else
|
|
1760
|
+
result += exprRenderer.renderExpr(withoutCast( art.default), env.withSubPath([ 'default' ]));
|
|
1761
|
+
return result;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1761
1764
|
// Render the nullability of an element or parameter (can be unset, true, or false)
|
|
1762
1765
|
function renderNullability( obj /* , env */) {
|
|
1763
1766
|
if (obj.notNull === undefined) {
|
|
@@ -1852,7 +1855,7 @@ function csnToCdl( csn, options ) {
|
|
|
1852
1855
|
name = name.substring(1);
|
|
1853
1856
|
// Take the annotation assignment apart into <nameBeforeVariant>#<variantAndRest>
|
|
1854
1857
|
const parts = name.split('#');
|
|
1855
|
-
|
|
1858
|
+
let nameBeforeVariant = parts[0];
|
|
1856
1859
|
const variant = parts.length > 1 ? parts.slice(1).join('#') : undefined;
|
|
1857
1860
|
const { parentheses } = config;
|
|
1858
1861
|
|
|
@@ -1860,8 +1863,13 @@ function csnToCdl( csn, options ) {
|
|
|
1860
1863
|
if (parentheses)
|
|
1861
1864
|
result += '(';
|
|
1862
1865
|
|
|
1866
|
+
// if the variant is empty, render '#' as part of the name, e.g `variant !== undefined`.
|
|
1867
|
+
if (variant === '')
|
|
1868
|
+
nameBeforeVariant += '#';
|
|
1869
|
+
|
|
1863
1870
|
result += quoteAnnotationPathIfRequired(nameBeforeVariant, env);
|
|
1864
|
-
|
|
1871
|
+
|
|
1872
|
+
if (variant !== undefined && variant !== '') {
|
|
1865
1873
|
// Unfortunately, the compiler does not allow `.@` after the first variant identifier,
|
|
1866
1874
|
// nor multiple `#`, so we're back at simple paths that are possibly quoted.
|
|
1867
1875
|
result += `#${quotePathIfRequired(variant, env)}`;
|
|
@@ -1939,7 +1947,7 @@ function csnToCdl( csn, options ) {
|
|
|
1939
1947
|
aliasOnly: x => x.as,
|
|
1940
1948
|
enum: x => `#${x['#']}`,
|
|
1941
1949
|
ref(x) {
|
|
1942
|
-
return `${
|
|
1950
|
+
return `${x.param ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
|
|
1943
1951
|
},
|
|
1944
1952
|
windowFunction(x) {
|
|
1945
1953
|
const funcDef = this.func(x);
|
|
@@ -2182,6 +2190,19 @@ function requiresQuotingForCdl( id, additionalKeywords ) {
|
|
|
2182
2190
|
additionalKeywords.includes(id.toUpperCase());
|
|
2183
2191
|
}
|
|
2184
2192
|
|
|
2193
|
+
/**
|
|
2194
|
+
* Returns true if the given type is an association or composition,
|
|
2195
|
+
* without type indirection.
|
|
2196
|
+
*
|
|
2197
|
+
* @param {string|string[]} type
|
|
2198
|
+
* @return {boolean}
|
|
2199
|
+
*/
|
|
2200
|
+
|
|
2201
|
+
function isDirectAssocOrComp( type ) {
|
|
2202
|
+
type = normalizeTypeRef(type);
|
|
2203
|
+
return (type === 'cds.Association' || type === 'cds.Composition');
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2185
2206
|
const conditionOperators = [
|
|
2186
2207
|
// Antlr rule 'condition', 'conditionAnd'
|
|
2187
2208
|
'AND', 'OR',
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -4,12 +4,14 @@ const {
|
|
|
4
4
|
getLastPartOf, getLastPartOfRef,
|
|
5
5
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
6
6
|
getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement, hasAnnotationValue,
|
|
7
|
+
pathName, isMagicVariable,
|
|
7
8
|
} = require('../model/csnUtils');
|
|
8
9
|
const keywords = require('../base/keywords');
|
|
9
10
|
const {
|
|
10
11
|
renderFunc, createExpressionRenderer, getRealName, addContextMarkers, addIntermediateContexts,
|
|
11
12
|
hasHanaComment, getHanaComment, funcWithoutParen, getSqlSnippets,
|
|
12
|
-
cdsToSqlTypes, cdsToHdbcdsTypes, withoutCast,
|
|
13
|
+
cdsToSqlTypes, cdsToHdbcdsTypes, withoutCast, variableForDialect,
|
|
14
|
+
isVariableReplacementRequired,
|
|
13
15
|
} = require('./utils/common');
|
|
14
16
|
const {
|
|
15
17
|
renderReferentialConstraint,
|
|
@@ -17,11 +19,11 @@ const {
|
|
|
17
19
|
const DuplicateChecker = require('./DuplicateChecker');
|
|
18
20
|
const { isDeprecatedEnabled, forEachDefinition } = require('../base/model');
|
|
19
21
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
20
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
21
22
|
const { timetrace } = require('../utils/timetrace');
|
|
22
23
|
|
|
23
24
|
const { smartId, delimitedId } = require('../sql-identifier');
|
|
24
25
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
26
|
+
const { pathId } = require('../model/csnRefs');
|
|
25
27
|
|
|
26
28
|
const $PROJECTION = '$projection';
|
|
27
29
|
const $SELF = '$self';
|
|
@@ -89,9 +91,10 @@ function renderStringForHdbcds( str ) {
|
|
|
89
91
|
*
|
|
90
92
|
* @param {CSN.Model} csn HANA transformed CSN
|
|
91
93
|
* @param {CSN.Options} [options] Transformation options
|
|
94
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
92
95
|
* @returns {object} Dictionary of filename: content
|
|
93
96
|
*/
|
|
94
|
-
function toHdbcdsSource( csn, options ) {
|
|
97
|
+
function toHdbcdsSource( csn, options, messageFunctions ) {
|
|
95
98
|
timetrace.start('HDBCDS rendering');
|
|
96
99
|
const plainNames = options.sqlMapping === 'plain';
|
|
97
100
|
const quotedNames = options.sqlMapping === 'quoted';
|
|
@@ -99,7 +102,13 @@ function toHdbcdsSource( csn, options ) {
|
|
|
99
102
|
|
|
100
103
|
const {
|
|
101
104
|
info, warning, error, throwWithAnyError, message,
|
|
102
|
-
} =
|
|
105
|
+
} = messageFunctions;
|
|
106
|
+
if (options.tenantAsColumn) {
|
|
107
|
+
error('api-unexpected-option', null, { option: 'tenantAsColumn', module: 'to.hdbcds' });
|
|
108
|
+
throwWithAnyError();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const reportedMissingReplacements = Object.create(null);
|
|
103
112
|
|
|
104
113
|
const exprRenderer = createExpressionRenderer({
|
|
105
114
|
finalize: x => x,
|
|
@@ -732,7 +741,7 @@ function toHdbcdsSource( csn, options ) {
|
|
|
732
741
|
|
|
733
742
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
734
743
|
if (path.ref.length > 1)
|
|
735
|
-
result += `.${
|
|
744
|
+
result += `.${renderTypeRef({ ref: path.ref.slice(1) }, env)}`;
|
|
736
745
|
|
|
737
746
|
return result;
|
|
738
747
|
}
|
|
@@ -1209,11 +1218,10 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1209
1218
|
*
|
|
1210
1219
|
* @param {string|object} s Path step
|
|
1211
1220
|
* @param {number} idx Path position
|
|
1212
|
-
* @param {any[]} ref
|
|
1213
1221
|
* @param {HdbcdsRenderEnvironment} env
|
|
1214
1222
|
* @returns {string} Rendered path step
|
|
1215
1223
|
*/
|
|
1216
|
-
function renderPathStep( s, idx,
|
|
1224
|
+
function renderPathStep( s, idx, env ) {
|
|
1217
1225
|
// Simple id or absolute name
|
|
1218
1226
|
if (typeof s === 'string') {
|
|
1219
1227
|
// HANA-specific extra magic (should actually be in forRelationalDB)
|
|
@@ -1227,12 +1235,6 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1227
1235
|
return plainNames ? renderAbsoluteNamePlain(env.currentArtifactName, env)
|
|
1228
1236
|
: renderAbsoluteNameWithQuotes(env.currentArtifactName, env);
|
|
1229
1237
|
}
|
|
1230
|
-
// HANA-specific translation of '$now' and '$user'
|
|
1231
|
-
if (s === '$now' && ref.length === 1)
|
|
1232
|
-
return 'CURRENT_TIMESTAMP';
|
|
1233
|
-
|
|
1234
|
-
// In first path position, do not quote $projection and magic $-variables like CURRENT_DATE, $now etc.
|
|
1235
|
-
// FIXME: We should rather explicitly recognize quoting somehow
|
|
1236
1238
|
|
|
1237
1239
|
// TODO: quote $parameters if it doesn't reference a parameter, this requires knowledge about the kind
|
|
1238
1240
|
// Example: both views are correct in HANA CDS
|
|
@@ -1316,42 +1318,61 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1316
1318
|
return renderFunc(funcName, x, 'hana', a => renderArgs(a, '=>', this.env));
|
|
1317
1319
|
}
|
|
1318
1320
|
|
|
1321
|
+
|
|
1319
1322
|
/**
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1323
|
+
* Render a magic variable. Values are determined in following order:
|
|
1324
|
+
* 1. User defined replacement in options.variableReplacements
|
|
1325
|
+
* 2. Predefined fallback values
|
|
1326
|
+
* 3. Rendering of the variable as a string (i.e. its name) + warning
|
|
1327
|
+
*
|
|
1328
|
+
* @param {CSN.Path} ref
|
|
1329
|
+
* @param {object} env
|
|
1330
|
+
* @return {string}
|
|
1323
1331
|
*/
|
|
1324
|
-
function
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1332
|
+
function renderMagicVariable( ref, env ) {
|
|
1333
|
+
const magicReplacement = getVariableReplacement(ref, options);
|
|
1334
|
+
if (magicReplacement !== null)
|
|
1335
|
+
return renderStringForHdbcds(magicReplacement);
|
|
1336
|
+
|
|
1337
|
+
const name = pathName(ref);
|
|
1338
|
+
const result = variableForDialect(options, name);
|
|
1339
|
+
if (result)
|
|
1340
|
+
return result;
|
|
1330
1341
|
|
|
1331
|
-
|
|
1342
|
+
if (isVariableReplacementRequired(name)) {
|
|
1343
|
+
reportedMissingReplacements[name] = true;
|
|
1344
|
+
error('ref-undefined-var', env.path, { '#': 'value', id: name, option: 'variableReplacements' });
|
|
1345
|
+
}
|
|
1346
|
+
else if (!reportedMissingReplacements[name]) {
|
|
1347
|
+
reportedMissingReplacements[name] = true;
|
|
1348
|
+
warning('ref-unsupported-variable', env.path, { name, option: 'variableReplacements' },
|
|
1349
|
+
'Variable $(NAME) is not supported. Use option $(OPTION) to specify a value for $(NAME)');
|
|
1350
|
+
}
|
|
1332
1351
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1352
|
+
return renderStringForHdbcds(name);
|
|
1353
|
+
}
|
|
1336
1354
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1355
|
+
/**
|
|
1356
|
+
* Must not be used for type refs, as something like `$user` will be interpreted as a magic
|
|
1357
|
+
* variable and not definition name.
|
|
1358
|
+
*
|
|
1359
|
+
* @param {object} x Expression with a ref property
|
|
1360
|
+
* @returns {string} Rendered expression
|
|
1361
|
+
* @todo no extra magic with x.param
|
|
1362
|
+
*/
|
|
1363
|
+
function renderExpressionRef( x ) {
|
|
1364
|
+
if (!x.param && isMagicVariable(pathId(x.ref[0])))
|
|
1365
|
+
return renderMagicVariable(x.ref, this.env);
|
|
1339
1366
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if (x.ref[1] === 'from')
|
|
1345
|
-
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-FROM\'))';
|
|
1367
|
+
const prefix = x.param ? ':' : '';
|
|
1368
|
+
const ref = x.ref.map((step, index) => renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.');
|
|
1369
|
+
return `${prefix}${ref}`;
|
|
1370
|
+
}
|
|
1346
1371
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
return renderStringForHdbcds(magicReplacement);
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
|
|
1372
|
+
function renderTypeRef( x, env ) {
|
|
1373
|
+
const prefix = x.param ? ':' : '';
|
|
1374
|
+
const ref = x.ref.map((step, index) => renderPathStep(step, index, env.withSubPath([ 'ref', index ]))).join('.');
|
|
1375
|
+
return `${prefix}${ref}`;
|
|
1355
1376
|
}
|
|
1356
1377
|
|
|
1357
1378
|
/**
|
package/lib/render/toRename.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
5
4
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
6
5
|
const { forEachDefinition } = require('../model/csnUtils');
|
|
7
|
-
const { optionProcessor } = require('../optionProcessor');
|
|
8
6
|
const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
|
|
9
7
|
const { getIdentifierUtils } = require('./utils/sql');
|
|
10
8
|
|
|
@@ -25,25 +23,23 @@ const { getIdentifierUtils } = require('./utils/sql');
|
|
|
25
23
|
* @todo clarify input parameters
|
|
26
24
|
* @param {CSN.Model} inputCsn CSN?
|
|
27
25
|
* @param {CSN.Options} options Transformation options
|
|
26
|
+
* @param {object} messageFunctions
|
|
28
27
|
* @returns {object} A dictionary of name: rename statement
|
|
29
28
|
*/
|
|
30
|
-
function toRename( inputCsn, options ) {
|
|
31
|
-
const { warning, throwWithError } =
|
|
29
|
+
function toRename( inputCsn, options, messageFunctions ) {
|
|
30
|
+
const { warning, throwWithError } = messageFunctions;
|
|
32
31
|
|
|
33
32
|
// Merge options with defaults.
|
|
33
|
+
// TODO: Use api/options.js if this ever becomes an official API.
|
|
34
34
|
options = Object.assign({ sqlMapping: 'hdbcds', sqlDialect: 'hana' }, options);
|
|
35
|
-
|
|
36
|
-
// Verify options
|
|
37
|
-
optionProcessor.verifyOptions(options, 'toRename')
|
|
38
|
-
// eslint-disable-next-line cds-compiler/message-template-string
|
|
39
|
-
.forEach(complaint => warning(null, null, `${complaint}`));
|
|
40
35
|
checkCSNVersion(inputCsn, options);
|
|
41
36
|
|
|
42
37
|
// Let users know that this is internal
|
|
43
38
|
warning(null, null, 'Generation of SQL rename statements is a beta feature and might change in the future');
|
|
44
39
|
|
|
45
40
|
// FIXME: Currently, 'toRename' implies transformation for HANA (transferring the options to forRelationalDB)
|
|
46
|
-
const csn = transformForRelationalDBWithCsn(inputCsn, options,
|
|
41
|
+
const csn = transformForRelationalDBWithCsn(inputCsn, options, messageFunctions);
|
|
42
|
+
messageFunctions.setModel(csn);
|
|
47
43
|
const hdbcdsOrQuotedIdentifiers = getIdentifierUtils(csn, options);
|
|
48
44
|
const plainIdentifiers = getIdentifierUtils(csn, { sqlDialect: 'hana', sqlMapping: 'plain' });
|
|
49
45
|
|