@sap/cds-compiler 5.4.2 → 5.5.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/bin/cds_remove_invalid_whitespace.js +4 -4
  3. package/bin/cds_update_annotations.js +3 -3
  4. package/bin/cds_update_identifiers.js +3 -3
  5. package/lib/api/main.js +18 -30
  6. package/lib/api/validate.js +6 -1
  7. package/lib/base/lazyload.js +28 -0
  8. package/lib/base/location.js +1 -0
  9. package/lib/base/message-registry.js +53 -11
  10. package/lib/base/messages.js +17 -3
  11. package/lib/checks/{dbFeatureFlags.js → featureFlags.js} +1 -1
  12. package/lib/checks/parameters.js +61 -4
  13. package/lib/checks/validator.js +14 -6
  14. package/lib/compiler/index.js +7 -7
  15. package/lib/compiler/shared.js +29 -13
  16. package/lib/gen/BaseParser.js +345 -235
  17. package/lib/gen/CdlParser.js +4434 -4492
  18. package/lib/gen/Dictionary.json +2 -2
  19. package/lib/json/to-csn.js +3 -1
  20. package/lib/language/antlrParser.js +2 -111
  21. package/lib/main.js +16 -37
  22. package/lib/modelCompare/utils/filter.js +47 -21
  23. package/lib/parsers/AstBuildingParser.js +59 -49
  24. package/lib/parsers/CdlGrammar.g4 +91 -130
  25. package/lib/parsers/index.js +123 -0
  26. package/lib/render/toSql.js +8 -2
  27. package/lib/render/utils/delta.js +33 -1
  28. package/lib/transform/db/{transformExists.js → assocsToQueries/transformExists.js} +12 -407
  29. package/lib/transform/db/assocsToQueries/utils.js +440 -0
  30. package/lib/transform/db/expansion.js +2 -2
  31. package/lib/transform/draft/db.js +14 -3
  32. package/lib/transform/effective/annotations.js +3 -3
  33. package/lib/transform/effective/main.js +5 -7
  34. package/lib/transform/featureFlags.js +5 -0
  35. package/lib/transform/forRelationalDB.js +125 -192
  36. package/lib/transform/odata/createForeignKeys.js +1 -1
  37. package/lib/transform/odata/flattening.js +1 -1
  38. package/lib/transform/transformUtils.js +0 -51
  39. package/package.json +2 -2
  40. 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('./db/featureFlags');
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 message, error, warning; // message functions
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
- const cleanup = validate.forRelationalDB(csn, {
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
- cleanup();
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
- // For generating DB stuff:
318
- // - table-entity with parameters: not allowed
319
- // - view with parameters: ok on HANA, not allowed otherwise
320
- // (don't complain about action/function with parameters)
321
- handleChecksForWithParameters,
322
- checkAssocsWithParams,
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
- // TODO: Could we maybe merge this with the final applyTransformations?
363
- forEachDefinition(csn, [
364
- /* assert that there will be no conflicting unique- and foreign key constraint identifiers */
365
- checkConstraintIdentifiers,
366
- /* Check Type Parameters (precision, scale, length ...) */
367
- checkTypeParameters,
368
- // (200) Strip 'key' property from type elements
369
- removeKeyPropInType,
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, warning, message, throwWithAnyError } = messageFunctions);
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
- * @param {CSN.Artifact} artifact
814
- * @param {string} artifactName
815
- * @todo can we do this earlier? Together with another forEachMemberRecursively?
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, artifactName) {
818
- forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
819
- // Check type parameters (length, precision, scale ...)
820
- if (!member.$ignore) {
821
- if (member.type)
822
- _check(member, memberName, csn, path);
823
- if (member.items?.type)
824
- _check(member.items, memberName, csn, path.concat([ 'items' ]));
825
- }
826
- }, [ 'definitions', artifactName ]);
827
-
828
- // Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
829
- function _check(node, nodeName, model, path) {
830
- if (node.type && !node.virtual) {
831
- const absolute = node.type;
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
- case 'cds.hana.BINARY':
849
- case 'cds.hana.NCHAR':
850
- case 'cds.hana.CHAR': {
851
- checkTypeParamValue(node, 'length', { min: 1, max: 2000 }, path);
852
- break;
853
- }
854
- case 'cds.hana.ST_POINT':
855
- case 'cds.hana.ST_GEOMETRY': {
856
- checkTypeParamValue(node, 'srid', { max: Number.MAX_SAFE_INTEGER }, path);
857
- break;
858
- }
859
- case 'cds.Map': {
860
- if (options.sqlDialect === 'plain')
861
- error('ref-unsupported-type', path, { '#': 'dialect', type: node.type, value: 'plain' });
862
- else if (options.transformation === 'hdbcds')
863
- error('ref-unsupported-type', path, {'#': 'hdbcds', type: node.type, value: options.sqlDialect });
864
- break;
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
- case 'cds.Vector': {
867
- if (options.sqlDialect !== 'hana') {
868
- error('ref-unsupported-type', path, {
869
- '#': 'hana', type: node.type, value: 'hana',
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
- // Check that the value of the type property `paramName` (e.g. length, precision, scale ...) is of `expectedType`
884
- // (which can currently only be 'positiveInteger') and (optional) the value is in a given range
885
- function checkTypeParamValue(node, paramName, range = null, path = null) {
886
- const paramValue = node[paramName];
887
- if (paramValue == null) {
888
- if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
889
- return true;
890
- } else {
891
- return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
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
- if (range) {
895
- if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
896
- error('type-unexpected-argument', path,
897
- { '#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false });
898
- return false;
899
- }
900
- if (range.min && paramValue < range.min) {
901
- error('type-unexpected-argument', path,
902
- { '#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false });
903
- return false;
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
 
@@ -10,7 +10,7 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
10
10
  const { error } = messageFunctions;
11
11
 
12
12
  applyTransformations(csn, { elements: createForeignKeysInCsn, params: createForeignKeysInCsn},
13
- [], Object.assign(iterateOptions, { skip: ['aspect', 'event'], skipStandard: { targetAspect: true } }));
13
+ [], Object.assign(iterateOptions, { skip: ['event'], skipStandard: { targetAspect: true } }));
14
14
 
15
15
  /**
16
16
  * Process a given elements or params dictionary and create foreign key elements.
@@ -142,7 +142,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, getFinalTy
142
142
  }
143
143
  // loop through types as well in order to collect the managaed associations
144
144
  // that reside in types definitions
145
- if ((def.kind === 'action' || def.kind === 'function' || def.kind === 'type') && !isExternalServiceMember(def, defName)) {
145
+ if ((def.kind === 'action' || def.kind === 'function' || def.kind === 'type' || def.kind === 'aspect') && !isExternalServiceMember(def, defName)) {
146
146
  if (def.kind === 'type' && csnUtils.isManagedAssociation(def))
147
147
  allMgdAssocDefs.push(def);
148
148
  else
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "5.4.2",
3
+ "version": "5.5.0",
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 --pretty --compile lib/gen/CdlParser.js --copy-base-parser lib/parsers/CdlGrammar.g4",
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",
@@ -1,5 +0,0 @@
1
- 'use strict';
2
-
3
- const featureFlags = Symbol.for('Feature flags forRelationalDb');
4
-
5
- module.exports = { featureFlags };