@sap/cds-compiler 2.7.0 → 2.11.2
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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/lib/model/csnUtils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp } = require('../base/model');
|
|
4
4
|
const { csnRefs } = require('../model/csnRefs');
|
|
5
|
+
const { isBuiltinType } = require('../compiler/builtins.js')
|
|
5
6
|
const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
|
|
6
7
|
const version = require('../../package.json').version;
|
|
7
8
|
|
|
@@ -11,8 +12,8 @@ const version = require('../../package.json').version;
|
|
|
11
12
|
* Generic Callback
|
|
12
13
|
*
|
|
13
14
|
* @callback genericCallback
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
15
|
+
* @param {any} art
|
|
16
|
+
* @param {string} name Artifact Name
|
|
16
17
|
* @param {string} prop Dictionary Property
|
|
17
18
|
* @param {CSN.Path} path Location
|
|
18
19
|
* @param {CSN.Artifact} [dictionary]
|
|
@@ -36,7 +37,7 @@ const version = require('../../package.json').version;
|
|
|
36
37
|
* @param {CSN.Model} model (Compact) CSN model
|
|
37
38
|
*/
|
|
38
39
|
function getUtils(model) {
|
|
39
|
-
const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
|
|
40
|
+
const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
|
|
40
41
|
|
|
41
42
|
return {
|
|
42
43
|
getCsnDef,
|
|
@@ -59,6 +60,7 @@ function getUtils(model) {
|
|
|
59
60
|
artifactRef,
|
|
60
61
|
effectiveType,
|
|
61
62
|
get$combined,
|
|
63
|
+
getOrigin,
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
/**
|
|
@@ -456,8 +458,8 @@ function getUtils(model) {
|
|
|
456
458
|
return resultNode;
|
|
457
459
|
}
|
|
458
460
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
+
|
|
462
|
+
|
|
461
463
|
/**
|
|
462
464
|
* Resolve to the final type of a type, that means follow type chains, references to other types or
|
|
463
465
|
* elements a.s.o
|
|
@@ -496,12 +498,12 @@ function getUtils(model) {
|
|
|
496
498
|
* Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
|
|
497
499
|
* the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
|
|
498
500
|
* no type (e.g. expr in a view without explicit type) returns 'null'
|
|
499
|
-
*
|
|
501
|
+
*
|
|
500
502
|
* @param {string|object} type Type - either string or ref
|
|
501
|
-
* @param {CSN.Path} path
|
|
503
|
+
* @param {CSN.Path} path
|
|
502
504
|
* @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
|
|
503
|
-
* @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
|
|
504
|
-
* @returns
|
|
505
|
+
* @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
|
|
506
|
+
* @returns
|
|
505
507
|
*/
|
|
506
508
|
function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
|
|
507
509
|
if (!type)
|
|
@@ -551,16 +553,6 @@ function getUtils(model) {
|
|
|
551
553
|
}
|
|
552
554
|
}
|
|
553
555
|
|
|
554
|
-
// Tell if a type is (directly) a builtin type
|
|
555
|
-
// Note that in CSN builtins are not in the definition of the model, so we can only check against their absolute names.
|
|
556
|
-
// Builtin types are "cds.<something>", i.e. they are directly in 'cds', but not for example
|
|
557
|
-
// in 'cds.foundation'. Also note, that a type might be a ref object, that refers to something else,
|
|
558
|
-
// so if you consider type chains don't forget first to resolve to the final type before
|
|
559
|
-
function isBuiltinType(type) {
|
|
560
|
-
return typeof(type) === 'string' && type.startsWith('cds.') && !type.startsWith('cds.foundation.')
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
556
|
/**
|
|
565
557
|
* Deeply clone the given CSN model and return it.
|
|
566
558
|
* In testMode (or with testSortCsn), definitions are sorted.
|
|
@@ -630,12 +622,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
630
622
|
// Backends rely on the fact that `forEachElement` also goes through all
|
|
631
623
|
// `elements` of the return type (if structured).
|
|
632
624
|
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
633
|
-
if (construct.returns) {
|
|
625
|
+
if (construct.returns && !iterateOptions.elementsOnly) {
|
|
634
626
|
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
|
|
635
627
|
}
|
|
636
628
|
|
|
637
629
|
path = [...path]; // Copy
|
|
638
|
-
const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
|
|
630
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
|
|
639
631
|
propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
|
|
640
632
|
}
|
|
641
633
|
|
|
@@ -816,11 +808,25 @@ function forAllQueries(query, callback, path = []){
|
|
|
816
808
|
}
|
|
817
809
|
}
|
|
818
810
|
|
|
819
|
-
function forAllElements(artifact, artifactName, cb){
|
|
811
|
+
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
820
812
|
if(artifact.elements) {
|
|
821
813
|
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
822
814
|
}
|
|
823
815
|
|
|
816
|
+
if(includeActions && artifact.actions) {
|
|
817
|
+
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
818
|
+
const path = ['definitions', artifactName, 'actions', actionName];
|
|
819
|
+
if(action.params) {
|
|
820
|
+
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
821
|
+
if(param.elements)
|
|
822
|
+
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
if(action.returns && action.returns.elements)
|
|
826
|
+
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
824
830
|
if(artifact.query) {
|
|
825
831
|
forAllQueries(artifact.query, (q, p) => {
|
|
826
832
|
const s = q.SELECT;
|
|
@@ -870,7 +876,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true) {
|
|
|
870
876
|
* function accepts EDM internal and external options
|
|
871
877
|
*
|
|
872
878
|
* @param {CSN.Element} elementCsn
|
|
873
|
-
* @param {
|
|
879
|
+
* @param {ODataOptions} options EDM specific options
|
|
874
880
|
*/
|
|
875
881
|
function isEdmPropertyRendered(elementCsn, options) {
|
|
876
882
|
if(options.toOdata)
|
|
@@ -920,10 +926,10 @@ function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
|
|
|
920
926
|
throw new Error('Unknown naming convention: ' + namingConvention);
|
|
921
927
|
}
|
|
922
928
|
else {
|
|
923
|
-
const namespace = csn;
|
|
924
929
|
console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
|
|
925
930
|
if (namingConvention === 'hdbcds') {
|
|
926
|
-
if (
|
|
931
|
+
if (csn) {
|
|
932
|
+
const namespace = String(csn);
|
|
927
933
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
928
934
|
}
|
|
929
935
|
return artifactName;
|
|
@@ -1097,7 +1103,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
1097
1103
|
for (let name of Object.getOwnPropertyNames( node )) {
|
|
1098
1104
|
const trans = transformers[name] || standard;
|
|
1099
1105
|
if(customTransformers[name])
|
|
1100
|
-
customTransformers[name](node, name, node[name], csnPath);
|
|
1106
|
+
customTransformers[name](node, name, node[name], csnPath, parent, prop);
|
|
1101
1107
|
|
|
1102
1108
|
trans( node, name, node[name], csnPath );
|
|
1103
1109
|
}
|
|
@@ -1106,6 +1112,9 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
|
|
|
1106
1112
|
}
|
|
1107
1113
|
|
|
1108
1114
|
function dictionary( node, prop, dict ) {
|
|
1115
|
+
// Allow skipping dicts like actions in forHanaNew
|
|
1116
|
+
if(options.skipDict && options.skipDict[prop])
|
|
1117
|
+
return;
|
|
1109
1118
|
csnPath.push( prop );
|
|
1110
1119
|
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
1111
1120
|
standard( dict, name, dict[name] );
|
|
@@ -1528,7 +1537,7 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1528
1537
|
if (!options.testMode)
|
|
1529
1538
|
return;
|
|
1530
1539
|
const sorted = Object.create(null);
|
|
1531
|
-
Object.keys(csn.definitions).sort().forEach((name) => {
|
|
1540
|
+
Object.keys(csn.definitions || {}).sort().forEach((name) => {
|
|
1532
1541
|
sorted[name] = csn.definitions[name];
|
|
1533
1542
|
});
|
|
1534
1543
|
csn.definitions = sorted;
|
|
@@ -1538,7 +1547,7 @@ function sortCsnDefinitionsForTests(csn, options) {
|
|
|
1538
1547
|
* Return an array of non-abstract service names contained in CSN
|
|
1539
1548
|
*
|
|
1540
1549
|
* @param {CSN.Model} csn
|
|
1541
|
-
* @returns {
|
|
1550
|
+
* @returns {string[]}
|
|
1542
1551
|
*/
|
|
1543
1552
|
function getServiceNames(csn) {
|
|
1544
1553
|
let result = [];
|
|
@@ -1552,8 +1561,8 @@ function getServiceNames(csn) {
|
|
|
1552
1561
|
|
|
1553
1562
|
/**
|
|
1554
1563
|
* Check wether the artifact is @cds.persistence.skip
|
|
1555
|
-
*
|
|
1556
|
-
* @param {CSN.Artifact} artifact
|
|
1564
|
+
*
|
|
1565
|
+
* @param {CSN.Artifact} artifact
|
|
1557
1566
|
* @returns {Boolean}
|
|
1558
1567
|
*/
|
|
1559
1568
|
function isSkipped(artifact) {
|
|
@@ -1562,9 +1571,9 @@ function isSkipped(artifact) {
|
|
|
1562
1571
|
|
|
1563
1572
|
/**
|
|
1564
1573
|
* Walk path in the CSN and return the result.
|
|
1565
|
-
*
|
|
1566
|
-
* @param {CSN.Model} csn
|
|
1567
|
-
* @param {CSN.Path} path
|
|
1574
|
+
*
|
|
1575
|
+
* @param {CSN.Model} csn
|
|
1576
|
+
* @param {CSN.Path} path
|
|
1568
1577
|
* @returns {object} Whatever is at the end of path
|
|
1569
1578
|
*/
|
|
1570
1579
|
function walkCsnPath(csn, path) {
|
|
@@ -1577,6 +1586,34 @@ function walkCsnPath(csn, path) {
|
|
|
1577
1586
|
return obj;
|
|
1578
1587
|
}
|
|
1579
1588
|
|
|
1589
|
+
/**
|
|
1590
|
+
* If provided, get the replacement string for the given magic variable ref.
|
|
1591
|
+
* No validation is done that the ref is actually magic!
|
|
1592
|
+
*
|
|
1593
|
+
* @param {array} ref
|
|
1594
|
+
* @param {CSN.Options} options
|
|
1595
|
+
* @returns {string|null}
|
|
1596
|
+
*/
|
|
1597
|
+
function getVariableReplacement(ref, options) {
|
|
1598
|
+
if(options && options.variableReplacements) {
|
|
1599
|
+
let replacement = options.variableReplacements;
|
|
1600
|
+
for(let i = 0; i < ref.length; i++) {
|
|
1601
|
+
replacement = replacement[ref[i]];
|
|
1602
|
+
if(replacement === undefined)
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
if(replacement === undefined)
|
|
1607
|
+
return null; // no valid replacement found
|
|
1608
|
+
else if(typeof replacement === 'string')
|
|
1609
|
+
return replacement; // valid replacement
|
|
1610
|
+
else
|
|
1611
|
+
return null; // $user.foo, but we only have configured $user.foo.bar -> error
|
|
1612
|
+
} else {
|
|
1613
|
+
return null;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1580
1617
|
module.exports = {
|
|
1581
1618
|
getUtils,
|
|
1582
1619
|
cloneCsn,
|
|
@@ -1617,4 +1654,5 @@ module.exports = {
|
|
|
1617
1654
|
getServiceNames,
|
|
1618
1655
|
isSkipped,
|
|
1619
1656
|
walkCsnPath,
|
|
1657
|
+
getVariableReplacement
|
|
1620
1658
|
};
|
package/lib/model/enrichCsn.js
CHANGED
|
@@ -39,6 +39,8 @@ function enrichCsn( csn, options = {} ) {
|
|
|
39
39
|
params: dictionary,
|
|
40
40
|
enum: dictionary,
|
|
41
41
|
mixin: dictionary,
|
|
42
|
+
returns: definition,
|
|
43
|
+
items: definition,
|
|
42
44
|
ref: pathRef,
|
|
43
45
|
type: simpleRef,
|
|
44
46
|
targetAspect: simpleRef,
|
|
@@ -81,10 +83,19 @@ function enrichCsn( csn, options = {} ) {
|
|
|
81
83
|
csnPath.pop();
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
function definition( parent, prop, obj ) {
|
|
87
|
+
const origin = getOrigin( obj ); // before standard for implicit protos inside
|
|
88
|
+
standard( parent, prop, obj );
|
|
89
|
+
if (obj.$origin === undefined && origin != null)
|
|
90
|
+
obj._origin = refLocation( origin );
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
function dictionary( parent, prop, dict ) {
|
|
94
|
+
if (!dict) // value null for inheritance interruption
|
|
95
|
+
return;
|
|
85
96
|
csnPath.push( prop );
|
|
86
97
|
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
87
|
-
|
|
98
|
+
definition( dict, name, dict[name] );
|
|
88
99
|
}
|
|
89
100
|
if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
|
|
90
101
|
parent['$'+prop] = dict;
|
|
@@ -123,13 +134,17 @@ function enrichCsn( csn, options = {} ) {
|
|
|
123
134
|
|
|
124
135
|
function $origin( parent, prop, ref ) {
|
|
125
136
|
if (options.testMode) {
|
|
126
|
-
if (Array.isArray( ref ))
|
|
127
|
-
parent._origin = refLocation( getOrigin( parent ) );
|
|
137
|
+
if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
|
|
138
|
+
parent._origin = refLocation( getOrigin( parent, true ) );
|
|
139
|
+
else if ( ref )
|
|
140
|
+
standard( parent, prop, ref )
|
|
128
141
|
}
|
|
129
142
|
else {
|
|
130
143
|
try {
|
|
131
|
-
if (Array.isArray( ref )) // $origin: […], not $origin: {…}
|
|
132
|
-
parent._origin = refLocation( getOrigin( parent ) );
|
|
144
|
+
if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
|
|
145
|
+
parent._origin = refLocation( getOrigin( parent, true ) );
|
|
146
|
+
else if ( ref )
|
|
147
|
+
standard( parent, prop, ref )
|
|
133
148
|
} catch (e) {
|
|
134
149
|
parent._origin = e.toString();
|
|
135
150
|
}
|
|
@@ -171,10 +186,10 @@ function enrichCsn( csn, options = {} ) {
|
|
|
171
186
|
csnPath.pop();
|
|
172
187
|
}
|
|
173
188
|
|
|
174
|
-
function _cache_debug( obj ) {
|
|
189
|
+
function _cache_debug( obj, subCache ) {
|
|
175
190
|
if (options.enrichCsn !== 'DEBUG')
|
|
176
191
|
return;
|
|
177
|
-
const cache = __getCache_forEnrichCsnDebugging( obj );
|
|
192
|
+
const cache = subCache || __getCache_forEnrichCsnDebugging( obj );
|
|
178
193
|
if (!cache)
|
|
179
194
|
return;
|
|
180
195
|
if (cache.$$objectNumber > 0) {
|
|
@@ -197,8 +212,20 @@ function enrichCsn( csn, options = {} ) {
|
|
|
197
212
|
}
|
|
198
213
|
else if (name[0] !== '$' || !Object.getPrototypeOf( val )) {
|
|
199
214
|
// ‹name›: dictionary of CSN nodes,
|
|
200
|
-
// ‹$name›: dictionary of cache values if no prototype
|
|
201
|
-
|
|
215
|
+
// ‹$name›: dictionary of cache values if no prototype
|
|
216
|
+
if (name !== '$aliases') {
|
|
217
|
+
obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const sub = Object.create(null);
|
|
221
|
+
for (const n in val) {
|
|
222
|
+
const alias = val[n];
|
|
223
|
+
const c = {};
|
|
224
|
+
_cache_debug( c, alias );
|
|
225
|
+
sub[n] = c.$$cacheObject;
|
|
226
|
+
}
|
|
227
|
+
obj.$$cacheObject[name] = sub;
|
|
228
|
+
}
|
|
202
229
|
}
|
|
203
230
|
else if (Array.isArray( val )) {
|
|
204
231
|
obj.$$cacheObject[name] = val.map( item => {
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
const msg = require('../base/messages');
|
|
15
15
|
|
|
16
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
17
|
+
|
|
16
18
|
class NOT_A_DICTIONARY {} // used for consol.log display
|
|
17
19
|
|
|
18
20
|
function locationString( loc ) {
|
|
@@ -32,15 +34,22 @@ const kindsRepresentedAsLinks = {
|
|
|
32
34
|
// represent SELECTs in query / SET-args property as link:
|
|
33
35
|
select: (art, parent) => art._main && parent !== art._main.$queries,
|
|
34
36
|
// represent table alias in from / join-args property as link:
|
|
35
|
-
$tableAlias:
|
|
36
|
-
// represent
|
|
37
|
+
$tableAlias: tableAliasAsLink,
|
|
38
|
+
// represent "navigation elemens" in _combined as links:
|
|
37
39
|
$navElement: (art, parent) => art._parent && parent !== art._parent.elements,
|
|
38
40
|
// represent mixin in $tableAliases as link:
|
|
39
|
-
mixin:
|
|
41
|
+
mixin: tableAliasAsLink,
|
|
40
42
|
// represent $projection as link, as it is just another search name for $self:
|
|
41
43
|
$self: (_a, _p, name) => name !== '$self',
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
function tableAliasAsLink( art, parent, name ) {
|
|
47
|
+
return art._parent && art._parent.$tableAliases && // initXYZ() is run
|
|
48
|
+
parent !== art._parent.$tableAliases && // not in $tableAliases
|
|
49
|
+
!(art.$duplicates === true && name && // and its $duplicates
|
|
50
|
+
parent === art._parent.$tableAliases[name].$duplicates);
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
function revealInternalProperties( model, name ) {
|
|
45
54
|
const transformers = {
|
|
46
55
|
messages: m => m,
|
|
@@ -65,6 +74,7 @@ function revealInternalProperties( model, name ) {
|
|
|
65
74
|
mixin: dictionary,
|
|
66
75
|
args: dictionary,
|
|
67
76
|
$tableAliases: dictionary,
|
|
77
|
+
$duplicates: duplicates,
|
|
68
78
|
$keysNavigation: dictionary,
|
|
69
79
|
$layerNumber: n => n,
|
|
70
80
|
$extra: e => e,
|
|
@@ -203,6 +213,8 @@ function revealInternalProperties( model, name ) {
|
|
|
203
213
|
for (let prop of Object.getOwnPropertyNames( node )) { // also non-enumerable
|
|
204
214
|
r[prop] = reveal( node[prop], node, prop );
|
|
205
215
|
}
|
|
216
|
+
if (node[$inferred] && !node['[$inferred]'])
|
|
217
|
+
r['[$inferred]'] = node[$inferred];
|
|
206
218
|
return r;
|
|
207
219
|
}
|
|
208
220
|
|
|
@@ -228,7 +240,7 @@ function revealInternalProperties( model, name ) {
|
|
|
228
240
|
if (node == null || typeof node !== 'object' )
|
|
229
241
|
return node
|
|
230
242
|
if (Array.isArray(node))
|
|
231
|
-
return node.map( n => reveal( n, node ) );
|
|
243
|
+
return node.map( n => reveal( n, node, name ) );
|
|
232
244
|
|
|
233
245
|
const asLinkTest = kindsRepresentedAsLinks[ node.kind ];
|
|
234
246
|
if (asLinkTest && asLinkTest( node, parent, name ))
|
|
@@ -246,6 +258,10 @@ function revealInternalProperties( model, name ) {
|
|
|
246
258
|
}
|
|
247
259
|
return r;
|
|
248
260
|
}
|
|
261
|
+
|
|
262
|
+
function duplicates( node, parent ) {
|
|
263
|
+
return reveal( node, parent, parent.name && parent.name.id );
|
|
264
|
+
}
|
|
249
265
|
}
|
|
250
266
|
|
|
251
267
|
function artifactIdentifier( node, parent ) {
|
|
@@ -12,11 +12,12 @@ const {
|
|
|
12
12
|
*
|
|
13
13
|
* @param beforeModel the before-model
|
|
14
14
|
* @param afterModel the after-model
|
|
15
|
-
* @param {hdiOptions|false} options
|
|
15
|
+
* @param {import('../api/main.js').hdiOptions|false} options
|
|
16
16
|
* @returns {object} the sets of deletions, extensions, and migrations of entities necessary to transform the before-model
|
|
17
17
|
* to the after-model, together with all the definitions of the after-model
|
|
18
18
|
*/
|
|
19
19
|
function compareModels(beforeModel, afterModel, options) {
|
|
20
|
+
// @ts-ignore
|
|
20
21
|
if(!(options && options.testMode)) // no $version with testMode
|
|
21
22
|
validateCsnVersions(beforeModel, afterModel, options);
|
|
22
23
|
|
package/lib/optionProcessor.js
CHANGED
|
@@ -27,8 +27,10 @@ optionProcessor
|
|
|
27
27
|
.option(' --internal-msg')
|
|
28
28
|
.option(' --beta-mode')
|
|
29
29
|
.option(' --beta <list>')
|
|
30
|
-
.option(' --
|
|
31
|
-
.option(' --
|
|
30
|
+
.option(' --integrity-not-validated')
|
|
31
|
+
.option(' --integrity-not-enforced')
|
|
32
|
+
.option(' --assert-integrity <mode>', [ 'true', 'false', 'individual' ])
|
|
33
|
+
.option(' --assert-integrity-type <type>', [ 'RT', 'DB' ])
|
|
32
34
|
.option(' --deprecated <list>')
|
|
33
35
|
.option(' --hana-flavor')
|
|
34
36
|
.option(' --direct-backend')
|
|
@@ -37,8 +39,11 @@ optionProcessor
|
|
|
37
39
|
.option(' --test-mode')
|
|
38
40
|
.option(' --test-sort-csn')
|
|
39
41
|
.option(' --doc-comment')
|
|
42
|
+
.option(' --add-texts-language-assoc')
|
|
40
43
|
.option(' --localized-without-coalesce')
|
|
41
|
-
.option(' --
|
|
44
|
+
.option(' --default-binary-length <length>')
|
|
45
|
+
.option(' --default-string-length <length>')
|
|
46
|
+
.option(' --no-recompile')
|
|
42
47
|
.positionalArgument('<files...>')
|
|
43
48
|
.help(`
|
|
44
49
|
Usage: cdsc <command> [options] <files...>
|
|
@@ -73,7 +78,8 @@ optionProcessor
|
|
|
73
78
|
-- Indicate the end of options (helpful if source names start with "-")
|
|
74
79
|
|
|
75
80
|
Type options
|
|
76
|
-
--
|
|
81
|
+
--default-binary-length <length> Default 'length' for 'cds.Binary'
|
|
82
|
+
--default-string-length <length> Default 'length' for 'cds.String'
|
|
77
83
|
|
|
78
84
|
Diagnostic options
|
|
79
85
|
--trace-parser Trace parser
|
|
@@ -88,15 +94,23 @@ optionProcessor
|
|
|
88
94
|
--beta-mode Enable all unsupported, incomplete (beta) features
|
|
89
95
|
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
|
|
90
96
|
Valid values are:
|
|
91
|
-
foreignKeyConstraints
|
|
92
97
|
addTextsLanguageAssoc
|
|
93
98
|
hanaAssocRealCardinality
|
|
94
99
|
mapAssocToJoinCardinality
|
|
95
100
|
ignoreAssocPublishingInUnion
|
|
96
|
-
--
|
|
97
|
-
|
|
98
|
-
--
|
|
99
|
-
|
|
101
|
+
--integrity-not-enforced If this option is supplied, referential constraints are NOT ENFORCED.
|
|
102
|
+
This option is also applied to result of "cdsc manageConstraints"
|
|
103
|
+
--integrity-not-validated If this option is supplied, referential constraints are NOT VALIDATED.
|
|
104
|
+
This option is also applied to result of "cdsc manageConstraints"
|
|
105
|
+
--assert-integrity <mode> Turn DB constraints on/off:
|
|
106
|
+
true : Constraints will be generated for all associations if
|
|
107
|
+
the assert-integrity-type is set to DB
|
|
108
|
+
false : No constraints will be generated
|
|
109
|
+
individual : Constraints will be generated for selected associations
|
|
110
|
+
--assert-integrity-type <type> Specifies how the referential integrity checks should be performed:
|
|
111
|
+
RT : (default) No database constraint for an association
|
|
112
|
+
if not explicitly demanded via annotation
|
|
113
|
+
DB : Create database constraints for associations
|
|
100
114
|
--deprecated <list> Comma separated list of deprecated options.
|
|
101
115
|
Valid values are:
|
|
102
116
|
noElementsExpansion
|
|
@@ -123,7 +137,10 @@ optionProcessor
|
|
|
123
137
|
OData CSN, CDL order and more. When --test-mode is enabled, this
|
|
124
138
|
option is implicitly enabled as well.
|
|
125
139
|
--doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
|
|
140
|
+
--add-texts-language-assoc In generated texts entities, add association "language"
|
|
141
|
+
to "sap.common.Languages" if it exists
|
|
126
142
|
--localized-without-coalesce Omit coalesce in localized convenience views
|
|
143
|
+
--no-recompile Don't recompile in case of internal errors
|
|
127
144
|
|
|
128
145
|
Commands
|
|
129
146
|
H, toHana [options] <files...> Generate HANA CDS source files
|
|
@@ -134,7 +151,7 @@ optionProcessor
|
|
|
134
151
|
parseCdl [options] <file> Generate a CSN that is close to the CDL source.
|
|
135
152
|
explain <message-id> Explain a compiler message.
|
|
136
153
|
toRename [options] <files...> (internal) Generate SQL DDL rename statements
|
|
137
|
-
manageConstraints [options] <files...>
|
|
154
|
+
manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
|
|
138
155
|
add / modify referential constraints.
|
|
139
156
|
`);
|
|
140
157
|
|
|
@@ -144,7 +161,6 @@ optionProcessor.command('H, toHana')
|
|
|
144
161
|
.option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
|
|
145
162
|
.option(' --render-virtual')
|
|
146
163
|
.option(' --joinfk')
|
|
147
|
-
.option(' --skip-db-constraints')
|
|
148
164
|
.option('-u, --user <user>')
|
|
149
165
|
.option('-s, --src')
|
|
150
166
|
.option('-c, --csn')
|
|
@@ -168,7 +184,6 @@ optionProcessor.command('H, toHana')
|
|
|
168
184
|
using element names with dots).
|
|
169
185
|
--render-virtual Render virtual elements in views and draft tables
|
|
170
186
|
--joinfk Create JOINs for foreign key accesses
|
|
171
|
-
--skip-db-constraints Do not render referential constraints for associations
|
|
172
187
|
-u, --user <user> Value for the "$user" variable
|
|
173
188
|
-s, --src (default) Generate HANA CDS source files "<artifact>.hdbcds"
|
|
174
189
|
-c, --csn Generate "hana_csn.json" with HANA-preprocessed model
|
|
@@ -235,7 +250,6 @@ optionProcessor.command('Q, toSql')
|
|
|
235
250
|
.option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
|
|
236
251
|
.option(' --render-virtual')
|
|
237
252
|
.option(' --joinfk')
|
|
238
|
-
.option(' --skip-db-constraints')
|
|
239
253
|
.option('-d, --dialect <dialect>', ['hana', 'sqlite', 'plain'])
|
|
240
254
|
.option('-u, --user <user>')
|
|
241
255
|
.option('-l, --locale <locale>')
|
|
@@ -261,7 +275,6 @@ optionProcessor.command('Q, toSql')
|
|
|
261
275
|
combination with "hana" dialect.
|
|
262
276
|
--render-virtual Render virtual elements in views and draft tables
|
|
263
277
|
--joinfk Create JOINs for foreign key accesses
|
|
264
|
-
--skip-db-constraints Do not render referential constraints for associations
|
|
265
278
|
-d, --dialect <dialect> SQL dialect to be generated:
|
|
266
279
|
plain : (default) Common SQL - no assumptions about DB restrictions
|
|
267
280
|
hana : SQL with HANA specific language features
|
|
@@ -309,7 +322,7 @@ optionProcessor.command('manageConstraints')
|
|
|
309
322
|
|
|
310
323
|
(internal, subject to change): Generate SQL DDL ALTER TABLE statements to add / modify
|
|
311
324
|
referential constraints on an existing model.
|
|
312
|
-
Combine with options "--
|
|
325
|
+
Combine with options "--integrity-not-enforced" and "--integrity-not-validated"
|
|
313
326
|
to switch off foreign key constraint enforcement / validation.
|
|
314
327
|
|
|
315
328
|
Options
|
|
@@ -357,6 +370,7 @@ optionProcessor.command('toCsn')
|
|
|
357
370
|
|
|
358
371
|
optionProcessor.command('parseCdl')
|
|
359
372
|
.option('-h, --help')
|
|
373
|
+
.positionalArgument('<file>')
|
|
360
374
|
.help(`
|
|
361
375
|
Usage: cdsc parseCdl [options] <file>
|
|
362
376
|
|
|
@@ -369,14 +383,17 @@ optionProcessor.command('parseCdl')
|
|
|
369
383
|
|
|
370
384
|
optionProcessor.command('explain')
|
|
371
385
|
.option('-h, --help')
|
|
386
|
+
.positionalArgument('<message-id>')
|
|
372
387
|
.help(`
|
|
373
388
|
Usage: cdsc explain [options] <message-id>
|
|
374
389
|
|
|
375
390
|
Explain the compiler message that has the given message-id.
|
|
376
391
|
The explanation contains a faulty example and a solution.
|
|
377
392
|
|
|
393
|
+
Use \`explain list\` to list all available messages.
|
|
394
|
+
|
|
378
395
|
Options
|
|
379
|
-
-h, --help
|
|
396
|
+
-h, --help Show this help text
|
|
380
397
|
`);
|
|
381
398
|
|
|
382
399
|
module.exports = {
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
"prefer-template": "error",
|
|
8
8
|
"no-trailing-spaces": "error",
|
|
9
9
|
"sonarjs/cognitive-complexity": "off",
|
|
10
|
+
"sonarjs/no-duplicate-string": ["off"],
|
|
10
11
|
"sonarjs/no-nested-template-literals": "off",
|
|
11
12
|
"template-curly-spacing":["error", "never"],
|
|
12
|
-
|
|
13
|
+
// Who cares - just very whiny and in the way
|
|
14
|
+
"complexity": "off",
|
|
13
15
|
"max-len": "off",
|
|
14
16
|
// We should enable this
|
|
15
17
|
"no-shadow": "off"
|
|
@@ -44,7 +44,7 @@ class DuplicateChecker {
|
|
|
44
44
|
* Add an artifact to the "seen"-list
|
|
45
45
|
*
|
|
46
46
|
* @param {string} name Persistence name of the artifact
|
|
47
|
-
* @param {CSN.Location} location CSN location of the artifact
|
|
47
|
+
* @param {CSN.Location|CSN.Path} location CSN location of the artifact
|
|
48
48
|
* @param {string} modelName CSN artifact name
|
|
49
49
|
*/
|
|
50
50
|
addArtifact( name, location, modelName ) {
|