@sap/cds-compiler 2.5.0 → 2.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +191 -9
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +33 -3
  4. package/lib/api/main.js +29 -101
  5. package/lib/api/options.js +15 -11
  6. package/lib/api/validate.js +12 -8
  7. package/lib/backends.js +0 -81
  8. package/lib/base/keywords.js +32 -2
  9. package/lib/base/message-registry.js +63 -9
  10. package/lib/base/messages.js +63 -21
  11. package/lib/base/model.js +2 -3
  12. package/lib/checks/defaultValues.js +27 -2
  13. package/lib/checks/elements.js +1 -6
  14. package/lib/checks/foreignKeys.js +0 -6
  15. package/lib/checks/managedWithoutKeys.js +17 -0
  16. package/lib/checks/nonexpandableStructured.js +38 -0
  17. package/lib/checks/onConditions.js +9 -45
  18. package/lib/checks/queryNoDbArtifacts.js +25 -7
  19. package/lib/checks/selectItems.js +25 -2
  20. package/lib/checks/types.js +26 -2
  21. package/lib/checks/unknownMagic.js +38 -0
  22. package/lib/checks/utils.js +61 -0
  23. package/lib/checks/validator.js +60 -7
  24. package/lib/compiler/assert-consistency.js +16 -7
  25. package/lib/compiler/builtins.js +2 -0
  26. package/lib/compiler/checks.js +6 -4
  27. package/lib/compiler/definer.js +99 -42
  28. package/lib/compiler/index.js +73 -27
  29. package/lib/compiler/resolver.js +288 -157
  30. package/lib/compiler/shared.js +31 -11
  31. package/lib/edm/annotations/genericTranslation.js +182 -186
  32. package/lib/edm/csn2edm.js +103 -108
  33. package/lib/edm/edm.js +18 -21
  34. package/lib/edm/edmPreprocessor.js +361 -114
  35. package/lib/edm/edmUtils.js +103 -33
  36. package/lib/gen/Dictionary.json +22 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +12 -1
  39. package/lib/gen/language.tokens +57 -53
  40. package/lib/gen/languageLexer.interp +10 -1
  41. package/lib/gen/languageLexer.js +770 -744
  42. package/lib/gen/languageLexer.tokens +49 -46
  43. package/lib/gen/languageParser.js +4713 -4279
  44. package/lib/json/from-csn.js +103 -45
  45. package/lib/json/to-csn.js +296 -117
  46. package/lib/language/antlrParser.js +4 -3
  47. package/lib/language/errorStrategy.js +1 -0
  48. package/lib/language/genericAntlrParser.js +21 -12
  49. package/lib/language/language.g4 +99 -31
  50. package/lib/main.d.ts +81 -3
  51. package/lib/main.js +30 -7
  52. package/lib/model/api.js +78 -0
  53. package/lib/model/csnRefs.js +329 -142
  54. package/lib/model/csnUtils.js +235 -58
  55. package/lib/model/enrichCsn.js +18 -1
  56. package/lib/model/revealInternalProperties.js +2 -1
  57. package/lib/modelCompare/compare.js +37 -20
  58. package/lib/optionProcessor.js +9 -3
  59. package/lib/render/.eslintrc.json +4 -1
  60. package/lib/render/DuplicateChecker.js +8 -5
  61. package/lib/render/toCdl.js +112 -33
  62. package/lib/render/toHdbcds.js +134 -64
  63. package/lib/render/toSql.js +91 -38
  64. package/lib/render/utils/common.js +8 -13
  65. package/lib/render/utils/sql.js +3 -3
  66. package/lib/sql-identifier.js +6 -1
  67. package/lib/transform/db/assertUnique.js +5 -6
  68. package/lib/transform/db/constraints.js +29 -13
  69. package/lib/transform/db/draft.js +8 -6
  70. package/lib/transform/db/expansion.js +582 -0
  71. package/lib/transform/db/flattening.js +325 -0
  72. package/lib/transform/db/groupByOrderBy.js +2 -2
  73. package/lib/transform/db/transformExists.js +284 -63
  74. package/lib/transform/forHanaNew.js +98 -381
  75. package/lib/transform/forOdataNew.js +21 -22
  76. package/lib/transform/localized.js +37 -10
  77. package/lib/transform/odata/attachPath.js +19 -4
  78. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  79. package/lib/transform/odata/referenceFlattener.js +60 -39
  80. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  81. package/lib/transform/odata/structuralPath.js +72 -0
  82. package/lib/transform/odata/structureFlattener.js +19 -18
  83. package/lib/transform/odata/typesExposure.js +22 -12
  84. package/lib/transform/transformUtilsNew.js +134 -78
  85. package/lib/transform/translateAssocsToJoins.js +17 -14
  86. package/lib/transform/universalCsnEnricher.js +67 -0
  87. package/lib/utils/file.js +0 -11
  88. package/lib/utils/moduleResolve.js +6 -8
  89. package/package.json +1 -1
  90. package/lib/json/walker.js +0 -26
  91. package/lib/transform/sqlite +0 -0
  92. package/lib/utils/string.js +0 -17
@@ -87,11 +87,11 @@
87
87
  * @returns {any} XSN property (e.g. string, object, ...)
88
88
  */
89
89
 
90
- const { makeMessageFunction } = require('../base/messages');
91
90
  const { dictAdd } = require('../base/dictionaries');
92
91
 
93
92
  let inExtensions = null;
94
- let vocabInDefinitions = null;
93
+
94
+ let vocabInDefinitions = null; // must be reset!
95
95
 
96
96
  // CSN property names reserved for CAP
97
97
  const ourpropsRegex = /^[_$]?[a-zA-Z]+[0-9]*$/;
@@ -145,7 +145,7 @@ const schemaClasses = {
145
145
  type: natnumOrStar,
146
146
  msgId: 'syntax-csn-expected-cardinality',
147
147
  },
148
- column: {
148
+ columns: {
149
149
  arrayOf: selectItem,
150
150
  msgId: 'syntax-csn-expected-column',
151
151
  defaultKind: '$column',
@@ -238,15 +238,15 @@ const schema = compileSchema( {
238
238
  validKinds: [],
239
239
  },
240
240
  columns: {
241
- class: 'column',
241
+ class: 'columns',
242
242
  inKind: [ 'extend' ], // only valid in extend and SELECT/projection
243
243
  },
244
244
  expand: {
245
- class: 'column',
245
+ class: 'columns',
246
246
  inKind: [ '$column' ], // only valid in $column
247
247
  },
248
248
  inline: {
249
- class: 'column',
249
+ class: 'columns',
250
250
  inKind: [ '$column' ], // only valid in $column
251
251
  },
252
252
  keys: {
@@ -272,7 +272,7 @@ const schema = compileSchema( {
272
272
  },
273
273
  annotate: {
274
274
  type: kindAndName,
275
- inKind: (kind => kind === 'annotate'), // using array would test 'entity' for extensions[]
275
+ inKind: [ 'annotate' ],
276
276
  },
277
277
  extend: {
278
278
  type: kindAndName,
@@ -326,7 +326,7 @@ const schema = compileSchema( {
326
326
  inKind: [ 'element', 'type', 'param', 'annotation' ],
327
327
  },
328
328
  scale: {
329
- type: natnum,
329
+ type: scalenum,
330
330
  inKind: [ 'element', 'type', 'param', 'annotation' ],
331
331
  },
332
332
  srid: {
@@ -456,9 +456,19 @@ const schema = compileSchema( {
456
456
  requires: 'from',
457
457
  optional: [
458
458
  'from', 'mixin', 'all', 'distinct', 'columns', 'excluding',
459
- 'where', 'groupBy', 'having', 'orderBy', 'limit',
459
+ 'where', 'groupBy', 'having', 'orderBy', 'limit', 'elements',
460
460
  ],
461
461
  inKind: [ '$column' ],
462
+ schema: {
463
+ elements: {
464
+ dictionaryOf: definition,
465
+ type: ( ...a ) => {
466
+ dictionaryOf( definition )( ...a );
467
+ }, // ignore, but test
468
+ defaultKind: 'element',
469
+ validKinds: [ 'element' ],
470
+ },
471
+ },
462
472
  },
463
473
  SET: {
464
474
  type: queryTerm,
@@ -599,10 +609,10 @@ const schema = compileSchema( {
599
609
  inKind: [ 'entity', 'type', 'aspect', 'event', 'extend' ],
600
610
  },
601
611
  returns: {
602
- type: definition,
612
+ type: returnsDefinition,
603
613
  defaultKind: 'param',
604
614
  validKinds: [ 'param' ],
605
- inKind: [ 'action', 'function' ],
615
+ inKind: [ 'action', 'function', 'annotate' ],
606
616
  },
607
617
  technicalConfig: { // treat it like external_property
608
618
  type: extra,
@@ -650,7 +660,8 @@ const schema = compileSchema( {
650
660
  indexNo: { // CSN v0.1.0, but ignored without message
651
661
  ignore: true, type: ignore,
652
662
  },
653
- $: { type: ignore, ignore: true }, // including $env
663
+ // TODO: should we keep $parens ?
664
+ $: { type: ignore, ignore: true }, // including $origin
654
665
  _: { type: ignore, ignore: true },
655
666
  } );
656
667
 
@@ -678,7 +689,7 @@ const validLiteralsExtra = Object.assign( Object.create(null), {
678
689
 
679
690
  /** @type {(id, location, textOrArguments, texts?) => void} */
680
691
  // eslint-disable-next-line no-unused-vars
681
- let message = (id, loc, textOrArguments, texts) => undefined;
692
+ let message = (_id, loc, textOrArguments, texts) => undefined;
682
693
  /** @type {(id, location, textOrArguments, texts?) => void} */
683
694
  // eslint-disable-next-line no-unused-vars
684
695
  let error = (id, loc, textOrArguments, texts) => undefined;
@@ -728,14 +739,15 @@ function compileSchema( specs, proto = null) {
728
739
  throw new Error( `Missing type specification for property "${ p }"` );
729
740
  }
730
741
  }
731
- if (proto)
732
- return r;
742
+ // Set property 'xorGroup' in main and sub schema:
733
743
  for (const group in xorGroups) {
734
744
  for (const prop of xorGroups[group]) {
735
745
  if (r[prop].xorGroup === undefined)
736
746
  r[prop].xorGroup = group;
737
747
  }
738
748
  }
749
+ if (proto)
750
+ return r;
739
751
  for (const prop of exprProperties) {
740
752
  if (r[prop].inValue === undefined)
741
753
  r[prop].inValue = true;
@@ -933,7 +945,10 @@ function definition( def, spec, xsn, csn, name ) {
933
945
  return s.inKind && s.inKind( kind, spec );
934
946
  return s.inKind.includes( kind ) &&
935
947
  // for an 'annotate', both 'annotate' and the "host" kind must be expected
936
- (!inExtensions || s.inKind.includes( inExtensions ));
948
+ (!inExtensions || s.inKind.includes( inExtensions ) ||
949
+ // extending elements in returns can be without 'returns' in CSN
950
+ // TODO: with warning/info?
951
+ inExtensions === 'action' && p === 'elements');
937
952
  }
938
953
  }
939
954
 
@@ -981,12 +996,34 @@ function keys( array, spec, xsn ) {
981
996
  }
982
997
 
983
998
  function selectItem( def, spec, xsn, csn ) {
984
- if (def === '*')
999
+ if (def === '*') // compile() will complain about repeated '*'s
985
1000
  return { val: '*', location: location() };
986
1001
 
987
1002
  return definition( def, spec, xsn, csn, null ); // definer sets name
988
1003
  }
989
1004
 
1005
+ function returnsDefinition( def, spec, xsn, csn, name ) {
1006
+ // TODO: be stricter in what is allowed inside returns
1007
+ if (!inExtensions)
1008
+ return definition( def, spec, xsn, csn, name );
1009
+ // for the moment, flatten elements in returns in an annotate
1010
+ // TODO: bigger Core Compiler changes would have to be done otherwise
1011
+ xsn.elements = definition( def, spec, xsn, csn, name ).elements;
1012
+ xsn.$syntax = 'returns';
1013
+ return undefined;
1014
+ }
1015
+
1016
+ // For v1 CSNs with annotation definitions
1017
+ function attachVocabInDefinitions( csn ) {
1018
+ if (!csn.vocabularies) {
1019
+ csn.vocabularies = vocabInDefinitions;
1020
+ }
1021
+ else {
1022
+ for (const name in vocabInDefinitions)
1023
+ dictAdd( csn.vocabularies, name, vocabInDefinitions[name] );
1024
+ }
1025
+ }
1026
+
990
1027
  // Kind, names and references (std signature) --------------------------------
991
1028
 
992
1029
  function kindAndName( id, spec, xsn ) {
@@ -1102,6 +1139,12 @@ function stringValOrNull( val, spec ) {
1102
1139
  return stringVal(val, spec);
1103
1140
  }
1104
1141
 
1142
+ function scalenum( val, spec ) {
1143
+ if ([ 'floating', 'variable' ].includes(val))
1144
+ return { val, literal: 'string', location: location() };
1145
+ return natnum(val, spec );
1146
+ }
1147
+
1105
1148
  function natnum( val, spec ) {
1106
1149
  if (typeof val === 'number' && val >= 0)
1107
1150
  // XSN TODO: do not require literal
@@ -1226,9 +1269,14 @@ function func( val, spec, xsn ) {
1226
1269
  return { path: [ { id: val, location: location() } ], location: location() };
1227
1270
  }
1228
1271
 
1229
- function xpr( exprs, spec, xsn ) {
1230
- xsn.op = { val: 'xpr', location: location() };
1231
- xsn.args = exists( exprs, spec, xsn );
1272
+ function xpr( exprs, spec, xsn, csn ) {
1273
+ if (csn.func) {
1274
+ xsn.suffix = exprArgs( exprs, spec );
1275
+ }
1276
+ else {
1277
+ xsn.op = { val: 'xpr', location: location() };
1278
+ xsn.args = exprArgs( exprs, spec, xsn );
1279
+ }
1232
1280
  }
1233
1281
 
1234
1282
  function list( exprs, spec, xsn ) {
@@ -1236,15 +1284,15 @@ function list( exprs, spec, xsn ) {
1236
1284
  xsn.args = arrayOf( exprOrString )( exprs, spec, xsn );
1237
1285
  }
1238
1286
 
1239
- function xprInValue( exprs, spec, xsn ) {
1287
+ function xprInValue( exprs, spec, xsn, csn ) {
1240
1288
  // if the top-level xpr is just for a cast:
1241
1289
  if (exprs.length === 1 && exprs[0].cast) {
1242
1290
  const x = {};
1243
- xpr( exprs, spec, x );
1291
+ xpr( exprs, spec, x, csn );
1244
1292
  Object.assign( xsn, x.args[0] );
1245
1293
  }
1246
1294
  else {
1247
- xpr( exprs, spec, xsn );
1295
+ xpr( exprs, spec, xsn, csn );
1248
1296
  }
1249
1297
  }
1250
1298
 
@@ -1293,9 +1341,9 @@ function exprOrString( e, spec ) {
1293
1341
  }
1294
1342
 
1295
1343
  // mark path argument of 'exits' predicate with $expected:'exists'
1296
- function exists( cond, spec, xsn, csn ) {
1297
- const rxsn = arrayOf( exprOrString )(cond, spec, xsn, csn);
1298
- if (Array.isArray(rxsn) && rxsn.some(x => x === 'exists')) {
1344
+ function exprArgs( cond, spec, xsn, csn ) {
1345
+ const rxsn = arrayOf( exprOrString )( cond, spec, xsn, csn );
1346
+ if (Array.isArray( rxsn ) && rxsn.some( x => x === 'exists' )) {
1299
1347
  for (let i = 0; i < rxsn.length - 1; i++) {
1300
1348
  if (rxsn[i] === 'exists' && rxsn[i + 1].path)
1301
1349
  rxsn[++i].$expected = 'exists';
@@ -1308,7 +1356,7 @@ function condition( cond, spec ) {
1308
1356
  const loc = location();
1309
1357
  const x = {
1310
1358
  op: { val: 'xpr', location: loc },
1311
- args: exists( cond, spec ),
1359
+ args: exprArgs( cond, spec ),
1312
1360
  location: loc,
1313
1361
  };
1314
1362
  return x;
@@ -1478,7 +1526,7 @@ function calculateKind( def, spec ) {
1478
1526
  return 'annotate';
1479
1527
  }
1480
1528
  if (spec.prop === 'extensions') {
1481
- inExtensions = (def.extend) ? '' : 'entity';
1529
+ inExtensions = (def.extend) ? '' : 'annotate';
1482
1530
  return (def.extend) ? 'extend' : 'annotate';
1483
1531
  }
1484
1532
  const kind = (def.kind === 'view') ? 'entity' : def.kind; // 'view' is CSN v0.1.0
@@ -1534,6 +1582,9 @@ function checkAndSetXorGroup( group, prop, xor ) {
1534
1582
  xor[group] = prop;
1535
1583
  return true;
1536
1584
  }
1585
+ if (prop === 'func' && xor[group] === 'xpr' ||
1586
+ prop === 'xpr' && xor[group] === 'func')
1587
+ return true; // hack for window function: both func and xpr is allowed
1537
1588
  error( 'syntax-csn-excluded-property', location(true),
1538
1589
  { prop, otherprop: xor[group] },
1539
1590
  'CSN property $(PROP) can only be used alternatively to $(OTHERPROP)');
@@ -1635,6 +1686,15 @@ function popLocation( obj ) {
1635
1686
  dollarLocations.pop();
1636
1687
  }
1637
1688
 
1689
+ function resetHeapModuleVars() {
1690
+ vocabInDefinitions = null;
1691
+ dollarLocations = [];
1692
+ message = () => undefined;
1693
+ error = () => undefined;
1694
+ warning = () => undefined;
1695
+ info = () => undefined;
1696
+ }
1697
+
1638
1698
  // API -----------------------------------------------------------------------
1639
1699
 
1640
1700
  /**
@@ -1645,7 +1705,7 @@ function popLocation( obj ) {
1645
1705
  * @param {CSN.Options} options
1646
1706
  * @returns {object} Augmented CSN (a.k.a XSN)
1647
1707
  */
1648
- function augment( csn, filename, options ) {
1708
+ function toXsn( csn, filename, options, messageFunctions ) {
1649
1709
  csnVersionZero = csn.version && csn.version.csn === '0.1.0';
1650
1710
  csnFilename = filename;
1651
1711
  virtualLine = 1;
@@ -1654,11 +1714,8 @@ function augment( csn, filename, options ) {
1654
1714
  vocabInDefinitions = null;
1655
1715
  const xsn = { $frontend: 'json' };
1656
1716
 
1657
- const msgFcts = makeMessageFunction( xsn, options, 'parse' );
1658
- message = msgFcts.message;
1659
- error = msgFcts.error;
1660
- warning = msgFcts.warning;
1661
- info = msgFcts.info;
1717
+ // eslint-disable-next-line object-curly-newline
1718
+ ({ message, error, warning, info } = messageFunctions);
1662
1719
 
1663
1720
  if (csnVersionZero) {
1664
1721
  warning( 'syntax-csn-zero-version', location(true),
@@ -1671,25 +1728,27 @@ function augment( csn, filename, options ) {
1671
1728
  csn.$sources.every( fname => typeof fname === 'string' ))
1672
1729
  // non-enumerable or enumerable, ignore with wrong value
1673
1730
  r.$sources = csn.$sources;
1731
+ resetHeapModuleVars();
1674
1732
  return Object.assign( xsn, r );
1675
1733
  }
1676
1734
 
1677
- // For v1 CSNs with annotation definitions
1678
- function attachVocabInDefinitions( csn ) {
1679
- if (!csn.vocabularies) {
1680
- csn.vocabularies = vocabInDefinitions;
1735
+
1736
+ function augment( csn, filename = 'csn.json', options = {}, messageFunctions ) {
1737
+ try {
1738
+ return toXsn( csn, filename, options, messageFunctions );
1681
1739
  }
1682
- else {
1683
- for (const name in vocabInDefinitions)
1684
- dictAdd( csn.vocabularies, name, vocabInDefinitions[name] );
1740
+ catch ( e ) {
1741
+ resetHeapModuleVars();
1742
+ throw e;
1685
1743
  }
1686
1744
  }
1687
1745
 
1688
- function parse( source, filename = 'csn.json', options = {} ) {
1746
+ function parse( source, filename = 'csn.json', options = {}, messageFunctions ) {
1689
1747
  try {
1690
- return augment( JSON.parse(source), filename, options );
1748
+ return augment( JSON.parse(source), filename, options, messageFunctions );
1691
1749
  }
1692
1750
  catch ( e ) {
1751
+ resetHeapModuleVars();
1693
1752
  if (!(e instanceof SyntaxError))
1694
1753
  throw e;
1695
1754
  const xsn = {};
@@ -1715,8 +1774,7 @@ function parse( source, filename = 'csn.json', options = {} ) {
1715
1774
  line,
1716
1775
  col: column,
1717
1776
  };
1718
- const msgs = makeMessageFunction( xsn, options, 'parse' );
1719
- msgs.error( 'syntax-csn-illegal-json', loc, { msg }, 'Illegal JSON: $(MSG)' );
1777
+ messageFunctions.error( 'syntax-csn-illegal-json', loc, { msg }, 'Illegal JSON: $(MSG)' );
1720
1778
  return xsn;
1721
1779
  }
1722
1780
  }