@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/bin/cdsc.js +4 -2
  3. package/doc/CHANGELOG_BETA.md +35 -0
  4. package/lib/api/main.js +153 -29
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/error.js +2 -2
  8. package/lib/base/keywords.js +106 -24
  9. package/lib/base/message-registry.js +177 -79
  10. package/lib/base/messages.js +78 -57
  11. package/lib/base/model.js +2 -1
  12. package/lib/checks/actionsFunctions.js +1 -1
  13. package/lib/checks/annotationsOData.js +2 -2
  14. package/lib/checks/arrayOfs.js +15 -7
  15. package/lib/checks/cdsPersistence.js +1 -1
  16. package/lib/checks/checkForTypes.js +53 -0
  17. package/lib/checks/defaultValues.js +4 -2
  18. package/lib/checks/elements.js +81 -6
  19. package/lib/checks/foreignKeys.js +12 -13
  20. package/lib/checks/invalidTarget.js +10 -11
  21. package/lib/checks/managedInType.js +21 -15
  22. package/lib/checks/nullableKeys.js +1 -1
  23. package/lib/checks/onConditions.js +9 -9
  24. package/lib/checks/parameters.js +23 -0
  25. package/lib/checks/queryNoDbArtifacts.js +1 -1
  26. package/lib/checks/selectItems.js +1 -1
  27. package/lib/checks/sql-snippets.js +12 -10
  28. package/lib/checks/types.js +2 -2
  29. package/lib/checks/utils.js +17 -7
  30. package/lib/checks/validator.js +36 -14
  31. package/lib/compiler/assert-consistency.js +21 -13
  32. package/lib/compiler/builtins.js +8 -0
  33. package/lib/compiler/checks.js +57 -40
  34. package/lib/compiler/define.js +139 -69
  35. package/lib/compiler/extend.js +319 -50
  36. package/lib/compiler/finalize-parse-cdl.js +14 -9
  37. package/lib/compiler/kick-start.js +2 -35
  38. package/lib/compiler/populate.js +111 -68
  39. package/lib/compiler/propagator.js +5 -3
  40. package/lib/compiler/resolve.js +71 -108
  41. package/lib/compiler/shared.js +82 -54
  42. package/lib/compiler/tweak-assocs.js +26 -14
  43. package/lib/compiler/utils.js +13 -2
  44. package/lib/edm/annotations/genericTranslation.js +10 -7
  45. package/lib/edm/csn2edm.js +11 -11
  46. package/lib/edm/edm.js +17 -9
  47. package/lib/edm/edmPreprocessor.js +53 -30
  48. package/lib/edm/edmUtils.js +7 -2
  49. package/lib/gen/Dictionary.json +14 -0
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -2
  52. package/lib/gen/languageParser.js +4312 -4186
  53. package/lib/inspect/inspectModelStatistics.js +1 -1
  54. package/lib/inspect/inspectPropagation.js +23 -9
  55. package/lib/json/csnVersion.js +13 -13
  56. package/lib/json/from-csn.js +161 -172
  57. package/lib/json/to-csn.js +70 -10
  58. package/lib/language/.eslintrc.json +4 -0
  59. package/lib/language/antlrParser.js +8 -11
  60. package/lib/language/docCommentParser.js +1 -2
  61. package/lib/language/errorStrategy.js +54 -27
  62. package/lib/language/genericAntlrParser.js +140 -93
  63. package/lib/language/language.g4 +57 -33
  64. package/lib/language/multiLineStringParser.js +75 -63
  65. package/lib/main.d.ts +3 -6
  66. package/lib/main.js +1 -0
  67. package/lib/model/.eslintrc.json +13 -0
  68. package/lib/model/api.js +4 -2
  69. package/lib/model/csnRefs.js +78 -50
  70. package/lib/model/csnUtils.js +272 -222
  71. package/lib/model/enrichCsn.js +41 -31
  72. package/lib/model/revealInternalProperties.js +61 -57
  73. package/lib/model/sortViews.js +35 -31
  74. package/lib/modelCompare/compare.js +52 -18
  75. package/lib/modelCompare/filter.js +83 -0
  76. package/lib/optionProcessor.js +10 -1
  77. package/lib/render/manageConstraints.js +11 -7
  78. package/lib/render/toCdl.js +151 -106
  79. package/lib/render/toHdbcds.js +8 -6
  80. package/lib/render/toRename.js +4 -4
  81. package/lib/render/toSql.js +17 -7
  82. package/lib/render/utils/common.js +27 -9
  83. package/lib/render/utils/sql.js +5 -5
  84. package/lib/sql-identifier.js +7 -0
  85. package/lib/transform/db/applyTransformations.js +32 -3
  86. package/lib/transform/db/assertUnique.js +27 -38
  87. package/lib/transform/db/expansion.js +92 -41
  88. package/lib/transform/db/flattening.js +1 -1
  89. package/lib/transform/db/temporal.js +3 -1
  90. package/lib/transform/db/transformExists.js +8 -2
  91. package/lib/transform/db/views.js +42 -13
  92. package/lib/transform/draft/db.js +2 -2
  93. package/lib/transform/forOdataNew.js +10 -7
  94. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
  95. package/lib/transform/localized.js +29 -20
  96. package/lib/transform/odata/toFinalBaseType.js +8 -11
  97. package/lib/transform/odata/typesExposure.js +2 -1
  98. package/lib/transform/parseExpr.js +245 -0
  99. package/lib/transform/transformUtilsNew.js +122 -51
  100. package/lib/transform/translateAssocsToJoins.js +17 -16
  101. package/lib/utils/moduleResolve.js +5 -5
  102. package/lib/utils/objectUtils.js +3 -3
  103. package/lib/utils/term.js +5 -5
  104. package/package.json +2 -2
  105. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  106. package/share/messages/check-proper-type-of.md +4 -4
  107. package/share/messages/check-proper-type.md +2 -2
  108. package/share/messages/duplicate-autoexposed.md +4 -4
  109. package/share/messages/extend-repeated-intralayer.md +4 -5
  110. package/share/messages/extend-unrelated-layer.md +4 -4
  111. package/share/messages/message-explanations.json +3 -1
  112. package/share/messages/redirected-to-ambiguous.md +7 -6
  113. package/share/messages/redirected-to-complex.md +63 -0
  114. package/share/messages/redirected-to-unrelated.md +6 -5
  115. package/share/messages/rewrite-not-supported.md +4 -4
  116. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
  117. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -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,23 @@
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' ], { value: this.options.sqlDialect },
17
+ 'Parameterized views can\'t be used with sqlDialect $(VALUE)');
18
+ }
19
+ }
20
+
21
+ module.exports = {
22
+ params: checkForParams,
23
+ };
@@ -101,7 +101,7 @@ function checkQueryForNoDBArtifacts(query) {
101
101
  this.error(null,
102
102
  obj.$path,
103
103
  { id: pathStep, elemref: obj },
104
- `Path step $(ID) of $(ELEMREF) has no foreign keys`);
104
+ 'Path step $(ID) of $(ELEMREF) has no foreign keys');
105
105
  }
106
106
 
107
107
  if (art.on) {
@@ -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
  };
@@ -13,15 +13,15 @@
13
13
  */
14
14
  function checkSqlAnnotationOnElement(member, memberName, prop, path) {
15
15
  if (member['@sql.replace'])
16
- this.error(null, path, { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
16
+ this.error(null, path, { anno: 'sql.replace' }, 'Annotation $(ANNO) is reserved and must not be used');
17
17
  if (member['@sql.prepend'])
18
- this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, `Annotation $(ANNO) can't be used on elements` );
18
+ this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, 'Annotation $(ANNO) can\'t be used on elements' );
19
19
 
20
20
  if (member['@sql.append']) {
21
21
  if (this.artifact.query)
22
- this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on elements in views` );
22
+ this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on elements in views' );
23
23
  else if (this.csnUtils.isStructured(member))
24
- this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on structured elements` );
24
+ this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on structured elements' );
25
25
  else
26
26
  checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
27
27
  }
@@ -37,7 +37,7 @@ function checkSqlAnnotationOnElement(member, memberName, prop, path) {
37
37
  function checkValidAnnoValue(carrier, annotation, path, error, options) {
38
38
  if (carrier[annotation] !== undefined && carrier[annotation] !== null) {
39
39
  if (typeof carrier[annotation] !== 'string')
40
- error(null, path, { anno: annotation.slice(1), type: typeof carrier[annotation] }, `Annotation $(ANNO) must be a string, found $(TYPE)` );
40
+ error(null, path, { anno: annotation.slice(1), type: typeof carrier[annotation] }, 'Annotation $(ANNO) must be a string, found $(TYPE)' );
41
41
  else if (options.transformation === 'sql') // HDI and HDBCDS do their own checks
42
42
  guardAgainstInjection(annotation, carrier[annotation], path, error);
43
43
  }
@@ -52,20 +52,22 @@ function checkValidAnnoValue(carrier, annotation, path, error, options) {
52
52
  function checkSqlAnnotationOnArtifact(artifact, artifactName) {
53
53
  if (artifact.kind !== 'entity') {
54
54
  if (artifact['@sql.prepend'])
55
- this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
55
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind }, 'Annotation $(NAME) can\'t be used on an artifact of kind $(KIND)' );
56
56
  if (artifact['@sql.append'])
57
- this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
57
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind }, 'Annotation $(NAME) can\'t be used on an artifact of kind $(KIND)' );
58
58
  }
59
59
  else if (artifact['@sql.prepend']) {
60
60
  if (artifact.query)
61
- this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, `Annotation $(NAME) can't be used on views` );
61
+ this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, 'Annotation $(NAME) can\'t be used on views' );
62
62
  else
63
63
  checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
64
64
  }
65
65
 
66
66
 
67
- if (artifact['@sql.replace'])
68
- this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
67
+ if (artifact['@sql.replace']) {
68
+ this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' },
69
+ 'Annotation $(ANNO) is reserved and must not be used');
70
+ }
69
71
 
70
72
  checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
71
73
  }
@@ -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
 
@@ -181,16 +190,29 @@ function mergeCsnValidators(csnValidators, that) {
181
190
  return remapped;
182
191
  }
183
192
 
193
+ /**
194
+ * Depending on the dialect we need to run different validations.
195
+ *
196
+ * @param {CSN.Options} options
197
+ * @returns {any[]} Array of validator functions (or objects?)
198
+ */
199
+ function getDBCsnValidators(options) {
200
+ if (options.sqlDialect === 'postgres' || options.sqlDialect === 'h2')
201
+ return [ ...forRelationalDBCsnValidators, checkForHanaTypes, checkForParams ];
202
+
203
+ return forRelationalDBCsnValidators;
204
+ }
205
+
184
206
  /**
185
207
  * @param {CSN.Model} csn CSN to check
186
208
  * @param {object} that Will be provided to the validators via "this"
187
209
  * @returns {Function} the validator function with the respective checks for the HANA backend
188
210
  */
189
- function forHana(csn, that) {
211
+ function forRelationalDB(csn, that) {
190
212
  return _validate(csn, that,
191
- forHanaCsnValidators,
192
- forHanaMemberValidators.concat(commonMemberValidators),
193
- forHanaArtifactValidators.concat(commonArtifactValidators).concat(
213
+ getDBCsnValidators(that.options),
214
+ forRelationalDBMemberValidators.concat(commonMemberValidators),
215
+ forRelationalDBArtifactValidators.concat(commonArtifactValidators).concat(
194
216
  // why is this hana exclusive
195
217
  (artifact) => {
196
218
  /* the validation itself performs a recursive check on structured elements.
@@ -200,7 +222,7 @@ function forHana(csn, that) {
200
222
  forEachMember(artifact, checkUsedTypesForAnonymousAspectComposition.bind(that));
201
223
  }
202
224
  ),
203
- forHanaQueryValidators.concat(commonQueryValidators),
225
+ forRelationalDBQueryValidators.concat(commonQueryValidators),
204
226
  {
205
227
  skipArtifact: artifact => artifact.abstract ||
206
228
  hasAnnotationValue(artifact, '@cds.persistence.skip') ||
@@ -235,4 +257,4 @@ function forOdata(csn, that) {
235
257
  });
236
258
  }
237
259
 
238
- module.exports = { forHana, forOdata };
260
+ module.exports = { forRelationalDB, forOdata };
@@ -229,11 +229,13 @@ function assertConsistency( model, stage ) {
229
229
  elements: { kind: true, inherits: 'definitions', also: [ 0 ] }, // 0 for cyclic expansions
230
230
  // specified elements in query entities (TODO: introduce real "specified elements" instead):
231
231
  elements$: { kind: true, enumerable: false, test: TODO },
232
+ enum$: { kind: true, enumerable: false, test: TODO },
232
233
  actions: { kind: true, inherits: 'definitions' },
233
234
  enum: { kind: true, inherits: 'definitions' },
234
235
  foreignKeys: { kind: true, inherits: 'definitions' },
235
236
  $keysNavigation: { kind: true, test: TODO },
236
237
  params: { kind: true, inherits: 'definitions' },
238
+ _extendType: { kind: true, test: TODO },
237
239
  mixin: { inherits: 'definitions' },
238
240
  query: {
239
241
  kind: true,
@@ -293,7 +295,7 @@ function assertConsistency( model, stage ) {
293
295
  none: { optional: () => true }, // parse error
294
296
  },
295
297
  columns: {
296
- kind: [ 'extend' ],
298
+ kind: [ 'extend', '$column' ],
297
299
  test: isArray( column ),
298
300
  optional: thoseWithKind,
299
301
  enum: [ '*' ],
@@ -307,6 +309,7 @@ function assertConsistency( model, stage ) {
307
309
  kind: 'element',
308
310
  test: isDictionary( definition ), // definition since redef
309
311
  requires: [ 'location', 'name' ],
312
+ optional: [ '$duplicates' ],
310
313
  },
311
314
  orderBy: { inherits: 'value', test: isArray( expression ) },
312
315
  sort: { test: locationVal( isString ), enum: [ 'asc', 'desc' ] },
@@ -330,7 +333,7 @@ function assertConsistency( model, stage ) {
330
333
  requires: [ 'location' ],
331
334
  optional: [
332
335
  'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
333
- 'scope', '_artifact', '$inferred', '$expand', '$tableAliases', '_$next',
336
+ 'scope', '_artifact', '$inferred', '$expand', '$inCycle', '$tableAliases', '_$next',
334
337
  '_effectiveType', // by propagation
335
338
  ],
336
339
  },
@@ -360,12 +363,13 @@ function assertConsistency( model, stage ) {
360
363
  suffix: { test: TODO },
361
364
  kind: {
362
365
  isRequired: !stageParser && (() => true),
366
+ kind: true,
363
367
  // required to be set by Core Compiler even with parse errors
364
368
  test: isString,
365
369
  enum: [
366
370
  'context', 'service', 'entity', 'type', 'aspect', 'const', 'annotation',
367
371
  'element', 'enum', 'action', 'function', 'param', 'key', 'event',
368
- 'annotate', 'extend',
372
+ 'annotate', 'extend', '$column',
369
373
  'select', '$join', 'mixin',
370
374
  'source', 'namespace', 'using',
371
375
  '$tableAlias', '$navElement',
@@ -395,7 +399,7 @@ function assertConsistency( model, stage ) {
395
399
  requires: [ 'location', 'path' ],
396
400
  optional: [ 'scope', 'variant', '_artifact', '$inferred', '$parens', 'sort', 'nulls' ],
397
401
  },
398
- none: { optional: [ 'location', '$parens' ] },
402
+ none: { optional: () => true }, // parse error
399
403
  // TODO: why optional / enough in name?
400
404
  // TODO: "yes" instead "none": val: true, optional literal/location
401
405
  val: {
@@ -504,8 +508,8 @@ function assertConsistency( model, stage ) {
504
508
  optional: [
505
509
  'enum',
506
510
  'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
507
- '_outer', '_effectiveType', 'notNull',
508
- '_origin', '_block', '$inferred', '$expand', '_deps',
511
+ '_outer', '_effectiveType', 'notNull', '_parent',
512
+ '_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
509
513
  '$syntax',
510
514
  '_status', '_redirected',
511
515
  ...typeProperties,
@@ -602,11 +606,13 @@ function assertConsistency( model, stage ) {
602
606
  kind: true,
603
607
  test: isOneOf([
604
608
  // Uppercase values are used in logic, lowercase value are "just for us", i.e.
605
- // debugging or to add non-enumerable properties such as $generated in Universal CSN.
609
+ // debugging or to add properties such as $generated in Universal CSN.
606
610
  // However, that is no longer true. For example, `autoexposed` is used in populate.js
607
611
  // as well.
608
612
  'IMPLICIT',
609
613
  'REDIRECTED',
614
+ 'NULL', // from propagator
615
+ 'prop', // from propagator
610
616
 
611
617
  '$autoElement', // for magicVars: $user is automatically changed to $user.id
612
618
  '$generated', // compiler generated annotations, e.g. @Core.Computed
@@ -618,8 +624,7 @@ function assertConsistency( model, stage ) {
618
624
  'composition-entity',
619
625
  'copy', // only used in rewriteCondition(): On-condition is copied
620
626
  'duplicate-autoexposed', // just like `autoexposed`, but with `duplicate` error.
621
- 'expand-element', // expanded elements
622
- 'expand-param', // expanded params (difference to expand-element only for debugging)
627
+ 'expanded', // expanded elements, items, params
623
628
  'include', // through includes, e.g. `entity E : F {}`
624
629
  'keys',
625
630
  'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
@@ -628,6 +633,8 @@ function assertConsistency( model, stage ) {
628
633
  'none', // only used in ensureColumnName(): Used in object representing empty alias
629
634
  'query', // inferred query properties, e.g. `key`
630
635
  'rewrite', // on-conditions or FKeys are rewritten
636
+ 'parent-origin', // annotation/property copied from parent that does not come through
637
+ // $origin and is not a direct annotation
631
638
  ]),
632
639
  },
633
640
 
@@ -639,6 +646,7 @@ function assertConsistency( model, stage ) {
639
646
  // See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
640
647
  test: isOneOf([ 'origin', 'annotate', 'target' ]),
641
648
  },
649
+ $inCycle: { kind: true, test: isBoolean },
642
650
 
643
651
  $autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
644
652
  $extra: { parser: true, test: TODO }, // for unexpected properties in CSN
@@ -814,7 +822,7 @@ function assertConsistency( model, stage ) {
814
822
  const sub = Object.assign( {}, s.inherits && schema[s.inherits], s );
815
823
  if (spec.requires && sub.requires)
816
824
  sub.requires = [ ...sub.requires, ...spec.requires ];
817
- if (spec.optional && sub.optional)
825
+ if (Array.isArray( spec.optional ) && Array.isArray( sub.optional ))
818
826
  sub.optional = [ ...sub.optional, ...spec.optional ];
819
827
  // console.log(expressionSpec(node) );
820
828
  (sub.test || standard)( node, parent, prop, sub, idx );
@@ -827,9 +835,9 @@ function assertConsistency( model, stage ) {
827
835
  return 'val';
828
836
  else if (node.query)
829
837
  return 'query';
830
- else if (!node.op)
831
- return 'none';
832
- return 'op';
838
+ else if (node.op)
839
+ return 'op';
840
+ return 'none'; // parse error
833
841
  }
834
842
 
835
843
  function args( node, parent, prop, spec ) {
@@ -19,6 +19,10 @@ const core = {
19
19
  DecimalFloat: { category: 'decimal', deprecated: true },
20
20
  Integer64: { category: 'integer' },
21
21
  Integer: { category: 'integer' },
22
+ UInt8: { category: 'integer' },
23
+ Int16: { category: 'integer' },
24
+ Int32: { category: 'integer' },
25
+ Int64: { category: 'integer' },
22
26
  Double: { category: 'decimal' },
23
27
  Date: { category: 'dateTime' },
24
28
  Time: { category: 'dateTime' },
@@ -54,6 +58,7 @@ const typeParameters = {
54
58
  srid: [ 'number' ],
55
59
  },
56
60
  };
61
+ // a.k.a "typeProperties"
57
62
  typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
58
63
 
59
64
  // const hana = {
@@ -137,6 +142,7 @@ const specialFunctions = compileFunctions( {
137
142
  separator: [ 'FLAG', 'IN', 'FROM', 'OCCURRENCE', 'GROUP' ],
138
143
  },
139
144
  ],
145
+ SUBSTR_REGEXPR: 'SUBSTRING_REGEXPR',
140
146
  } );
141
147
 
142
148
  function compileFunctions( special ) {
@@ -364,6 +370,8 @@ function isInReservedNamespace(absolute) {
364
370
  * check against their absolute names. Builtin types are "cds.<something>", i.e. they
365
371
  * are directly in 'cds', but not for example in 'cds.foundation'.
366
372
  *
373
+ * TODO: Handle `{ ref: [ "cds.Integer" ] }`
374
+ *
367
375
  * @param {string} type
368
376
  * @returns {boolean}
369
377
  */