@sap/cds-compiler 3.4.0 → 3.4.4

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 (60) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/bin/cdsc.js +3 -4
  3. package/bin/cdshi.js +19 -6
  4. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  5. package/lib/api/main.js +6 -3
  6. package/lib/base/message-registry.js +61 -38
  7. package/lib/base/messages.js +7 -3
  8. package/lib/checks/.eslintrc.json +2 -0
  9. package/lib/compiler/.eslintrc.json +4 -1
  10. package/lib/compiler/assert-consistency.js +8 -6
  11. package/lib/compiler/builtins.js +13 -13
  12. package/lib/compiler/checks.js +50 -33
  13. package/lib/compiler/define.js +9 -6
  14. package/lib/compiler/extend.js +83 -55
  15. package/lib/compiler/finalize-parse-cdl.js +3 -3
  16. package/lib/compiler/populate.js +16 -5
  17. package/lib/compiler/propagator.js +22 -29
  18. package/lib/compiler/resolve.js +2 -15
  19. package/lib/compiler/shared.js +4 -4
  20. package/lib/compiler/utils.js +14 -0
  21. package/lib/edm/annotations/genericTranslation.js +68 -56
  22. package/lib/edm/csn2edm.js +214 -174
  23. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  24. package/lib/edm/edmInboundChecks.js +2 -2
  25. package/lib/edm/edmPreprocessor.js +1 -1
  26. package/lib/edm/edmUtils.js +3 -3
  27. package/lib/gen/Dictionary.json +176 -8
  28. package/lib/gen/language.checksum +1 -1
  29. package/lib/gen/language.interp +2 -1
  30. package/lib/gen/languageParser.js +4776 -4513
  31. package/lib/json/from-csn.js +21 -16
  32. package/lib/json/to-csn.js +37 -41
  33. package/lib/language/.eslintrc.json +4 -1
  34. package/lib/language/antlrParser.js +5 -2
  35. package/lib/language/docCommentParser.js +6 -6
  36. package/lib/language/errorStrategy.js +43 -23
  37. package/lib/language/genericAntlrParser.js +54 -95
  38. package/lib/language/language.g4 +92 -66
  39. package/lib/language/multiLineStringParser.js +2 -2
  40. package/lib/language/textUtils.js +2 -2
  41. package/lib/model/csnRefs.js +5 -0
  42. package/lib/modelCompare/compare.js +2 -2
  43. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  44. package/lib/modelCompare/utils/filter.js +99 -0
  45. package/lib/render/.eslintrc.json +1 -0
  46. package/lib/render/toCdl.js +96 -127
  47. package/lib/render/toHdbcds.js +38 -35
  48. package/lib/render/toSql.js +75 -161
  49. package/lib/render/utils/common.js +133 -83
  50. package/lib/render/utils/delta.js +227 -0
  51. package/lib/transform/db/.eslintrc.json +2 -0
  52. package/lib/transform/draft/.eslintrc.json +1 -35
  53. package/lib/transform/forOdataNew.js +33 -22
  54. package/lib/transform/forRelationalDB.js +1 -1
  55. package/lib/transform/localized.js +9 -8
  56. package/lib/transform/odata/typesExposure.js +26 -4
  57. package/lib/transform/transformUtilsNew.js +15 -8
  58. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  59. package/package.json +2 -3
  60. package/lib/modelCompare/filter.js +0 -83
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { makeMessageFunction } = require('../base/messages');
4
4
  const { setProp, isDeprecatedEnabled} = require('../base/model');
5
- const { hasErrors } = require('../base/messages');
6
5
  const { forEachKey } = require('../utils/objectUtils');
7
6
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
7
  const {
@@ -44,7 +43,10 @@ const _targetFor = Symbol('_targetFor');
44
43
  */
45
44
 
46
45
  /**
47
- * Create transitive localized convenience views
46
+ * Create transitive localized convenience views.
47
+ *
48
+ * A convenience view is created if the entity/view has a localized element
49
+ * or if it exposes an association leading to a localized-tagged target.
48
50
  *
49
51
  * INTERNALS:
50
52
  * We have three kinds of localized convenience views:
@@ -72,13 +74,11 @@ const _targetFor = Symbol('_targetFor');
72
74
  * @param {object} config
73
75
  */
74
76
  function _addLocalizationViews(csn, options, useJoins, config) {
75
- // Don't try to create convenience views with errors.
76
- if (hasErrors(options.messages)) // TODO: this is actually wrong, consider --test-mode
77
- return csn;
78
-
79
77
  const messageFunctions = makeMessageFunction(csn, options);
80
- if (hasExistingLocalizationViews(csn, options, messageFunctions))
78
+ if (checkExistingLocalizationViews(csn, options, messageFunctions)) {
79
+ messageFunctions.throwWithError();
81
80
  return csn;
81
+ }
82
82
 
83
83
  const { acceptLocalizedView, ignoreUnknownExtensions } = config;
84
84
  const noCoalesce = (options.localizedLanguageFallback === 'none' ||
@@ -89,6 +89,7 @@ function _addLocalizationViews(csn, options, useJoins, config) {
89
89
  cleanDefinitionSymbols();
90
90
  applyAnnotationsForLocalizedViews();
91
91
  sortCsnDefinitionsForTests(csn, options);
92
+ messageFunctions.throwWithError();
92
93
  return csn;
93
94
 
94
95
  /**
@@ -711,7 +712,7 @@ function copyPersistenceAnnotations(target, source, options) {
711
712
  * @param {CSN.Options} options
712
713
  * @param {object} messageFunctions
713
714
  */
714
- function hasExistingLocalizationViews(csn, options, messageFunctions) {
715
+ function checkExistingLocalizationViews(csn, options, messageFunctions) {
715
716
  if (!csn || !csn.definitions)
716
717
  return false;
717
718
 
@@ -108,9 +108,10 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
108
108
  if (newType) {
109
109
  // error, if it was not exposed by us
110
110
  if (!exposedTypes[fullQualifiedNewTypeName]) {
111
+ setProp(node, '$NameClashReported', true);
111
112
  error(null, path, { type: fullQualifiedNewTypeName, name: memberName },
112
113
  'Can\'t create artificial type $(TYPE) for $(NAME) because the name is already used');
113
- return;
114
+ return { isExposable, typeDef, typeName, isAnonymous };
114
115
  }
115
116
  }
116
117
  else {
@@ -132,8 +133,28 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
132
133
  if (node.elements && node.elements[elemName].$location)
133
134
  setProp(newElem, '$location', node.elements[elemName].$location);
134
135
  defName = typeDef.kind === 'type' ? typeName : defName;
135
- exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
136
- getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
136
+ {
137
+ const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
138
+ getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
139
+ // if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
140
+ // been catched by expandToFinalBaseType() (forODataNew must not modify external imported services)
141
+ if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
142
+ if(typeDef.items) {
143
+ newElem.items = typeDef.items;
144
+ delete newElem.type;
145
+ }
146
+ else if(newElem.items) {
147
+ newElem.items.type = typeName;
148
+ if(typeDef.enum)
149
+ newElem.items.enum = typeDef.enum;
150
+ }
151
+ else {
152
+ newElem.type = typeName;
153
+ if(typeDef.enum)
154
+ newElem.enum = typeDef.enum;
155
+ }
156
+ }
157
+ }
137
158
  });
138
159
  copyAnnotations(typeDef, newType);
139
160
  // if the origin type had items, add items to exposed type
@@ -155,6 +176,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
155
176
  node.type = fullQualifiedNewTypeName;
156
177
  }
157
178
  }
179
+ return { isExposable, typeDef, typeName, isAnonymous };
158
180
 
159
181
  /**
160
182
  * Check if the node's type can be exposed:
@@ -197,7 +219,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
197
219
  }
198
220
  }
199
221
  }
200
- return { isExposable: false };
222
+ return { isExposable: false, typeDef, typeName, isAnonymous: false };
201
223
  }
202
224
 
203
225
 
@@ -1220,17 +1220,24 @@ function getTransformers(model, options, pathDelimiter = '_') {
1220
1220
  }
1221
1221
  // t_0 OR ... OR t_n with t = (a <not equal> b)
1222
1222
  const bop = (op === 'is' && not) || op === '!=' || op === '<>' ? 'or' : 'and';
1223
- rc.push('(');
1223
+ const xpr = { xpr: [] };
1224
1224
  xrefvalues.filter(x => x.lhs && x.rhs).forEach((x,i) => {
1225
- if(i>0)
1226
- rc.push(bop);
1227
- rc.push(x.lhs);
1228
- rc.push(op);
1225
+ xpr.i = i;
1226
+ if(i>0) {
1227
+ xpr.xpr.push(bop);
1228
+ }
1229
+ xpr.xpr.push(x.lhs);
1230
+ xpr.xpr.push(op);
1229
1231
  if(not)
1230
- rc.push('not')
1231
- rc.push(x.rhs);
1232
+ xpr.xpr.push('not')
1233
+ xpr.xpr.push(x.rhs);
1232
1234
  });
1233
- rc.push(')');
1235
+ if(xpr.i > 0) {
1236
+ delete xpr.i;
1237
+ rc.push(xpr);
1238
+ }
1239
+ else
1240
+ rc.push(...xpr.xpr);
1234
1241
  i += not ? 3 : 2;
1235
1242
  }
1236
1243
  else
@@ -28,6 +28,7 @@ module.exports = (csn, options) => {
28
28
  // Properties on definition level that we treat specially.
29
29
  const definitionPropagationRules = {
30
30
  '@cds.autoexpose': onlyViaArtifact,
31
+ '@cds.external': skip,
31
32
  '@fiori.draft.enabled': onlyViaArtifact,
32
33
  '@': nullStopsPropagation,
33
34
  // Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "3.4.0",
3
+ "version": "3.4.4",
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)",
@@ -30,7 +30,7 @@
30
30
  "testmigration": "npm install --no-save @sap/hana-client@2.3.123 @sap/hdi-deploy@3.10.0 && node scripts/verifyGrammarChecksum.js && cross-env TESTMIGRATION='TRUE' mocha",
31
31
  "testall": "npm install --no-save @sap/hana-client@2.3.123 @sap/hdi-deploy@3.10.0 && node scripts/verifyGrammarChecksum.js && cross-env TESTDB='TRUE' TESTMIGRATION='TRUE' mocha",
32
32
  "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/ && nyc report --reporter=lcov",
33
- "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
33
+ "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
34
34
  "tslint": "tsc --pretty -p .",
35
35
  "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
36
36
  "generateCompilerRefs": "cross-env MAKEREFS='true' mocha test/testCompiler.js",
@@ -41,7 +41,6 @@
41
41
  "generateToSqlRefs": "cross-env MAKEREFS='true' mocha test/testToSql.js",
42
42
  "generateToRenameRefs": "cross-env MAKEREFS='true' mocha test/testToRename.js",
43
43
  "generateChecksRefs": "cross-env MAKEREFS='true' mocha test/testChecks.js",
44
- "generateScenarioRefs": "cross-env MAKEREFS='true' mocha test/testScenarios.js",
45
44
  "generateDraftRefs": "cross-env MAKEREFS='true' mocha test/testDraft.js",
46
45
  "generateAllRefs": "node scripts/verifyGrammarChecksum.js && cross-env MAKEREFS='true' mocha --reporter-option maxDiffSize=0 test/ test3/"
47
46
  },
@@ -1,83 +0,0 @@
1
- // Each db has some changes that it can and cannot represent, or that cause problems only on that specific db
2
- // In this file, we define rules for each db-dialect to detect and act on these cases.
3
-
4
- const { forEach } = require("../utils/objectUtils");
5
- const { isPersistedAsTable } = require('../model/csnUtils');
6
-
7
- function isKey(element) {
8
- return element.key;
9
- }
10
-
11
- module.exports = {
12
- sqlite: getFilterObject(
13
- 'sqlite',
14
- (extend, name, element, error) => {
15
- if(isKey(element)) { // Key must not be extended
16
- error(null, ['definitions', extend, 'elements', name], {id: name, name: 'sqlite'}, "Added element $(ID) is a primary key change and will not work with $(NAME)")
17
- }
18
- },
19
- (migrate, name, migration, change, error) => {
20
- const newIsKey = isKey(migration.new);
21
- const oldIsKey = isKey(migration.old);
22
- if((newIsKey || oldIsKey) && oldIsKey !== newIsKey) { // Turned into key or key was removed
23
- error(null, ['definitions', migrate, 'elements', name], {id: name, name: 'sqlite'}, "Changed element $(ID) is a primary key change and will not work with $(NAME)")
24
- } else { // Ignore simple migrations
25
- delete change[name];
26
- }
27
- })
28
- }
29
-
30
- function getFilterObject(dialect, extensionCallback, migrationCallback) {
31
- return {
32
- // will be called with a simple Array.forEach
33
- extension: ({ elements, extend }, error) => {
34
- forEach(elements, (name, element) => {
35
- extensionCallback(extend, name, element, error);
36
- });
37
- },
38
- // will be called with a Array.map, as we need to filter "change" for SQLite
39
- migration: ({ change, migrate, remove }, error) => {
40
- forEach(remove, (name) => {
41
- error(null, ['definitions', migrate, 'elements', name], {}, "Dropping elements is not supported")
42
- });
43
-
44
- forEach(change, (name, migration) => {
45
- if(migration.new.type !== migration.old.type && typeChangeIsNotCompatible(dialect, migration.old.type, migration.new.type)) {
46
- error(null, ['definitions', migrate, 'elements', name], { id: name, name: migration.old.type, type: migration.new.type }, "Changed element $(ID) is a lossy type change from $(NAME) to $(TYPE) and is not supported")
47
- } else if(migration.new.length < migration.old.length) {
48
- error(null, ['definitions', migrate, 'elements', name], { id: name }, "Changed element $(ID) is a length reduction and is not supported")
49
- } else {
50
- migrationCallback(migrate, name, migration, change, error);
51
- }
52
-
53
- // TODO: precision/scale growth
54
- });
55
- },
56
- deletion: ([artifactName, artifact ], error) => {
57
- if(isPersistedAsTable(artifact))
58
- error(null, ['definitions', artifactName], "Dropping tables is not supported");
59
- }
60
- }
61
- }
62
-
63
- const baseMatrix = {
64
- // Integer types
65
- 'cds.hana.tinyint':['cds.UInt8', 'cds.Int16', 'cds.Int32', 'cds.Integer', 'cds.Int64', 'cds.Integer64'],
66
- 'cds.UInt8': ['cds.hana.tinyint', 'cds.Int16', 'cds.Int32', 'cds.Integer', 'cds.Int64', 'cds.Integer64'],
67
- 'cds.Int16': ['cds.hana.smallint', 'cds.Int32', 'cds.Integer', 'cds.Int64', 'cds.Integer64'],
68
- 'cds.hana.smallint':['cds.Int16', 'cds.Int32', 'cds.Integer', 'cds.Int64', 'cds.Integer64'],
69
- 'cds.Int32': ['cds.Integer', 'cds.Int64', 'cds.Integer64'],
70
- 'cds.Integer': ['cds.Int32', 'cds.Int64', 'cds.Integer64'],
71
- 'cds.Integer64': ['cds.Int64'],
72
- 'cds.Int64': ['cds.Integer64']
73
- }
74
-
75
- const allowedTypeChanges = {
76
- 'sqlite': baseMatrix
77
- };
78
-
79
- function typeChangeIsNotCompatible(dialect, before, after) {
80
- if(allowedTypeChanges[dialect])
81
- return allowedTypeChanges[dialect][before]?.indexOf(after) === -1;
82
- return true;
83
- }