@sap/cds 5.9.2 → 5.9.5
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 +44 -0
- package/lib/compile/for/drafts.js +1 -1
- package/lib/index.js +1 -1
- package/lib/serve/Service-methods.js +47 -1
- package/libx/_runtime/auth/index.js +16 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/BatchRequestListBuilder.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -3
- package/libx/_runtime/common/aspects/utils.js +8 -2
- package/libx/_runtime/common/composition/data.js +22 -13
- package/libx/_runtime/common/composition/delete.js +14 -12
- package/libx/_runtime/common/generic/auth/expand.js +1 -0
- package/libx/_runtime/common/generic/input.js +1 -0
- package/libx/_runtime/common/generic/put.js +1 -0
- package/libx/_runtime/common/utils/cqn.js +5 -10
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +39 -75
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +28 -8
- package/libx/_runtime/common/utils/path.js +3 -3
- package/libx/_runtime/common/utils/require.js +2 -1
- package/libx/_runtime/common/utils/resolveView.js +3 -0
- package/libx/_runtime/common/utils/structured.js +6 -1
- package/libx/_runtime/db/Service.js +10 -0
- package/libx/_runtime/db/expand/expand-v2.js +13 -5
- package/libx/_runtime/db/expand/expandCQNToJoin.js +56 -26
- package/libx/_runtime/db/utils/generateAliases.js +9 -0
- package/libx/_runtime/extensibility/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/fiori/generic/read.js +83 -31
- package/libx/_runtime/fiori/generic/readOverDraft.js +22 -13
- package/libx/_runtime/fiori/utils/handler.js +3 -0
- package/libx/_runtime/fiori/utils/where.js +38 -25
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/search2cqn4sql.js +4 -1
- package/libx/_runtime/remote/Service.js +3 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +19 -12
- package/package.json +1 -1
|
@@ -17,14 +17,14 @@ const getPathFromRef = ref => {
|
|
|
17
17
|
/*
|
|
18
18
|
* returns the target entity for the given path
|
|
19
19
|
*/
|
|
20
|
-
const getEntityFromPath = (path,
|
|
21
|
-
let current = { elements:
|
|
20
|
+
const getEntityFromPath = (path, def) => {
|
|
21
|
+
let current = def.definitions ? { elements: def.definitions } : def
|
|
22
22
|
path = typeof path === 'string' ? cds.parse.path(path) : path
|
|
23
23
|
const segments = [...path.ref]
|
|
24
24
|
while (segments.length) {
|
|
25
25
|
const segment = segments.shift()
|
|
26
26
|
current = current.elements[segment.id || segment]
|
|
27
|
-
if (current && current.target) current =
|
|
27
|
+
if (current && current.target) current = current._target
|
|
28
28
|
}
|
|
29
29
|
return current
|
|
30
30
|
}
|
|
@@ -243,6 +243,9 @@ const _newEntries = (entries = [], transition, service) =>
|
|
|
243
243
|
|
|
244
244
|
const _newWhere = (where = [], transition, tableName, alias, isSubselect = false) => {
|
|
245
245
|
const newWhere = where.map(whereElement => {
|
|
246
|
+
if (whereElement.xpr) {
|
|
247
|
+
return { xpr: _newWhere(whereElement.xpr, transition, tableName, alias, isSubselect) }
|
|
248
|
+
}
|
|
246
249
|
const newWhereElement = { ...whereElement }
|
|
247
250
|
if (!whereElement.ref && !whereElement.SELECT && !whereElement.func) return whereElement
|
|
248
251
|
if (whereElement.SELECT && whereElement.SELECT.where) {
|
|
@@ -47,7 +47,8 @@ const _flattenStructuredInExpand = (column, { _target: expandedEntity }) => {
|
|
|
47
47
|
if (orderBy) {
|
|
48
48
|
column.orderBy = orderBy
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
const columnWhere = flattenStructuredWhereHaving(column.where, expandedEntity)
|
|
51
|
+
if (columnWhere) column.where = columnWhere
|
|
51
52
|
column.expand = column.expand.filter(e => !e.ref || !toBeDeleted.includes(e.ref[e.ref.length - 1]))
|
|
52
53
|
column.expand.push(...flattenedElements)
|
|
53
54
|
}
|
|
@@ -205,6 +206,10 @@ const flattenStructuredWhereHaving = (filterArray, csnEntity, model) => {
|
|
|
205
206
|
|
|
206
207
|
const newFilterArray = []
|
|
207
208
|
for (let i = 0; i < filterArray.length; i++) {
|
|
209
|
+
if (filterArray[i].xpr) {
|
|
210
|
+
newFilterArray.push({ xpr: flattenStructuredWhereHaving(filterArray[i].xpr, csnEntity, model) })
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
208
213
|
if (filterArray[i + 1] in OPERATIONS_MAP) {
|
|
209
214
|
const refElement = filterArray[i].ref ? filterArray[i] : filterArray[i + 2]
|
|
210
215
|
|
|
@@ -38,6 +38,16 @@ class DatabaseService extends cds.Service {
|
|
|
38
38
|
// REVISIT: how to generic handler registration?
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** Database services don't support custom-defined operations */
|
|
42
|
+
operations() {
|
|
43
|
+
return []
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Database services don't support custom-defined events */
|
|
47
|
+
events() {
|
|
48
|
+
return []
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
/*
|
|
42
52
|
* tx
|
|
43
53
|
*/
|
|
@@ -79,16 +79,24 @@ const _autoExpandNavsAndAttachToResult = async (entity, previousResult, depth, o
|
|
|
79
79
|
return previousResult
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const _fkForOnCOnd = (onCond, requiredFks) => {
|
|
83
|
+
for (const ele of onCond) {
|
|
84
|
+
if (ele.xpr) {
|
|
85
|
+
_fkForOnCOnd(ele.xpr, requiredFks)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (ele.ref && ele.ref[0] === 'parent') {
|
|
89
|
+
requiredFks.add(ele.ref.slice(1).join('_'))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
const _foreignKeysOfTopLevelNavs = (entity, options) => {
|
|
83
95
|
const requiredFks = new Set()
|
|
84
96
|
for (const nav in entity._associations) {
|
|
85
97
|
if (options.onlyCompositions && entity._associations[nav]._isAssociationEffective) continue
|
|
86
98
|
const onCond = entity._relations[nav].join('child', 'parent')
|
|
87
|
-
|
|
88
|
-
if (ele.ref && ele.ref[0] === 'parent') {
|
|
89
|
-
requiredFks.add(ele.ref.slice(1).join('_'))
|
|
90
|
-
}
|
|
91
|
-
}
|
|
99
|
+
_fkForOnCOnd(onCond, requiredFks)
|
|
92
100
|
}
|
|
93
101
|
return [...requiredFks]
|
|
94
102
|
}
|
|
@@ -395,6 +395,9 @@ class JoinCQNFromExpanded {
|
|
|
395
395
|
*/
|
|
396
396
|
_adaptWhereSELECT(aliasedTable, where, tableAlias) {
|
|
397
397
|
return where.map(element => {
|
|
398
|
+
if (element.xpr) {
|
|
399
|
+
return { xpr: this._adaptWhereSELECT(aliasedTable, element.xpr, tableAlias) }
|
|
400
|
+
}
|
|
398
401
|
return this._elementAliasNeedsReplacement(element, aliasedTable)
|
|
399
402
|
? Object.assign({}, element, { ref: [tableAlias, element.ref[1]] })
|
|
400
403
|
: element
|
|
@@ -870,13 +873,21 @@ class JoinCQNFromExpanded {
|
|
|
870
873
|
const aliases = this._getAliases(subSelectColumns)
|
|
871
874
|
const on = entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
|
|
872
875
|
|
|
876
|
+
this._adjustAliases(on, aliases)
|
|
877
|
+
|
|
878
|
+
return on
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
_adjustAliases(on, aliases) {
|
|
873
882
|
for (const element of on) {
|
|
883
|
+
if (element.xpr) {
|
|
884
|
+
this._adjustAliases(element.xpr, aliases)
|
|
885
|
+
continue
|
|
886
|
+
}
|
|
874
887
|
if (element.ref && aliases[element.ref[0]] && aliases[element.ref[0]][element.ref[1]]) {
|
|
875
888
|
element.ref[1] = aliases[element.ref[0]][element.ref[1]]
|
|
876
889
|
}
|
|
877
890
|
}
|
|
878
|
-
|
|
879
|
-
return on
|
|
880
891
|
}
|
|
881
892
|
|
|
882
893
|
_addJoinKeyColumnsToUnion(args, on, parentAlias) {
|
|
@@ -899,23 +910,27 @@ class JoinCQNFromExpanded {
|
|
|
899
910
|
}
|
|
900
911
|
|
|
901
912
|
_addColumns(columns, on, parentAlias, withAlias = false) {
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
913
|
+
for (const entry of on) {
|
|
914
|
+
if (entry.xpr) {
|
|
915
|
+
this._addColumns(columns, entry.xpr, parentAlias, withAlias)
|
|
916
|
+
continue
|
|
917
|
+
}
|
|
918
|
+
if (
|
|
919
|
+
entry.ref &&
|
|
920
|
+
entry.ref[0] === parentAlias &&
|
|
921
|
+
!columns.some(column => column.ref && column.ref[column.ref.length - 1] === entry.ref[1])
|
|
922
|
+
) {
|
|
923
|
+
columns.push(
|
|
924
|
+
withAlias
|
|
925
|
+
? { ref: [parentAlias, entry.ref[1]], as: `${parentAlias}_${entry.ref[1]}` }
|
|
926
|
+
: { ref: [entry.ref[1]] }
|
|
908
927
|
)
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
withAlias ? { ref: [parentAlias, entry.ref[1]], as: `${parentAlias}_${entry.ref[1]}` } : { ref: [entry.ref[1]] }
|
|
912
|
-
)
|
|
913
|
-
if (keyColumns.length === 0) return
|
|
914
|
-
columns.push(...keyColumns)
|
|
928
|
+
}
|
|
929
|
+
}
|
|
915
930
|
}
|
|
916
931
|
|
|
917
932
|
/**
|
|
918
|
-
* Add
|
|
933
|
+
* Add a unique alias to each column, to avoid ambiguity.
|
|
919
934
|
* Add this information to the post process config.
|
|
920
935
|
*
|
|
921
936
|
* @param {object} column
|
|
@@ -1318,6 +1333,26 @@ class JoinCQNFromExpanded {
|
|
|
1318
1333
|
return newObj
|
|
1319
1334
|
}
|
|
1320
1335
|
|
|
1336
|
+
_getFilterColumns(readToOneCQN, on, parentAlias) {
|
|
1337
|
+
const columns = []
|
|
1338
|
+
const outerColumns = []
|
|
1339
|
+
|
|
1340
|
+
for (const entry of on) {
|
|
1341
|
+
if (entry.xpr) {
|
|
1342
|
+
const { columns: cols, outerColumns: outerCols } = this._getFilterColumns(readToOneCQN, entry.xpr, parentAlias)
|
|
1343
|
+
columns.push(...cols)
|
|
1344
|
+
outerColumns.push(...outerCols)
|
|
1345
|
+
continue
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof entry === 'object' && entry.ref && entry.ref[0] === 'filterExpand') {
|
|
1348
|
+
columns.push(this._getColumnObjectForFilterExpand(readToOneCQN, parentAlias, entry.ref[1]))
|
|
1349
|
+
outerColumns.push({ ref: [entry.ref[1]] })
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
return { columns, outerColumns }
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1321
1356
|
/**
|
|
1322
1357
|
* Reduce column list to column(s) needed to merge the result into one.
|
|
1323
1358
|
*
|
|
@@ -1329,22 +1364,13 @@ class JoinCQNFromExpanded {
|
|
|
1329
1364
|
* @private
|
|
1330
1365
|
*/
|
|
1331
1366
|
_getFilterExpandCQN(readToOneCQN, on, parentAlias, keyObject) {
|
|
1332
|
-
const columns = []
|
|
1333
|
-
|
|
1334
|
-
const outerColumns = []
|
|
1335
|
-
|
|
1336
|
-
for (const entry of on) {
|
|
1337
|
-
if (typeof entry === 'object' && entry.ref && entry.ref[0] === 'filterExpand') {
|
|
1338
|
-
columns.push(this._getColumnObjectForFilterExpand(readToOneCQN, parentAlias, entry.ref[1]))
|
|
1339
|
-
outerColumns.push({ ref: [entry.ref[1]] })
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
1367
|
const keys = Object.keys(keyObject).filter(
|
|
1344
1368
|
key =>
|
|
1345
1369
|
key !== 'IsActiveEntity' && !keyObject[key].is2one && !keyObject[key].is2many && !keyObject[key]._isStructured
|
|
1346
1370
|
)
|
|
1347
1371
|
|
|
1372
|
+
const { columns, outerColumns } = this._getFilterColumns(readToOneCQN, on, parentAlias)
|
|
1373
|
+
|
|
1348
1374
|
for (const key of keys) {
|
|
1349
1375
|
if (!columns.map(entry => entry.as).includes(key)) {
|
|
1350
1376
|
columns.push(this._getColumnObjectForFilterExpand(readToOneCQN, parentAlias, key))
|
|
@@ -1455,6 +1481,10 @@ class JoinCQNFromExpanded {
|
|
|
1455
1481
|
this._addColumNames(entity, parentAlias, columnNames)
|
|
1456
1482
|
|
|
1457
1483
|
for (const entry of on) {
|
|
1484
|
+
if (entry.xpr) {
|
|
1485
|
+
columns.push(...this._getJoinColumnsFromOnAddToMapping(mapping, parentAlias, entry.xpr, entity))
|
|
1486
|
+
continue
|
|
1487
|
+
}
|
|
1458
1488
|
if (typeof entry === 'object' && entry.ref && entry.ref[0] !== 'filterExpand') {
|
|
1459
1489
|
const as = entry.ref.join('_')
|
|
1460
1490
|
columns.push({
|
|
@@ -20,6 +20,11 @@ const _redirectXpr = (xpr, aliasMap) => {
|
|
|
20
20
|
return
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
if (element.xpr) {
|
|
24
|
+
_redirectXpr(element.xpr, aliasMap)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
if (element.func) {
|
|
24
29
|
_redirectXpr(element.args, aliasMap)
|
|
25
30
|
return
|
|
@@ -98,6 +103,10 @@ const generateAliases = query => {
|
|
|
98
103
|
|
|
99
104
|
const _addParentAlias = (where, alias) => {
|
|
100
105
|
where.forEach(e => {
|
|
106
|
+
if (e.xpr) {
|
|
107
|
+
_addParentAlias(e.xpr, alias)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
101
110
|
if (e.ref && e.ref[0].match(PARENT_ALIAS_REGEX)) {
|
|
102
111
|
e.ref[0] = alias
|
|
103
112
|
}
|
|
@@ -21,6 +21,7 @@ const _processorFn = ({ row, key }) => {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function transformExtendedFieldsCREATE(req) {
|
|
24
|
+
if (!req.query) return // FIXME: the code below expects req.query to be defined
|
|
24
25
|
if (!req.target) return
|
|
25
26
|
if (!req.query.INSERT.entries) return // REVISIT: breaks at cds.deploy -> should anyways not kick in during cds.deploy
|
|
26
27
|
|
|
@@ -19,9 +19,18 @@ const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively
|
|
|
19
19
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
20
20
|
const { adaptStreamCQN } = require('../../cds-services/adapter/odata-v4/utils/stream')
|
|
21
21
|
|
|
22
|
+
const _findSubselect = where => {
|
|
23
|
+
return where.find((e, i) => {
|
|
24
|
+
if (e.xpr) {
|
|
25
|
+
return _findSubselect(e.xpr)
|
|
26
|
+
}
|
|
27
|
+
return e.SELECT && where[i - 1] === 'exists'
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
const _findRootSubSelectFor = query => {
|
|
23
32
|
if (query.SELECT.where) {
|
|
24
|
-
const subSelect =
|
|
33
|
+
const subSelect = _findSubselect(query.SELECT.where)
|
|
25
34
|
return subSelect ? _findRootSubSelectFor(subSelect) : query
|
|
26
35
|
}
|
|
27
36
|
return query
|
|
@@ -422,8 +431,8 @@ const _allActive = (req, columns, model) => {
|
|
|
422
431
|
cqn.leftJoin(ensureDraftsSuffix(table.ref[0]) + ' as drafts').on(`${table.as}.${ids[0]} = drafts.${ids[0]}`)
|
|
423
432
|
|
|
424
433
|
for (let i = 1; i < ids.length; i++) {
|
|
425
|
-
//
|
|
426
|
-
cqn.and(
|
|
434
|
+
// this 'and' belongs to the join condition and is not a where and
|
|
435
|
+
cqn.and({ ref: [table.as, ids[i]] }, '=', { ref: ['drafts', ids[i]] })
|
|
427
436
|
}
|
|
428
437
|
}
|
|
429
438
|
|
|
@@ -557,27 +566,38 @@ const _alignAliasForUnion = (table, as, select) => {
|
|
|
557
566
|
return select
|
|
558
567
|
}
|
|
559
568
|
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
if (where[idx + 1] && where[idx + 1] === '=' && isTargetRef(where[idx + 2])) {
|
|
571
|
-
if (links.length) links.push('and')
|
|
572
|
-
links.push(el, '=', where[idx + 2])
|
|
573
|
-
}
|
|
569
|
+
const isTargetRef = (el, targetAlias) => targetAlias && el.ref && el.ref.length > 1 && el.ref[0] === targetAlias
|
|
570
|
+
|
|
571
|
+
const _joinFromWhere = (where, parentAlias, targetAlias) => {
|
|
572
|
+
return where.reduce((links, el, idx, where) => {
|
|
573
|
+
if (el.xpr) {
|
|
574
|
+
const result = _joinFromWhere(el.xpr, parentAlias, targetAlias)
|
|
575
|
+
if (result.length) {
|
|
576
|
+
if (links.length) links.push('and')
|
|
577
|
+
links.push(...result)
|
|
574
578
|
}
|
|
575
579
|
return links
|
|
576
|
-
}
|
|
577
|
-
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (el.ref && el.ref[0] === parentAlias && el.ref[el.ref.length - 1] !== 'IsActiveEntity') {
|
|
583
|
+
if (where[idx - 1] && where[idx - 1] === '=' && isTargetRef(where[idx - 2], targetAlias)) {
|
|
584
|
+
if (links.length) links.push('and')
|
|
585
|
+
links.push(el, '=', where[idx - 2])
|
|
586
|
+
}
|
|
587
|
+
if (where[idx + 1] && where[idx + 1] === '=' && isTargetRef(where[idx + 2], targetAlias)) {
|
|
588
|
+
if (links.length) links.push('and')
|
|
589
|
+
links.push(el, '=', where[idx + 2])
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return links
|
|
593
|
+
}, [])
|
|
578
594
|
}
|
|
579
595
|
|
|
580
|
-
const
|
|
596
|
+
const _findJoinInQuery = (query, parentAlias) => {
|
|
597
|
+
const targetAlias = query.SELECT.from.as
|
|
598
|
+
if (query.SELECT && query.SELECT.where) return _joinFromWhere(query.SELECT.where, parentAlias, targetAlias)
|
|
599
|
+
return []
|
|
600
|
+
}
|
|
581
601
|
|
|
582
602
|
const _isDraftField = element => element.ref && element.ref.length > 1 && element.ref[0] === 'DraftAdministrativeData'
|
|
583
603
|
|
|
@@ -597,6 +617,10 @@ const _isLogicalFunction = (where, index) => {
|
|
|
597
617
|
const _getWhereForActive = where => {
|
|
598
618
|
const activeWhere = []
|
|
599
619
|
for (let i = 0; i < where.length; i++) {
|
|
620
|
+
if (where[i].xpr) {
|
|
621
|
+
activeWhere.push({ xpr: _getWhereForActive(where[i].xpr) })
|
|
622
|
+
continue
|
|
623
|
+
}
|
|
600
624
|
if (_isDraftField(where[i])) {
|
|
601
625
|
activeWhere.push({ val: null })
|
|
602
626
|
} else if (_functionContainsDraftField(where[i])) {
|
|
@@ -721,10 +745,20 @@ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
|
|
|
721
745
|
const draftAdminAlias = _isDraftAdminScenario(req) && req.query.SELECT.from.as
|
|
722
746
|
const params = [...req.params].reverse()
|
|
723
747
|
const _getSiblingQueryFromWhere = (query, queryIndex, parentQuery) => {
|
|
724
|
-
if (query.SELECT && query.SELECT.where) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
748
|
+
if (query.SELECT && query.SELECT.where && queryIndex > 0) {
|
|
749
|
+
for (let i = 0; i < query.SELECT.where.length; i++) {
|
|
750
|
+
if (query.SELECT.where[i].xpr && queryIndex > 0) {
|
|
751
|
+
const sibilingQueryFromWhere = _getSiblingQueryFromWhere(
|
|
752
|
+
{ SELECT: { where: query.SELECT.where[i].xpr } },
|
|
753
|
+
queryIndex - 1,
|
|
754
|
+
query
|
|
755
|
+
)
|
|
756
|
+
if (sibilingQueryFromWhere) return sibilingQueryFromWhere
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (query.SELECT.where[i] === 'exists' && queryIndex > 0) {
|
|
760
|
+
return _getSiblingQueryFromWhere(query.SELECT.where[i + 1], queryIndex - 1, query)
|
|
761
|
+
}
|
|
728
762
|
}
|
|
729
763
|
}
|
|
730
764
|
const target = { name: query.SELECT.from.ref[0].id || query.SELECT.from.ref[0], as: query.SELECT.from.as }
|
|
@@ -741,9 +775,16 @@ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
|
|
|
741
775
|
return _getSiblingQueryFromWhere(req.query, siblingIndex)
|
|
742
776
|
}
|
|
743
777
|
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
778
|
+
const _replaceWhereExists = (query, _siblingIndex, siblingCQN) => {
|
|
779
|
+
if (query.SELECT && query.SELECT.where) {
|
|
780
|
+
for (let i = 0; i < query.SELECT.where.length; i++) {
|
|
781
|
+
const whereElement = query.SELECT.where[i]
|
|
782
|
+
if (whereElement.xpr) {
|
|
783
|
+
const res = _replaceWhereExists({ SELECT: { where: whereElement.xpr } }, _siblingIndex, siblingCQN)
|
|
784
|
+
if (res) return res
|
|
785
|
+
continue
|
|
786
|
+
}
|
|
787
|
+
|
|
747
788
|
const indexExists = query.SELECT.where.indexOf('exists')
|
|
748
789
|
if (indexExists > -1) {
|
|
749
790
|
if (_siblingIndex > 0) return _replaceWhereExists(query.SELECT.where[indexExists + 1], _siblingIndex - 1)
|
|
@@ -751,7 +792,10 @@ const _mergeSiblingIntoCQN = (cqn, { cqn: siblingCQN }, siblingIndex) => {
|
|
|
751
792
|
}
|
|
752
793
|
}
|
|
753
794
|
}
|
|
754
|
-
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const _mergeSiblingIntoCQN = (cqn, { cqn: siblingCQN }, siblingIndex) => {
|
|
798
|
+
return _replaceWhereExists(cqn, siblingIndex, siblingCQN)
|
|
755
799
|
}
|
|
756
800
|
|
|
757
801
|
const _getDraftDoc = (req, draftName, draftWhere) => {
|
|
@@ -796,6 +840,11 @@ const _getOrderByEnrichedColumns = (orderBy, columns, entity) => {
|
|
|
796
840
|
|
|
797
841
|
const _replaceDraftAlias = where => {
|
|
798
842
|
where.forEach(element => {
|
|
843
|
+
if (element.xpr) {
|
|
844
|
+
_replaceDraftAlias(element.xpr)
|
|
845
|
+
return
|
|
846
|
+
}
|
|
847
|
+
|
|
799
848
|
if (_isDraftField(element)) {
|
|
800
849
|
element.ref[0] = 'filterAdmin'
|
|
801
850
|
}
|
|
@@ -981,7 +1030,10 @@ const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, column
|
|
|
981
1030
|
_isValidDraftOfWhichIAmOwner(draftParameters.isActiveEntity) && _draftOfWhichIAmOwner(req, draftWhere, columns)
|
|
982
1031
|
|
|
983
1032
|
const _draftInSubSelect = (where, req) => {
|
|
984
|
-
return where.some(({ SELECT }) => {
|
|
1033
|
+
return where.some(({ SELECT, xpr }) => {
|
|
1034
|
+
if (xpr) {
|
|
1035
|
+
return _draftInSubSelect(xpr, req)
|
|
1036
|
+
}
|
|
985
1037
|
if (SELECT && SELECT.where) {
|
|
986
1038
|
const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
|
|
987
1039
|
if (isActiveEntity) {
|
|
@@ -1022,7 +1074,7 @@ const _generateCQN = (originalFrom, req, columns, model) => {
|
|
|
1022
1074
|
return _draftAdminTable(req)
|
|
1023
1075
|
}
|
|
1024
1076
|
|
|
1025
|
-
if (!req.query.SELECT.where
|
|
1077
|
+
if (!req.query.SELECT.where) {
|
|
1026
1078
|
return _allActive(req, columns, model)
|
|
1027
1079
|
}
|
|
1028
1080
|
|
|
@@ -1032,7 +1084,7 @@ const _generateCQN = (originalFrom, req, columns, model) => {
|
|
|
1032
1084
|
if (
|
|
1033
1085
|
draftParameters.isActiveEntity &&
|
|
1034
1086
|
_isTrue(draftParameters.isActiveEntity.value.val) &&
|
|
1035
|
-
!draftParameters.siblingIsActive &&
|
|
1087
|
+
!(draftParameters.siblingIsActive && draftParameters.siblingIsActive.value.val === null) &&
|
|
1036
1088
|
!draftParameters.hasDraftEntity
|
|
1037
1089
|
) {
|
|
1038
1090
|
return _allActive(req, columns, model)
|
|
@@ -5,18 +5,14 @@ const { readAndDeleteKeywords } = require('../utils/where')
|
|
|
5
5
|
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
6
6
|
const { isActiveEntityRequested } = require('../../../_runtime/fiori/utils/where')
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cqnDraft.where(whereDraft)
|
|
8
|
+
const _modifyWhere = (where, context) => {
|
|
9
|
+
for (let i = 0; i < where.length; i++) {
|
|
10
|
+
const element = where[i]
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < cqnDraft.SELECT.where.length; i++) {
|
|
19
|
-
const element = cqnDraft.SELECT.where[i]
|
|
12
|
+
if (element.xpr) {
|
|
13
|
+
_modifyWhere(element.xpr, context)
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
20
16
|
|
|
21
17
|
if (element.SELECT) {
|
|
22
18
|
const subCqnDraft = SELECT.from(
|
|
@@ -27,12 +23,25 @@ const _modifyCQN = (cqnDraft, where, context) => {
|
|
|
27
23
|
[1]
|
|
28
24
|
)
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
where[i] = subCqnDraft
|
|
31
27
|
_modifyCQN(subCqnDraft, element.SELECT.where, context)
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
|
|
32
|
+
const _modifyCQN = (cqnDraft, where, context) => {
|
|
33
|
+
const whereDraft = [...where]
|
|
34
|
+
const result = readAndDeleteKeywords(['IsActiveEntity'], whereDraft)
|
|
35
|
+
cqnDraft.where(whereDraft)
|
|
36
|
+
|
|
37
|
+
if (result && result.value.val === false) {
|
|
38
|
+
const fromRef = cqnDraft.SELECT.from.ref
|
|
39
|
+
cqnDraft.SELECT.from.ref[fromRef.length - 1] = ensureDraftsSuffix(fromRef[fromRef.length - 1])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_modifyWhere(cqnDraft.SELECT.where, context)
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
const _hasNavToNonDraftEnclosedAssoc = (pathSegments, definitions, excludeAssoc) => {
|
|
37
46
|
if (pathSegments.length < 2) return false
|
|
38
47
|
const entity = definitions[pathSegments[0]]
|
|
@@ -112,7 +121,7 @@ const _readOverDraftHandler = async function (req, next) {
|
|
|
112
121
|
|
|
113
122
|
const hasDraftEntity = hasDraft(definitions, sqlQuery)
|
|
114
123
|
|
|
115
|
-
if (hasDraftEntity && sqlQuery.SELECT.where && sqlQuery.SELECT.where.length
|
|
124
|
+
if (hasDraftEntity && sqlQuery.SELECT.where && sqlQuery.SELECT.where.length) {
|
|
116
125
|
let cqnDraft = SELECT.from({
|
|
117
126
|
ref: [...sqlQuery.SELECT.from.ref],
|
|
118
127
|
as: sqlQuery.SELECT.from.as
|
|
@@ -93,6 +93,9 @@ const getUpdateDraftAdminCQN = ({ user }, draftUUID) => {
|
|
|
93
93
|
const _addAlias = (where, tableName) => {
|
|
94
94
|
// copy where
|
|
95
95
|
return where.map(element => {
|
|
96
|
+
if (element.xpr) {
|
|
97
|
+
return { xpr: _addAlias(element.xpr, tableName) }
|
|
98
|
+
}
|
|
96
99
|
if (element.ref && element.ref.length === 1) {
|
|
97
100
|
// and copy ref
|
|
98
101
|
return { ref: [tableName, element.ref[0]] }
|
|
@@ -57,7 +57,9 @@ const _removeIsActiveEntityCondition = where => {
|
|
|
57
57
|
} else if (where[i] === 'and' && where[i + 1] === '(' && _isActiveEntity(where[i + 2])) {
|
|
58
58
|
i = i + 6
|
|
59
59
|
} else if (where[i].xpr) {
|
|
60
|
-
|
|
60
|
+
if (where[i].xpr.length) {
|
|
61
|
+
newWhere.push({ xpr: _removeIsActiveEntityCondition(where[i].xpr) })
|
|
62
|
+
}
|
|
61
63
|
i++
|
|
62
64
|
} else {
|
|
63
65
|
newWhere.push(where[i])
|
|
@@ -65,7 +67,7 @@ const _removeIsActiveEntityCondition = where => {
|
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
if (newWhere[0] === 'and') {
|
|
70
|
+
if (newWhere[0] === 'and' || newWhere[0] === 'or') {
|
|
69
71
|
newWhere.splice(0, 1)
|
|
70
72
|
} else if (newWhere[0] === '(' && newWhere[1] === 'and') {
|
|
71
73
|
newWhere.splice(0, 2)
|
|
@@ -90,32 +92,35 @@ const deleteCondition = (index, whereCondition, isXpr = false) => {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
const readAndDeleteKeywords = (keywords, whereCondition, toDelete = true) => {
|
|
93
|
-
let index =
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
let index = -1
|
|
96
|
+
for (let i = 0; i < whereCondition.length; i++) {
|
|
97
|
+
const entry = whereCondition[i]
|
|
98
|
+
if (entry.xpr) {
|
|
99
|
+
const result = readAndDeleteKeywords(keywords, entry.xpr, toDelete)
|
|
100
|
+
if (result) {
|
|
101
|
+
if (entry.xpr.length === 0) {
|
|
102
|
+
deleteCondition(i, whereCondition, true)
|
|
103
|
+
}
|
|
104
|
+
return result
|
|
105
|
+
}
|
|
106
|
+
} else if (entry.ref) {
|
|
107
|
+
const refLastIndex = entry.ref.length - 1
|
|
108
|
+
|
|
109
|
+
if (keywords.length === 1) {
|
|
110
|
+
if (entry.ref[refLastIndex] === keywords[0]) {
|
|
111
|
+
index = i
|
|
112
|
+
break
|
|
113
|
+
}
|
|
99
114
|
}
|
|
100
|
-
return result
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
index = whereCondition.findIndex(({ ref }) => {
|
|
105
|
-
if (!ref) {
|
|
106
|
-
return false
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const refLastIndex = ref.length - 1
|
|
110
|
-
|
|
111
|
-
if (keywords.length === 1) {
|
|
112
|
-
return ref[refLastIndex] === keywords[0]
|
|
113
|
-
}
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (keywords.length === 2 && entry.ref.length >= 2) {
|
|
117
|
+
if (entry.ref[refLastIndex - 1] === keywords[0] && entry.ref[refLastIndex] === keywords[1]) {
|
|
118
|
+
index = i
|
|
119
|
+
break
|
|
120
|
+
}
|
|
121
|
+
}
|
|
117
122
|
}
|
|
118
|
-
}
|
|
123
|
+
}
|
|
119
124
|
|
|
120
125
|
if (index === -1) {
|
|
121
126
|
return
|
|
@@ -135,6 +140,10 @@ const readAndDeleteKeywords = (keywords, whereCondition, toDelete = true) => {
|
|
|
135
140
|
|
|
136
141
|
const removeIsActiveEntityRecursively = where => {
|
|
137
142
|
for (const entry of where) {
|
|
143
|
+
if (entry.xpr) {
|
|
144
|
+
entry.xpr = removeIsActiveEntityRecursively(entry.xpr)
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
138
147
|
if (entry.SELECT && entry.SELECT.where && entry.SELECT.from.ref && !entry.SELECT.from.ref[0].endsWith('_drafts')) {
|
|
139
148
|
entry.SELECT.where = removeIsActiveEntityRecursively(entry.SELECT.where)
|
|
140
149
|
|
|
@@ -153,6 +162,10 @@ const isActiveEntityRequested = where => {
|
|
|
153
162
|
let i = 0
|
|
154
163
|
|
|
155
164
|
while (where[i]) {
|
|
165
|
+
if (where[i].xpr) {
|
|
166
|
+
const isRequested = isActiveEntityRequested(where.xpr)
|
|
167
|
+
if (isRequested) return true
|
|
168
|
+
}
|
|
156
169
|
if (
|
|
157
170
|
where[i].ref &&
|
|
158
171
|
where[i].ref[where[i].ref.length - 1] === 'IsActiveEntity' &&
|
|
@@ -37,7 +37,7 @@ function _connectHdb(creds, tenant) {
|
|
|
37
37
|
// tls keep alive
|
|
38
38
|
if (process.env.HDB_TCP_KEEP_ALIVE_IDLE) {
|
|
39
39
|
const num = Number(process.env.HDB_TCP_KEEP_ALIVE_IDLE)
|
|
40
|
-
creds.tcpKeepAliveIdle = Number.
|
|
40
|
+
creds.tcpKeepAliveIdle = Number.isNaN(num) ? false : num
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const hdbClient = this.createClient(creds)
|
|
@@ -37,8 +37,11 @@ const search2cqn4sql = (query, entity, options) => {
|
|
|
37
37
|
if (resolveLocalizedDataAtRuntime) {
|
|
38
38
|
const onCondition = entity._relations[localizedAssociation.name].join(localizedAssociation.target, entity.name)
|
|
39
39
|
|
|
40
|
+
// REVISIT this is dirty but works for now
|
|
40
41
|
// replace $user_locale placeholder with the user locale or the HANA session context
|
|
41
|
-
onCondition[
|
|
42
|
+
if (onCondition[0].xpr) {
|
|
43
|
+
onCondition[0].xpr[onCondition[0].xpr.length - 1] = { val: locale || "SESSION_CONTEXT('LOCALE')" }
|
|
44
|
+
} else onCondition[onCondition.length - 2] = { val: locale || "SESSION_CONTEXT('LOCALE')" }
|
|
42
45
|
|
|
43
46
|
// inner join the target table with the _texts table (the _texts table contains
|
|
44
47
|
// the translated texts)
|