@sap/cds 5.6.2 → 5.7.2
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 +133 -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 +7 -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 +13 -4
- 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 +4 -2
- package/lib/log/index.js +2 -2
- package/lib/ql/Whereable.js +1 -0
- 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 -65
- 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 +26 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +5 -5
- 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/rest-to-cqn/index.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
- package/libx/_runtime/cds-services/services/Service.js +0 -6
- package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -7
- 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 -35
- 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/paging.js +2 -2
- package/libx/_runtime/common/generic/put.js +4 -10
- package/libx/_runtime/common/generic/sorting.js +12 -30
- 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 +297 -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/expand/expand-v2.js +130 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +38 -52
- package/libx/_runtime/db/expand/index.js +3 -1
- package/libx/_runtime/db/generic/arrayed.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/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 +155 -57
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +0 -4
- package/libx/_runtime/fiori/uiflex/index.js +1 -1
- package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +6 -4
- 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.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} +28 -16
- package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
- package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +182 -118
- package/libx/odata/index.js +18 -15
- 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
|
@@ -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
|
|
@@ -257,12 +282,29 @@ const _getOuterMostColumns = (columnsFromRequest, additionalDraftColumns) => {
|
|
|
257
282
|
return columns
|
|
258
283
|
}
|
|
259
284
|
|
|
285
|
+
// adds base columns 'InProcessByUser' and 'CreatedByUser' to columns param if needed
|
|
286
|
+
// those are required for calculating 'DraftIsProcessedByMe' and 'DraftIsCreatedByMe'
|
|
287
|
+
const _ensureDraftAdminColumnsForCalculation = columns => {
|
|
288
|
+
columns.forEach((c, i) => {
|
|
289
|
+
if (c.ref && c.ref[0] === 'DraftIsCreatedByMe' && !columns.find(e => e.ref && e.ref[0] === 'CreatedByUser')) {
|
|
290
|
+
columns.push({ ref: ['CreatedByUser'] })
|
|
291
|
+
} else if (
|
|
292
|
+
c.ref &&
|
|
293
|
+
c.ref[0] === 'DraftIsProcessedByMe' &&
|
|
294
|
+
!columns.find(e => e.ref && e.ref[0] === 'InProcessByUser')
|
|
295
|
+
) {
|
|
296
|
+
columns.push({ ref: ['InProcessByUser'] })
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
260
301
|
const _draftAdminTable = req => {
|
|
261
302
|
const { table } = _getTableName(req)
|
|
262
303
|
|
|
263
304
|
let cqn = SELECT.from(table)
|
|
264
305
|
if (req.query.SELECT.columns) {
|
|
265
306
|
cqn = cqn.columns(...req.query.SELECT.columns)
|
|
307
|
+
_ensureDraftAdminColumnsForCalculation(cqn.SELECT.columns)
|
|
266
308
|
}
|
|
267
309
|
|
|
268
310
|
return {
|
|
@@ -282,21 +324,24 @@ const _allInactive = (req, columns) => {
|
|
|
282
324
|
_getDefaultDraftProperties({ hasDraft: false, isActive: false, withDraftUUID: false })
|
|
283
325
|
)
|
|
284
326
|
|
|
285
|
-
const ids = filterKeys(req.target.keys)
|
|
286
327
|
const isCount = columns.some(element => element.func === 'count')
|
|
287
328
|
|
|
329
|
+
// ensure only own drafts are read
|
|
288
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))
|
|
289
340
|
|
|
290
341
|
if (isCount) {
|
|
291
342
|
cqn.columns(...outerMostColumns)
|
|
292
343
|
} else {
|
|
293
344
|
cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), { ref: ['HasActiveEntity'] })
|
|
294
|
-
cqn.leftJoin(ensureNoDraftsSuffix(table.ref[0]) + ' as active').on(`${table.as}.${ids[0]} = active.${ids[0]}`)
|
|
295
|
-
|
|
296
|
-
for (let i = 1; i < ids.length; i++) {
|
|
297
|
-
// REVISIT: this is extremely expensive as it repeatedly invokes the compiler's cds.parse.expr -> better extend plain CQN yourself here
|
|
298
|
-
cqn.and(`${table.as}.${ids[i]} = active.${ids[i]}`)
|
|
299
|
-
}
|
|
300
345
|
}
|
|
301
346
|
|
|
302
347
|
cqn.where(req.query.SELECT.where)
|
|
@@ -490,11 +535,11 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
|
|
|
490
535
|
subSelect
|
|
491
536
|
)
|
|
492
537
|
|
|
493
|
-
subSelect.SELECT.where = _getWhereWithAppendedDraftRestrictions(subSelect.SELECT.where, req)
|
|
494
|
-
|
|
495
538
|
const outerMostColumns = _getOuterMostColumns(columns, draftColumns)
|
|
496
539
|
|
|
497
|
-
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])
|
|
498
543
|
|
|
499
544
|
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_IN_PROCESS }
|
|
500
545
|
}
|
|
@@ -581,13 +626,21 @@ const _getWhereForActive = where => {
|
|
|
581
626
|
return activeWhere
|
|
582
627
|
}
|
|
583
628
|
|
|
584
|
-
const _siblingEntity = (
|
|
629
|
+
const _siblingEntity = (
|
|
630
|
+
{ query, target, nav, params },
|
|
631
|
+
columns,
|
|
632
|
+
model,
|
|
633
|
+
draftAdminAlias,
|
|
634
|
+
parentQuery,
|
|
635
|
+
siblingIndex,
|
|
636
|
+
req
|
|
637
|
+
) => {
|
|
585
638
|
const parentLinks = parentQuery ? _findJoinInQuery(query, parentQuery.SELECT.from.as) : []
|
|
586
639
|
const keys = (nav[siblingIndex + 1].where && (params[siblingIndex] || params[0])) || {}
|
|
587
640
|
const siblingQuery = query.SELECT.where[query.SELECT.where.indexOf('exists') + 1]
|
|
588
641
|
const onCond = _findJoinInQuery(siblingQuery, target.as)
|
|
589
642
|
const siblingAlias = siblingQuery.SELECT.from.as
|
|
590
|
-
const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond)
|
|
643
|
+
const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req)
|
|
591
644
|
const isSiblingDraft = subScenario
|
|
592
645
|
? subScenario.isSiblingActive || subScenario.scenario === 'ACTIVE' || subScenario.scenario === 'ALL_ACTIVE'
|
|
593
646
|
: keys.IsActiveEntity && keys.IsActiveEntity !== 'false'
|
|
@@ -635,12 +688,17 @@ const _siblingEntity = ({ query, target, nav, params }, columns, model, draftAdm
|
|
|
635
688
|
return { cqn, scenario: SCENARIO.SIBLING_ENTITY, isSiblingActive: !isSiblingDraft }
|
|
636
689
|
}
|
|
637
690
|
|
|
638
|
-
function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond) {
|
|
691
|
+
function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req) {
|
|
639
692
|
if (nav[siblingIndex + 1].where) return
|
|
640
693
|
let subScenario
|
|
641
694
|
const subNav = nav.slice(siblingIndex + 1)
|
|
642
695
|
const subSiblingIndex = subNav.indexOf('SiblingEntity')
|
|
643
|
-
const subReq = {
|
|
696
|
+
const subReq = {
|
|
697
|
+
query: siblingQuery,
|
|
698
|
+
target: model.definitions[target.name],
|
|
699
|
+
params: [...params].reverse(),
|
|
700
|
+
user: req.user
|
|
701
|
+
}
|
|
644
702
|
if (subSiblingIndex > -1) {
|
|
645
703
|
subScenario = _getSiblingScenario(subReq, [{ val: 1 }], model, subSiblingIndex, subNav, params)
|
|
646
704
|
if (subSiblingIndex > 0) {
|
|
@@ -653,8 +711,8 @@ function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, mo
|
|
|
653
711
|
subReq.query = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
|
|
654
712
|
const existsIdx = siblingQuery.SELECT.where.indexOf('exists')
|
|
655
713
|
if (existsIdx > -1) subReq.query.where(siblingQuery.SELECT.where.slice(existsIdx, existsIdx + 2))
|
|
656
|
-
const
|
|
657
|
-
subScenario = _generateCQN(
|
|
714
|
+
const subOrigFrom = { ref: [...subNav].reverse() }
|
|
715
|
+
subScenario = _generateCQN(subOrigFrom, subReq, [{ val: 1 }], model)
|
|
658
716
|
subScenario.cqn.where(onCond)
|
|
659
717
|
}
|
|
660
718
|
return subScenario
|
|
@@ -671,7 +729,15 @@ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
|
|
|
671
729
|
}
|
|
672
730
|
}
|
|
673
731
|
const target = { name: query.SELECT.from.ref[0].id || query.SELECT.from.ref[0], as: query.SELECT.from.as }
|
|
674
|
-
return _siblingEntity(
|
|
732
|
+
return _siblingEntity(
|
|
733
|
+
{ query, target, params, nav },
|
|
734
|
+
columns,
|
|
735
|
+
model,
|
|
736
|
+
draftAdminAlias,
|
|
737
|
+
parentQuery,
|
|
738
|
+
siblingIndex,
|
|
739
|
+
req
|
|
740
|
+
)
|
|
675
741
|
}
|
|
676
742
|
return _getSiblingQueryFromWhere(req.query, siblingIndex)
|
|
677
743
|
}
|
|
@@ -714,7 +780,7 @@ const _getDraftDoc = (req, draftName, draftWhere) => {
|
|
|
714
780
|
|
|
715
781
|
const _getOrderByEnrichedColumns = (orderBy, columns) => {
|
|
716
782
|
const enrichedCol = []
|
|
717
|
-
if (orderBy.length > 1) {
|
|
783
|
+
if (orderBy && orderBy.length > 1) {
|
|
718
784
|
const colNames = columns.map(el => el.ref[el.ref.length - 1])
|
|
719
785
|
// REVISIT: GET Books?$select=title&$expand=NotBooks($select=pages)&$orderby=NotBooks/title - what's then?
|
|
720
786
|
for (const el of orderBy) {
|
|
@@ -740,10 +806,13 @@ const _replaceDraftAlias = where => {
|
|
|
740
806
|
|
|
741
807
|
const _poorMansAlias4 = xpr => '_' + xpr.ref.join('_') + '_'
|
|
742
808
|
|
|
743
|
-
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
|
|
809
|
+
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model) => {
|
|
744
810
|
const draftActiveWhere = _getWhereForActive(draftWhere)
|
|
745
811
|
const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
|
|
812
|
+
activeDocs.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
813
|
+
convertWhereExists(activeDocs, model, {})
|
|
746
814
|
|
|
815
|
+
// @restrict.where not applicable for drafts (I can ALWAYS read mine)
|
|
747
816
|
_replaceDraftAlias(draftWhere)
|
|
748
817
|
const draftDocs = _getDraftDoc(req, draftName, draftWhere)
|
|
749
818
|
|
|
@@ -829,7 +898,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
|
|
|
829
898
|
.columns(..._filterDraftColumnsBySelected(DRAFT_COLUMNS_CASTED, req.query.SELECT.columns))
|
|
830
899
|
}
|
|
831
900
|
|
|
832
|
-
const _excludeActiveDraftExists = (req, draftWhere, columns) => {
|
|
901
|
+
const _excludeActiveDraftExists = (req, draftWhere, columns, model) => {
|
|
833
902
|
const { table, name } = _getTableName(req, true)
|
|
834
903
|
const draftName = table.ref[0]
|
|
835
904
|
|
|
@@ -848,10 +917,8 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
|
|
|
848
917
|
subSelect.where([{ ref: [ensureNoDraftsSuffix(req.target.name), key] }, '=', { ref: [draftName, key] }])
|
|
849
918
|
}
|
|
850
919
|
|
|
851
|
-
draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
|
|
852
|
-
|
|
853
920
|
draftWhere = removeIsActiveEntityRecursively(draftWhere)
|
|
854
|
-
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere)
|
|
921
|
+
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere, model)
|
|
855
922
|
cqn.SELECT.from.as = name
|
|
856
923
|
|
|
857
924
|
if (cqn.SELECT.orderBy) {
|
|
@@ -883,13 +950,13 @@ const _validatedActiveWithoutDraft = (req, draftWhere, draftParameters, columns)
|
|
|
883
950
|
_isValidActiveWithoutDraft(draftParameters.isActiveEntity, draftParameters.hasDraftEntity) &&
|
|
884
951
|
_activeWithoutDraft(req, draftWhere, columns)
|
|
885
952
|
|
|
886
|
-
const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, columns) => {
|
|
953
|
+
const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, columns, model) => {
|
|
887
954
|
const { isActiveEntity, siblingIsActive, draftInProcessByUser } = draftParameters
|
|
888
955
|
if (
|
|
889
956
|
!draftInProcessByUser &&
|
|
890
957
|
_isValidExcludeActiveDraftExists(draftParameters.isActiveEntity, draftParameters.siblingIsActive)
|
|
891
958
|
)
|
|
892
|
-
return _excludeActiveDraftExists(req, draftWhere, columns)
|
|
959
|
+
return _excludeActiveDraftExists(req, draftWhere, columns, model)
|
|
893
960
|
if (
|
|
894
961
|
draftInProcessByUser.op === '!=' &&
|
|
895
962
|
_isValidWithDraftLocked(isActiveEntity, siblingIsActive, draftInProcessByUser)
|
|
@@ -908,9 +975,7 @@ const _draftInSubSelect = (where, req) => {
|
|
|
908
975
|
if (SELECT && SELECT.where) {
|
|
909
976
|
const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
|
|
910
977
|
if (isActiveEntity) {
|
|
911
|
-
|
|
912
|
-
if (isFalse) SELECT.where = _getWhereWithAppendedDraftRestrictions(SELECT.where, req)
|
|
913
|
-
return isFalse
|
|
978
|
+
return _isFalse(isActiveEntity.value.val)
|
|
914
979
|
}
|
|
915
980
|
|
|
916
981
|
return _draftInSubSelect(SELECT.where, req)
|
|
@@ -923,10 +988,16 @@ const _draftInSubSelect = (where, req) => {
|
|
|
923
988
|
const _isDraftAdminScenario = req =>
|
|
924
989
|
req.target.query && req.target.query._target && req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
|
|
925
990
|
|
|
926
|
-
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
991
|
+
const _generateCQN = (originalFrom, req, columns, model) => {
|
|
992
|
+
const nav = [...originalFrom.ref].reverse() || []
|
|
993
|
+
let siblingIndex = nav.indexOf('SiblingEntity')
|
|
994
|
+
|
|
995
|
+
// it can also be a property access (new parser), then we must shift it
|
|
996
|
+
if (siblingIndex === 1 && req.target.elements[nav[0]]) {
|
|
997
|
+
nav.shift()
|
|
998
|
+
siblingIndex = siblingIndex - 1
|
|
999
|
+
}
|
|
1000
|
+
|
|
930
1001
|
let siblingScenario
|
|
931
1002
|
if (siblingIndex > -1) {
|
|
932
1003
|
siblingScenario = _getSiblingScenario(req, columns, model, siblingIndex, nav)
|
|
@@ -958,8 +1029,6 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
958
1029
|
}
|
|
959
1030
|
|
|
960
1031
|
if (!draftParameters.isActiveEntity) {
|
|
961
|
-
// _draftInSubSelect adds draft restrictions in case check is truthy
|
|
962
|
-
// -> not nice but works for now and we don't need to go in recursively again
|
|
963
1032
|
if (_draftInSubSelect(req.query.SELECT.where, req) || (siblingScenario && !siblingScenario.isSiblingActive)) {
|
|
964
1033
|
// this is only the case when navigating into tree
|
|
965
1034
|
return _allInactive(req, columns)
|
|
@@ -972,23 +1041,20 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
972
1041
|
}
|
|
973
1042
|
|
|
974
1043
|
if (draftParameters.siblingIsActive) {
|
|
975
|
-
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns)
|
|
1044
|
+
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns, model)
|
|
976
1045
|
}
|
|
977
1046
|
|
|
978
1047
|
return _validatedDraftOfWhichIAmOwner(req, req.query.SELECT.where, draftParameters, columns)
|
|
979
1048
|
}
|
|
980
1049
|
|
|
981
|
-
const _getColumns = ({ query: { SELECT } }, model) => {
|
|
1050
|
+
const _getColumns = ({ query: { SELECT }, target }, model) => {
|
|
982
1051
|
return SELECT.columns
|
|
983
1052
|
? SELECT.columns.filter(
|
|
984
1053
|
col =>
|
|
985
1054
|
(col.ref && !DRAFT_COLUMNS.includes(col.ref[col.ref.length - 1])) ||
|
|
986
1055
|
(!col.ref && !DRAFT_COLUMNS.includes(col))
|
|
987
1056
|
)
|
|
988
|
-
: getColumns(
|
|
989
|
-
onlyNames: true,
|
|
990
|
-
removeIgnore: true
|
|
991
|
-
})
|
|
1057
|
+
: getColumns(target, { onlyNames: true, removeIgnore: true })
|
|
992
1058
|
}
|
|
993
1059
|
|
|
994
1060
|
const _isIsActiveEntity = element => element.ref && element.ref[element.ref.length - 1] === 'IsActiveEntity'
|
|
@@ -1018,6 +1084,10 @@ const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
|
|
|
1018
1084
|
}
|
|
1019
1085
|
} else if (element.SELECT) {
|
|
1020
1086
|
_adaptSubSelects(element, scenario)
|
|
1087
|
+
} else if (element.xpr) {
|
|
1088
|
+
for (const ele of element.xpr.filter(e => e.SELECT)) {
|
|
1089
|
+
_adaptSubSelects(ele, scenario)
|
|
1090
|
+
}
|
|
1021
1091
|
}
|
|
1022
1092
|
}
|
|
1023
1093
|
}
|
|
@@ -1119,12 +1189,26 @@ const _getLocalizedEntity = (model, target, user) => {
|
|
|
1119
1189
|
return localizedEntity || model.definitions[`${prefix}.${target.name}`]
|
|
1120
1190
|
}
|
|
1121
1191
|
|
|
1192
|
+
const _getLastSubQuery = query => (query.SELECT.from.SELECT ? _getLastSubQuery(query.SELECT.from) : query)
|
|
1193
|
+
const _setLastSubQuery = (query, last, prev = query) => {
|
|
1194
|
+
if (query.SELECT.from.SELECT) return _setLastSubQuery(query.SELECT.from, last, query)
|
|
1195
|
+
else prev.SELECT.from = _copyCQNPartial(last)
|
|
1196
|
+
return prev
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
const _adaptDraftAdminExpand = cqn => {
|
|
1200
|
+
const draftAdminExpand =
|
|
1201
|
+
cqn.SELECT.columns && cqn.SELECT.columns.find(c => c.expand && c.ref[0] === 'DraftAdministrativeData')
|
|
1202
|
+
if (draftAdminExpand) {
|
|
1203
|
+
_ensureDraftAdminColumnsForCalculation(draftAdminExpand.expand)
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1122
1207
|
/**
|
|
1123
1208
|
* Generic Handler for READ requests in the context of draft.
|
|
1124
1209
|
*
|
|
1125
1210
|
* @param req
|
|
1126
1211
|
*/
|
|
1127
|
-
// eslint-disable-next-line complexity
|
|
1128
1212
|
const _handler = async function (req) {
|
|
1129
1213
|
// handle localized here as it was previously handled for req.target
|
|
1130
1214
|
req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
|
|
@@ -1132,6 +1216,8 @@ const _handler = async function (req) {
|
|
|
1132
1216
|
// REVISIT
|
|
1133
1217
|
delete req.query._validationQuery
|
|
1134
1218
|
|
|
1219
|
+
const originalFrom = _copyCQNPartial(req.query.SELECT.from)
|
|
1220
|
+
|
|
1135
1221
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
1136
1222
|
const sqlQuery = cqn2cqn4sql(req.query, this.model, { draft: true })
|
|
1137
1223
|
|
|
@@ -1146,14 +1232,26 @@ const _handler = async function (req) {
|
|
|
1146
1232
|
reqClone.query._streaming = true
|
|
1147
1233
|
return cds.tx(req).run(reqClone.query)
|
|
1148
1234
|
}
|
|
1235
|
+
let cqnScenario
|
|
1149
1236
|
|
|
1150
|
-
|
|
1237
|
+
// to replace scenario CQNs for queries with $apply SELECT chain (new odata2cqn parser)
|
|
1238
|
+
// just to make existing tests working with new parser. not really tested, not needed to be supported
|
|
1239
|
+
if (reqClone.query.SELECT.from.SELECT) {
|
|
1240
|
+
const subQueryReq = { __proto__: req, query: _copyCQNPartial(_getLastSubQuery(reqClone.query)) }
|
|
1241
|
+
cqnScenario = _generateCQN(originalFrom.SELECT.from, subQueryReq, _getColumns(subQueryReq, this.model), this.model)
|
|
1242
|
+
cqnScenario.cqn = _setLastSubQuery(reqClone.query, cqnScenario.cqn)
|
|
1243
|
+
} else {
|
|
1244
|
+
cqnScenario = _generateCQN(originalFrom, reqClone, _getColumns(reqClone, this.model), this.model)
|
|
1245
|
+
}
|
|
1151
1246
|
|
|
1152
1247
|
if (!cqnScenario) {
|
|
1153
1248
|
req.reject(400)
|
|
1154
1249
|
return
|
|
1155
1250
|
}
|
|
1156
1251
|
|
|
1252
|
+
// ensure base columns for calculation are selected in draft admin expand
|
|
1253
|
+
_adaptDraftAdminExpand(cqnScenario.cqn)
|
|
1254
|
+
|
|
1157
1255
|
if (cqnScenario.scenario === SCENARIO.ALL_ACTIVE && cqnScenario.cqn.SELECT.where) {
|
|
1158
1256
|
cqnScenario.cqn.SELECT.where = removeIsActiveEntityRecursively(cqnScenario.cqn.SELECT.where)
|
|
1159
1257
|
}
|
|
@@ -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
|
|
@@ -55,13 +55,15 @@ const _addViews = csn => {
|
|
|
55
55
|
})
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const _needsQuotations = t => t instanceof cds.builtin.classes.string || t instanceof cds.builtin.classes.date
|
|
59
|
+
|
|
58
60
|
const _handleDefaults = async (extension, dbEntity, req, cds, draftEntity) => {
|
|
59
61
|
const ext = Object.keys(extension.elements)
|
|
60
62
|
.filter(key => extension.elements[key].default)
|
|
61
63
|
.map(key => {
|
|
62
64
|
const element = extension.elements[key]
|
|
63
65
|
const t = cds.model.definitions[element.type] || cds.builtin.types[element.type]
|
|
64
|
-
const value = t && t
|
|
66
|
+
const value = t && _needsQuotations(t) ? `"${element.default.val}"` : element.default.val
|
|
65
67
|
return `"${key}":${value}`
|
|
66
68
|
})
|
|
67
69
|
|
|
@@ -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
|
|