@sap/cds-compiler 3.4.2 → 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.
- package/CHANGELOG.md +8 -0
- package/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +60 -37
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +71 -45
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +26 -19
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/package.json +2 -3
- 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 (
|
|
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
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
1223
|
+
const xpr = { xpr: [] };
|
|
1224
1224
|
xrefvalues.filter(x => x.lhs && x.rhs).forEach((x,i) => {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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
|
-
|
|
1231
|
-
|
|
1232
|
+
xpr.xpr.push('not')
|
|
1233
|
+
xpr.xpr.push(x.rhs);
|
|
1232
1234
|
});
|
|
1233
|
-
|
|
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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "3.4.
|
|
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
|
-
}
|