@sap/cds-compiler 2.7.0 → 2.11.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.
- package/CHANGELOG.md +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- 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 +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- 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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getVariableReplacement } = require('../model/csnUtils');
|
|
4
|
+
|
|
5
|
+
// We only care about the "wild" ones - $at is validated by the compiler
|
|
6
|
+
const magicVariables = {
|
|
7
|
+
$user: [
|
|
8
|
+
'id', // $user.id
|
|
9
|
+
'locale', // $user.locale
|
|
10
|
+
],
|
|
11
|
+
$session: [
|
|
12
|
+
// no valid ways for this
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check that the given ref does not use magic variables for which we don't have
|
|
18
|
+
* a valid way of rendering.
|
|
19
|
+
*
|
|
20
|
+
* Valid ways:
|
|
21
|
+
* - We know what to do -> $user.id on HANA
|
|
22
|
+
* - The user tells us what to do -> options.variableReplacements
|
|
23
|
+
*
|
|
24
|
+
* @param {object} parent Object with the ref as a property
|
|
25
|
+
* @param {string} name Name of the ref property on parent
|
|
26
|
+
* @param {Array} ref to check
|
|
27
|
+
*/
|
|
28
|
+
function unknownMagicVariable(parent, name, ref) {
|
|
29
|
+
if (parent.$scope && parent.$scope === '$magic') {
|
|
30
|
+
const [ head, ...rest ] = ref;
|
|
31
|
+
const tail = rest.join('.');
|
|
32
|
+
const magicVariable = magicVariables[head];
|
|
33
|
+
if (magicVariable && magicVariable.indexOf(tail) === -1 &&
|
|
34
|
+
getVariableReplacement(ref, this.options) === null)
|
|
35
|
+
this.error(null, parent.$location, { id: tail, elemref: parent }, 'No configuration for magic variable was provided - path $(ELEMREF), step $(ID)');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
ref: unknownMagicVariable,
|
|
41
|
+
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -23,13 +23,17 @@ const {
|
|
|
23
23
|
// both
|
|
24
24
|
const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
|
|
25
25
|
const validateForeignKeys = require('./foreignKeys');
|
|
26
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
checkTypeDefinitionHasType, checkElementTypeDefinitionHasType,
|
|
28
|
+
checkTypeIsScalar, checkDecimalScale,
|
|
29
|
+
} = require('./types');
|
|
27
30
|
const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc } = require('./elements');
|
|
28
31
|
const checkForInvalidTarget = require('./invalidTarget');
|
|
29
32
|
const { validateAssociationsInItems } = require('./arrayOfs');
|
|
30
33
|
const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
|
|
31
34
|
const checkExplicitlyNullableKeys = require('./nullableKeys');
|
|
32
35
|
const nonexpandableStructuredInExpression = require('./nonexpandableStructured');
|
|
36
|
+
const unknownMagic = require('./unknownMagic');
|
|
33
37
|
const managedWithoutKeys = require('./managedWithoutKeys');
|
|
34
38
|
|
|
35
39
|
const forHanaMemberValidators
|
|
@@ -37,6 +41,7 @@ const forHanaMemberValidators
|
|
|
37
41
|
// For HANA CDS specifically, reject any default parameter values, as these are not supported.
|
|
38
42
|
rejectParamDefaultsInHanaCds,
|
|
39
43
|
checkTypeIsScalar,
|
|
44
|
+
checkDecimalScale,
|
|
40
45
|
checkExplicitlyNullableKeys,
|
|
41
46
|
managedWithoutKeys,
|
|
42
47
|
warnAboutDefaultOnAssociationForHanaCds,
|
|
@@ -50,7 +55,7 @@ const forHanaArtifactValidators
|
|
|
50
55
|
checkForEmptyOrOnlyVirtual,
|
|
51
56
|
];
|
|
52
57
|
|
|
53
|
-
const forHanaCsnValidators = [ nonexpandableStructuredInExpression ];
|
|
58
|
+
const forHanaCsnValidators = [ nonexpandableStructuredInExpression, unknownMagic ];
|
|
54
59
|
/**
|
|
55
60
|
* @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
|
|
56
61
|
*/
|
|
@@ -98,6 +98,8 @@ function assertConsistency( model, stage ) {
|
|
|
98
98
|
'$blocks',
|
|
99
99
|
'$newfeatures',
|
|
100
100
|
'$messageFunctions',
|
|
101
|
+
'$functions',
|
|
102
|
+
'$volatileFunctions',
|
|
101
103
|
],
|
|
102
104
|
},
|
|
103
105
|
':parser': { // top-level from parser
|
|
@@ -246,7 +248,7 @@ function assertConsistency( model, stage ) {
|
|
|
246
248
|
optional: [
|
|
247
249
|
'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
|
|
248
250
|
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
|
|
249
|
-
'_projections', '_block', '_parent', '_main', '_effectiveType',
|
|
251
|
+
'_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
|
|
250
252
|
'$tableAliases', 'kind', '_$next', '_combined', '$inlines',
|
|
251
253
|
],
|
|
252
254
|
},
|
|
@@ -324,7 +326,7 @@ function assertConsistency( model, stage ) {
|
|
|
324
326
|
requires: [ 'location' ],
|
|
325
327
|
optional: [
|
|
326
328
|
'path', 'elements', '_outer',
|
|
327
|
-
'scope', '_artifact', '$inferred',
|
|
329
|
+
'scope', '_artifact', '$inferred', '$expand',
|
|
328
330
|
'_effectiveType', // by propagation
|
|
329
331
|
],
|
|
330
332
|
},
|
|
@@ -351,6 +353,7 @@ function assertConsistency( model, stage ) {
|
|
|
351
353
|
$delimited: { parser: true, test: isBoolean },
|
|
352
354
|
scope: { test: isScope },
|
|
353
355
|
func: { test: TODO },
|
|
356
|
+
suffix: { test: TODO },
|
|
354
357
|
kind: {
|
|
355
358
|
isRequired: !stageParser && (() => true),
|
|
356
359
|
// required to be set by Core Compiler even with parse errors
|
|
@@ -398,6 +401,7 @@ function assertConsistency( model, stage ) {
|
|
|
398
401
|
optional: [
|
|
399
402
|
'args',
|
|
400
403
|
'func',
|
|
404
|
+
'suffix',
|
|
401
405
|
'quantifier',
|
|
402
406
|
'$inferred',
|
|
403
407
|
'$parens',
|
|
@@ -426,7 +430,7 @@ function assertConsistency( model, stage ) {
|
|
|
426
430
|
struct: { inherits: 'val', test: isDictionary( definition ) }, // def because double @
|
|
427
431
|
args: {
|
|
428
432
|
inherits: 'value',
|
|
429
|
-
optional: [ 'name', '$duplicate', '$expected' ],
|
|
433
|
+
optional: [ 'name', '$duplicate', '$expected', 'args', 'suffix' ],
|
|
430
434
|
test: args,
|
|
431
435
|
},
|
|
432
436
|
on: { kind: true, inherits: 'value', test: expression },
|
|
@@ -477,6 +481,7 @@ function assertConsistency( model, stage ) {
|
|
|
477
481
|
},
|
|
478
482
|
items: {
|
|
479
483
|
kind: true,
|
|
484
|
+
also: [ 0 ], // 0 for cyclic expansions
|
|
480
485
|
requires: [ 'location' ],
|
|
481
486
|
optional: [
|
|
482
487
|
'enum',
|
|
@@ -533,7 +538,7 @@ function assertConsistency( model, stage ) {
|
|
|
533
538
|
],
|
|
534
539
|
optional: [
|
|
535
540
|
'_effectiveType', '$parens',
|
|
536
|
-
'_deps',
|
|
541
|
+
'_deps', '$expand',
|
|
537
542
|
// query specific
|
|
538
543
|
'where', 'columns', 'mixin', 'quantifier', 'offset',
|
|
539
544
|
'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
|
|
@@ -574,7 +579,11 @@ function assertConsistency( model, stage ) {
|
|
|
574
579
|
$duplicates: { parser: true, kind: true, test: TODO }, // array of arts or true
|
|
575
580
|
$extension: { kind: true, test: TODO }, // TODO: introduce $applied instead or $status
|
|
576
581
|
$inferred: { parser: true, kind: true, test: isString },
|
|
577
|
-
|
|
582
|
+
|
|
583
|
+
// Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
|
|
584
|
+
// client, universal: render expanded elements? gensrc: produce annotate statements?
|
|
585
|
+
$expand: { kind: true, test: isString }, // TODO: rename it to $elementsExpand ?
|
|
586
|
+
|
|
578
587
|
$autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
|
|
579
588
|
$a2j: { kind: true, enumerable: true, test: TODO },
|
|
580
589
|
$extra: { parser: true, test: TODO }, // for unexpected properties in CSN
|
|
@@ -582,6 +591,8 @@ function assertConsistency( model, stage ) {
|
|
|
582
591
|
$sources: { parser: true, test: isArray( isString ) },
|
|
583
592
|
$expected: { parser: true, test: isString },
|
|
584
593
|
$messageFunctions: { test: TODO },
|
|
594
|
+
$functions: { test: TODO },
|
|
595
|
+
$volatileFunctions: { test: TODO },
|
|
585
596
|
};
|
|
586
597
|
let _noSyntaxErrors = null;
|
|
587
598
|
assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
|
|
@@ -653,6 +664,8 @@ function assertConsistency( model, stage ) {
|
|
|
653
664
|
}
|
|
654
665
|
|
|
655
666
|
function standard( node, parent, prop, spec, name ) {
|
|
667
|
+
if (spec.also && spec.also.includes( node ))
|
|
668
|
+
return;
|
|
656
669
|
isObject( node, parent, prop, spec, name );
|
|
657
670
|
|
|
658
671
|
const names = Object.getOwnPropertyNames( node );
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Base Definitions for the Core Compiler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const dictKinds = {
|
|
7
|
+
definitions: 'absolute',
|
|
8
|
+
elements: 'element',
|
|
9
|
+
enum: 'enum',
|
|
10
|
+
foreignKeys: 'key',
|
|
11
|
+
actions: 'action',
|
|
12
|
+
params: 'param',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const kindProperties = {
|
|
16
|
+
// TODO: also foreignKeys ?
|
|
17
|
+
namespace: { artifacts: true }, // on-the-fly context
|
|
18
|
+
context: { artifacts: true, normalized: 'namespace' },
|
|
19
|
+
service: { artifacts: true, normalized: 'namespace' },
|
|
20
|
+
entity: { elements: true, actions: true, params: () => false },
|
|
21
|
+
select: { normalized: 'select', elements: true },
|
|
22
|
+
$join: { normalized: 'select' },
|
|
23
|
+
$tableAlias: { normalized: 'alias' }, // table alias in select
|
|
24
|
+
$self: { normalized: 'alias' }, // table alias in select
|
|
25
|
+
$navElement: { normalized: 'element' },
|
|
26
|
+
$inline: { normalized: 'element' }, // column with inline property
|
|
27
|
+
event: { elements: true },
|
|
28
|
+
type: { elements: propExists, enum: propExists },
|
|
29
|
+
aspect: { elements: propExists },
|
|
30
|
+
annotation: { elements: propExists, enum: propExists },
|
|
31
|
+
enum: { normalized: 'element' },
|
|
32
|
+
element: { elements: propExists, enum: propExists, dict: 'elements' },
|
|
33
|
+
mixin: { normalized: 'alias' },
|
|
34
|
+
action: {
|
|
35
|
+
params: () => false, elements: () => false, enum: () => false, dict: 'actions',
|
|
36
|
+
}, // no extend params, only annotate
|
|
37
|
+
function: {
|
|
38
|
+
params: () => false, elements: () => false, enum: () => false, normalized: 'action',
|
|
39
|
+
}, // no extend params, only annotate
|
|
40
|
+
key: { normalized: 'element' },
|
|
41
|
+
param: { elements: () => false, enum: () => false, dict: 'params' },
|
|
42
|
+
source: { artifacts: true }, // TODO -> $source
|
|
43
|
+
using: {},
|
|
44
|
+
extend: {
|
|
45
|
+
isExtension: true,
|
|
46
|
+
noDep: 'special',
|
|
47
|
+
elements: true, /* only for parse-cdl */
|
|
48
|
+
actions: true, /* only for parse-cdl */
|
|
49
|
+
},
|
|
50
|
+
annotate: {
|
|
51
|
+
isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
|
|
52
|
+
},
|
|
53
|
+
builtin: {}, // = CURRENT_DATE, TODO: improve
|
|
54
|
+
$parameters: {}, // $parameters in query entities
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function propExists( prop, parent ) {
|
|
58
|
+
const obj = parent.returns || parent;
|
|
59
|
+
return (obj.items || obj.targetAspect || obj)[prop];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
dictKinds,
|
|
64
|
+
kindProperties,
|
|
65
|
+
};
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { forEachInDict } = require('../base/dictionaries');
|
|
6
6
|
const { builtinLocation } = require('../base/location');
|
|
7
|
-
const { setProp } = require('
|
|
7
|
+
const { setProp } = require('./utils');
|
|
8
8
|
|
|
9
9
|
const core = {
|
|
10
10
|
String: { parameters: [ 'length' ], category: 'string' },
|
|
@@ -83,6 +83,8 @@ const specialFunctions = {
|
|
|
83
83
|
const magicVariables = {
|
|
84
84
|
$user: {
|
|
85
85
|
elements: { id: {}, locale: {} },
|
|
86
|
+
// Allow $user.<any>
|
|
87
|
+
$uncheckedElements: true,
|
|
86
88
|
// Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
|
|
87
89
|
$autoElement: 'id',
|
|
88
90
|
}, // CDS-specific, not part of SQL
|
|
@@ -165,6 +167,31 @@ function isRelationTypeName(typeName) {
|
|
|
165
167
|
return typeCategories.relation.includes(typeName);
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Checks whether the given absolute path is inside a reserved namespace.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} absolute
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isInReservedNamespace(absolute) {
|
|
177
|
+
return absolute.startsWith( 'cds.') &&
|
|
178
|
+
!absolute.match(/^cds\.foundation(\.|$)/) &&
|
|
179
|
+
!absolute.match(/^cds\.outbox(\.|$)/); // Requested by Node runtime
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Tell if a type is (directly) a builtin type
|
|
184
|
+
* Note that in CSN builtins are not in the definition of the model, so we can only
|
|
185
|
+
* check against their absolute names. Builtin types are "cds.<something>", i.e. they
|
|
186
|
+
* are directly in 'cds', but not for example in 'cds.foundation'.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} type
|
|
189
|
+
* @returns {boolean}
|
|
190
|
+
*/
|
|
191
|
+
function isBuiltinType(type) {
|
|
192
|
+
return typeof type === 'string' && isInReservedNamespace(type);
|
|
193
|
+
}
|
|
194
|
+
|
|
168
195
|
/**
|
|
169
196
|
* Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
|
|
170
197
|
* `definitions` of the XSN model as well as to `$builtins`.
|
|
@@ -267,6 +294,8 @@ module.exports = {
|
|
|
267
294
|
functionsWithoutParens,
|
|
268
295
|
specialFunctions,
|
|
269
296
|
initBuiltins,
|
|
297
|
+
isInReservedNamespace,
|
|
298
|
+
isBuiltinType,
|
|
270
299
|
isIntegerTypeName,
|
|
271
300
|
isDecimalTypeName,
|
|
272
301
|
isNumericTypeName,
|
package/lib/compiler/checks.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
// const { hasArtifactTypeInformation } = require('../model/csnUtils')
|
|
17
17
|
const builtins = require('../compiler/builtins');
|
|
18
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
forEachGeneric, forEachDefinition, forEachMember,
|
|
20
|
+
} = require('../base/model');
|
|
19
21
|
|
|
20
22
|
function check( model ) { // = XSN
|
|
21
23
|
const {
|
|
@@ -630,8 +632,9 @@ function check( model ) { // = XSN
|
|
|
630
632
|
* @returns {void}
|
|
631
633
|
*/
|
|
632
634
|
function checkTokenStreamExpression(xpr, allowAssocTail) {
|
|
635
|
+
const args = Array.isArray(xpr.args) ? xpr.args : Object.values(xpr.args || {});
|
|
633
636
|
// Check for illegal argument usage within the expression
|
|
634
|
-
for (const arg of
|
|
637
|
+
for (const arg of args) {
|
|
635
638
|
if (isVirtualElement(arg))
|
|
636
639
|
error(null, arg.location, 'Virtual elements can\'t be used in an expression');
|
|
637
640
|
|