@sap/cds 5.6.4 → 5.7.4
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 +134 -0
- package/_i18n/i18n_fr.properties +4 -4
- package/apis/cds.d.ts +7 -10
- package/apis/connect.d.ts +3 -3
- package/apis/core.d.ts +2 -4
- package/apis/models.d.ts +2 -3
- package/apis/ql.d.ts +0 -1
- package/apis/services.d.ts +3 -3
- package/bin/build/buildTaskFactory.js +16 -10
- package/bin/build/buildTaskProviderFactory.js +3 -3
- package/bin/build/constants.js +2 -1
- package/bin/build/provider/buildTaskProviderInternal.js +14 -14
- package/bin/build/provider/hana/2migration.js +2 -3
- package/bin/build/provider/hana/index.js +34 -0
- package/bin/build/provider/hana/migrationtable.js +90 -22
- package/bin/build/provider/hana/template/undeploy.json +5 -0
- package/bin/build/provider/node-cf/index.js +9 -2
- package/bin/serve.js +16 -18
- package/lib/compile/cdsc.js +15 -5
- package/lib/compile/etc/_localized.js +4 -4
- package/lib/compile/extend.js +8 -0
- package/lib/compile/index.js +3 -1
- package/lib/compile/minify.js +61 -0
- package/lib/compile/resolve.js +4 -1
- package/lib/compile/to/gql.js +9 -0
- package/lib/compile/to/sql.js +26 -30
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +0 -3
- package/lib/core/infer.js +1 -0
- package/lib/core/reflect.js +0 -34
- package/lib/deploy.js +25 -17
- package/lib/env/defaults.js +3 -1
- package/lib/env/index.js +8 -3
- package/lib/env/presets.js +38 -0
- package/lib/env/requires.js +16 -11
- package/lib/index.js +13 -11
- package/lib/log/format/kibana.js +3 -1
- package/lib/log/index.js +2 -2
- package/lib/req/cds-context.js +79 -0
- package/lib/req/context.js +5 -77
- package/lib/req/request.js +1 -1
- package/lib/serve/Service-api.js +8 -4
- package/lib/serve/Service-dispatch.js +0 -7
- package/lib/serve/Service-methods.js +6 -8
- package/lib/serve/Transaction.js +35 -30
- package/lib/serve/adapters.js +1 -4
- package/lib/utils/axios.js +1 -1
- package/libx/_runtime/audit/Service.js +44 -20
- package/libx/_runtime/audit/generic/personal/access.js +16 -11
- package/libx/_runtime/audit/generic/personal/modification.js +5 -5
- package/libx/_runtime/audit/generic/personal/utils.js +46 -37
- package/libx/_runtime/{common/auth → auth}/index.js +21 -7
- package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
- package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -69
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +29 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
- package/libx/_runtime/cds-services/services/Service.js +1 -7
- package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -6
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
- package/libx/_runtime/cds-services/util/assert.js +1 -262
- package/libx/_runtime/cds.js +6 -9
- package/libx/_runtime/common/aspects/entity.js +1 -1
- package/libx/_runtime/common/composition/delete.js +4 -2
- package/libx/_runtime/common/composition/update.js +22 -38
- package/libx/_runtime/common/composition/utils.js +3 -7
- package/libx/_runtime/common/error/standardError.js +11 -0
- package/libx/_runtime/common/generic/auth.js +63 -33
- package/libx/_runtime/common/generic/crud.js +11 -23
- package/libx/_runtime/common/generic/input.js +20 -0
- package/libx/_runtime/common/generic/put.js +4 -10
- package/libx/_runtime/common/generic/sorting.js +12 -30
- package/libx/_runtime/common/i18n/messages.properties +2 -0
- package/libx/_runtime/common/perf/index.js +24 -0
- package/libx/_runtime/common/utils/cqn.js +58 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +298 -121
- package/libx/_runtime/common/utils/csn.js +38 -56
- package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
- package/libx/_runtime/common/utils/resolveView.js +4 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
- package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
- package/libx/_runtime/common/utils/structured.js +35 -25
- package/libx/_runtime/db/Service.js +0 -6
- package/libx/_runtime/db/data-conversion/post-processing.js +22 -22
- package/libx/_runtime/db/expand/expand-v2.js +130 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +57 -75
- package/libx/_runtime/db/expand/index.js +3 -1
- package/libx/_runtime/db/generic/input.js +52 -10
- package/libx/_runtime/db/generic/integrity.js +367 -26
- package/libx/_runtime/db/generic/virtual.js +51 -13
- package/libx/_runtime/db/query/read.js +12 -8
- package/libx/_runtime/db/query/update.js +9 -3
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
- package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
- package/libx/_runtime/fiori/generic/activate.js +1 -0
- package/libx/_runtime/fiori/generic/before.js +2 -1
- package/libx/_runtime/fiori/generic/edit.js +1 -0
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +128 -57
- package/libx/_runtime/fiori/uiflex/index.js +1 -1
- package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +3 -3
- package/libx/_runtime/fiori/utils/delete.js +7 -1
- package/libx/_runtime/hana/Service.js +1 -8
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
- package/libx/_runtime/hana/execute.js +10 -4
- package/libx/_runtime/hana/pool.js +55 -45
- package/libx/_runtime/hana/search.js +7 -6
- package/libx/_runtime/hana/search2cqn4sql.js +8 -5
- package/libx/_runtime/hana/searchToContains.js +3 -1
- package/libx/_runtime/index.js +5 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
- package/libx/_runtime/messaging/Outbox.js +53 -0
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
- package/libx/_runtime/messaging/common-utils/connections.js +14 -9
- package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing-utils/options-messaging.js +1 -0
- package/libx/_runtime/messaging/message-queuing.js +2 -3
- package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
- package/libx/_runtime/messaging/outbox/utils.js +192 -0
- package/libx/_runtime/messaging/service.js +16 -30
- package/libx/_runtime/remote/Service.js +15 -0
- package/libx/_runtime/remote/utils/client.js +15 -3
- package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
- package/libx/_runtime/sqlite/Service.js +7 -10
- package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
- package/libx/_runtime/sqlite/execute.js +18 -12
- package/libx/_runtime/types/api.js +2 -1
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +1 -1
- package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +19 -15
- package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
- package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +217 -148
- package/libx/odata/index.js +21 -13
- package/libx/odata/parser.js +1 -0
- package/libx/odata/utils.js +57 -0
- package/libx/rest/RestAdapter.js +2 -6
- package/libx/rest/utils/data.js +1 -6
- package/package.json +4 -3
- package/server.js +4 -5
- package/srv/audit-log.cds +87 -0
- package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
- package/srv/flex.js +1 -0
- package/srv/outbox.cds +11 -0
- package/srv/outbox.js +0 -0
- package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
- package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
- package/libx/odata/odata2cqn/index.js +0 -3
- package/libx/odata/odata2cqn/parser.js +0 -1
- package/libx/odata/readme.md +0 -1
- package/libx/odata/utils/index.js +0 -64
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
|
+
const _autoGenerate = e => e && e.type === 'cds.UUID' && e.key
|
|
4
|
+
|
|
3
5
|
const _generateParentField = ({ parentElement }, row) => {
|
|
4
6
|
if (_autoGenerate(parentElement) && !row[parentElement.name]) {
|
|
5
7
|
row[parentElement.name] = cds.utils.uuid()
|
|
@@ -14,8 +16,6 @@ const _generateChildField = ({ deep, childElement }, childRow) => {
|
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
const _autoGenerate = e => e && e.type === 'cds.UUID' && e.key
|
|
18
|
-
|
|
19
19
|
const _getNestedVal = (row, prefix) => {
|
|
20
20
|
let val = row
|
|
21
21
|
const splitted = prefix.split('_')
|
|
@@ -52,12 +52,12 @@ const _propagateToParent = ({ parentElement, childElement, deep }, childRow, row
|
|
|
52
52
|
if (deep) {
|
|
53
53
|
_propagateToParent(deep.propagation, childRow[deep.targetName], childRow)
|
|
54
54
|
}
|
|
55
|
-
if (parentElement && childElement && childRow &&
|
|
55
|
+
if (parentElement && childElement && childRow && childElement.name in childRow) {
|
|
56
56
|
row[parentElement.name] = childRow[childElement.name]
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
module.exports = (tKey, row, foreignKeyPropagations, isCompositionEffective) => {
|
|
61
61
|
const childRows = Array.isArray(row[tKey]) ? row[tKey] : [row[tKey]]
|
|
62
62
|
|
|
63
63
|
for (const childRow of childRows) {
|
|
@@ -65,12 +65,13 @@ const propagateForeignKeys = (tKey, row, foreignKeyPropagations, isCompositionEf
|
|
|
65
65
|
|
|
66
66
|
for (const foreignKeyPropagation of foreignKeyPropagations) {
|
|
67
67
|
if (foreignKeyPropagation.fillChild) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
// propagate or generate in parent
|
|
69
|
+
const pk = foreignKeyPropagation.parentElement && foreignKeyPropagation.parentElement.name
|
|
70
|
+
if (pk && !(pk in row)) _propagateToParent(foreignKeyPropagation, childRow, row)
|
|
71
|
+
if (!(pk in row)) _generateParentField(foreignKeyPropagation, row)
|
|
72
|
+
|
|
73
|
+
if (!isCompositionEffective) delete row[tKey]
|
|
74
|
+
else _propagateToChid(foreignKeyPropagation, row, childRow)
|
|
74
75
|
} else {
|
|
75
76
|
_generateChildField(foreignKeyPropagation, childRow)
|
|
76
77
|
_propagateToParent(foreignKeyPropagation, childRow, row)
|
|
@@ -78,7 +79,3 @@ const propagateForeignKeys = (tKey, row, foreignKeyPropagations, isCompositionEf
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
module.exports = {
|
|
83
|
-
propagateForeignKeys
|
|
84
|
-
}
|
|
@@ -147,6 +147,7 @@ const _handler = async function (req) {
|
|
|
147
147
|
r.getUriInfo = () => req.getUriInfo()
|
|
148
148
|
r.getUrlObject = () => req.getUrlObject()
|
|
149
149
|
r._.params = req.params
|
|
150
|
+
r._.query = req.query
|
|
150
151
|
|
|
151
152
|
// use finally to preserve r.messages in success or error case
|
|
152
153
|
let result
|
|
@@ -190,8 +190,9 @@ module.exports = cds.service.impl(function () {
|
|
|
190
190
|
_new._initial = true
|
|
191
191
|
_patchUpdate._initial = true
|
|
192
192
|
_deleteCancel._initial = true
|
|
193
|
+
const entities = Object.values(this.entities).filter(e => e._isDraftEnabled)
|
|
193
194
|
|
|
194
|
-
for (const entity of
|
|
195
|
+
for (const entity of entities) {
|
|
195
196
|
this.before('NEW', entity, _new)
|
|
196
197
|
this.before(['PATCH', 'UPDATE'], entity, _patchUpdate)
|
|
197
198
|
this.before(['DELETE', 'CANCEL'], entity, _deleteCancel)
|
|
@@ -130,6 +130,7 @@ const _handler = async function (req) {
|
|
|
130
130
|
const lockAndSelectCQNs = [lockRecordCQN, draftExistsCQN, ...selectCQNs]
|
|
131
131
|
|
|
132
132
|
const dbtx = cds.tx(req)
|
|
133
|
+
// REVISIT: Use service.read with expand **
|
|
133
134
|
const [, draftExists, ...results] = await _select(lockAndSelectCQNs, req, dbtx)
|
|
134
135
|
|
|
135
136
|
if (!results[0].length) {
|
|
@@ -62,7 +62,7 @@ const _getUpdateDraftCQN = ({ query, target: { name } }, keysCondition) => {
|
|
|
62
62
|
* @param req
|
|
63
63
|
*/
|
|
64
64
|
const _handler = async function (req) {
|
|
65
|
-
if (req.data.IsActiveEntity === 'true') req.reject(400)
|
|
65
|
+
if (req.data.IsActiveEntity === 'true') req.reject(400, 'Patch can only be applied to a draft entity')
|
|
66
66
|
|
|
67
67
|
const keysCondition = getKeysCondition(req.target, req.data)
|
|
68
68
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
3
|
|
|
4
|
-
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
4
|
+
const { cqn2cqn4sql, convertWhereExists } = require('../../common/utils/cqn2cqn4sql')
|
|
5
5
|
const { getElementDeep } = require('../../common/utils/csn')
|
|
6
6
|
|
|
7
7
|
const { DRAFT_COLUMNS, DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
|
|
@@ -21,26 +21,51 @@ const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively
|
|
|
21
21
|
|
|
22
22
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
23
23
|
|
|
24
|
+
const _findRootSubSelectFor = query => {
|
|
25
|
+
if (query.SELECT.where) {
|
|
26
|
+
const subSelect = query.SELECT.where.find((e, i) => e.SELECT && query.SELECT.where[i - 1] === 'exists')
|
|
27
|
+
return subSelect ? _findRootSubSelectFor(subSelect) : query
|
|
28
|
+
}
|
|
29
|
+
return query
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
// append where with clauses from @restrict
|
|
25
33
|
const _getWhereWithAppendedDraftRestrictions = (where = [], req, scenarioAlias, model) => {
|
|
26
34
|
if (req.query._draftRestrictions) {
|
|
27
35
|
for (const each of req.query._draftRestrictions) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const xpr = each._xpr
|
|
37
|
+
if (each.target.name === ensureUnlocalized(req.target.name)) {
|
|
38
|
+
// > restriction directly on child
|
|
39
|
+
// REVISIT: remove support for selects in restriction with cds^6
|
|
40
|
+
// adjust alias of @restrict where "exists (select ...)"
|
|
41
|
+
if (scenarioAlias && model)
|
|
42
|
+
xpr
|
|
43
|
+
.filter(e => e.SELECT && e.SELECT.from && e.SELECT.where)
|
|
44
|
+
.forEach(e => {
|
|
45
|
+
const entity = model.definitions[e.SELECT.from.ref[0]]
|
|
46
|
+
e.SELECT.where = e.SELECT.where.map(w => {
|
|
47
|
+
if (w.ref && w.ref.length === 1 && !entity.elements[w.ref[0]]) w.ref.unshift(scenarioAlias)
|
|
48
|
+
return w
|
|
49
|
+
})
|
|
40
50
|
})
|
|
41
|
-
})
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
if (where.length) where.push('and')
|
|
53
|
+
where.push(...xpr)
|
|
54
|
+
} else {
|
|
55
|
+
// > restriction inherited from parent via autoexposure
|
|
56
|
+
// find inner most sub select if available and append restriction to where clause
|
|
57
|
+
const rootSubSelect = _findRootSubSelectFor({ SELECT: { where } })
|
|
58
|
+
if (rootSubSelect && rootSubSelect.SELECT.from) {
|
|
59
|
+
if (rootSubSelect.SELECT.where && rootSubSelect.SELECT.where.length) rootSubSelect.SELECT.where.push('and')
|
|
60
|
+
const tableAlias = rootSubSelect.SELECT.from.as
|
|
61
|
+
rootSubSelect.SELECT.where.push(
|
|
62
|
+
...xpr.map(e => {
|
|
63
|
+
if (e.ref) return tableAlias ? { ref: [tableAlias, ...e.ref] } : { ref: [...e.ref] }
|
|
64
|
+
return e
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
44
69
|
}
|
|
45
70
|
}
|
|
46
71
|
return where
|
|
@@ -299,21 +324,24 @@ const _allInactive = (req, columns) => {
|
|
|
299
324
|
_getDefaultDraftProperties({ hasDraft: false, isActive: false, withDraftUUID: false })
|
|
300
325
|
)
|
|
301
326
|
|
|
302
|
-
const ids = filterKeys(req.target.keys)
|
|
303
327
|
const isCount = columns.some(element => element.func === 'count')
|
|
304
328
|
|
|
329
|
+
// ensure only own drafts are read
|
|
305
330
|
const cqn = SELECT.from(table)
|
|
331
|
+
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
332
|
+
.on([
|
|
333
|
+
{ ref: [table.as, 'DraftAdministrativeData_DraftUUID'] },
|
|
334
|
+
'=',
|
|
335
|
+
{
|
|
336
|
+
ref: ['filterAdmin', 'DraftUUID']
|
|
337
|
+
}
|
|
338
|
+
])
|
|
339
|
+
.where(_inProcessByUserWhere(req.user.id))
|
|
306
340
|
|
|
307
341
|
if (isCount) {
|
|
308
342
|
cqn.columns(...outerMostColumns)
|
|
309
343
|
} else {
|
|
310
344
|
cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), { ref: ['HasActiveEntity'] })
|
|
311
|
-
cqn.leftJoin(ensureNoDraftsSuffix(table.ref[0]) + ' as active').on(`${table.as}.${ids[0]} = active.${ids[0]}`)
|
|
312
|
-
|
|
313
|
-
for (let i = 1; i < ids.length; i++) {
|
|
314
|
-
// REVISIT: this is extremely expensive as it repeatedly invokes the compiler's cds.parse.expr -> better extend plain CQN yourself here
|
|
315
|
-
cqn.and(`${table.as}.${ids[i]} = active.${ids[i]}`)
|
|
316
|
-
}
|
|
317
345
|
}
|
|
318
346
|
|
|
319
347
|
cqn.where(req.query.SELECT.where)
|
|
@@ -507,11 +535,11 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
|
|
|
507
535
|
subSelect
|
|
508
536
|
)
|
|
509
537
|
|
|
510
|
-
subSelect.SELECT.where = _getWhereWithAppendedDraftRestrictions(subSelect.SELECT.where, req)
|
|
511
|
-
|
|
512
538
|
const outerMostColumns = _getOuterMostColumns(columns, draftColumns)
|
|
513
539
|
|
|
514
|
-
const cqn = SELECT.from(active.table).columns(outerMostColumns)
|
|
540
|
+
const cqn = SELECT.from(active.table).columns(outerMostColumns)
|
|
541
|
+
cqn.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
542
|
+
cqn.where(['exists', subSelect])
|
|
515
543
|
|
|
516
544
|
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_IN_PROCESS }
|
|
517
545
|
}
|
|
@@ -598,13 +626,21 @@ const _getWhereForActive = where => {
|
|
|
598
626
|
return activeWhere
|
|
599
627
|
}
|
|
600
628
|
|
|
601
|
-
const _siblingEntity = (
|
|
629
|
+
const _siblingEntity = (
|
|
630
|
+
{ query, target, nav, params },
|
|
631
|
+
columns,
|
|
632
|
+
model,
|
|
633
|
+
draftAdminAlias,
|
|
634
|
+
parentQuery,
|
|
635
|
+
siblingIndex,
|
|
636
|
+
req
|
|
637
|
+
) => {
|
|
602
638
|
const parentLinks = parentQuery ? _findJoinInQuery(query, parentQuery.SELECT.from.as) : []
|
|
603
639
|
const keys = (nav[siblingIndex + 1].where && (params[siblingIndex] || params[0])) || {}
|
|
604
640
|
const siblingQuery = query.SELECT.where[query.SELECT.where.indexOf('exists') + 1]
|
|
605
641
|
const onCond = _findJoinInQuery(siblingQuery, target.as)
|
|
606
642
|
const siblingAlias = siblingQuery.SELECT.from.as
|
|
607
|
-
const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond)
|
|
643
|
+
const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req)
|
|
608
644
|
const isSiblingDraft = subScenario
|
|
609
645
|
? subScenario.isSiblingActive || subScenario.scenario === 'ACTIVE' || subScenario.scenario === 'ALL_ACTIVE'
|
|
610
646
|
: keys.IsActiveEntity && keys.IsActiveEntity !== 'false'
|
|
@@ -652,12 +688,17 @@ const _siblingEntity = ({ query, target, nav, params }, columns, model, draftAdm
|
|
|
652
688
|
return { cqn, scenario: SCENARIO.SIBLING_ENTITY, isSiblingActive: !isSiblingDraft }
|
|
653
689
|
}
|
|
654
690
|
|
|
655
|
-
function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond) {
|
|
691
|
+
function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req) {
|
|
656
692
|
if (nav[siblingIndex + 1].where) return
|
|
657
693
|
let subScenario
|
|
658
694
|
const subNav = nav.slice(siblingIndex + 1)
|
|
659
695
|
const subSiblingIndex = subNav.indexOf('SiblingEntity')
|
|
660
|
-
const subReq = {
|
|
696
|
+
const subReq = {
|
|
697
|
+
query: siblingQuery,
|
|
698
|
+
target: model.definitions[target.name],
|
|
699
|
+
params: [...params].reverse(),
|
|
700
|
+
user: req.user
|
|
701
|
+
}
|
|
661
702
|
if (subSiblingIndex > -1) {
|
|
662
703
|
subScenario = _getSiblingScenario(subReq, [{ val: 1 }], model, subSiblingIndex, subNav, params)
|
|
663
704
|
if (subSiblingIndex > 0) {
|
|
@@ -670,8 +711,8 @@ function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, mo
|
|
|
670
711
|
subReq.query = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
|
|
671
712
|
const existsIdx = siblingQuery.SELECT.where.indexOf('exists')
|
|
672
713
|
if (existsIdx > -1) subReq.query.where(siblingQuery.SELECT.where.slice(existsIdx, existsIdx + 2))
|
|
673
|
-
const
|
|
674
|
-
subScenario = _generateCQN(
|
|
714
|
+
const subOrigFrom = { ref: [...subNav].reverse() }
|
|
715
|
+
subScenario = _generateCQN(subOrigFrom, subReq, [{ val: 1 }], model)
|
|
675
716
|
subScenario.cqn.where(onCond)
|
|
676
717
|
}
|
|
677
718
|
return subScenario
|
|
@@ -688,7 +729,15 @@ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
|
|
|
688
729
|
}
|
|
689
730
|
}
|
|
690
731
|
const target = { name: query.SELECT.from.ref[0].id || query.SELECT.from.ref[0], as: query.SELECT.from.as }
|
|
691
|
-
return _siblingEntity(
|
|
732
|
+
return _siblingEntity(
|
|
733
|
+
{ query, target, params, nav },
|
|
734
|
+
columns,
|
|
735
|
+
model,
|
|
736
|
+
draftAdminAlias,
|
|
737
|
+
parentQuery,
|
|
738
|
+
siblingIndex,
|
|
739
|
+
req
|
|
740
|
+
)
|
|
692
741
|
}
|
|
693
742
|
return _getSiblingQueryFromWhere(req.query, siblingIndex)
|
|
694
743
|
}
|
|
@@ -731,7 +780,7 @@ const _getDraftDoc = (req, draftName, draftWhere) => {
|
|
|
731
780
|
|
|
732
781
|
const _getOrderByEnrichedColumns = (orderBy, columns) => {
|
|
733
782
|
const enrichedCol = []
|
|
734
|
-
if (orderBy.length > 1) {
|
|
783
|
+
if (orderBy && orderBy.length > 1) {
|
|
735
784
|
const colNames = columns.map(el => el.ref[el.ref.length - 1])
|
|
736
785
|
// REVISIT: GET Books?$select=title&$expand=NotBooks($select=pages)&$orderby=NotBooks/title - what's then?
|
|
737
786
|
for (const el of orderBy) {
|
|
@@ -757,15 +806,19 @@ const _replaceDraftAlias = where => {
|
|
|
757
806
|
|
|
758
807
|
const _poorMansAlias4 = xpr => '_' + xpr.ref.join('_') + '_'
|
|
759
808
|
|
|
760
|
-
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
|
|
809
|
+
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model) => {
|
|
761
810
|
const draftActiveWhere = _getWhereForActive(draftWhere)
|
|
762
811
|
const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
|
|
812
|
+
activeDocs.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
813
|
+
convertWhereExists(activeDocs, model, {})
|
|
763
814
|
|
|
815
|
+
// @restrict.where not applicable for drafts (I can ALWAYS read mine)
|
|
764
816
|
_replaceDraftAlias(draftWhere)
|
|
765
817
|
const draftDocs = _getDraftDoc(req, draftName, draftWhere)
|
|
766
818
|
|
|
767
819
|
const union = SELECT.from({ SET: { op: 'union', all: true, args: [draftDocs, activeDocs] } })
|
|
768
820
|
if (req.query.SELECT.count) union.SELECT.count = true
|
|
821
|
+
if (req.query.SELECT.__countAggregated) union.SELECT.__countAggregated = true
|
|
769
822
|
|
|
770
823
|
if (req.query.SELECT.from.as) {
|
|
771
824
|
draftDocs.SELECT.from.as = req.query.SELECT.from.as
|
|
@@ -846,7 +899,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
|
|
|
846
899
|
.columns(..._filterDraftColumnsBySelected(DRAFT_COLUMNS_CASTED, req.query.SELECT.columns))
|
|
847
900
|
}
|
|
848
901
|
|
|
849
|
-
const _excludeActiveDraftExists = (req, draftWhere, columns) => {
|
|
902
|
+
const _excludeActiveDraftExists = (req, draftWhere, columns, model) => {
|
|
850
903
|
const { table, name } = _getTableName(req, true)
|
|
851
904
|
const draftName = table.ref[0]
|
|
852
905
|
|
|
@@ -865,10 +918,8 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
|
|
|
865
918
|
subSelect.where([{ ref: [ensureNoDraftsSuffix(req.target.name), key] }, '=', { ref: [draftName, key] }])
|
|
866
919
|
}
|
|
867
920
|
|
|
868
|
-
draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
|
|
869
|
-
|
|
870
921
|
draftWhere = removeIsActiveEntityRecursively(draftWhere)
|
|
871
|
-
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere)
|
|
922
|
+
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere, model)
|
|
872
923
|
cqn.SELECT.from.as = name
|
|
873
924
|
|
|
874
925
|
if (cqn.SELECT.orderBy) {
|
|
@@ -900,13 +951,13 @@ const _validatedActiveWithoutDraft = (req, draftWhere, draftParameters, columns)
|
|
|
900
951
|
_isValidActiveWithoutDraft(draftParameters.isActiveEntity, draftParameters.hasDraftEntity) &&
|
|
901
952
|
_activeWithoutDraft(req, draftWhere, columns)
|
|
902
953
|
|
|
903
|
-
const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, columns) => {
|
|
954
|
+
const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, columns, model) => {
|
|
904
955
|
const { isActiveEntity, siblingIsActive, draftInProcessByUser } = draftParameters
|
|
905
956
|
if (
|
|
906
957
|
!draftInProcessByUser &&
|
|
907
958
|
_isValidExcludeActiveDraftExists(draftParameters.isActiveEntity, draftParameters.siblingIsActive)
|
|
908
959
|
)
|
|
909
|
-
return _excludeActiveDraftExists(req, draftWhere, columns)
|
|
960
|
+
return _excludeActiveDraftExists(req, draftWhere, columns, model)
|
|
910
961
|
if (
|
|
911
962
|
draftInProcessByUser.op === '!=' &&
|
|
912
963
|
_isValidWithDraftLocked(isActiveEntity, siblingIsActive, draftInProcessByUser)
|
|
@@ -925,9 +976,7 @@ const _draftInSubSelect = (where, req) => {
|
|
|
925
976
|
if (SELECT && SELECT.where) {
|
|
926
977
|
const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
|
|
927
978
|
if (isActiveEntity) {
|
|
928
|
-
|
|
929
|
-
if (isFalse) SELECT.where = _getWhereWithAppendedDraftRestrictions(SELECT.where, req)
|
|
930
|
-
return isFalse
|
|
979
|
+
return _isFalse(isActiveEntity.value.val)
|
|
931
980
|
}
|
|
932
981
|
|
|
933
982
|
return _draftInSubSelect(SELECT.where, req)
|
|
@@ -940,10 +989,16 @@ const _draftInSubSelect = (where, req) => {
|
|
|
940
989
|
const _isDraftAdminScenario = req =>
|
|
941
990
|
req.target.query && req.target.query._target && req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
|
|
942
991
|
|
|
943
|
-
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
|
|
992
|
+
const _generateCQN = (originalFrom, req, columns, model) => {
|
|
993
|
+
const nav = [...originalFrom.ref].reverse() || []
|
|
994
|
+
let siblingIndex = nav.indexOf('SiblingEntity')
|
|
995
|
+
|
|
996
|
+
// it can also be a property access (new parser), then we must shift it
|
|
997
|
+
if (siblingIndex === 1 && req.target.elements[nav[0]]) {
|
|
998
|
+
nav.shift()
|
|
999
|
+
siblingIndex = siblingIndex - 1
|
|
1000
|
+
}
|
|
1001
|
+
|
|
947
1002
|
let siblingScenario
|
|
948
1003
|
if (siblingIndex > -1) {
|
|
949
1004
|
siblingScenario = _getSiblingScenario(req, columns, model, siblingIndex, nav)
|
|
@@ -975,8 +1030,6 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
975
1030
|
}
|
|
976
1031
|
|
|
977
1032
|
if (!draftParameters.isActiveEntity) {
|
|
978
|
-
// _draftInSubSelect adds draft restrictions in case check is truthy
|
|
979
|
-
// -> not nice but works for now and we don't need to go in recursively again
|
|
980
1033
|
if (_draftInSubSelect(req.query.SELECT.where, req) || (siblingScenario && !siblingScenario.isSiblingActive)) {
|
|
981
1034
|
// this is only the case when navigating into tree
|
|
982
1035
|
return _allInactive(req, columns)
|
|
@@ -989,23 +1042,20 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
989
1042
|
}
|
|
990
1043
|
|
|
991
1044
|
if (draftParameters.siblingIsActive) {
|
|
992
|
-
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns)
|
|
1045
|
+
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns, model)
|
|
993
1046
|
}
|
|
994
1047
|
|
|
995
1048
|
return _validatedDraftOfWhichIAmOwner(req, req.query.SELECT.where, draftParameters, columns)
|
|
996
1049
|
}
|
|
997
1050
|
|
|
998
|
-
const _getColumns = ({ query: { SELECT } }, model) => {
|
|
1051
|
+
const _getColumns = ({ query: { SELECT }, target }, model) => {
|
|
999
1052
|
return SELECT.columns
|
|
1000
1053
|
? SELECT.columns.filter(
|
|
1001
1054
|
col =>
|
|
1002
1055
|
(col.ref && !DRAFT_COLUMNS.includes(col.ref[col.ref.length - 1])) ||
|
|
1003
1056
|
(!col.ref && !DRAFT_COLUMNS.includes(col))
|
|
1004
1057
|
)
|
|
1005
|
-
: getColumns(
|
|
1006
|
-
onlyNames: true,
|
|
1007
|
-
removeIgnore: true
|
|
1008
|
-
})
|
|
1058
|
+
: getColumns(target, { onlyNames: true, removeIgnore: true })
|
|
1009
1059
|
}
|
|
1010
1060
|
|
|
1011
1061
|
const _isIsActiveEntity = element => element.ref && element.ref[element.ref.length - 1] === 'IsActiveEntity'
|
|
@@ -1035,6 +1085,10 @@ const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
|
|
|
1035
1085
|
}
|
|
1036
1086
|
} else if (element.SELECT) {
|
|
1037
1087
|
_adaptSubSelects(element, scenario)
|
|
1088
|
+
} else if (element.xpr) {
|
|
1089
|
+
for (const ele of element.xpr.filter(e => e.SELECT)) {
|
|
1090
|
+
_adaptSubSelects(ele, scenario)
|
|
1091
|
+
}
|
|
1038
1092
|
}
|
|
1039
1093
|
}
|
|
1040
1094
|
}
|
|
@@ -1136,6 +1190,13 @@ const _getLocalizedEntity = (model, target, user) => {
|
|
|
1136
1190
|
return localizedEntity || model.definitions[`${prefix}.${target.name}`]
|
|
1137
1191
|
}
|
|
1138
1192
|
|
|
1193
|
+
const _getLastSubQuery = query => (query.SELECT.from.SELECT ? _getLastSubQuery(query.SELECT.from) : query)
|
|
1194
|
+
const _setLastSubQuery = (query, last, prev = query) => {
|
|
1195
|
+
if (query.SELECT.from.SELECT) return _setLastSubQuery(query.SELECT.from, last, query)
|
|
1196
|
+
else prev.SELECT.from = _copyCQNPartial(last)
|
|
1197
|
+
return prev
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1139
1200
|
const _adaptDraftAdminExpand = cqn => {
|
|
1140
1201
|
const draftAdminExpand =
|
|
1141
1202
|
cqn.SELECT.columns && cqn.SELECT.columns.find(c => c.expand && c.ref[0] === 'DraftAdministrativeData')
|
|
@@ -1149,7 +1210,6 @@ const _adaptDraftAdminExpand = cqn => {
|
|
|
1149
1210
|
*
|
|
1150
1211
|
* @param req
|
|
1151
1212
|
*/
|
|
1152
|
-
// eslint-disable-next-line complexity
|
|
1153
1213
|
const _handler = async function (req) {
|
|
1154
1214
|
// handle localized here as it was previously handled for req.target
|
|
1155
1215
|
req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
|
|
@@ -1157,6 +1217,8 @@ const _handler = async function (req) {
|
|
|
1157
1217
|
// REVISIT
|
|
1158
1218
|
delete req.query._validationQuery
|
|
1159
1219
|
|
|
1220
|
+
const originalFrom = _copyCQNPartial(req.query.SELECT.from)
|
|
1221
|
+
|
|
1160
1222
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
1161
1223
|
const sqlQuery = cqn2cqn4sql(req.query, this.model, { draft: true })
|
|
1162
1224
|
|
|
@@ -1171,8 +1233,17 @@ const _handler = async function (req) {
|
|
|
1171
1233
|
reqClone.query._streaming = true
|
|
1172
1234
|
return cds.tx(req).run(reqClone.query)
|
|
1173
1235
|
}
|
|
1236
|
+
let cqnScenario
|
|
1174
1237
|
|
|
1175
|
-
|
|
1238
|
+
// to replace scenario CQNs for queries with $apply SELECT chain (new odata2cqn parser)
|
|
1239
|
+
// just to make existing tests working with new parser. not really tested, not needed to be supported
|
|
1240
|
+
if (reqClone.query.SELECT.from.SELECT) {
|
|
1241
|
+
const subQueryReq = { __proto__: req, query: _copyCQNPartial(_getLastSubQuery(reqClone.query)) }
|
|
1242
|
+
cqnScenario = _generateCQN(originalFrom.SELECT.from, subQueryReq, _getColumns(subQueryReq, this.model), this.model)
|
|
1243
|
+
cqnScenario.cqn = _setLastSubQuery(reqClone.query, cqnScenario.cqn)
|
|
1244
|
+
} else {
|
|
1245
|
+
cqnScenario = _generateCQN(originalFrom, reqClone, _getColumns(reqClone, this.model), this.model)
|
|
1246
|
+
}
|
|
1176
1247
|
|
|
1177
1248
|
if (!cqnScenario) {
|
|
1178
1249
|
req.reject(400)
|
|
@@ -29,7 +29,7 @@ module.exports = async () => {
|
|
|
29
29
|
.before('READ', transformExtendedFieldsREAD)
|
|
30
30
|
.after('READ', transformExtendedFieldsRESULT)
|
|
31
31
|
if ('cds_r.ExtensibilityService' in cds.services) return
|
|
32
|
-
const model = require('path').join(__dirname, '
|
|
32
|
+
const model = require('path').join(__dirname, '../../../..', 'srv/flex.cds')
|
|
33
33
|
return cds.serve(model, { silent: true }).to('odata').in(cds.app)
|
|
34
34
|
})
|
|
35
35
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const cds = require('
|
|
2
|
-
const { ensureDraftsSuffix } = require('
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
const { ensureDraftsSuffix } = require('../../common/utils/draft')
|
|
3
3
|
|
|
4
|
-
const { EXT_BACK_PACK } = require('
|
|
4
|
+
const { EXT_BACK_PACK } = require('./utils')
|
|
5
5
|
|
|
6
6
|
const _getDraftTable = (view, cds) => {
|
|
7
7
|
return cds.model.definitions[view]._isDraftEnabled ? ensureDraftsSuffix(view) : undefined
|
|
@@ -75,7 +75,13 @@ const deleteDraft = async (req, srv, includingActive = false) => {
|
|
|
75
75
|
const delCQNs = []
|
|
76
76
|
|
|
77
77
|
if (includingActive) {
|
|
78
|
-
|
|
78
|
+
const r = new cds.Request({ query: DELETE.from(ensureNoDraftsSuffix(req.target.name)).where(keys.keyList) })
|
|
79
|
+
|
|
80
|
+
// REVISIT: should not be necessary
|
|
81
|
+
r._ = Object.assign(r._, req._)
|
|
82
|
+
r._.query = req.query
|
|
83
|
+
|
|
84
|
+
await dbtx.dispatch(r)
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
if (draftResult.length !== 0) {
|
|
@@ -45,13 +45,6 @@ class HanaDatabase extends DatabaseService {
|
|
|
45
45
|
this._run = this._queries.run(this._insert, this._read, this._update, this._delete, execute.cqn, execute.sql)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
set model(csn) {
|
|
49
|
-
const m = csn && 'definitions' in csn ? cds.linked(cds.compile.for.odata(csn)) : csn
|
|
50
|
-
// with compiler v2 we always need to localized the csn
|
|
51
|
-
cds.alpha_localized(m)
|
|
52
|
-
super.model = m
|
|
53
|
-
}
|
|
54
|
-
|
|
55
48
|
init() {
|
|
56
49
|
this._registerBeforeHandlers()
|
|
57
50
|
this._registerOnHandlers()
|
|
@@ -134,7 +127,7 @@ class HanaDatabase extends DatabaseService {
|
|
|
134
127
|
async acquire(arg) {
|
|
135
128
|
// REVISIT: remove fallback arg.user.tenant with cds^6
|
|
136
129
|
const tenant = (typeof arg === 'string' ? arg : arg.tenant || (arg.user && arg.user.tenant)) || 'anonymous'
|
|
137
|
-
const dbc = await pool.acquire(tenant, this
|
|
130
|
+
const dbc = await pool.acquire(tenant, this)
|
|
138
131
|
|
|
139
132
|
if (typeof arg !== 'string') {
|
|
140
133
|
_setSessionContext(dbc, 'APPLICATIONUSER', arg.user.id || 'ANONYMOUS')
|
|
@@ -34,18 +34,9 @@ class CustomSelectBuilder extends SelectBuilder {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
getParameters() {
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
(cds.env.runtime && cds.env.runtime.skipWithParameters)
|
|
41
|
-
) {
|
|
42
|
-
return ''
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// REVISIT: remove feature flag skip_with_parameters after grace period of at least two months (> June release)
|
|
46
|
-
if (cds.env.features && cds.env.features.skip_with_parameters === false) {
|
|
47
|
-
return this._options.locale ? `with parameters ('LOCALE' = '${this._options.locale}')` : ''
|
|
48
|
-
}
|
|
37
|
+
const { with_parameters: withParameters } = cds.env.features
|
|
38
|
+
if (withParameters === false || !this._options.locale) return ''
|
|
39
|
+
if (withParameters === true) return `with parameters ('LOCALE' = '${this._options.locale}')`
|
|
49
40
|
|
|
50
41
|
// skip with parameters if all orderby columns are not strings
|
|
51
42
|
let skip
|
|
@@ -78,9 +69,9 @@ class CustomSelectBuilder extends SelectBuilder {
|
|
|
78
69
|
}
|
|
79
70
|
}
|
|
80
71
|
}
|
|
81
|
-
if (skip) return ''
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
if (skip) return ''
|
|
74
|
+
return `with parameters ('LOCALE' = '${this._options.locale}')`
|
|
84
75
|
}
|
|
85
76
|
}
|
|
86
77
|
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
getStructMapper,
|
|
8
8
|
postProcess
|
|
9
9
|
} = require('../db/data-conversion/post-processing')
|
|
10
|
-
const { createJoinCQNFromExpanded, hasExpand, rawToExpanded } = require('../db/expand')
|
|
10
|
+
const { createJoinCQNFromExpanded, hasExpand, rawToExpanded, expandV2 } = require('../db/expand')
|
|
11
11
|
const {
|
|
12
12
|
hasStreamInsert,
|
|
13
13
|
hasStreamUpdate,
|
|
@@ -33,6 +33,8 @@ function _cqnToSQL(model, query, user, locale, txTimestamp) {
|
|
|
33
33
|
const cds = require('../cds')
|
|
34
34
|
const LOG = cds.log('hana|db|sql')
|
|
35
35
|
|
|
36
|
+
const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
37
|
+
|
|
36
38
|
function _getOutputParameters(stmt) {
|
|
37
39
|
const result = {}
|
|
38
40
|
const info = stmt.getParameterInfo()
|
|
@@ -50,7 +52,7 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
|
|
|
50
52
|
dbc.prepare(sql, function (err, stmt) {
|
|
51
53
|
if (err) {
|
|
52
54
|
err.query = sql
|
|
53
|
-
if (values) err.values = values
|
|
55
|
+
if (values) err.values = SANITIZE_VALUES ? ['***'] : values
|
|
54
56
|
return reject(err)
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -76,7 +78,7 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
|
|
|
76
78
|
if (err) {
|
|
77
79
|
stmt.drop(() => {})
|
|
78
80
|
err.query = sql
|
|
79
|
-
if (values) err.values = values
|
|
81
|
+
if (values) err.values = SANITIZE_VALUES ? ['***'] : values
|
|
80
82
|
return reject(err)
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -99,7 +101,7 @@ function _executeSimpleSQL(dbc, sql, values) {
|
|
|
99
101
|
const res = sql.match(regex)
|
|
100
102
|
if (res) sql = sql.replace(regex, '') + ' ' + res[0]
|
|
101
103
|
|
|
102
|
-
LOG._debug && LOG.debug(
|
|
104
|
+
LOG._debug && LOG.debug(sql, SANITIZE_VALUES ? ['***'] : values)
|
|
103
105
|
return new Promise((resolve, reject) => {
|
|
104
106
|
// hana-client only accepts arrays
|
|
105
107
|
if (dbc.name !== 'hdb' && typeof values === 'object') {
|
|
@@ -149,6 +151,10 @@ function _processExpand(model, dbc, cqn, user, locale, txTimestamp) {
|
|
|
149
151
|
|
|
150
152
|
function executeSelectCQN(model, dbc, query, user, locale, txTimestamp) {
|
|
151
153
|
if (hasExpand(query)) {
|
|
154
|
+
// expand: '**' or '*3' is handled by new impl
|
|
155
|
+
if (query.SELECT.columns.some(c => c.expand && typeof c.expand === 'string' && /^\*{1}[\d|*]+/.test(c.expand))) {
|
|
156
|
+
return expandV2(model, dbc, query, user, locale, txTimestamp, executeSelectCQN)
|
|
157
|
+
}
|
|
152
158
|
return _processExpand(model, dbc, query, user, locale, txTimestamp)
|
|
153
159
|
}
|
|
154
160
|
|