@sap/cds-compiler 6.9.2 → 7.0.1
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 +86 -2
- package/bin/cdsc.js +4 -33
- package/doc/IncompatibleChanges_v7.md +639 -0
- package/lib/api/main.js +4 -56
- package/lib/api/options.js +6 -15
- package/lib/api/validate.js +1 -0
- package/lib/base/builtins.js +1 -2
- package/lib/base/csnRefs.js +2 -6
- package/lib/base/message-registry.js +82 -76
- package/lib/base/messages.js +23 -4
- package/lib/base/optionProcessor.js +2 -72
- package/lib/base/specialOptions.js +20 -17
- package/lib/checks/defaultValues.js +1 -39
- package/lib/checks/hasPersistedElements.js +19 -3
- package/lib/checks/parameters.js +0 -34
- package/lib/checks/selectItems.js +2 -38
- package/lib/checks/typeParameters.js +162 -0
- package/lib/checks/validator.js +5 -8
- package/lib/compiler/assert-consistency.js +19 -5
- package/lib/compiler/checks.js +47 -43
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +102 -111
- package/lib/compiler/generate.js +4 -8
- package/lib/compiler/populate.js +4 -7
- package/lib/compiler/propagator.js +9 -9
- package/lib/compiler/resolve.js +205 -7
- package/lib/compiler/shared.js +76 -82
- package/lib/compiler/tweak-assocs.js +102 -22
- package/lib/compiler/utils.js +57 -12
- package/lib/compiler/xpr-rewrite.js +2 -15
- package/lib/edm/annotations/edmJson.js +14 -10
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/edm/annotations/preprocessAnnotations.js +9 -26
- package/lib/edm/csn2edm.js +27 -20
- package/lib/edm/edmUtils.js +25 -0
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2237 -2241
- package/lib/gen/Dictionary.json +17 -2
- package/lib/json/from-csn.js +67 -52
- package/lib/json/to-csn.js +28 -25
- package/lib/language/textUtils.js +0 -13
- package/lib/main.d.ts +34 -59
- package/lib/main.js +1 -1
- package/lib/model/csnUtils.js +9 -8
- package/lib/parsers/AstBuildingParser.js +45 -55
- package/lib/parsers/Lexer.js +2 -0
- package/lib/parsers/identifiers.js +0 -9
- package/lib/render/toCdl.js +41 -40
- package/lib/render/toSql.js +8 -1
- package/lib/render/utils/common.js +1 -1
- package/lib/render/utils/sql.js +2 -3
- package/lib/tool-lib/enrichCsn.js +1 -2
- package/lib/transform/db/applyTransformations.js +7 -5
- package/lib/transform/db/assertUnique.js +8 -51
- package/lib/transform/db/associations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -15
- package/lib/transform/db/expansion.js +9 -12
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/groupByOrderBy.js +0 -16
- package/lib/transform/db/views.js +57 -161
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdata.js +25 -14
- package/lib/transform/forRelationalDB.js +93 -301
- package/lib/transform/localized.js +33 -102
- package/lib/transform/odata/flattening.js +11 -2
- package/lib/transform/transformUtils.js +25 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
- package/package.json +2 -2
- package/lib/render/toHdbcds.js +0 -1810
package/lib/gen/Dictionary.json
CHANGED
|
@@ -1741,10 +1741,18 @@
|
|
|
1741
1741
|
},
|
|
1742
1742
|
"PersonalData.IsPotentiallySensitive": {
|
|
1743
1743
|
"AppliesTo": [
|
|
1744
|
-
"Property"
|
|
1744
|
+
"Property",
|
|
1745
|
+
"EntityType"
|
|
1745
1746
|
],
|
|
1746
1747
|
"Type": "Core.Tag"
|
|
1747
1748
|
},
|
|
1749
|
+
"PersonalData.RelatedDataCategoryID": {
|
|
1750
|
+
"AppliesTo": [
|
|
1751
|
+
"Property",
|
|
1752
|
+
"EntityType"
|
|
1753
|
+
],
|
|
1754
|
+
"Type": "Collection(Edm.String)"
|
|
1755
|
+
},
|
|
1748
1756
|
"Repeatability.DeleteWithClientIDSupported": {
|
|
1749
1757
|
"AppliesTo": [
|
|
1750
1758
|
"EntityContainer"
|
|
@@ -3434,7 +3442,6 @@
|
|
|
3434
3442
|
"$kind": "ComplexType",
|
|
3435
3443
|
"Properties": {
|
|
3436
3444
|
"Discretionary": "Edm.Boolean",
|
|
3437
|
-
"EffectTypes": "Common.EffectType",
|
|
3438
3445
|
"SourceEntities": "Collection(Edm.NavigationPropertyPath)",
|
|
3439
3446
|
"SourceEvents": "Collection(Edm.String)",
|
|
3440
3447
|
"SourceProperties": "Collection(Edm.PropertyPath)",
|
|
@@ -4235,6 +4242,10 @@
|
|
|
4235
4242
|
"Type": "String",
|
|
4236
4243
|
"Value": "ContractRelatedID"
|
|
4237
4244
|
},
|
|
4245
|
+
"DataCategoryID": {
|
|
4246
|
+
"Type": "String",
|
|
4247
|
+
"Value": "DataCategoryID"
|
|
4248
|
+
},
|
|
4238
4249
|
"DataControllerID": {
|
|
4239
4250
|
"Type": "String",
|
|
4240
4251
|
"Value": "DataControllerID"
|
|
@@ -4255,6 +4266,10 @@
|
|
|
4255
4266
|
"Type": "String",
|
|
4256
4267
|
"Value": "EndOfRetentionDate"
|
|
4257
4268
|
},
|
|
4269
|
+
"IsBlockedIndicator": {
|
|
4270
|
+
"Type": "String",
|
|
4271
|
+
"Value": "IsBlockedIndicator"
|
|
4272
|
+
},
|
|
4258
4273
|
"LegalEntityID": {
|
|
4259
4274
|
"Type": "String",
|
|
4260
4275
|
"Value": "LegalEntityID"
|
package/lib/json/from-csn.js
CHANGED
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
|
|
118
118
|
const { dictAdd } = require('../compiler/dictionaries');
|
|
119
119
|
const { quotedLiteralPatterns } = require('../compiler/builtins');
|
|
120
|
-
const { exprProperties } = require('../base/builtins');
|
|
120
|
+
const { exprProperties, primaryExprProperties } = require('../base/builtins');
|
|
121
121
|
const { CompilerAssertion } = require('../base/error');
|
|
122
122
|
const { Location } = require('../base/location');
|
|
123
123
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
@@ -499,9 +499,9 @@ const schema = compileSchema( {
|
|
|
499
499
|
'#': {
|
|
500
500
|
noPrefix: true, // schema spec for '#', not for '#whatever'
|
|
501
501
|
type: symbol,
|
|
502
|
-
// Note:
|
|
503
|
-
//
|
|
504
|
-
inKind: [ '$column', 'enum' ],
|
|
502
|
+
// Note: currently, we allow `#` in enum symbol definitions (→ also via CSN),
|
|
503
|
+
// the compiler just emits a warning ref-unexpected-enum
|
|
504
|
+
inKind: [ '$column', 'enum' ], // TODO v8: delete 'enum'
|
|
505
505
|
xorException: 'val', // see xorGroup :expr
|
|
506
506
|
// see also extra handling for 'element' in extension, see definition()
|
|
507
507
|
},
|
|
@@ -656,14 +656,14 @@ const schema = compileSchema( {
|
|
|
656
656
|
'-expr': { // '-expr' and '-' must not exist top-level
|
|
657
657
|
prop: '@‹anno›',
|
|
658
658
|
type: object,
|
|
659
|
-
optional: [
|
|
660
|
-
'=',
|
|
661
|
-
'
|
|
662
|
-
'cast',
|
|
659
|
+
optional: [ // exprProperties + '=' - 'SELECT' - 'SET'
|
|
660
|
+
'=',
|
|
661
|
+
'ref', 'xpr', 'list', 'val', '#', 'func',
|
|
662
|
+
'param', 'literal', 'args', 'cast',
|
|
663
663
|
],
|
|
664
664
|
schema: {
|
|
665
665
|
'=': {
|
|
666
|
-
type: renameTo( '$tokenTexts',
|
|
666
|
+
type: renameTo( '$tokenTexts', stringOrTrue ),
|
|
667
667
|
xorGroups: null, // reset xorGroup; allow '=' for all :expr
|
|
668
668
|
},
|
|
669
669
|
},
|
|
@@ -678,10 +678,6 @@ const schema = compileSchema( {
|
|
|
678
678
|
type: boolOrNull,
|
|
679
679
|
inKind: [ 'element', '$column' ],
|
|
680
680
|
},
|
|
681
|
-
masked: {
|
|
682
|
-
type: masked,
|
|
683
|
-
inKind: [ 'element' ],
|
|
684
|
-
},
|
|
685
681
|
notNull: {
|
|
686
682
|
type: boolOrNull,
|
|
687
683
|
inKind: [ 'element', 'param', 'type' ], // TODO: $column - or if so: in 'cast'?
|
|
@@ -703,6 +699,7 @@ const schema = compileSchema( {
|
|
|
703
699
|
},
|
|
704
700
|
includes: {
|
|
705
701
|
arrayOf: stringRef,
|
|
702
|
+
type: includeRefs,
|
|
706
703
|
inKind: [ 'entity', 'type', 'aspect', 'event', 'extend' ],
|
|
707
704
|
},
|
|
708
705
|
returns: {
|
|
@@ -768,6 +765,11 @@ const schema = compileSchema( {
|
|
|
768
765
|
$generated: {
|
|
769
766
|
type: string,
|
|
770
767
|
},
|
|
768
|
+
$postponeIncludes: { // elements to be added before applying include
|
|
769
|
+
arrayOf: natnum,
|
|
770
|
+
type: postponeIncludes, // values stored with `includes` references
|
|
771
|
+
inKind: [ 'entity', 'type', 'aspect', 'event' ], // not 'extend' yet
|
|
772
|
+
},
|
|
771
773
|
$: {
|
|
772
774
|
noPrefix: false, // just '$' is no CSN property
|
|
773
775
|
type: ignore,
|
|
@@ -1132,6 +1134,44 @@ function returnsDefinition( def, spec, xsn, csn ) {
|
|
|
1132
1134
|
return definition( def, spec, xsn, csn, '' );
|
|
1133
1135
|
}
|
|
1134
1136
|
|
|
1137
|
+
function includeRefs( def, spec, xsn, csn ) {
|
|
1138
|
+
const array = arrayOf( stringRef )( def, spec, xsn, csn );
|
|
1139
|
+
if (array && Array.isArray( csn.$postponeIncludes )) {
|
|
1140
|
+
array.forEach( ( ref, idx ) => {
|
|
1141
|
+
const postpone = csn.$postponeIncludes[idx];
|
|
1142
|
+
ref.$postponeInclude = (Number.isSafeInteger( postpone ) && postpone >= 0 )
|
|
1143
|
+
? postpone
|
|
1144
|
+
: 0;
|
|
1145
|
+
} );
|
|
1146
|
+
}
|
|
1147
|
+
return array;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function postponeIncludes( array, spec, xsn, csn ) {
|
|
1151
|
+
if (!isArray( array ) || !array.length)
|
|
1152
|
+
return;
|
|
1153
|
+
let noElems = (!csn.elements || typeof csn.elements !== 'object')
|
|
1154
|
+
? 0
|
|
1155
|
+
: Object.values( csn.elements )
|
|
1156
|
+
.reduce( ( acc, elem ) => (elem?.kind === 'extend' ? acc : acc + 1), 0 );
|
|
1157
|
+
let noIncludes = csn.includes?.length || 0;
|
|
1158
|
+
const { prop } = spec;
|
|
1159
|
+
for (const num of array) {
|
|
1160
|
+
++virtualLine;
|
|
1161
|
+
if (--noIncludes === -1) {
|
|
1162
|
+
error( 'syntax-invalid-special', location(true), { '#': 'long', prop } );
|
|
1163
|
+
}
|
|
1164
|
+
else if (noIncludes < -1) {
|
|
1165
|
+
ignore( num ); // no further item check after syntax-invalid-special#long
|
|
1166
|
+
}
|
|
1167
|
+
else if (natnum( num, spec ) && (noElems -= num) < 0) {
|
|
1168
|
+
error( 'syntax-invalid-special', location(true), { '#': 'sum', prop } );
|
|
1169
|
+
noIncludes = -1;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
++virtualLine;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1135
1175
|
// Temporary function as long as the message below is not a hard error
|
|
1136
1176
|
function elementsDict( def, spec, xsn ) {
|
|
1137
1177
|
const elements = dictionaryOf( definition )( def, spec );
|
|
@@ -1319,11 +1359,11 @@ function string( val, spec ) {
|
|
|
1319
1359
|
return ignore( val );
|
|
1320
1360
|
}
|
|
1321
1361
|
|
|
1322
|
-
function
|
|
1323
|
-
if (typeof val === 'string' && val ||
|
|
1324
|
-
return
|
|
1362
|
+
function stringOrTrue( val, spec ) {
|
|
1363
|
+
if (typeof val === 'string' && val || val === true)
|
|
1364
|
+
return true;
|
|
1325
1365
|
error( 'syntax-expecting-string', location(true),
|
|
1326
|
-
{ '#': spec.msgVariant || 'or-
|
|
1366
|
+
{ '#': spec.msgVariant || 'or-true', prop: spec.msgProp } );
|
|
1327
1367
|
return ignore( val );
|
|
1328
1368
|
}
|
|
1329
1369
|
|
|
@@ -1331,8 +1371,7 @@ function stringVal( val, spec ) {
|
|
|
1331
1371
|
if (typeof val === 'string' && val)
|
|
1332
1372
|
// XSN TODO: do not require literal
|
|
1333
1373
|
return { val, literal: 'string', location: location() };
|
|
1334
|
-
error( 'syntax-expecting-string', location(true), { prop: spec.msgProp }
|
|
1335
|
-
'Expecting non-empty string for property $(PROP)' );
|
|
1374
|
+
error( 'syntax-expecting-string', location(true), { prop: spec.msgProp } );
|
|
1336
1375
|
return ignore( val );
|
|
1337
1376
|
}
|
|
1338
1377
|
|
|
@@ -1451,33 +1490,11 @@ function annoValue( val, spec ) {
|
|
|
1451
1490
|
}
|
|
1452
1491
|
return retval;
|
|
1453
1492
|
}
|
|
1454
|
-
else if (
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
if (valKeys.length === 1 && typeof val['='] === 'string') {
|
|
1460
|
-
++virtualLine;
|
|
1461
|
-
const r = refSplit( val['='], '=' ); // i.e. no extra `variant` stuff
|
|
1462
|
-
++virtualLine;
|
|
1463
|
-
return r;
|
|
1464
|
-
}
|
|
1465
|
-
else if (exprProperties.some( prop => val[prop] !== undefined )) {
|
|
1466
|
-
const s = schema['@'].schema['-expr'];
|
|
1467
|
-
const r = { location: location() };
|
|
1468
|
-
Object.assign( r, object( val, s ) );
|
|
1469
|
-
return r;
|
|
1470
|
-
}
|
|
1471
|
-
// fallthrough -> unchecked structure
|
|
1472
|
-
}
|
|
1473
|
-
if (typeof val['#'] === 'string') {
|
|
1474
|
-
if (Object.keys( val ).length === 1) {
|
|
1475
|
-
++virtualLine;
|
|
1476
|
-
const xsn = { location: location() };
|
|
1477
|
-
symbol( val['#'], schema['#'], xsn );
|
|
1478
|
-
++virtualLine;
|
|
1479
|
-
return xsn;
|
|
1480
|
-
}
|
|
1493
|
+
else if (primaryExprProperties.some( prop => val[prop] !== undefined )) {
|
|
1494
|
+
const s = schema['@'].schema['-expr'];
|
|
1495
|
+
const r = object( val, s );
|
|
1496
|
+
r.$tokenTexts = true; // property `=` might not be present
|
|
1497
|
+
return r;
|
|
1481
1498
|
}
|
|
1482
1499
|
else if (val['...'] !== undefined && Object.keys( val ).length === 1) {
|
|
1483
1500
|
// TODO: only if not nested - see error above
|
|
@@ -1495,8 +1512,11 @@ function annoValue( val, spec ) {
|
|
|
1495
1512
|
}
|
|
1496
1513
|
const r = { struct: Object.create(null), literal: 'struct', location: location() };
|
|
1497
1514
|
++virtualLine;
|
|
1515
|
+
// Here, we do not really care whether the anno value is an unchecked reference
|
|
1516
|
+
// or a structure with a `=` property → just check the `=` value to be a string
|
|
1498
1517
|
for (const name of Object.keys( val )) {
|
|
1499
|
-
|
|
1518
|
+
if (name !== '=' || string( val[name], schema['='] ))
|
|
1519
|
+
r.struct[name] = annotation( val[name], schema['@'], null, val, name );
|
|
1500
1520
|
++virtualLine;
|
|
1501
1521
|
}
|
|
1502
1522
|
return r;
|
|
@@ -1770,11 +1790,6 @@ function duplicateExcluding( name, loc ) {
|
|
|
1770
1790
|
error( 'syntax-duplicate-excluding', loc, { '#': 'csn', name, prop: 'excluding[]' } );
|
|
1771
1791
|
}
|
|
1772
1792
|
|
|
1773
|
-
function masked( val, spec ) {
|
|
1774
|
-
message( 'syntax-unsupported-masked', location(), { '#': 'csn', prop: 'masked' } );
|
|
1775
|
-
return boolOrNull( val, spec );
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
1793
|
function setOp( val, spec ) { // UNION, ...
|
|
1779
1794
|
// similar to string(), but without literal
|
|
1780
1795
|
return string( val, spec ) && { val, location: location() };
|
package/lib/json/to-csn.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const { locationString } = require('../base/messages');
|
|
15
15
|
const { isBetaEnabled } = require('../base/specialOptions');
|
|
16
|
-
const { pathName } = require('../compiler/utils');
|
|
16
|
+
const { pathName, hasSpecialPathItemProp } = require('../compiler/utils');
|
|
17
17
|
const { CompilerAssertion } = require('../base/error');
|
|
18
18
|
|
|
19
19
|
const compilerVersion = require('../../package.json').version;
|
|
@@ -60,7 +60,6 @@ const transformers = {
|
|
|
60
60
|
virtual: value,
|
|
61
61
|
key: value,
|
|
62
62
|
unique: value,
|
|
63
|
-
masked: value,
|
|
64
63
|
params,
|
|
65
64
|
// early expression / query properties -------------------------------------
|
|
66
65
|
op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
|
|
@@ -96,7 +95,7 @@ const transformers = {
|
|
|
96
95
|
foreignKeys,
|
|
97
96
|
enum: enumDict,
|
|
98
97
|
items,
|
|
99
|
-
includes
|
|
98
|
+
includes,
|
|
100
99
|
// late expressions / query properties -------------------------------------
|
|
101
100
|
mixin: insertOrderDict, // only in queries with special handling
|
|
102
101
|
columns,
|
|
@@ -275,7 +274,7 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
275
274
|
(options.parseCdl ? 'parsed' : 'inferred');
|
|
276
275
|
if (!options.testMode) {
|
|
277
276
|
csn.meta = Object.assign( {}, model.meta, meta,
|
|
278
|
-
{ creator,
|
|
277
|
+
{ creator, compilerCsnFlavor } );
|
|
279
278
|
csn.$version = csnVersion;
|
|
280
279
|
}
|
|
281
280
|
else if (meta !== undefined) {
|
|
@@ -723,7 +722,7 @@ function definition( art, csn, _node, prop ) {
|
|
|
723
722
|
}
|
|
724
723
|
|
|
725
724
|
const c = standard( art );
|
|
726
|
-
// The XSN of actions in extensions do not contain a returns yet - TODO?
|
|
725
|
+
// The XSN of actions in extensions do not contain a returns yet - TODO: still?
|
|
727
726
|
const elems = c.elements;
|
|
728
727
|
if (elems && (prop === 'actions' || art.$syntax === 'returns')) {
|
|
729
728
|
delete c.elements;
|
|
@@ -752,12 +751,12 @@ function queryOrigin( xsn ) {
|
|
|
752
751
|
/**
|
|
753
752
|
* Create $origin specification for `includes` of `art`.
|
|
754
753
|
*/
|
|
755
|
-
function includesOrigin(
|
|
756
|
-
const $origin = originRef(
|
|
757
|
-
if (
|
|
754
|
+
function includesOrigin( refs, art ) {
|
|
755
|
+
const $origin = originRef( refs[0]._artifact );
|
|
756
|
+
if (refs.length === 1)
|
|
758
757
|
return $origin;
|
|
759
758
|
const result = { $origin };
|
|
760
|
-
for (const incl of
|
|
759
|
+
for (const incl of refs.slice(1)) {
|
|
761
760
|
const aspect = incl._artifact;
|
|
762
761
|
for (const prop in aspect) {
|
|
763
762
|
if ((prop.charAt(0) === '@' || prop === 'doc') &&
|
|
@@ -1104,18 +1103,14 @@ function artifactRef( node, terse ) {
|
|
|
1104
1103
|
: ref[0];
|
|
1105
1104
|
}
|
|
1106
1105
|
|
|
1106
|
+
function includes( refs, csn ) {
|
|
1107
|
+
csn.includes = refs.map( r => artifactRef( r, true ) );
|
|
1108
|
+
if (gensrcFlavor && refs.some( r => r.$postponeInclude ))
|
|
1109
|
+
csn.$postponeIncludes = refs.map( r => r.$postponeInclude ?? 0 );
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1107
1112
|
function pathItem( item ) {
|
|
1108
|
-
|
|
1109
|
-
!item.where &&
|
|
1110
|
-
!item.groupBy &&
|
|
1111
|
-
!item.having &&
|
|
1112
|
-
!item.limit &&
|
|
1113
|
-
!item.orderBy &&
|
|
1114
|
-
!item.cardinality &&
|
|
1115
|
-
!item.$extra &&
|
|
1116
|
-
!item.$syntax)
|
|
1117
|
-
return item.id;
|
|
1118
|
-
return standard( item );
|
|
1113
|
+
return (hasSpecialPathItemProp( item )) ? standard( item ) : item.id;
|
|
1119
1114
|
}
|
|
1120
1115
|
|
|
1121
1116
|
function args( node ) {
|
|
@@ -1140,9 +1135,7 @@ function anno( node ) {
|
|
|
1140
1135
|
}
|
|
1141
1136
|
if (node.$inferred && gensrcFlavor)
|
|
1142
1137
|
return undefined;
|
|
1143
|
-
|
|
1144
|
-
return Object.assign({ '=': node.$tokenTexts }, expression( node ));
|
|
1145
|
-
return value(node);
|
|
1138
|
+
return annoValue( node );
|
|
1146
1139
|
}
|
|
1147
1140
|
|
|
1148
1141
|
function docComment( doc ) {
|
|
@@ -1152,6 +1145,16 @@ function docComment( doc ) {
|
|
|
1152
1145
|
return undefined;
|
|
1153
1146
|
}
|
|
1154
1147
|
|
|
1148
|
+
function annoValue( node ) {
|
|
1149
|
+
if (!node.$tokenTexts)
|
|
1150
|
+
return value( node );
|
|
1151
|
+
const csn = expression( node ); // expressions in annotation values
|
|
1152
|
+
return (!csn?.ref || gensrcFlavor || universalCsn ||
|
|
1153
|
+
csn.cast || csn.param || csn.ref.some( id => typeof id !== 'string' ))
|
|
1154
|
+
? csn
|
|
1155
|
+
: Object.assign({ '=': csn.ref.join( '.' ) }, csn );
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1155
1158
|
function value( node ) {
|
|
1156
1159
|
// "Short" value form, e.g. for annotation assignments
|
|
1157
1160
|
if (!node)
|
|
@@ -1175,7 +1178,7 @@ function value( node ) {
|
|
|
1175
1178
|
if (node.literal === 'enum')
|
|
1176
1179
|
return enumValue( node );
|
|
1177
1180
|
if (node.literal === 'array')
|
|
1178
|
-
return node.val.map(
|
|
1181
|
+
return node.val.map( annoValue );
|
|
1179
1182
|
if (node.literal === 'token' && node.val === '...')
|
|
1180
1183
|
return extra( { '...': !node.upTo || value( node.upTo ) } );
|
|
1181
1184
|
if (node.literal !== 'struct')
|
|
@@ -1183,7 +1186,7 @@ function value( node ) {
|
|
|
1183
1186
|
return node.name && !('val' in node) || node.val;
|
|
1184
1187
|
const r = Object.create( dictionaryPrototype );
|
|
1185
1188
|
for (const prop in node.struct)
|
|
1186
|
-
r[prop] =
|
|
1189
|
+
r[prop] = annoValue( node.struct[prop] );
|
|
1187
1190
|
return r;
|
|
1188
1191
|
}
|
|
1189
1192
|
|
|
@@ -38,18 +38,6 @@ function isWhitespaceCharacterNoNewline( char ) {
|
|
|
38
38
|
return whitespaceRegEx.test(char);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Normalized CDL newlines to LF (\n). CDL newlines also contain PS and other
|
|
43
|
-
* characters, see cdlNewLineRegEx.
|
|
44
|
-
*
|
|
45
|
-
* @param {string} str
|
|
46
|
-
* @return {string}
|
|
47
|
-
*/
|
|
48
|
-
function normalizeNewLine( str ) {
|
|
49
|
-
// Note: cdlNewLineRegEx does not have `g`.
|
|
50
|
-
return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
41
|
/**
|
|
54
42
|
* Normalizes the given number (as a string):
|
|
55
43
|
*
|
|
@@ -69,6 +57,5 @@ module.exports = {
|
|
|
69
57
|
isWhitespaceOrNewLineOnly,
|
|
70
58
|
isWhitespaceCharacterNoNewline,
|
|
71
59
|
cdlNewLineRegEx,
|
|
72
|
-
normalizeNewLine,
|
|
73
60
|
normalizeNumberString,
|
|
74
61
|
};
|
package/lib/main.d.ts
CHANGED
|
@@ -32,6 +32,18 @@ declare namespace compiler {
|
|
|
32
32
|
* during compilation otherwise.
|
|
33
33
|
*/
|
|
34
34
|
severities?: { [messageId: string]: MessageSeverity}
|
|
35
|
+
/**
|
|
36
|
+
* Downgrade the errors raised for `annotate` statements containing a
|
|
37
|
+
* security-relevant annotation (`@restrict`, `@requires`, `@ams`) but
|
|
38
|
+
* whose target does not exist. Intended as a long-lived migration
|
|
39
|
+
* switch for projects that cannot fix all such statements at once.
|
|
40
|
+
*
|
|
41
|
+
* Explicit per-id entries in `severities` still take precedence over
|
|
42
|
+
* this option.
|
|
43
|
+
*
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
noErrorForUnknownAnnotateTarget?: boolean
|
|
35
47
|
/**
|
|
36
48
|
* Dictionary of beta flag names. This option allows fine-grained control
|
|
37
49
|
* over which beta features should be enabled.
|
|
@@ -72,20 +84,6 @@ declare namespace compiler {
|
|
|
72
84
|
* @default 'client'
|
|
73
85
|
*/
|
|
74
86
|
csnFlavor?: string | 'client' | 'gensrc' | 'universal'
|
|
75
|
-
/**
|
|
76
|
-
* If set to false, backends will create localized convenience views for those views,
|
|
77
|
-
* that only have an association to a localized entity/view. If set to true, views will
|
|
78
|
-
* only get a convenience view, if they themselves contain localized elements (i.e. either
|
|
79
|
-
* have simple projection on localized elements and CDL-casts to a localized element).
|
|
80
|
-
*
|
|
81
|
-
* If true, the OData backend will not set `$localized: true` markers for such cases.
|
|
82
|
-
*
|
|
83
|
-
* Does not work for backends to.hdi(), to.hdbcds() or to.sql() with `sqlDialect: 'hana'`,
|
|
84
|
-
* since in all those dialects, associations still exist in generated artifacts.
|
|
85
|
-
*
|
|
86
|
-
* @default true
|
|
87
|
-
*/
|
|
88
|
-
fewerLocalizedViews?: boolean
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
/**
|
|
@@ -464,6 +462,26 @@ declare namespace compiler {
|
|
|
464
462
|
* @default true (since v5)
|
|
465
463
|
*/
|
|
466
464
|
betterSqliteSessionVariables?: boolean
|
|
465
|
+
/**
|
|
466
|
+
* @deprecated Use decimal_affinity instead. Will be removed in v7.
|
|
467
|
+
* Controls SQLite type affinity for decimal types (old boolean version).
|
|
468
|
+
* - true: Use REAL_DECIMAL types (real affinity)
|
|
469
|
+
* - false: Use DECIMAL types (numeric affinity)
|
|
470
|
+
*
|
|
471
|
+
* @since Before v3
|
|
472
|
+
*/
|
|
473
|
+
sqliteRealAffinityForDecimal?: boolean
|
|
474
|
+
/**
|
|
475
|
+
* Controls SQLite type affinity for decimal types.
|
|
476
|
+
* - 'numeric': Use DECIMAL types (numeric affinity)
|
|
477
|
+
* - 'real': Use REAL_DECIMAL types (real affinity)
|
|
478
|
+
*
|
|
479
|
+
* Only affects SQLite dialect. Values are case-sensitive (must be lowercase).
|
|
480
|
+
*
|
|
481
|
+
* @default 'real'
|
|
482
|
+
* @since v6.9.0
|
|
483
|
+
*/
|
|
484
|
+
decimal_affinity?: 'numeric' | 'real'
|
|
467
485
|
/**
|
|
468
486
|
* If set, operator `!=` will be treated as a boolean-logic operator,
|
|
469
487
|
* instead of a three-valued operator, by rendering it as `IS DISTINCT FROM`
|
|
@@ -503,38 +521,6 @@ declare namespace compiler {
|
|
|
503
521
|
dollarNowAsTimestamp?: boolean
|
|
504
522
|
}
|
|
505
523
|
|
|
506
|
-
/**
|
|
507
|
-
* Options used by to.hdbcds()
|
|
508
|
-
*/
|
|
509
|
-
export interface HdbcdsOptions extends Options {
|
|
510
|
-
/**
|
|
511
|
-
* The SQL naming mode decides how names are represented.
|
|
512
|
-
* Among others, this includes whether identifiers are quoted or not.
|
|
513
|
-
*
|
|
514
|
-
* - `plain`:
|
|
515
|
-
* In this naming mode, dots are replaced by underscores.
|
|
516
|
-
* Identifiers are always uppercased. If "smart-quoting" is used, they are also quoted
|
|
517
|
-
if they are also HDBCDS keywords.
|
|
518
|
-
* - `quoted`:
|
|
519
|
-
* In this mode, all identifiers are quoted. Dots are not replaced in table
|
|
520
|
-
* and view names but are still replaced by underscores in element names.
|
|
521
|
-
* Identifier casing is kept as specified in the source.
|
|
522
|
-
* - `hdbcds`:
|
|
523
|
-
* This mode uses names that are compatible to SAP HANA CDS.
|
|
524
|
-
* In this mode, all identifiers are quoted. Dots are neither replaced in table
|
|
525
|
-
* nor element names: Structured elements persist, contexts are nested.
|
|
526
|
-
* Managed associations/compositions are left as-is. No association-to-join-translation
|
|
527
|
-
* is done.
|
|
528
|
-
*
|
|
529
|
-
* @default 'plain'
|
|
530
|
-
*/
|
|
531
|
-
sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
|
|
532
|
-
/**
|
|
533
|
-
* For to.hdbcds(), the SQL dialect is always set to 'hana'.
|
|
534
|
-
*/
|
|
535
|
-
sqlDialect?: 'hana'
|
|
536
|
-
}
|
|
537
|
-
|
|
538
524
|
/**
|
|
539
525
|
* Options used by to.hdi() and to.hdi.migration()
|
|
540
526
|
*/
|
|
@@ -1306,21 +1292,10 @@ declare namespace compiler {
|
|
|
1306
1292
|
}
|
|
1307
1293
|
|
|
1308
1294
|
/**
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
* DEPRECATED! This backend is deprecated and will be removed with cds-compiler v7!
|
|
1312
|
-
*
|
|
1313
|
-
* @deprecated
|
|
1314
|
-
* @returns A map of `{ '<artifactName>.hdbcds|hdbconstraint>': '<content>' }` entries.
|
|
1295
|
+
* @deprecated to.hdbcds() has been removed. Use to.hdi() instead.
|
|
1315
1296
|
*/
|
|
1316
|
-
function hdbcds(csn: CSN, options?:
|
|
1297
|
+
function hdbcds(csn: CSN, options?: Options): Record<string, string>;
|
|
1317
1298
|
namespace hdbcds {
|
|
1318
|
-
/**
|
|
1319
|
-
* Immutable list of SAP HANA CDS keywords, used for smart quoting in
|
|
1320
|
-
* {@link to.hdbcds} with option `sqlMapping: 'plain'`.
|
|
1321
|
-
*
|
|
1322
|
-
* @deprecated
|
|
1323
|
-
*/
|
|
1324
1299
|
const keywords: string[];
|
|
1325
1300
|
}
|
|
1326
1301
|
|
package/lib/main.js
CHANGED
|
@@ -173,7 +173,7 @@ module.exports = {
|
|
|
173
173
|
keywords: Object.freeze([ ...keywords.hana ] ),
|
|
174
174
|
}),
|
|
175
175
|
hdbcds: Object.assign((...args) => snapi.hdbcds(...args), {
|
|
176
|
-
keywords: Object.freeze([ ...keywords.hdbcds ]
|
|
176
|
+
keywords: Object.freeze([ ...keywords.hdbcds ]),
|
|
177
177
|
}),
|
|
178
178
|
edm: Object.assign((...args) => snapi.edm(...args), {
|
|
179
179
|
all: (...args) => snapi.edm.all(...args),
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
applyTransformationsOnDictionary,
|
|
10
10
|
mergeTransformers,
|
|
11
11
|
} = require('../transform/db/applyTransformations');
|
|
12
|
-
const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
|
|
12
|
+
const { isBuiltinType, isAnnotationExpression, primaryExprProperties } = require('../base/builtins');
|
|
13
13
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
14
14
|
const { typeParameters } = require('../compiler/builtins');
|
|
15
15
|
const { forEach } = require('../utils/objectUtils');
|
|
@@ -1474,13 +1474,14 @@ function pathName( ref ) {
|
|
|
1474
1474
|
*/
|
|
1475
1475
|
function findAnnotationExpression( node, prop ) {
|
|
1476
1476
|
let isExpr = false;
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1477
|
+
const updateIsExpr = (p) => {
|
|
1478
|
+
isExpr ||= isAnnotationExpression(p);
|
|
1479
|
+
};
|
|
1480
|
+
const xprTransformers = Object.fromEntries(primaryExprProperties.map( p => [ p, updateIsExpr ]));
|
|
1481
|
+
|
|
1482
|
+
if (prop[0] === '@')
|
|
1483
|
+
transformExpression(node, prop, xprTransformers);
|
|
1484
|
+
|
|
1484
1485
|
return isExpr;
|
|
1485
1486
|
}
|
|
1486
1487
|
|