@sap/cds-compiler 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +19 -0
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +24 -2
- package/doc/CHANGELOG_DEPRECATED.md +21 -1
- package/lib/api/main.js +7 -7
- package/lib/api/options.js +2 -3
- package/lib/base/message-registry.js +17 -5
- package/lib/base/messages.js +18 -39
- package/lib/base/model.js +2 -0
- package/lib/checks/actionsFunctions.js +8 -7
- package/lib/checks/selectItems.js +96 -14
- package/lib/checks/types.js +5 -8
- package/lib/checks/validator.js +1 -2
- package/lib/compiler/assert-consistency.js +64 -12
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +58 -8
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +25 -22
- package/lib/compiler/extend.js +16 -10
- package/lib/compiler/finalize-parse-cdl.js +5 -9
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +34 -31
- package/lib/compiler/propagator.js +11 -6
- package/lib/compiler/resolve.js +14 -15
- package/lib/compiler/shared.js +53 -26
- package/lib/compiler/tweak-assocs.js +5 -11
- package/lib/compiler/utils.js +13 -4
- package/lib/edm/annotations/preprocessAnnotations.js +8 -4
- package/lib/edm/csn2edm.js +3 -3
- package/lib/edm/edm.js +9 -1
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +295 -638
- package/lib/edm/edmUtils.js +85 -5
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -2
- package/lib/gen/languageLexer.js +3 -0
- package/lib/gen/languageParser.js +4344 -4530
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +8 -6
- package/lib/language/genericAntlrParser.js +121 -63
- package/lib/language/language.g4 +19 -57
- package/lib/main.d.ts +1 -0
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +55 -29
- package/lib/model/csnUtils.js +11 -7
- package/lib/model/revealInternalProperties.js +2 -3
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +27 -0
- package/lib/render/toCdl.js +57 -32
- package/lib/render/toSql.js +24 -8
- package/lib/render/utils/common.js +3 -4
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +0 -1
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/forHanaNew.js +11 -2
- package/lib/transform/forOdataNew.js +1 -1
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/utils/moduleResolve.js +0 -1
- package/package.json +2 -2
- package/lib/checks/unknownMagic.js +0 -41
package/lib/language/language.g4
CHANGED
|
@@ -385,10 +385,9 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
385
385
|
}
|
|
386
386
|
// #ATN: EXTEND elem, while CONTEXT, ENTITY etc are not reserved
|
|
387
387
|
( extendContext[ $art, $outer ]
|
|
388
|
-
| extendEntity[ $art, $outer ]
|
|
388
|
+
| extendEntity[ $art, $outer ] // or aspect
|
|
389
389
|
| extendProjection[ $art, $outer ]
|
|
390
390
|
| extendType[ $art, $outer ]
|
|
391
|
-
| extendAspect[ $art, $outer ]
|
|
392
391
|
// Streamlined Syntax
|
|
393
392
|
| extendArtifact[ $art, $outer ]
|
|
394
393
|
)
|
|
@@ -544,11 +543,12 @@ projectionExclusion[ outer ] locals[ art = {} ]
|
|
|
544
543
|
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
545
544
|
;
|
|
546
545
|
|
|
546
|
+
// also used for aspect
|
|
547
547
|
extendEntity[ art, outer ] locals[ name = {} ]
|
|
548
548
|
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
549
549
|
:
|
|
550
|
-
ENTITY simplePath[ $name, 'Extend' ]
|
|
551
|
-
{ $art.expectedKind =
|
|
550
|
+
kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
|
|
551
|
+
{ $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
|
|
552
552
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
553
553
|
}
|
|
554
554
|
(
|
|
@@ -556,7 +556,7 @@ extendEntity[ art, outer ] locals[ name = {} ]
|
|
|
556
556
|
annotationAssignment_ll1[ $art ]*
|
|
557
557
|
// ATN: the ref can start with ACTIONS
|
|
558
558
|
(
|
|
559
|
-
includeRef[ $art ]
|
|
559
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
560
560
|
requiredSemi
|
|
561
561
|
|
|
|
562
562
|
extendForEntity[ $art ]
|
|
@@ -754,17 +754,6 @@ extendType[ art, outer ] locals[ name = {} ]
|
|
|
754
754
|
extendWithOptElements[ $art, $art ]
|
|
755
755
|
;
|
|
756
756
|
|
|
757
|
-
extendAspect[ art, outer ] locals[ name = {} ]
|
|
758
|
-
@after { this.attachLocation( $art ); }
|
|
759
|
-
:
|
|
760
|
-
// aspects are types, i.e. kind is 'type' for aspects
|
|
761
|
-
ASPECT simplePath[ $name, 'Extend' ]
|
|
762
|
-
{ $art.expectedKind = 'aspect'; $art.name = $name;
|
|
763
|
-
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
764
|
-
}
|
|
765
|
-
extendWithOptElements[ $art, $art ]
|
|
766
|
-
;
|
|
767
|
-
|
|
768
757
|
annotationDef[ art, outer ] locals[ name = {} ]
|
|
769
758
|
@after { this.attachLocation( $art ); }
|
|
770
759
|
:
|
|
@@ -805,7 +794,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
805
794
|
// #ATN: DEFINITIONS, COLUMNS, ACTIONS etc are not reserved and could be identifiers (ref).
|
|
806
795
|
// TODO: exclude "expected" according to disallowElementExtension()
|
|
807
796
|
(
|
|
808
|
-
includeRef[ $art ]
|
|
797
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
809
798
|
requiredSemi
|
|
810
799
|
|
|
|
811
800
|
'{' { $art.elements = this.createDict(); }
|
|
@@ -860,7 +849,7 @@ extendWithOptElements[ art ]
|
|
|
860
849
|
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
861
850
|
annotationAssignment_ll1[ $art ]*
|
|
862
851
|
(
|
|
863
|
-
includeRef[ $art ]
|
|
852
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
864
853
|
requiredSemi
|
|
865
854
|
|
|
|
866
855
|
'{' { $art.elements = this.createDict(); }
|
|
@@ -1558,46 +1547,14 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1558
1547
|
|
|
|
1559
1548
|
// alt lookahead includes MANY '{'
|
|
1560
1549
|
{ $art.type = {}; }
|
|
1550
|
+
// Can't use typeRefOptArgs because of clash with include rule below (ATN would change)
|
|
1561
1551
|
simplePath[ $art.type, 'artref' ]
|
|
1562
1552
|
(
|
|
1563
|
-
typeRefArgs[ $art ]
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
enumSymbolDef[ $art ]*
|
|
1569
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1570
|
-
(
|
|
1571
|
-
optionalSemi
|
|
1572
|
-
|
|
|
1573
|
-
defaultValue[ $art ]
|
|
1574
|
-
requiredSemi
|
|
1575
|
-
)
|
|
1576
|
-
|
|
|
1577
|
-
defaultValue[ $art ]?
|
|
1578
|
-
requiredSemi
|
|
1579
|
-
)
|
|
1580
|
-
|
|
|
1581
|
-
':' // with element, e.g. `type T : E:elem enum { ... }`
|
|
1582
|
-
{ $art.type.scope = $art.type.path.length; }
|
|
1583
|
-
simplePath[ $art.type, 'ref']
|
|
1584
|
-
{ this.docComment( $art ); }
|
|
1585
|
-
annotationAssignment_ll1[ $art ]*
|
|
1586
|
-
(
|
|
1587
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1588
|
-
enumSymbolDef[ $art ]*
|
|
1589
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1590
|
-
(
|
|
1591
|
-
optionalSemi
|
|
1592
|
-
|
|
|
1593
|
-
defaultValue[ $art ]
|
|
1594
|
-
requiredSemi
|
|
1595
|
-
)
|
|
1596
|
-
|
|
|
1597
|
-
defaultValue[ $art ]?
|
|
1598
|
-
requiredSemi
|
|
1599
|
-
)
|
|
1600
|
-
|
|
|
1553
|
+
( typeRefArgs[ $art ]
|
|
1554
|
+
| ':' // with element, e.g. `type T : E:elem enum { ... }`
|
|
1555
|
+
{ $art.type.scope = $art.type.path.length; }
|
|
1556
|
+
simplePath[ $art.type, 'ref']
|
|
1557
|
+
)?
|
|
1601
1558
|
{ this.docComment( $art ); }
|
|
1602
1559
|
annotationAssignment_ll1[ $art ]*
|
|
1603
1560
|
(
|
|
@@ -2481,7 +2438,12 @@ pathArguments[ pathStep, considerSpecial ]
|
|
|
2481
2438
|
'(' // dict or array, see below
|
|
2482
2439
|
// Make sure that we do not introduce A:B paths in expressions!
|
|
2483
2440
|
// Need to avoid adaptPredict(), otherwise Generic keywords won't work in funcExpression
|
|
2484
|
-
|
|
2441
|
+
//
|
|
2442
|
+
// For code completion, we need to handle generic tokens directly after the
|
|
2443
|
+
// '('. To avoid invalidating an assoc `trim` to an entity with parameter
|
|
2444
|
+
// `leading` (ok, a bit constructed), we do not do it with named parameters.
|
|
2445
|
+
{ if (!this.setLocalTokenForId( { ':': 'HelperToken1', '=>': 'HelperToken2' } ))
|
|
2446
|
+
this.prepareGenericKeywords( $considerSpecial ); }
|
|
2485
2447
|
(
|
|
2486
2448
|
{ $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; }
|
|
2487
2449
|
id=HelperToken1 ':'
|
package/lib/main.d.ts
CHANGED
package/lib/model/api.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Each function in `userFunctions` and `defaultFunctions` is called with:
|
|
12
12
|
* - `userFunctions`
|
|
13
13
|
* - the current CSN node, i.e. ‹parent node›.‹property name›
|
|
14
|
-
* - the
|
|
14
|
+
* - the ‹parent node›
|
|
15
15
|
* - the ‹property name› (might be useful if the same function is used for several props)
|
|
16
16
|
*/
|
|
17
17
|
const defaultFunctions = {
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -210,8 +210,11 @@ const referenceSemantics = {
|
|
|
210
210
|
ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
|
|
211
211
|
on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
|
|
212
212
|
// there are also 'on_join' and 'on_mixin' with default semantics
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
orderBy_ref: { lexical: query => query, dynamic: 'query' },
|
|
214
|
+
orderBy_expr: { lexical: query => query, dynamic: 'source' }, // ref in ORDER BY expression
|
|
215
|
+
orderBy_set_ref: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
|
|
216
|
+
// refs in ORDER BY expr in UNION not really allowed - only with table alias (of outer queries) or $self
|
|
217
|
+
orderBy_set_expr: { lexical: query => query.$next, dynamic: false },
|
|
215
218
|
// default: { lexical: query => query, dynamic: 'source' }
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -551,37 +554,39 @@ function csnRefs( csn, universalReady ) {
|
|
|
551
554
|
}
|
|
552
555
|
}
|
|
553
556
|
// now the dynamic environment: ------------------------------------------
|
|
554
|
-
if (semantics.dynamic
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
557
|
+
if (semantics.dynamic !== false) {
|
|
558
|
+
if (semantics.dynamic === 'target') { // ref in keys
|
|
559
|
+
const target = assocTarget( parent, refCtx );
|
|
560
|
+
return resolvePath( path, target.elements[head], target, 'target' );
|
|
561
|
+
}
|
|
562
|
+
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
563
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
564
|
+
if (!query) { // outside queries - TODO: items?
|
|
565
|
+
let art = parent.elements[head];
|
|
566
|
+
// Ref to up_ in anonymous aspect
|
|
567
|
+
if (!art && head === 'up_') {
|
|
568
|
+
const up = getCache( parent, '_parent' );
|
|
569
|
+
const target = up && typeof up.target === 'string' && csn.definitions[up.target];
|
|
570
|
+
if (target && target.elements) {
|
|
571
|
+
initDefinition( target );
|
|
572
|
+
art = target.elements.up_;
|
|
573
|
+
}
|
|
569
574
|
}
|
|
575
|
+
return resolvePath( path, art, parent, 'parent' );
|
|
570
576
|
}
|
|
571
|
-
return resolvePath( path, art, parent, 'parent' );
|
|
572
|
-
}
|
|
573
577
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
578
|
+
if (semantics.dynamic === 'query')
|
|
579
|
+
// TODO: for ON condition in expand, would need to use cached _element
|
|
580
|
+
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
581
|
+
for (const name in qcache.$aliases) {
|
|
582
|
+
const alias = qcache.$aliases[name];
|
|
583
|
+
const found = alias.elements[head];
|
|
584
|
+
if (found)
|
|
585
|
+
return resolvePath( path, found, alias._ref, 'source', name )
|
|
586
|
+
}
|
|
582
587
|
}
|
|
583
588
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
584
|
-
throw new ModelError ( `Path item
|
|
589
|
+
throw new ModelError ( `Path item 0=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
585
590
|
}
|
|
586
591
|
|
|
587
592
|
/**
|
|
@@ -933,6 +938,15 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
933
938
|
parent = art;
|
|
934
939
|
art = obj[prop];
|
|
935
940
|
}
|
|
941
|
+
else if (refCtx === 'orderBy') {
|
|
942
|
+
const isSelect = isSelectQuery( query );
|
|
943
|
+
// use _query_ elements with direct refs (consider sub-optimal CSN,
|
|
944
|
+
// representation of the CAST function), otherwise source elements:
|
|
945
|
+
if (obj[prop].ref && !obj[prop].cast)
|
|
946
|
+
refCtx = (isSelect ? 'orderBy_ref' : 'orderBy_set_ref');
|
|
947
|
+
else
|
|
948
|
+
refCtx = (isSelect ? 'orderBy_expr' : 'orderBy_set_expr');
|
|
949
|
+
}
|
|
936
950
|
isName = false;
|
|
937
951
|
}
|
|
938
952
|
else if (artifactProperties.includes( String(prop) )) {
|
|
@@ -983,7 +997,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
983
997
|
refCtx = prop;
|
|
984
998
|
}
|
|
985
999
|
else if (prop === 'orderBy') {
|
|
986
|
-
refCtx =
|
|
1000
|
+
refCtx = 'orderBy';
|
|
987
1001
|
}
|
|
988
1002
|
else if (prop !== 'xpr') {
|
|
989
1003
|
refCtx = prop;
|
|
@@ -996,6 +1010,18 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
996
1010
|
return resolve( obj, refCtx, main, query, parent, baseEnv );
|
|
997
1011
|
}
|
|
998
1012
|
|
|
1013
|
+
// A SELECT which is (unnecessarily) put into parentheses, the CSN
|
|
1014
|
+
// representation uses SET without `op` and args of length 1:
|
|
1015
|
+
function isSelectQuery( query ) {
|
|
1016
|
+
while (query.SET) {
|
|
1017
|
+
const { args } = query.SET;
|
|
1018
|
+
if (args.length !== 1)
|
|
1019
|
+
return false;
|
|
1020
|
+
query = args[0];
|
|
1021
|
+
}
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
999
1025
|
module.exports = {
|
|
1000
1026
|
csnRefs,
|
|
1001
1027
|
traverseQuery,
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -492,15 +492,18 @@ function getUtils(model, universalReady) {
|
|
|
492
492
|
|
|
493
493
|
/**
|
|
494
494
|
* Resolve to the final type of a type, that means follow type chains, references, etc.
|
|
495
|
-
* Input is a type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
495
|
+
* Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
496
496
|
*
|
|
497
497
|
* Returns `null` if the type can't be resolved or if the referenced element has no type,
|
|
498
498
|
* e.g. `typeof V:calculated`.
|
|
499
499
|
* Otherwise, if scalar, returns an object that has a `type` property and all collected type
|
|
500
500
|
* properties, or the type object with `elements` or `items` property if structured/arrayed.
|
|
501
501
|
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
502
|
+
* Notes:
|
|
503
|
+
* - Caches type lookups. If the CSN changes drastically, you will need to re-call
|
|
504
|
+
* `csnUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
|
|
505
|
+
* - Does _not_ return the underlying type definition! It is an object with all relevant
|
|
506
|
+
* type properties collected while traversing the type chain!
|
|
504
507
|
*
|
|
505
508
|
* @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
|
|
506
509
|
* @returns {object|null}
|
|
@@ -517,7 +520,7 @@ function getUtils(model, universalReady) {
|
|
|
517
520
|
|
|
518
521
|
// We differentiate between ref and type to avoid collisions due to dict key.
|
|
519
522
|
// Delimiter chosen arbitrarily; just one that is rarely used.
|
|
520
|
-
const resolvedKey = (typeof type === 'object') ? `ref:${type.ref.join('\\')}` : `type:${type}`;
|
|
523
|
+
const resolvedKey = (typeof type === 'object') ? `ref[${type.ref.length}]:${type.ref.join('\\')}` : `type:${type}`;
|
|
521
524
|
|
|
522
525
|
if (finalBaseTypeCache[resolvedKey]) {
|
|
523
526
|
if (finalBaseTypeCache[resolvedKey] === true)
|
|
@@ -786,10 +789,12 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
786
789
|
executeCallbacks( dictObj, name );
|
|
787
790
|
}
|
|
788
791
|
function executeCallbacks(o, name ) {
|
|
792
|
+
const p = iterateOptions.pathWithoutProp ? [ name ] : [ prop, name ];
|
|
793
|
+
|
|
789
794
|
if (Array.isArray(callback))
|
|
790
|
-
callback.forEach(cb => cb( o, name, prop, path.concat(
|
|
795
|
+
callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
|
|
791
796
|
else
|
|
792
|
-
callback( o, name, prop, path.concat(
|
|
797
|
+
callback( o, name, prop, path.concat(p), construct )
|
|
793
798
|
}
|
|
794
799
|
}
|
|
795
800
|
|
|
@@ -1286,7 +1291,6 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
|
1286
1291
|
* override: Whether to ignore existing annotations.
|
|
1287
1292
|
* filter: Positive filter. If it returns true, annotations for the referenced artifact
|
|
1288
1293
|
* will be applied.
|
|
1289
|
-
* @todo Improve to also apply element annotations
|
|
1290
1294
|
*/
|
|
1291
1295
|
function applyAnnotationsFromExtensions(csn, config) {
|
|
1292
1296
|
if (!csn.extensions)
|
|
@@ -58,7 +58,6 @@ function tableAliasAsLink( art, parent, name ) {
|
|
|
58
58
|
*
|
|
59
59
|
* @param {XSN.Model} model
|
|
60
60
|
* @param {string} [nameOrPath]
|
|
61
|
-
* @returns {string}
|
|
62
61
|
*/
|
|
63
62
|
function revealInternalProperties( model, nameOrPath ) {
|
|
64
63
|
const transformers = {
|
|
@@ -299,8 +298,8 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
299
298
|
function array( node, fn ) {
|
|
300
299
|
const r = node.map( n => fn( n, node ) );
|
|
301
300
|
if (node[$location])
|
|
302
|
-
r.push( { $location: locationString( node[$location] ) } );
|
|
303
|
-
return r;
|
|
301
|
+
r.push( { '[$location]': locationString( node[$location] ) } );
|
|
302
|
+
return (node.$prefix) ? [ { $prefix: node.$prefix }, ...node ] : r;
|
|
304
303
|
}
|
|
305
304
|
|
|
306
305
|
function artifactIdentifier( node, parent ) {
|
|
@@ -169,6 +169,9 @@ function getElementComparator(otherArtifact, addedElementsDict = null, changedEl
|
|
|
169
169
|
}
|
|
170
170
|
if (relevantTypeChange(element.type, otherElement.type) || typeParametersChanged(element, otherElement)) {
|
|
171
171
|
// Type or parameters, e.g. association target, changed.
|
|
172
|
+
if(otherElement.notNull && element.notNull === undefined) {
|
|
173
|
+
element.$notNull = false; // Explictily set notNull to the implicit default so we render the correct ALTER
|
|
174
|
+
}
|
|
172
175
|
changedElementsDict[name] = changedElement(element, otherElement);
|
|
173
176
|
}
|
|
174
177
|
|
package/lib/optionProcessor.js
CHANGED
|
@@ -102,8 +102,11 @@ optionProcessor
|
|
|
102
102
|
hanaAssocRealCardinality
|
|
103
103
|
mapAssocToJoinCardinality
|
|
104
104
|
ignoreAssocPublishingInUnion
|
|
105
|
+
odataOpenType
|
|
106
|
+
optionalActionFunctionParameters
|
|
105
107
|
--deprecated <list> Comma separated list of deprecated options.
|
|
106
108
|
Valid values are:
|
|
109
|
+
autoCorrectOrderBySourceRefs
|
|
107
110
|
eagerPersistenceForGeneratedEntities
|
|
108
111
|
--fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
|
|
109
112
|
parser as a fallback. Valid values are:
|
|
@@ -138,6 +141,7 @@ optionProcessor
|
|
|
138
141
|
toRename [options] <files...> (internal) Generate SQL DDL rename statements
|
|
139
142
|
manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
|
|
140
143
|
add / modify referential constraints.
|
|
144
|
+
inspect [options] <files...> (internal) Inspect the given CDS files.
|
|
141
145
|
`);
|
|
142
146
|
|
|
143
147
|
// ----------- toHana -----------
|
|
@@ -153,6 +157,7 @@ optionProcessor.command('H, toHana')
|
|
|
153
157
|
.option(' --integrity-not-enforced')
|
|
154
158
|
.option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
|
|
155
159
|
.option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
|
|
160
|
+
.option(' --disable-hana-comments')
|
|
156
161
|
.help(`
|
|
157
162
|
Usage: cdsc toHana [options] <files...>
|
|
158
163
|
|
|
@@ -187,6 +192,7 @@ optionProcessor.command('H, toHana')
|
|
|
187
192
|
RT : (default) No database constraint for an association
|
|
188
193
|
if not explicitly demanded via annotation
|
|
189
194
|
DB : Create database constraints for associations
|
|
195
|
+
--disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
|
|
190
196
|
`);
|
|
191
197
|
|
|
192
198
|
optionProcessor.command('O, toOdata')
|
|
@@ -263,6 +269,7 @@ optionProcessor.command('Q, toSql')
|
|
|
263
269
|
.option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
|
|
264
270
|
.option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
|
|
265
271
|
.option(' --constraints-in-create-table')
|
|
272
|
+
.option(' --disable-hana-comments')
|
|
266
273
|
.help(`
|
|
267
274
|
Usage: cdsc toSql [options] <files...>
|
|
268
275
|
|
|
@@ -310,6 +317,7 @@ optionProcessor.command('Q, toSql')
|
|
|
310
317
|
--constraints-in-create-table If set, the foreign key constraints will be rendered as
|
|
311
318
|
part of the "CREATE TABLE" statements rather than as separate
|
|
312
319
|
"ALTER TABLE ADD CONSTRAINT" statements
|
|
320
|
+
--disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
|
|
313
321
|
`);
|
|
314
322
|
|
|
315
323
|
optionProcessor.command('toRename')
|
|
@@ -377,6 +385,7 @@ optionProcessor.command('toCsn')
|
|
|
377
385
|
.option('-h, --help')
|
|
378
386
|
.option('-f, --csn-flavor <flavor>', ['client', 'gensrc', 'universal'], { aliases: ['--flavor'] })
|
|
379
387
|
.option(' --with-localized')
|
|
388
|
+
.option(' --with-locations')
|
|
380
389
|
.help(`
|
|
381
390
|
Usage: cdsc toCsn [options] <files...>
|
|
382
391
|
|
|
@@ -391,6 +400,8 @@ optionProcessor.command('toCsn')
|
|
|
391
400
|
statements, but not suitable for consumption by clients or
|
|
392
401
|
backends
|
|
393
402
|
universal: in development (BETA)
|
|
403
|
+
--with-locations Add $location to CSN artifacts. In contrast to \`--enrich-csn\`,
|
|
404
|
+
$location is an object with 'file', 'line' and 'col' properties.
|
|
394
405
|
|
|
395
406
|
Internal options (for testing only, may be changed/removed at any time)
|
|
396
407
|
--with-localized Add localized convenience views to the CSN output.
|
|
@@ -444,6 +455,22 @@ optionProcessor.command('explain')
|
|
|
444
455
|
-h, --help Show this help text
|
|
445
456
|
`);
|
|
446
457
|
|
|
458
|
+
optionProcessor.command('inspect')
|
|
459
|
+
.option('-h, --help')
|
|
460
|
+
.option(' --statistics')
|
|
461
|
+
.option(' --propagation <art>')
|
|
462
|
+
.positionalArgument('<files...>')
|
|
463
|
+
.help(`
|
|
464
|
+
Usage: cdsc inspect [options] <files...>
|
|
465
|
+
|
|
466
|
+
(internal): Inspect the CSN model compiled from the provided CDS files.
|
|
467
|
+
|
|
468
|
+
Options
|
|
469
|
+
-h, --help Show this help text
|
|
470
|
+
--statistics Print model statistics
|
|
471
|
+
--propagation <art> Show propagation sources for <art>
|
|
472
|
+
`);
|
|
473
|
+
|
|
447
474
|
module.exports = {
|
|
448
475
|
optionProcessor
|
|
449
476
|
};
|
package/lib/render/toCdl.js
CHANGED
|
@@ -142,11 +142,10 @@ function csnToCdl(csn, options) {
|
|
|
142
142
|
let result = renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
143
143
|
|
|
144
144
|
if (ext.includes && ext.includes.length > 0) {
|
|
145
|
-
// Includes can't be combined with anything in braces {}.
|
|
146
|
-
// are possible through CSN, but in CDL, only one include at once is possible.
|
|
145
|
+
// Includes can't be combined with anything in braces {}.
|
|
147
146
|
const affix = isElementExtend ? 'element ' : '';
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
const includes = ext.includes.map(inc => quotePathIfRequired(inc)).join(', ');
|
|
148
|
+
result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
|
|
150
149
|
return result;
|
|
151
150
|
}
|
|
152
151
|
|
|
@@ -358,17 +357,21 @@ function csnToCdl(csn, options) {
|
|
|
358
357
|
case 'context':
|
|
359
358
|
case 'service':
|
|
360
359
|
return renderContextOrService(artifactName, art, env);
|
|
360
|
+
|
|
361
361
|
case 'type':
|
|
362
362
|
case 'aspect':
|
|
363
363
|
case 'annotation': // annotation in 'csn.definitions' for compiler v1 compatibility
|
|
364
364
|
return renderTypeOrAnnotation(artifactName, art, env);
|
|
365
|
+
|
|
365
366
|
case 'action':
|
|
366
367
|
case 'function':
|
|
367
368
|
return renderActionOrFunction(artifactName, art, env);
|
|
369
|
+
|
|
368
370
|
case 'event':
|
|
369
371
|
return renderEvent(artifactName, art, env);
|
|
372
|
+
|
|
370
373
|
default:
|
|
371
|
-
throw new ModelError(`Unknown artifact kind: ${art.kind}`);
|
|
374
|
+
throw new ModelError(`to.cdl: Unknown artifact kind: ${art.kind}`);
|
|
372
375
|
}
|
|
373
376
|
}
|
|
374
377
|
|
|
@@ -426,14 +429,16 @@ function csnToCdl(csn, options) {
|
|
|
426
429
|
*/
|
|
427
430
|
function renderEntity(artifactName, art, env) {
|
|
428
431
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
result += env.indent + (art.abstract ? 'abstract ' : '');
|
|
433
|
+
result += `entity ${renderArtifactName(artifactName)}`;
|
|
434
|
+
|
|
435
|
+
if (art.params)
|
|
436
|
+
result += renderParameters(art, env);
|
|
437
|
+
|
|
434
438
|
if (art.includes)
|
|
435
439
|
result += renderIncludes(art.includes);
|
|
436
440
|
result += ' {\n';
|
|
441
|
+
const childEnv = increaseIndent(env);
|
|
437
442
|
for (const name in art.elements) {
|
|
438
443
|
const element = art.elements[name];
|
|
439
444
|
result += renderElement(name, element, childEnv);
|
|
@@ -664,8 +669,11 @@ function csnToCdl(csn, options) {
|
|
|
664
669
|
if (path.ref[0].args)
|
|
665
670
|
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
666
671
|
|
|
667
|
-
if (path.ref[0].where)
|
|
668
|
-
|
|
672
|
+
if (path.ref[0].where) {
|
|
673
|
+
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
674
|
+
const expr = renderExpr(path.ref[0].where, env, true, true);
|
|
675
|
+
result += `[${cardinality}${expr}]`;
|
|
676
|
+
}
|
|
669
677
|
|
|
670
678
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
671
679
|
if (path.ref.length > 1)
|
|
@@ -841,14 +849,9 @@ function csnToCdl(csn, options) {
|
|
|
841
849
|
const syntax = (art.projection) ? 'projection' : 'entity';
|
|
842
850
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
843
851
|
result += `${env.indent}${art.abstract ? 'abstract ' : ''}${syntax === 'projection' ? 'entity' : syntax} ${renderArtifactName(artifactName)}`;
|
|
844
|
-
if (art.params)
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
result += `(\n${parameters}\n${env.indent}) as `;
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
result += ' as ';
|
|
851
|
-
}
|
|
852
|
+
if (art.params)
|
|
853
|
+
result += renderParameters(art, env);
|
|
854
|
+
result += ' as ';
|
|
852
855
|
result += renderQuery(getNormalizedQuery(art).query, true, syntax, env, [ 'definitions', artifactName, 'query' ], art.elements);
|
|
853
856
|
result += ';\n';
|
|
854
857
|
result += renderQueryElementAnnotations(artifactName, art, env);
|
|
@@ -959,9 +962,10 @@ function csnToCdl(csn, options) {
|
|
|
959
962
|
if (limit.rows !== undefined)
|
|
960
963
|
limitStr += `limit ${renderExpr(limit.rows, limitEnv)}`;
|
|
961
964
|
|
|
962
|
-
if (limit.offset !== undefined)
|
|
963
|
-
|
|
964
|
-
|
|
965
|
+
if (limit.offset !== undefined) {
|
|
966
|
+
const offsetIndent = (limitStr === '') ? '' : `\n${increaseIndent(limitEnv).indent}`;
|
|
967
|
+
limitStr += `${offsetIndent}offset ${renderExpr(limit.offset, limitEnv)}`;
|
|
968
|
+
}
|
|
965
969
|
return limitStr;
|
|
966
970
|
}
|
|
967
971
|
|
|
@@ -1040,10 +1044,9 @@ function csnToCdl(csn, options) {
|
|
|
1040
1044
|
* @return {string}
|
|
1041
1045
|
*/
|
|
1042
1046
|
function renderActionOrFunction(actionName, act, env) {
|
|
1043
|
-
let result =
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
|
|
1047
|
+
let result = renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
|
|
1048
|
+
result += ` ${renderArtifactName(actionName)}`;
|
|
1049
|
+
result += renderParameters(act, env);
|
|
1047
1050
|
if (act.returns) {
|
|
1048
1051
|
const actEnv = envAddPath(env, [ 'returns' ]);
|
|
1049
1052
|
result += ` returns ${renderTypeReferenceAndProps(act.returns, actEnv)}`;
|
|
@@ -1053,6 +1056,23 @@ function csnToCdl(csn, options) {
|
|
|
1053
1056
|
return result;
|
|
1054
1057
|
}
|
|
1055
1058
|
|
|
1059
|
+
/**
|
|
1060
|
+
* Render art.params, i.e. list of parameter in parentheses. If there is only one
|
|
1061
|
+
* parameter, a single line is used, otherwise an indented list is used.
|
|
1062
|
+
* If there are no params, an empty list `()` is returned.
|
|
1063
|
+
*
|
|
1064
|
+
* @param {CSN.Artifact} art
|
|
1065
|
+
* @param {CdlRenderEnvironment} env
|
|
1066
|
+
* @returns {string}
|
|
1067
|
+
*/
|
|
1068
|
+
function renderParameters(art, env) {
|
|
1069
|
+
const childEnv = increaseIndent(env);
|
|
1070
|
+
const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
|
|
1071
|
+
if (parameters.length === 0)
|
|
1072
|
+
return '()';
|
|
1073
|
+
return `(\n${parameters.join(',\n')}\n${env.indent})`;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1056
1076
|
/**
|
|
1057
1077
|
* Render an action or function parameter 'par' with name 'parName'. Return the resulting source string (no trailing LF).
|
|
1058
1078
|
*
|
|
@@ -1075,7 +1095,7 @@ function csnToCdl(csn, options) {
|
|
|
1075
1095
|
* @param {string} artifactName
|
|
1076
1096
|
* @param {CSN.Artifact} art
|
|
1077
1097
|
* @param {CdlRenderEnvironment} env
|
|
1078
|
-
* @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind.
|
|
1098
|
+
* @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind.
|
|
1079
1099
|
* @return {string}
|
|
1080
1100
|
*/
|
|
1081
1101
|
function renderTypeOrAnnotation(artifactName, art, env, artType) {
|
|
@@ -1085,9 +1105,11 @@ function csnToCdl(csn, options) {
|
|
|
1085
1105
|
result += renderIncludes(art.includes);
|
|
1086
1106
|
|
|
1087
1107
|
if (!art.type && art.elements) // For nicer output, no colon if unnamed structure is used.
|
|
1088
|
-
result += ` ${renderTypeReferenceAndProps(art, env)}
|
|
1108
|
+
result += ` ${renderTypeReferenceAndProps(art, env)}`;
|
|
1089
1109
|
else
|
|
1090
|
-
result += ` : ${renderTypeReferenceAndProps(art, env)}
|
|
1110
|
+
result += ` : ${renderTypeReferenceAndProps(art, env)}`;
|
|
1111
|
+
// for aspects, but since types don't have `actions` this does not hurt
|
|
1112
|
+
result += `${renderActionsAndFunctions(art, env)};\n`;
|
|
1091
1113
|
return result;
|
|
1092
1114
|
}
|
|
1093
1115
|
|
|
@@ -1373,7 +1395,9 @@ function csnToCdl(csn, options) {
|
|
|
1373
1395
|
}
|
|
1374
1396
|
if (s.where) {
|
|
1375
1397
|
// Filter, possibly with cardinality
|
|
1376
|
-
|
|
1398
|
+
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1399
|
+
const expr = renderExpr(s.where, env, inline, true);
|
|
1400
|
+
result += `[${cardinality}${expr}]`;
|
|
1377
1401
|
}
|
|
1378
1402
|
|
|
1379
1403
|
return result;
|
|
@@ -1516,7 +1540,8 @@ function csnToCdl(csn, options) {
|
|
|
1516
1540
|
* @return {string}
|
|
1517
1541
|
*/
|
|
1518
1542
|
function renderForeignKey(fKey, env) {
|
|
1519
|
-
|
|
1543
|
+
const alias = fKey.as ? (` as ${fKey.as}`) : '';
|
|
1544
|
+
return renderExpr(fKey, env) + alias;
|
|
1520
1545
|
}
|
|
1521
1546
|
|
|
1522
1547
|
/**
|