@sap/cds-compiler 6.4.2 → 6.5.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 +87 -1159
- package/README.md +1 -10
- package/doc/IncompatibleChanges_v5.md +436 -0
- package/doc/IncompatibleChanges_v6.md +659 -0
- package/doc/Versioning.md +3 -7
- package/lib/api/main.js +1 -0
- package/lib/api/options.js +5 -0
- package/lib/api/validate.js +3 -0
- package/lib/base/message-registry.js +25 -2
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +3 -2
- package/lib/checks/actionsFunctions.js +6 -3
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
- package/lib/checks/existsInForbiddenPlaces.js +32 -0
- package/lib/checks/existsMustEndInAssoc.js +1 -1
- package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
- package/lib/checks/validator.js +6 -2
- package/lib/compiler/assert-consistency.js +5 -7
- package/lib/compiler/builtins.js +5 -6
- package/lib/compiler/checks.js +4 -8
- package/lib/compiler/define.js +244 -459
- package/lib/compiler/extend.js +297 -11
- package/lib/compiler/finalize-parse-cdl.js +2 -10
- package/lib/compiler/generate.js +29 -1
- package/lib/compiler/populate.js +21 -63
- package/lib/compiler/propagator.js +1 -2
- package/lib/compiler/resolve.js +2 -12
- package/lib/compiler/shared.js +145 -114
- package/lib/compiler/tweak-assocs.js +14 -10
- package/lib/compiler/utils.js +97 -0
- package/lib/compiler/xpr-rewrite.js +113 -140
- package/lib/edm/annotations/edmJson.js +9 -6
- package/lib/edm/annotations/genericTranslation.js +8 -4
- package/lib/edm/csn2edm.js +3 -4
- package/lib/edm/edmInboundChecks.js +1 -2
- package/lib/edm/edmPreprocessor.js +3 -3
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +4 -3
- package/lib/gen/Dictionary.json +16 -1
- package/lib/json/from-csn.js +4 -6
- package/lib/json/to-csn.js +3 -3
- package/lib/model/csnRefs.js +13 -4
- package/lib/model/enrichCsn.js +4 -2
- package/lib/optionProcessor.js +8 -4
- package/lib/parsers/AstBuildingParser.js +1 -1
- package/lib/render/utils/sql.js +3 -2
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +3 -3
- package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
- package/lib/transform/db/assocsToQueries/transformExists.js +17 -13
- package/lib/transform/db/assocsToQueries/utils.js +1 -6
- package/lib/transform/db/backlinks.js +4 -4
- package/lib/transform/db/cdsPersistence.js +4 -4
- package/lib/transform/db/constraints.js +4 -4
- package/lib/transform/db/expansion.js +5 -5
- package/lib/transform/db/flattening.js +4 -5
- package/lib/transform/db/rewriteCalculatedElements.js +3 -3
- package/lib/transform/db/temporal.js +11 -11
- package/lib/transform/draft/db.js +2 -0
- package/lib/transform/draft/odata.js +5 -7
- package/lib/transform/effective/flattening.js +1 -2
- package/lib/transform/forOdata.js +3 -3
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +13 -20
- package/lib/transform/odata/createForeignKeys.js +1 -2
- package/lib/transform/odata/flattening.js +1 -2
- package/lib/transform/odata/toFinalBaseType.js +52 -55
- package/lib/transform/transformUtils.js +3 -4
- package/package.json +3 -3
- package/doc/CHANGELOG_BETA.md +0 -464
- package/doc/CHANGELOG_DEPRECATED.md +0 -235
package/lib/compiler/utils.js
CHANGED
|
@@ -134,6 +134,28 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// Initialization (define.js), shared with extend.js: ---------------------------
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Initialize artifact links inside `obj.items` (for nested ones as well).
|
|
141
|
+
* Does nothing, it `obj.items` does not exist.
|
|
142
|
+
*
|
|
143
|
+
* @param {XSN.Artifact} obj
|
|
144
|
+
* @param {object} block
|
|
145
|
+
* @return {XSN.Artifact}
|
|
146
|
+
*/
|
|
147
|
+
function initItemsLinks( obj, block ) {
|
|
148
|
+
let { items } = obj;
|
|
149
|
+
while (items) {
|
|
150
|
+
setLink( items, '_outer', obj );
|
|
151
|
+
setLink( items, '_parent', obj._parent );
|
|
152
|
+
setLink( items, '_block', block );
|
|
153
|
+
obj = items;
|
|
154
|
+
items = obj.items;
|
|
155
|
+
}
|
|
156
|
+
return obj;
|
|
157
|
+
}
|
|
158
|
+
|
|
137
159
|
/**
|
|
138
160
|
* Set the member `elem` to have a _parent link to `parent` and a corresponding
|
|
139
161
|
* _main link. Also set the member's name accordingly, where argument `name`
|
|
@@ -162,6 +184,76 @@ function createAndLinkCalcDepElement( elem ) {
|
|
|
162
184
|
setLink( r, '_outer', elem );
|
|
163
185
|
}
|
|
164
186
|
|
|
187
|
+
function initExprAnnoBlock( art, block ) {
|
|
188
|
+
// remark: `art` could also be the extension
|
|
189
|
+
for (const prop in art) {
|
|
190
|
+
if (prop.charAt(0) !== '@')
|
|
191
|
+
continue;
|
|
192
|
+
const anno = art[prop];
|
|
193
|
+
// _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
|
|
194
|
+
if (anno.literal === 'array') {
|
|
195
|
+
anno.kind = '$annotation';
|
|
196
|
+
for (const item of anno.val)
|
|
197
|
+
setLink( item, '_block', block );
|
|
198
|
+
}
|
|
199
|
+
else if (anno.$tokenTexts) {
|
|
200
|
+
// remark: it wouldn't hurt to set it always...
|
|
201
|
+
anno.kind = '$annotation';
|
|
202
|
+
setLink( anno, '_block', block );
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function initDollarSelf( art ) {
|
|
208
|
+
// TODO: use setMemberParent() ?
|
|
209
|
+
const self = {
|
|
210
|
+
name: { id: '$self', location: art.location },
|
|
211
|
+
kind: '$self',
|
|
212
|
+
location: art.location,
|
|
213
|
+
};
|
|
214
|
+
setLink( self, '_parent', art );
|
|
215
|
+
setLink( self, '_main', art ); // used on main artifact
|
|
216
|
+
setLink( self, '_origin', art );
|
|
217
|
+
art.$tableAliases = Object.create( null );
|
|
218
|
+
art.$tableAliases.$self = self;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function initDollarParameters( art ) {
|
|
222
|
+
// TODO: use setMemberParent() ?
|
|
223
|
+
const parameters = {
|
|
224
|
+
name: { id: '$parameters' },
|
|
225
|
+
kind: '$parameters',
|
|
226
|
+
location: art.location,
|
|
227
|
+
deprecated: true, // hide in code completion
|
|
228
|
+
};
|
|
229
|
+
setLink( parameters, '_parent', art );
|
|
230
|
+
setLink( parameters, '_main', art );
|
|
231
|
+
// Search for :const after :param. If there will be a possibility in the
|
|
232
|
+
// future that we can extend <query>.columns, we must be sure to use
|
|
233
|
+
// _block of that new column after :param (or just allow $parameters there).
|
|
234
|
+
setLink( parameters, '_block', art._block );
|
|
235
|
+
if (art.params) {
|
|
236
|
+
parameters.elements = art.params;
|
|
237
|
+
parameters.$tableAliases = art.params; // TODO: find better name - $lexical?
|
|
238
|
+
}
|
|
239
|
+
art.$tableAliases.$parameters = parameters;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function initBoundSelfParam( params, main ) {
|
|
243
|
+
if (!params)
|
|
244
|
+
return;
|
|
245
|
+
const first = params[Object.keys( params )[0] || ''];
|
|
246
|
+
const type = first?.type || first?.items?.type; // this sequence = no derived type
|
|
247
|
+
const path = type?.path;
|
|
248
|
+
if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
|
|
249
|
+
const $self = main.$tableAliases?.$self ||
|
|
250
|
+
main.kind === 'extend' && { name: { id: '$self' } };
|
|
251
|
+
// remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
|
|
252
|
+
setLink( type, '_artifact', $self );
|
|
253
|
+
setLink( path[0], '_artifact', $self );
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
165
257
|
/**
|
|
166
258
|
* Adds a dependency user -> art with the given location.
|
|
167
259
|
*
|
|
@@ -702,8 +794,13 @@ module.exports = {
|
|
|
702
794
|
proxyCopyMembers,
|
|
703
795
|
dependsOn,
|
|
704
796
|
dependsOnSilent,
|
|
797
|
+
initItemsLinks,
|
|
705
798
|
setMemberParent,
|
|
706
799
|
createAndLinkCalcDepElement,
|
|
800
|
+
initExprAnnoBlock,
|
|
801
|
+
initDollarSelf,
|
|
802
|
+
initDollarParameters,
|
|
803
|
+
initBoundSelfParam,
|
|
707
804
|
storeExtension,
|
|
708
805
|
withAssociation,
|
|
709
806
|
pathName,
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
// the path up to the last path step in the type-of.
|
|
73
73
|
// - an element definition, `$self.sub.elem` needs to be replaced by the element name.
|
|
74
74
|
// Paths without `$self` on the "type-of element" itself need to have
|
|
75
|
-
// the first path step be replaced by the
|
|
75
|
+
// the first path step be replaced by the destination element name.
|
|
76
76
|
// - a parameter, `$self.sub.elem` needs to be replaced by the parameter name.
|
|
77
77
|
// Paths without `$self` on the "type-of element" itself need to have
|
|
78
|
-
// the first path step be replaced by the
|
|
78
|
+
// the first path step be replaced by the destination parameter name.
|
|
79
79
|
// - a return parameter, `$self` needs to be rejected, because there is no way
|
|
80
80
|
// to refer to `returns`. Paths without `$self` on the "type-of element" itself
|
|
81
81
|
// (not sub-elements), need to be rejected as well.
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
// was projected or if a prefix was projected (e.g. for structures or associations).
|
|
109
109
|
// The same rules as for ON-condition rewriting apply.
|
|
110
110
|
//
|
|
111
|
-
// Furthermore, as the
|
|
111
|
+
// Furthermore, as the destination is a select item, and this select item belongs to a table
|
|
112
112
|
// alias, we should rewrite all annotation paths only to projected elements of that
|
|
113
113
|
// table alias. Cross-rewriting between table aliases should not be done.
|
|
114
114
|
// This is the same we do for association rewriting.
|
|
@@ -157,14 +157,13 @@ const { isSimpleCdlIdentifier } = require('../parsers/identifiers');
|
|
|
157
157
|
// Config object passed around all "rewrite" functions.
|
|
158
158
|
class AnnoRewriteConfig {
|
|
159
159
|
anno;
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
destination;
|
|
161
|
+
destinationRoot;
|
|
162
162
|
origin;
|
|
163
|
-
|
|
163
|
+
fromDestinationType;
|
|
164
164
|
fromCalcElement;
|
|
165
165
|
expandedRoot;
|
|
166
166
|
expandedRootType;
|
|
167
|
-
isInFilter;
|
|
168
167
|
tokenExpr;
|
|
169
168
|
}
|
|
170
169
|
|
|
@@ -185,45 +184,50 @@ function xprRewriteFns( model ) {
|
|
|
185
184
|
};
|
|
186
185
|
|
|
187
186
|
/**
|
|
187
|
+
* Report anno-missing-rewrite if rewrite for annotations.
|
|
188
|
+
* Returns traverseExpr.STOP.
|
|
189
|
+
*
|
|
188
190
|
* @param expr
|
|
189
191
|
* @param {AnnoRewriteConfig} config
|
|
190
192
|
* @param {string} [variant]
|
|
191
193
|
*/
|
|
192
194
|
function reportAnnoRewriteError( expr, config, variant = 'std' ) {
|
|
193
|
-
|
|
195
|
+
if (config.tokenExpr) {
|
|
194
196
|
error( 'anno-missing-rewrite', [
|
|
195
|
-
weakLocation( config.
|
|
197
|
+
weakLocation( config.destination.location ), config.destination,
|
|
196
198
|
], {
|
|
197
199
|
'#': variant,
|
|
198
200
|
anno: config.anno,
|
|
199
201
|
art: config.origin,
|
|
200
202
|
elemref: expr,
|
|
201
203
|
});
|
|
204
|
+
}
|
|
205
|
+
return traverseExpr.STOP;
|
|
202
206
|
}
|
|
203
207
|
|
|
204
208
|
/**
|
|
205
|
-
* Rewrite the propagated annotation relative to the
|
|
209
|
+
* Rewrite the propagated annotation relative to the destination.
|
|
206
210
|
*
|
|
207
|
-
* @param {XSN.Artifact}
|
|
211
|
+
* @param {XSN.Artifact} destination
|
|
208
212
|
* @param {XSN.Artifact} origin
|
|
209
213
|
* @param {string} annoName
|
|
210
214
|
*/
|
|
211
|
-
function rewriteAnnotationsRefs(
|
|
215
|
+
function rewriteAnnotationsRefs( destination, origin, annoName ) {
|
|
212
216
|
// Make sure not to waste time if no inherited annotation has references:
|
|
213
217
|
if (!origin?.$contains?.$annotation?.$path)
|
|
214
218
|
return;
|
|
215
219
|
|
|
216
|
-
const anno =
|
|
220
|
+
const anno = destination[annoName];
|
|
217
221
|
// only annotations with expressions have a kind
|
|
218
222
|
// also, don't report errors twice
|
|
219
223
|
if (!anno.kind || anno.$invalidPaths)
|
|
220
224
|
return;
|
|
221
225
|
|
|
222
|
-
const hasError = rewriteRefsInExpression(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
const hasError = rewriteRefsInExpression( destination, origin, annoName );
|
|
227
|
+
destination.$contains ??= {};
|
|
228
|
+
destination.$contains.$annotation ??= {};
|
|
229
|
+
destination.$contains.$annotation.$path ||= origin.$contains.$annotation.$path;
|
|
230
|
+
destination.$contains.$annotation.$self ||= origin.$contains.$annotation.$self;
|
|
227
231
|
if (hasError)
|
|
228
232
|
anno.$invalidPaths = true; // avoid subsequent errors
|
|
229
233
|
}
|
|
@@ -231,23 +235,23 @@ function xprRewriteFns( model ) {
|
|
|
231
235
|
function rewriteRefsInExpression( destination, origin, propName ) {
|
|
232
236
|
// Annotation comes from the destination's type. That's important to know, because
|
|
233
237
|
// path prefixes need to be adapted.
|
|
234
|
-
const
|
|
238
|
+
const fromDestinationType = destination.type?._artifact === origin;
|
|
235
239
|
// Annotation comes from the destination's calculated element.
|
|
236
240
|
// A special case propagation rule, e.g
|
|
237
241
|
// for `calcString: String = str;`. We also need to adapt path prefixes.
|
|
238
|
-
const fromCalcElement = !
|
|
242
|
+
const fromCalcElement = !fromDestinationType && destination.$calcDepElement &&
|
|
239
243
|
destination.value?._artifact === origin;
|
|
240
244
|
|
|
241
245
|
const { expandedRoot, expandedRootType }
|
|
242
|
-
= !
|
|
246
|
+
= !fromDestinationType && getExpandRoot( destination ) || {};
|
|
243
247
|
|
|
244
248
|
const config = {
|
|
245
249
|
__proto__: AnnoRewriteConfig.prototype,
|
|
246
250
|
anno: propName,
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
destination,
|
|
252
|
+
destinationRoot: annoRootArt( destination ),
|
|
249
253
|
origin,
|
|
250
|
-
|
|
254
|
+
fromDestinationType,
|
|
251
255
|
fromCalcElement,
|
|
252
256
|
expandedRoot,
|
|
253
257
|
expandedRootType,
|
|
@@ -255,10 +259,8 @@ function xprRewriteFns( model ) {
|
|
|
255
259
|
|
|
256
260
|
if (propName.charAt( 0 ) === '@')
|
|
257
261
|
return rewriteAnnotationExpr( destination[propName], config );
|
|
258
|
-
return traverseExpr
|
|
259
|
-
|
|
260
|
-
// eslint-disable-next-line @stylistic/max-len, @stylistic/function-paren-newline
|
|
261
|
-
(e, refCtx) => (rewriteAnnoExpr( e, config, refCtx ) ? traverseExpr.STOP : traverseExpr.SKIP) );
|
|
262
|
+
return traverseExpr( destination[propName], 'annoRewrite', destination,
|
|
263
|
+
rewriteAnnoExpr.bind( config ) );
|
|
262
264
|
}
|
|
263
265
|
|
|
264
266
|
/**
|
|
@@ -277,37 +279,36 @@ function xprRewriteFns( model ) {
|
|
|
277
279
|
else if (expr.$tokenTexts) {
|
|
278
280
|
// used to set `$tokenText` to true in case of rewritten annotation
|
|
279
281
|
config.tokenExpr = expr;
|
|
280
|
-
return traverseExpr
|
|
281
|
-
|
|
282
|
-
// eslint-disable-next-line @stylistic/max-len, @stylistic/function-paren-newline
|
|
283
|
-
(e, refCtx) => (rewriteAnnoExpr( e, config, refCtx ) ? traverseExpr.STOP : traverseExpr.SKIP) );
|
|
282
|
+
return traverseExpr( expr, 'annoRewrite', config.destination,
|
|
283
|
+
rewriteAnnoExpr.bind( config ) );
|
|
284
284
|
}
|
|
285
285
|
return false;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* @param {string} refCtx
|
|
292
|
-
* @returns {null|true} Returns true if the expression couldn't be rewritten.
|
|
289
|
+
* Rewrite the annotation expression `expr`.
|
|
290
|
+
* Returns traverseExpr.STOP if the expression couldn't be rewritten.
|
|
293
291
|
*/
|
|
294
|
-
function rewriteAnnoExpr( expr,
|
|
292
|
+
function rewriteAnnoExpr( expr, refCtx, user ) {
|
|
293
|
+
const config = this; // we use rewriteAnnoExpr.bind( config )
|
|
295
294
|
const root = expr.path && (expr.path[0]?._navigation || expr.path[0]?._artifact);
|
|
296
295
|
if (!root || !expr._artifact)
|
|
297
296
|
return null; // invalid path
|
|
298
297
|
|
|
299
|
-
|
|
298
|
+
if (refCtx === 'filter')
|
|
299
|
+
return rewriteGenericAnnoPath( expr, refCtx, config, user );
|
|
300
300
|
|
|
301
301
|
// Report obsolete $parameters; parameters on non-actions not supported, yet.
|
|
302
|
-
if (root.kind === '$parameters' ||
|
|
303
|
-
|
|
302
|
+
if (root.kind === '$parameters' ||
|
|
303
|
+
(root.kind === 'param' && root._parent.kind !== 'action' &&
|
|
304
|
+
root._parent.kind !== 'function'))
|
|
304
305
|
return reportAnnoRewriteError( expr, config, 'unsupported' );
|
|
305
306
|
|
|
306
307
|
if (root.kind === 'key') {
|
|
307
308
|
// References starting with a foreign key name (only possible in an
|
|
308
309
|
// annotation on a foreign key) have length 1 + the foreign key cannot be
|
|
309
310
|
// renamed → nothing to do. Otherwise, foreign keys cannot be directly
|
|
310
|
-
// addressed, except
|
|
311
|
+
// addressed, except via `annotate`. (Even as annotation value for
|
|
311
312
|
// foreign keys, `$self.assoc.target_id` always refers to the target side.)
|
|
312
313
|
return null;
|
|
313
314
|
}
|
|
@@ -318,37 +319,15 @@ function xprRewriteFns( model ) {
|
|
|
318
319
|
return null;
|
|
319
320
|
|
|
320
321
|
let hasError = false;
|
|
321
|
-
if (config.
|
|
322
|
-
hasError = adaptPathPrefixViaType( expr, config );
|
|
322
|
+
if (config.fromDestinationType || config.fromCalcElement)
|
|
323
|
+
hasError = adaptPathPrefixViaType( expr, config, user );
|
|
323
324
|
else if (config.expandedRoot)
|
|
324
|
-
hasError = adaptPathPrefixViaTypeExpansion( expr, config );
|
|
325
|
+
hasError = adaptPathPrefixViaTypeExpansion( expr, config, user );
|
|
325
326
|
|
|
326
|
-
hasError ||= rewriteGenericAnnoPath( expr, config,
|
|
327
|
+
hasError ||= rewriteGenericAnnoPath( expr, refCtx, config, user );
|
|
327
328
|
|
|
328
329
|
if (hasError)
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
// TODO: Remove extra loop once filter traversal is added to traverseExpr (#12068)
|
|
332
|
-
for (const step of expr.path) {
|
|
333
|
-
if (step?._artifact && step.where && !Array.isArray( step._artifact ) ) {
|
|
334
|
-
// We must not prefix `$`-renamed variables with `$self`, as it would
|
|
335
|
-
// change meaning, see (#11775). Also, the path's target changes.
|
|
336
|
-
const assocTarget = step._artifact.target._artifact;
|
|
337
|
-
if (target) {
|
|
338
|
-
const filterConfig = { ...config, target: assocTarget, isInFilter: true };
|
|
339
|
-
if (traverseExpr.STOP === traverseExpr(
|
|
340
|
-
step.where, 'filter', step,
|
|
341
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
342
|
-
(e, ctx) => expr.path && (rewriteGenericAnnoPath( e, filterConfig, ctx ) ? traverseExpr.STOP : traverseExpr.SKIP)
|
|
343
|
-
))
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
// can't happen: rejected earlier by compiler
|
|
348
|
-
return reportAnnoRewriteError( expr, config, 'unsupported' );
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
330
|
+
return traverseExpr.STOP;
|
|
352
331
|
|
|
353
332
|
if (expr.$tokenTexts === true) {
|
|
354
333
|
// TODO: do not do with Universal-CSN (and gensrc, but that does not matter)
|
|
@@ -365,46 +344,44 @@ function xprRewriteFns( model ) {
|
|
|
365
344
|
if (model.options.testMode) {
|
|
366
345
|
// re-resolve the modified path; all paths steps must match what we rewrote
|
|
367
346
|
const ref = { ...expr, path: [ ...expr.path.map(item => ({ ...item })) ] };
|
|
368
|
-
if (!resolvePath( ref, refCtx,
|
|
347
|
+
if (!resolvePath( ref, refCtx, user ))
|
|
369
348
|
throw new CompilerAssertion(`rewritten anno path must be resolvable: ${ JSON.stringify(ref.path) }`);
|
|
370
|
-
|
|
371
|
-
for (let i = 0; i < ref.path.length; ++i) {
|
|
372
|
-
const actual = ref.path[i];
|
|
373
|
-
const expected = ref.path[i];
|
|
374
|
-
if (actual._artifact !== expected._artifact) {
|
|
375
|
-
throw new CompilerAssertion(`rewritten anno path contains incorrect artifact links: ${
|
|
376
|
-
JSON.stringify(ref.path) }; step ${ i }`);
|
|
377
|
-
}
|
|
378
|
-
else if (actual._navigation !== undefined && actual._navigation !== expected._navigation) {
|
|
379
|
-
throw new CompilerAssertion(`rewritten anno path contains incorrect navigation links: ${
|
|
380
|
-
JSON.stringify(ref.path) }; step ${ i }`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
349
|
}
|
|
384
|
-
|
|
385
350
|
return false;
|
|
386
351
|
}
|
|
387
352
|
|
|
388
353
|
/**
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
354
|
+
* Return the destination node of the expr rewrite (stored in param `config`), or
|
|
355
|
+
* if we are in a filter expression, the association target of the corresponding
|
|
356
|
+
* path item (provided via param `user`).
|
|
357
|
+
*
|
|
358
|
+
* TODO: in the end, we should directly use the standard expression parameter
|
|
359
|
+
* `user` in all functions below. Probably if we unify this module with
|
|
360
|
+
* rewriteCondition() of tweak-assocs.js.
|
|
392
361
|
*/
|
|
393
|
-
function
|
|
394
|
-
|
|
362
|
+
function destinationOrAssocTarget( config, user ) {
|
|
363
|
+
return (user.id && user.where)
|
|
364
|
+
? user._artifact.target._artifact
|
|
365
|
+
: config.destination;
|
|
366
|
+
}
|
|
395
367
|
|
|
368
|
+
/**
|
|
369
|
+
* TODO: what exactly is a root environment?
|
|
370
|
+
*/
|
|
371
|
+
function getRootEnv( expr, config, destination ) {
|
|
396
372
|
if (expr.scope === 'param') // path is absolute
|
|
397
|
-
return navigationEnv( config.
|
|
373
|
+
return navigationEnv( config.destinationRoot, null, null, 'nav' );
|
|
398
374
|
|
|
399
375
|
// On select items, use navigation elements or table alias
|
|
400
376
|
// TODO: Expand/inline paths don't have a `_navigation` property on their last
|
|
401
377
|
// path step, yet. We need to implement expand/inline.
|
|
402
|
-
const isSimpleSelectItem =
|
|
378
|
+
const isSimpleSelectItem = destination.value?.path && destination._main?.query &&
|
|
379
|
+
!destination._columnParent;
|
|
403
380
|
if (isSimpleSelectItem) {
|
|
404
381
|
const isSelfPath = (expr.path[0]?._navigation?.kind === '$self');
|
|
405
382
|
if (isSelfPath) {
|
|
406
383
|
// Path is absolute, use table alias to resolve it.
|
|
407
|
-
let tableAlias =
|
|
384
|
+
let tableAlias = destination.value.path[0]._navigation;
|
|
408
385
|
while (tableAlias && tableAlias.kind === '$navElement')
|
|
409
386
|
tableAlias = tableAlias._parent;
|
|
410
387
|
if (tableAlias)
|
|
@@ -412,37 +389,36 @@ function xprRewriteFns( model ) {
|
|
|
412
389
|
}
|
|
413
390
|
else {
|
|
414
391
|
// Path is relative
|
|
415
|
-
const nav =
|
|
392
|
+
const nav = destination.value.path[destination.value.path.length - 1]._navigation?._parent;
|
|
416
393
|
if (nav)
|
|
417
394
|
return nav;
|
|
418
395
|
}
|
|
419
396
|
}
|
|
420
397
|
|
|
421
|
-
if (isSimpleSelectItem && model.options.testMode
|
|
422
|
-
|
|
398
|
+
if (isSimpleSelectItem && model.options.testMode &&
|
|
399
|
+
destination.value?.path[0]._navigation?.kind !== 'mixin')
|
|
400
|
+
throw new CompilerAssertion(`select item has no table alias: ${ JSON.stringify(destination.value.path) }`);
|
|
423
401
|
|
|
424
402
|
if (isAnnoPathAbsolute( expr ))
|
|
425
|
-
return navigationEnv( config.
|
|
403
|
+
return navigationEnv( config.destinationRoot, null, null, 'nav' );
|
|
426
404
|
|
|
427
405
|
// anno path is relative / element reference (others were already rejected)
|
|
428
|
-
// if the
|
|
429
|
-
return navigationEnv( isAnnoRootArt(
|
|
406
|
+
// if the destination is a root artifact, use it. Otherwise, use its parent.
|
|
407
|
+
return navigationEnv( isAnnoRootArt( destination ) ? destination : destination._parent,
|
|
408
|
+
null, null, 'nav' );
|
|
430
409
|
}
|
|
431
410
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
* @returns {boolean}
|
|
437
|
-
*/
|
|
438
|
-
function rewriteGenericAnnoPath( expr, config, refCtx ) {
|
|
411
|
+
function rewriteGenericAnnoPath( expr, refCtx, config, user ) {
|
|
412
|
+
if (expr._artifact?.kind === 'builtin')
|
|
413
|
+
return false;
|
|
414
|
+
const destination = destinationOrAssocTarget( config, user );
|
|
439
415
|
const isAbsolute = isAnnoPathAbsolute( expr );
|
|
440
416
|
const startIndex = isAbsolute ? 1 : 0;
|
|
441
417
|
|
|
442
418
|
// We get the root environment now, even though below we resolve the root item
|
|
443
419
|
// again if it was absolute (e.g. $self). We do so, because for queries, we
|
|
444
420
|
// want to respect the select item's corresponding table alias.
|
|
445
|
-
const rootEnv = getRootEnv( expr, config );
|
|
421
|
+
const rootEnv = getRootEnv( expr, config, destination );
|
|
446
422
|
|
|
447
423
|
// reset artifact link; we'll set it again if there are no errors
|
|
448
424
|
setArtifactLink( expr, null );
|
|
@@ -454,7 +430,7 @@ function xprRewriteFns( model ) {
|
|
|
454
430
|
delete expr.path[0]._navigation;
|
|
455
431
|
// TODO: What about `up_`? Shouldn't we set `_navigation` as well?
|
|
456
432
|
// TODO: Can we handle `$self` of anonymous-composition-of-aspect?
|
|
457
|
-
const root = resolvePathRoot( expr, refCtx,
|
|
433
|
+
const root = resolvePathRoot( expr, refCtx, destination );
|
|
458
434
|
if (!root)
|
|
459
435
|
return reportAnnoRewriteError( expr, config );
|
|
460
436
|
}
|
|
@@ -495,13 +471,13 @@ function xprRewriteFns( model ) {
|
|
|
495
471
|
setArtifactLink( expr, art );
|
|
496
472
|
|
|
497
473
|
if (startIndex === 0 && expr.path[0].id.startsWith('$')) {
|
|
498
|
-
if (
|
|
474
|
+
if (refCtx === 'filter') {
|
|
499
475
|
// In filters, we must not prepend `$self`, as that would change its meaning.
|
|
500
476
|
// We must reject it. See #11775
|
|
501
477
|
return reportAnnoRewriteError( expr, config );
|
|
502
478
|
}
|
|
503
479
|
// After rewriting, if an element starts with `$` -> add root prefix
|
|
504
|
-
prependRootPath( config.origin, config.
|
|
480
|
+
prependRootPath( config.origin, config.destinationRoot, expr );
|
|
505
481
|
}
|
|
506
482
|
|
|
507
483
|
return false;
|
|
@@ -513,12 +489,12 @@ function xprRewriteFns( model ) {
|
|
|
513
489
|
*
|
|
514
490
|
* @returns {boolean} Returns true if it couldn't be rewritten.
|
|
515
491
|
*/
|
|
516
|
-
function adaptPathPrefixViaType( expr, config ) {
|
|
517
|
-
const {
|
|
518
|
-
if (!
|
|
492
|
+
function adaptPathPrefixViaType( expr, config, user ) {
|
|
493
|
+
const { destination, origin } = config;
|
|
494
|
+
if (!destination._main && !origin._main)
|
|
519
495
|
return false; // no need to rewrite; both are top-level
|
|
520
496
|
|
|
521
|
-
if (rejectOuterReference( expr, origin, config ))
|
|
497
|
+
if (rejectOuterReference( expr, origin, config, user ))
|
|
522
498
|
return true;
|
|
523
499
|
|
|
524
500
|
// $self-paths via types from/to non-main artifacts always need to be rewritten.
|
|
@@ -529,16 +505,16 @@ function xprRewriteFns( model ) {
|
|
|
529
505
|
stripAbsolutePathPrefix( expr, origin );
|
|
530
506
|
|
|
531
507
|
if (wasAbsolute) {
|
|
532
|
-
prependRootPath( origin,
|
|
508
|
+
prependRootPath( origin, destination, expr );
|
|
533
509
|
}
|
|
534
|
-
else if (!isAnnoRootArt(
|
|
535
|
-
const item = { id:
|
|
536
|
-
setArtifactLink( item,
|
|
510
|
+
else if (!isAnnoRootArt( destination )) { // destination is element
|
|
511
|
+
const item = { id: destination.name.id };
|
|
512
|
+
setArtifactLink( item, destination );
|
|
537
513
|
prependToStrippedPath( origin, expr, [ item ] );
|
|
538
514
|
}
|
|
539
|
-
else if (
|
|
515
|
+
else if (destination.kind === 'param') {
|
|
540
516
|
// annotations on parameters need a `:prefix`
|
|
541
|
-
prependRootPath( origin,
|
|
517
|
+
prependRootPath( origin, destination, expr );
|
|
542
518
|
}
|
|
543
519
|
else {
|
|
544
520
|
prependToStrippedPath( origin, expr, [ ] );
|
|
@@ -556,7 +532,7 @@ function xprRewriteFns( model ) {
|
|
|
556
532
|
*
|
|
557
533
|
* @returns {boolean} Returns true if it couldn't be rewritten.
|
|
558
534
|
*/
|
|
559
|
-
function adaptPathPrefixViaTypeExpansion( expr, config ) {
|
|
535
|
+
function adaptPathPrefixViaTypeExpansion( expr, config, user ) {
|
|
560
536
|
const root = expr.path[0]?._navigation;
|
|
561
537
|
if (root?.kind !== '$self') {
|
|
562
538
|
// non-self paths are always valid in expanded artifacts
|
|
@@ -569,14 +545,14 @@ function xprRewriteFns( model ) {
|
|
|
569
545
|
if (!isBetaEnabled( model.options, 'rewriteAnnotationExpressionsViaType' ))
|
|
570
546
|
return reportAnnoRewriteError( expr, config, 'unsupported' );
|
|
571
547
|
|
|
572
|
-
if (rejectOuterReference( expr, config.expandedRootType, config ))
|
|
548
|
+
if (rejectOuterReference( expr, config.expandedRootType, config, user ))
|
|
573
549
|
return true;
|
|
574
550
|
|
|
575
551
|
stripAbsolutePathPrefix( expr, config.expandedRootType );
|
|
576
552
|
prependRootPath( config.expandedRootType, config.expandedRoot, expr );
|
|
577
|
-
setExpandStatusAnnotate( config.
|
|
553
|
+
setExpandStatusAnnotate( config.destination, 'annotate' );
|
|
578
554
|
|
|
579
|
-
config.
|
|
555
|
+
config.destination[config.anno].$inferred = 'anno-rewrite';
|
|
580
556
|
// $self-paths via type expansion always need to be rewritten.
|
|
581
557
|
if (config.tokenExpr)
|
|
582
558
|
config.tokenExpr.$tokenTexts = true;
|
|
@@ -621,32 +597,29 @@ function xprRewriteFns( model ) {
|
|
|
621
597
|
}
|
|
622
598
|
|
|
623
599
|
/**
|
|
624
|
-
* Returns false if the path can be propagated to the
|
|
625
|
-
* to any "outer" elements. It differentiates between the
|
|
600
|
+
* Returns false if the path can be propagated to the destination without referring
|
|
601
|
+
* to any "outer" elements. It differentiates between the destination being a main
|
|
626
602
|
* artifact and elements, because an element annotation referring to itself can't
|
|
627
603
|
* be propagated to a type:
|
|
628
604
|
*
|
|
629
605
|
* type T1 : { @a: (elem) elem: String; };
|
|
630
606
|
* type T2 : T1:elem; // invalid
|
|
631
607
|
*
|
|
632
|
-
* Also considers other
|
|
633
|
-
*
|
|
634
|
-
* @param {XSN.Expression} expr
|
|
635
|
-
* @param {XSN.Artifact} origin
|
|
636
|
-
* @param {AnnoRewriteConfig} config
|
|
637
|
-
* @returns {boolean}
|
|
608
|
+
* Also considers other destinations such as `returns`, etc.
|
|
638
609
|
*/
|
|
639
|
-
function rejectOuterReference( expr, origin, config ) {
|
|
610
|
+
function rejectOuterReference( expr, origin, config, user ) {
|
|
640
611
|
if (!isAnnoPathAbsolute( expr ) && !origin._main)
|
|
641
612
|
return false;
|
|
642
613
|
|
|
643
614
|
const root = expr.path[0]?._navigation;
|
|
644
615
|
const found = findRelativeRoot( expr, origin );
|
|
616
|
+
const destination = destinationOrAssocTarget( config, user );
|
|
645
617
|
const isInvalid = (found === -1) ||
|
|
646
618
|
// Can't use paths with `$self` in `returns`.
|
|
647
|
-
(root?.kind === '$self' && isReturnParam( config.
|
|
619
|
+
(root?.kind === '$self' && isReturnParam( config.destinationRoot )) ||
|
|
648
620
|
// siblings are allowed for non-main artifacts, except for 'returns'
|
|
649
|
-
(!
|
|
621
|
+
(!destination._main || isReturnParam( destination )) &&
|
|
622
|
+
(expr.path.length - found) <= 1;
|
|
650
623
|
|
|
651
624
|
if (isInvalid)
|
|
652
625
|
return reportAnnoRewriteError( expr, config );
|
|
@@ -720,7 +693,7 @@ function xprRewriteFns( model ) {
|
|
|
720
693
|
* @returns {*|null}
|
|
721
694
|
*/
|
|
722
695
|
function rewriteItem( expr, config, env, index ) {
|
|
723
|
-
const rewriteTarget = findRewriteTarget( expr, index, env, config.
|
|
696
|
+
const rewriteTarget = findRewriteTarget( expr, index, env, config.destination );
|
|
724
697
|
const found = setArtifactLink( expr.path[index], rewriteTarget[0] );
|
|
725
698
|
if (!found)
|
|
726
699
|
return null;
|
|
@@ -784,20 +757,20 @@ function isReturnParam( art ) {
|
|
|
784
757
|
}
|
|
785
758
|
|
|
786
759
|
/**
|
|
787
|
-
* Gets the artifact (e.g. element) that was expanded.
|
|
788
|
-
* is an expanded element.
|
|
760
|
+
* Gets the artifact (e.g. element) that was expanded.
|
|
761
|
+
* `destination` is a sub-artifact of that root and is an expanded element.
|
|
789
762
|
*
|
|
790
763
|
* - expandedRoot: Top-most structure that was expanded.
|
|
791
764
|
* - expandedRootType: The type of expandedRoot.
|
|
792
765
|
*
|
|
793
|
-
* @param {XSN.Artifact}
|
|
766
|
+
* @param {XSN.Artifact} destination
|
|
794
767
|
* @returns { {expandedRoot: XSN.Artifact, expandedRootType: XSN.Artifact}}
|
|
795
768
|
*/
|
|
796
|
-
function getExpandRoot(
|
|
797
|
-
if (
|
|
769
|
+
function getExpandRoot( destination ) {
|
|
770
|
+
if (destination.$inferred !== 'expanded' && destination.$inferred !== 'rewrite')
|
|
798
771
|
return { expandedRoot: null, expandedRootType: null };
|
|
799
772
|
|
|
800
|
-
let expandedRoot =
|
|
773
|
+
let expandedRoot = destination;
|
|
801
774
|
|
|
802
775
|
// 'expanded' for structures, 'rewrite' for foreign keys
|
|
803
776
|
while (expandedRoot.$inferred === 'expanded' || expandedRoot.$inferred === 'rewrite')
|