@sap/cds-compiler 2.10.4 → 2.11.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 (70) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +4 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +9 -23
  7. package/lib/api/options.js +12 -4
  8. package/lib/api/validate.js +23 -2
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/message-registry.js +10 -2
  12. package/lib/base/messages.js +23 -9
  13. package/lib/base/model.js +5 -4
  14. package/lib/base/optionProcessorHelper.js +56 -22
  15. package/lib/checks/selectItems.js +4 -0
  16. package/lib/checks/unknownMagic.js +6 -3
  17. package/lib/compiler/assert-consistency.js +7 -0
  18. package/lib/compiler/base.js +65 -0
  19. package/lib/compiler/builtins.js +28 -1
  20. package/lib/compiler/checks.js +2 -1
  21. package/lib/compiler/definer.js +58 -91
  22. package/lib/compiler/index.js +16 -4
  23. package/lib/compiler/propagator.js +5 -2
  24. package/lib/compiler/resolver.js +93 -34
  25. package/lib/compiler/shared.js +29 -202
  26. package/lib/compiler/utils.js +173 -0
  27. package/lib/edm/annotations/genericTranslation.js +1 -1
  28. package/lib/edm/csn2edm.js +3 -2
  29. package/lib/edm/edmPreprocessor.js +31 -36
  30. package/lib/edm/edmUtils.js +3 -3
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +17 -1
  33. package/lib/gen/language.tokens +79 -73
  34. package/lib/gen/languageLexer.interp +19 -1
  35. package/lib/gen/languageLexer.js +779 -731
  36. package/lib/gen/languageLexer.tokens +71 -65
  37. package/lib/gen/languageParser.js +4668 -4072
  38. package/lib/json/from-csn.js +10 -10
  39. package/lib/json/to-csn.js +169 -34
  40. package/lib/language/antlrParser.js +11 -0
  41. package/lib/language/genericAntlrParser.js +72 -14
  42. package/lib/language/language.g4 +73 -0
  43. package/lib/main.d.ts +136 -17
  44. package/lib/main.js +3 -1
  45. package/lib/model/api.js +2 -2
  46. package/lib/model/csnRefs.js +108 -31
  47. package/lib/model/csnUtils.js +63 -29
  48. package/lib/model/enrichCsn.js +36 -9
  49. package/lib/model/revealInternalProperties.js +20 -4
  50. package/lib/modelCompare/compare.js +2 -1
  51. package/lib/optionProcessor.js +29 -18
  52. package/lib/render/DuplicateChecker.js +1 -1
  53. package/lib/render/toCdl.js +9 -3
  54. package/lib/render/toHdbcds.js +16 -36
  55. package/lib/render/toSql.js +23 -5
  56. package/lib/transform/db/constraints.js +278 -119
  57. package/lib/transform/db/draft.js +3 -2
  58. package/lib/transform/db/expansion.js +6 -4
  59. package/lib/transform/db/flattening.js +17 -1
  60. package/lib/transform/db/transformExists.js +61 -2
  61. package/lib/transform/db/views.js +438 -0
  62. package/lib/transform/forHanaNew.js +56 -435
  63. package/lib/transform/forOdataNew.js +9 -2
  64. package/lib/transform/localized.js +2 -0
  65. package/lib/transform/transformUtilsNew.js +10 -0
  66. package/lib/transform/translateAssocsToJoins.js +5 -13
  67. package/lib/utils/file.js +5 -3
  68. package/lib/utils/term.js +65 -42
  69. package/lib/utils/timetrace.js +48 -26
  70. package/package.json +1 -1
@@ -22,7 +22,7 @@ const { flattenCSN } = require('./odata/structureFlattener');
22
22
  const generateForeignKeys = require('./odata/generateForeignKeyElements');
23
23
  const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssociations');
24
24
  const expandToFinalBaseType = require('./odata/toFinalBaseType');
25
- const timetrace = require('../utils/timetrace');
25
+ const { timetrace } = require('../utils/timetrace');
26
26
  const { attachPath } = require('./odata/attachPath');
27
27
  const enrichUniversalCsn = require('./universalCsnEnricher');
28
28
 
@@ -191,7 +191,11 @@ function transform4odataWithCsn(inputModel, options) {
191
191
  generateForeignKeys(csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember);
192
192
 
193
193
  // Apply default type facets as set by options
194
- // Flatten on-conditions in unmanaged associations
194
+ // Flatten on-conditions in unmanaged associations
195
+ /* FIXME (HJB): Is this comment still correct? processOnCond only strips $self
196
+ We should not remove $self prefixes in structured OData to not
197
+ interfer with path resolution
198
+ */
195
199
  // This must be done before all the draft logic as all
196
200
  // composition targets are annotated with @odata.draft.enabled in this step
197
201
  forEachDefinition(csn, [ setDefaultTypeFacets, processOnCond ], { skipArtifact: isExternalServiceMember });
@@ -569,6 +573,9 @@ function transform4odataWithCsn(inputModel, options) {
569
573
  // CDXCORE-481
570
574
  // (4.5) If the member is an association whose target has @cds.odata.valuelist annotate it
571
575
  // with @Common.ValueList.viaAssociation.
576
+ /*
577
+ FIXME (HJB): Comment outdated: Anno propagation to FKs is done in EdmPreprocessor
578
+ */
572
579
  // This must be done before foreign keys are calculated and the annotations are propagated
573
580
  // to them. This will make sure that association and all its foreign keys are annotated with
574
581
  // Common.ValueList in the final EDM.
@@ -698,6 +698,8 @@ function copyPersistenceAnnotations(target, source) {
698
698
  * @param {CSN.Options} options
699
699
  */
700
700
  function hasExistingLocalizationViews(csn, options) {
701
+ if (!csn || !csn.definitions)
702
+ return false;
701
703
  const firstLocalizedView = Object.keys(csn.definitions).find(isInLocalizedNamespace);
702
704
  if (firstLocalizedView) {
703
705
  const { info } = makeMessageFunction(csn, options);
@@ -80,6 +80,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
80
80
  else if(defStrLen5k)
81
81
  element.length = 5000;
82
82
  }
83
+ if (element.type === 'cds.Binary' && element.length === undefined) {
84
+ if(options.defaultBinaryLength) {
85
+ element.length = options.defaultBinaryLength;
86
+ setProp(element, '$default', true);
87
+ }
88
+ else if(defStrLen5k)
89
+ element.length = 5000;
90
+ }
83
91
  /*
84
92
  if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
85
93
  element.precision = options.precision;
@@ -343,6 +351,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
343
351
 
344
352
  /**
345
353
  * Copy properties of the referenced type, but don't resolve to the final base type.
354
+ *
355
+ * Do not copy the length if it was just set via the default-value.
346
356
  *
347
357
  * @param {any} node Node to copy to
348
358
  * @returns {void}
@@ -3,10 +3,10 @@
3
3
  const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
4
4
  var { makeMessageFunction } = require('../base/messages');
5
5
  const { recompileX } = require('../compiler/index');
6
- var { linkToOrigin } = require('../compiler/shared');
6
+ var { linkToOrigin } = require('../compiler/utils');
7
7
  const {compactModel, compactExpr} = require('../json/to-csn');
8
8
  const { deduplicateMessages } = require('../base/messages');
9
- const timetrace = require('../utils/timetrace');
9
+ const { timetrace } = require('../utils/timetrace');
10
10
  // Paths that start with an artifact of protected kind are special
11
11
  // either ignore them in QAT building or in path rewriting
12
12
  const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
@@ -179,7 +179,7 @@ function translateAssocsToJoins(model, inputOptions = {})
179
179
  let joinTree = query.from;
180
180
  for(let tan in query.$tableAliases)
181
181
  {
182
- if(!['$projection', '$self'].includes(tan)) // don't drive into $projection/$self tableAlias (yet)
182
+ if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
183
183
  {
184
184
  let ta = query.$tableAliases[tan];
185
185
  joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
@@ -200,7 +200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
200
200
  function createQAForFromClauseSubQuery(query, env)
201
201
  {
202
202
  for (let taName in query.$tableAliases) {
203
- if (!['$self', '$projection'].includes(taName)) {
203
+ if (query.$tableAliases[taName].kind !== '$self') {
204
204
  let ta = query.$tableAliases[taName];
205
205
  if(!ta.$QA) {
206
206
  ta.$QA = createQA(env, ta._origin, taName, undefined);
@@ -241,14 +241,6 @@ function translateAssocsToJoins(model, inputOptions = {})
241
241
  {
242
242
  art.$QA = createQA(env, art.target._artifact, art.name.id );
243
243
  art.$QA.mixin = true;
244
- /* Mark mixin definition to be _ignored:
245
- - If the mixin is used, it is now resolved into a join => definition vaporizes
246
- - If the mixin is published, forHana backend must create a __copy with rewritten
247
- $projection ON conditon and publish it with alias.
248
- - If the mixin is neither be used nor published it shall not be visible to the database
249
- (internal mixin).
250
- */
251
- art.$a2j = { _ignore: true };
252
244
  }
253
245
  });
254
246
  }
@@ -1403,7 +1395,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1403
1395
 
1404
1396
  let [head, ...tail] = path;
1405
1397
 
1406
- if(['$projection', '$self'].includes(head.id) && tail.length) {
1398
+ if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
1407
1399
  // make sure not to truncate tail
1408
1400
  if(tail.length > 1)
1409
1401
  [head, ...tail] = tail;
package/lib/utils/file.js CHANGED
@@ -30,7 +30,7 @@ function cdsFs(fileCache, enableTrace) {
30
30
  const readFile = _wrapReadFileCached(fs.readFile);
31
31
  const readFileSync = _wrapReadFileCached((filename, enc, cb) => {
32
32
  try {
33
- cb(null, fs.readFileSync( filename, enc ));
33
+ cb(null, fs.readFileSync( filename, { encoding: enc } ));
34
34
  }
35
35
  catch (err) {
36
36
  cb(err, null);
@@ -72,7 +72,7 @@ function cdsFs(fileCache, enableTrace) {
72
72
  * Wraps the given reader into a cached environment including a trace.
73
73
  * The given @p reader must have the same signature as fs.readFile.
74
74
  *
75
- * @param {(filename: string, enc: string, cb: (err, data) => void) => void} reader
75
+ * @param {(filename: string, enc, cb: (err, data) => void) => void} reader
76
76
  */
77
77
  function _wrapReadFileCached( reader ) {
78
78
  return (filename, enc, cb) => {
@@ -93,7 +93,9 @@ function cdsFs(fileCache, enableTrace) {
93
93
  body.syscall = 'open';
94
94
  body.path = filename;
95
95
  }
96
- if (body instanceof Error) {
96
+ if (body && body.stack && body.message) {
97
+ // NOTE: checks for instanceof Error are not reliable if error
98
+ // created in different execution env
97
99
  traceFS( 'READFILE:cache-error:', filename, body.message );
98
100
  cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
99
101
  }
package/lib/utils/term.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // This file is used for color output to stderr and stdout.
3
3
  // Use `term.error`, `term.warn` and `term.info` as they use color output
4
4
  // per default if the process runs in a TTY, i.e. stdout as well as
5
- // stderr are TTYs. stderr/stdout are no TTYs if they (for example)
6
- // are piped to another process or written to file:
5
+ // stderr are TTYs. stderr/stdout are no TTYs if they are
6
+ // (for example) piped into another process or written to file:
7
7
  //
8
8
  // node myApp.js # stdout.isTTY: true, stderr.isTTY: true
9
9
  // node myApp.js | cat # stdout.isTTY: undefined, stderr.isTTY: true
@@ -17,23 +17,9 @@
17
17
  const stderrHasColor = process.stderr.isTTY;
18
18
  const stdoutHasColor = process.stdout.isTTY;
19
19
 
20
- let hasColor = stdoutHasColor && stderrHasColor;
21
-
22
- module.exports.useColor = (mode) => {
23
- switch (mode) {
24
- case false:
25
- case 'never':
26
- hasColor = false;
27
- break;
28
- case true:
29
- case 'always':
30
- hasColor = true;
31
- break;
32
- default:
33
- hasColor = stdoutHasColor && stderrHasColor;
34
- break;
35
- }
36
- };
20
+ // Note: We require both stderr and stdout to be TTYs, as we don't
21
+ // know (in our exported functions) where the text will end up.
22
+ const hasColorShell = stdoutHasColor && stderrHasColor;
37
23
 
38
24
  // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
39
25
  const t = {
@@ -47,29 +33,66 @@ const t = {
47
33
  cyan: '\x1b[36m', // Foreground Cyan
48
34
  };
49
35
 
50
- const as = (codes, o) => (hasColor ? (codes + o + t.reset) : (`${ o }`));
36
+ function term(useColor = 'auto') {
37
+ let hasColor = hasColorShell;
38
+ changeColorMode(useColor);
51
39
 
52
- const asError = o => as(t.red + t.bold, o);
53
- const asWarning = o => as(t.yellow, o);
54
- const asInfo = o => as(t.green, o);
55
- const asHelp = o => as(t.cyan, o);
56
- module.exports.underline = o => as(t.underline, o);
57
- module.exports.bold = o => as(t.bold, o);
58
-
59
- module.exports.asSeverity = (severity, msg) => {
60
- switch ((`${ severity }`).toLowerCase()) {
61
- case 'error': return asError(msg);
62
- case 'warning': return asWarning(msg);
63
- case 'info': return asInfo(msg);
64
- case 'help': return asHelp(msg);
65
- // or e.g. 'none'
66
- default: return msg;
40
+ function changeColorMode(mode) {
41
+ switch (mode) {
42
+ case false:
43
+ case 'never':
44
+ hasColor = false;
45
+ break;
46
+ case true:
47
+ case 'always':
48
+ hasColor = true;
49
+ break;
50
+ default:
51
+ // Note: See also: https://no-color.org/
52
+ // > Command-line software which adds ANSI color to its output by default
53
+ // > should check for the presence of a `NO_COLOR` environment variable
54
+ // > that, when present (regardless of its value), prevents the addition
55
+ // > of ANSI color.
56
+ // Note: To be able to disable colors in tests, we check the environment
57
+ // variable here again.
58
+ hasColor = hasColorShell && (process.env.NO_COLOR === undefined);
59
+ break;
60
+ }
67
61
  }
68
- };
69
62
 
70
- module.exports.codes = t;
71
- module.exports.as = as;
72
- module.exports.error = asError;
73
- module.exports.warn = asWarning;
74
- module.exports.info = asInfo;
75
- module.exports.help = asHelp;
63
+ const as = (codes, o) => (hasColor ? (codes + o + t.reset) : (`${ o }`));
64
+
65
+ const asError = o => as(t.red + t.bold, o);
66
+ const asWarning = o => as(t.yellow, o);
67
+ const asInfo = o => as(t.green, o);
68
+ const asHelp = o => as(t.cyan, o);
69
+
70
+ const underline = o => as(t.underline, o);
71
+ const bold = o => as(t.bold, o);
72
+
73
+ const asSeverity = (severity, msg) => {
74
+ switch ((`${ severity }`).toLowerCase()) {
75
+ case 'error': return asError(msg);
76
+ case 'warning': return asWarning(msg);
77
+ case 'info': return asInfo(msg);
78
+ case 'help': return asHelp(msg);
79
+ // or e.g. 'none'
80
+ default: return msg;
81
+ }
82
+ };
83
+
84
+ return {
85
+ changeColorMode,
86
+ as,
87
+ underline,
88
+ bold,
89
+
90
+ severity: asSeverity,
91
+ error: asError,
92
+ warn: asWarning,
93
+ info: asInfo,
94
+ help: asHelp,
95
+ };
96
+ }
97
+
98
+ module.exports = { term };
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @class TimeTrace
7
7
  */
8
- class TimeTrace {
8
+ class StopWatch {
9
9
  /**
10
10
  * Creates an instance of TimeTrace.
11
11
  * @param {string} id
@@ -13,29 +13,46 @@ class TimeTrace {
13
13
  * @memberOf TimeTrace
14
14
  */
15
15
  constructor(id) {
16
- let startTime;
17
- /**
18
- * Start measuring.
19
- *
20
- * @param {number} indent
21
- */
22
- this.start = function start(indent) {
23
- // eslint-disable-next-line no-console
24
- console.error(`${ ' '.repeat((indent) * 2) }${ id } started`);
25
- startTime = process.hrtime();
26
- };
16
+ this.id = id;
17
+ // eslint-disable-next-line no-multi-assign
18
+ this.startTime = this.lapTime = process.hrtime();
19
+ }
27
20
 
28
- /**
29
- * Stop measuring and log the result
30
- *
31
- * @param {number} indent
21
+ /**
22
+ * Start watch.
32
23
  */
33
- this.stop = function stop(indent) {
34
- const endTime = process.hrtime(startTime);
35
- const base = `${ ' '.repeat(indent * 2) }${ id } took:`;
36
- // eslint-disable-next-line no-console
37
- console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, endTime[0], endTime[1] / 1000000);
38
- };
24
+ start() {
25
+ // eslint-disable-next-line no-multi-assign
26
+ this.startTime = this.lapTime = process.hrtime();
27
+ }
28
+
29
+ /**
30
+ * Stop and return delta T
31
+ * but do not set start time
32
+ */
33
+ stop() {
34
+ return process.hrtime(this.startTime);
35
+ }
36
+
37
+ /**
38
+ * return lap time
39
+ */
40
+ lap() {
41
+ const dt = process.hrtime(this.lapTime);
42
+ this.lapTime = process.hrtime();
43
+ return dt;
44
+ }
45
+
46
+ // stop as sec.ns float
47
+ stopInFloatSecs() {
48
+ const dt = this.stop();
49
+ return dt[0] + dt[1] / 1000000000;
50
+ }
51
+
52
+ // lap as sec.ns float
53
+ lapInFloatSecs() {
54
+ const dt = this.lap();
55
+ return dt[0] + dt[1] / 1000000000;
39
56
  }
40
57
  }
41
58
 
@@ -67,9 +84,11 @@ class TimeTracer {
67
84
  */
68
85
  start(id) {
69
86
  try {
70
- const b = new TimeTrace(id);
87
+ const b = new StopWatch(id);
71
88
  this.traceStack.push(b);
72
- b.start(this.traceStack.length - 1);
89
+ b.start();
90
+ // eslint-disable-next-line no-console
91
+ console.error(`${ ' '.repeat((this.traceStack.length - 1) * 2) }${ id } started`);
73
92
  }
74
93
  catch (e) {
75
94
  // eslint-disable-next-line no-console
@@ -86,7 +105,10 @@ class TimeTracer {
86
105
  stop() {
87
106
  try {
88
107
  const current = this.traceStack.pop();
89
- current.stop(this.traceStack.length);
108
+ const dT = current.stop();
109
+ const base = `${ ' '.repeat(this.traceStack.length * 2) }${ current.id } took:`;
110
+ // eslint-disable-next-line no-console
111
+ console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, dT[0], dT[1] / 1000000);
90
112
  }
91
113
  catch (e) {
92
114
  // eslint-disable-next-line no-console
@@ -101,4 +123,4 @@ const ignoreTimeTrace = {
101
123
  };
102
124
 
103
125
  const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
104
- module.exports = doTimeTrace ? new TimeTracer() : ignoreTimeTrace;
126
+ module.exports = { timetrace: (doTimeTrace ? new TimeTracer() : ignoreTimeTrace), StopWatch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.10.4",
3
+ "version": "2.11.0",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",