@sap/cds-compiler 3.5.4 → 3.6.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 (84) hide show
  1. package/CHANGELOG.md +65 -2
  2. package/bin/cdsc.js +14 -6
  3. package/doc/CHANGELOG_ARCHIVE.md +10 -10
  4. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  5. package/lib/api/main.js +32 -55
  6. package/lib/api/options.js +3 -2
  7. package/lib/api/validate.js +5 -0
  8. package/lib/base/message-registry.js +104 -32
  9. package/lib/base/messages.js +277 -212
  10. package/lib/base/optionProcessorHelper.js +9 -2
  11. package/lib/base/shuffle.js +50 -0
  12. package/lib/checks/actionsFunctions.js +37 -20
  13. package/lib/checks/foreignKeys.js +13 -6
  14. package/lib/checks/nonexpandableStructured.js +1 -2
  15. package/lib/checks/onConditions.js +21 -19
  16. package/lib/checks/parameters.js +1 -1
  17. package/lib/checks/queryNoDbArtifacts.js +2 -0
  18. package/lib/checks/types.js +16 -22
  19. package/lib/compiler/assert-consistency.js +31 -28
  20. package/lib/compiler/builtins.js +20 -4
  21. package/lib/compiler/checks.js +72 -63
  22. package/lib/compiler/define.js +396 -314
  23. package/lib/compiler/extend.js +55 -49
  24. package/lib/compiler/index.js +5 -0
  25. package/lib/compiler/populate.js +28 -11
  26. package/lib/compiler/propagator.js +2 -1
  27. package/lib/compiler/resolve.js +28 -13
  28. package/lib/compiler/shared.js +15 -10
  29. package/lib/compiler/utils.js +7 -7
  30. package/lib/edm/annotations/genericTranslation.js +51 -46
  31. package/lib/edm/annotations/preprocessAnnotations.js +37 -40
  32. package/lib/edm/csn2edm.js +69 -21
  33. package/lib/edm/edm.js +2 -2
  34. package/lib/edm/edmInboundChecks.js +6 -8
  35. package/lib/edm/edmPreprocessor.js +88 -80
  36. package/lib/edm/edmUtils.js +6 -15
  37. package/lib/gen/Dictionary.json +81 -13
  38. package/lib/gen/language.checksum +1 -1
  39. package/lib/gen/language.interp +2 -1
  40. package/lib/gen/languageParser.js +4680 -4484
  41. package/lib/inspect/inspectModelStatistics.js +2 -1
  42. package/lib/inspect/inspectPropagation.js +2 -1
  43. package/lib/json/from-csn.js +131 -78
  44. package/lib/json/to-csn.js +39 -23
  45. package/lib/language/antlrParser.js +0 -3
  46. package/lib/language/docCommentParser.js +7 -3
  47. package/lib/language/errorStrategy.js +3 -2
  48. package/lib/language/genericAntlrParser.js +96 -41
  49. package/lib/language/language.g4 +112 -128
  50. package/lib/language/multiLineStringParser.js +2 -1
  51. package/lib/main.d.ts +115 -2
  52. package/lib/main.js +16 -3
  53. package/lib/model/csnRefs.js +32 -4
  54. package/lib/model/csnUtils.js +109 -179
  55. package/lib/model/enrichCsn.js +13 -8
  56. package/lib/model/revealInternalProperties.js +4 -3
  57. package/lib/optionProcessor.js +22 -3
  58. package/lib/render/manageConstraints.js +11 -15
  59. package/lib/render/toCdl.js +144 -47
  60. package/lib/render/toHdbcds.js +22 -22
  61. package/lib/render/toRename.js +3 -4
  62. package/lib/render/toSql.js +31 -22
  63. package/lib/render/utils/delta.js +3 -1
  64. package/lib/render/utils/sql.js +2 -14
  65. package/lib/transform/db/associations.js +6 -6
  66. package/lib/transform/db/cdsPersistence.js +3 -3
  67. package/lib/transform/db/constraints.js +4 -6
  68. package/lib/transform/db/expansion.js +4 -4
  69. package/lib/transform/db/flattening.js +12 -15
  70. package/lib/transform/db/temporal.js +4 -3
  71. package/lib/transform/db/transformExists.js +13 -7
  72. package/lib/transform/draft/db.js +7 -7
  73. package/lib/transform/forOdataNew.js +15 -4
  74. package/lib/transform/forRelationalDB.js +59 -41
  75. package/lib/transform/odata/toFinalBaseType.js +106 -82
  76. package/lib/transform/odata/typesExposure.js +26 -17
  77. package/lib/transform/odata/utils.js +1 -1
  78. package/lib/transform/parseExpr.js +1 -1
  79. package/lib/transform/transformUtilsNew.js +33 -10
  80. package/lib/transform/translateAssocsToJoins.js +8 -7
  81. package/lib/transform/universalCsn/coreComputed.js +7 -5
  82. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
  83. package/lib/utils/timetrace.js +2 -2
  84. package/package.json +1 -2
@@ -5,6 +5,7 @@ const {
5
5
  isWhitespaceCharacterNoNewline,
6
6
  cdlNewLineRegEx,
7
7
  } = require('./textUtils');
8
+ const { CompilerAssertion } = require('../base/error');
8
9
 
9
10
  /**
10
11
  * Strips and counts the indentation from the given string.
@@ -76,7 +77,7 @@ class MultiLineStringParser {
76
77
 
77
78
  if (this.str[0] !== '`' || this.str[this.str.length - 1] !== '`')
78
79
  // eslint-disable-next-line max-len
79
- throw new Error('Invalid multi-line string sequence: Require string to be surrounded by back-ticks!');
80
+ throw new CompilerAssertion('Invalid multi-line string sequence: Require string to be surrounded by back-ticks!');
80
81
 
81
82
  this.output = [];
82
83
  this.isTextBlock = this.str.startsWith('```');
package/lib/main.d.ts CHANGED
@@ -236,7 +236,7 @@ declare namespace compiler {
236
236
  *
237
237
  * Different databases may support different feature sets of SQL.
238
238
  * For example, timestamps are handled differently. Furthermore, "smart-quoting"
239
- * is enabled for `sqlite` and `hana`. This is useful if identifiers
239
+ * is enabled for all flavors except `plain`. This is useful if identifiers
240
240
  * collide with reserved keywords.
241
241
  *
242
242
  * - `plain`:
@@ -252,10 +252,20 @@ declare namespace compiler {
252
252
  * - `hana`:
253
253
  * Use this SQL dialect for best compatibility with SAP HANA.
254
254
  * "smart-quoting" upper-cases and quotes identifiers.
255
+ * - `postgres:
256
+ * This SQL dialect ensures compatibility with PostgreSQL.
257
+ * Does not support `hana.*` types. Requires `sqlMapping: 'plain'`.
258
+ * "smart-quoting" quotes identifiers that are reserved keywords, but does not upper-case them.
259
+ * Since v3.3.0
260
+ * - `h2`
261
+ * This SQL dialect ensures compatibility with H2 v2.
262
+ * Does not support `hana.*` types. Requires `sqlMapping: 'plain'`.
263
+ * "smart-quoting" quotes identifiers that are reserved keywords and upper-cases them.
264
+ * Since v3.4.0
255
265
  *
256
266
  * @default 'plain'
257
267
  */
258
- sqlDialect?: string | 'plain' | 'sqlite' | 'hana'
268
+ sqlDialect?: string | 'plain' | 'sqlite' | 'hana' | 'postgres' | 'h2'
259
269
  /**
260
270
  * Object containing magic variables. These magic variables are
261
271
  * used as placeholder values.
@@ -686,6 +696,59 @@ declare namespace compiler {
686
696
  * Only relevant for element references of path length 1.
687
697
  */
688
698
  const functions: string[];
699
+
700
+ /**
701
+ * If the given `name` requires brackets in SQL, return an escaped
702
+ * identifier in brackets.
703
+ * Otherwise, return the name without brackets.
704
+ *
705
+ * Example:
706
+ * ```js
707
+ * to.cdl.smartId('with ![brackets]')
708
+ * // '![with ![brackets]]]'
709
+ * to.cdl.smartId('OCCURRENCE', null)
710
+ * // 'OCCURRENCE'
711
+ * to.cdl.smartId('OCCURRENCE', 'REPLACE_REGEXPR')
712
+ * // '![OCCURRENCE]'
713
+ * to.cdl.smartId('myid')
714
+ * // 'myid'
715
+ * ```
716
+ *
717
+ * @param name
718
+ * @param [insideFunction=null]
719
+ */
720
+ function smartId(name: string, insideFunction?: string|null) : string;
721
+ /**
722
+ * If the given function `name` requires quoting in CDL, return an escaped
723
+ * function identifier in brackets for CDL.
724
+ * Otherwise, return the function name without brackets.
725
+ *
726
+ * Example:
727
+ * ```js
728
+ * to.cdl.smartFunctionId('with ![brackets]')
729
+ * // '![with ![brackets]]]'
730
+ * to.cdl.smartFunctionId('myfunction')
731
+ * // 'myfunction'
732
+ * ```
733
+ *
734
+ * @param name
735
+ */
736
+ function smartFunctionId(name: string) : string;
737
+ /**
738
+ * Escapes the given name according to the CDL language and puts it
739
+ * into `![` and `]`, properly escaping all `]` in the identifier.
740
+ *
741
+ * Example:
742
+ * ```js
743
+ * to.cdl.delimitedId('with ![brackets]')
744
+ * // '![with ![brackets]]]'
745
+ * to.cdl.delimitedId('myid')
746
+ * // '![myid]'
747
+ * ```
748
+ *
749
+ * @param name
750
+ */
751
+ function delimitedId(name: string) : string;
689
752
  }
690
753
 
691
754
  /**
@@ -702,8 +765,58 @@ declare namespace compiler {
702
765
  */
703
766
  const keywords: string[];
704
767
  }
768
+
769
+ /**
770
+ * If the given `name` requires quoting for SQL dialect `dialect`,
771
+ * returns a quoted and escaped identifier for that SQL dialect.
772
+ * Otherwise, returns the name without quotes.
773
+ *
774
+ * Example:
775
+ * ```js
776
+ * to.sql.smartId('with "quotes"', 'sqlite')
777
+ * // '"with ""quotes"""'
778
+ * to.sql.smartId('SELECT', 'sqlite')
779
+ * // '"SELECT"'
780
+ * to.sql.smartId('myid', 'sqlite')
781
+ * // 'myid'
782
+ * ```
783
+ *
784
+ * @param name
785
+ * @param dialect
786
+ */
705
787
  function smartId(name: string, dialect: string) : string;
788
+ /**
789
+ * If the given function `name` requires quoting for SQL dialect `dialect`,
790
+ * returns a quoted and escaped function identifier for that SQL dialect.
791
+ * Otherwise, returns the function name without quotes.
792
+ *
793
+ * Example:
794
+ * ```js
795
+ * to.sql.smartFunctionId('with "quotes"', 'sqlite')
796
+ * // '"with ""quotes"""'
797
+ * to.sql.smartFunctionId('myfunction', 'sqlite')
798
+ * // 'myfunction'
799
+ * ```
800
+ *
801
+ * @param name
802
+ * @param dialect
803
+ */
706
804
  function smartFunctionId(name: string, dialect: string) : string;
805
+ /**
806
+ * Escapes the given name according to the SQL dialect and puts it
807
+ * into quotes.
808
+ *
809
+ * Example:
810
+ * ```js
811
+ * to.sql.delimitedId('with "quotes"', 'sqlite')
812
+ * // '"with ""quotes"""'
813
+ * to.sql.delimitedId('myid', 'sqlite')
814
+ * // '"myid"'
815
+ * ```
816
+ *
817
+ * @param name
818
+ * @param dialect
819
+ */
707
820
  function delimitedId(name: string, dialect: string) : string;
708
821
  }
709
822
 
package/lib/main.js CHANGED
@@ -19,6 +19,7 @@ const model_api = lazyload('./model/api');
19
19
  const messages = lazyload('./base/messages');
20
20
  const sqlIdentifier = lazyload('./sql-identifier');
21
21
  const keywords = lazyload( './base/keywords' );
22
+ const toCdl = lazyload('./render/toCdl');
22
23
 
23
24
  const parseLanguage = lazyload('./language/antlrParser');
24
25
  const compiler = lazyload('./compiler');
@@ -106,13 +107,22 @@ module.exports = {
106
107
  hasErrors: (...args) => messages.hasErrors(...args),
107
108
 
108
109
  // additional API:
109
- parse: { cdl: (...args) => parseCdl(...args), cql: (...args) => parseCql(...args), expr: (...args) => parseExpr(...args) },
110
+ parse: {
111
+ cdl: (...args) => parseCdl(...args),
112
+ cql: (...args) => parseCql(...args),
113
+ expr: (...args) => parseExpr(...args)
114
+ },
110
115
  // SNAPI
111
- for: { odata: (...args) => snapi.odata(...args) },
116
+ for: {
117
+ odata: (...args) => snapi.odata(...args)
118
+ },
112
119
  to: {
113
120
  cdl: Object.assign((...args) => snapi.cdl(...args), {
114
121
  keywords: Object.freeze([ ...keywords.cdl ] ),
115
122
  functions: Object.freeze([ ...keywords.cdl_functions ] ),
123
+ smartId: (...args) => toCdl.smartId(...args),
124
+ smartFunctionId: (...args) => toCdl.smartFunctionId(...args),
125
+ delimitedId: (...args) => toCdl.delimitedId(...args),
116
126
  }),
117
127
  sql: Object.assign((...args) => snapi.sql(...args), {
118
128
  migration: (...args) => snapi.sql.migration(...args),
@@ -147,7 +157,10 @@ module.exports = {
147
157
  // INTERNAL functions for the cds-lsp package and friends - before you use
148
158
  // it, you MUST talk with us - there can be potential incompatibilities with
149
159
  // new releases (even having the same major version):
150
- $lsp: { parse: (...args) => compiler.parseX(...args), compile: (...args) => compiler.compileX(...args), getArtifactName: a => a.name },
160
+ $lsp: { parse: (...args) => compiler.parseX(...args),
161
+ compile: (...args) => compiler.compileX(...args),
162
+ getArtifactName: a => a.name
163
+ },
151
164
 
152
165
  // CSN Model related functionality
153
166
  model: {
@@ -74,7 +74,7 @@
74
74
  // const csnPath = ['definitions','P','projection','columns',0];
75
75
  // const subElement = inspectRef( csnPath ); // type T is involved
76
76
  // csn.definitions.T.type = 'some.other.type';
77
- // // ({ inspectRef } = csnRefs( csn )); // invalidate caches
77
+ // // ({ inspectRef } = csnRefs( csn )); // drop caches
78
78
  // … = inspectRef( csnPath ); // type T - using the cached or the new?
79
79
  //
80
80
  // On request, we might add a functions for individual cache invalidations or
@@ -155,6 +155,8 @@
155
155
  // which contains cached data. Such data can be a link to a CSN node (like
156
156
  // `_effectiveType`/`elements`), scalar (like `$queryNumber`) or link to
157
157
  // another cache object (like `$next`).
158
+ // - A cache entry must not link to a cache object of another main definition;
159
+ // otherwise, individual cache invalidation does not work.
158
160
  // - Usually, each CSN object has an individual cache object.
159
161
  // - For CSN queries nodes, cache objects are _shared_: both the CSN nodes
160
162
  // `‹query› = { SELECT: ‹select›, … }` and `‹select›` share the same cache
@@ -260,6 +262,7 @@ function csnRefs( csn, universalReady ) {
260
262
  getColumn: elem => getCache( elem, '_column' ),
261
263
  getElement: col => getCache( col, '_element' ),
262
264
  initDefinition,
265
+ dropDefinitionCache,
263
266
  targetAspect,
264
267
  __getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
265
268
  };
@@ -334,7 +337,7 @@ function csnRefs( csn, universalReady ) {
334
337
  return art;
335
338
  }
336
339
  if (notFound !== undefined && typeof ref === 'string')
337
- return notFound; // is only meant for builtins
340
+ return notFound; // is only meant for builtins and $self
338
341
  // Backend bug workaround, TODO: delete next 2 lines
339
342
  if (notFound !== undefined)
340
343
  return notFound;
@@ -396,7 +399,7 @@ function csnRefs( csn, universalReady ) {
396
399
  const location = $location &&
397
400
  (typeof $location === 'string' ? $location : locationString( $location ));
398
401
  const def = Object.keys( art ).join('+') + (location ? `:${ location }` : '');
399
- throw new Error( `Inspecting non-initialized CSN node {${ def }}` );
402
+ throw new CompilerAssertion( `Inspecting non-initialized CSN node {${ def }}` );
400
403
  }
401
404
  const step = getCache( art, '$origin$step' );
402
405
  if (!step)
@@ -436,7 +439,7 @@ function csnRefs( csn, universalReady ) {
436
439
  return effectiveType( art ).items;
437
440
  if (step.target)
438
441
  return targetAspect( effectiveType( art ) );
439
- throw Error( `Illegal navigation step ${ Object.keys(step)[0] }` );
442
+ throw new CompilerAssertion( `Illegal navigation step ${ Object.keys(step)[0] }` );
440
443
  }
441
444
 
442
445
  function effectiveArtFor( art, property ) {
@@ -486,6 +489,31 @@ function csnRefs( csn, universalReady ) {
486
489
  setCache( art, '$origin$step', (kind === 'element' ? step : { [kind]: step }) );
487
490
  }
488
491
 
492
+ function dropDefinitionCache( main ) {
493
+ const queries = getCache( main, '$queries' );
494
+ if (!queries) // not yet initialized
495
+ return;
496
+ if (!cache.delete( main )) // not yet initialized
497
+ return;
498
+ for (const qcache of queries || []) {
499
+ const { _select } = qcache;
500
+ for (const n of Object.keys( _select.mixin || {} ))
501
+ cache.delete( _select.mixin[n] );
502
+ dropColumnsCache( _select.columns );
503
+ traverseDef( _select, null, null, null, a => cache.delete( a ) ); // elements
504
+ }
505
+ traverseDef( main, null, null, null, a => cache.delete( a ) );
506
+ }
507
+
508
+ function dropColumnsCache( select ) {
509
+ if (!select)
510
+ return;
511
+ for (const col of select.columns || select.expand || select.inline || []) {
512
+ dropColumnsCache( col );
513
+ cache.delete( select );
514
+ }
515
+ }
516
+
489
517
  /**
490
518
  * @param {CSN.Path} csnPath
491
519
  *