@sap/cds-compiler 3.1.2 → 3.3.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 (100) hide show
  1. package/CHANGELOG.md +80 -3
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +18 -0
  4. package/lib/api/main.js +8 -13
  5. package/lib/base/error.js +2 -2
  6. package/lib/base/keywords.js +2 -24
  7. package/lib/base/message-registry.js +43 -14
  8. package/lib/base/messages.js +20 -10
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/actionsFunctions.js +1 -1
  11. package/lib/checks/annotationsOData.js +2 -2
  12. package/lib/checks/arrayOfs.js +15 -7
  13. package/lib/checks/cdsPersistence.js +1 -1
  14. package/lib/checks/checkForTypes.js +48 -0
  15. package/lib/checks/defaultValues.js +2 -2
  16. package/lib/checks/elements.js +81 -6
  17. package/lib/checks/foreignKeys.js +12 -13
  18. package/lib/checks/invalidTarget.js +10 -11
  19. package/lib/checks/managedInType.js +21 -15
  20. package/lib/checks/nullableKeys.js +1 -1
  21. package/lib/checks/onConditions.js +9 -9
  22. package/lib/checks/parameters.js +21 -0
  23. package/lib/checks/selectItems.js +1 -1
  24. package/lib/checks/types.js +2 -2
  25. package/lib/checks/utils.js +17 -7
  26. package/lib/checks/validator.js +26 -14
  27. package/lib/compiler/assert-consistency.js +13 -6
  28. package/lib/compiler/builtins.js +8 -0
  29. package/lib/compiler/checks.js +40 -33
  30. package/lib/compiler/define.js +50 -44
  31. package/lib/compiler/extend.js +303 -37
  32. package/lib/compiler/kick-start.js +2 -35
  33. package/lib/compiler/populate.js +83 -62
  34. package/lib/compiler/propagator.js +1 -1
  35. package/lib/compiler/resolve.js +61 -104
  36. package/lib/compiler/shared.js +16 -6
  37. package/lib/compiler/tweak-assocs.js +25 -12
  38. package/lib/compiler/utils.js +2 -2
  39. package/lib/edm/annotations/genericTranslation.js +3 -3
  40. package/lib/edm/csn2edm.js +10 -10
  41. package/lib/edm/edm.js +17 -9
  42. package/lib/edm/edmPreprocessor.js +53 -30
  43. package/lib/edm/edmUtils.js +7 -2
  44. package/lib/gen/Dictionary.json +14 -0
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +3 -2
  47. package/lib/gen/languageParser.js +4205 -4100
  48. package/lib/inspect/inspectModelStatistics.js +1 -1
  49. package/lib/inspect/inspectPropagation.js +23 -9
  50. package/lib/json/csnVersion.js +1 -1
  51. package/lib/json/from-csn.js +26 -19
  52. package/lib/json/to-csn.js +47 -5
  53. package/lib/language/antlrParser.js +1 -1
  54. package/lib/language/genericAntlrParser.js +29 -13
  55. package/lib/language/language.g4 +28 -8
  56. package/lib/main.d.ts +3 -6
  57. package/lib/model/.eslintrc.json +13 -0
  58. package/lib/model/api.js +4 -2
  59. package/lib/model/csnRefs.js +74 -47
  60. package/lib/model/csnUtils.js +236 -218
  61. package/lib/model/enrichCsn.js +41 -31
  62. package/lib/model/revealInternalProperties.js +61 -57
  63. package/lib/model/sortViews.js +31 -31
  64. package/lib/modelCompare/compare.js +6 -6
  65. package/lib/optionProcessor.js +5 -0
  66. package/lib/render/manageConstraints.js +2 -2
  67. package/lib/render/toCdl.js +31 -44
  68. package/lib/render/toHdbcds.js +7 -5
  69. package/lib/render/toRename.js +4 -4
  70. package/lib/render/toSql.js +11 -5
  71. package/lib/render/utils/common.js +20 -9
  72. package/lib/render/utils/sql.js +5 -5
  73. package/lib/transform/db/applyTransformations.js +32 -3
  74. package/lib/transform/db/expansion.js +81 -37
  75. package/lib/transform/db/flattening.js +1 -1
  76. package/lib/transform/db/temporal.js +1 -1
  77. package/lib/transform/db/transformExists.js +1 -1
  78. package/lib/transform/forOdataNew.js +10 -7
  79. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
  80. package/lib/transform/localized.js +28 -19
  81. package/lib/transform/odata/toFinalBaseType.js +8 -11
  82. package/lib/transform/odata/typesExposure.js +1 -1
  83. package/lib/transform/transformUtilsNew.js +101 -39
  84. package/lib/transform/translateAssocsToJoins.js +5 -4
  85. package/lib/utils/moduleResolve.js +5 -5
  86. package/lib/utils/objectUtils.js +3 -3
  87. package/package.json +2 -2
  88. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  89. package/share/messages/check-proper-type-of.md +4 -4
  90. package/share/messages/check-proper-type.md +2 -2
  91. package/share/messages/duplicate-autoexposed.md +4 -4
  92. package/share/messages/extend-repeated-intralayer.md +4 -5
  93. package/share/messages/extend-unrelated-layer.md +4 -4
  94. package/share/messages/message-explanations.json +3 -1
  95. package/share/messages/redirected-to-ambiguous.md +7 -6
  96. package/share/messages/redirected-to-complex.md +63 -0
  97. package/share/messages/redirected-to-unrelated.md +6 -5
  98. package/share/messages/rewrite-not-supported.md +4 -4
  99. package/share/messages/syntax-expected-integer.md +3 -3
  100. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
4
+
5
+ /**
6
+ * Check that `cds.hana` types are not used - we don't support them for postgres
7
+ *
8
+ * @param {object} parent Object with a type
9
+ * @param {string} name Name of the type property on parent
10
+ * @param {Array} type type to check
11
+ * @param {CSN.Path} path
12
+ */
13
+ function checkForHanaTypes(parent, name, type, path) {
14
+ const artifact = this.csn.definitions[path[1]];
15
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && typeof parent.type === 'string' && parent.type.startsWith('cds.hana.'))
16
+ this.error('ref-unexpected-hana-type', [ ...path, 'type' ], {}, `Types in the ”cds.hana“ namespace can't be used with sqlDialect “postgres”`);
17
+ }
18
+
19
+ /**
20
+ * Check that `cds.UInt8`is not used - we don't have a clear idea how to represent it on postgres
21
+ *
22
+ * @param {object} parent Object with a type
23
+ * @param {string} name Name of the type property on parent
24
+ * @param {Array} type type to check
25
+ * @param {CSN.Path} path
26
+ */
27
+ function checkForUInt8(parent, name, type, path) {
28
+ const artifact = this.csn.definitions[path[1]];
29
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && parent.type === 'cds.UInt8')
30
+ this.error('ref-unexpected-type', [ ...path, 'type' ], {}, `Type ”cds.UInt8“ can't be used with sqlDialect “postgres”`);
31
+ }
32
+
33
+ /**
34
+ * Check types - specifically for postgres
35
+ *
36
+ * @param {object} parent Object with a type
37
+ * @param {string} name Name of the type property on parent
38
+ * @param {Array} type type to check
39
+ * @param {CSN.Path} path
40
+ */
41
+ function checkTypes(parent, name, type, path) {
42
+ checkForHanaTypes.bind(this)(parent, name, type, path);
43
+ checkForUInt8.bind(this)(parent, name, type, path);
44
+ }
45
+
46
+ module.exports = {
47
+ type: checkTypes,
48
+ };
@@ -21,7 +21,7 @@ function validateDefaultValues(member, memberName, prop, path) {
21
21
  while (member.default.xpr[i] === '-' || member.default.xpr[i] === '+')
22
22
  i++;
23
23
  if (i > 1)
24
- this.error(null, path, `Illegal number of unary '+/-' operators`);
24
+ this.error(null, path, {}, `Illegal number of unary '+/-' operators`);
25
25
  }
26
26
  }
27
27
  }
@@ -36,7 +36,7 @@ function validateDefaultValues(member, memberName, prop, path) {
36
36
  */
37
37
  function rejectParamDefaultsInHanaCds(member, memberName, prop, path) {
38
38
  if (member.default && prop === 'params' && this.options.transformation === 'hdbcds')
39
- this.error(null, path, 'Parameter default values are not supported in SAP HANA CDS');
39
+ this.error(null, path, {}, 'Parameter default values are not supported in SAP HANA CDS');
40
40
  }
41
41
 
42
42
  /**
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const { forEachMember, forEachMemberRecursively } = require('../model/csnUtils');
3
+ const { forEachMember, forEachMemberRecursively, isBuiltinType } = require('../model/csnUtils');
4
4
  const { isGeoTypeName } = require('../compiler/builtins');
5
-
5
+ const { setProp } = require('../base/model');
6
6
  // Only to be used with validator.js - a correct `this` value needs to be provided!
7
7
 
8
8
  /**
@@ -42,13 +42,15 @@ function checkPrimaryKey(art) {
42
42
  { type: finalBaseType.type, name: elemFqName },
43
43
  'Type $(TYPE) can\'t be used as primary key in element $(NAME)');
44
44
  }
45
- else if (finalBaseType && this.csnUtils.isStructured(finalBaseType)) {
45
+ else if (finalBaseType && this.csnUtils.isStructured(finalBaseType) && !finalBaseType.$visited) {
46
+ setProp(finalBaseType, '$visited', true);
46
47
  forEachMemberRecursively(finalBaseType,
47
48
  (subMember, subMemberName) => checkIfPrimaryKeyIsOfGeoType
48
49
  .bind(this)(subMember,
49
50
  `${ elemFqName }/${ subMemberName }`,
50
51
  member.key || parentIsKey,
51
52
  member.$path));
53
+ delete finalBaseType.$visited;
52
54
  }
53
55
  }
54
56
  }
@@ -68,13 +70,15 @@ function checkPrimaryKey(art) {
68
70
  this.error(null, parentPath || member.$path, { name: elemFqName },
69
71
  'Array-like type in element $(NAME) can\'t be used as primary key');
70
72
  }
71
- else if (finalBaseType && this.csnUtils.isStructured(finalBaseType)) {
73
+ else if (finalBaseType && this.csnUtils.isStructured(finalBaseType) && !finalBaseType.$visited) {
74
+ setProp(finalBaseType, '$visited', true);
72
75
  forEachMemberRecursively(finalBaseType,
73
76
  (subMember, subMemberName) => checkIfPrimaryKeyIsArray
74
77
  .bind(this)(subMember,
75
78
  `${ elemFqName }/${ subMemberName }`,
76
79
  member.key || parentIsKey,
77
80
  member.$path));
81
+ delete finalBaseType.$visited;
78
82
  }
79
83
  }
80
84
  }
@@ -89,7 +93,7 @@ function checkPrimaryKey(art) {
89
93
  function checkVirtualElement(member) {
90
94
  if (member.virtual) {
91
95
  if (this.csnUtils.isAssociation(member.type)) { // or Composition ???
92
- this.error(null, member.$path, `Element can't be virtual and an association`);
96
+ this.error(null, member.$path, {}, `Element can't be virtual and an association`);
93
97
  }
94
98
  }
95
99
  }
@@ -135,4 +139,75 @@ function checkManagedAssoc(art) {
135
139
  }
136
140
  }
137
141
 
138
- module.exports = { checkPrimaryKey, checkVirtualElement, checkManagedAssoc };
142
+ /**
143
+ * All DB & OData flat mode must reject recursive type usages in entities
144
+ * 'items' break recursion as 'items' will turn into an NCLOB and the path
145
+ * prefix to 'items' can be flattend in the DB.
146
+ * In OData flat mode the first appearance of 'items' breaks out into structured
147
+ * mode producing (legal) recursive complex types.
148
+ *
149
+ * @param {CSN.Artifact} art The artifact
150
+ */
151
+ function checkRecursiveTypeUsage(art) {
152
+ const visit = (def) => {
153
+ const loc = def.$path;
154
+ // recursive types are allowed inside arrays
155
+ if (def.items)
156
+ return;
157
+ let { type } = def;
158
+ let prevType;
159
+ let isDeref = false;
160
+ if (type && !isBuiltinType(type) && !def.elements) {
161
+ do {
162
+ prevType = type;
163
+ // TODO: `type.ref.length > 1`, but OData backend must be tested first (#5144)
164
+ // e.g. `{ ref: [ "MyType" ] }`
165
+ if (type.ref) {
166
+ def = this.artifactRef(type);
167
+ isDeref = true;
168
+ }
169
+ else {
170
+ def = this.csn.definitions[type];
171
+ }
172
+ type = def.type;
173
+ } while (type && !isBuiltinType(type) && !def.items && !def.elements && prevType !== type);
174
+ }
175
+ if (def.$visited || (type && prevType === type)) {
176
+ // Recursion via type is allowed in V4 struct, but not via dereferencing
177
+ if (!isDeref && this.options.odataVersion === 'v4' && this.options.odataFormat === 'structured')
178
+ return;
179
+ if (!def.$recErr) {
180
+ this.error(null, loc, {}, 'Unexpected recursive type definition');
181
+ setProp(def, '$recErr', true);
182
+ }
183
+ }
184
+ else if (def.elements) {
185
+ setProp(def, '$visited', true);
186
+ for (const n in def.elements)
187
+ visit(def.elements[n]);
188
+ delete def.$visited;
189
+ }
190
+ };
191
+ // elements & params are flattening candidates
192
+ // FUTURE:
193
+ // Once we have universal CSN for the runtimes
194
+ // Validate service members only for OData
195
+ if (art.kind === 'entity') {
196
+ for (const n in art.elements)
197
+ visit(art.elements[n]);
198
+ for (const n in art.params)
199
+ visit(art.params[n]);
200
+ }
201
+ if (this.options.odataVersion) {
202
+ // func/action params/returns don't allow recursive type derefs
203
+ if (art.kind === 'action' || art.kind === 'function') {
204
+ for (const n in art.params)
205
+ visit(art.params[n]);
206
+ if (art.returns)
207
+ visit(art.returns);
208
+ }
209
+ }
210
+ }
211
+ module.exports = {
212
+ checkPrimaryKey, checkVirtualElement, checkManagedAssoc, checkRecursiveTypeUsage,
213
+ };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { setProp } = require('../base/model');
4
+
3
5
  // Only to be used with validator.js - a correct this value needs to be provided!
4
6
 
5
7
  /**
@@ -41,10 +43,10 @@ function validateForeignKeys(member) {
41
43
  // Declared as arrow-function to keep scope the same (this value)
42
44
  const checkForItems = (mem) => {
43
45
  if (mem.items) {
44
- this.error(null, member.$path, 'Array-like properties must not be foreign keys');
46
+ this.error(null, member.$path, {}, 'Array-like properties must not be foreign keys');
45
47
  }
46
48
  else if (isUnmanagedAssoc(mem)) {
47
- this.error(null, member.$path, 'Unmanaged association must not be a foreign key');
49
+ this.error(null, member.$path, {}, 'Unmanaged association must not be a foreign key');
48
50
  }
49
51
  else if (mem.keys) {
50
52
  handleAssociation(mem);
@@ -52,17 +54,14 @@ function validateForeignKeys(member) {
52
54
  else if (mem.elements) {
53
55
  handleStructured(mem);
54
56
  }
55
- else if (mem.type && mem.type.ref) { // type of
56
- checkForItems(this.artifactRef(mem.type));
57
- }
58
- else { // type T where T might contain items
59
- const type = this.csn.definitions[mem.type];
60
- if (type) {
61
- if (type.keys)
62
- handleAssociation(type);
63
-
64
- else if (type.elements)
65
- handleStructured(type);
57
+ else if (mem.type) {
58
+ const type = mem.type.ref
59
+ ? this.artifactRef(mem.type)
60
+ : this.csn.definitions[mem.type];
61
+ if (type && !type.$visited) {
62
+ setProp(type, '$visited', true);
63
+ checkForItems(type);
64
+ delete type.$visited;
66
65
  }
67
66
  }
68
67
  };
@@ -3,6 +3,7 @@
3
3
  // Only to be used with validator.js - a correct this value needs to be provided!
4
4
 
5
5
  const { ModelError } = require('../base/error');
6
+ const { setProp } = require('../base/model');
6
7
 
7
8
  /**
8
9
  * Assert that targets of associations and compositions are entities.
@@ -38,18 +39,16 @@ function invalidTarget(member) {
38
39
  );
39
40
  }
40
41
  }
41
- else if (mem.type && mem.type.ref) {
42
- // type of
43
- checkForInvalidTarget(this.artifactRef(mem.type));
42
+ // elements have precedence over type
43
+ else if (mem.elements) {
44
+ handleStructured(mem);
44
45
  }
45
- else {
46
- // type T
47
- const type = this.csn.definitions[mem.type];
48
- if (type) {
49
- if (type.elements)
50
- handleStructured(type);
51
- else
52
- checkForInvalidTarget(type);
46
+ else if (mem.type) {
47
+ const type = mem.type.ref ? this.artifactRef(mem.type) : this.csn.definitions[mem.type];
48
+ if (type && !type.$visited) {
49
+ setProp(type, '$visited', true);
50
+ checkForInvalidTarget(type);
51
+ delete type.$visited;
53
52
  }
54
53
  }
55
54
  };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { setProp } = require('../base/model');
4
+
3
5
  // Only to be used with validator.js - a correct this value needs to be provided!
4
6
 
5
7
  /**
@@ -35,18 +37,20 @@ function checkUsedTypesForAnonymousAspectComposition(member) {
35
37
  }
36
38
  else if (mem.type && (mem.type === 'cds.Composition') && !mem.on) {
37
39
  if (!mem.target && mem.targetAspect && typeof mem.targetAspect !== 'string')
38
- this.error(null, member.$path, 'Types with anonymous aspect compositions can\'t be used');
40
+ this.error(null, member.$path, {}, 'Types with anonymous aspect compositions can\'t be used');
39
41
  }
40
42
  else if (mem.elements) {
41
43
  handleStructured(mem, assertNoAnonymousAspectComposition);
42
44
  }
43
- else if (mem.type && mem.type.ref) { // type of
44
- const type = this.artifactRef(mem.type);
45
- assertNoAnonymousAspectComposition(type);
46
- }
47
- else if (mem.type && this.csn.definitions[mem.type]) { // type T
48
- const type = this.csn.definitions[mem.type];
49
- assertNoAnonymousAspectComposition(type);
45
+ else if (mem.type) {
46
+ const type = mem.type.ref
47
+ ? this.artifactRef(mem.type)
48
+ : this.csn.definitions[mem.type];
49
+ if (type && !type.$visited) {
50
+ setProp(type, '$visited', true);
51
+ assertNoAnonymousAspectComposition(type);
52
+ delete type.$visited;
53
+ }
50
54
  }
51
55
  };
52
56
 
@@ -62,13 +66,15 @@ function checkUsedTypesForAnonymousAspectComposition(member) {
62
66
  else if (mem.elements) {
63
67
  handleStructured(mem, checkTypeUsages);
64
68
  }
65
- else if (mem.type && mem.type.ref) { // type of
66
- const type = this.artifactRef(mem.type);
67
- assertNoAnonymousAspectComposition(type);
68
- }
69
- else if (mem.type && this.csn.definitions[mem.type]) { // type T where T might contain items
70
- const type = this.csn.definitions[mem.type];
71
- assertNoAnonymousAspectComposition(type);
69
+ else if (mem.type) { // type of
70
+ const type = mem.type.ref
71
+ ? this.artifactRef(mem.type)
72
+ : this.csn.definitions[mem.type];
73
+ if (type && !type.$visited) {
74
+ setProp(type, '$visited', true);
75
+ assertNoAnonymousAspectComposition(type);
76
+ delete type.$visited;
77
+ }
72
78
  }
73
79
  };
74
80
 
@@ -7,7 +7,7 @@
7
7
  */
8
8
  function checkExplicitlyNullableKeys(element) {
9
9
  if (element.key && element.notNull === false)
10
- this.error(null, element.$path, 'Expecting primary key element to be not nullable');
10
+ this.error(null, element.$path, {}, 'Expecting primary key element to be not nullable');
11
11
  }
12
12
 
13
13
  module.exports = checkExplicitlyNullableKeys;
@@ -61,7 +61,7 @@ function validateOnCondition(member, memberName, property, path) {
61
61
  if (member && member.on) {
62
62
  // complain about nullability constraint on managed composition
63
63
  if (member.targetAspect && {}.hasOwnProperty.call(member, 'notNull')) {
64
- this.warning(null, path.concat([ 'on' ]),
64
+ this.warning(null, path.concat([ 'on' ]), {},
65
65
  'Unexpected nullability constraint defined on managed composition');
66
66
  }
67
67
  for (let i = 0; i < member.on.length; i++) {
@@ -83,24 +83,24 @@ function validateOnCondition(member, memberName, property, path) {
83
83
  if (_links[j].art.target && !((_links[j].art === member) || ref[j] === '$self' || ref[j] === '$projection' || (validDollarSelf && j === _links.length - 1))) {
84
84
  if (_links[j].art.on) {
85
85
  // It's an unmanaged association - traversal is always forbidden
86
- this.error(null, csnPath, { id, elemref }, 'ON-Conditions can\'t follow unmanaged associations, step $(ID) of path $(ELEMREF)');
86
+ this.error(null, csnPath, { id, elemref }, 'ON-conditions can\'t follow unmanaged associations, step $(ID) of path $(ELEMREF)');
87
87
  }
88
88
  else {
89
89
  // It's a managed association - access of the foreign keys is allowed
90
90
  const nextRef = ref[j + 1].id || ref[j + 1];
91
91
  if (!_links[j].art.keys.some(r => r.ref[0] === nextRef))
92
- this.error(null, csnPath, { id, elemref }, 'ON-Conditions can only follow managed associations to the foreign keys of the managed association, step $(ID) of path $(ELEMREF)');
92
+ this.error(null, csnPath, { id, elemref }, 'ON-conditions can only follow managed associations to the foreign keys of the managed association, step $(ID) of path $(ELEMREF)');
93
93
  }
94
94
  }
95
95
 
96
96
  if (_links[j].art.virtual)
97
- this.error(null, csnPath, { id, elemref }, 'Virtual elements can\'t be used in ON-Conditions, step $(ID) of path $(ELEMREF)');
97
+ this.error(null, csnPath, { id, elemref }, 'Virtual elements can\'t be used in ON-conditions, step $(ID) of path $(ELEMREF)');
98
98
 
99
99
  if (ref[j].where)
100
- this.error(null, csnPath, { id, elemref }, 'ON-Conditions must not contain filters, step $(ID) of path $(ELEMREF)');
100
+ this.error(null, csnPath, { id, elemref }, 'ON-conditions must not contain filters, step $(ID) of path $(ELEMREF)');
101
101
 
102
102
  if (ref[j].args)
103
- this.error(null, csnPath, { id, elemref }, 'ON-Conditions must not contain parameters, step $(ID) of path $(ELEMREF)');
103
+ this.error(null, csnPath, { id, elemref }, 'ON-conditions must not contain parameters, step $(ID) of path $(ELEMREF)');
104
104
  }
105
105
  if (_art && $scope !== '$self') {
106
106
  _art = resolveArtifactType.call(this, _art);
@@ -117,15 +117,15 @@ function validateOnCondition(member, memberName, property, path) {
117
117
  /* 2) */ (_art.target && validDollarSelf)) &&
118
118
  !_art.virtual) {
119
119
  this.error(null, onPath, { elemref: { ref } },
120
- 'The last path of an on-condition must be a scalar value, path $(ELEMREF)');
120
+ 'The last path of an ON-condition must be a scalar value, path $(ELEMREF)');
121
121
  }
122
122
  else if (_art.items && !_art.virtual) {
123
123
  this.error(null, onPath, { elemref: { ref } },
124
- 'ON-Conditions can\'t use array-like elements, path $(ELEMREF)');
124
+ 'ON-conditions can\'t use array-like elements, path $(ELEMREF)');
125
125
  }
126
126
  else if (_art.virtual) {
127
127
  this.error(null, onPath, { elemref: { ref } },
128
- 'Virtual elements can\'t be used in ON-Conditions, path $(ELEMREF)');
128
+ 'Virtual elements can\'t be used in ON-conditions, path $(ELEMREF)');
129
129
  }
130
130
  }
131
131
  }
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
4
+
5
+ /**
6
+ * Check that we don't have parameterized views - as we don't know yet how to represent them on postgres
7
+ *
8
+ * @param {object} parent Object with .params
9
+ * @param {string} name Name of the params property on parent
10
+ * @param {object} params params
11
+ * @param {CSN.Path} path
12
+ */
13
+ function checkForParams(parent, name, params, path) {
14
+ const artifact = this.csn.definitions[path[1]];
15
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && !(parent.kind !== 'entity'))
16
+ this.error('ref-unexpected-params', [ ...path, 'params' ], `Parameterized views can't be used with sqlDialect “postgres”`);
17
+ }
18
+
19
+ module.exports = {
20
+ params: checkForParams,
21
+ };
@@ -106,7 +106,7 @@ function validateSelectItems(query) {
106
106
  if (this.options.transformation === 'hdbcds') {
107
107
  transformers.xpr = (parent) => {
108
108
  if (parent.func) {
109
- this.error(null, parent.$path,
109
+ this.error(null, parent.$path, {},
110
110
  'Window functions are not supported by SAP HANA CDS');
111
111
  }
112
112
  };
@@ -33,7 +33,7 @@ function checkDecimalScale(member, memberName, prop, path) {
33
33
  */
34
34
  function checkTypeIsScalar(member, memberName, prop, path) {
35
35
  if ( prop === 'params' && this.csnUtils.isStructured(member))
36
- this.error(null, path, 'View parameter type must be scalar');
36
+ this.error(null, path, {}, 'View parameter type must be scalar');
37
37
  }
38
38
 
39
39
  /**
@@ -69,7 +69,7 @@ function checkElementTypeDefinitionHasType(member, memberName, prop, path) {
69
69
  }
70
70
  else if (member._type) {
71
71
  if ( this.isAspect(member._type) || member._type.kind === 'type' && member._type.$syntax === 'aspect')
72
- this.error('ref-sloppy-type', path, 'A type or an element is expected here');
72
+ this.error('ref-sloppy-type', path, {}, 'A type or an element is expected here');
73
73
  }
74
74
  return;
75
75
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { isBuiltinType } = require('../model/csnUtils');
4
-
4
+ const { RelationalOperators } = require('../transform/transformUtilsNew');
5
5
  /**
6
6
  * Prepare the ref steps so that they are loggable
7
7
  *
@@ -17,17 +17,27 @@ function logReady(refStep) {
17
17
  * structured that can be used for tuple expansion. This can either be a
18
18
  * real 'elements' thing or a managed association/composition with foreign keys.
19
19
  *
20
+ * The RHS may be 'null' or any value
21
+ *
20
22
  * @param {Array} on the on condition which to check
21
23
  * @param {number} startIndex the index of the relational term in the on condition array
22
24
  * @returns {boolean} indicates whether the other side of a relational term is expandable
23
25
  */
24
26
  function otherSideIsExpandableStructure(on, startIndex) {
25
- if (on[startIndex - 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex - 1]))
26
- return isOk(resolveArtifactType.call(this, on[startIndex - 2]._art));
27
-
28
- else if (on[startIndex + 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex + 1]))
29
- return isOk(resolveArtifactType.call(this, on[startIndex + 2]._art));
30
-
27
+ if (on[startIndex - 1] && RelationalOperators.includes(on[startIndex - 1])) {
28
+ const lhs = on[startIndex - 2];
29
+ // if ever lhs is allowed to be a value uncomment this
30
+ return /* lhs?.val !== undefined || */ isOk(resolveArtifactType.call(this, lhs?._art));
31
+ }
32
+ else if (on[startIndex + 1] && RelationalOperators.includes(on[startIndex + 1])) {
33
+ const op = on[startIndex + 1];
34
+ const rhs = on[startIndex + 2];
35
+ if (op === 'is')
36
+ // check for unary operator 'is [not] null' as token stream
37
+ return rhs === 'null' || (rhs === 'not' && on[startIndex + 3] === 'null');
38
+ // if ever rhs is allowed to be a value uncomment this
39
+ return /* rhs?.val !== undefined || */ isOk(resolveArtifactType.call(this, rhs?._art));
40
+ }
31
41
  return false;
32
42
 
33
43
  /**
@@ -6,12 +6,14 @@ const {
6
6
  } = require('../model/csnUtils');
7
7
  const enrich = require('./enricher');
8
8
 
9
- // forHana
9
+ // forRelationalDB
10
10
  const { validateSelectItems } = require('./selectItems');
11
11
  const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
12
12
  const validateCdsPersistenceAnnotation = require('./cdsPersistence');
13
13
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
14
14
  const checkForEmptyOrOnlyVirtual = require('./emptyOrOnlyVirtual');
15
+ const checkForHanaTypes = require('./checkForTypes');
16
+ const checkForParams = require('./parameters');
15
17
  // forOdata
16
18
  const { validateDefaultValues } = require('./defaultValues');
17
19
  const { checkActionOrFunction } = require('./actionsFunctions');
@@ -26,7 +28,9 @@ const {
26
28
  checkTypeDefinitionHasType, checkElementTypeDefinitionHasType,
27
29
  checkTypeIsScalar, checkDecimalScale,
28
30
  } = require('./types');
29
- const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc } = require('./elements');
31
+ const {
32
+ checkPrimaryKey, checkVirtualElement, checkManagedAssoc, checkRecursiveTypeUsage,
33
+ } = require('./elements');
30
34
  const checkForInvalidTarget = require('./invalidTarget');
31
35
  const { validateAssociationsInItems } = require('./arrayOfs');
32
36
  const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
@@ -38,7 +42,7 @@ const {
38
42
  checkSqlAnnotationOnElement,
39
43
  } = require('./sql-snippets');
40
44
 
41
- const forHanaMemberValidators
45
+ const forRelationalDBMemberValidators
42
46
  = [
43
47
  // For HANA CDS specifically, reject any default parameter values, as these are not supported.
44
48
  rejectParamDefaultsInHanaCds,
@@ -51,7 +55,7 @@ const forHanaMemberValidators
51
55
  checkSqlAnnotationOnElement,
52
56
  ];
53
57
 
54
- const forHanaArtifactValidators
58
+ const forRelationalDBArtifactValidators
55
59
  = [
56
60
  // @cds.persistence has no impact on odata
57
61
  validateCdsPersistenceAnnotation,
@@ -61,12 +65,12 @@ const forHanaArtifactValidators
61
65
  checkSqlAnnotationOnArtifact,
62
66
  ];
63
67
 
64
- const forHanaCsnValidators = [ nonexpandableStructuredInExpression ];
68
+ const forRelationalDBCsnValidators = [ nonexpandableStructuredInExpression ];
65
69
  /**
66
70
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
67
71
  */
68
- const forHanaQueryValidators = [
69
- // TODO reason why this is forHana exclusive
72
+ const forRelationalDBQueryValidators = [
73
+ // TODO reason why this is forRelationalDB exclusive
70
74
  validateSelectItems,
71
75
  checkQueryForNoDBArtifacts,
72
76
  ];
@@ -103,7 +107,12 @@ const commonMemberValidators
103
107
  checkVirtualElement, checkElementTypeDefinitionHasType ];
104
108
 
105
109
  // TODO: checkManagedAssoc is a forEachMemberRecursively!
106
- const commonArtifactValidators = [ checkTypeDefinitionHasType, checkPrimaryKey, checkManagedAssoc ];
110
+ const commonArtifactValidators = [
111
+ checkTypeDefinitionHasType,
112
+ checkPrimaryKey,
113
+ checkManagedAssoc,
114
+ checkRecursiveTypeUsage,
115
+ ];
107
116
  // TODO: Does it make sense to run the on-condition check as part of a CSN validator?
108
117
  const commonQueryValidators = [ validateMixinOnCondition ];
109
118
 
@@ -186,11 +195,14 @@ function mergeCsnValidators(csnValidators, that) {
186
195
  * @param {object} that Will be provided to the validators via "this"
187
196
  * @returns {Function} the validator function with the respective checks for the HANA backend
188
197
  */
189
- function forHana(csn, that) {
198
+ function forRelationalDB(csn, that) {
190
199
  return _validate(csn, that,
191
- forHanaCsnValidators,
192
- forHanaMemberValidators.concat(commonMemberValidators),
193
- forHanaArtifactValidators.concat(commonArtifactValidators).concat(
200
+ that.options.sqlDialect === 'postgres'
201
+ ? [ ...forRelationalDBCsnValidators,
202
+ checkForHanaTypes,
203
+ checkForParams ] : forRelationalDBCsnValidators,
204
+ forRelationalDBMemberValidators.concat(commonMemberValidators),
205
+ forRelationalDBArtifactValidators.concat(commonArtifactValidators).concat(
194
206
  // why is this hana exclusive
195
207
  (artifact) => {
196
208
  /* the validation itself performs a recursive check on structured elements.
@@ -200,7 +212,7 @@ function forHana(csn, that) {
200
212
  forEachMember(artifact, checkUsedTypesForAnonymousAspectComposition.bind(that));
201
213
  }
202
214
  ),
203
- forHanaQueryValidators.concat(commonQueryValidators),
215
+ forRelationalDBQueryValidators.concat(commonQueryValidators),
204
216
  {
205
217
  skipArtifact: artifact => artifact.abstract ||
206
218
  hasAnnotationValue(artifact, '@cds.persistence.skip') ||
@@ -235,4 +247,4 @@ function forOdata(csn, that) {
235
247
  });
236
248
  }
237
249
 
238
- module.exports = { forHana, forOdata };
250
+ module.exports = { forRelationalDB, forOdata };