@sap/cds-compiler 4.5.0 → 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 +43 -7
- package/bin/cdsc.js +13 -11
- package/doc/CHANGELOG_BETA.md +6 -0
- package/lib/api/main.js +256 -115
- package/lib/api/options.js +8 -0
- package/lib/base/message-registry.js +17 -4
- package/lib/base/messages.js +15 -3
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +2 -3
- package/lib/compiler/assert-consistency.js +2 -1
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +30 -6
- package/lib/compiler/define.js +31 -9
- package/lib/compiler/resolve.js +26 -21
- package/lib/compiler/shared.js +19 -9
- package/lib/compiler/tweak-assocs.js +82 -107
- package/lib/compiler/utils.js +2 -1
- package/lib/edm/annotations/edmJson.js +23 -22
- package/lib/edm/annotations/genericTranslation.js +14 -4
- package/lib/edm/csn2edm.js +24 -10
- package/lib/edm/edmInboundChecks.js +1 -2
- package/lib/edm/edmPreprocessor.js +11 -9
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +2 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -1
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +5253 -5214
- package/lib/json/to-csn.js +7 -1
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +21 -4
- package/lib/language/genericAntlrParser.js +9 -11
- 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 +12 -7
- package/lib/optionProcessor.js +21 -19
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +18 -15
- package/lib/render/toHdbcds.js +59 -28
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +57 -82
- package/lib/render/utils/common.js +17 -0
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +27 -31
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/flattening.js +68 -69
- 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 +13 -9
- package/lib/transform/forRelationalDB.js +36 -17
- 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 +33 -8
- package/package.json +2 -2
package/lib/json/to-csn.js
CHANGED
|
@@ -206,6 +206,10 @@ const typeProperties = [
|
|
|
206
206
|
'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
|
|
207
207
|
'$typeArgs', // for unresolved type arguments, e.g. through parseCql
|
|
208
208
|
];
|
|
209
|
+
// Properties which cause a `cast` property to be rendered
|
|
210
|
+
const castProperties = [
|
|
211
|
+
'target', 'enum', 'items', 'type',
|
|
212
|
+
];
|
|
209
213
|
|
|
210
214
|
const csnDictionaries = [
|
|
211
215
|
'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
|
|
@@ -1495,7 +1499,9 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1495
1499
|
// elements of sub queries (in expr) are hidden (not set via Object.assign):
|
|
1496
1500
|
if (!expr.cast && expr.elements)
|
|
1497
1501
|
setHidden( col, 'elements', expr.elements );
|
|
1498
|
-
|
|
1502
|
+
// CDL-style cast with explicit type properties
|
|
1503
|
+
if (castProperties.findIndex(prop => (elem[prop] &&
|
|
1504
|
+
!elem[prop].$inferred && !elem[prop][$inferred])) > -1)
|
|
1499
1505
|
cast( col, elem );
|
|
1500
1506
|
}
|
|
1501
1507
|
finally {
|
|
@@ -78,9 +78,11 @@ class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
|
|
|
78
78
|
function initTokenRewrite( recognizer, ts ) { // ts = tokenStream
|
|
79
79
|
ts.DOTbeforeBRACE = Parser.DOTbeforeBRACE;
|
|
80
80
|
ts.BRACE = tokenTypeOf( recognizer, "'{'" );
|
|
81
|
+
ts.BRACE_CLOSE = tokenTypeOf( recognizer, "'}'" );
|
|
81
82
|
ts.DOT = tokenTypeOf( recognizer, "'.'" );
|
|
82
83
|
ts.ASTERISK = tokenTypeOf( recognizer, "'*'" );
|
|
83
84
|
ts.AT = tokenTypeOf( recognizer, "'@'" );
|
|
85
|
+
ts.SEMICOLON = tokenTypeOf( recognizer, "';'" );
|
|
84
86
|
ts.NEW = Parser.NEW;
|
|
85
87
|
ts.Identifier = Parser.Identifier;
|
|
86
88
|
ts.PAREN = tokenTypeOf( recognizer, "'('" );
|
|
@@ -90,7 +92,22 @@ function initTokenRewrite( recognizer, ts ) { // ts = tokenStream
|
|
|
90
92
|
if (ts.DOT && ts.DOTbeforeBRACE)
|
|
91
93
|
recognizer.tokenRewrite[ts.DOTbeforeBRACE - Parser.Identifier] = ts.DOT;
|
|
92
94
|
}
|
|
93
|
-
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function initCodeCompletionTokenArrays( parser ) {
|
|
98
|
+
// Set of top-level keywords used for code completion after token '}'
|
|
99
|
+
// belonging to a top-level definition
|
|
100
|
+
const startRuleIndex = parser.ruleNames.indexOf('start');
|
|
101
|
+
const startState = parser.atn.ruleToStartState[startRuleIndex].stateNumber;
|
|
102
|
+
const tokens = parser.atn.nextTokens(parser.atn.states[startState]);
|
|
103
|
+
tokens.removeOne(parser.symbolicNames.indexOf('NAMESPACE'));
|
|
104
|
+
tokens.removeOne(parser.symbolicNames.indexOf('HideAlternatives'));
|
|
105
|
+
|
|
106
|
+
parser.topLevelKeywords = [];
|
|
107
|
+
for (const interval of tokens.intervals) {
|
|
108
|
+
for (let i = interval.start; i < interval.stop; i++)
|
|
109
|
+
parser.topLevelKeywords.push(i);
|
|
110
|
+
}
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
function tokenTypeOf( recognizer, literalName ) {
|
|
@@ -123,6 +140,7 @@ function parse( source, filename = '<undefined>.cds',
|
|
|
123
140
|
parser.$messageFunctions = messageFunctions;
|
|
124
141
|
|
|
125
142
|
initTokenRewrite( parser, tokenStream );
|
|
143
|
+
initCodeCompletionTokenArrays(parser);
|
|
126
144
|
// comment the following 2 lines if you want to output the parser errors directly:
|
|
127
145
|
parser.messageErrorListener = errorListener;
|
|
128
146
|
parser._errHandler = new errorStrategy.KeywordErrorStrategy();
|
|
@@ -447,16 +447,33 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
|
|
|
447
447
|
for (let j = v.start; j < v.stop; j++) {
|
|
448
448
|
// a generic keyword as such does not appear in messages, only its replacements,
|
|
449
449
|
// which are function name and argument position dependent:
|
|
450
|
-
if (j === pc.GenericExpr)
|
|
450
|
+
if (j === pc.GenericExpr) {
|
|
451
451
|
names.push( ...recognizer.$genericKeywords.expr );
|
|
452
|
-
|
|
452
|
+
}
|
|
453
|
+
else if (j === pc.GenericSeparator) {
|
|
453
454
|
names.push( ...recognizer.$genericKeywords.separator );
|
|
454
|
-
|
|
455
|
+
}
|
|
456
|
+
else if (j === pc.GenericIntro) {
|
|
455
457
|
names.push( ...recognizer.$genericKeywords.introMsg );
|
|
458
|
+
}
|
|
459
|
+
else if (j === pc.SemicolonTopLevel) {
|
|
460
|
+
// We only insert a semikolon (i.e. make it optional) after a closing brace.
|
|
461
|
+
// If the previous token is not `}`, don't propose these keywords, as ';' is required.
|
|
462
|
+
if (recognizer._input.LA(-1) === recognizer._input.BRACE_CLOSE) {
|
|
463
|
+
const name = recognizer.topLevelKeywords.map(i => expected
|
|
464
|
+
.elementName(recognizer.literalNames, recognizer.symbolicNames, i));
|
|
465
|
+
names.push(...name);
|
|
466
|
+
if (recognizer._ctx.outer?.kind !== 'source') {
|
|
467
|
+
if (names.includes('<EOF>'))
|
|
468
|
+
names.splice(names.indexOf('<EOF>'), 1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
456
472
|
// other expected tokens usually appear in messages, except the helper tokens
|
|
457
473
|
// which are used to solve ambiguities via the parser method setLocalToken():
|
|
458
|
-
else if (j !== pc.HelperToken1 && j !== pc.HelperToken2)
|
|
474
|
+
else if (j !== pc.HelperToken1 && j !== pc.HelperToken2) {
|
|
459
475
|
names.push( expected.elementName(recognizer.literalNames, recognizer.symbolicNames, j ) );
|
|
476
|
+
}
|
|
460
477
|
}
|
|
461
478
|
}
|
|
462
479
|
// The parser method excludeExpected() additionally removes some tokens from the message:
|
|
@@ -1247,30 +1247,28 @@ function finalizeDictOrArray( dict ) {
|
|
|
1247
1247
|
}
|
|
1248
1248
|
|
|
1249
1249
|
function insertSemicolon() {
|
|
1250
|
-
// hand-selected keyword list: first() set of `artifactDefOrExtend`
|
|
1251
|
-
const allowedKeywords = [ this.constructor.ACTION, this.constructor.ABSTRACT,
|
|
1252
|
-
this.constructor.ANNOTATE, this.constructor.ANNOTATION, this.constructor.ASPECT,
|
|
1253
|
-
this.constructor.CONTEXT, this.constructor.DEFINE, this.constructor.ENTITY,
|
|
1254
|
-
this.constructor.EOF, this.constructor.EVENT, this.constructor.EXTEND,
|
|
1255
|
-
this.constructor.FUNCTION, this.constructor.SERVICE,
|
|
1256
|
-
this.constructor.TYPE, this.constructor.VIEW, this.literalNames.indexOf( "'@'" ) ];
|
|
1257
|
-
|
|
1258
1250
|
const currentToken = this._input.tokens[this._input.index];
|
|
1251
|
+
const requireSemicolon = this.topLevelKeywords.includes(currentToken.type);
|
|
1259
1252
|
|
|
1260
|
-
if (
|
|
1253
|
+
if (requireSemicolon) {
|
|
1254
|
+
this.noAssignmentInSameLine();
|
|
1261
1255
|
const prev = this._input.LT(-1);
|
|
1262
1256
|
const t = CommonTokenFactory.create(
|
|
1263
|
-
currentToken.source,
|
|
1264
|
-
''
|
|
1257
|
+
currentToken.source,
|
|
1258
|
+
this.literalNames.indexOf( "';'" ),
|
|
1259
|
+
'', antlr4.Token.DEFAULT_CHANNEL,
|
|
1265
1260
|
prev.stop, prev.stop,
|
|
1266
1261
|
prev.line, prev.column
|
|
1267
1262
|
);
|
|
1263
|
+
|
|
1268
1264
|
t.tokenIndex = prev.tokenIndex + 1;
|
|
1265
|
+
|
|
1269
1266
|
this._input.tokens.splice(t.tokenIndex, 0, t);
|
|
1270
1267
|
|
|
1271
1268
|
// Update tokenIndex: There could have been comments between two non-hidden tokens.
|
|
1272
1269
|
for (let tokenIndex = t.tokenIndex + 1; tokenIndex < this._input.tokens.length; tokenIndex++)
|
|
1273
1270
|
this._input.tokens[tokenIndex].tokenIndex += 1;
|
|
1271
|
+
|
|
1274
1272
|
this._input.index = t.tokenIndex;
|
|
1275
1273
|
}
|
|
1276
1274
|
}
|
package/lib/main.d.ts
CHANGED
|
@@ -27,9 +27,9 @@ declare namespace compiler {
|
|
|
27
27
|
messages?: object[]
|
|
28
28
|
/**
|
|
29
29
|
* Dictionary of message-ids and their reclassified severity. This option
|
|
30
|
-
* can be used to increase the severity of messages. The compiler
|
|
31
|
-
* ignore decreased severities as this may lead to issues
|
|
32
|
-
* compilation otherwise.
|
|
30
|
+
* can be used to increase the severity of messages. The compiler may
|
|
31
|
+
* ignore decreased severities of error messages as this may lead to issues
|
|
32
|
+
* during compilation otherwise.
|
|
33
33
|
*/
|
|
34
34
|
severities?: { [messageId: string]: MessageSeverity}
|
|
35
35
|
/**
|
|
@@ -1023,6 +1023,31 @@ declare namespace compiler {
|
|
|
1023
1023
|
function migration(csn: CSN, options: SqlOptions, beforeImage: CSN): SqlMigrationResult;
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
+
/**
|
|
1027
|
+
* Renders the given CSN into EDM in the JSON format _and_ XML format.
|
|
1028
|
+
* That is, it is a combination of `to.edm()` and `to.edmx()`.
|
|
1029
|
+
* Requires `options.service` to be set.
|
|
1030
|
+
*
|
|
1031
|
+
* Not to be confused with `for.odata()`, which returns an OData transformed CSN.
|
|
1032
|
+
*
|
|
1033
|
+
* @returns An object `'<protocol>': object` where the value is `'<serviceName>': object` entry
|
|
1034
|
+
* which consists of `{edmx: string, edm?: object}`.
|
|
1035
|
+
* @since v4.6.0
|
|
1036
|
+
*/
|
|
1037
|
+
function odata(csn: CSN, options?: ODataOptions): Record<string, object>;
|
|
1038
|
+
namespace odata {
|
|
1039
|
+
/**
|
|
1040
|
+
* Renders the given CSN into EDM in JSON format _and_ XML format for each service.
|
|
1041
|
+
* That is, it is a combination of `to.edm.all()` and `to.edmx.all()`.
|
|
1042
|
+
* If `options.serviceNames` is not set, all services will be rendered.
|
|
1043
|
+
*
|
|
1044
|
+
* @returns A map of `'<protocol>': object` where each entry is `'<serviceName>': object` entries where
|
|
1045
|
+
* each entry consists of `{edmx: string, edm?: object}`.
|
|
1046
|
+
* @since v4.6.0
|
|
1047
|
+
*/
|
|
1048
|
+
function all(csn: CSN, options: ODataOptions): Record<string, object>;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1026
1051
|
/**
|
|
1027
1052
|
* Renders the given CSN into EDM (JSON format). Requires `options.service` to be set.
|
|
1028
1053
|
*
|
package/lib/main.js
CHANGED
|
@@ -158,6 +158,9 @@ module.exports = {
|
|
|
158
158
|
edmx: Object.assign((...args) => snapi.edmx(...args), {
|
|
159
159
|
all: (...args) => snapi.edmx.all(...args)
|
|
160
160
|
}),
|
|
161
|
+
odata: Object.assign((...args) => snapi.odata2(...args), {
|
|
162
|
+
all: (...args) => snapi.odata2.all(...args)
|
|
163
|
+
}),
|
|
161
164
|
},
|
|
162
165
|
// Convenience for hdbtabledata calculation in @sap/cds
|
|
163
166
|
getArtifactCdsPersistenceName: (...args) => csnUtils.getArtifactDatabaseNameOf(...args),
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -613,7 +613,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
613
613
|
return resolvePath( path, alias._select || alias._ref, null,
|
|
614
614
|
'alias', ncache.$queryNumber );
|
|
615
615
|
}
|
|
616
|
-
const mixin = ncache._select.mixin
|
|
616
|
+
const mixin = ncache._select.mixin?.[head];
|
|
617
617
|
if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
|
|
618
618
|
setCache( mixin, '_parent', qcache._select );
|
|
619
619
|
return resolvePath( path, mixin, null, 'mixin', ncache.$queryNumber );
|
|
@@ -818,6 +818,9 @@ function csnRefs( csn, universalReady ) {
|
|
|
818
818
|
as = `$_column_${ colIndex + 1 }`;
|
|
819
819
|
setCache( col, '$as', as );
|
|
820
820
|
let type = parentElementOrQueryCache;
|
|
821
|
+
if (col.cast)
|
|
822
|
+
traverseType( col.cast, col, 'column', colIndex, initNode );
|
|
823
|
+
|
|
821
824
|
while (type.items)
|
|
822
825
|
type = type.items;
|
|
823
826
|
const elem = setCache( col, '_element', type.elements[as] );
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { csnRefs, implicitAs } = require('
|
|
3
|
+
const { csnRefs, implicitAs, pathId } = require('./csnRefs');
|
|
4
4
|
const { applyTransformations, applyTransformationsOnNonDictionary, applyTransformationsOnDictionary } = require('../transform/db/applyTransformations');
|
|
5
|
-
const { isBuiltinType } = require('../compiler/builtins.js');
|
|
5
|
+
const { isBuiltinType, isMagicVariable, isAnnotationExpression } = require('../compiler/builtins.js');
|
|
6
6
|
const {
|
|
7
7
|
sortCsn,
|
|
8
8
|
cloneCsnDictionary: _cloneCsnDictionary,
|
|
@@ -1088,18 +1088,21 @@ function getLastPartOfRef( ref ) {
|
|
|
1088
1088
|
* @param {object} toNode
|
|
1089
1089
|
* @param {boolean} [overwrite]
|
|
1090
1090
|
* @param {object} excludes
|
|
1091
|
+
* @param {array} annoNames (copy only these annotations or all if undefined)
|
|
1092
|
+
* @returns {array} copiedAnnoNames
|
|
1091
1093
|
*/
|
|
1092
|
-
function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {} ) {
|
|
1094
|
+
function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {}, annoNames = undefined ) {
|
|
1093
1095
|
// Ignore if no toNode (in case of errors)
|
|
1094
1096
|
if (!toNode)
|
|
1095
1097
|
return;
|
|
1096
1098
|
|
|
1097
|
-
|
|
1099
|
+
if (annoNames == null)
|
|
1100
|
+
annoNames = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1098
1101
|
|
|
1099
|
-
|
|
1102
|
+
annoNames.forEach((anno) => {
|
|
1100
1103
|
if ((toNode[anno] === undefined || overwrite) && !excludes[anno])
|
|
1101
1104
|
toNode[anno] = cloneAnnotationValue(fromNode[anno]);
|
|
1102
|
-
}
|
|
1105
|
+
});
|
|
1103
1106
|
}
|
|
1104
1107
|
|
|
1105
1108
|
|
|
@@ -1446,7 +1449,7 @@ function isAssociationOperand( arg, path, inspectRef ) {
|
|
|
1446
1449
|
}
|
|
1447
1450
|
|
|
1448
1451
|
function pathName( ref ) {
|
|
1449
|
-
return ref ? ref.map(
|
|
1452
|
+
return ref ? ref.map( pathId ).join( '.' ) : '';
|
|
1450
1453
|
}
|
|
1451
1454
|
|
|
1452
1455
|
module.exports = {
|
|
@@ -1456,6 +1459,8 @@ module.exports = {
|
|
|
1456
1459
|
cloneCsnDictionary,
|
|
1457
1460
|
cloneAnnotationValue,
|
|
1458
1461
|
isBuiltinType,
|
|
1462
|
+
isMagicVariable,
|
|
1463
|
+
isAnnotationExpression,
|
|
1459
1464
|
applyAnnotationsFromExtensions,
|
|
1460
1465
|
forEachGeneric,
|
|
1461
1466
|
forEachDefinition,
|
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')
|
|
@@ -228,8 +230,8 @@ optionProcessor.command('O, toOdata')
|
|
|
228
230
|
.option(' --odata-v2-partial-constr')
|
|
229
231
|
.option(' --odata-vocabularies <list>')
|
|
230
232
|
.option('-c, --csn')
|
|
231
|
-
.option('-f, --odata-format <format>', ['flat', 'structured'])
|
|
232
|
-
.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' ] })
|
|
233
235
|
.option('-s, --service-names <list>')
|
|
234
236
|
.option(' --fewer-localized-views')
|
|
235
237
|
.help(`
|
|
@@ -285,18 +287,18 @@ optionProcessor.command('C, toCdl')
|
|
|
285
287
|
|
|
286
288
|
optionProcessor.command('Q, toSql')
|
|
287
289
|
.option('-h, --help')
|
|
288
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
289
|
-
.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' ] })
|
|
290
292
|
.option(' --render-virtual')
|
|
291
293
|
.option(' --joinfk')
|
|
292
294
|
.option('-u, --user <user>')
|
|
293
295
|
.option('-l, --locale <locale>')
|
|
294
|
-
.option('-s, --src <style>', ['sql', 'hdi'])
|
|
296
|
+
.option('-s, --src <style>', { valid: ['sql', 'hdi'] })
|
|
295
297
|
.option('-c, --csn')
|
|
296
298
|
.option(' --integrity-not-validated')
|
|
297
299
|
.option(' --integrity-not-enforced')
|
|
298
|
-
.option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
|
|
299
|
-
.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 })
|
|
300
302
|
.option(' --constraints-in-create-table')
|
|
301
303
|
.option(' --pre2134ReferentialConstraintNames')
|
|
302
304
|
.option(' --disable-hana-comments')
|
|
@@ -364,7 +366,7 @@ optionProcessor.command('Q, toSql')
|
|
|
364
366
|
|
|
365
367
|
optionProcessor.command('toRename')
|
|
366
368
|
.option('-h, --help')
|
|
367
|
-
.option('-n, --sql-mapping <style>', ['quoted', 'hdbcds'],
|
|
369
|
+
.option('-n, --sql-mapping <style>', { valid: ['quoted', 'hdbcds'], aliases: ['--names'] })
|
|
368
370
|
.help(`
|
|
369
371
|
Usage: cdsc toRename [options] <files...>
|
|
370
372
|
|
|
@@ -386,14 +388,14 @@ optionProcessor.command('toRename')
|
|
|
386
388
|
|
|
387
389
|
optionProcessor.command('manageConstraints')
|
|
388
390
|
.option('-h, --help')
|
|
389
|
-
.option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'],
|
|
390
|
-
.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'] })
|
|
391
393
|
.option(' --drop')
|
|
392
394
|
.option(' --alter')
|
|
393
395
|
.option(' --violations')
|
|
394
396
|
.option(' --integrity-not-validated')
|
|
395
397
|
.option(' --integrity-not-enforced')
|
|
396
|
-
.option('-d, --sql-dialect <dialect>',
|
|
398
|
+
.option('-d, --sql-dialect <dialect>', { valid: ['hana', 'sqlite', 'plain', 'postgres', 'h2'], aliases: [ '--dialect' ] })
|
|
397
399
|
.help(`
|
|
398
400
|
Usage: cdsc manageConstraints [options] <files...>
|
|
399
401
|
|
|
@@ -432,7 +434,7 @@ optionProcessor.command('manageConstraints')
|
|
|
432
434
|
|
|
433
435
|
optionProcessor.command('toCsn')
|
|
434
436
|
.option('-h, --help')
|
|
435
|
-
.option('-f, --csn-flavor <flavor>', ['client', 'gensrc', 'universal'],
|
|
437
|
+
.option('-f, --csn-flavor <flavor>', { valid: ['client', 'gensrc', 'universal'], aliases: ['--flavor'] })
|
|
436
438
|
.option(' --with-localized')
|
|
437
439
|
.option(' --with-locations')
|
|
438
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, xprInAnnoProperties } = 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
|
|
|
@@ -1353,7 +1346,7 @@ function csnToCdl( csn, options ) {
|
|
|
1353
1346
|
result += renderNullability(artifact);
|
|
1354
1347
|
|
|
1355
1348
|
if (artifact.default && !artifact.on)
|
|
1356
|
-
result +=
|
|
1349
|
+
result += renderDefaultExpr(artifact, env);
|
|
1357
1350
|
return result;
|
|
1358
1351
|
}
|
|
1359
1352
|
|
|
@@ -1386,7 +1379,7 @@ function csnToCdl( csn, options ) {
|
|
|
1386
1379
|
// If there is a default value, and it's a calculated element, do not
|
|
1387
1380
|
// render the default (because it's not supported for calc elements).
|
|
1388
1381
|
if (artifact.default !== undefined && !artifact.value)
|
|
1389
|
-
result +=
|
|
1382
|
+
result += renderDefaultExpr(artifact, env);
|
|
1390
1383
|
|
|
1391
1384
|
return result;
|
|
1392
1385
|
}
|
|
@@ -1463,8 +1456,7 @@ function csnToCdl( csn, options ) {
|
|
|
1463
1456
|
* @param {CdlRenderEnvironment} env
|
|
1464
1457
|
*/
|
|
1465
1458
|
function renderAnnotationValue( annoValue, env ) {
|
|
1466
|
-
|
|
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) {
|