@sap/cds-compiler 3.1.2 → 3.4.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.
- package/CHANGELOG.md +101 -3
- package/bin/cdsc.js +4 -2
- package/doc/CHANGELOG_BETA.md +35 -0
- package/lib/api/main.js +153 -29
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +106 -24
- package/lib/base/message-registry.js +177 -79
- package/lib/base/messages.js +78 -57
- package/lib/base/model.js +2 -1
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/arrayOfs.js +15 -7
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +53 -0
- package/lib/checks/defaultValues.js +4 -2
- package/lib/checks/elements.js +81 -6
- package/lib/checks/foreignKeys.js +12 -13
- package/lib/checks/invalidTarget.js +10 -11
- package/lib/checks/managedInType.js +21 -15
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +9 -9
- package/lib/checks/parameters.js +23 -0
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +36 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +57 -40
- package/lib/compiler/define.js +139 -69
- package/lib/compiler/extend.js +319 -50
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +111 -68
- package/lib/compiler/propagator.js +5 -3
- package/lib/compiler/resolve.js +71 -108
- package/lib/compiler/shared.js +82 -54
- package/lib/compiler/tweak-assocs.js +26 -14
- package/lib/compiler/utils.js +13 -2
- package/lib/edm/annotations/genericTranslation.js +10 -7
- package/lib/edm/csn2edm.js +11 -11
- package/lib/edm/edm.js +17 -9
- package/lib/edm/edmPreprocessor.js +53 -30
- package/lib/edm/edmUtils.js +7 -2
- package/lib/gen/Dictionary.json +14 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -2
- package/lib/gen/languageParser.js +4312 -4186
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +161 -172
- package/lib/json/to-csn.js +70 -10
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +8 -11
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +140 -93
- package/lib/language/language.g4 +57 -33
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.d.ts +3 -6
- package/lib/main.js +1 -0
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +78 -50
- package/lib/model/csnUtils.js +272 -222
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +35 -31
- package/lib/modelCompare/compare.js +52 -18
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +10 -1
- package/lib/render/manageConstraints.js +11 -7
- package/lib/render/toCdl.js +151 -106
- package/lib/render/toHdbcds.js +8 -6
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +17 -7
- package/lib/render/utils/common.js +27 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +92 -41
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +8 -2
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
- package/lib/transform/localized.js +29 -20
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +122 -51
- package/lib/transform/translateAssocsToJoins.js +17 -16
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
- package/share/messages/check-proper-type-of.md +4 -4
- package/share/messages/check-proper-type.md +2 -2
- package/share/messages/duplicate-autoexposed.md +4 -4
- package/share/messages/extend-repeated-intralayer.md +4 -5
- package/share/messages/extend-unrelated-layer.md +4 -4
- package/share/messages/message-explanations.json +3 -1
- package/share/messages/redirected-to-ambiguous.md +7 -6
- package/share/messages/redirected-to-complex.md +63 -0
- package/share/messages/redirected-to-unrelated.md +6 -5
- package/share/messages/rewrite-not-supported.md +4 -4
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
- package/share/messages/wildcard-excluding-one.md +37 -0
|
@@ -12,14 +12,17 @@ const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnU
|
|
|
12
12
|
const { typeParameters, isBuiltinType } = require('../compiler/builtins');
|
|
13
13
|
const { ModelError } = require("../base/error");
|
|
14
14
|
const { forEach } = require('../utils/objectUtils');
|
|
15
|
+
const { pathName } = require("../compiler/utils");
|
|
15
16
|
|
|
17
|
+
const RestrictedOperators = ['<', '>', '>=', '<='];
|
|
18
|
+
const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOperators];
|
|
16
19
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
17
20
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
18
21
|
// 'model' is compacted new style CSN
|
|
19
22
|
// TODO: Error and warnings handling with compacted CSN? - currently just throw new ModelError for everything
|
|
20
23
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
21
24
|
function getTransformers(model, options, pathDelimiter = '_') {
|
|
22
|
-
const { error, warning, info } = makeMessageFunction(model, options);
|
|
25
|
+
const { message, error, warning, info } = makeMessageFunction(model, options);
|
|
23
26
|
const csnUtils = getUtils(model);
|
|
24
27
|
const {
|
|
25
28
|
getCsnDef,
|
|
@@ -500,7 +503,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
500
503
|
* Create a 'DraftAdministrativeData' projection on entity 'DRAFT.DraftAdministrativeData'
|
|
501
504
|
* in service 'service' and add it to the model.
|
|
502
505
|
*
|
|
503
|
-
* For
|
|
506
|
+
* For forRelationalDB, use String(36) instead of UUID and UTCTimestamp instead of Timestamp
|
|
504
507
|
*
|
|
505
508
|
* @param {string} service
|
|
506
509
|
* @param {boolean} [hanaMode=false] Turn UUID into String(36)
|
|
@@ -709,7 +712,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
709
712
|
if (artifactName) {
|
|
710
713
|
path = ['definitions', artifactName, 'elements', elemName];
|
|
711
714
|
}
|
|
712
|
-
error(null, path, { name: elemName },
|
|
715
|
+
error(null, path, { name: elemName }, 'Generated element $(NAME) conflicts with existing element');
|
|
713
716
|
return;
|
|
714
717
|
}
|
|
715
718
|
|
|
@@ -863,9 +866,9 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
863
866
|
if (array.length > 1) {
|
|
864
867
|
const loc = ['definitions', artifactName];
|
|
865
868
|
if (err === true) {
|
|
866
|
-
error(null, loc, { anno: annoName },
|
|
869
|
+
error(null, loc, { anno: annoName }, 'Annotation $(ANNO) must be assigned only once');
|
|
867
870
|
} else {
|
|
868
|
-
warning(null, loc, { anno: annoName }
|
|
871
|
+
warning(null, loc, { anno: annoName },'Annotation $(ANNO) must be assigned only once');
|
|
869
872
|
}
|
|
870
873
|
}
|
|
871
874
|
}
|
|
@@ -956,7 +959,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
956
959
|
node[name] = value;
|
|
957
960
|
if(wasOverwritten)
|
|
958
961
|
info(null, path, { anno: name, prop: value, otherprop: oldValue },
|
|
959
|
-
|
|
962
|
+
'Value $(OTHERPROP) of annotation $(ANNO) is overwritten with new value $(PROP)');
|
|
960
963
|
return wasOverwritten;
|
|
961
964
|
}
|
|
962
965
|
|
|
@@ -1113,92 +1116,122 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1113
1116
|
for(let i = 0; i < expr.length; i++)
|
|
1114
1117
|
{
|
|
1115
1118
|
if(Array.isArray(expr[i]))
|
|
1116
|
-
rc.push(expr[i].map(expand, location));
|
|
1119
|
+
rc.push(expr[i].map(e => expand(e, location)));
|
|
1117
1120
|
|
|
1118
1121
|
if(i < expr.length-2)
|
|
1119
1122
|
{
|
|
1120
|
-
|
|
1123
|
+
let [lhs, op, not, rhs] = expr.slice(i);
|
|
1124
|
+
if(not !== 'not') {
|
|
1125
|
+
rhs = not;
|
|
1126
|
+
not = false;
|
|
1127
|
+
}
|
|
1128
|
+
if(lhs === undefined || op === undefined || rhs === undefined)
|
|
1129
|
+
return expr;
|
|
1121
1130
|
|
|
1122
1131
|
// we might have to ad-hoc resolve a ref, since handleExists is run before hand and generates new refs.
|
|
1123
1132
|
const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
|
|
1124
1133
|
const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
|
|
1125
|
-
|
|
1126
|
-
if
|
|
1127
|
-
|
|
1128
|
-
isExpandable(lhsArt) && isExpandable(rhsArt) &&
|
|
1129
|
-
['=', '<', '>', '>=', '<=', '!=', '<>'].includes(op) &&
|
|
1130
|
-
!(isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs))) {
|
|
1134
|
+
const lhsIsVal = (lhs.val !== undefined);
|
|
1135
|
+
// if ever rhs should be alowed to be a value uncomment this
|
|
1136
|
+
const rhsIsVal = (rhs === 'null' /*|| rhs.val !== undefined*/);
|
|
1131
1137
|
|
|
1138
|
+
// lhs & rhs must be expandable types (structures or managed associations)
|
|
1139
|
+
// if ever lhs should be alowed to be a value uncomment this
|
|
1140
|
+
if(!(lhsIsVal /*&& rhsIsVal*/) &&
|
|
1141
|
+
!(isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs)) &&
|
|
1142
|
+
RelationalOperators.includes(op) &&
|
|
1143
|
+
(lhsIsVal || (lhsArt && lhs.ref && isExpandable(lhsArt))) &&
|
|
1144
|
+
(rhsIsVal || (rhsArt && rhs.ref && isExpandable(rhsArt)))
|
|
1145
|
+
) {
|
|
1146
|
+
|
|
1147
|
+
if(RestrictedOperators.includes(op)) {
|
|
1148
|
+
message('expr-unexpected-operator', location, { op }, 'Unexpected operator $(OP) in structural comparison');
|
|
1149
|
+
}
|
|
1132
1150
|
// if path is scalar and no assoc or has no type (@Core.Computed) use original expression
|
|
1133
1151
|
// only do the expansion on (managed) assocs and (items.)elements, array of check in ON cond is done elsewhere
|
|
1134
|
-
const lhspaths =
|
|
1135
|
-
const rhspaths =
|
|
1152
|
+
const lhspaths = lhsIsVal ? [] : flattenPath({ _art: lhsArt, ref: lhs.ref }, false, true );
|
|
1153
|
+
const rhspaths = rhsIsVal ? [] : flattenPath({ _art: rhsArt, ref: rhs.ref }, false, true );
|
|
1136
1154
|
|
|
1137
1155
|
// mapping dict for lhs/rhs for mismatch check
|
|
1138
1156
|
// strip lhs/rhs prefix from flattened paths to check remaining common trailing path
|
|
1139
1157
|
// if path is idempotent, it doesn't produce new flattened paths (ends on scalar type)
|
|
1140
1158
|
// key is then empty string on both sides '' (=> equality)
|
|
1141
1159
|
// Path matches if lhs/rhs are available
|
|
1142
|
-
const xref = lhspaths
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
}, Object.create(null));
|
|
1146
|
-
|
|
1147
|
-
rhspaths.forEach(v => {
|
|
1148
|
-
const k = v.ref.slice(rhs.ref.length).join('.');
|
|
1149
|
-
if(xref[k])
|
|
1150
|
-
xref[k].rhs = v;
|
|
1151
|
-
else
|
|
1152
|
-
xref[k] = { rhs: v };
|
|
1153
|
-
});
|
|
1154
|
-
|
|
1160
|
+
const xref = createXRef(lhspaths, rhspaths, lhs, rhs, lhsIsVal, rhsIsVal);
|
|
1161
|
+
const xrefkeys = Object.keys(xref);
|
|
1162
|
+
const xrefvalues = Object.values(xref);
|
|
1155
1163
|
let cont = true;
|
|
1156
|
-
for(const xn in xref) {
|
|
1157
|
-
const x = xref[xn];
|
|
1158
1164
|
|
|
1165
|
+
if(op === 'like' && xrefvalues.reduce((a, v) => {
|
|
1166
|
+
return (v.lhs && v.rhs) ? a + 1: a;
|
|
1167
|
+
}, 0) === 0) {
|
|
1168
|
+
// error if intersection of paths is zero
|
|
1169
|
+
error(null, location, { lhs: pathName(lhs.ref), op, rhs: pathName(rhs.ref) },
|
|
1170
|
+
'Expected compatible types for $(LHS) $(OP) $(RHS)');
|
|
1171
|
+
cont = false;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
cont && xrefkeys.forEach(xn => {
|
|
1175
|
+
const x = xref[xn];
|
|
1176
|
+
const prefix = `${pathName(lhs.ref)} ${op} ${pathName(rhs.ref)}`;
|
|
1159
1177
|
// do the paths match?
|
|
1160
|
-
if(!(x.lhs && x.rhs)) {
|
|
1161
|
-
if(xn.length)
|
|
1162
|
-
error(null, location,
|
|
1163
|
-
|
|
1164
|
-
|
|
1178
|
+
if(op !== 'like' && !(x.lhs && x.rhs)) {
|
|
1179
|
+
if(xn.length) {
|
|
1180
|
+
error(null, location, { prefix, name: xn, alias: pathName((x.lhs ? rhs : lhs).ref) },
|
|
1181
|
+
'$(PREFIX): Sub path $(NAME) not found in $(ALIAS)');
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
error(null, location, { prefix, name: pathName((x.lhs ? lhs : rhs).ref), alias: pathName((x.lhs ? rhs : lhs).ref) },
|
|
1185
|
+
'$(PREFIX): Path $(NAME) does not match $(ALIAS)');
|
|
1186
|
+
}
|
|
1165
1187
|
cont = false;
|
|
1166
1188
|
}
|
|
1167
1189
|
// lhs && rhs are present, consistency checks that affect both ends
|
|
1168
1190
|
else {
|
|
1169
1191
|
// is lhs scalar?
|
|
1170
|
-
if(!isScalarOrNoType(x.lhs._art)) {
|
|
1171
|
-
error(null, location,
|
|
1192
|
+
if(!lhsIsVal && x.lhs && !isScalarOrNoType(x.lhs._art)) {
|
|
1193
|
+
error(null, location, { prefix, name: `${pathName(x.lhs.ref)}${(xn.length ? '.' + xn : '')}` },
|
|
1194
|
+
'$(PREFIX): Path $(NAME) must end on a scalar type')
|
|
1172
1195
|
cont = false;
|
|
1173
1196
|
}
|
|
1174
1197
|
// is rhs scalar?
|
|
1175
|
-
if(!isScalarOrNoType(x.rhs._art)) {
|
|
1176
|
-
error(null, location,
|
|
1198
|
+
if(!rhsIsVal && x.rhs && !isScalarOrNoType(x.rhs._art)) {
|
|
1199
|
+
error(null, location, { prefix, name: `${pathName(x.rhs.ref)}${(xn.length ? '.' + xn : '')}` },
|
|
1200
|
+
'$(PREFIX): Path $(NAME) must end on a scalar type');
|
|
1177
1201
|
cont = false;
|
|
1178
1202
|
}
|
|
1179
|
-
// info about type incompatibility if no other errors
|
|
1180
|
-
if(xn && cont) {
|
|
1203
|
+
// info about type incompatibility if no other errors occurred
|
|
1204
|
+
if(!(lhsIsVal || rhsIsVal) && x.lhs && x.rhs && xn && cont) {
|
|
1181
1205
|
const lhst = getType(x.lhs._art);
|
|
1182
1206
|
const rhst = getType(x.rhs._art);
|
|
1183
1207
|
if(lhst !== rhst) {
|
|
1184
|
-
info(null, location,
|
|
1208
|
+
info(null, location, { prefix, name: xn },'$(PREFIX): Types for sub path $(NAME) don\'t match');
|
|
1185
1209
|
}
|
|
1186
1210
|
}
|
|
1187
1211
|
}
|
|
1188
|
-
}
|
|
1212
|
+
});
|
|
1189
1213
|
// don't continue if there are path errors
|
|
1190
1214
|
if(!cont)
|
|
1191
1215
|
return expr;
|
|
1192
1216
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1217
|
+
// if lhs and rhs are refs set operator from 'like' to '='
|
|
1218
|
+
if(op === 'like' && !(lhsIsVal || rhsIsVal)) {
|
|
1219
|
+
op = '=';
|
|
1220
|
+
}
|
|
1221
|
+
// t_0 OR ... OR t_n with t = (a <not equal> b)
|
|
1222
|
+
const bop = (op === 'is' && not) || op === '!=' || op === '<>' ? 'or' : 'and';
|
|
1223
|
+
rc.push('(');
|
|
1224
|
+
xrefvalues.filter(x => x.lhs && x.rhs).forEach((x,i) => {
|
|
1195
1225
|
if(i>0)
|
|
1196
|
-
rc.push(
|
|
1226
|
+
rc.push(bop);
|
|
1197
1227
|
rc.push(x.lhs);
|
|
1198
1228
|
rc.push(op);
|
|
1229
|
+
if(not)
|
|
1230
|
+
rc.push('not')
|
|
1199
1231
|
rc.push(x.rhs);
|
|
1200
1232
|
});
|
|
1201
|
-
|
|
1233
|
+
rc.push(')');
|
|
1234
|
+
i += not ? 3 : 2;
|
|
1202
1235
|
}
|
|
1203
1236
|
else
|
|
1204
1237
|
rc.push(expr[i]);
|
|
@@ -1208,6 +1241,44 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1208
1241
|
}
|
|
1209
1242
|
return rc;
|
|
1210
1243
|
|
|
1244
|
+
function createXRef(lhspaths, rhspaths, lhs, rhs, lhsIsVal, rhsIsVal) {
|
|
1245
|
+
// mapping dict for lhs/rhs for mismatch check
|
|
1246
|
+
// strip lhs/rhs prefix from flattened paths to check remaining common trailing path
|
|
1247
|
+
// if path is idempotent, it doesn't produce new flattened paths (ends on scalar type)
|
|
1248
|
+
// key is then empty string on both sides '' (=> equality)
|
|
1249
|
+
// Path matches if lhs/rhs are available
|
|
1250
|
+
let xref;
|
|
1251
|
+
if(!lhsIsVal) {
|
|
1252
|
+
xref = lhspaths.reduce((a, v) => {
|
|
1253
|
+
a[v.ref.slice(lhs.ref.length).join('.')] = rhsIsVal ? { lhs: v, rhs } : { lhs: v };
|
|
1254
|
+
return a;
|
|
1255
|
+
}, Object.create(null));
|
|
1256
|
+
|
|
1257
|
+
rhspaths.forEach(v => {
|
|
1258
|
+
const k = v.ref.slice(rhs.ref.length).join('.');
|
|
1259
|
+
if(xref[k])
|
|
1260
|
+
xref[k].rhs = v;
|
|
1261
|
+
else
|
|
1262
|
+
xref[k] = { rhs: v };
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
else if(!rhsIsVal) {
|
|
1266
|
+
xref = rhspaths.reduce((a, v) => {
|
|
1267
|
+
a[v.ref.slice(rhs.ref.length).join('.')] = lhsIsVal ? { lhs, rhs: v } : { rhs: v };
|
|
1268
|
+
return a;
|
|
1269
|
+
}, Object.create(null));
|
|
1270
|
+
|
|
1271
|
+
lhspaths.forEach(v => {
|
|
1272
|
+
const k = v.ref.slice(lhs.ref.length).join('.');
|
|
1273
|
+
if(xref[k])
|
|
1274
|
+
xref[k].lhs = v;
|
|
1275
|
+
else
|
|
1276
|
+
xref[k] = { lhs: v };
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
return xref;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1211
1282
|
function getType(art) {
|
|
1212
1283
|
const effart = effectiveType(art);
|
|
1213
1284
|
return Object.keys(effart).length ? effart : art.type;
|
|
@@ -1218,7 +1289,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1218
1289
|
if(art) {
|
|
1219
1290
|
// items in ON conds are illegal but this should be checked elsewhere
|
|
1220
1291
|
const elements = art.elements || (art.items && art.items.elements);
|
|
1221
|
-
return (elements || art.target && art.keys)
|
|
1292
|
+
return !!(elements || art.target && art.keys)
|
|
1222
1293
|
}
|
|
1223
1294
|
return false;
|
|
1224
1295
|
}
|
|
@@ -1241,7 +1312,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1241
1312
|
|
|
1242
1313
|
}
|
|
1243
1314
|
|
|
1244
|
-
|
|
1245
1315
|
/**
|
|
1246
1316
|
* Modify the given CSN/artifact in-place, applying the given customTransformations.
|
|
1247
1317
|
* Dictionaries are correctly handled - a "type" transformer will not be called on an entity called "type".
|
|
@@ -1333,5 +1403,6 @@ function transformModel(csn, customTransformations, transformNonEnumerableElemen
|
|
|
1333
1403
|
module.exports = {
|
|
1334
1404
|
// This function retrieves the actual exports
|
|
1335
1405
|
getTransformers,
|
|
1336
|
-
transformModel
|
|
1406
|
+
transformModel,
|
|
1407
|
+
RelationalOperators
|
|
1337
1408
|
};
|
|
@@ -9,7 +9,7 @@ const { deduplicateMessages } = require('../base/messages');
|
|
|
9
9
|
const { timetrace } = require('../utils/timetrace');
|
|
10
10
|
// Paths that start with an artifact of protected kind are special
|
|
11
11
|
// either ignore them in QAT building or in path rewriting
|
|
12
|
-
const internalArtifactKinds = ['builtin'
|
|
12
|
+
const internalArtifactKinds = ['builtin', '$parameters', 'param'];
|
|
13
13
|
|
|
14
14
|
function translateAssocsToJoinsCSN(csn, options){
|
|
15
15
|
timetrace.start('Recompiling model');
|
|
@@ -444,7 +444,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
444
444
|
}
|
|
445
445
|
else if(art.target) { // it's not an artifact, so it should be an assoc step
|
|
446
446
|
if(joinTree === undefined)
|
|
447
|
-
throw Error('
|
|
447
|
+
throw Error('Can\'t follow Associations without starting Entity');
|
|
448
448
|
|
|
449
449
|
if(!childQat.$QA)
|
|
450
450
|
childQat.$QA = createQA(env, art.target._artifact, art.name.id, childQat._namedArgs);
|
|
@@ -762,7 +762,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
762
762
|
|
|
763
763
|
let [head, ...tail] = path;
|
|
764
764
|
|
|
765
|
-
|
|
765
|
+
// don't rewrite path
|
|
766
|
+
if(internalArtifactKinds.includes(head._artifact.kind))
|
|
766
767
|
return pathNode;
|
|
767
768
|
|
|
768
769
|
// strip the absolute path indicators
|
|
@@ -901,29 +902,29 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
901
902
|
// source side from view point of view (target side from forward point of view)
|
|
902
903
|
path = tail; // pop assoc step
|
|
903
904
|
let elt = env.lead._combined[path[0].id];
|
|
904
|
-
let err = 'Element "' + path[0].id +
|
|
905
|
-
'" referred in association "' + assoc.name.id +'" of Artifact "' + assoc.name.absolute +'"';
|
|
906
905
|
|
|
907
906
|
if(elt) {
|
|
908
907
|
if(Array.isArray(elt)) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
908
|
+
const names = elt.map(e => e._origin.name.absolute);
|
|
909
|
+
error(null, [ assocQAT._origin.location, assocQAT._origin ], { elemref: path[0].id, id: assoc.name.id, art: assoc.name.absolute, names },
|
|
910
|
+
'Element $(ELEMREF) referred in association $(ID) of artifact $(ART) is available from multiple query sources $(NAMES)');
|
|
912
911
|
return pathNode.path;
|
|
913
912
|
} else {
|
|
914
913
|
// check if element has same origin on both ends
|
|
915
914
|
if(elt._origin._main !== path[0]._artifact._origin._main) {
|
|
916
|
-
|
|
917
|
-
path[0].
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
915
|
+
warning(null, [ assocQAT._origin.location, assocQAT._origin ], {
|
|
916
|
+
elemref: path[0].id,
|
|
917
|
+
id: assoc.name.id, art: assoc.name.absolute,
|
|
918
|
+
name: path[0]._artifact._origin._main.name.absolute,
|
|
919
|
+
alias: elt._origin._main.name.absolute,
|
|
920
|
+
source: elt._main.name.absolute,
|
|
921
|
+
}, 'Element $(ELEMREF) referred in association $(ID) of artifact $(ART) originates from $(NAME) and from $(ALIAS) in $(SOURCE)');
|
|
921
922
|
}
|
|
922
923
|
_navigation = elt._parent;
|
|
923
924
|
}
|
|
924
925
|
} else {
|
|
925
|
-
|
|
926
|
-
|
|
926
|
+
error(null, [ assocQAT._origin.location, assocQAT._origin ], { elemref: path[0].id, id: assoc.name.id, art: assoc.name.absolute },
|
|
927
|
+
'Element $(ELEMREF) referred in association $(ID) of artifact $(ART) has not been found');
|
|
927
928
|
return pathNode.path;
|
|
928
929
|
}
|
|
929
930
|
} else {
|
|
@@ -1536,7 +1537,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1536
1537
|
( // not leaf
|
|
1537
1538
|
i < tail.length-1 &&
|
|
1538
1539
|
// path terminates on a scalar type
|
|
1539
|
-
// _effectiveType.elements can be removed if
|
|
1540
|
+
// _effectiveType.elements can be removed if forRelationalDB can expand fk paths correctly
|
|
1540
1541
|
!(tail[tail.length-1]._artifact._effectiveType.elements || tail[tail.length-1]._artifact._effectiveType.target) &&
|
|
1541
1542
|
// association is managed
|
|
1542
1543
|
art.foreignKeys &&
|
|
@@ -173,13 +173,13 @@ function _errorFileNotFound(dep, options, { error }) {
|
|
|
173
173
|
resolved = resolved.replace( /\\/g, '/' );
|
|
174
174
|
for (const from of dep.usingFroms) {
|
|
175
175
|
error( 'file-not-readable', from.location, { file: resolved },
|
|
176
|
-
'
|
|
176
|
+
'Can\'t read file $(FILE)' );
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
else if (isLocalFile( dep.module ) ) {
|
|
180
180
|
for (const from of dep.usingFroms) {
|
|
181
181
|
error( 'file-unknown-local', from.location, { file: dep.module },
|
|
182
|
-
'
|
|
182
|
+
'Can\'t find local module $(FILE)' );
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
else {
|
|
@@ -187,8 +187,8 @@ function _errorFileNotFound(dep, options, { error }) {
|
|
|
187
187
|
for (const from of dep.usingFroms) {
|
|
188
188
|
error( 'file-unknown-package', from.location,
|
|
189
189
|
{ file: dep.module, '#': internal }, {
|
|
190
|
-
std: '
|
|
191
|
-
internal: '
|
|
190
|
+
std: 'Can\'t find package $(FILE)',
|
|
191
|
+
internal: 'Can\'t find package module $(FILE)',
|
|
192
192
|
} );
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -417,7 +417,7 @@ function resolveCDS(moduleName, options, callback) {
|
|
|
417
417
|
* @returns {Error}
|
|
418
418
|
*/
|
|
419
419
|
function makeNotFoundError() {
|
|
420
|
-
const moduleError = new Error(`
|
|
420
|
+
const moduleError = new Error(`Can't find module '${ moduleName }' from '${ options.basedir }'`);
|
|
421
421
|
// eslint-disable-next-line
|
|
422
422
|
moduleError['code'] = 'MODULE_NOT_FOUND';
|
|
423
423
|
return moduleError;
|
package/lib/utils/objectUtils.js
CHANGED
|
@@ -37,7 +37,7 @@ function createDict(obj) {
|
|
|
37
37
|
*/
|
|
38
38
|
function forEach(obj, callback) {
|
|
39
39
|
for (const key in obj) {
|
|
40
|
-
if (Object.hasOwnProperty.call(obj, key))
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(obj, key))
|
|
41
41
|
callback(key, obj[key]);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -51,7 +51,7 @@ function forEach(obj, callback) {
|
|
|
51
51
|
*/
|
|
52
52
|
function forEachValue(o, callback) {
|
|
53
53
|
for (const key in o) {
|
|
54
|
-
if (Object.hasOwnProperty.call(o, key))
|
|
54
|
+
if (Object.prototype.hasOwnProperty.call(o, key))
|
|
55
55
|
callback(o[key]);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -65,7 +65,7 @@ function forEachValue(o, callback) {
|
|
|
65
65
|
*/
|
|
66
66
|
function forEachKey(o, callback) {
|
|
67
67
|
for (const key in o) {
|
|
68
|
-
if (Object.hasOwnProperty.call(o, key))
|
|
68
|
+
if (Object.prototype.hasOwnProperty.call(o, key))
|
|
69
69
|
callback(key);
|
|
70
70
|
}
|
|
71
71
|
}
|
package/lib/utils/term.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//
|
|
2
2
|
// This file is used for color output to stderr and stdout.
|
|
3
|
-
// Use `term.
|
|
3
|
+
// Use `term.asError`, `term.asWarn` and `term.asInfo` as they use color output
|
|
4
4
|
// per default if the process runs in a TTY, i.e. stdout as well as
|
|
5
5
|
// stderr are TTYs. stderr/stdout are no TTYs if they are
|
|
6
6
|
// (for example) piped into another process or written to file:
|
|
@@ -88,10 +88,10 @@ function term(useColor = 'auto') {
|
|
|
88
88
|
bold,
|
|
89
89
|
|
|
90
90
|
severity: asSeverity,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
asError,
|
|
92
|
+
asWarning,
|
|
93
|
+
asInfo,
|
|
94
|
+
asHelp,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.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)",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"download": "node scripts/downloadANTLR.js",
|
|
18
18
|
"gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js",
|
|
19
|
-
"xmakeBeforeInstall": "echo \"Due to binary mirror, use sqlite 5.
|
|
19
|
+
"xmakeBeforeInstall": "echo \"Due to binary mirror, use sqlite 5.1.2 explicitly\" && npm install --save --save-exact --no-package-lock sqlite3@5.1.2",
|
|
20
20
|
"xmakeAfterInstall": "npm run gen",
|
|
21
21
|
"xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
|
|
22
22
|
"test": "node scripts/verifyGrammarChecksum.js && mocha --reporter min --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter min --reporter-option maxDiffSize=0 test/ test3/",
|
|
@@ -11,7 +11,7 @@ CDL file is equivalent to a layer.
|
|
|
11
11
|
|
|
12
12
|
Erroneous code example using four CDS files:
|
|
13
13
|
|
|
14
|
-
```
|
|
14
|
+
```cds
|
|
15
15
|
// (1) Base.cds: Contains the artifact that should be annotated
|
|
16
16
|
entity FooBar { }
|
|
17
17
|
|
|
@@ -41,15 +41,15 @@ represents one layer.
|
|
|
41
41
|
|
|
42
42
|
## How to Fix
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
Remove one of the duplicate annotations. Chances are, that only one was
|
|
45
|
+
intended to begin with. For the erroneous example above, remove the annotation
|
|
46
|
+
from (3).
|
|
47
47
|
|
|
48
48
|
Alternatively, add an annotation assignment to (4). This annotation has
|
|
49
49
|
precedence and the error will vanish. For the example above, (4) will look
|
|
50
50
|
like this:
|
|
51
51
|
|
|
52
|
-
```
|
|
52
|
+
```cds
|
|
53
53
|
// (4) All.cds: Combine all files
|
|
54
54
|
using from './FooAnnotate';
|
|
55
55
|
using from './BarAnnotate';
|
|
@@ -60,7 +60,7 @@ annotate FooBar with @Anno: 'Bar';
|
|
|
60
60
|
You can also make (3) depend on (2) so that they are no longer in unrelated
|
|
61
61
|
layers and the compiler can determine which annotation to apply.
|
|
62
62
|
|
|
63
|
-
```
|
|
63
|
+
```cds
|
|
64
64
|
// (3) BarAnnotate.cds: Now depends on (2)
|
|
65
65
|
using from './FooAnnotate';
|
|
66
66
|
annotate FooBar with @Anno: 'Bar';
|
|
@@ -4,13 +4,13 @@ An element in a `type of` expression doesn’t have proper type information.
|
|
|
4
4
|
|
|
5
5
|
The message's severity is `Info` but may be raised to `Error` in the SQL,
|
|
6
6
|
SAP HANA, and OData backends. These backends require elements to have a type.
|
|
7
|
-
Otherwise they aren’t able to render elements (for example, to SQL columns).
|
|
7
|
+
Otherwise, they aren’t able to render elements (for example, to SQL columns).
|
|
8
8
|
|
|
9
9
|
## Example
|
|
10
10
|
|
|
11
11
|
Erroneous code example:
|
|
12
12
|
|
|
13
|
-
```
|
|
13
|
+
```cds
|
|
14
14
|
entity Foo {
|
|
15
15
|
key id : Integer;
|
|
16
16
|
};
|
|
@@ -33,9 +33,9 @@ properties but won’t have a proper type, which is required by some backends.
|
|
|
33
33
|
|
|
34
34
|
## How to Fix
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Assign an explicit type to `ViewFoo:calculatedField`.
|
|
37
37
|
|
|
38
|
-
```
|
|
38
|
+
```cds
|
|
39
39
|
view ViewFoo as select from Foo {
|
|
40
40
|
1+1 as calculatedField @(anno) : Integer
|
|
41
41
|
};
|
|
@@ -27,8 +27,8 @@ Erroneous code example:
|
|
|
27
27
|
|
|
28
28
|
## How to Fix
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
Add explicit type information to `MainType`, for example, add an `elements`
|
|
31
|
+
property to make a structured type.
|
|
32
32
|
|
|
33
33
|
```json
|
|
34
34
|
{
|
|
@@ -15,7 +15,7 @@ adapt your model to fix the error.
|
|
|
15
15
|
|
|
16
16
|
Erroneous code example:
|
|
17
17
|
|
|
18
|
-
```
|
|
18
|
+
```cds
|
|
19
19
|
// (1)
|
|
20
20
|
entity ns.first.Foo {
|
|
21
21
|
key parent : Association to one ns.Base;
|
|
@@ -46,13 +46,13 @@ of (2) and (3) are ignored, a name collision happens.
|
|
|
46
46
|
|
|
47
47
|
## How to Fix
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
You need to explicitly expose one or more entities under a name that does not
|
|
50
|
+
exist in the service, yet.
|
|
51
51
|
|
|
52
52
|
For the erroneous example above, you could add these two lines to the service
|
|
53
53
|
`ns.MyService`:
|
|
54
54
|
|
|
55
|
-
```
|
|
55
|
+
```cds
|
|
56
56
|
entity first.Foo as projection on ns.first.Foo; // (5)
|
|
57
57
|
entity second.Foo as projection on ns.second.Foo; // (6)
|
|
58
58
|
```
|
|
@@ -11,7 +11,7 @@ They form a cyclic connection through their dependencies
|
|
|
11
11
|
|
|
12
12
|
Erroneous code example with a single CDL file:
|
|
13
13
|
|
|
14
|
-
```
|
|
14
|
+
```cds
|
|
15
15
|
entity FooBar { }
|
|
16
16
|
|
|
17
17
|
extend FooBar { foo : Integer; }
|
|
@@ -24,7 +24,7 @@ inside `FooBar` may not be stable. You therefore can’t depend on it.
|
|
|
24
24
|
It's also possible to trigger this warning with multiple files.
|
|
25
25
|
Look at the following example:
|
|
26
26
|
|
|
27
|
-
```
|
|
27
|
+
```cds
|
|
28
28
|
// (1) Definition.cds
|
|
29
29
|
using from './Extension.cds';
|
|
30
30
|
entity FooBar { };
|
|
@@ -40,10 +40,9 @@ layer with multiple extensions. Again, the element order isn’t stable.
|
|
|
40
40
|
|
|
41
41
|
## How to Fix
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
block:
|
|
43
|
+
Move extensions for the same artifact into the same extension block:
|
|
45
44
|
|
|
46
|
-
```
|
|
45
|
+
```cds
|
|
47
46
|
// (1) Definition.cds : No extension block
|
|
48
47
|
using from './Extension.cds';
|
|
49
48
|
entity FooBar { }
|
|
@@ -11,7 +11,7 @@ They form a cyclic connection through their dependencies
|
|
|
11
11
|
|
|
12
12
|
Erroneous code example using four CDS files:
|
|
13
13
|
|
|
14
|
-
```
|
|
14
|
+
```cds
|
|
15
15
|
// (1) Base.cds: Contains the artifact that should be extended
|
|
16
16
|
entity FooBar { }
|
|
17
17
|
|
|
@@ -38,13 +38,13 @@ represents one layer.
|
|
|
38
38
|
|
|
39
39
|
## How to Fix
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
Move extensions for the same artifact into the same layer, that is, the same
|
|
42
|
+
file.
|
|
43
43
|
|
|
44
44
|
For the erroneous example above, remove the extension from (3) and move
|
|
45
45
|
it to (2):
|
|
46
46
|
|
|
47
|
-
```
|
|
47
|
+
```cds
|
|
48
48
|
// (2) FooExtend.cds
|
|
49
49
|
using from './Base';
|
|
50
50
|
extend FooBar {
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
"extend-repeated-intralayer",
|
|
9
9
|
"extend-unrelated-layer",
|
|
10
10
|
"redirected-to-ambiguous",
|
|
11
|
+
"redirected-to-complex",
|
|
11
12
|
"redirected-to-unrelated",
|
|
12
13
|
"rewrite-not-supported",
|
|
13
|
-
"syntax-
|
|
14
|
+
"syntax-expecting-integer",
|
|
15
|
+
"wildcard-excluding-one"
|
|
14
16
|
]
|
|
15
17
|
}
|