@sap/cds-compiler 5.5.2 → 5.7.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 (57) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/bin/cdsse.js +4 -0
  3. package/bin/cdsv2m.js +2 -1
  4. package/doc/Versioning.md +4 -4
  5. package/lib/api/options.js +1 -0
  6. package/lib/base/builtins.js +2 -2
  7. package/lib/base/dictionaries.js +1 -2
  8. package/lib/base/keywords.js +3 -1
  9. package/lib/base/lazyload.js +1 -1
  10. package/lib/base/message-registry.js +170 -143
  11. package/lib/base/messages.js +69 -59
  12. package/lib/base/model.js +3 -3
  13. package/lib/base/node-helpers.js +17 -16
  14. package/lib/base/optionProcessorHelper.js +13 -14
  15. package/lib/base/shuffle.js +4 -1
  16. package/lib/checks/structuredAnnoExpressions.js +1 -1
  17. package/lib/compiler/assert-consistency.js +1 -1
  18. package/lib/compiler/builtins.js +4 -1
  19. package/lib/compiler/extend.js +20 -5
  20. package/lib/compiler/resolve.js +45 -9
  21. package/lib/compiler/shared.js +1 -0
  22. package/lib/edm/annotations/edmJson.js +3 -3
  23. package/lib/edm/annotations/genericTranslation.js +5 -1
  24. package/lib/edm/annotations/vocabularyDefinitions.js +8 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +142 -103
  27. package/lib/gen/CdlParser.js +2240 -2201
  28. package/lib/gen/Dictionary.json +185 -6
  29. package/lib/json/from-csn.js +2 -0
  30. package/lib/json/to-csn.js +13 -4
  31. package/lib/language/docCommentParser.js +11 -5
  32. package/lib/language/errorStrategy.js +3 -3
  33. package/lib/language/genericAntlrParser.js +2 -0
  34. package/lib/model/csnUtils.js +6 -1
  35. package/lib/optionProcessor.js +5 -1
  36. package/lib/parsers/AstBuildingParser.js +200 -86
  37. package/lib/parsers/CdlGrammar.g4 +142 -86
  38. package/lib/parsers/Lexer.js +5 -3
  39. package/lib/parsers/index.js +1 -1
  40. package/lib/render/toCdl.js +6 -5
  41. package/lib/render/toHdbcds.js +1 -1
  42. package/lib/render/toSql.js +5 -3
  43. package/lib/render/utils/common.js +19 -6
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/expansion.js +3 -0
  47. package/lib/transform/db/flattening.js +18 -77
  48. package/lib/transform/db/groupByOrderBy.js +2 -2
  49. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  50. package/lib/transform/db/temporal.js +2 -1
  51. package/lib/transform/forOdata.js +1 -1
  52. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  53. package/lib/transform/odata/createForeignKeys.js +25 -9
  54. package/lib/transform/odata/flattening.js +11 -1
  55. package/lib/transform/transformUtils.js +20 -85
  56. package/package.json +2 -1
  57. package/bin/cds_update_annotations.js +0 -180
@@ -1114,6 +1114,13 @@
1114
1114
  ],
1115
1115
  "$experimental": true
1116
1116
  },
1117
+ "Common.WebSocketChannel": {
1118
+ "Type": "Edm.String",
1119
+ "AppliesTo": [
1120
+ "EntityContainer"
1121
+ ],
1122
+ "$experimental": true
1123
+ },
1117
1124
  "Communication.Contact": {
1118
1125
  "Type": "Communication.ContactType",
1119
1126
  "AppliesTo": [
@@ -1463,6 +1470,61 @@
1463
1470
  "EntitySet"
1464
1471
  ]
1465
1472
  },
1473
+ "EntityRelationship.entityType": {
1474
+ "Type": "Edm.String",
1475
+ "AppliesTo": [
1476
+ "EntityType"
1477
+ ],
1478
+ "$experimental": true
1479
+ },
1480
+ "EntityRelationship.propertyType": {
1481
+ "Type": "Edm.String",
1482
+ "AppliesTo": [
1483
+ "Property"
1484
+ ],
1485
+ "$experimental": true
1486
+ },
1487
+ "EntityRelationship.entityIds": {
1488
+ "Type": "Collection(EntityRelationship.entityId)",
1489
+ "AppliesTo": [
1490
+ "EntityType"
1491
+ ],
1492
+ "$experimental": true
1493
+ },
1494
+ "EntityRelationship.reference": {
1495
+ "Type": "EntityRelationship.singleReference",
1496
+ "AppliesTo": [
1497
+ "Property"
1498
+ ]
1499
+ },
1500
+ "EntityRelationship.compositeReferences": {
1501
+ "Type": "Collection(EntityRelationship.compositeReference)",
1502
+ "AppliesTo": [
1503
+ "EntityType"
1504
+ ],
1505
+ "$experimental": true
1506
+ },
1507
+ "EntityRelationship.temporalIds": {
1508
+ "Type": "Collection(EntityRelationship.temporalId)",
1509
+ "AppliesTo": [
1510
+ "EntityType"
1511
+ ],
1512
+ "$experimental": true
1513
+ },
1514
+ "EntityRelationship.temporalReferences": {
1515
+ "Type": "Collection(EntityRelationship.temporalReference)",
1516
+ "AppliesTo": [
1517
+ "EntityType"
1518
+ ],
1519
+ "$experimental": true
1520
+ },
1521
+ "EntityRelationship.referencesWithConstantIds": {
1522
+ "Type": "Collection(EntityRelationship.referencesWithConstantId)",
1523
+ "AppliesTo": [
1524
+ "EntityType"
1525
+ ],
1526
+ "$experimental": true
1527
+ },
1466
1528
  "Graph.traceId": {
1467
1529
  "Type": "Edm.String",
1468
1530
  "$experimental": true
@@ -1719,6 +1781,7 @@
1719
1781
  "EntityType",
1720
1782
  "Action",
1721
1783
  "Function",
1784
+ "ActionImport",
1722
1785
  "FunctionImport"
1723
1786
  ]
1724
1787
  },
@@ -1783,7 +1846,8 @@
1783
1846
  "Type": "Collection(UI.CriticalityLabelType)",
1784
1847
  "AppliesTo": [
1785
1848
  "Property",
1786
- "EntityType"
1849
+ "EntityType",
1850
+ "TypeDefinition"
1787
1851
  ],
1788
1852
  "$experimental": true
1789
1853
  },
@@ -1823,6 +1887,16 @@
1823
1887
  "EntityType"
1824
1888
  ]
1825
1889
  },
1890
+ "UI.OperationParameterFacets": {
1891
+ "Type": "Collection(UI.ReferenceFacet)",
1892
+ "AppliesTo": [
1893
+ "Action",
1894
+ "Function",
1895
+ "ActionImport",
1896
+ "FunctionImport"
1897
+ ],
1898
+ "$experimental": true
1899
+ },
1826
1900
  "UI.SelectionPresentationVariant": {
1827
1901
  "Type": "UI.SelectionPresentationVariantType",
1828
1902
  "AppliesTo": [
@@ -1878,14 +1952,16 @@
1878
1952
  "Type": "Core.Tag",
1879
1953
  "AppliesTo": [
1880
1954
  "Property",
1881
- "Term"
1955
+ "Term",
1956
+ "TypeDefinition"
1882
1957
  ]
1883
1958
  },
1884
1959
  "UI.IsImage": {
1885
1960
  "Type": "Core.Tag",
1886
1961
  "AppliesTo": [
1887
1962
  "Property",
1888
- "EntityType"
1963
+ "EntityType",
1964
+ "TypeDefinition"
1889
1965
  ],
1890
1966
  "$experimental": true
1891
1967
  },
@@ -1894,7 +1970,8 @@
1894
1970
  "AppliesTo": [
1895
1971
  "Property",
1896
1972
  "PropertyValue",
1897
- "Parameter"
1973
+ "Parameter",
1974
+ "TypeDefinition"
1898
1975
  ]
1899
1976
  },
1900
1977
  "UI.Placeholder": {
@@ -2070,7 +2147,8 @@
2070
2147
  "Type": "UI.RecommendationListType",
2071
2148
  "AppliesTo": [
2072
2149
  "Property",
2073
- "Parameter"
2150
+ "Parameter",
2151
+ "TypeDefinition"
2074
2152
  ]
2075
2153
  },
2076
2154
  "UI.Recommendations": {
@@ -2089,7 +2167,8 @@
2089
2167
  "UI.DoNotCheckScaleOfMeasuredQuantity": {
2090
2168
  "Type": "Edm.Boolean",
2091
2169
  "AppliesTo": [
2092
- "Property"
2170
+ "Property",
2171
+ "TypeDefinition"
2093
2172
  ],
2094
2173
  "$experimental": true
2095
2174
  },
@@ -3301,6 +3380,7 @@
3301
3380
  "Properties": {
3302
3381
  "SourceProperties": "Collection(Edm.PropertyPath)",
3303
3382
  "SourceEntities": "Collection(Edm.NavigationPropertyPath)",
3383
+ "SourceEvents": "Collection(Edm.String)",
3304
3384
  "TargetProperties": "Collection(Edm.String)",
3305
3385
  "TargetEntities": "Collection(Edm.NavigationPropertyPath)",
3306
3386
  "EffectTypes": "Common.EffectType",
@@ -3777,6 +3857,105 @@
3777
3857
  "DELETE"
3778
3858
  ]
3779
3859
  },
3860
+ "EntityRelationship.singleReference": {
3861
+ "$kind": "ComplexType",
3862
+ "Properties": {
3863
+ "name": "Edm.String",
3864
+ "referencedEntityType": "EntityRelationship.entityTypeID",
3865
+ "referencedPropertyType": "EntityRelationship.propertyTypeID"
3866
+ }
3867
+ },
3868
+ "EntityRelationship.entityId": {
3869
+ "$kind": "ComplexType",
3870
+ "Properties": {
3871
+ "name": "Edm.String",
3872
+ "description": "Edm.String",
3873
+ "propertyTypes": "Collection(EntityRelationship.propertyTypeID)"
3874
+ }
3875
+ },
3876
+ "EntityRelationship.compositeReference": {
3877
+ "$kind": "ComplexType",
3878
+ "Properties": {
3879
+ "name": "Edm.String",
3880
+ "referencedEntityType": "EntityRelationship.entityTypeID",
3881
+ "referencedPropertyType": "Collection(EntityRelationship.referencedPropertyType)"
3882
+ }
3883
+ },
3884
+ "EntityRelationship.referencedPropertyType": {
3885
+ "$kind": "ComplexType",
3886
+ "Properties": {
3887
+ "referencedPropertyType": "EntityRelationship.propertyTypeID",
3888
+ "localPropertyName": "Edm.PropertyPath"
3889
+ }
3890
+ },
3891
+ "EntityRelationship.temporalId": {
3892
+ "$kind": "ComplexType",
3893
+ "Properties": {
3894
+ "name": "Edm.String",
3895
+ "description": "Edm.String",
3896
+ "propertyTypes": "Collection(EntityRelationship.propertyTypeID)",
3897
+ "temporalIntervalType": "EntityRelationship.temporalIntervalTypeEnum",
3898
+ "temporalType": "EntityRelationship.temporalTypeEnum",
3899
+ "temporalIntervalStartProperty": "Edm.PropertyPath",
3900
+ "temporalIntervalEndProperty": "Edm.PropertyPath"
3901
+ }
3902
+ },
3903
+ "EntityRelationship.temporalReference": {
3904
+ "$kind": "ComplexType",
3905
+ "Properties": {
3906
+ "name": "Edm.String",
3907
+ "referencedEntityType": "EntityRelationship.entityTypeID",
3908
+ "referencedPropertyType": "Collection(EntityRelationship.referencedPropertyType)",
3909
+ "category": "EntityRelationship.temporalCategoryEnum",
3910
+ "selectionDateProperty": "Edm.PropertyPath"
3911
+ }
3912
+ },
3913
+ "EntityRelationship.referenceWithConstantId": {
3914
+ "$kind": "ComplexType",
3915
+ "Properties": {
3916
+ "name": "Edm.String",
3917
+ "referencedEntityType": "EntityRelationship.entityTypeID",
3918
+ "referencedPropertyType": "Collection(EntityRelationship.referencedPropertyTypeWithConstantId)"
3919
+ }
3920
+ },
3921
+ "EntityRelationship.referencedPropertyTypeWithConstantId": {
3922
+ "$kind": "ComplexType",
3923
+ "Properties": {
3924
+ "referencedPropertyType": "EntityRelationship.propertyTypeID",
3925
+ "localPropertyName": "Edm.PropertyPath",
3926
+ "constantValue": "Edm.String"
3927
+ }
3928
+ },
3929
+ "EntityRelationship.temporalIntervalTypeEnum": {
3930
+ "$kind": "EnumType",
3931
+ "Members": [
3932
+ "CLOSED_CLOSED",
3933
+ "OPEN_OPEN",
3934
+ "OPEN_CLOSED",
3935
+ "CLOSED_OPEN"
3936
+ ]
3937
+ },
3938
+ "EntityRelationship.temporalTypeEnum": {
3939
+ "$kind": "EnumType",
3940
+ "Members": [
3941
+ "DATE",
3942
+ "DATETIME"
3943
+ ]
3944
+ },
3945
+ "EntityRelationship.temporalCategoryEnum": {
3946
+ "$kind": "EnumType",
3947
+ "Members": [
3948
+ "TEMPORAL_DATE"
3949
+ ]
3950
+ },
3951
+ "EntityRelationship.propertyTypeID": {
3952
+ "$kind": "TypeDefinition",
3953
+ "UnderlyingType": "Edm.String"
3954
+ },
3955
+ "EntityRelationship.entityTypeID": {
3956
+ "$kind": "TypeDefinition",
3957
+ "UnderlyingType": "Edm.String"
3958
+ },
3780
3959
  "Graph.DetailsType": {
3781
3960
  "$kind": "ComplexType",
3782
3961
  "Properties": {
@@ -486,6 +486,7 @@ const schema = compileSchema( {
486
486
  val: {
487
487
  type: value,
488
488
  inKind: [ '$column', 'enum' ],
489
+ xorException: '#', // see xorGroup :expr
489
490
  // see also extra handling for 'element' in extension, see definition()
490
491
  },
491
492
  literal: {
@@ -499,6 +500,7 @@ const schema = compileSchema( {
499
500
  // Note: We emit a warning if '#' is used in enums. Because the compiler
500
501
  // can generate CSN like this, we need to be able to parse it.
501
502
  inKind: [ '$column', 'enum' ],
503
+ xorException: 'val', // see xorGroup :expr
502
504
  // see also extra handling for 'element' in extension, see definition()
503
505
  },
504
506
  path: { // in CSN v0.1.0 'foreignKeys'
@@ -1132,7 +1132,7 @@ function value( node ) {
1132
1132
  return extra( { '=': node.variant ? `${ ref }#${ pathName(node.variant.path) }` : ref }, node );
1133
1133
  }
1134
1134
  if (node.literal === 'enum')
1135
- return extra( { '#': node.sym.id }, node );
1135
+ return enumValue( node );
1136
1136
  if (node.literal === 'array')
1137
1137
  return node.val.map( value );
1138
1138
  if (node.literal === 'token' && node.val === '...')
@@ -1146,6 +1146,15 @@ function value( node ) {
1146
1146
  return r;
1147
1147
  }
1148
1148
 
1149
+ function enumValue( node ) {
1150
+ const r = extra( { '#': node.sym.id }, node );
1151
+ const sym = node.sym._artifact;
1152
+ // add calculated `val`, but not for chained symbols:
1153
+ if (sym && (!gensrcFlavor || gensrcFlavor === 'column') && !sym.value?.sym)
1154
+ r.val = sym.value ? sym.value.val : sym.name.id;
1155
+ return r;
1156
+ }
1157
+
1149
1158
  function targetElement( val, csn, node ) {
1150
1159
  const key = addExplicitAs( { ref: val.path.map( pathItem ) },
1151
1160
  node.name, neqPath( val ) );
@@ -1210,10 +1219,10 @@ function exprInternal( node, xprParens ) {
1210
1219
  return extra( { ref }, node );
1211
1220
  }
1212
1221
  if (node.literal) {
1213
- if (typeof node.val === node.literal || node.val === null)
1222
+ if (node.literal === 'enum')
1223
+ return enumValue( node );
1224
+ else if (typeof node.val === node.literal || node.val === null)
1214
1225
  return extra( { val: node.val }, node );
1215
- else if (node.literal === 'enum')
1216
- return extra( { '#': node.sym.id }, node );
1217
1226
  else if (node.literal === 'token')
1218
1227
  return node.val; // * in COUNT(*)
1219
1228
  return extra( { val: node.val, literal: node.literal }, node );
@@ -7,7 +7,6 @@ const {
7
7
  } = require('./textUtils');
8
8
 
9
9
  const fencedCommentRegEx = /^\s*[*]/;
10
- const footerFenceRegEx = /\s*[*]+\/$/;
11
10
  const hasContentOnFirstLineRegEx = /\/\*+\s*\S/;
12
11
 
13
12
  /**
@@ -37,9 +36,8 @@ function parseDocComment( comment ) {
37
36
  if (lines.length === 1) {
38
37
  // Special case for one-liners.
39
38
  // Remove "/***/" and trim white space and asterisks.
40
- const content = lines[0]
41
- .replace(/^\/[*]{2,}/, '')
42
- .replace(/\**\/$/, '') // for `/*****/`, only `/` remains
39
+ let content = lines[0].replace(/^\/[*]{2,}/, '');
40
+ content = removeFooterFence(content) // for `/*****/`, only `/` remains
43
41
  .replace('*\\/', '*/') // escape sequence
44
42
  .trim();
45
43
  return isWhitespaceOrNewLineOnly(content) ? null : content;
@@ -153,7 +151,15 @@ function removeHeaderFence( line ) {
153
151
  * @returns {string} header without fence
154
152
  */
155
153
  function removeFooterFence( line ) {
156
- return line.replace(footerFenceRegEx, '');
154
+ let trimAt = line.length - 1;
155
+ // '-1': remove trailing `/`
156
+ for (let i = trimAt - 1; i >= 0 && line[i] === '*'; --i)
157
+ trimAt = i;
158
+ // We know that trimAt is at a '*', regardless of whether the previous loop ran.
159
+ for (let i = trimAt - 1; i >= 0 && /^\s$/.test(line[i]); --i)
160
+ trimAt = i;
161
+ // Either trimAt is a ' ' or '*', regardless of whether any loop ran.
162
+ return line.slice(0, trimAt);
157
163
  }
158
164
 
159
165
  /**
@@ -235,9 +235,9 @@ function singleTokenDeletion( recognizer ) {
235
235
  return null;
236
236
 
237
237
  const nextTokenType = recognizer.getTokenStream().LA(2);
238
- const { Number } = recognizer.constructor;
239
- if (nextTokenType > Number && // next token is Id|Unreserved|IllegalToken
240
- token.type <= Number) // current token is not
238
+ const { Number: num } = recognizer.constructor;
239
+ if (nextTokenType > num && // next token is Id|Unreserved|IllegalToken
240
+ token.type <= num) // current token is not
241
241
  return null;
242
242
 
243
243
  const expecting = this.getExpectedTokens(recognizer);
@@ -1059,6 +1059,8 @@ function assignAnnotationValue( anno, value ) {
1059
1059
  }
1060
1060
 
1061
1061
  function relevantDigits( val ) {
1062
+ // We know the value does not contain newlines, hence the RegEx is safe.
1063
+ // eslint-disable-next-line sonarjs/slow-regex
1062
1064
  val = val.replace( /e.+$/i, '' );
1063
1065
 
1064
1066
  // To avoid the super-linear RegEx `0+$`, use the non-backtracking version and
@@ -1014,7 +1014,12 @@ function getRootArtifactName( artifactName, csn ) {
1014
1014
  // 'foo' => 'foo';
1015
1015
  // 'foo::bar' => 'bar'
1016
1016
  function getLastPartOf( name ) {
1017
- return name.substring(name.search(/[^.:]+$/));
1017
+ // Not using RegEx /[^.:]+$/ to avoid ReDoS.
1018
+ for (let i = name.length - 1; i >= 0; --i) {
1019
+ if (name[i] === '.' || name[i] === ':')
1020
+ return name.substring(i + 1);
1021
+ }
1022
+ return name;
1018
1023
  }
1019
1024
 
1020
1025
  // Return the last part of reference array 'ref'
@@ -197,6 +197,7 @@ optionProcessor.command('H, toHana')
197
197
  .option(' --assert-integrity-type <type>', { valid: ['RT', 'DB'], ignoreCase: true })
198
198
  .option(' --pre2134ReferentialConstraintNames')
199
199
  .option(' --disable-hana-comments')
200
+ .option(' --standard-database-functions')
200
201
  .help(`
201
202
  Usage: cdsc toHana [options] <files...>
202
203
 
@@ -233,6 +234,7 @@ optionProcessor.command('H, toHana')
233
234
  DB : Create database constraints for associations
234
235
  --pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
235
236
  --disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
237
+ --standard-database-functions Enable rendering of standard database function mappings.
236
238
  `);
237
239
 
238
240
  optionProcessor.command('O, toOdata')
@@ -340,6 +342,7 @@ optionProcessor.command('Q, toSql')
340
342
  .option(' --better-sqlite-session-variables <bool>')
341
343
  .option(' --transitive-localized-views')
342
344
  .option(' --with-hana-associations <bool>', { valid: [ 'true', 'false' ] })
345
+ .option(' --standard-database-functions')
343
346
  .help(`
344
347
  Usage: cdsc toSql [options] <files...>
345
348
 
@@ -399,9 +402,10 @@ optionProcessor.command('Q, toSql')
399
402
  --transitive-localized-views If set, the backends will create localized convenience views for
400
403
  those views, that only have an association to a localized entity/view.
401
404
  --with-hana-associations <bool>
402
- Enable and disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
405
+ Enable or disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
403
406
  true : (default) Render "WITH ASSOCIATIONS"
404
407
  false : Do not render "WITH ASSOCIATIONS"
408
+ --standard-database-functions Enable rendering of standard database function mappings.
405
409
  `);
406
410
 
407
411
  optionProcessor.command('toRename')