@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
package/lib/compiler/definer.js
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
// Sub Phase 2 (initXYZ)
|
|
89
89
|
// - set _parent, _main (later: _service?) links, and _block links of members
|
|
90
90
|
// - add _subArtifacts dictionary and "namespace artifacts" for name resolution
|
|
91
|
-
// - duplicate checks
|
|
91
|
+
// - duplicate checks
|
|
92
92
|
// - structure checks ?
|
|
93
93
|
// - annotation assignments
|
|
94
94
|
// - POST: resolvePath() can be called for artifact references (if complete model)
|
|
@@ -101,9 +101,11 @@
|
|
|
101
101
|
// to avoid consequential or repeated errors.
|
|
102
102
|
// - But: The same artifact is added to multiple dictionaries.
|
|
103
103
|
// - Solution part 1: $duplicates as property of the artifact or member
|
|
104
|
-
// for
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
|
|
105
|
+
// dictionary of the whole model, `$tableAliases` dictionary of queries.
|
|
106
|
+
// - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
|
|
107
|
+
// dictionary, `_combined` dictionary for query search, `$tableAliases`
|
|
108
|
+
// of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
|
|
107
109
|
|
|
108
110
|
'use strict';
|
|
109
111
|
|
|
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
|
|
|
111
113
|
const {
|
|
112
114
|
isDeprecatedEnabled, isBetaEnabled,
|
|
113
115
|
setProp, forEachGeneric, forEachInOrder,
|
|
114
|
-
|
|
116
|
+
forEachDefinition,
|
|
115
117
|
forEachMemberRecursivelyWithQuery,
|
|
116
118
|
} = require('../base/model');
|
|
117
119
|
const {
|
|
@@ -120,20 +122,20 @@ const {
|
|
|
120
122
|
const {
|
|
121
123
|
dictLocation,
|
|
122
124
|
} = require('../base/location');
|
|
125
|
+
const { kindProperties, dictKinds } = require('./base');
|
|
123
126
|
const {
|
|
124
|
-
annotationVal,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
dictKinds,
|
|
128
|
-
kindProperties,
|
|
129
|
-
fns,
|
|
127
|
+
annotationVal,
|
|
128
|
+
annotationIsFalse,
|
|
129
|
+
annotateWith,
|
|
130
130
|
linkToOrigin,
|
|
131
131
|
setMemberParent,
|
|
132
132
|
storeExtension,
|
|
133
133
|
dependsOnSilent,
|
|
134
|
-
|
|
134
|
+
augmentPath,
|
|
135
|
+
splitIntoPath,
|
|
136
|
+
} = require('./utils');
|
|
135
137
|
const { compareLayer, layer } = require('./moduleLayers');
|
|
136
|
-
const { initBuiltins } = require('./builtins');
|
|
138
|
+
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
137
139
|
const setLink = setProp;
|
|
138
140
|
|
|
139
141
|
/**
|
|
@@ -148,7 +150,7 @@ const setLink = setProp;
|
|
|
148
150
|
*
|
|
149
151
|
* @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
|
|
150
152
|
*/
|
|
151
|
-
function
|
|
153
|
+
function define( model ) {
|
|
152
154
|
const { options } = model;
|
|
153
155
|
// Get simplified "resolve" functionality and the message function:
|
|
154
156
|
const {
|
|
@@ -160,24 +162,29 @@ function getDefinerFunctions( model ) {
|
|
|
160
162
|
resolveTypeArguments,
|
|
161
163
|
defineAnnotations,
|
|
162
164
|
attachAndEmitValidNames,
|
|
163
|
-
} =
|
|
164
|
-
|
|
165
|
-
let addTextsLanguageAssoc = false;
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
define,
|
|
165
|
+
} = model.$functions;
|
|
166
|
+
Object.assign( model.$functions, {
|
|
169
167
|
initArtifact,
|
|
170
168
|
lateExtensions,
|
|
171
169
|
projectionAncestor,
|
|
172
170
|
hasTruthyProp,
|
|
171
|
+
} );
|
|
172
|
+
// During the definer, we can only resolve artifact references, i.e,
|
|
173
|
+
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
174
|
+
model.$volatileFunctions.environment = function artifactsEnv( art ) {
|
|
175
|
+
return art._subArtifacts || Object.create(null);
|
|
173
176
|
};
|
|
174
177
|
|
|
178
|
+
const extensionsDict = Object.create(null);
|
|
179
|
+
let addTextsLanguageAssoc = false;
|
|
180
|
+
return doDefine();
|
|
181
|
+
|
|
175
182
|
/**
|
|
176
183
|
* Main function of the definer.
|
|
177
184
|
*
|
|
178
185
|
* @returns {XSN.Model}
|
|
179
186
|
*/
|
|
180
|
-
function
|
|
187
|
+
function doDefine() {
|
|
181
188
|
if (options.deprecated &&
|
|
182
189
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
183
190
|
warning( 'api-deprecated-option', {},
|
|
@@ -206,8 +213,6 @@ function getDefinerFunctions( model ) {
|
|
|
206
213
|
|
|
207
214
|
if (options.parseCdl) {
|
|
208
215
|
initExtensionsWithoutApplying();
|
|
209
|
-
// Check for redefinitions
|
|
210
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
211
216
|
// If no extensions shall be applied then we can skip further
|
|
212
217
|
// artifact processing and return the model with an `extensions` property.
|
|
213
218
|
return model;
|
|
@@ -215,7 +220,6 @@ function getDefinerFunctions( model ) {
|
|
|
215
220
|
|
|
216
221
|
applyExtensions();
|
|
217
222
|
|
|
218
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
219
223
|
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
220
224
|
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
221
225
|
model.definitions['sap.common.Languages'];
|
|
@@ -238,13 +242,13 @@ function getDefinerFunctions( model ) {
|
|
|
238
242
|
* @param {XSN.AST} src
|
|
239
243
|
*/
|
|
240
244
|
function addSource( src ) {
|
|
241
|
-
// handle sub model from
|
|
245
|
+
// handle sub model from parser
|
|
242
246
|
if (!src.kind)
|
|
243
247
|
src.kind = 'source';
|
|
244
248
|
|
|
245
249
|
let namespace = src.namespace && src.namespace.path;
|
|
246
250
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
247
|
-
if (
|
|
251
|
+
if (isInReservedNamespace(prefix)) {
|
|
248
252
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
249
253
|
// TODO: use $(NAME)
|
|
250
254
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -280,8 +284,7 @@ function getDefinerFunctions( model ) {
|
|
|
280
284
|
function addDefinition( art, block ) {
|
|
281
285
|
const { absolute } = art.name;
|
|
282
286
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
283
|
-
if (absolute === 'cds' ||
|
|
284
|
-
absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
|
|
287
|
+
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
285
288
|
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
286
289
|
// TODO: use $(NAME)
|
|
287
290
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -305,7 +308,7 @@ function getDefinerFunctions( model ) {
|
|
|
305
308
|
}
|
|
306
309
|
else {
|
|
307
310
|
setLink( art, '_block', block );
|
|
308
|
-
// dictAdd might set $duplicates to true
|
|
311
|
+
// dictAdd might set $duplicates to true
|
|
309
312
|
dictAdd( model.definitions, absolute, art );
|
|
310
313
|
return true;
|
|
311
314
|
}
|
|
@@ -444,6 +447,23 @@ function getDefinerFunctions( model ) {
|
|
|
444
447
|
|
|
445
448
|
// Phase 2 ("init") --------------------------------------------------------
|
|
446
449
|
|
|
450
|
+
function checkRedefinition( art ) {
|
|
451
|
+
if (!art.$duplicates)
|
|
452
|
+
return;
|
|
453
|
+
if (art._main) {
|
|
454
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
455
|
+
name: art.name.id,
|
|
456
|
+
'#': kindProperties[art.kind].normalized || art.kind,
|
|
457
|
+
} );
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
461
|
+
name: art.name.absolute,
|
|
462
|
+
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
463
|
+
} );
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
447
467
|
function initNamespaceAndUsing( src ) {
|
|
448
468
|
if (src.namespace) {
|
|
449
469
|
const decl = src.namespace;
|
|
@@ -488,6 +508,7 @@ function getDefinerFunctions( model ) {
|
|
|
488
508
|
if (!reInit)
|
|
489
509
|
initParentLink( art, model.definitions );
|
|
490
510
|
const block = art._block;
|
|
511
|
+
checkRedefinition( art );
|
|
491
512
|
defineAnnotations( art, art, block );
|
|
492
513
|
initMembers( art, art, block );
|
|
493
514
|
initDollarSelf( art ); // $self
|
|
@@ -513,6 +534,7 @@ function getDefinerFunctions( model ) {
|
|
|
513
534
|
|
|
514
535
|
function initVocabulary( art ) {
|
|
515
536
|
initParentLink( art, model.vocabularies );
|
|
537
|
+
checkRedefinition( art );
|
|
516
538
|
const block = art._block;
|
|
517
539
|
defineAnnotations( art, art, block );
|
|
518
540
|
initMembers( art, art, block );
|
|
@@ -554,20 +576,6 @@ function getDefinerFunctions( model ) {
|
|
|
554
576
|
|
|
555
577
|
// From here til EOF, reexamine code ---------------------------------------
|
|
556
578
|
|
|
557
|
-
// currently called from preProcessArtifact(), do be called in "init"
|
|
558
|
-
function checkRedefinitions( obj, name, prop ) {
|
|
559
|
-
forEachMember( obj, checkRedefinitions, obj.targetAspect );
|
|
560
|
-
if (!obj.$duplicates)
|
|
561
|
-
return;
|
|
562
|
-
if (obj.name.location.file === '<built-in>') {
|
|
563
|
-
// builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
|
|
564
|
-
// The error shall only be printed for the user-defined conflicting artifact.
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
error( 'duplicate-definition', [ obj.name.location, obj ],
|
|
568
|
-
{ name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
|
|
569
|
-
}
|
|
570
|
-
|
|
571
579
|
function initDollarSelf( art ) {
|
|
572
580
|
const selfname = '$self';
|
|
573
581
|
// TODO: use setMemberParent() ?
|
|
@@ -774,12 +782,13 @@ function getDefinerFunctions( model ) {
|
|
|
774
782
|
setMemberParent( table, table.name.id, query );
|
|
775
783
|
setProp( table, '_block', query._block );
|
|
776
784
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
777
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': '
|
|
785
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
778
786
|
} );
|
|
779
787
|
// also add to JOIN nodes for name restrictions:
|
|
780
788
|
for (const p of joinParents) {
|
|
781
|
-
//
|
|
782
|
-
|
|
789
|
+
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
790
|
+
// already used for duplicate aliases of queries:
|
|
791
|
+
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
783
792
|
}
|
|
784
793
|
if (table.name.id[0] === '$') {
|
|
785
794
|
warning( 'syntax-dollar-ident', [ table.name.location, table ], {
|
|
@@ -866,7 +875,7 @@ function getDefinerFunctions( model ) {
|
|
|
866
875
|
// assignments on the mixin... (also for future mixin definitions
|
|
867
876
|
// with generated values)
|
|
868
877
|
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
869
|
-
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '
|
|
878
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
870
879
|
} );
|
|
871
880
|
if (mixin.name.id[0] === '$') {
|
|
872
881
|
warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
|
|
@@ -993,6 +1002,7 @@ function getDefinerFunctions( model ) {
|
|
|
993
1002
|
setProp( elem, '_block', bl );
|
|
994
1003
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
995
1004
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1005
|
+
checkRedefinition( elem );
|
|
996
1006
|
defineAnnotations( elem, elem, bl );
|
|
997
1007
|
initMembers( elem, elem, bl, initExtensions );
|
|
998
1008
|
|
|
@@ -1958,24 +1968,6 @@ function getDefinerFunctions( model ) {
|
|
|
1958
1968
|
}
|
|
1959
1969
|
}
|
|
1960
1970
|
|
|
1961
|
-
// TODO: move to "init" phase
|
|
1962
|
-
/**
|
|
1963
|
-
* Check whether redefinitions of the given artifact name exist and
|
|
1964
|
-
* adapt to `targetAspect`.
|
|
1965
|
-
*
|
|
1966
|
-
* @param {string} name
|
|
1967
|
-
*/
|
|
1968
|
-
function preProcessArtifact( name ) {
|
|
1969
|
-
const art = model.definitions[name];
|
|
1970
|
-
if (Array.isArray(art.$duplicates)) {
|
|
1971
|
-
// A definition name containing a `.` is not invalid (TODO: starting or
|
|
1972
|
-
// ending with a dot is invalid and could be checked here)
|
|
1973
|
-
for (const a of art.$duplicates)
|
|
1974
|
-
checkRedefinitions( a, name, 'definitions' );
|
|
1975
|
-
}
|
|
1976
|
-
checkRedefinitions( art, name, 'definitions' );
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
1971
|
/**
|
|
1980
1972
|
* Process "composition of" artifacts.
|
|
1981
1973
|
*
|
|
@@ -2326,26 +2318,6 @@ function pathName(path) {
|
|
|
2326
2318
|
return path.map( id => id.id ).join('.');
|
|
2327
2319
|
}
|
|
2328
2320
|
|
|
2329
|
-
/**
|
|
2330
|
-
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
2331
|
-
* Each segment will have the given location assigned.
|
|
2332
|
-
*
|
|
2333
|
-
* @param {CSN.Location} location
|
|
2334
|
-
* @param {string} name
|
|
2335
|
-
* @returns {XSN.Path}
|
|
2336
|
-
*/
|
|
2337
|
-
function splitIntoPath( location, name ) {
|
|
2338
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
/**
|
|
2342
|
-
* @param {CSN.Location} location
|
|
2343
|
-
* @param {...any} args
|
|
2344
|
-
*/
|
|
2345
|
-
function augmentPath( location, ...args ) {
|
|
2346
|
-
return { path: args.map( id => ({ id, location }) ), location };
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
2321
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
2350
2322
|
const args = relations.map( eq );
|
|
2351
2323
|
return (args.length === 1)
|
|
@@ -2374,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
|
2374
2346
|
// these function could be used to a future lib/compiler/utils.js, but DO NOT
|
|
2375
2347
|
// SHARE with utility functions for CSN processors
|
|
2376
2348
|
|
|
2377
|
-
module.exports = {
|
|
2378
|
-
define: model => getDefinerFunctions( model ).define(),
|
|
2379
|
-
getDefinerFunctions,
|
|
2380
|
-
augmentPath,
|
|
2381
|
-
splitIntoPath,
|
|
2382
|
-
};
|
|
2349
|
+
module.exports = { define };
|
package/lib/compiler/index.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
// Main XSN-based compiler functions
|
|
2
2
|
|
|
3
|
+
// ...
|
|
4
|
+
|
|
5
|
+
// How functions are shared across the Core Compiler sub modules:
|
|
6
|
+
|
|
7
|
+
// - Shared XSN-related functions which do not use a context are in utils.js,
|
|
8
|
+
// they are `require`d as usual at the beginning of sub modules.
|
|
9
|
+
// - The XSN is the only context which context-dependent functions can depend on.
|
|
10
|
+
// - Sharing such a function is by adding it to `‹xsn›.$functions`,
|
|
11
|
+
// e.g. `resolvePath` and similar will be attached to the XSN.
|
|
12
|
+
// - Functions which might be overwritten in a next sub module
|
|
13
|
+
// are added to `‹xsn›.$volatileFunctions`, currently just `environment`.
|
|
14
|
+
|
|
3
15
|
'use strict';
|
|
4
16
|
|
|
5
17
|
const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
|
|
@@ -8,6 +20,7 @@ const parseCsn = require('../json/from-csn');
|
|
|
8
20
|
|
|
9
21
|
const assertConsistency = require('./assert-consistency');
|
|
10
22
|
const moduleLayers = require('./moduleLayers');
|
|
23
|
+
const { fns } = require('./shared');
|
|
11
24
|
const { define } = require('./definer');
|
|
12
25
|
const resolve = require('./resolver');
|
|
13
26
|
const propagator = require('./propagator');
|
|
@@ -110,7 +123,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
110
123
|
// fileCache = Object.assign( Object.create(null), fileCache );
|
|
111
124
|
dir = path.resolve(dir);
|
|
112
125
|
const a = processFilenames( filenames, dir );
|
|
113
|
-
a.fileContentDict = Object.create(null);
|
|
114
126
|
|
|
115
127
|
const model = { sources: a.sources, options };
|
|
116
128
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -149,7 +161,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
149
161
|
}
|
|
150
162
|
else {
|
|
151
163
|
try {
|
|
152
|
-
a.fileContentDict[filename] = source;
|
|
153
164
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
154
165
|
a.sources[filename] = ast;
|
|
155
166
|
ast.location = { file: rel };
|
|
@@ -220,7 +231,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
220
231
|
// absolute file names - they start with `/` or `\` or similar
|
|
221
232
|
dir = path.resolve(dir);
|
|
222
233
|
const a = processFilenames( filenames, dir );
|
|
223
|
-
a.fileContentDict = Object.create(null);
|
|
224
234
|
|
|
225
235
|
const model = { sources: a.sources, options };
|
|
226
236
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -281,7 +291,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
281
291
|
}
|
|
282
292
|
else {
|
|
283
293
|
try {
|
|
284
|
-
a.fileContentDict[filename] = source;
|
|
285
294
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
286
295
|
a.sources[filename] = ast;
|
|
287
296
|
ast.location = { file: rel };
|
|
@@ -416,6 +425,9 @@ function compileDoX( model ) {
|
|
|
416
425
|
throwWithError();
|
|
417
426
|
return model;
|
|
418
427
|
}
|
|
428
|
+
model.$functions = {};
|
|
429
|
+
model.$volatileFunctions = {};
|
|
430
|
+
fns( model ); // attach (mostly) paths functions
|
|
419
431
|
define( model );
|
|
420
432
|
// do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
|
|
421
433
|
// TODO: do not use this function for parseCdl anyway…
|
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
setProp,
|
|
9
9
|
isDeprecatedEnabled,
|
|
10
10
|
} = require( '../base/model');
|
|
11
|
-
const { linkToOrigin, withAssociation } = require('./
|
|
11
|
+
const { linkToOrigin, withAssociation } = require('./utils');
|
|
12
12
|
// const { refString } = require( '../base/messages')
|
|
13
13
|
|
|
14
14
|
function propagate( model ) {
|
|
@@ -179,6 +179,8 @@ function propagate( model ) {
|
|
|
179
179
|
// accessed at their type being a main artifact
|
|
180
180
|
function expensive( prop, target, source ) {
|
|
181
181
|
// console.log(prop,source.name,'->',target.kind,target.name);
|
|
182
|
+
if (source.kind === 'builtin')
|
|
183
|
+
return;
|
|
182
184
|
if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
|
|
183
185
|
// foreignKeys must always be copied with target to avoid any confusion
|
|
184
186
|
// whether we have to generated implicit keys
|
|
@@ -211,7 +213,8 @@ function propagate( model ) {
|
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
function onlyViaParent( prop, target, source ) {
|
|
214
|
-
if (target.$inferred === 'proxy'
|
|
216
|
+
if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
|
|
217
|
+
// assocs and enums do not have 'include'
|
|
215
218
|
always( prop, target, source );
|
|
216
219
|
}
|
|
217
220
|
|
package/lib/compiler/resolver.js
CHANGED
|
@@ -47,25 +47,28 @@ const {
|
|
|
47
47
|
const { dictLocation } = require('../base/location');
|
|
48
48
|
const { searchName, weakLocation } = require('../base/messages');
|
|
49
49
|
const { combinedLocation } = require('../base/location');
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
const { kindProperties } = require('./base');
|
|
51
52
|
const {
|
|
52
|
-
|
|
53
|
+
pushLink,
|
|
54
|
+
setLink,
|
|
53
55
|
augmentPath,
|
|
54
56
|
splitIntoPath,
|
|
55
|
-
|
|
57
|
+
linkToOrigin,
|
|
58
|
+
setMemberParent,
|
|
59
|
+
withAssociation,
|
|
60
|
+
storeExtension,
|
|
61
|
+
dependsOn,
|
|
62
|
+
dependsOnSilent,
|
|
63
|
+
} = require('./utils');
|
|
56
64
|
|
|
57
65
|
const detectCycles = require('./cycle-detector');
|
|
58
66
|
const layers = require('./moduleLayers');
|
|
59
67
|
|
|
60
|
-
const {
|
|
61
|
-
kindProperties, fns, setLink, linkToOrigin, setMemberParent, withAssociation, storeExtension,
|
|
62
|
-
dependsOn, dependsOnSilent,
|
|
63
|
-
} = require('./shared');
|
|
64
|
-
|
|
65
68
|
const annotationPriorities = {
|
|
66
69
|
define: 1, extend: 2, annotate: 2, edmx: 3,
|
|
67
70
|
};
|
|
68
|
-
|
|
71
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
69
72
|
|
|
70
73
|
// Export function of this file. Resolve type references in augmented CSN
|
|
71
74
|
// `model`. If the model has a property argument `messages`, do not throw
|
|
@@ -73,21 +76,21 @@ const annotationPriorities = {
|
|
|
73
76
|
// that property (should be a vector).
|
|
74
77
|
function resolve( model ) {
|
|
75
78
|
const { options } = model;
|
|
76
|
-
// Get shared
|
|
79
|
+
// Get shared functionality and the message function:
|
|
80
|
+
const {
|
|
81
|
+
info, warning, error, message,
|
|
82
|
+
} = model.$messageFunctions;
|
|
77
83
|
const {
|
|
78
84
|
resolvePath,
|
|
79
85
|
resolveTypeArguments,
|
|
80
86
|
defineAnnotations,
|
|
81
87
|
attachAndEmitValidNames,
|
|
82
|
-
} = fns( model, environment );
|
|
83
|
-
const {
|
|
84
|
-
info, warning, error, message,
|
|
85
|
-
} = model.$messageFunctions;
|
|
86
|
-
const {
|
|
87
88
|
initArtifact,
|
|
88
89
|
lateExtensions,
|
|
89
90
|
projectionAncestor,
|
|
90
|
-
} =
|
|
91
|
+
} = model.$functions;
|
|
92
|
+
model.$volatileFunctions.environment = environment;
|
|
93
|
+
|
|
91
94
|
/** @type {any} may also be a boolean */
|
|
92
95
|
let newAutoExposed = [];
|
|
93
96
|
|
|
@@ -242,6 +245,7 @@ function resolve( model ) {
|
|
|
242
245
|
const chain = [];
|
|
243
246
|
while (art && !('_effectiveType' in art) &&
|
|
244
247
|
(art.type || art._origin || art.value && art.value.path) &&
|
|
248
|
+
// TODO: really stop at art.enum?
|
|
245
249
|
!art.target && !art.enum && !art.elements && !art.items) {
|
|
246
250
|
chain.push( art );
|
|
247
251
|
setProp( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
@@ -273,13 +277,17 @@ function resolve( model ) {
|
|
|
273
277
|
// collect the "latest" cardinality (calculate lazyly if necessary)
|
|
274
278
|
let cardinality = art.cardinality ||
|
|
275
279
|
art._effectiveType && (() => getCardinality( art._effectiveType ));
|
|
280
|
+
let prev = art;
|
|
276
281
|
for (const a of chain) {
|
|
277
282
|
if (a.cardinality)
|
|
278
283
|
cardinality = a.cardinality;
|
|
279
284
|
if (a.expand && expandFromColumns( a, art, cardinality ) ||
|
|
280
285
|
art.target && redirectImplicitly( a, art ) ||
|
|
281
|
-
art.elements && expandElements( a, art, eType )
|
|
286
|
+
art.elements && expandElements( a, art, eType ) ||
|
|
287
|
+
art.items && expandItems( a, art, eType ))
|
|
282
288
|
art = a;
|
|
289
|
+
else if (art.enum && expandEnum( a, prev ))
|
|
290
|
+
prev = a; // do not set art - effective type is base
|
|
283
291
|
setProp( a, '_effectiveType', art );
|
|
284
292
|
}
|
|
285
293
|
}
|
|
@@ -405,7 +413,7 @@ function resolve( model ) {
|
|
|
405
413
|
for (const view of resolveChain.reverse()) {
|
|
406
414
|
if (view._status !== '_query' ) { // not already resolved
|
|
407
415
|
setProp( view, '_status', '_query' );
|
|
408
|
-
traverseQueryPost( view.query,
|
|
416
|
+
traverseQueryPost( view.query, null, populateQuery );
|
|
409
417
|
if (view.elements$) // specified elements
|
|
410
418
|
mergeSpecifiedElements( view );
|
|
411
419
|
if (!view.$entity) {
|
|
@@ -494,7 +502,8 @@ function resolve( model ) {
|
|
|
494
502
|
});
|
|
495
503
|
}
|
|
496
504
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
497
|
-
|
|
505
|
+
if (elem.$duplicates !== true)
|
|
506
|
+
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
498
507
|
});
|
|
499
508
|
}
|
|
500
509
|
}
|
|
@@ -535,6 +544,23 @@ function resolve( model ) {
|
|
|
535
544
|
setMemberParent( key, name, elem ); // TODO: set _block here if not present?
|
|
536
545
|
}
|
|
537
546
|
|
|
547
|
+
function expandItems( art, origin, eType ) {
|
|
548
|
+
if (!enableExpandElements || art.items)
|
|
549
|
+
return false;
|
|
550
|
+
if (isInParents( art, eType )) {
|
|
551
|
+
art.items = 0; // circular
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
const ref = art.type || art.value || art.name;
|
|
555
|
+
const location = ref && ref.location || art.location;
|
|
556
|
+
art.items = { $inferred: 'expand-element', location };
|
|
557
|
+
setProp( art.items, '_outer', art );
|
|
558
|
+
setProp( art.items, '_origin', origin.items );
|
|
559
|
+
if (!art.$expand)
|
|
560
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
|
|
538
564
|
function expandElements( art, struct, eType ) {
|
|
539
565
|
if (!enableExpandElements)
|
|
540
566
|
return false;
|
|
@@ -566,7 +592,28 @@ function resolve( model ) {
|
|
|
566
592
|
// member property):
|
|
567
593
|
if (!art.$expand)
|
|
568
594
|
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
569
|
-
// TODO: have some art.elements[SYM.$inferred] = 'expand-
|
|
595
|
+
// TODO: have some art.elements[SYM.$inferred] = 'expand-element';
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function expandEnum( art, origin ) {
|
|
600
|
+
if (!enableExpandElements || art.enum)
|
|
601
|
+
return false;
|
|
602
|
+
const ref = art.type || art.value || art.name;
|
|
603
|
+
const location = weakLocation( ref && ref.location || art.location );
|
|
604
|
+
art.enum = Object.create(null);
|
|
605
|
+
for (const name in origin.enum) {
|
|
606
|
+
const orig = origin.enum[name];
|
|
607
|
+
linkToOrigin( orig, name, art, 'enum', location, true )
|
|
608
|
+
// or should we use orig.location? - TODO: try to find test to see message
|
|
609
|
+
.$inferred = 'expand-element';
|
|
610
|
+
}
|
|
611
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
612
|
+
// elements expansion will take place on artifact with existing other
|
|
613
|
+
// member property):
|
|
614
|
+
if (!art.$expand)
|
|
615
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
616
|
+
art.enum[$inferred] = 'expand-element';
|
|
570
617
|
return true;
|
|
571
618
|
}
|
|
572
619
|
|
|
@@ -604,7 +651,7 @@ function resolve( model ) {
|
|
|
604
651
|
// not counting propagated ones; set up to the definition (main artifact)
|
|
605
652
|
// (only set with anno on $inferred elem)
|
|
606
653
|
// Usage according to CSN flavor:
|
|
607
|
-
// - gensrc: do not render
|
|
654
|
+
// - gensrc: do not render inferred elements (including expanded elements),
|
|
608
655
|
// collect annotate statements with value 'annotate'
|
|
609
656
|
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
610
657
|
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
@@ -1441,8 +1488,9 @@ function resolve( model ) {
|
|
|
1441
1488
|
}
|
|
1442
1489
|
// Resolve projections/views
|
|
1443
1490
|
// if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
|
|
1444
|
-
|
|
1445
|
-
|
|
1491
|
+
|
|
1492
|
+
if (art.$queries)
|
|
1493
|
+
art.$queries.forEach( resolveQuery );
|
|
1446
1494
|
|
|
1447
1495
|
if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
|
|
1448
1496
|
effectiveType(obj); // set _effectiveType if appropriate, (future?): copy elems if extended
|
|
@@ -1658,6 +1706,8 @@ function resolve( model ) {
|
|
|
1658
1706
|
location,
|
|
1659
1707
|
$inferred: 'expand-param',
|
|
1660
1708
|
};
|
|
1709
|
+
setProp( art.returns, '_parent', art );
|
|
1710
|
+
setProp( art.returns, '_main', art._main || art );
|
|
1661
1711
|
setProp( art.returns, '_origin', origin.returns );
|
|
1662
1712
|
}
|
|
1663
1713
|
}
|
|
@@ -1868,7 +1918,7 @@ function resolve( model ) {
|
|
|
1868
1918
|
function resolveQuery( query ) {
|
|
1869
1919
|
if (!query._main) // parse error
|
|
1870
1920
|
return;
|
|
1871
|
-
|
|
1921
|
+
traverseQueryPost( query, null, populateQuery );
|
|
1872
1922
|
forEachGeneric( query, '$tableAliases', ( alias ) => {
|
|
1873
1923
|
// console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
|
|
1874
1924
|
if (alias.kind === 'mixin')
|
|
@@ -2593,6 +2643,8 @@ function resolve( model ) {
|
|
|
2593
2643
|
}
|
|
2594
2644
|
|
|
2595
2645
|
function resolveExpr( expr, expected, user, extDict, expandOrInline) {
|
|
2646
|
+
// TODO: when we have rewritten the resolvePath functions,
|
|
2647
|
+
// define a traverseExpr() in ./utils.js
|
|
2596
2648
|
// TODO: extra "expected" 'expand'/'inline' instead o param `expandOrInline`
|
|
2597
2649
|
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
2598
2650
|
return;
|
|
@@ -2629,7 +2681,7 @@ function resolve( model ) {
|
|
|
2629
2681
|
else if (expr.query) {
|
|
2630
2682
|
const { query } = expr;
|
|
2631
2683
|
if (query.kind || query._leadingQuery) { // UNION has _leadingQuery
|
|
2632
|
-
traverseQueryPost( query, false, resolveQuery );
|
|
2684
|
+
// traverseQueryPost( query, false, resolveQuery );
|
|
2633
2685
|
}
|
|
2634
2686
|
else {
|
|
2635
2687
|
error( 'expr-no-subquery', [ expr.location, user ], {},
|
|
@@ -2640,9 +2692,10 @@ function resolve( model ) {
|
|
|
2640
2692
|
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
2641
2693
|
args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
|
|
2642
2694
|
}
|
|
2643
|
-
if (expr.suffix &&
|
|
2695
|
+
if (expr.suffix && isDeprecatedEnabled( options )) {
|
|
2644
2696
|
const { location } = expr.suffix[0] || expr;
|
|
2645
|
-
error( null, [ location, user ],
|
|
2697
|
+
error( null, [ location, user ], { prop: 'deprecated' },
|
|
2698
|
+
'Window functions are not supported if $(PROP) options are set' );
|
|
2646
2699
|
}
|
|
2647
2700
|
if (expr.suffix)
|
|
2648
2701
|
expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
|
|
@@ -2830,8 +2883,6 @@ function navProjection( navigation, preferred ) {
|
|
|
2830
2883
|
// Query tree post-order traversal - called for everything which makes a query
|
|
2831
2884
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
2832
2885
|
function traverseQueryPost( query, simpleOnly, callback ) {
|
|
2833
|
-
while (Array.isArray(query)) // query in parentheses, TODO: remove
|
|
2834
|
-
query = query[0];
|
|
2835
2886
|
if (!query) // parser error
|
|
2836
2887
|
return;
|
|
2837
2888
|
if (!query.op) { // in FROM (not JOIN)
|
|
@@ -2851,11 +2902,19 @@ function traverseQueryPost( query, simpleOnly, callback ) {
|
|
|
2851
2902
|
// console.log('FE:')
|
|
2852
2903
|
}
|
|
2853
2904
|
else if (query.args) { // JOIN, UNION, INTERSECT
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2905
|
+
if (!query.join && simpleOnly == null) {
|
|
2906
|
+
// enough for elements: traverse only first args for UNION/INTERSECT
|
|
2907
|
+
// TODO: we might use this also when we do not rewrite associations
|
|
2908
|
+
// in non-referred sub queries
|
|
2909
|
+
traverseQueryPost( query.args[0], simpleOnly, callback );
|
|
2910
|
+
}
|
|
2911
|
+
else {
|
|
2912
|
+
for (const q of query.args)
|
|
2913
|
+
traverseQueryPost( q, simpleOnly, callback );
|
|
2914
|
+
// The ON condition has to be traversed extra, because it must be evaluated
|
|
2915
|
+
// after the complete FROM has been traversed. It is also not necessary to
|
|
2916
|
+
// evaluate it in populateQuery().
|
|
2917
|
+
}
|
|
2859
2918
|
}
|
|
2860
2919
|
// else: with parse error (`select from <EOF>`, `select distinct from;`)
|
|
2861
2920
|
}
|