@sap/cds-compiler 5.8.2 → 5.9.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/cds_remove_invalid_whitespace.js +5 -3
  3. package/bin/cds_update_identifiers.js +9 -6
  4. package/bin/cdsc.js +79 -59
  5. package/bin/cdsse.js +14 -10
  6. package/bin/cdsv2m.js +3 -1
  7. package/lib/api/options.js +28 -6
  8. package/lib/base/message-registry.js +15 -4
  9. package/lib/checks/validator.js +3 -0
  10. package/lib/compiler/base.js +1 -1
  11. package/lib/compiler/checks.js +70 -50
  12. package/lib/compiler/extend.js +1 -1
  13. package/lib/compiler/generate.js +8 -2
  14. package/lib/compiler/index.js +1 -1
  15. package/lib/compiler/lsp-api.js +1 -1
  16. package/lib/compiler/propagator.js +2 -2
  17. package/lib/compiler/resolve.js +78 -31
  18. package/lib/compiler/shared.js +3 -3
  19. package/lib/compiler/tweak-assocs.js +1 -1
  20. package/lib/compiler/utils.js +10 -0
  21. package/lib/compiler/xpr-rewrite.js +1 -1
  22. package/lib/edm/annotations/edmJson.js +42 -39
  23. package/lib/edm/annotations/genericTranslation.js +55 -55
  24. package/lib/edm/annotations/preprocessAnnotations.js +5 -5
  25. package/lib/edm/csn2edm.js +16 -16
  26. package/lib/edm/edm.js +62 -62
  27. package/lib/edm/edmAnnoPreprocessor.js +2 -2
  28. package/lib/edm/edmInboundChecks.js +1 -1
  29. package/lib/edm/edmPreprocessor.js +32 -32
  30. package/lib/edm/edmUtils.js +8 -8
  31. package/lib/gen/CdlGrammar.checksum +1 -1
  32. package/lib/gen/CdlParser.js +77 -81
  33. package/lib/gen/Dictionary.json +3062 -3072
  34. package/lib/gen/language.checksum +1 -1
  35. package/lib/gen/language.interp +1 -1
  36. package/lib/gen/languageParser.js +1238 -1236
  37. package/lib/json/from-csn.js +1 -1
  38. package/lib/json/to-csn.js +30 -3
  39. package/lib/language/genericAntlrParser.js +16 -0
  40. package/lib/main.d.ts +79 -1
  41. package/lib/model/csnRefs.js +12 -5
  42. package/lib/model/xprAsTree.js +71 -0
  43. package/lib/modelCompare/utils/filter.js +1 -1
  44. package/lib/optionProcessor.js +46 -32
  45. package/lib/parsers/CdlGrammar.g4 +33 -28
  46. package/lib/parsers/Lexer.js +1 -1
  47. package/lib/parsers/XprTree.js +25 -16
  48. package/lib/render/toCdl.js +902 -414
  49. package/lib/render/toHdbcds.js +1 -1
  50. package/lib/render/toSql.js +8 -0
  51. package/lib/render/utils/common.js +2 -2
  52. package/lib/render/utils/operators.js +160 -0
  53. package/lib/render/utils/pretty.js +337 -0
  54. package/lib/sql-identifier.js +7 -9
  55. package/lib/transform/addTenantFields.js +39 -41
  56. package/lib/transform/db/applyTransformations.js +4 -4
  57. package/lib/transform/db/assertUnique.js +6 -5
  58. package/lib/transform/db/associations.js +3 -3
  59. package/lib/transform/db/assocsToQueries/transformExists.js +13 -13
  60. package/lib/transform/db/assocsToQueries/utils.js +8 -0
  61. package/lib/transform/db/backlinks.js +19 -14
  62. package/lib/transform/db/constraints.js +6 -6
  63. package/lib/transform/db/expansion.js +1 -1
  64. package/lib/transform/db/flattening.js +2 -2
  65. package/lib/transform/db/groupByOrderBy.js +1 -1
  66. package/lib/transform/db/processSqlServices.js +3 -3
  67. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  68. package/lib/transform/db/temporal.js +7 -9
  69. package/lib/transform/db/views.js +6 -6
  70. package/lib/transform/draft/odata.js +2 -0
  71. package/lib/transform/effective/annotations.js +1 -1
  72. package/lib/transform/effective/associations.js +1 -1
  73. package/lib/transform/effective/main.js +1 -0
  74. package/lib/transform/effective/service.js +2 -2
  75. package/lib/transform/forRelationalDB.js +11 -5
  76. package/lib/transform/localized.js +2 -0
  77. package/lib/transform/odata/adaptAnnotationRefs.js +10 -9
  78. package/lib/transform/parseExpr.js +2 -2
  79. package/lib/transform/transformUtils.js +9 -7
  80. package/lib/transform/translateAssocsToJoins.js +0 -2
  81. package/lib/transform/universalCsn/coreComputed.js +2 -2
  82. package/lib/utils/moduleResolve.js +7 -5
  83. package/package.json +1 -1
  84. package/share/messages/def-upcoming-virtual-change.md +55 -0
  85. package/share/messages/file-unexpected-case-mismatch.md +61 -0
  86. package/share/messages/message-explanations.json +2 -0
  87. package/lib/transform/braceExpression.js +0 -77
@@ -167,14 +167,17 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
167
167
  if(csn.meta?.[featureFlags]?.$calculatedElements)
168
168
  rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
169
169
 
170
+ timetrace.start('Where-Exists handling');
170
171
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
171
172
  handleExists(csn, options, error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
173
+ timetrace.stop('Where-Exists handling');
172
174
 
173
175
  // Check if structured elements and managed associations are compared in an expression
174
176
  // and expand these structured elements. This tuple expansion allows all other
175
177
  // subsequent procession steps (especially a2j) to see plain paths in expressions.
176
178
  // If errors are detected, throwWithAnyError() will return from further processing
177
179
 
180
+ timetrace.start('Expand Structures (expressions + refs)');
178
181
  // If this function is ever undefined, we have a bug in our logic.
179
182
  // @ts-ignore
180
183
  expandStructsInExpression(csn, { drillRef: true });
@@ -194,6 +197,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
194
197
  bindCsnReference();
195
198
  }
196
199
 
200
+ timetrace.stop('Expand Structures (expressions + refs)');
197
201
 
198
202
  // Remove properties attached by validator - they do not "grow" as the model grows.
199
203
  applyTransformations(csn, {
@@ -211,6 +215,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
211
215
 
212
216
  bindCsnReferenceOnly();
213
217
 
218
+ timetrace.start('Flattening (refs + elements)');
214
219
  // TODO: Instead of 3 separate applyTransformations, we could have each of them just return the "listeners", merge them into
215
220
  // one big listener that then gets passed into one single applyTransformations. Each listener would then have to return an array of callbacks to call.
216
221
  // With that, we could still ensure the processing order (assuming we don't run into problems with scoping).
@@ -229,6 +234,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
229
234
  // For to.hdbcds with naming mode hdbcds we also need to resolve the types
230
235
  flattening.resolveTypeReferences(csn, options, messageFunctions, new WeakMap(), pathDelimiter);
231
236
  }
237
+ timetrace.stop('Flattening (refs + elements)');
232
238
 
233
239
  // With flattening errors, it makes little sense to continue.
234
240
  throwWithAnyError();
@@ -635,6 +641,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
635
641
  }
636
642
 
637
643
  function handleAssocToJoins() {
644
+ timetrace.start('A2J');
638
645
  // the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
639
646
  // simply make it invisible and copy it over to the result csn
640
647
  forEachDefinition(csn, (art) => {
@@ -650,18 +657,18 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
650
657
 
651
658
  // restore all (non-enumerable) properties that wouldn't survive reaugmentation/compactification into the new compact model
652
659
  forEachDefinition(csn, (art, artName) => {
653
- if(art['$tableConstraints']) {
660
+ if (art['$tableConstraints']) {
654
661
  newCsn.definitions[artName].$tableConstraints = art['$tableConstraints'];
655
662
  }
656
663
  if (art.technicalConfig)
657
664
  newCsn.definitions[artName].technicalConfig = art.technicalConfig;
658
-
659
665
  });
660
666
 
661
667
  // To ensure we preserve feature flags
662
668
  newCsn.meta = csn.meta;
663
669
 
664
670
  csn = newCsn;
671
+ timetrace.stop('A2J');
665
672
  }
666
673
 
667
674
 
@@ -768,9 +775,8 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
768
775
  * Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
769
776
 
770
777
  * @param {*} node
771
- * @param {*} nodeName
772
- * @param {*} model
773
- * @param {*} path
778
+ * @param {CSN.Artifact} artifact
779
+ * @param {CSN.Path} path
774
780
  */
775
781
  function checkTypeParameters(node, artifact, path) {
776
782
  if (node.type && !node.virtual) {
@@ -93,6 +93,8 @@ const annoPersistenceSkip = '@cds.persistence.skip';
93
93
  * @param {boolean} [options.fewerLocalizedViews]
94
94
  * Default: true
95
95
  *
96
+ * @param {boolean} [options.testMode]
97
+ *
96
98
  * @param {object} config
97
99
  * Configuration for creating convenience views. Non-user visible options.
98
100
  *
@@ -8,20 +8,21 @@ const { transformAnnotationExpression, implicitAs, } = require('../../model/csnU
8
8
  * key declared in the scope, we replace it with referencing the foreign key itself. If a reference is a $self reference,
9
9
  * we do nothing and if a ref points to a structure/managed association, an error is thrown
10
10
  *
11
- * @param {Array[object]||Array[]} generatedForeignKeys
12
- * @param {object} csnUtils
13
- * @param {object} messageFunctions
11
+ * @param {object[]|string[][]} generatedForeignKeys
12
+ * @param {object} csnUtils
13
+ * @param {object} messageFunctions
14
+ * @param {CSN.Path} elementPath
14
15
  */
15
16
  function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, elementPath) {
16
- if(Array.isArray(generatedForeignKeys && generatedForeignKeys[0])) {
17
+ if(Array.isArray(generatedForeignKeys?.[0])) {
17
18
  // ensure we are always called with an array of objects. TODO: Cleanup fk creation in for.effective to create array of objects
18
- adaptAnnotationsRefs(remapToArrayOfObjects(generatedForeignKeys), csnUtils, { error }, elementPath);
19
+ adaptAnnotationsRefs(remapToArrayOfObjects(generatedForeignKeys), csnUtils, { error }, elementPath);
19
20
  } else {
20
21
  const reportedErrorsForAnnoPath = {};
21
22
  generatedForeignKeys.forEach((gfk, index) => {
22
23
  Object.entries(gfk.foreignKey).forEach(([key, value]) => {
23
24
  if (key[0] !== '@') return;
24
-
25
+
25
26
  transformAnnotationExpression(gfk.foreignKey, key, {
26
27
  ref: (_parent, _prop, ref, path, _p, _ppn, ctx) => {
27
28
  // if the reference is a $self reference, we do nothing,
@@ -30,8 +31,8 @@ function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, element
30
31
  // if annotation was not propagated from the keys array during foreign keys creation,
31
32
  // means that it is not a candidate for foreign key substitution
32
33
  if (gfk.keyAnnotations !== null && !gfk.keyAnnotations.includes(key)) return;
33
-
34
- const art = gfk.originalKey._art ||
34
+
35
+ const art = gfk.originalKey._art ||
35
36
  csnUtils.inspectRef(elementPath ? path : getOriginatingKeyPath(gfk, path)).art; // OData uses getOriginatingKeyPath - as it relies on $path
36
37
  if (csnUtils.isManagedAssociation(art)) {
37
38
  if (!reportedErrorsForAnnoPath[path]) {
@@ -42,7 +43,7 @@ function adaptAnnotationsRefs(generatedForeignKeys, csnUtils, { error }, element
42
43
  const gfkForRef = findGeneratedForeignKeyForKeyRef(generatedForeignKeys, ref);
43
44
  if (gfkForRef.length === 1) {
44
45
  ref[0] = gfkForRef[0].prefix;
45
-
46
+
46
47
  if (ctx?.annoExpr?.['=']) {
47
48
  ctx.annoExpr['='] = true;
48
49
  }
@@ -12,7 +12,7 @@
12
12
  * Multiplication/Division: '*', '/'
13
13
  * Addition/Subtraction: '+', '-'
14
14
  * Concatenation: '||'
15
- * Relational: '=', '<>', '>', '>=', '<', '<=', '!=', 'like', 'in', 'exists', 'between and'
15
+ * Relational: '=', '<>', '>', '>=', '<', '<=', '==', '!=', 'like', 'in', 'exists', 'between and'
16
16
  * Unary: 'is [not] null', 'not'
17
17
  * Conditional: 'case [when then]+ [else]? end', 'and', 'or'
18
18
  *
@@ -245,7 +245,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
245
245
  }
246
246
  }
247
247
  return binaryExpr(xpr, (xpr, s, e) => {
248
- const token = ['=', '<>', '>', '>=', '<', '<=', '!=', 'like', 'in'];
248
+ const token = ['=', '<>', '>', '>=', '<', '<=', '==', '!=', 'like', 'in'];
249
249
  while(s < e && !token.includes(xpr[s])) s++;
250
250
  if(s < e) {
251
251
  if(xpr[s-1] === 'not' && (xpr[s] === 'in' || xpr[s] === 'like'))
@@ -16,7 +16,7 @@ const { cloneCsnNonDict, cloneCsnDict } = require('../model/cloneCsn');
16
16
  const { addTenantFieldToArt } = require('./addTenantFields');
17
17
 
18
18
  const RestrictedOperators = ['<', '>', '>=', '<='];
19
- const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOperators];
19
+ const RelationalOperators = ['=', '<>', '==', '!=', 'is' /*, 'like'*/,...RestrictedOperators];
20
20
  // Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
21
21
  // Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
22
22
  // 'model' is compacted new style CSN
@@ -196,7 +196,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
196
196
  '@odata.Collation': 1,
197
197
  '@odata.Unicode': 1,
198
198
  } : {};
199
-
199
+
200
200
  copyAnnotations(elem, flatElem, false, excludes);
201
201
 
202
202
  // Copy selected type properties
@@ -220,15 +220,15 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
220
220
  * [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
221
221
  * [ (Entity), (struct1_struct2_assoc), (elem) ]
222
222
  *
223
- * @param {string[]} ref
223
+ * @param {CSN.Ref} ref
224
224
  * @param {CSN.Path} path CSN path to the ref
225
225
  * @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
226
226
  * @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
227
227
  * @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
228
- * @param {bool} [suspend] suspend flattening by caller until association path step
229
- * @param {int} [suspendPos] suspend if starting pos is lower or equal to suspendPos and suspend is true
230
- * @param {bool} [revokeAtSuspendPos] revoke suspension after suspendPos (binding parameter path use case)
231
- * @param {bool} [flattenParameters] Whether to flatten references into structured parameters. OData flattens parameters, SQL/for.effective does not.
228
+ * @param {boolean} [suspend] suspend flattening by caller until association path step
229
+ * @param {number} [suspendPos] suspend if starting pos is lower or equal to suspendPos and suspend is true
230
+ * @param {boolean} [revokeAtSuspendPos] revoke suspension after suspendPos (binding parameter path use case)
231
+ * @param {boolean} [flattenParameters] Whether to flatten references into structured parameters. OData flattens parameters, SQL/for.effective does not.
232
232
  *
233
233
  * @todo: Refactor to take config object instead of N boolean arguments.
234
234
  * @returns [string[], bool]
@@ -1028,6 +1028,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1028
1028
  Flattening stops on all non-structured types.
1029
1029
  */
1030
1030
  function expand(expr, location) {
1031
+ if (!Array.isArray(expr))
1032
+ return expr; // don't traverse strings, etc.
1031
1033
  const rc = [];
1032
1034
  for(let i = 0; i < expr.length; i++)
1033
1035
  {
@@ -25,7 +25,6 @@ function translateAssocsToJoinsCSN(csn, options){
25
25
  timetrace.start('A2J: Translating associations to joins');
26
26
  translateAssocsToJoins(model, options);
27
27
  timetrace.stop('A2J: Translating associations to joins');
28
- timetrace.start('A2J: Post-processing columns');
29
28
  // Use the effective elements list as columns
30
29
  forEachDefinition(model, art => {
31
30
  if (art.$queries) {
@@ -40,7 +39,6 @@ function translateAssocsToJoinsCSN(csn, options){
40
39
  }
41
40
  }
42
41
  });
43
- timetrace.stop('A2J: Post-processing columns');
44
42
 
45
43
  if (options.messages) {
46
44
  // Make sure that we don't complain twice about the same things
@@ -81,7 +81,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
81
81
  const origin = getOrigin(element);
82
82
  if (origin)
83
83
  return origin;
84
- throw new CompilerAssertion(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
84
+ throw new CompilerAssertion(`Could not find ancestor for ${ JSON.stringify(element) } named ${ name }`);
85
85
  }
86
86
 
87
87
  /**
@@ -114,7 +114,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
114
114
  return checkJoinSources(base.args, name);
115
115
  }
116
116
 
117
- throw new CompilerAssertion(`Element “${name}” not found in: ${JSON.stringify(base)}`);
117
+ throw new CompilerAssertion(`Element “${ name }” not found in: ${ JSON.stringify(base) }`);
118
118
  }
119
119
 
120
120
  /**
@@ -567,9 +567,9 @@ function packageCdsMain( pkg ) {
567
567
  * @param {object} dep
568
568
  * @param {string} realpath
569
569
  * @param {string} nativeRealpath
570
- * @param {Function} warning
570
+ * @param {object} messageFunctions
571
571
  */
572
- function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
572
+ function checkFileCase( dep, realpath, nativeRealpath, messageFunctions ) {
573
573
  if (realpath === nativeRealpath)
574
574
  return;
575
575
  if (realpath.toLowerCase() !== nativeRealpath.toLowerCase()) {
@@ -578,6 +578,7 @@ function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
578
578
  return;
579
579
  }
580
580
  for (const using of dep.usingFroms) {
581
+ const { warning } = messageFunctions;
581
582
  warning('file-unexpected-case-mismatch', [ using.location, using ], {},
582
583
  // eslint-disable-next-line @stylistic/js/max-len
583
584
  'The imported filename differs on the filesystem; ensure that capitalization matches the actual file\'s name');
@@ -589,9 +590,10 @@ function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
589
590
  * @property {string[]} lookupDirs
590
591
  * Directories to look in for modules, e.g. node_modules/.
591
592
  * @property {string[]} extensions
592
- * @property {(path: string, callback: (err, foundAndIsFile) => void) => void} isFile
593
- * @property {(path: string, encoding, callback: (err, content) => void) => void} readFile
594
- * @property {(path: string, callback: (err, realpath) => void) => void} realpath
593
+ * @property {(path: string, callback: (err, foundAndIsFile: boolean) => void) => void} isFile
594
+ * @property {(path: string, encoding: string, callback:
595
+ * (err, content: string) => void) => void} readFile
596
+ * @property {(path: string, callback: (err, realpath: string) => void) => void} realpath
595
597
  * used to read `package.json` files.
596
598
  */
597
599
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "5.8.2",
3
+ "version": "5.9.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)",
@@ -0,0 +1,55 @@
1
+ # def-upcoming-virtual-change
2
+
3
+ The behavior of `@sap/cds-compiler` v6 will change for a selected element.
4
+
5
+ ## Example
6
+
7
+ Erroneous code example:
8
+
9
+ ```cds
10
+ entity Source {
11
+ key ID : String;
12
+ a : String;
13
+ };
14
+
15
+ entity Proj as projection on Source {
16
+ ID,
17
+ virtual a, // ❌ behavior will change in v6
18
+ };
19
+ ```
20
+
21
+ In `@sap/cds-compiler` v5 and earlier, element `Proj:a` is a reference
22
+ to element `Source:a`, which was marked virtual.
23
+
24
+ In `@sap/cds-compiler` v6 and later, it will instead of a _new_ element,
25
+ without any reference to `Source:a`.
26
+
27
+ This may or may not affect your runtime coding, hence the warning.
28
+
29
+
30
+ ## How to Fix
31
+
32
+ If the v6 behavior works for you, there is nothing you need to do.
33
+
34
+ However, if you want to keep a reference to `Source:a` in CSN, for example
35
+ because you use the reference at runtime, then you can keep the old behavior
36
+ by either:
37
+
38
+ 1. prepending a table alias to the reference
39
+ 2. adding a column alias
40
+
41
+ ```cds
42
+ // (1) prepend a table alias
43
+ entity V as projection on E {
44
+ ID,
45
+ virtual E.a, // ok
46
+ };
47
+ ```
48
+
49
+ ```cds
50
+ // (2) add an alias
51
+ entity V as projection on E {
52
+ ID,
53
+ virtual a as a, // ok
54
+ };
55
+ ```
@@ -0,0 +1,61 @@
1
+ # file-unexpected-case-mismatch
2
+
3
+ The filename of a `using` statement does not match
4
+ the file's actual name on disk.
5
+
6
+ To avoid operating-system dependent issues, the compiler checks if the name of
7
+ an imported file matches the name of the file in the filesystem / on disk.
8
+ For example, by default macOS uses a case-insensitive file system.
9
+ Hence, a file named `model.cds` will also be loaded by `using from './Model.cds'`
10
+ on such systems.
11
+
12
+ However, on other filesystems that are case-sensitive, e.g. when building your
13
+ application in another environment, the file will not be found.
14
+
15
+ Hence, the `using` statement needs to be adapted.
16
+
17
+ ## Example
18
+
19
+ Erroneous code example:
20
+
21
+ ```cds
22
+ // index.cds
23
+ using from './Model';
24
+ ```
25
+
26
+ using following directory tree:
27
+
28
+ ```
29
+ ├── index.cds
30
+ └── model.cds
31
+ ```
32
+
33
+ On case-insensitive systems, the file can be loaded, but the compiler will warn
34
+ about the mismatch.
35
+ On case-sensitive file systems, compilation will fail, as the imported file
36
+ can't be found.
37
+
38
+ While in this case, compilation will fail on case-sensitive systems, it could
39
+ instead end up with semantic changes, too.
40
+ Given the same `index.cds`, but a different directory tree:
41
+
42
+ ```
43
+ ├── index.cds
44
+ ├── Model
45
+ │ └── index.cds
46
+ └── model.cds
47
+ ```
48
+
49
+ On case-sensitive systems, `./Model/index.cds` will be loaded.
50
+ On case-insensitive systems, however, `model.cds` will be loaded,
51
+ as the compiler first tries to load `Model.cds`, before looking for
52
+ `Model/index.cds`.
53
+
54
+
55
+ ## How to Fix
56
+
57
+ Adapt the filename in your `using` statement.
58
+
59
+ If you have both `model.cds` and `Model/index.cds`, but don't want to use
60
+ a `.cds` suffix, use `using from './Model/'`, i.e. add a trailing slash to
61
+ indicate that you want to load from the folder `Model`.
@@ -6,8 +6,10 @@
6
6
  "check-proper-type-of",
7
7
  "def-duplicate-autoexposed",
8
8
  "def-missing-type",
9
+ "def-upcoming-virtual-change",
9
10
  "extend-repeated-intralayer",
10
11
  "extend-unrelated-layer",
12
+ "file-unexpected-case-mismatch",
11
13
  "redirected-to-ambiguous",
12
14
  "redirected-to-complex",
13
15
  "redirected-to-unrelated",
@@ -1,77 +0,0 @@
1
- // Currently unused, but may become useful again if HDBCDS -> HDBTABLE
2
- // handover becomes more prominent. Historically used with the no longer
3
- // existent option `--compatibility`.
4
- // If necessary, more complex expressions could be parsed with parseExpr.js
5
- // and then stringified with parentheses again.
6
-
7
- 'use strict';
8
-
9
- function isAlreadyBraced(expression, start, end){
10
- return start - 1 > -1 &&
11
- end + 1 < expression.length &&
12
- expression[start-1] === '(' &&
13
- expression[end+1] === ')';
14
- }
15
-
16
- function binarycomparison(expression, token, index){
17
- if(!isAlreadyBraced(expression, index-1, index+1)){
18
- expression.splice(index+2 > expression.length ? expression.length : index +2 ,0,')');
19
- expression.splice(index-1 > -1 ? index - 1 : 0,0,'(');
20
- }
21
-
22
- return index + 3;
23
- }
24
-
25
- function between(expression, token, index){
26
- let start = index-1, end = index+4;
27
- if(expression[index-1] === 'not'){
28
- start -= 1;
29
- }
30
-
31
- if(!isAlreadyBraced(expression, start, end)){
32
- expression.splice(end > expression.length ? expression.length : end ,0,')');
33
- expression.splice(start > -1 ? start : 0,0,'(');
34
- }
35
-
36
- return index + 4;
37
- }
38
-
39
- function like(expression, token, index){
40
- let start = index-1, end = index+2;
41
- if(expression[index-1] === 'not'){
42
- start -= 1;
43
- }
44
-
45
- if(!isAlreadyBraced(expression, start, end)){
46
- expression.splice(end > expression.length ? expression.length : end ,0,')');
47
- expression.splice(start > -1 ? start : 0,0,'(');
48
- }
49
-
50
- return index + 3;
51
- }
52
-
53
- const bracers = {
54
- '=' : binarycomparison,
55
- '>' : binarycomparison,
56
- '<' : binarycomparison,
57
- '>=': binarycomparison,
58
- '<=': binarycomparison,
59
- '!=': binarycomparison,
60
- 'between': between,
61
- 'like': like
62
- }
63
-
64
- function braceExpression(expr){
65
- for(let i = 0; i < expr.length; i++){
66
- const token = expr[i];
67
- if(token && token.xpr){
68
- token.xpr = braceExpression(token.xpr);
69
- }
70
- if(bracers[token]){
71
- i = bracers[token](expr, token, i);
72
- }
73
- }
74
-
75
- return expr;
76
- }
77
- module.exports = braceExpression;