@sap/cds-compiler 4.2.4 → 4.3.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 (66) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +7 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +9 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +42 -38
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
@@ -4,7 +4,7 @@ const { setProp, isBetaEnabled } = require('../base/model');
4
4
  const { cloneCsnNonDict,
5
5
  forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
6
6
  getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
7
- isAspect, walkCsnPath, isPersistedOnDatabase,
7
+ isAspect, walkCsnPath, isPersistedOnDatabase
8
8
  } = require('../model/csnUtils');
9
9
  const { makeMessageFunction } = require('../base/messages');
10
10
  const transformUtils = require('./transformUtils');
@@ -29,8 +29,8 @@ const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
29
29
  const { getViewTransformer, ensureColumnNames } = require('./db/views');
30
30
  const cdsPersistence = require('./db/cdsPersistence');
31
31
  const temporal = require('./db/temporal');
32
- const associations = require('./db/associations')
33
- const { ModelError } = require('../base/error');
32
+ const associations = require('./db/associations');
33
+ const backlinks = require('./db/backlinks');
34
34
  const { getDefaultTypeLengths } = require('../render/utils/common');
35
35
 
36
36
  // By default: Do not process non-entities/views
@@ -125,12 +125,10 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
125
125
  /** @type {() => void} */
126
126
  let throwWithAnyError;
127
127
  // transformUtils
128
- let addDefaultTypeFacets,
128
+ let addDefaultTypeFacets,
129
129
  expandStructsInExpression,
130
130
  flattenStructuredElement,
131
- flattenStructStepsInRef,
132
- isAssociationOperand,
133
- isDollarSelfOrProjectionOperand;
131
+ flattenStructStepsInRef;
134
132
 
135
133
  bindCsnReference();
136
134
 
@@ -158,6 +156,9 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
158
156
  });
159
157
  timetrace.stop('Validate');
160
158
 
159
+ // exit if validators found errors
160
+ throwWithAnyError();
161
+
161
162
  rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
162
163
 
163
164
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
@@ -174,8 +175,6 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
174
175
  // @ts-ignore
175
176
  expandStructsInExpression(csn, { drillRef: true });
176
177
 
177
- throwWithAnyError();
178
-
179
178
  forEachDefinition(csn, [
180
179
  // (001) Add a temporal where condition to views where applicable before assoc2join
181
180
  // assoc2join eventually rewrites the table aliases
@@ -215,6 +214,9 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
215
214
  flattening.resolveTypeReferences(csn, options, new WeakMap(), pathDelimiter);
216
215
  }
217
216
 
217
+ // With flattening errors, it makes little sense to continue.
218
+ throwWithAnyError();
219
+
218
220
  // (010) If requested, translate associations to joins
219
221
  if (doA2J)
220
222
  handleAssocToJoins();
@@ -335,7 +337,7 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
335
337
  // draft shadow entities have crooked '_artifact' links, confusing the backlink processing.
336
338
  // But it must also happen after flattenForeignKeys has been called for all artifacts,
337
339
  // because otherwise we would produce wrong ON-conditions for the keys involved. Sigh ...
338
- forEachDefinition(csn, transformSelfInBacklinks);
340
+ forEachDefinition(csn, backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, pathDelimiter, doA2J));
339
341
 
340
342
  /**
341
343
  * Referential Constraints are only supported for sql-dialect "hana" and "sqlite".
@@ -451,8 +453,6 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
451
453
 
452
454
  ({ flattenStructuredElement,
453
455
  flattenStructStepsInRef,
454
- isAssociationOperand,
455
- isDollarSelfOrProjectionOperand,
456
456
  addDefaultTypeFacets,
457
457
  expandStructsInExpression,
458
458
  csnUtils
@@ -599,28 +599,6 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
599
599
  }
600
600
  }
601
601
 
602
-
603
- /**
604
- * @param {CSN.Artifact} artifact
605
- * @param {string} artifactName
606
- */
607
- function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
608
- // Fixme: For toHana mixins must be transformed, for toSql -d hana
609
- // mixin elements must be transformed, why can't toSql also use mixins?
610
- if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
611
- doit(artifact.elements, path.concat([ 'elements' ]));
612
- if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
613
- doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
614
-
615
- function doit(dict, subPath) {
616
- for (const elemName in dict) {
617
- const elem = dict[elemName];
618
- if (elem.on && csnUtils.isAssocOrComposition(elem))
619
- processBacklinkAssoc(elem, elemName, artifact, artifactName, subPath.concat([ elemName, 'on' ]));
620
- }
621
- }
622
- }
623
-
624
602
  /**
625
603
  * @param {CSN.Artifact} artifact
626
604
  * @param {string} artifactName
@@ -700,8 +678,6 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
700
678
  }
701
679
 
702
680
  function handleAssocToJoins() {
703
- // With flattening errors, it makes little sense to continue.
704
- throwWithAnyError();
705
681
  // the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
706
682
  // simply make it invisible and copy it over to the result csn
707
683
  forEachDefinition(csn,
@@ -838,206 +814,7 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
838
814
  }
839
815
 
840
816
 
841
- // If the association element 'elem' of 'art' is a backlink association, massage its ON-condition
842
- // (in place) so that it
843
- // - compares the generated foreign key fields of the corresponding forward
844
- // association with their respective keys in 'art' (for managed forward associations)
845
- // - contains the corresponding forward association's ON-condition in "reversed" form,
846
- // i.e. as seen from 'elem' (for unmanaged associations)
847
- // Otherwise, do nothing.
848
- function processBacklinkAssoc(elem, elemName, art, artName, pathToOn) {
849
- // Don't add braces if it is a single expression (ignoring superfluous braces)
850
- const multipleExprs = elem.on.filter(x => x !== '(' && x !== ')' ).length > 3;
851
- /**
852
- * Process the args
853
- *
854
- * @param {Array} xprArgs
855
- * @param {CSN.Path} path
856
- * @returns {Array} Array of parsed expression
857
- */
858
- function processExpressionArgs(xprArgs, path) {
859
- const result = [];
860
- let i = 0;
861
- while (i < xprArgs.length) {
862
- // Only token tripel `<path>, '=', <path>` are of interest here
863
- if (i < xprArgs.length - 2 && xprArgs[i + 1] === '=') {
864
- // Check if one side is $self and the other an association
865
- // (if so, replace all three tokens with the condition generated from the other side, in parentheses)
866
- if (isDollarSelfOrProjectionOperand(xprArgs[i]) && isAssociationOperand(xprArgs[i + 2], path.concat([ i + 2 ]))) {
867
- const assoc = csnUtils.inspectRef(path.concat([ i + 2 ])).art;
868
- if (multipleExprs)
869
- result.push('(');
870
- const backlinkName = xprArgs[i + 2].ref[xprArgs[i + 2].ref.length - 1];
871
- result.push(...transformDollarSelfComparison(xprArgs[i + 2],
872
- assoc,
873
- backlinkName,
874
- elem, elemName, art, artName, path.concat([ i ])
875
- ));
876
- if (multipleExprs)
877
- result.push(')');
878
- i += 3;
879
- attachBacklinkInformation(backlinkName);
880
- }
881
- else if (isDollarSelfOrProjectionOperand(xprArgs[i + 2]) && isAssociationOperand(xprArgs[i], path.concat([ i ]))) {
882
- const assoc = csnUtils.inspectRef(path.concat([ i ])).art;
883
- if (multipleExprs)
884
- result.push('(');
885
- const backlinkName = xprArgs[i].ref[xprArgs[i].ref.length - 1];
886
- result.push(...transformDollarSelfComparison(xprArgs[i], assoc, backlinkName, elem, elemName, art, artName, path.concat([ i + 2 ])));
887
- if (multipleExprs)
888
- result.push(')');
889
- i += 3;
890
- attachBacklinkInformation(backlinkName);
891
- }
892
- // Otherwise take one (!) token unchanged
893
- else {
894
- result.push(xprArgs[i]);
895
- i++;
896
- }
897
- }
898
- // Process subexpressions - but keep them as subexpressions
899
- else if(xprArgs[i].xpr){
900
- result.push({xpr: processExpressionArgs(xprArgs[i].xpr, path.concat([i, 'xpr']))});
901
- i++;
902
- }
903
- // Take all other tokens unchanged
904
- else {
905
- result.push(xprArgs[i]);
906
- i++;
907
- }
908
- }
909
- return result;
910
-
911
- /**
912
- * The knowledge whether an association was an `<up_>` association in a
913
- * `$self = <comp>.<up_>` comparison, is important for the foreign key constraints.
914
- * By the time we generate them, such on-conditions are already transformed
915
- * --> no more `$self` in the on-conditions, that is why we need to remember it here.
916
- *
917
- * @param {string} backlinkName name of `<up_>` in a `$self = <comp>.<up_>` comparison
918
- */
919
- function attachBacklinkInformation(backlinkName) {
920
- if (elem.$selfOnCondition)
921
- elem.$selfOnCondition.up_.push(backlinkName);
922
- else {
923
- setProp(elem, '$selfOnCondition', {
924
- up_: [backlinkName]
925
- });
926
- }
927
- }
928
- }
929
-
930
- elem.on = processExpressionArgs(elem.on, pathToOn);
931
-
932
- // Return the condition to replace the comparison `<assocOp> = $self` in the ON-condition
933
- // of element <elem> of artifact 'art'. If there is anything to complain, use location <loc>
934
- function transformDollarSelfComparison(assocOp, assoc, assocName, elem, elemName, art, artifactName, path) {
935
- // Check: The forward link <assocOp> must point back to this artifact
936
- // FIXME: Unfortunately, we can currently only check this for non-views (because when a view selects
937
- // a backlink association element from an entity, the forward link will point to the entity,
938
- // not to the view).
939
- // FIXME: This also means that corresponding key fields should be in the select list etc ...
940
- if (!art.query && !art.projection && assoc.target && assoc.target !== artifactName)
941
- error( null, path, { id: '$self', name: artifactName, target: assoc.target },
942
- 'Expected association using $(ID) to point back to $(NAME) but found $(TARGET)' );
943
-
944
- // Check: The forward link <assocOp> must not contain '$self' in its own ON-condition
945
- if (assoc.on) {
946
- const containsDollarSelf = assoc.on.some(isDollarSelfOrProjectionOperand);
947
-
948
- if (containsDollarSelf)
949
- error(null, path, { name: '$self' },
950
- 'An association that uses $(NAME) in its ON-condition can\'t be compared to $(NAME)');
951
- }
952
-
953
- // Transform comparison of $self to managed association into AND-combined foreign key comparisons
954
- if (assoc.keys) {
955
- if(assoc.keys.length)
956
- return transformDollarSelfComparisonWithManagedAssoc(assocOp, assoc, assocName, elemName);
957
- else {
958
- elem.$ignore = true;
959
- return [];
960
- }
961
- }
962
-
963
- // Transform comparison of $self to unmanaged association into "reversed" ON-condition
964
- else if (assoc.on)
965
- return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName);
966
-
967
- throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
968
- }
969
817
 
970
- // For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
971
- // where <assoc> is a managed association, return a condition comparing the generated
972
- // foreign key elements <elemName>.<assoc>_<fkey1..n> of <assoc> to the corresponding
973
- // keys in this artifact.
974
- // For example, `ON elem.ass = $self` becomes `ON elem.ass_key1 = key1 AND elem.ass_key2 = key2`
975
- // (assuming that `ass` has the foreign keys `key1` and `key2`)
976
- function transformDollarSelfComparisonWithManagedAssoc(assocOp, assoc, originalAssocName, elemName) {
977
- const conditions = [];
978
- // if the element was structured then it was flattened => change of the delimiter from '.' to '_'
979
- // this is done in the flattening, but as we do not alter the onCond itself there should be done here as well
980
- const assocName = originalAssocName.replace(/\./g, pathDelimiter);
981
- elemName = elemName.replace(/\./g, pathDelimiter);
982
-
983
- assoc.keys.forEach((k) => {
984
- // Depending on naming conventions, the foreign key may two path steps (hdbcds) or be a single path step with a flattened name (plain, quoted)
985
- // With to.hdbcds in conjunction with hdbcds naming, we need to NOT use the alias - else we get deployment errors
986
- const keyName = k.as && doA2J ? [k.as] : k.ref;
987
- const fKeyPath = !doA2J ? [ assocName, ...keyName ] : [ `${ assocName }${ pathDelimiter }${ keyName[0] }` ];
988
- // FIXME: _artifact to the args ???
989
- const a = [
990
- {
991
- ref: [ elemName, ...fKeyPath ],
992
- },
993
- { ref: k.ref },
994
- ];
995
-
996
- conditions.push([ a[0], '=', a[1] ]);
997
- });
998
-
999
- return conditions.reduce((prev, current) => {
1000
- if (prev.length === 0)
1001
- return [ ...current ];
1002
-
1003
- return [ ...prev, 'and', ...current ];
1004
- }, []);
1005
- }
1006
-
1007
- // For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
1008
- // where <assoc> is an unmanaged association, return the ON-condition of <assoc> as it would
1009
- // be written from the perspective of the artifact containing association <elemName>.
1010
- // For example, `ON elem.ass = $self` becomes `ON a = elem.x AND b = elem.y`
1011
- // (assuming that `ass` has the ON-condition `ON ass.a = x AND ass.b = y`)
1012
- function transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, originalAssocName, elemName) {
1013
- // if the element was structured then it may have been flattened => change of the delimiter from '.' to '_'
1014
- // this is done in the flattening, but as we do not alter the onCond itself there should be done here as well
1015
- elemName = elemName.replace(/\./g, pathDelimiter);
1016
- const assocName = originalAssocName.replace(/\./g, pathDelimiter);
1017
- // clone the onCond for later use in the path transformation
1018
- const newOnCond = cloneCsnNonDict(assoc.on, options);
1019
- applyTransformationsOnNonDictionary({on: newOnCond}, 'on', {
1020
- ref: (parent, prop, ref) => {
1021
- if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
1022
- {
1023
- ref.shift();
1024
- } else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
1025
- // We could also have a $self in front of the assoc name - so we would need to shift twice
1026
- ref.shift();
1027
- ref.shift();
1028
- }
1029
- else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
1030
- ref.unshift(elemName);
1031
- // if there was a $self identifier in the forwarding association onCond
1032
- // we do not need it anymore, as we prepended in the previous step the back association's id
1033
- if (ref[1] === '$self')
1034
- ref.splice(1, 1);
1035
- }
1036
- }
1037
- });
1038
- return newOnCond;
1039
- }
1040
- }
1041
818
 
1042
819
  /**
1043
820
  * @param {CSN.Artifact} artifact
@@ -395,8 +395,7 @@ function _addLocalizationViews(csn, options, config) {
395
395
  }
396
396
  if (!art[annoPersistenceSkip] && textsEntity[annoPersistenceSkip]) {
397
397
  messageFunctions.message( 'anno-unexpected-localized-skip', artPath,
398
- { name: textsName, art: artName, anno: annoPersistenceSkip },
399
- 'Compiler generated entity $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped' );
398
+ { name: textsName, art: artName, anno: annoPersistenceSkip } );
400
399
  return null;
401
400
  }
402
401
 
@@ -654,8 +653,28 @@ function _addLocalizationViews(csn, options, config) {
654
653
  }
655
654
  },
656
655
  });
656
+ forEachDefinition(csn, checkAnnotationsOnLocalized);
657
657
  }
658
658
 
659
+ /**
660
+ * @param {CSN.Definition} def
661
+ * @param {string} defName
662
+ */
663
+ function checkAnnotationsOnLocalized(def, defName) {
664
+ const localizedPrefix = 'localized.';
665
+ if (defName.startsWith(localizedPrefix)) {
666
+ const artName = defName.substring(localizedPrefix.length);
667
+ const art = csn.definitions[artName];
668
+ if (def[annoPersistenceSkip] && !art?.[annoPersistenceSkip]) {
669
+ messageFunctions.message( 'anno-unexpected-localized-skip', ['definitions', defName], {
670
+ '#': 'view',
671
+ name: defName,
672
+ art: artName,
673
+ anno: annoPersistenceSkip
674
+ });
675
+ }
676
+ }
677
+ }
659
678
  }
660
679
 
661
680
  /**
@@ -740,7 +759,7 @@ function copyPersistenceAnnotations(target, source, options) {
740
759
  // lead to some localization views referencing non-existing ones.
741
760
  // But that is the contract: User says that it already exists!
742
761
  // In v2, `.exists` was never copied.
743
- if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
762
+ if (anno === annoPersistenceSkip || (!doNotCopyExists && anno === '@cds.persistence.exists'))
744
763
  target[anno] = source[anno];
745
764
  });
746
765
  }
@@ -167,7 +167,7 @@ function parseExpr(xpr, state = { anno: 0, array: true, nary: false }) {
167
167
  function conditionTerm(xpr, s, e, state) {
168
168
  if(Array.isArray(xpr)) {
169
169
  if(xpr.length >= 3 && xpr[s+1] === 'is') {
170
- const isnull = conditionOR(xpr[s], state);
170
+ const isnull = conditionOR(xpr[s], 0, 0, state);
171
171
  if(xpr[s+2] === 'null')
172
172
  return state.array ? [ isnull, 'is', 'null' ] : { 'isNull': isnull };
173
173
  else if(xpr[s+2] === 'not' && xpr[s+3] === 'null')
@@ -253,12 +253,16 @@ function parseExpr(xpr, state = { anno: 0, array: true, nary: false }) {
253
253
  }
254
254
 
255
255
  function exprMulDiv(xpr, s, e, state) {
256
- return binaryExpr(xpr, ['*', '/'], unary, s, e, state);
256
+ return binaryExpr(xpr, ['*', '/'], (state.array ? unary : dot), s, e, state);
257
+ }
258
+
259
+ function dot(xpr, s, e, state) {
260
+ return binaryExpr(xpr, ['.'], unary, s, e, state);
257
261
  }
258
262
 
259
263
  function unary(xpr, s, e, state) {
260
264
  if(Array.isArray(xpr)) {
261
- if(xpr[s] === '+' || xpr[s] === '-') {
265
+ if(xpr[s] === '+' || xpr[s] === '-' || (!state.array && xpr[s] === 'new')) {
262
266
  return [ xpr[s], unary(xpr, s+1, e, state) ];
263
267
  }
264
268
  }
@@ -7,7 +7,7 @@
7
7
  const { makeMessageFunction } = require('../base/messages');
8
8
  const { setProp } = require('../base/model');
9
9
 
10
- const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
10
+ const { copyAnnotations, applyTransformations, isDollarSelfOrProjectionOperand } = require('../model/csnUtils');
11
11
  const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
12
12
  const { typeParameters, isBuiltinType } = require('../compiler/builtins');
13
13
  const { ModelError, CompilerAssertion} = require('../base/error');
@@ -43,8 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
43
43
  flattenStructStepsInRef,
44
44
  toFinalBaseType,
45
45
  copyTypeProperties,
46
- isAssociationOperand,
47
- isDollarSelfOrProjectionOperand,
48
46
  createExposingProjection,
49
47
  createAndAddDraftAdminDataProjection,
50
48
  createScalarElement,
@@ -73,7 +71,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
73
71
  *
74
72
  * @param {CSN.Element} element
75
73
  * @param {null|object} [internalDefaultLengths] Either null (no implicit default) or an object `{ 'cds.String': N, 'cds.Binary': N }`.
76
- * */
74
+ **/
77
75
  function addDefaultTypeFacets(element, internalDefaultLengths = null) {
78
76
  if (!element || !element.type)
79
77
  return;
@@ -121,7 +119,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
121
119
  }
122
120
  }
123
121
 
124
- if (!options.forHana)
122
+ if (options.transformation === 'odata' || options.transformation === 'effective')
125
123
  copyAnnotations(assoc, foreignKeyElement, true);
126
124
 
127
125
  // If the association is non-fkArtifact resp. key, so should be the foreign key field
@@ -134,7 +132,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
134
132
  // Establish the relationship between generated field and association:
135
133
  // - generated field has annotation '@odata.foreignKey4'.
136
134
  // - foreign key info has 'generatedFieldName'
137
- foreignKeyElement['@odata.foreignKey4'] = assocName;
135
+ if(options.transformation !== 'effective')
136
+ foreignKeyElement['@odata.foreignKey4'] = assocName;
138
137
  foreignKey.$generatedFieldName = foreignKeyElementName;
139
138
  // attach $path to the newly created element - used for inspectRef in processAssociationOrComposition
140
139
  setProp(foreignKeyElement, '$path', path);
@@ -608,22 +607,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
608
607
  return result;
609
608
  }
610
609
 
611
- // Return true if 'arg' is an expression argument denoting "$self" || "$projection"
612
- function isDollarSelfOrProjectionOperand(arg) {
613
- return arg.ref && arg.ref.length === 1 && (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
614
- }
615
-
616
- // Return true if 'arg' is an expression argument of type association or composition
617
- function isAssociationOperand(arg, path) {
618
- if (!arg.ref) {
619
- // Not a path, hence not an association (literal, expression, function, whatever ...)
620
- return false;
621
- }
622
- const { art } = inspectRef(path);
623
- // If it has a target, it is an association or composition
624
- return art && art.target !== undefined;
625
- }
626
-
627
610
  // Create an artificial element 'elemName' of type 'cds.Association',
628
611
  // having association target 'target'. If 'isManaged' is true, take all keys
629
612
  // of 'target' as foreign keys.