@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.
- package/CHANGELOG.md +50 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +4 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +9 -23
- package/lib/api/options.js +12 -4
- package/lib/api/validate.js +23 -2
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/message-registry.js +10 -2
- package/lib/base/messages.js +23 -9
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +7 -0
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +28 -1
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +58 -91
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +93 -34
- package/lib/compiler/shared.js +29 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +31 -36
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +169 -34
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/genericAntlrParser.js +72 -14
- package/lib/language/language.g4 +73 -0
- package/lib/main.d.ts +136 -17
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +108 -31
- package/lib/model/csnUtils.js +63 -29
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +29 -18
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +9 -3
- package/lib/render/toHdbcds.js +16 -36
- package/lib/render/toSql.js +23 -5
- package/lib/transform/db/constraints.js +278 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +17 -1
- package/lib/transform/db/transformExists.js +61 -2
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +56 -435
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/transformUtilsNew.js +10 -0
- package/lib/transform/translateAssocsToJoins.js +5 -13
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- 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/
|
|
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(
|
|
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 (
|
|
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
|
|
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
|
|
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.
|
|
6
|
-
//
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
36
|
+
function term(useColor = 'auto') {
|
|
37
|
+
let hasColor = hasColorShell;
|
|
38
|
+
changeColorMode(useColor);
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 };
|
package/lib/utils/timetrace.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @class TimeTrace
|
|
7
7
|
*/
|
|
8
|
-
class
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* @param {number} indent
|
|
21
|
+
/**
|
|
22
|
+
* Start watch.
|
|
32
23
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
87
|
+
const b = new StopWatch(id);
|
|
71
88
|
this.traceStack.push(b);
|
|
72
|
-
b.start(
|
|
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(
|
|
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 };
|