@sap/cds-compiler 4.4.4 → 4.6.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 +88 -0
- package/bin/cdsc.js +18 -11
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +22 -0
- package/lib/api/main.js +306 -144
- package/lib/api/options.js +18 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +45 -10
- package/lib/base/messages.js +33 -16
- package/lib/base/model.js +4 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +8 -7
- package/lib/compiler/assert-consistency.js +40 -17
- package/lib/compiler/builtins.js +30 -53
- package/lib/compiler/checks.js +46 -14
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +35 -10
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +94 -35
- package/lib/compiler/shared.js +60 -33
- package/lib/compiler/tweak-assocs.js +188 -92
- package/lib/compiler/utils.js +11 -1
- package/lib/edm/annotations/edmJson.js +41 -66
- package/lib/edm/annotations/genericTranslation.js +27 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +28 -11
- package/lib/edm/edmInboundChecks.js +58 -15
- package/lib/edm/edmPreprocessor.js +12 -16
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +10 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +15 -2
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +6557 -5618
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +29 -4
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +28 -7
- package/lib/language/genericAntlrParser.js +118 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/main.d.ts +28 -3
- package/lib/main.js +3 -0
- package/lib/model/csnRefs.js +4 -1
- package/lib/model/csnUtils.js +20 -14
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +23 -22
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +47 -26
- package/lib/render/toHdbcds.js +63 -42
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +71 -117
- package/lib/render/utils/common.js +41 -6
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +57 -4
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +70 -71
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/draft/db.js +2 -16
- package/lib/transform/draft/odata.js +3 -3
- package/lib/transform/effective/associations.js +3 -5
- package/lib/transform/effective/main.js +6 -9
- package/lib/transform/forOdata.js +26 -55
- package/lib/transform/forRelationalDB.js +38 -18
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/transform/transformUtils.js +47 -34
- package/lib/transform/translateAssocsToJoins.js +45 -11
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Add tenant field MANDT to entities
|
|
2
|
+
|
|
3
|
+
// Prerequisites:
|
|
4
|
+
|
|
5
|
+
// - the input CSN is a `client` style CSN from the Core Compiler
|
|
6
|
+
// - using structure types with unmanaged associations is not supported by the
|
|
7
|
+
// Core Compiler (due to missing ON-rewrite)
|
|
8
|
+
|
|
9
|
+
// TODO entities without MANDT:
|
|
10
|
+
|
|
11
|
+
// - cache whether structure type contains (managed) association to entity with MANDT
|
|
12
|
+
// - disallow use of such a type in entity without MANDT
|
|
13
|
+
|
|
14
|
+
// Implementation remark:
|
|
15
|
+
|
|
16
|
+
// - the functions `forEachDefinition` & friends in csnUtils.js have become quite
|
|
17
|
+
// (too) general and are probably slow → not used here
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const { createMessageFunctions } = require( '../base/messages' );
|
|
22
|
+
const { traverseQuery } = require( '../model/csnRefs' );
|
|
23
|
+
|
|
24
|
+
const fieldName = 'tenant';
|
|
25
|
+
const fieldDef = {
|
|
26
|
+
key: true,
|
|
27
|
+
type: 'cds.String',
|
|
28
|
+
length: 36,
|
|
29
|
+
'@cds.api.ignore': true, // and/or $generated: 'tenant' for the full Universal CSN?
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function addTenantFields( csn, options ) {
|
|
33
|
+
const { definitions } = csn;
|
|
34
|
+
if (!definitions)
|
|
35
|
+
return csn;
|
|
36
|
+
const { error, throwWithError } = createMessageFunctions( options, 'tenant', csn );
|
|
37
|
+
|
|
38
|
+
const csnPath = [ 'definitions', '' ];
|
|
39
|
+
let projection;
|
|
40
|
+
|
|
41
|
+
for (const name in definitions) {
|
|
42
|
+
const art = definitions[name];
|
|
43
|
+
if (art?.kind !== 'entity')
|
|
44
|
+
continue;
|
|
45
|
+
csnPath[1] = name;
|
|
46
|
+
|
|
47
|
+
if (art['@cds.tenant.independent'] != null) {
|
|
48
|
+
error( null, csnPath, { anno: '@cds.tenant.independent' },
|
|
49
|
+
'Can\'t yet add annotation $(ANNO) to an entity' );
|
|
50
|
+
}
|
|
51
|
+
if (!handleElements( art ))
|
|
52
|
+
continue;
|
|
53
|
+
projection = art.query || art.projection && art;
|
|
54
|
+
if (projection)
|
|
55
|
+
traverseQuery( projection, null, null, handleQuery );
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
(csn.extensions || []).forEach( (ext, idx) => {
|
|
59
|
+
const tenant = ext.elements?.[fieldName];
|
|
60
|
+
const name = ext.annotate || ext.extend; // extend should not happen
|
|
61
|
+
if (tenant && definitions[name]?.kind === 'entity') { // TODO: ok for tenant-independent
|
|
62
|
+
error( null, [ 'extensions', idx, 'elements', 'tenant' ],
|
|
63
|
+
{ name: fieldName },
|
|
64
|
+
'Can\'t annotate element $(NAME) of a tenant-dependent entity' );
|
|
65
|
+
}
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
throwWithError();
|
|
69
|
+
return csn; // input CSN changed by side effect
|
|
70
|
+
|
|
71
|
+
function handleElements( art ) {
|
|
72
|
+
const { elements } = art;
|
|
73
|
+
if (elements[fieldName]) {
|
|
74
|
+
error( null, [ ...csnPath, 'elements', fieldName ], { name: fieldName },
|
|
75
|
+
'Can\'t add tenant field to entity having an element $(NAME)' );
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!Object.values( elements ).some( e => e.key )) {
|
|
79
|
+
error( null, csnPath, {},
|
|
80
|
+
'There must be a key in a tenant-dependent entity' );
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
handleAssociations( art );
|
|
84
|
+
art.elements = { [fieldName]: { ...fieldDef }, ...elements };
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleQuery( query ) {
|
|
89
|
+
// TODO: errors are temporary: start with simple projections only = no better
|
|
90
|
+
// message $location necessary yet
|
|
91
|
+
if (!projection) // error already reported
|
|
92
|
+
return;
|
|
93
|
+
if (query.ref) {
|
|
94
|
+
if ((query.as || implicitAs( query.ref )) === fieldName) {
|
|
95
|
+
error( null, csnPath, { name: fieldName },
|
|
96
|
+
'Can\'t have a table alias named $(NAME) in a tenant-dependent entity' );
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const select = query.SELECT || query.projection;
|
|
102
|
+
if (query.SET || query !== projection || !select?.from?.ref) {
|
|
103
|
+
error( null, csnPath, {},
|
|
104
|
+
'Can\'t add tenant columns to non-simple query entities' );
|
|
105
|
+
projection = null;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (query.projection)
|
|
110
|
+
csnPath.push( 'projection' );
|
|
111
|
+
else
|
|
112
|
+
csnPath.push( 'query', 'SELECT' );
|
|
113
|
+
|
|
114
|
+
if (select.mixin)
|
|
115
|
+
handleMixins( select.mixin );
|
|
116
|
+
if (select.excluding)
|
|
117
|
+
checkExcluding( select.excluding );
|
|
118
|
+
if (select.columns)
|
|
119
|
+
handleColumns( select.columns );
|
|
120
|
+
// TODO: for subqueries, we might need to adapt the inferred elements
|
|
121
|
+
// TODO: where exists ref -
|
|
122
|
+
// TODO: select and query clauses, especially with aggregation functions
|
|
123
|
+
handleGroupBy( select );
|
|
124
|
+
csnPath.length = 2;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function handleMixins( mixin ) {
|
|
128
|
+
csnPath.push( 'mixin', '' );
|
|
129
|
+
for (const name in mixin) {
|
|
130
|
+
csnPath[csnPath.length - 1] = name;
|
|
131
|
+
if (name !== fieldName) {
|
|
132
|
+
addToCondition( mixin[name], name );
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
error( null, csnPath, { name },
|
|
136
|
+
'Can\'t define a mixin named $(NAME) in a tenant-dependent entity' );
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
csnPath.length -= 2;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function checkExcluding( excludeList ) {
|
|
143
|
+
if (excludeList.includes( fieldName )) {
|
|
144
|
+
error( null, csnPath, { name: fieldName },
|
|
145
|
+
'Can\'t exclude $(NAME) from query source' );
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function handleGroupBy( select ) {
|
|
150
|
+
// TODO: in the future, we allow model-wise keyless views when using
|
|
151
|
+
// aggregation function, and add a GROUP BY for MANDT in this case. Now, also
|
|
152
|
+
// views with agg functions need to have a key element → it very likely
|
|
153
|
+
// already contains a GROUP BY. And anyway: if we miss to add GROUP BY MANDT,
|
|
154
|
+
// the database will complain → no safetly risk.
|
|
155
|
+
if (select.groupBy)
|
|
156
|
+
select.groupBy.unshift( { ref: [ fieldName ] } );
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function handleColumns( columns ) {
|
|
160
|
+
let specifiedKey = false;
|
|
161
|
+
csnPath.push( 'columns', -1 );
|
|
162
|
+
for (const col of columns) {
|
|
163
|
+
++csnPath[csnPath.length - 1];
|
|
164
|
+
if (col.expand || col.inline) {
|
|
165
|
+
error( null, csnPath, {},
|
|
166
|
+
'Can\'t use expand/inline in a tenant-dependent entity' );
|
|
167
|
+
}
|
|
168
|
+
if (col.key != null) // yes, also with key: false
|
|
169
|
+
specifiedKey = true;
|
|
170
|
+
if (col.cast?.on) // REDIRECTED TO with explicit ON - TODO (low prio): less $self
|
|
171
|
+
addToCondition( col.cast, col.as || implicitAs( col.ref ) );
|
|
172
|
+
}
|
|
173
|
+
csnPath.length -= 2;
|
|
174
|
+
columns.unshift( specifiedKey
|
|
175
|
+
? { key: true, ref: [ fieldName ] }
|
|
176
|
+
: { ref: [ fieldName ] } );
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function handleAssociations( elem ) {
|
|
180
|
+
const { elements } = elem;
|
|
181
|
+
if (elements) {
|
|
182
|
+
csnPath.push( 'elements', '' );
|
|
183
|
+
for (const name in elements) {
|
|
184
|
+
csnPath[csnPath.length - 1] = name;
|
|
185
|
+
handleAssociations( elements[name] );
|
|
186
|
+
}
|
|
187
|
+
csnPath.length -= 2;
|
|
188
|
+
}
|
|
189
|
+
else if (elem.target) {
|
|
190
|
+
if (elem.on) {
|
|
191
|
+
addToCondition( elem, csnPath[csnPath.length - 1] );
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
error( null, csnPath, {},
|
|
195
|
+
'Can\'t yet use managed associations in a tenant-dependent entity' );
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (elem.items) {
|
|
199
|
+
csnPath.push( 'items' );
|
|
200
|
+
handleAssociations( elem.items );
|
|
201
|
+
--csnPath.length;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function addToCondition( elem, assoc ) {
|
|
206
|
+
if (!elem.on)
|
|
207
|
+
return;
|
|
208
|
+
const withSelf = csnPath.length > 4;
|
|
209
|
+
elem.on = [
|
|
210
|
+
{ ref: [ assoc, fieldName ] }, // TODO: consider assoc name starting with '$'
|
|
211
|
+
'=',
|
|
212
|
+
{ ref: (withSelf) ? [ '$self', fieldName ] : [ fieldName ] },
|
|
213
|
+
'and',
|
|
214
|
+
// TODO: avoid (...) for standard AND-ed EQ-comparisons ?
|
|
215
|
+
{ xpr: elem.on },
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function implicitAs( ref ) {
|
|
221
|
+
const item = ref[ref.length - 1];
|
|
222
|
+
const id = (typeof item === 'string') ? item : item.id;
|
|
223
|
+
return id.substring( id.lastIndexOf('.') + 1 );
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
addTenantFields,
|
|
228
|
+
};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
const { setProp } = require('../../base/model');
|
|
16
|
-
const {
|
|
16
|
+
const { isAnnotationExpression } = require('../../compiler/builtins');
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -85,6 +85,8 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
85
85
|
const trans = transformers[name] || transformers[name.charAt(0)] || standard;
|
|
86
86
|
if (customTransformers[name])
|
|
87
87
|
customTransformers[name](node, name, node[name], csnPath, _parent, _prop);
|
|
88
|
+
else if (options.processAnnotations && customTransformers['@'] && name.charAt(0) === '@')
|
|
89
|
+
customTransformers['@'](node, name, node[name], csnPath, _parent, _prop);
|
|
88
90
|
trans( node, name, node[name], csnPath );
|
|
89
91
|
}
|
|
90
92
|
}
|
|
@@ -109,6 +111,8 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
109
111
|
const trans = transformers[name] || transformers[name.charAt(0)] || standard;
|
|
110
112
|
if (customTransformers[name])
|
|
111
113
|
customTransformers[name](node, name, node[name], csnPath, dict);
|
|
114
|
+
else if (options.processAnnotations && customTransformers['@'] && name.charAt(0) === '@')
|
|
115
|
+
customTransformers['@'](node, name, node[name], csnPath, dict);
|
|
112
116
|
trans( node, name, node[name], csnPath );
|
|
113
117
|
}
|
|
114
118
|
csnPath.pop();
|
|
@@ -144,14 +148,19 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
|
|
|
144
148
|
*/
|
|
145
149
|
function annotation( _parent, _prop, node ) {
|
|
146
150
|
if (options.processAnnotations) {
|
|
147
|
-
if (
|
|
151
|
+
if (isAnnotationExpression(node)) {
|
|
148
152
|
standard(_parent, _prop, node);
|
|
149
153
|
}
|
|
150
154
|
else if (node && typeof node === 'object') {
|
|
151
155
|
csnPath.push(_prop);
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
annotation( node,
|
|
157
|
+
if (Array.isArray(node)) {
|
|
158
|
+
node.forEach( (n, i) => annotation( node, i, n ) );
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
for (const name of Object.getOwnPropertyNames( node ))
|
|
162
|
+
annotation( node, name, node[name] );
|
|
163
|
+
}
|
|
155
164
|
|
|
156
165
|
csnPath.pop();
|
|
157
166
|
}
|
|
@@ -287,7 +296,51 @@ function applyTransformationsOnDictionary( dictionary, customTransformers = {},
|
|
|
287
296
|
return applyTransformationsInternal(dictionary, null, customTransformers, [], { directDict: true, ...options }, path);
|
|
288
297
|
}
|
|
289
298
|
|
|
299
|
+
/**
|
|
300
|
+
* transformExpression is a lightweight version of applyTransformations
|
|
301
|
+
* used primarily to transform annotation expressions.
|
|
302
|
+
* If propName is undefined, all properties of parent are transformed.
|
|
303
|
+
* @param {object} parent Start node
|
|
304
|
+
* @param {string} propName Start at specific property of parent
|
|
305
|
+
* @param {object} transformers Map of callback functions
|
|
306
|
+
* @param {CSN.Path} path Path to parent
|
|
307
|
+
* @returns {object} transformed node
|
|
308
|
+
*/
|
|
309
|
+
function transformExpression( parent, propName, transformers, path = [] ) {
|
|
310
|
+
if (propName != null) {
|
|
311
|
+
const child = parent[propName];
|
|
312
|
+
if (!child || typeof child !== 'object' ||
|
|
313
|
+
!{}.propertyIsEnumerable.call( parent, propName ))
|
|
314
|
+
return parent;
|
|
315
|
+
|
|
316
|
+
path = [ ...path, propName ];
|
|
317
|
+
if (Array.isArray(child)) {
|
|
318
|
+
child.forEach( (n, i) => transformExpression( child, i, transformers, path ) );
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
for (const cpn of Object.getOwnPropertyNames( child )) {
|
|
322
|
+
const ct = transformers[cpn];
|
|
323
|
+
if (ct) {
|
|
324
|
+
const ppn = propName;
|
|
325
|
+
if (Array.isArray(ct))
|
|
326
|
+
ct.forEach(cti => cti(child, cpn, child[cpn], path, parent, ppn));
|
|
327
|
+
|
|
328
|
+
else
|
|
329
|
+
ct(child, cpn, child[cpn], path, parent, ppn);
|
|
330
|
+
}
|
|
331
|
+
transformExpression(child, cpn, transformers, path);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
for (propName of Object.getOwnPropertyNames( parent ))
|
|
337
|
+
transformExpression( parent, propName, transformers, path );
|
|
338
|
+
}
|
|
339
|
+
return parent;
|
|
340
|
+
}
|
|
341
|
+
|
|
290
342
|
module.exports = {
|
|
343
|
+
transformExpression,
|
|
291
344
|
applyTransformations,
|
|
292
345
|
applyTransformationsOnNonDictionary,
|
|
293
346
|
applyTransformationsOnDictionary,
|
|
@@ -16,12 +16,12 @@ const { pathName } = require('../../compiler/utils');
|
|
|
16
16
|
*
|
|
17
17
|
* @param {CSN.Model} csn Overall CSN model
|
|
18
18
|
* @param {CSN.Options} options Options
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {Function} info Message function for info
|
|
19
|
+
* @param {object} messageFunctions Message functions (error(), info(), …)
|
|
21
20
|
* @returns {Function} forEachDefinition callback
|
|
22
21
|
*/
|
|
23
|
-
function processAssertUnique( csn, options,
|
|
24
|
-
const { resolvePath, flattenPath } = getTransformers(csn, options);
|
|
22
|
+
function processAssertUnique( csn, options, messageFunctions ) {
|
|
23
|
+
const { resolvePath, flattenPath } = getTransformers(csn, options, messageFunctions);
|
|
24
|
+
const { error, info } = messageFunctions;
|
|
25
25
|
|
|
26
26
|
return handleAssertUnique;
|
|
27
27
|
/**
|
|
@@ -18,6 +18,7 @@ const { forEach } = require('../../utils/objectUtils');
|
|
|
18
18
|
* @returns {import('../../model/csnUtils').genericCallback} callback for forEachDefinition
|
|
19
19
|
*/
|
|
20
20
|
function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimiter, doA2J = true ) {
|
|
21
|
+
let prepend$self = false;
|
|
21
22
|
return transformSelfInBacklinks;
|
|
22
23
|
/**
|
|
23
24
|
* @param {CSN.Artifact} artifact
|
|
@@ -26,12 +27,15 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
26
27
|
* @param {CSN.Path} path
|
|
27
28
|
*/
|
|
28
29
|
function transformSelfInBacklinks( artifact, artifactName, dummy, path ) {
|
|
30
|
+
prepend$self = false;
|
|
29
31
|
// Fixme: For toHana mixins must be transformed, for toSql -d hana
|
|
30
32
|
// mixin elements must be transformed, why can't toSql also use mixins?
|
|
31
33
|
if (options.transformation === 'effective' && artifact.elements || artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
|
|
32
34
|
processDict(artifact.elements, path.concat([ 'elements' ]));
|
|
33
|
-
if (artifact.query?.SELECT?.mixin)
|
|
35
|
+
if (artifact.query?.SELECT?.mixin) {
|
|
36
|
+
prepend$self = options.transformation === 'effective';
|
|
34
37
|
processDict(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
|
|
38
|
+
}
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
41
|
* Loop over the dict and start the processing.
|
|
@@ -237,6 +241,10 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
237
241
|
{ ref: k.ref },
|
|
238
242
|
];
|
|
239
243
|
|
|
244
|
+
if (prepend$self)
|
|
245
|
+
a[1].ref = [ '$self', ...a[1].ref ];
|
|
246
|
+
|
|
247
|
+
|
|
240
248
|
conditions.push([ a[0], '=', a[1] ]);
|
|
241
249
|
});
|
|
242
250
|
|
|
@@ -273,11 +281,15 @@ function getBacklinkTransformer( csnUtils, messageFunctions, options, pathDelimi
|
|
|
273
281
|
// we are in the "path" from the forwarding assoc => need to remove the first part of the path
|
|
274
282
|
if (ref[0] === assocName) {
|
|
275
283
|
ref.shift();
|
|
284
|
+
if (prepend$self)
|
|
285
|
+
ref.unshift('$self');
|
|
276
286
|
}
|
|
277
287
|
else if (ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
|
|
278
288
|
// We could also have a $self in front of the assoc name - so we would need to shift twice
|
|
279
289
|
ref.shift();
|
|
280
290
|
ref.shift();
|
|
291
|
+
if (prepend$self)
|
|
292
|
+
ref.unshift('$self');
|
|
281
293
|
}
|
|
282
294
|
else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
|
|
283
295
|
ref.unshift(elemName);
|
|
@@ -106,7 +106,7 @@ function getPersistenceTableProcessor( csn, options, messageFunctions ) {
|
|
|
106
106
|
const { error } = messageFunctions;
|
|
107
107
|
const {
|
|
108
108
|
recurseElements,
|
|
109
|
-
} = transformUtils.getTransformers(csn, options, '_');
|
|
109
|
+
} = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
110
110
|
|
|
111
111
|
return handleQueryish;
|
|
112
112
|
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
const { implicitAs, columnAlias, pathId } = require('../../model/csnRefs');
|
|
11
11
|
const { setProp } = require('../../base/model');
|
|
12
12
|
const { forEach } = require('../../utils/objectUtils');
|
|
13
|
+
const { killNonrequiredAnno } = require('./killAnnotations');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* For keys, columns, groupBy and orderBy, expand structured things.
|
|
@@ -27,7 +28,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
27
28
|
|
|
28
29
|
rewriteExpandInline();
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
const transformers = {
|
|
31
32
|
keys: (parent, name, keys, path) => {
|
|
32
33
|
parent.keys = expand(keys, path.concat('keys'), true);
|
|
33
34
|
},
|
|
@@ -53,7 +54,13 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
53
54
|
orderBy: (parent, name, orderBy, path) => {
|
|
54
55
|
parent.orderBy = expand(orderBy, path.concat('orderBy'));
|
|
55
56
|
},
|
|
56
|
-
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// To not have a whole model loop for such a "small" thing, we kill all non-sql-backend relevant annotations here
|
|
60
|
+
if (options.transformation === 'sql' || options.transformation === 'hdbcds')
|
|
61
|
+
transformers['@'] = killNonrequiredAnno;
|
|
62
|
+
|
|
63
|
+
applyTransformations(csn, transformers, [], iterateOptions);
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
66
|
* Turn .expand/.inline into normal refs. @cds.persistence.skip .expand with to-many (and all transitive views).
|
|
@@ -529,7 +536,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
529
536
|
else
|
|
530
537
|
newThing.push(col);
|
|
531
538
|
}
|
|
532
|
-
else if (col.ref && col.$scope === '$magic' && ( col.ref[0] === '$user' || col.ref[0] === '$session' ) && !col.as) {
|
|
539
|
+
else if (col.ref && col.$scope === '$magic' && ( col.ref[0] === '$user' || col.ref[0] === '$tenant' || col.ref[0] === '$session' ) && !col.as) {
|
|
533
540
|
col.as = implicitAs(col.ref);
|
|
534
541
|
newThing.push(col);
|
|
535
542
|
}
|
|
@@ -618,6 +625,20 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
618
625
|
setProp(obj, '$implicitAlias', true);
|
|
619
626
|
}
|
|
620
627
|
|
|
628
|
+
// If our column/thing was cast to a structured type, we need to keep the "cast" insync with the
|
|
629
|
+
// flattened out leaf elements that we turn the ref into
|
|
630
|
+
if (obj.cast?.type) {
|
|
631
|
+
const addedRef = currentRef.slice(root.ref.length);
|
|
632
|
+
if (addedRef.length > 0) {
|
|
633
|
+
// Decouple from other leafs
|
|
634
|
+
obj.cast = { ...obj.cast };
|
|
635
|
+
if (!obj.cast.type.ref)
|
|
636
|
+
obj.cast.type = { ref: [ obj.cast.type ] };
|
|
637
|
+
|
|
638
|
+
obj.cast.type.ref = [ ...obj.cast.type.ref, ...addedRef ];
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
621
642
|
// The Java runtime, as of 2023-09-13, assumes that for _simple projections_, all references
|
|
622
643
|
// are relative to the query source. To avoid breaking that assumption unless necessary,
|
|
623
644
|
// we only add the table alias if:
|