@sap/cds-compiler 5.4.4 → 5.5.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 +22 -1
- package/bin/cds_remove_invalid_whitespace.js +4 -4
- package/bin/cds_update_annotations.js +3 -3
- package/bin/cds_update_identifiers.js +3 -3
- package/lib/api/main.js +18 -30
- package/lib/api/validate.js +6 -1
- package/lib/base/lazyload.js +28 -0
- package/lib/base/location.js +1 -0
- package/lib/base/message-registry.js +47 -11
- package/lib/base/messages.js +17 -3
- package/lib/checks/cdsMap.js +27 -0
- package/lib/checks/{dbFeatureFlags.js → featureFlags.js} +1 -1
- package/lib/checks/parameters.js +61 -4
- package/lib/checks/validator.js +17 -7
- package/lib/compiler/define.js +1 -0
- package/lib/compiler/index.js +7 -7
- package/lib/gen/BaseParser.js +345 -235
- package/lib/gen/CdlParser.js +4438 -4492
- package/lib/gen/Dictionary.json +2 -2
- package/lib/language/antlrParser.js +2 -111
- package/lib/main.js +16 -37
- package/lib/model/cloneCsn.js +1 -5
- package/lib/modelCompare/utils/filter.js +47 -21
- package/lib/parsers/AstBuildingParser.js +92 -73
- package/lib/parsers/CdlGrammar.g4 +110 -137
- package/lib/parsers/index.js +123 -0
- package/lib/render/toSql.js +8 -2
- package/lib/render/utils/delta.js +33 -1
- package/lib/transform/db/{transformExists.js → assocsToQueries/transformExists.js} +12 -407
- package/lib/transform/db/assocsToQueries/utils.js +440 -0
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/draft/db.js +14 -3
- package/lib/transform/effective/annotations.js +3 -3
- package/lib/transform/effective/main.js +5 -7
- package/lib/transform/featureFlags.js +5 -0
- package/lib/transform/forRelationalDB.js +125 -192
- package/lib/transform/transformUtils.js +0 -51
- package/lib/utils/objectUtils.js +13 -0
- package/package.json +2 -2
- package/lib/transform/db/featureFlags.js +0 -5
|
@@ -17,7 +17,7 @@ const { addLocalizationViewsWithJoins, addLocalizationViews } = require('../tran
|
|
|
17
17
|
const { timetrace } = require('../utils/timetrace');
|
|
18
18
|
const { createReferentialConstraints, assertConstraintIdentifierUniqueness } = require('./db/constraints');
|
|
19
19
|
const { createDict, forEach } = require('../utils/objectUtils');
|
|
20
|
-
const handleExists = require('./db/transformExists');
|
|
20
|
+
const handleExists = require('./db/assocsToQueries/transformExists');
|
|
21
21
|
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('./db/rewriteCalculatedElements');
|
|
22
22
|
const replaceAssociationsInGroupByOrderBy = require('./db/groupByOrderBy');
|
|
23
23
|
const _forEachDefinition = require('../model/csnUtils').forEachDefinition;
|
|
@@ -32,7 +32,7 @@ const temporal = require('./db/temporal');
|
|
|
32
32
|
const associations = require('./db/associations');
|
|
33
33
|
const backlinks = require('./db/backlinks');
|
|
34
34
|
const { getDefaultTypeLengths } = require('../render/utils/common');
|
|
35
|
-
const { featureFlags } = require('./
|
|
35
|
+
const { featureFlags } = require('./featureFlags');
|
|
36
36
|
const { cloneCsnNonDict, cloneFullCsn } = require('../model/cloneCsn');
|
|
37
37
|
const { processSqlServices } = require('./db/processSqlServices');
|
|
38
38
|
|
|
@@ -126,7 +126,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
126
126
|
/** @type {object} */
|
|
127
127
|
let csnUtils;
|
|
128
128
|
/** @type {object} */
|
|
129
|
-
let
|
|
129
|
+
let error; // message functions
|
|
130
130
|
/** @type {() => void} */
|
|
131
131
|
let throwWithAnyError;
|
|
132
132
|
// transformUtils
|
|
@@ -156,7 +156,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
156
156
|
|
|
157
157
|
timetrace.start('Validate');
|
|
158
158
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
159
|
-
|
|
159
|
+
validate.forRelationalDB(csn, {
|
|
160
160
|
...messageFunctions, csnUtils, ...csnUtils, csn, options, isAspect
|
|
161
161
|
});
|
|
162
162
|
timetrace.stop('Validate');
|
|
@@ -194,8 +194,20 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
194
194
|
bindCsnReference();
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
|
|
197
198
|
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
198
|
-
|
|
199
|
+
applyTransformations(csn, {
|
|
200
|
+
_art: killProp,
|
|
201
|
+
_links: killProp,
|
|
202
|
+
_element: killProp,
|
|
203
|
+
_column: killProp,
|
|
204
|
+
_from: killProp,
|
|
205
|
+
_type: killProp,
|
|
206
|
+
_target: killProp,
|
|
207
|
+
$env: killProp,
|
|
208
|
+
$path: killProp,
|
|
209
|
+
$scope: killProp,
|
|
210
|
+
});
|
|
199
211
|
|
|
200
212
|
bindCsnReferenceOnly();
|
|
201
213
|
|
|
@@ -305,21 +317,14 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
305
317
|
else
|
|
306
318
|
addLocalizationViews(csn, options);
|
|
307
319
|
|
|
308
|
-
!doA2J && forEachDefinition(csn, (definition, artName, prop, path) => {
|
|
309
|
-
if (definition.query && isPersistedOnDatabase(definition)) {
|
|
310
|
-
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
311
|
-
const that = { csnUtils, options, error };
|
|
312
|
-
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
|
|
316
320
|
forEachDefinition(csn, [
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
(definition, artName, prop, path) => {
|
|
322
|
+
if (!doA2J && definition.query && isPersistedOnDatabase(definition)) {
|
|
323
|
+
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
324
|
+
const that = { csnUtils, options, error };
|
|
325
|
+
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
|
|
326
|
+
}
|
|
327
|
+
},
|
|
323
328
|
// (170) Transform '$self' in backlink associations to appropriate key comparisons
|
|
324
329
|
// Must happen before draft processing because the artificial ON-conditions in generated
|
|
325
330
|
// draft shadow entities have crooked '_artifact' links, confusing the backlink processing.
|
|
@@ -355,25 +360,39 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
355
360
|
const transformEntityOrViewPass2 = getViewTransformer(csn, options, messageFunctions);
|
|
356
361
|
forEachDefinition(csn, transformViews);
|
|
357
362
|
|
|
358
|
-
const checkConstraintIdentifiers = (artifact, artifactName, prop, path) => {
|
|
359
|
-
assertConstraintIdentifierUniqueness(artifact, artifactName, path, error);
|
|
360
|
-
};
|
|
361
363
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
364
|
+
if(!doA2J) {
|
|
365
|
+
forEachDefinition(csn, [
|
|
366
|
+
// (200) Strip 'key' property from type elements
|
|
367
|
+
removeKeyPropInType,
|
|
368
|
+
(artifact, artifactName) => {
|
|
369
|
+
if(artifact.kind === 'type') {
|
|
370
|
+
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
371
|
+
// Check type parameters (length, precision, scale ...)
|
|
372
|
+
if (!member.$ignore) {
|
|
373
|
+
if (member.type)
|
|
374
|
+
checkTypeParameters(member, artifact, path);
|
|
375
|
+
if (member.items?.type)
|
|
376
|
+
checkTypeParameters(member.items, artifact, path.concat([ 'items' ]));
|
|
377
|
+
}
|
|
378
|
+
}, [ 'definitions', artifactName ]);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
]);
|
|
382
|
+
}
|
|
371
383
|
|
|
372
384
|
// TODO: Might have to do this earlier if we want special rendering for projections?
|
|
373
385
|
const findAndMarkSqlServiceArtifacts = options.sqlDialect === 'hana' && options.src === 'hdi' && csn.meta?.[featureFlags]?.$sqlService ? processSqlServices(csn): () => {}
|
|
374
386
|
|
|
375
387
|
// TODO: Could we maybe merge this with the final applyTransformations?
|
|
376
388
|
applyTransformations(csn, {
|
|
389
|
+
type: (parent, prop, type, path) => {
|
|
390
|
+
checkTypeParameters(parent, csn.definitions[path[1]], path);
|
|
391
|
+
},
|
|
392
|
+
$tableConstraints: (parent, prop, tableConstraints, path) => {
|
|
393
|
+
/* assert that there will be no conflicting unique- and foreign key constraint identifiers */
|
|
394
|
+
assertConstraintIdentifierUniqueness(parent, path[1], path, error);
|
|
395
|
+
},
|
|
377
396
|
elements: (parent, prop, elements, path) => {
|
|
378
397
|
// Attach @cds.persistence.name to elements, replace enums by their base value
|
|
379
398
|
const artifact = csn.definitions[path[1]];
|
|
@@ -485,7 +504,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
485
504
|
|
|
486
505
|
function bindCsnReference(){
|
|
487
506
|
messageFunctions.setModel(csn);
|
|
488
|
-
({ error,
|
|
507
|
+
({ error, throwWithAnyError } = messageFunctions);
|
|
489
508
|
|
|
490
509
|
({ flattenStructuredElement,
|
|
491
510
|
flattenStructStepsInRef,
|
|
@@ -614,79 +633,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
614
633
|
}
|
|
615
634
|
}
|
|
616
635
|
|
|
617
|
-
/**
|
|
618
|
-
* @param {CSN.Artifact} artifact
|
|
619
|
-
* @param {string} artifactName
|
|
620
|
-
*/
|
|
621
|
-
function checkAssocsWithParams(artifact, artifactName) {
|
|
622
|
-
if(options.transformation === 'hdbcds') {
|
|
623
|
-
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
624
|
-
// Report an error on
|
|
625
|
-
// - view with parameters that has an element of type association/composition
|
|
626
|
-
// - association that points to entity with parameters
|
|
627
|
-
if (member.target && csnUtils.isAssocOrComposition(member)) {
|
|
628
|
-
if (artifact.params) {
|
|
629
|
-
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
630
|
-
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
631
|
-
message('def-unexpected-paramview-assoc', path, { '#': 'source' });
|
|
632
|
-
}
|
|
633
|
-
else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
|
|
634
|
-
// UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
|
|
635
|
-
const anno = artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
636
|
-
message('def-unexpected-calcview-assoc', path, { '#': 'source', anno });
|
|
637
|
-
}
|
|
638
|
-
if (csn.definitions[member.target].params) {
|
|
639
|
-
// HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
|
|
640
|
-
// SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
|
|
641
|
-
message('def-unexpected-paramview-assoc', path, { '#': 'target' });
|
|
642
|
-
}
|
|
643
|
-
else if(csn.definitions[member.target]['@cds.persistence.udf'] || csn.definitions[member.target]['@cds.persistence.calcview']) {
|
|
644
|
-
// HANA won't check the assoc target but when querying an association with target UDF, this is the error:
|
|
645
|
-
// SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
|
|
646
|
-
// CREATE TABLE F (id INTEGER NOT NULL);
|
|
647
|
-
// CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
|
|
648
|
-
// CREATE TABLE Y ( id INTEGER NOT NULL, toUDF_id INTEGER) WITH ASSOCIATIONS (MANY TO ONE JOIN UDF AS toUDF ON (toUDF.id = toUDF_id));
|
|
649
|
-
// CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
|
|
650
|
-
const anno = csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
651
|
-
message('def-unexpected-calcview-assoc', path, { '#': 'target', anno });
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
}, [ 'definitions', artifactName ]);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* @param {CSN.Artifact} artifact
|
|
661
|
-
* @param {string} artifactName
|
|
662
|
-
*/
|
|
663
|
-
function handleChecksForWithParameters(artifact, artifactName) {
|
|
664
|
-
if (!artifact.$ignore && artifact.params && (artifact.kind === 'entity')) {
|
|
665
|
-
if (!artifact.query) { // table entity with params
|
|
666
|
-
// Allow with plain
|
|
667
|
-
error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
|
|
668
|
-
std: 'Table-like entities with parameters are not supported for conversion to SAP HANA CDS',
|
|
669
|
-
sql: 'Table-like entities with parameters are not supported for conversion to SQL',
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
else if (options.sqlDialect === 'sqlite') { // view with params
|
|
673
|
-
// Allow with plain
|
|
674
|
-
error(null, [ 'definitions', artifactName ], 'SQLite does not support entities with parameters');
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
for (const pname in artifact.params) {
|
|
678
|
-
if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
|
|
679
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ], 'Expecting regular SQL-Identifier');
|
|
680
|
-
}
|
|
681
|
-
else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
|
|
682
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
|
|
683
|
-
'Expecting parameter to be uppercase in naming mode $(NAME)');
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
636
|
function handleAssocToJoins() {
|
|
691
637
|
// the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
|
|
692
638
|
// simply make it invisible and copy it over to the result csn
|
|
@@ -806,105 +752,92 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
806
752
|
}
|
|
807
753
|
}
|
|
808
754
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
755
|
/**
|
|
813
|
-
*
|
|
814
|
-
|
|
815
|
-
* @
|
|
756
|
+
* Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
|
|
757
|
+
|
|
758
|
+
* @param {*} node
|
|
759
|
+
* @param {*} nodeName
|
|
760
|
+
* @param {*} model
|
|
761
|
+
* @param {*} path
|
|
816
762
|
*/
|
|
817
|
-
function checkTypeParameters(artifact,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
switch (absolute) {
|
|
833
|
-
case 'cds.String':
|
|
834
|
-
case 'cds.Binary':
|
|
835
|
-
case 'cds.hana.VARCHAR': {
|
|
836
|
-
checkTypeParamValue(node, 'length', { min: 1, max: 5000 }, path);
|
|
837
|
-
break;
|
|
838
|
-
}
|
|
839
|
-
case 'cds.Decimal': {
|
|
840
|
-
// Don't check with "plain"?
|
|
841
|
-
if (node.precision || node.scale) {
|
|
842
|
-
checkTypeParamValue(node, 'precision', { max: 38 }, path);
|
|
843
|
-
checkTypeParamValue(node, 'scale', { max: node.precision }, path);
|
|
844
|
-
}
|
|
845
|
-
break;
|
|
763
|
+
function checkTypeParameters(node, artifact, path) {
|
|
764
|
+
if (node.type && !node.virtual) {
|
|
765
|
+
const absolute = node.type;
|
|
766
|
+
switch (absolute) {
|
|
767
|
+
case 'cds.String':
|
|
768
|
+
case 'cds.Binary':
|
|
769
|
+
case 'cds.hana.VARCHAR': {
|
|
770
|
+
checkTypeParamValue(node, 'length', { min: 1, max: 5000 }, path);
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
case 'cds.Decimal': {
|
|
774
|
+
// Don't check with "plain"?
|
|
775
|
+
if (node.precision || node.scale) {
|
|
776
|
+
checkTypeParamValue(node, 'precision', { max: 38 }, path);
|
|
777
|
+
checkTypeParamValue(node, 'scale', { max: node.precision }, path);
|
|
846
778
|
}
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
847
781
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
782
|
+
case 'cds.hana.BINARY':
|
|
783
|
+
case 'cds.hana.NCHAR':
|
|
784
|
+
case 'cds.hana.CHAR': {
|
|
785
|
+
checkTypeParamValue(node, 'length', { min: 1, max: 2000 }, path);
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
case 'cds.hana.ST_POINT':
|
|
789
|
+
case 'cds.hana.ST_GEOMETRY': {
|
|
790
|
+
checkTypeParamValue(node, 'srid', { max: Number.MAX_SAFE_INTEGER }, path);
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
case 'cds.Map': {
|
|
794
|
+
if (options.sqlDialect === 'plain')
|
|
795
|
+
error('ref-unsupported-type', path, { '#': 'dialect', type: node.type, value: 'plain' });
|
|
796
|
+
else if (options.transformation === 'hdbcds')
|
|
797
|
+
error('ref-unsupported-type', path, {'#': 'hdbcds', type: node.type, value: options.sqlDialect });
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
case 'cds.Vector': {
|
|
801
|
+
if (options.sqlDialect !== 'hana') {
|
|
802
|
+
error('ref-unsupported-type', path, {
|
|
803
|
+
'#': 'hana', type: node.type, value: 'hana',
|
|
804
|
+
othervalue: options.sqlDialect
|
|
805
|
+
});
|
|
865
806
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
othervalue: options.sqlDialect
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
else if (options.transformation === 'hdbcds') {
|
|
874
|
-
error('ref-unsupported-type', path, {
|
|
875
|
-
'#': 'hdbcds', type: node.type, value: options.sqlDialect
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
break;
|
|
807
|
+
else if (options.transformation === 'hdbcds') {
|
|
808
|
+
error('ref-unsupported-type', path, {
|
|
809
|
+
'#': 'hdbcds', type: node.type, value: options.sqlDialect
|
|
810
|
+
});
|
|
879
811
|
}
|
|
812
|
+
break;
|
|
880
813
|
}
|
|
881
814
|
}
|
|
815
|
+
}
|
|
882
816
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
817
|
+
// Check that the value of the type property `paramName` (e.g. length, precision, scale ...) is of `expectedType`
|
|
818
|
+
// (which can currently only be 'positiveInteger') and (optional) the value is in a given range
|
|
819
|
+
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
820
|
+
const paramValue = node[paramName];
|
|
821
|
+
if (paramValue == null) {
|
|
822
|
+
if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
|
|
823
|
+
return true;
|
|
824
|
+
} else {
|
|
825
|
+
return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
|
|
893
826
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
827
|
+
}
|
|
828
|
+
if (range) {
|
|
829
|
+
if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
|
|
830
|
+
error('type-unexpected-argument', path,
|
|
831
|
+
{ '#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false });
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
if (range.min && paramValue < range.min) {
|
|
835
|
+
error('type-unexpected-argument', path,
|
|
836
|
+
{ '#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false });
|
|
837
|
+
return false;
|
|
905
838
|
}
|
|
906
|
-
return true;
|
|
907
839
|
}
|
|
840
|
+
return true;
|
|
908
841
|
}
|
|
909
842
|
}
|
|
910
843
|
|
|
@@ -39,7 +39,6 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
39
39
|
resolvePath,
|
|
40
40
|
flattenPath,
|
|
41
41
|
addDefaultTypeFacets,
|
|
42
|
-
createForeignKeyElement,
|
|
43
42
|
getForeignKeyArtifact,
|
|
44
43
|
flattenStructuredElement,
|
|
45
44
|
flattenStructStepsInRef,
|
|
@@ -153,56 +152,6 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
153
152
|
return [ foreignKeyElementName, createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName) ];
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
// (1) Create an artificial foreign key element for association 'assoc' (possibly part
|
|
157
|
-
// of nested struct, i.e. containing dots) in 'artifact', using foreign key info
|
|
158
|
-
// from 'foreignKey'.
|
|
159
|
-
// (2) Inserting it into 'elements' of 'artifact'.
|
|
160
|
-
// (3) Add a property '$generatedFieldName' to the corresponding 'foreignKey' of the assoc.
|
|
161
|
-
//
|
|
162
|
-
// Note that this must happen after struct flattening(flattenStructuredElement) - both fot elements and foreign keys.
|
|
163
|
-
// Return the newly generated foreign key element.
|
|
164
|
-
function createForeignKeyElement(assoc, assocName, foreignKey, artifact, artifactName, path) {
|
|
165
|
-
const result = {};
|
|
166
|
-
|
|
167
|
-
// FIXME: Duplicate code (postfix is added herein, can this be optimized?)
|
|
168
|
-
// Assemble foreign key element name from assoc name, '_' and foreign key name/alias
|
|
169
|
-
const foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const fkArtifact = inspectRef(path).art;
|
|
173
|
-
newForeignKey(fkArtifact, foreignKeyElementName);
|
|
174
|
-
|
|
175
|
-
function processAssociationOrComposition(fkArtifact,foreignKeyElementName) {
|
|
176
|
-
fkArtifact.keys.forEach(iKey => {
|
|
177
|
-
const iKeyArtifact = inspectRef(iKey.$path).art;
|
|
178
|
-
if(iKey.ref.length>1)
|
|
179
|
-
throw new CompilerAssertion(`createForeignKeyElement(${artifactName},${assocName},${iKey.$path.join('/')}) unexpected reference: `+ iKey.ref)
|
|
180
|
-
newForeignKey(iKeyArtifact,foreignKeyElementName+'_'+iKey.ref[0])
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// compose new foreign key out of 'fkArtifact' named 'foreignKeyElementName'
|
|
185
|
-
function newForeignKey(fkArtifact, foreignKeyElementName) {
|
|
186
|
-
if (fkArtifact.type === 'cds.Association' || fkArtifact.type === 'cds.Composition') {
|
|
187
|
-
processAssociationOrComposition(fkArtifact, foreignKeyElementName)
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);
|
|
192
|
-
|
|
193
|
-
// FIXME: must this code go into createRealFK?
|
|
194
|
-
// Not present in getForeignKeyArtifact
|
|
195
|
-
if (artifact.items) // proceed to items of such
|
|
196
|
-
artifact = artifact.items;
|
|
197
|
-
|
|
198
|
-
// Insert artificial element into artifact, with all cross-links
|
|
199
|
-
artifact.elements[foreignKeyElementName] = foreignKeyElement;
|
|
200
|
-
|
|
201
|
-
result[foreignKeyElementName] = foreignKeyElement;
|
|
202
|
-
} // function newForeignKey
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
155
|
// For a structured element 'elem', return a dictionary of flattened elements to
|
|
207
156
|
// replace it, flattening names with pathDelimiter's value and propagating all annotations and the
|
|
208
157
|
// type properties 'key', 'notNull', 'virtual', 'masked' to the flattened elements.
|
package/lib/utils/objectUtils.js
CHANGED
|
@@ -83,6 +83,18 @@ function setHidden( obj, prop, val ) {
|
|
|
83
83
|
} );
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Check if the given object has the property as non-enumerable
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} object
|
|
90
|
+
* @param {string} propertyName
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function hasNonEnumerable( object, propertyName ) {
|
|
94
|
+
return Object.prototype.hasOwnProperty.call( object, propertyName ) &&
|
|
95
|
+
!Object.prototype.propertyIsEnumerable.call( object, propertyName );
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
module.exports = {
|
|
87
99
|
copyPropIfExist,
|
|
88
100
|
createDict,
|
|
@@ -90,4 +102,5 @@ module.exports = {
|
|
|
90
102
|
forEachValue,
|
|
91
103
|
forEachKey,
|
|
92
104
|
setHidden,
|
|
105
|
+
hasNonEnumerable,
|
|
93
106
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.2",
|
|
4
4
|
"description": "CDS (Core Data Services) compiler and backends",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"types": "lib/main.d.ts",
|
|
16
16
|
"scripts": {
|
|
17
17
|
"download": "node scripts/downloadANTLR.js",
|
|
18
|
-
"gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js && node ./redepage/bin/redepage --
|
|
18
|
+
"gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js && node ./redepage/bin/redepage --compile lib/gen/CdlParser.js --copy-base-parser lib/parsers/CdlGrammar.g4",
|
|
19
19
|
"xmakeAfterInstall": "npm run gen",
|
|
20
20
|
"xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
|
|
21
21
|
"test": "npm run test:piper",
|