@sap/cds-compiler 2.5.2 → 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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- 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 +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
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,19 +101,19 @@
|
|
|
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
|
|
|
110
|
-
const {
|
|
111
|
-
makeMessageFunction, searchName, weakLocation,
|
|
112
|
-
} = require('../base/messages');
|
|
112
|
+
const { searchName, weakLocation } = require('../base/messages');
|
|
113
113
|
const {
|
|
114
114
|
isDeprecatedEnabled, isBetaEnabled,
|
|
115
115
|
setProp, forEachGeneric, forEachInOrder,
|
|
116
|
-
|
|
116
|
+
forEachDefinition,
|
|
117
117
|
forEachMemberRecursivelyWithQuery,
|
|
118
118
|
} = require('../base/model');
|
|
119
119
|
const {
|
|
@@ -122,20 +122,20 @@ const {
|
|
|
122
122
|
const {
|
|
123
123
|
dictLocation,
|
|
124
124
|
} = require('../base/location');
|
|
125
|
+
const { kindProperties, dictKinds } = require('./base');
|
|
125
126
|
const {
|
|
126
|
-
annotationVal,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
dictKinds,
|
|
130
|
-
kindProperties,
|
|
131
|
-
fns,
|
|
127
|
+
annotationVal,
|
|
128
|
+
annotationIsFalse,
|
|
129
|
+
annotateWith,
|
|
132
130
|
linkToOrigin,
|
|
133
131
|
setMemberParent,
|
|
134
132
|
storeExtension,
|
|
135
133
|
dependsOnSilent,
|
|
136
|
-
|
|
134
|
+
augmentPath,
|
|
135
|
+
splitIntoPath,
|
|
136
|
+
} = require('./utils');
|
|
137
137
|
const { compareLayer, layer } = require('./moduleLayers');
|
|
138
|
-
const { initBuiltins } = require('./builtins');
|
|
138
|
+
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
139
139
|
const setLink = setProp;
|
|
140
140
|
|
|
141
141
|
/**
|
|
@@ -150,36 +150,41 @@ const setLink = setProp;
|
|
|
150
150
|
*
|
|
151
151
|
* @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
|
|
152
152
|
*/
|
|
153
|
-
function
|
|
153
|
+
function define( model ) {
|
|
154
154
|
const { options } = model;
|
|
155
155
|
// Get simplified "resolve" functionality and the message function:
|
|
156
156
|
const {
|
|
157
157
|
message, error, warning, info, messages,
|
|
158
|
-
} =
|
|
158
|
+
} = model.$messageFunctions;
|
|
159
159
|
const {
|
|
160
160
|
resolveUncheckedPath,
|
|
161
161
|
resolvePath,
|
|
162
162
|
resolveTypeArguments,
|
|
163
163
|
defineAnnotations,
|
|
164
164
|
attachAndEmitValidNames,
|
|
165
|
-
} =
|
|
166
|
-
|
|
167
|
-
let addTextsLanguageAssoc = false;
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
define,
|
|
165
|
+
} = model.$functions;
|
|
166
|
+
Object.assign( model.$functions, {
|
|
171
167
|
initArtifact,
|
|
172
168
|
lateExtensions,
|
|
173
169
|
projectionAncestor,
|
|
174
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);
|
|
175
176
|
};
|
|
176
177
|
|
|
178
|
+
const extensionsDict = Object.create(null);
|
|
179
|
+
let addTextsLanguageAssoc = false;
|
|
180
|
+
return doDefine();
|
|
181
|
+
|
|
177
182
|
/**
|
|
178
183
|
* Main function of the definer.
|
|
179
184
|
*
|
|
180
185
|
* @returns {XSN.Model}
|
|
181
186
|
*/
|
|
182
|
-
function
|
|
187
|
+
function doDefine() {
|
|
183
188
|
if (options.deprecated &&
|
|
184
189
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
185
190
|
warning( 'api-deprecated-option', {},
|
|
@@ -206,10 +211,8 @@ function getDefinerFunctions( model ) {
|
|
|
206
211
|
|
|
207
212
|
mergeI18nBlocks( model );
|
|
208
213
|
|
|
209
|
-
if (
|
|
214
|
+
if (options.parseCdl) {
|
|
210
215
|
initExtensionsWithoutApplying();
|
|
211
|
-
// Check for redefinitions
|
|
212
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
213
216
|
// If no extensions shall be applied then we can skip further
|
|
214
217
|
// artifact processing and return the model with an `extensions` property.
|
|
215
218
|
return model;
|
|
@@ -217,8 +220,8 @@ function getDefinerFunctions( model ) {
|
|
|
217
220
|
|
|
218
221
|
applyExtensions();
|
|
219
222
|
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
224
|
+
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
222
225
|
model.definitions['sap.common.Languages'];
|
|
223
226
|
addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
|
|
224
227
|
commonLanguagesEntity.elements.code);
|
|
@@ -239,13 +242,13 @@ function getDefinerFunctions( model ) {
|
|
|
239
242
|
* @param {XSN.AST} src
|
|
240
243
|
*/
|
|
241
244
|
function addSource( src ) {
|
|
242
|
-
// handle sub model from
|
|
245
|
+
// handle sub model from parser
|
|
243
246
|
if (!src.kind)
|
|
244
247
|
src.kind = 'source';
|
|
245
248
|
|
|
246
249
|
let namespace = src.namespace && src.namespace.path;
|
|
247
250
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
248
|
-
if (
|
|
251
|
+
if (isInReservedNamespace(prefix)) {
|
|
249
252
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
250
253
|
// TODO: use $(NAME)
|
|
251
254
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -281,8 +284,7 @@ function getDefinerFunctions( model ) {
|
|
|
281
284
|
function addDefinition( art, block ) {
|
|
282
285
|
const { absolute } = art.name;
|
|
283
286
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
284
|
-
if (absolute === 'cds' ||
|
|
285
|
-
absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
|
|
287
|
+
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
286
288
|
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
287
289
|
// TODO: use $(NAME)
|
|
288
290
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -306,7 +308,7 @@ function getDefinerFunctions( model ) {
|
|
|
306
308
|
}
|
|
307
309
|
else {
|
|
308
310
|
setLink( art, '_block', block );
|
|
309
|
-
// dictAdd might set $duplicates to true
|
|
311
|
+
// dictAdd might set $duplicates to true
|
|
310
312
|
dictAdd( model.definitions, absolute, art );
|
|
311
313
|
return true;
|
|
312
314
|
}
|
|
@@ -445,6 +447,23 @@ function getDefinerFunctions( model ) {
|
|
|
445
447
|
|
|
446
448
|
// Phase 2 ("init") --------------------------------------------------------
|
|
447
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
|
+
|
|
448
467
|
function initNamespaceAndUsing( src ) {
|
|
449
468
|
if (src.namespace) {
|
|
450
469
|
const decl = src.namespace;
|
|
@@ -489,12 +508,13 @@ function getDefinerFunctions( model ) {
|
|
|
489
508
|
if (!reInit)
|
|
490
509
|
initParentLink( art, model.definitions );
|
|
491
510
|
const block = art._block;
|
|
511
|
+
checkRedefinition( art );
|
|
492
512
|
defineAnnotations( art, art, block );
|
|
493
513
|
initMembers( art, art, block );
|
|
494
514
|
initDollarSelf( art ); // $self
|
|
495
515
|
if (art.params)
|
|
496
516
|
initParams( art ); // $parameters
|
|
497
|
-
if (art.includes && !(art.name.absolute in extensionsDict))
|
|
517
|
+
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
498
518
|
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
499
519
|
|
|
500
520
|
if (!art.query)
|
|
@@ -514,6 +534,7 @@ function getDefinerFunctions( model ) {
|
|
|
514
534
|
|
|
515
535
|
function initVocabulary( art ) {
|
|
516
536
|
initParentLink( art, model.vocabularies );
|
|
537
|
+
checkRedefinition( art );
|
|
517
538
|
const block = art._block;
|
|
518
539
|
defineAnnotations( art, art, block );
|
|
519
540
|
initMembers( art, art, block );
|
|
@@ -555,20 +576,6 @@ function getDefinerFunctions( model ) {
|
|
|
555
576
|
|
|
556
577
|
// From here til EOF, reexamine code ---------------------------------------
|
|
557
578
|
|
|
558
|
-
// currently called from preProcessArtifact(), do be called in "init"
|
|
559
|
-
function checkRedefinitions( obj, name, prop ) {
|
|
560
|
-
forEachMember( obj, checkRedefinitions, obj.targetAspect );
|
|
561
|
-
if (!obj.$duplicates)
|
|
562
|
-
return;
|
|
563
|
-
if (obj.name.location.file === '<built-in>') {
|
|
564
|
-
// builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
|
|
565
|
-
// The error shall only be printed for the user-defined conflicting artifact.
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
error( 'duplicate-definition', [ obj.name.location, obj ],
|
|
569
|
-
{ name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
|
|
570
|
-
}
|
|
571
|
-
|
|
572
579
|
function initDollarSelf( art ) {
|
|
573
580
|
const selfname = '$self';
|
|
574
581
|
// TODO: use setMemberParent() ?
|
|
@@ -609,14 +616,7 @@ function getDefinerFunctions( model ) {
|
|
|
609
616
|
if (query.on)
|
|
610
617
|
initExprForQuery( query.on, query );
|
|
611
618
|
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
612
|
-
|
|
613
|
-
if (elem && (elem.value || elem.expand)) {
|
|
614
|
-
setProp( elem, '_block', query._block );
|
|
615
|
-
defineAnnotations( elem, elem, query._block );
|
|
616
|
-
initExprForQuery( elem.value, query );
|
|
617
|
-
initExpandInline( elem );
|
|
618
|
-
}
|
|
619
|
-
}
|
|
619
|
+
initSelectItems( query, query.columns );
|
|
620
620
|
if (query.where)
|
|
621
621
|
initExprForQuery( query.where, query );
|
|
622
622
|
if (query.having)
|
|
@@ -624,22 +624,41 @@ function getDefinerFunctions( model ) {
|
|
|
624
624
|
initMembers( query, query, query._block );
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
function
|
|
628
|
-
// TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
629
|
-
|
|
630
|
-
|
|
627
|
+
function initSelectItems( parent, columns ) {
|
|
628
|
+
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
629
|
+
let wildcard = null;
|
|
630
|
+
for (const col of columns || parent.expand || parent.inline || []) {
|
|
631
|
+
if (!col) // parse error
|
|
631
632
|
continue;
|
|
632
|
-
if (
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
633
|
+
if (!columns) {
|
|
634
|
+
if (parent.value)
|
|
635
|
+
setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
636
|
+
else if (parent._pathHead)
|
|
637
|
+
setProp( col, '_pathHead', parent._pathHead );
|
|
638
|
+
}
|
|
639
|
+
if (col.val === '*') {
|
|
640
|
+
if (!wildcard) {
|
|
641
|
+
wildcard = col;
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
645
|
+
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
646
|
+
error( 'syntax-duplicate-clause', [ col.location, null ],
|
|
647
|
+
{ prop: '*', line: wildcard.location.line, col: wildcard.location.col },
|
|
648
|
+
'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
|
|
649
|
+
// TODO: extra text variants for expand/inline? - probably not
|
|
650
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else if (col.value || col.expand) {
|
|
654
|
+
setProp( col, '_block', parent._block );
|
|
655
|
+
defineAnnotations( col, col, parent._block ); // TODO: complain with inline
|
|
656
|
+
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
657
|
+
if (columns)
|
|
658
|
+
initExprForQuery( col.value, parent );
|
|
659
|
+
initSelectItems( col );
|
|
640
660
|
}
|
|
641
661
|
}
|
|
642
|
-
// TODO: allow sub queries in top-level expand without parallel ref
|
|
643
662
|
}
|
|
644
663
|
|
|
645
664
|
function initExprForQuery( expr, query ) {
|
|
@@ -658,9 +677,38 @@ function getDefinerFunctions( model ) {
|
|
|
658
677
|
}
|
|
659
678
|
else if (expr.path && expr.$expected === 'exists') {
|
|
660
679
|
expr.$expected = 'approved-exists';
|
|
680
|
+
approveExistsInChildren(expr);
|
|
661
681
|
}
|
|
662
682
|
}
|
|
663
683
|
|
|
684
|
+
/**
|
|
685
|
+
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
686
|
+
* since we will have a top-level subquery after exists-processing in the forHanaNew.
|
|
687
|
+
*
|
|
688
|
+
* Recursively drill down into:
|
|
689
|
+
* - the .path
|
|
690
|
+
* - the .args
|
|
691
|
+
* - the .where.args
|
|
692
|
+
*
|
|
693
|
+
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
694
|
+
*
|
|
695
|
+
* working: exists toE[exists toE] -> select from E where exists toE
|
|
696
|
+
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
697
|
+
*
|
|
698
|
+
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
699
|
+
*/
|
|
700
|
+
function approveExistsInChildren(exprOrPathElement) {
|
|
701
|
+
if (exprOrPathElement.$expected === 'exists')
|
|
702
|
+
exprOrPathElement.$expected = 'approved-exists';
|
|
703
|
+
// Drill down
|
|
704
|
+
if (exprOrPathElement.args)
|
|
705
|
+
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
706
|
+
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
707
|
+
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
708
|
+
else if (exprOrPathElement.path)
|
|
709
|
+
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
710
|
+
}
|
|
711
|
+
|
|
664
712
|
// table is table expression in FROM, becomes an alias
|
|
665
713
|
function initTableExpression( table, query, joinParents ) {
|
|
666
714
|
if (!table) // parse error
|
|
@@ -708,6 +756,8 @@ function getDefinerFunctions( model ) {
|
|
|
708
756
|
// ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
|
|
709
757
|
// : ta._parent.from ) // directly in FROM
|
|
710
758
|
// Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
|
|
759
|
+
if (!tab) // parse error; time for #6241
|
|
760
|
+
return; // (parser method to only add non-null to array)
|
|
711
761
|
setProp( tab, '_joinParent', table );
|
|
712
762
|
tab.$joinArgsIndex = index;
|
|
713
763
|
initTableExpression( tab, query, joinParents );
|
|
@@ -732,12 +782,13 @@ function getDefinerFunctions( model ) {
|
|
|
732
782
|
setMemberParent( table, table.name.id, query );
|
|
733
783
|
setProp( table, '_block', query._block );
|
|
734
784
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
735
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': '
|
|
785
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
736
786
|
} );
|
|
737
787
|
// also add to JOIN nodes for name restrictions:
|
|
738
788
|
for (const p of joinParents) {
|
|
739
|
-
//
|
|
740
|
-
|
|
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 );
|
|
741
792
|
}
|
|
742
793
|
if (table.name.id[0] === '$') {
|
|
743
794
|
warning( 'syntax-dollar-ident', [ table.name.location, table ], {
|
|
@@ -824,7 +875,7 @@ function getDefinerFunctions( model ) {
|
|
|
824
875
|
// assignments on the mixin... (also for future mixin definitions
|
|
825
876
|
// with generated values)
|
|
826
877
|
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
827
|
-
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '
|
|
878
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
828
879
|
} );
|
|
829
880
|
if (mixin.name.id[0] === '$') {
|
|
830
881
|
warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
|
|
@@ -847,7 +898,7 @@ function getDefinerFunctions( model ) {
|
|
|
847
898
|
// TODO: error if CSN has both target.elements and targetAspect.elements -> delete target
|
|
848
899
|
return true;
|
|
849
900
|
}
|
|
850
|
-
if (elem.targetAspect || !isDirectComposition( elem ))
|
|
901
|
+
if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
|
|
851
902
|
return false;
|
|
852
903
|
const name = resolveUncheckedPath( target, 'compositionTarget', elem );
|
|
853
904
|
const aspect = name && model.definitions[name];
|
|
@@ -861,6 +912,7 @@ function getDefinerFunctions( model ) {
|
|
|
861
912
|
* (which is basically the component name of the `parent` element plus a dot).
|
|
862
913
|
*/
|
|
863
914
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
915
|
+
// TODO: split extend from init
|
|
864
916
|
const isQueryExtension = kindProperties[construct.kind].isExtension &&
|
|
865
917
|
(parent._main || parent).query;
|
|
866
918
|
let obj = construct;
|
|
@@ -916,7 +968,7 @@ function getDefinerFunctions( model ) {
|
|
|
916
968
|
forEachInOrder( construct, 'params', init );
|
|
917
969
|
const { returns } = construct;
|
|
918
970
|
if (returns) {
|
|
919
|
-
returns.kind = 'param';
|
|
971
|
+
returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
|
|
920
972
|
init( returns, '' ); // '' is special name for returns parameter
|
|
921
973
|
}
|
|
922
974
|
return;
|
|
@@ -950,6 +1002,7 @@ function getDefinerFunctions( model ) {
|
|
|
950
1002
|
setProp( elem, '_block', bl );
|
|
951
1003
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
952
1004
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1005
|
+
checkRedefinition( elem );
|
|
953
1006
|
defineAnnotations( elem, elem, bl );
|
|
954
1007
|
initMembers( elem, elem, bl, initExtensions );
|
|
955
1008
|
|
|
@@ -985,6 +1038,7 @@ function getDefinerFunctions( model ) {
|
|
|
985
1038
|
}
|
|
986
1039
|
|
|
987
1040
|
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1041
|
+
// TODO: do differently, see also annotateMembers() in resolver
|
|
988
1042
|
// To have been checked by parsers:
|
|
989
1043
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
990
1044
|
if (!dict)
|
|
@@ -1269,7 +1323,7 @@ function getDefinerFunctions( model ) {
|
|
|
1269
1323
|
}
|
|
1270
1324
|
|
|
1271
1325
|
function createTargetEntity( target, elem, keys, entityName, base ) {
|
|
1272
|
-
const location = elem.
|
|
1326
|
+
const { location } = elem.targetAspect || elem.target || elem;
|
|
1273
1327
|
elem.on = {
|
|
1274
1328
|
location,
|
|
1275
1329
|
op: { val: '=', location },
|
|
@@ -1314,7 +1368,7 @@ function getDefinerFunctions( model ) {
|
|
|
1314
1368
|
// If 'up_' shall be rendered unmanaged, infer the parent
|
|
1315
1369
|
// primary keys and add the ON condition
|
|
1316
1370
|
if (isDeprecatedEnabled( options, 'unmanagedUpInComponent' )) {
|
|
1317
|
-
addProxyElements( art, keys, 'aspect-composition', location,
|
|
1371
|
+
addProxyElements( art, keys, 'aspect-composition', target.name && location,
|
|
1318
1372
|
'up__', '@odata.containment.ignore' );
|
|
1319
1373
|
up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
|
|
1320
1374
|
}
|
|
@@ -1328,7 +1382,7 @@ function getDefinerFunctions( model ) {
|
|
|
1328
1382
|
setLink( art, '_base', base._base || base );
|
|
1329
1383
|
|
|
1330
1384
|
dictAdd( art.elements, 'up_', up);
|
|
1331
|
-
addProxyElements( art, target.elements, 'aspect-composition', location );
|
|
1385
|
+
addProxyElements( art, target.elements, 'aspect-composition', target.name && location );
|
|
1332
1386
|
|
|
1333
1387
|
setLink( art, '_block', model.$internal );
|
|
1334
1388
|
model.definitions[entityName] = art;
|
|
@@ -1375,7 +1429,7 @@ function getDefinerFunctions( model ) {
|
|
|
1375
1429
|
const exts = model.$lateExtensions[name];
|
|
1376
1430
|
if (art && art.kind !== 'namespace') {
|
|
1377
1431
|
if (art.builtin) {
|
|
1378
|
-
for (const ext
|
|
1432
|
+
for (const ext of exts)
|
|
1379
1433
|
info( 'anno-builtin', [ ext.name.location, ext ] );
|
|
1380
1434
|
}
|
|
1381
1435
|
// created texts entity, autoexposed entity
|
|
@@ -1423,6 +1477,12 @@ function getDefinerFunctions( model ) {
|
|
|
1423
1477
|
|
|
1424
1478
|
model.extensions.push(annotationArtifact);
|
|
1425
1479
|
extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
|
|
1480
|
+
// if one of the annotate statement mentions 'returns', assume it
|
|
1481
|
+
// TODO: with warning/info?
|
|
1482
|
+
for (const ext of exts) {
|
|
1483
|
+
if (ext.$syntax === 'returns')
|
|
1484
|
+
annotationArtifact.$syntax = 'returns';
|
|
1485
|
+
}
|
|
1426
1486
|
}
|
|
1427
1487
|
}
|
|
1428
1488
|
}
|
|
@@ -1706,6 +1766,7 @@ function getDefinerFunctions( model ) {
|
|
|
1706
1766
|
}
|
|
1707
1767
|
|
|
1708
1768
|
function extendMembers( extensions, art, noExtend ) {
|
|
1769
|
+
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1709
1770
|
const elemExtensions = [];
|
|
1710
1771
|
extensions.sort( compareLayer );
|
|
1711
1772
|
for (const ext of extensions) {
|
|
@@ -1751,7 +1812,12 @@ function getDefinerFunctions( model ) {
|
|
|
1751
1812
|
[ 'elements', 'actions' ].forEach( (prop) => {
|
|
1752
1813
|
const dict = art._extend && art._extend[prop];
|
|
1753
1814
|
for (const name in dict) {
|
|
1754
|
-
|
|
1815
|
+
let obj = art;
|
|
1816
|
+
if (obj.targetAspect)
|
|
1817
|
+
obj = obj.targetAspect;
|
|
1818
|
+
while (obj.items)
|
|
1819
|
+
obj = obj.items;
|
|
1820
|
+
const validDict = obj[prop] || prop === 'elements' && obj.enum;
|
|
1755
1821
|
const member = validDict[name];
|
|
1756
1822
|
if (!member)
|
|
1757
1823
|
extendNothing( dict[name], prop, name, art, validDict );
|
|
@@ -1902,24 +1968,6 @@ function getDefinerFunctions( model ) {
|
|
|
1902
1968
|
}
|
|
1903
1969
|
}
|
|
1904
1970
|
|
|
1905
|
-
// TODO: move to "init" phase
|
|
1906
|
-
/**
|
|
1907
|
-
* Check whether redefinitions of the given artifact name exist and
|
|
1908
|
-
* adapt to `targetAspect`.
|
|
1909
|
-
*
|
|
1910
|
-
* @param {string} name
|
|
1911
|
-
*/
|
|
1912
|
-
function preProcessArtifact( name ) {
|
|
1913
|
-
const art = model.definitions[name];
|
|
1914
|
-
if (Array.isArray(art.$duplicates)) {
|
|
1915
|
-
// A definition name containing a `.` is not invalid (TODO: starting or
|
|
1916
|
-
// ending with a dot is invalid and could be checked here)
|
|
1917
|
-
for (const a of art.$duplicates)
|
|
1918
|
-
checkRedefinitions( a, name, 'definitions' );
|
|
1919
|
-
}
|
|
1920
|
-
checkRedefinitions( art, name, 'definitions' );
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
1971
|
/**
|
|
1924
1972
|
* Process "composition of" artifacts.
|
|
1925
1973
|
*
|
|
@@ -1989,8 +2037,9 @@ function getDefinerFunctions( model ) {
|
|
|
1989
2037
|
}
|
|
1990
2038
|
|
|
1991
2039
|
if (isKey && isLocalized) { // key with localized is wrong - ignore localized
|
|
1992
|
-
|
|
1993
|
-
|
|
2040
|
+
const errpos = elem.localized || elem.type || elem.name;
|
|
2041
|
+
warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
|
|
2042
|
+
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
1994
2043
|
}
|
|
1995
2044
|
}
|
|
1996
2045
|
if (textElems.length <= keys)
|
|
@@ -2132,9 +2181,9 @@ function getDefinerFunctions( model ) {
|
|
|
2132
2181
|
});
|
|
2133
2182
|
}
|
|
2134
2183
|
}
|
|
2135
|
-
|
|
2184
|
+
if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
|
|
2136
2185
|
const localized = orig.localized || orig.type || orig.name;
|
|
2137
|
-
elem.localized = { val:
|
|
2186
|
+
elem.localized = { val: null, $inferred: 'localized', location: localized.location };
|
|
2138
2187
|
}
|
|
2139
2188
|
}
|
|
2140
2189
|
if (fioriEnabled)
|
|
@@ -2250,9 +2299,9 @@ function mergeI18nBlocks( model ) {
|
|
|
2250
2299
|
model.i18n[langKey][textKey] = sourceVal;
|
|
2251
2300
|
}
|
|
2252
2301
|
else if (modelVal.val !== sourceVal.val) {
|
|
2253
|
-
|
|
2254
|
-
warning('i18n-different-value',
|
|
2255
|
-
|
|
2302
|
+
// TODO: put mergeI18nBlocks() into main function instead
|
|
2303
|
+
model.$messageFunctions.warning( 'i18n-different-value', sourceVal.location,
|
|
2304
|
+
{ prop: textKey, otherprop: langKey } );
|
|
2256
2305
|
}
|
|
2257
2306
|
}
|
|
2258
2307
|
}
|
|
@@ -2269,26 +2318,6 @@ function pathName(path) {
|
|
|
2269
2318
|
return path.map( id => id.id ).join('.');
|
|
2270
2319
|
}
|
|
2271
2320
|
|
|
2272
|
-
/**
|
|
2273
|
-
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
2274
|
-
* Each segment will have the given location assigned.
|
|
2275
|
-
*
|
|
2276
|
-
* @param {CSN.Location} location
|
|
2277
|
-
* @param {string} name
|
|
2278
|
-
* @returns {XSN.Path}
|
|
2279
|
-
*/
|
|
2280
|
-
function splitIntoPath( location, name ) {
|
|
2281
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
2282
|
-
}
|
|
2283
|
-
|
|
2284
|
-
/**
|
|
2285
|
-
* @param {CSN.Location} location
|
|
2286
|
-
* @param {...any} args
|
|
2287
|
-
*/
|
|
2288
|
-
function augmentPath( location, ...args ) {
|
|
2289
|
-
return { path: args.map( id => ({ id, location }) ), location };
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
2321
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
2293
2322
|
const args = relations.map( eq );
|
|
2294
2323
|
return (args.length === 1)
|
|
@@ -2317,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
|
2317
2346
|
// these function could be used to a future lib/compiler/utils.js, but DO NOT
|
|
2318
2347
|
// SHARE with utility functions for CSN processors
|
|
2319
2348
|
|
|
2320
|
-
module.exports = {
|
|
2321
|
-
define: model => getDefinerFunctions( model ).define(),
|
|
2322
|
-
getDefinerFunctions,
|
|
2323
|
-
augmentPath,
|
|
2324
|
-
splitIntoPath,
|
|
2325
|
-
};
|
|
2349
|
+
module.exports = { define };
|