@sap/cds-compiler 2.10.2 → 2.11.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 (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
@@ -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);
@@ -10,7 +10,7 @@ const structuralNodeHandlers = {
10
10
  returns: traverseTyped,
11
11
  on: traverseArray,
12
12
  keys: traverseArray,
13
- ref: traverseRef,
13
+ ref: traverseArray,
14
14
  query: traverseTyped,
15
15
  SELECT: traverseTyped,
16
16
  SET: traverseTyped,
@@ -33,10 +33,6 @@ function structuralPath(csn, path) {
33
33
  return traverseDict(csn.definitions, path, 1, ['definitions']);
34
34
  }
35
35
 
36
- function traverseRef(obj, path, index, typeStack) {
37
- return traverseArray(obj, path, index, typeStack);
38
- }
39
-
40
36
  function traverseArray(obj, path, index, typeStack) {
41
37
  if(!Array.isArray(obj)) return typeStack;
42
38
  const name = path[index];
@@ -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}
@@ -1084,16 +1094,16 @@ function getTransformers(model, options, pathDelimiter = '_') {
1084
1094
  function expandStructsInExpression(csn, options = {}) {
1085
1095
  applyTransformations(csn, {
1086
1096
  'on': (parent, name, on, path) => {
1087
- parent.on = expand(parent.on, path);
1097
+ parent.on = expand(parent.on, path.concat(name));
1088
1098
  },
1089
1099
  'having': (parent, name, having, path) => {
1090
- parent.having = expand(parent.having, path);
1100
+ parent.having = expand(parent.having, path.concat(name));
1091
1101
  },
1092
1102
  'where': (parent, name, where, path) => {
1093
- parent.where = expand(parent.where, path);
1103
+ parent.where = expand(parent.where, path.concat(name));
1094
1104
  },
1095
1105
  'xpr': (parent, name, xpr, path) => {
1096
- parent.xpr = expand(parent.xpr, path);
1106
+ parent.xpr = expand(parent.xpr, path.concat(name));
1097
1107
  }
1098
1108
  }, undefined, undefined, options);
1099
1109
 
@@ -1111,17 +1121,21 @@ function getTransformers(model, options, pathDelimiter = '_') {
1111
1121
  if(i < expr.length-2)
1112
1122
  {
1113
1123
  const [lhs, op, rhs] = expr.slice(i);
1124
+
1125
+ // we might have to ad-hoc resolve a ref, since handleExists is run before hand and generates new refs.
1126
+ const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
1127
+ const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
1114
1128
  // lhs & rhs must be expandable types (structures or managed associations)
1115
- if(lhs._art && rhs._art &&
1129
+ if(lhsArt && rhsArt &&
1116
1130
  lhs.ref && rhs.ref &&
1117
- isExpandable(lhs._art) && isExpandable(rhs._art) &&
1131
+ isExpandable(lhsArt) && isExpandable(rhsArt) &&
1118
1132
  ['=', '<', '>', '>=', '<=', '!=', '<>'].includes(op) &&
1119
1133
  !(isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs))) {
1120
1134
 
1121
1135
  // if path is scalar and no assoc or has no type (@Core.Computed) use original expression
1122
1136
  // only do the expansion on (managed) assocs and (items.)elements, array of check in ON cond is done elsewhere
1123
- const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art: lhs._art, ref: lhs.ref }, false, true );
1124
- const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art: rhs._art, ref: rhs.ref }, false, true );
1137
+ const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art: lhsArt, ref: lhs.ref }, false, true );
1138
+ const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art: rhsArt, ref: rhs.ref }, false, true );
1125
1139
 
1126
1140
  // mapping dict for lhs/rhs for mismatch check
1127
1141
  // strip lhs/rhs prefix from flattened paths to check remaining common trailing path
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
4
- var { makeMessageFunction } = require('../base/messages');
4
+ const { makeMessageFunction } = require('../base/messages');
5
5
  const { recompileX } = require('../compiler/index');
6
- var { linkToOrigin } = require('../compiler/shared');
6
+ const { linkToOrigin, pathName } = 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
  }
@@ -317,7 +309,7 @@ function translateAssocsToJoins(model, inputOptions = {})
317
309
  let [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
318
310
  if(!QA) {
319
311
  error(null, pathNode.$location,
320
- { name: pathNode.path.map(ps=ps.id).join('.') },
312
+ { name: pathName(pathNode.path) },
321
313
  'Please debug me: No QA found for generic path rewriting in $(NAME)')
322
314
  return;
323
315
  }
@@ -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
@@ -3,6 +3,7 @@
3
3
  'use strict';
4
4
 
5
5
  const fs = require('fs');
6
+ const util = require('util');
6
7
 
7
8
  /**
8
9
  * Split the given source string into its lines. Respects Unix,
@@ -19,8 +20,8 @@ function splitLines(src) {
19
20
  * Returns filesystem utils readFile(), isFile(), realpath() for _CDS_ usage.
20
21
  * This includes a trace as well as usage of a file cache.
21
22
  *
22
- * Note: The synchronous versions accept a callback as well, which is executed
23
- * immediately! This is different from NodeJS's readFileSync()!
23
+ * Note: The synchronous versions accept a callback instead of being async (duh!), which
24
+ * is executed immediately! This is different from NodeJS's readFileSync()!
24
25
  * This is done to allow using it in places where fs.readFile (async) is used.
25
26
  *
26
27
  * @param {object} fileCache
@@ -30,7 +31,7 @@ function cdsFs(fileCache, enableTrace) {
30
31
  const readFile = _wrapReadFileCached(fs.readFile);
31
32
  const readFileSync = _wrapReadFileCached((filename, enc, cb) => {
32
33
  try {
33
- cb(null, fs.readFileSync( filename, enc ));
34
+ cb(null, fs.readFileSync( filename, { encoding: enc } ));
34
35
  }
35
36
  catch (err) {
36
37
  cb(err, null);
@@ -47,10 +48,13 @@ function cdsFs(fileCache, enableTrace) {
47
48
  });
48
49
 
49
50
  return {
51
+ readFileAsync: util.promisify(readFile),
50
52
  readFile,
51
53
  readFileSync,
54
+ isFileAsync: util.promisify(isFile),
52
55
  isFile,
53
56
  isFileSync,
57
+ realpathAsync: util.promisify(realpath),
54
58
  realpath,
55
59
  realpathSync,
56
60
  };
@@ -72,7 +76,7 @@ function cdsFs(fileCache, enableTrace) {
72
76
  * Wraps the given reader into a cached environment including a trace.
73
77
  * The given @p reader must have the same signature as fs.readFile.
74
78
  *
75
- * @param {(filename: string, enc: string, cb: (err, data) => void) => void} reader
79
+ * @param {(filename: string, enc, cb: (err, data) => void) => void} reader
76
80
  */
77
81
  function _wrapReadFileCached( reader ) {
78
82
  return (filename, enc, cb) => {
@@ -93,7 +97,9 @@ function cdsFs(fileCache, enableTrace) {
93
97
  body.syscall = 'open';
94
98
  body.path = filename;
95
99
  }
96
- if (body instanceof Error) {
100
+ if (body && body.stack && body.message) {
101
+ // NOTE: checks for instanceof Error are not reliable if error
102
+ // created in different execution env
97
103
  traceFS( 'READFILE:cache-error:', filename, body.message );
98
104
  cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
99
105
  }
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.2",
3
+ "version": "2.11.4",
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)",
@@ -1,58 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * If a mixin association is published, return the mixin association.
5
- *
6
- * @param {CSN.Query} query Query of the artifact to check
7
- * @param {object} association Association (Element) published by the view
8
- * @param {string} associationName
9
- * @returns {object} The mixin association
10
- */
11
- function getMixinAssocOfQueryIfPublished(query, association, associationName) {
12
- if (query && query.SELECT && query.SELECT.mixin) {
13
- const aliasedColumnsMap = Object.create(null);
14
- if (query.SELECT.columns) {
15
- for (const column of query.SELECT.columns) {
16
- if (column.as && column.ref && column.ref.length === 1)
17
- aliasedColumnsMap[column.as] = column;
18
- }
19
- }
20
-
21
- for (const elem of Object.keys(query.SELECT.mixin)) {
22
- const mixinElement = query.SELECT.mixin[elem];
23
- let originalName = associationName;
24
- if (aliasedColumnsMap[associationName])
25
- originalName = aliasedColumnsMap[associationName].ref[0];
26
-
27
- if (elem === originalName)
28
- return { mixinElement, mixinName: originalName };
29
- }
30
- }
31
- return {};
32
- }
33
-
34
- /**
35
- * Check wether the given artifact uses the given mixin association.
36
- *
37
- * @param {CSN.Query} query Query of the artifact to check
38
- * @param {object} association Mixin association (Element) to check for
39
- * @param {string} associationName
40
- * @returns {boolean} True if used
41
- */
42
- function usesMixinAssociation(query, association, associationName) {
43
- if (query && query.SELECT && query.SELECT.columns) {
44
- for (const column of query.SELECT.columns) {
45
- if (typeof column === 'object' && column.ref && column.ref.length > 1 && (column.ref[0] === associationName || column.ref[0].id === associationName)) {
46
- // FIXME: This is not necessarily correct: the assoc name needs not be the first component, as e.g. $projection.assoc
47
- // would be also valid. Check other paths like $self.assoc ....
48
- return true;
49
- }
50
- }
51
- }
52
- return false;
53
- }
54
-
55
- module.exports = {
56
- usesMixinAssociation,
57
- getMixinAssocOfQueryIfPublished,
58
- };