@sap/cds 5.7.5 → 5.8.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 +97 -0
- package/app/fiori/routes.js +1 -1
- package/bin/deploy/to-hana/cfUtil.js +251 -138
- package/bin/deploy/to-hana/gitUtil.js +55 -0
- package/bin/deploy/to-hana/hana.js +92 -93
- package/bin/deploy/to-hana/hdiDeployUtil.js +42 -27
- package/bin/deploy/to-hana/index.js +14 -13
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +1 -1
- package/bin/version.js +1 -0
- package/lib/compile/cdsc.js +0 -6
- package/lib/compile/resolve.js +1 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/core/classes.js +21 -1
- package/lib/env/index.js +3 -2
- package/lib/env/requires.js +4 -0
- package/lib/i18n/localize.js +5 -8
- package/lib/index.js +1 -0
- package/lib/log/errors.js +1 -1
- package/lib/log/format/kibana.js +3 -3
- package/lib/ql/SELECT.js +2 -2
- package/lib/req/cds-context.js +1 -1
- package/lib/req/context.js +1 -1
- package/lib/serve/Transaction.js +9 -5
- package/lib/serve/index.js +13 -21
- package/lib/utils/tests.js +90 -66
- package/libx/_runtime/audit/generic/personal/modification.js +0 -8
- package/libx/_runtime/auth/index.js +7 -6
- package/libx/_runtime/auth/strategies/dwc.js +43 -0
- package/libx/_runtime/auth/utils.js +24 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +11 -32
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +24 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +43 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +11 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +17 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +12 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +23 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +7 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +19 -47
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +4 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/CommandExecutor.js +0 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ErrorJsonSerializer.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +6 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +41 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +1 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +60 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +47 -10
- package/libx/_runtime/cds-services/adapter/rest/Rest.js +22 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +8 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +3 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +5 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +15 -16
- package/libx/_runtime/cds-services/services/utils/differ.js +2 -8
- package/libx/_runtime/common/aspects/Association.js +16 -0
- package/libx/_runtime/common/composition/data.js +28 -37
- package/libx/_runtime/common/composition/delete.js +107 -58
- package/libx/_runtime/common/composition/index.js +3 -3
- package/libx/_runtime/common/composition/insert.js +14 -27
- package/libx/_runtime/common/composition/tree.js +1 -1
- package/libx/_runtime/common/composition/update.js +39 -34
- package/libx/_runtime/common/error/frontend.js +19 -5
- package/libx/_runtime/common/generic/auth.js +20 -85
- package/libx/_runtime/common/generic/crud.js +22 -1
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/utils/cqn.js +2 -6
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +95 -122
- package/libx/_runtime/common/utils/csn.js +29 -6
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +21 -1
- package/libx/_runtime/common/utils/keys.js +2 -1
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +12 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +27 -13
- package/libx/_runtime/common/utils/search2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/structured.js +10 -4
- package/libx/_runtime/common/utils/vcap.js +27 -10
- package/libx/_runtime/db/data-conversion/post-processing.js +20 -13
- package/libx/_runtime/db/expand/expand-v2.js +21 -12
- package/libx/_runtime/db/expand/expandCQNToJoin.js +67 -26
- package/libx/_runtime/db/expand/index.js +3 -0
- package/libx/_runtime/db/generic/create.js +0 -10
- package/libx/_runtime/db/generic/index.js +3 -0
- package/libx/_runtime/db/generic/read.js +2 -24
- package/libx/_runtime/db/generic/rewrite.js +1 -3
- package/libx/_runtime/db/generic/update.js +1 -1
- package/libx/_runtime/db/query/delete.js +10 -4
- package/libx/_runtime/db/query/insert.js +3 -4
- package/libx/_runtime/db/query/read.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +9 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +3 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -3
- package/libx/_runtime/db/sql-builder/index.js +3 -0
- package/libx/_runtime/db/utils/columns.js +5 -2
- package/libx/_runtime/db/utils/deep.js +16 -14
- package/libx/_runtime/db/utils/generateAliases.js +56 -6
- package/libx/_runtime/fiori/generic/before.js +73 -49
- package/libx/_runtime/fiori/generic/edit.js +14 -18
- package/libx/_runtime/fiori/generic/patch.js +8 -11
- package/libx/_runtime/fiori/generic/read.js +20 -19
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -4
- package/libx/_runtime/fiori/utils/handler.js +1 -11
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/dynatrace.js +11 -5
- package/libx/_runtime/hana/execute.js +132 -19
- package/libx/_runtime/hana/search.js +3 -3
- package/libx/_runtime/hana/search2cqn4sql.js +23 -25
- package/libx/_runtime/hana/searchToContains.js +1 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +0 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/messaging/file-based.js +3 -1
- package/libx/_runtime/messaging/service.js +4 -1
- package/libx/_runtime/remote/utils/client.js +41 -24
- package/libx/_runtime/remote/utils/data.js +54 -12
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/conversion.js +10 -0
- package/libx/_runtime/types/api.js +2 -2
- package/libx/gql/resolvers/crud/update.js +8 -5
- package/libx/gql/resolvers/parse/ast/enrich.js +1 -0
- package/libx/odata/afterburner.js +29 -6
- package/libx/odata/cqn2odata.js +9 -0
- package/libx/odata/grammar.pegjs +49 -21
- package/libx/odata/index.js +2 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -2
- package/libx/rest/RestAdapter.js +29 -1
- package/libx/rest/middleware/auth.js +1 -3
- package/libx/rest/middleware/parse.js +1 -0
- package/package.json +1 -1
- package/server.js +1 -1
- package/bin/deploy/to-hana/logger.js +0 -27
- package/bin/deploy/to-hana/runCommand.js +0 -113
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +0 -37
- package/libx/_runtime/common/utils/auth.js +0 -16
|
@@ -10,7 +10,6 @@ const {
|
|
|
10
10
|
} = require('../utils/handler')
|
|
11
11
|
const { getKeysCondition } = require('../utils/where')
|
|
12
12
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
13
|
-
|
|
14
13
|
const { DRAFT_COLUMNS_CQN } = require('../../common/constants/draft')
|
|
15
14
|
|
|
16
15
|
const _getSelectCQN = (model, { data, target: { name } }, keysCondition, checkUser = true) => {
|
|
@@ -24,6 +23,7 @@ const _getSelectCQN = (model, { data, target: { name } }, keysCondition, checkUs
|
|
|
24
23
|
),
|
|
25
24
|
...DRAFT_COLUMNS_CQN
|
|
26
25
|
]
|
|
26
|
+
|
|
27
27
|
if (checkUser) {
|
|
28
28
|
columns.push({ ref: ['DRAFT.DraftAdministrativeData', 'inProcessByUser'], as: 'draftAdmin_inProcessByUser' })
|
|
29
29
|
}
|
|
@@ -42,14 +42,16 @@ const _getSelectCQN = (model, { data, target: { name } }, keysCondition, checkUs
|
|
|
42
42
|
|
|
43
43
|
const _getUpdateDraftCQN = ({ query, target: { name } }, keysCondition) => {
|
|
44
44
|
const set = {}
|
|
45
|
+
|
|
45
46
|
for (const entry in query.UPDATE.data) {
|
|
46
47
|
if (entry === 'DraftAdministrativeData_DraftUUID') {
|
|
47
48
|
continue
|
|
48
49
|
}
|
|
50
|
+
|
|
49
51
|
set[entry] = query.UPDATE.data[entry]
|
|
50
52
|
}
|
|
51
|
-
if (set.IsActiveEntity) set.IsActiveEntity = false
|
|
52
53
|
|
|
54
|
+
if (set.IsActiveEntity) set.IsActiveEntity = false
|
|
53
55
|
return UPDATE(ensureDraftsSuffix(name)).data(set).where(keysCondition)
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -65,9 +67,7 @@ const _handler = async function (req) {
|
|
|
65
67
|
if (req.data.IsActiveEntity === 'true') req.reject(400, 'Patch can only be applied to a draft entity')
|
|
66
68
|
|
|
67
69
|
const keysCondition = getKeysCondition(req.target, req.data)
|
|
68
|
-
|
|
69
70
|
const dbtx = cds.tx(req)
|
|
70
|
-
|
|
71
71
|
let result = await dbtx.run(_getSelectCQN(this.model, req, keysCondition))
|
|
72
72
|
|
|
73
73
|
// Potential timeout scenario supported
|
|
@@ -80,19 +80,16 @@ const _handler = async function (req) {
|
|
|
80
80
|
const updateDraftAdminCQN = getUpdateDraftAdminCQN(req, result[0].DraftAdministrativeData_DraftUUID)
|
|
81
81
|
|
|
82
82
|
await Promise.all([dbtx.run(updateDraftCQN), dbtx.run(updateDraftAdminCQN)])
|
|
83
|
-
|
|
84
83
|
result = await dbtx.run(_getSelectCQN(this.model, req, keysCondition, false))
|
|
85
|
-
if (result.length === 0)
|
|
86
|
-
req.reject(404)
|
|
87
|
-
}
|
|
88
|
-
|
|
84
|
+
if (result.length === 0) req.reject(404)
|
|
89
85
|
removeDraftUUIDIfNecessary(result[0], req)
|
|
90
|
-
|
|
91
86
|
return result[0]
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
module.exports = cds.service.impl(function () {
|
|
95
|
-
|
|
90
|
+
const entities = Object.values(this.entities).filter(e => e._isDraftEnabled)
|
|
91
|
+
|
|
92
|
+
for (const entity of entities) {
|
|
96
93
|
this.on('PATCH', entity, _handler)
|
|
97
94
|
}
|
|
98
95
|
})
|
|
@@ -3,10 +3,8 @@ const { SELECT } = cds.ql
|
|
|
3
3
|
|
|
4
4
|
const { cqn2cqn4sql, convertWhereExists } = require('../../common/utils/cqn2cqn4sql')
|
|
5
5
|
const { getElementDeep } = require('../../common/utils/csn')
|
|
6
|
-
|
|
7
6
|
const { DRAFT_COLUMNS, DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
|
|
8
7
|
const {
|
|
9
|
-
adaptStreamCQN,
|
|
10
8
|
addColumnAlias,
|
|
11
9
|
draftIsLocked,
|
|
12
10
|
ensureDraftsSuffix,
|
|
@@ -18,8 +16,8 @@ const {
|
|
|
18
16
|
filterKeys
|
|
19
17
|
} = require('../utils/handler')
|
|
20
18
|
const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
21
|
-
|
|
22
19
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
20
|
+
const { adaptStreamCQN } = require('../../cds-services/adapter/odata-v4/utils/stream')
|
|
23
21
|
|
|
24
22
|
const _findRootSubSelectFor = query => {
|
|
25
23
|
if (query.SELECT.where) {
|
|
@@ -779,13 +777,16 @@ const _getDraftDoc = (req, draftName, draftWhere) => {
|
|
|
779
777
|
return draftDocs
|
|
780
778
|
}
|
|
781
779
|
|
|
782
|
-
const _getOrderByEnrichedColumns = (orderBy, columns) => {
|
|
780
|
+
const _getOrderByEnrichedColumns = (orderBy, columns, entity) => {
|
|
783
781
|
const enrichedCol = []
|
|
784
782
|
if (orderBy && orderBy.length > 1) {
|
|
785
783
|
const colNames = columns.map(el => el.ref[el.ref.length - 1])
|
|
786
784
|
// REVISIT: GET Books?$select=title&$expand=NotBooks($select=pages)&$orderby=NotBooks/title - what's then?
|
|
787
785
|
for (const el of orderBy) {
|
|
788
|
-
|
|
786
|
+
// For associations we need to 'materialise' the resulting field, otherwise we cannot access it in an outer SELECT.
|
|
787
|
+
if (entity && entity.elements[el.ref[0]] && entity.elements[el.ref[0]].isAssociation) {
|
|
788
|
+
enrichedCol.push({ ref: [...el.ref], as: _poorMansAlias4(el) })
|
|
789
|
+
} else if (!DRAFT_COLUMNS.includes(el.ref[el.ref.length - 1]) && !colNames.includes(el.ref[el.ref.length - 1])) {
|
|
789
790
|
enrichedCol.push({ ref: [...el.ref] })
|
|
790
791
|
}
|
|
791
792
|
}
|
|
@@ -807,11 +808,11 @@ const _replaceDraftAlias = where => {
|
|
|
807
808
|
|
|
808
809
|
const _poorMansAlias4 = xpr => '_' + xpr.ref.join('_') + '_'
|
|
809
810
|
|
|
810
|
-
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model) => {
|
|
811
|
+
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model, entity) => {
|
|
811
812
|
const draftActiveWhere = _getWhereForActive(draftWhere)
|
|
812
813
|
const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
|
|
813
814
|
activeDocs.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
814
|
-
convertWhereExists(activeDocs, model, {})
|
|
815
|
+
convertWhereExists(activeDocs.SELECT, model, {})
|
|
815
816
|
|
|
816
817
|
// @restrict.where not applicable for drafts (I can ALWAYS read mine)
|
|
817
818
|
_replaceDraftAlias(draftWhere)
|
|
@@ -838,7 +839,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model) =>
|
|
|
838
839
|
return union.columns({ func: 'sum', args: [{ ref: ['$count'] }], as: '$count' })
|
|
839
840
|
}
|
|
840
841
|
|
|
841
|
-
const enrichedColumns = _getOrderByEnrichedColumns(req.query.SELECT.orderBy, columns)
|
|
842
|
+
const enrichedColumns = _getOrderByEnrichedColumns(req.query.SELECT.orderBy, columns, entity)
|
|
842
843
|
|
|
843
844
|
for (const col of enrichedColumns) {
|
|
844
845
|
// if we have columns for outer order by that may also be needed for joins, we need to duplicate them
|
|
@@ -915,12 +916,14 @@ const _excludeActiveDraftExists = (req, draftWhere, columns, model) => {
|
|
|
915
916
|
])
|
|
916
917
|
.where(_inProcessByUserWhere(req.user.id))
|
|
917
918
|
|
|
919
|
+
const targetName = ensureNoDraftsSuffix(req.target.name)
|
|
918
920
|
for (const key of _getTargetKeys(req)) {
|
|
919
|
-
subSelect.where([{ ref: [
|
|
921
|
+
subSelect.where([{ ref: [targetName, key] }, '=', { ref: [draftName, key] }])
|
|
920
922
|
}
|
|
921
923
|
|
|
924
|
+
const entity = model.definitions[targetName]
|
|
922
925
|
draftWhere = removeIsActiveEntityRecursively(draftWhere)
|
|
923
|
-
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere, model)
|
|
926
|
+
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere, model, entity)
|
|
924
927
|
cqn.SELECT.from.as = name
|
|
925
928
|
|
|
926
929
|
if (cqn.SELECT.orderBy) {
|
|
@@ -960,13 +963,16 @@ const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, column
|
|
|
960
963
|
)
|
|
961
964
|
return _excludeActiveDraftExists(req, draftWhere, columns, model)
|
|
962
965
|
if (
|
|
966
|
+
draftInProcessByUser &&
|
|
963
967
|
draftInProcessByUser.op === '!=' &&
|
|
964
968
|
_isValidWithDraftLocked(isActiveEntity, siblingIsActive, draftInProcessByUser)
|
|
965
969
|
) {
|
|
966
970
|
return _activeWithDraftInProcess(req, draftWhere, columns, req.user.id)
|
|
967
|
-
} else if (_isValidWithDraftTimeout(isActiveEntity, siblingIsActive, draftInProcessByUser)) {
|
|
971
|
+
} else if (draftInProcessByUser && _isValidWithDraftTimeout(isActiveEntity, siblingIsActive, draftInProcessByUser)) {
|
|
968
972
|
return _activeWithDraftInProcess(req, draftWhere, columns, null)
|
|
969
973
|
}
|
|
974
|
+
|
|
975
|
+
//
|
|
970
976
|
}
|
|
971
977
|
|
|
972
978
|
const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, columns) =>
|
|
@@ -1215,16 +1221,13 @@ const _handler = async function (req) {
|
|
|
1215
1221
|
// handle localized here as it was previously handled for req.target
|
|
1216
1222
|
req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
|
|
1217
1223
|
|
|
1218
|
-
// REVISIT
|
|
1219
|
-
delete req.query._validationQuery
|
|
1220
|
-
|
|
1221
1224
|
const originalFrom = _copyCQNPartial(req.query.SELECT.from)
|
|
1222
1225
|
|
|
1223
1226
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
1224
|
-
const
|
|
1227
|
+
const query4sql = cqn2cqn4sql(req.query, this.model, { _4fiori: true })
|
|
1225
1228
|
|
|
1226
1229
|
// do not clone with Object.assign as that would skip all non-enumerable properties
|
|
1227
|
-
const reqClone = { __proto__: req, query: _copyCQNPartial(
|
|
1230
|
+
const reqClone = { __proto__: req, query: _copyCQNPartial(query4sql) }
|
|
1228
1231
|
|
|
1229
1232
|
// ensure draft restrictions are copied to new query
|
|
1230
1233
|
reqClone.query._draftRestrictions = req.query._draftRestrictions
|
|
@@ -1234,6 +1237,7 @@ const _handler = async function (req) {
|
|
|
1234
1237
|
reqClone.query._streaming = true
|
|
1235
1238
|
return cds.tx(req).run(reqClone.query)
|
|
1236
1239
|
}
|
|
1240
|
+
|
|
1237
1241
|
let cqnScenario
|
|
1238
1242
|
|
|
1239
1243
|
// to replace scenario CQNs for queries with $apply SELECT chain (new odata2cqn parser)
|
|
@@ -1264,16 +1268,13 @@ const _handler = async function (req) {
|
|
|
1264
1268
|
)
|
|
1265
1269
|
|
|
1266
1270
|
_adaptSubSelects(cqnScenario.cqn, cqnScenario.scenario)
|
|
1267
|
-
|
|
1268
1271
|
_adaptAnnotationAliases(cqnScenario.cqn)
|
|
1269
1272
|
|
|
1270
1273
|
// unlocalize for db and after handlers as it was before
|
|
1271
1274
|
req.target = this.model.definitions[ensureUnlocalized(req.target.name)]
|
|
1272
1275
|
|
|
1273
1276
|
const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
|
|
1274
|
-
|
|
1275
1277
|
const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
|
|
1276
|
-
|
|
1277
1278
|
removeDraftUUIDIfNecessary(resultAsArray, req)
|
|
1278
1279
|
|
|
1279
1280
|
if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
|
|
@@ -45,7 +45,7 @@ const _handler = async function (req) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
48
|
-
const sqlQuery = cqn2cqn4sql(req.query, this.model, {
|
|
48
|
+
const sqlQuery = cqn2cqn4sql(req.query, this.model, { _4fiori: true })
|
|
49
49
|
if (req.query._streaming) {
|
|
50
50
|
sqlQuery._streaming = true
|
|
51
51
|
}
|
|
@@ -53,9 +53,6 @@ const _handler = async function (req) {
|
|
|
53
53
|
const hasDraftEntity = hasDraft(this.model.definitions, sqlQuery)
|
|
54
54
|
|
|
55
55
|
if (hasDraftEntity && sqlQuery.SELECT.where && sqlQuery.SELECT.where.length !== 0) {
|
|
56
|
-
// REVISIT
|
|
57
|
-
delete req.query._validationQuery
|
|
58
|
-
|
|
59
56
|
let cqnDraft = SELECT.from({
|
|
60
57
|
ref: [...sqlQuery.SELECT.from.ref],
|
|
61
58
|
as: sqlQuery.SELECT.from.as
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const { UPDATE, SELECT } = cds.ql
|
|
3
|
-
const { removeIsActiveEntityRecursively, isActiveEntityRequested } = require('./where')
|
|
4
3
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
5
4
|
const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require('../../common/utils/draft')
|
|
6
5
|
const getTemplate = require('../../common/utils/template')
|
|
@@ -126,7 +125,7 @@ const getEnrichedCQN = (cqn, select, draftWhere, scenarioAlias, addLimitOrder =
|
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
if (select.distinct) {
|
|
129
|
-
cqn.distinct
|
|
128
|
+
cqn.SELECT.distinct = true
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
const alias = (select.from && select.from.as) || scenarioAlias
|
|
@@ -249,14 +248,6 @@ const replaceRefWithDraft = ref => {
|
|
|
249
248
|
ref[0] = ensureDraftsSuffix(ref[0])
|
|
250
249
|
}
|
|
251
250
|
|
|
252
|
-
const adaptStreamCQN = cqn => {
|
|
253
|
-
if (isActiveEntityRequested(cqn.SELECT.where)) {
|
|
254
|
-
cqn.SELECT.where = removeIsActiveEntityRecursively(cqn.SELECT.where)
|
|
255
|
-
} else {
|
|
256
|
-
replaceRefWithDraft(cqn.SELECT.from.ref)
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
251
|
const draftIsLocked = lastChangedAt => {
|
|
261
252
|
// default timeout timer is 15 minutes
|
|
262
253
|
const DRAFT_CANCEL_TIMEOUT_IN_MS = ((cds.env.drafts && cds.env.drafts.cancellationTimeout) || 15) * 60 * 1000
|
|
@@ -289,7 +280,6 @@ module.exports = {
|
|
|
289
280
|
hasDraft,
|
|
290
281
|
proxifyToNoDraftsName,
|
|
291
282
|
addColumnAlias,
|
|
292
|
-
adaptStreamCQN,
|
|
293
283
|
replaceRefWithDraft,
|
|
294
284
|
getKeyProperty,
|
|
295
285
|
filterKeys,
|
|
@@ -41,7 +41,7 @@ class HanaDatabase extends DatabaseService {
|
|
|
41
41
|
this._insert = this._queries.insert(execute.insert)
|
|
42
42
|
this._read = this._queries.read(execute.select, execute.stream)
|
|
43
43
|
this._update = this._queries.update(execute.update, execute.select)
|
|
44
|
-
this._delete = this._queries.delete(execute.delete)
|
|
44
|
+
this._delete = this._queries.delete(execute.delete, execute.update)
|
|
45
45
|
this._run = this._queries.run(this._insert, this._read, this._update, this._delete, execute.cqn, execute.sql)
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const cds = require('../cds')
|
|
2
|
+
|
|
1
3
|
const convertToBoolean = boolean => {
|
|
2
4
|
if (boolean === null) {
|
|
3
5
|
return null
|
|
@@ -44,9 +46,18 @@ const HANA_TYPE_CONVERSION_MAP = new Map([
|
|
|
44
46
|
['cds.Integer64', convertInt64ToString],
|
|
45
47
|
['cds.DateTime', convertToISONoMillis],
|
|
46
48
|
['cds.Timestamp', convertToISO],
|
|
47
|
-
['cds.LargeString', convertToString]
|
|
49
|
+
['cds.LargeString', convertToString],
|
|
50
|
+
['cds.hana.CLOB', convertToString]
|
|
48
51
|
])
|
|
49
52
|
|
|
53
|
+
if (cds.env.features.bigjs) {
|
|
54
|
+
const Big = require('big.js')
|
|
55
|
+
const convertToBig = value => new Big(value)
|
|
56
|
+
|
|
57
|
+
HANA_TYPE_CONVERSION_MAP.set('cds.Integer64', convertToBig)
|
|
58
|
+
HANA_TYPE_CONVERSION_MAP.set('cds.Decimal', convertToBig)
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
module.exports = {
|
|
51
62
|
HANA_TYPE_CONVERSION_MAP
|
|
52
63
|
}
|
|
@@ -7,11 +7,12 @@ try {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const isDynatraceEnabled = () => {
|
|
10
|
-
return dynatrace.sdk !== undefined
|
|
10
|
+
return dynatrace.sdk !== undefined && !process.env.CDS_SKIP_DYNATRACE
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const _dynatraceResultCallback = function (tracer, cb) {
|
|
14
|
-
return function (err,
|
|
14
|
+
return function (err, ...args) {
|
|
15
|
+
const results = args.shift()
|
|
15
16
|
if (err) {
|
|
16
17
|
tracer.error(err)
|
|
17
18
|
} else {
|
|
@@ -19,7 +20,7 @@ const _dynatraceResultCallback = function (tracer, cb) {
|
|
|
19
20
|
rowsReturned: (results && results.length) || results
|
|
20
21
|
})
|
|
21
22
|
}
|
|
22
|
-
tracer.end(cb, err, results,
|
|
23
|
+
tracer.end(cb, err, results, ...args)
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -73,9 +74,14 @@ const dynatraceClient = (client, credentials, tenant) => {
|
|
|
73
74
|
// hana-client does not like decorating.
|
|
74
75
|
// because of that, we need to override the fn and pass the original fn for execution
|
|
75
76
|
const originalExecFn = client.exec
|
|
76
|
-
const originalPrepareFn = client.prepare
|
|
77
77
|
client.exec = _execUsingDynatrace(client, originalExecFn, dbInfo)
|
|
78
|
-
|
|
78
|
+
const originalPrepareFn = client.prepare
|
|
79
|
+
if (client.name === '@sap/hana-client') {
|
|
80
|
+
// client.prepare = ... doesn't work for hana-client
|
|
81
|
+
Object.defineProperty(client, 'prepare', { value: _preparedStmtUsingDynatrace(client, originalPrepareFn, dbInfo) })
|
|
82
|
+
} else {
|
|
83
|
+
client.prepare = _preparedStmtUsingDynatrace(client, originalPrepareFn, dbInfo)
|
|
84
|
+
}
|
|
79
85
|
|
|
80
86
|
return client
|
|
81
87
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const cds = require('../cds')
|
|
2
|
+
const LOG = cds.log('hana|db|sql')
|
|
3
|
+
|
|
1
4
|
const { HANA_TYPE_CONVERSION_MAP } = require('./conversion')
|
|
2
5
|
const CustomBuilder = require('./customBuilder')
|
|
3
6
|
const { sqlFactory } = require('../db/sql-builder/')
|
|
@@ -30,9 +33,6 @@ function _cqnToSQL(model, query, user, locale, txTimestamp) {
|
|
|
30
33
|
)
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
const cds = require('../cds')
|
|
34
|
-
const LOG = cds.log('hana|db|sql')
|
|
35
|
-
|
|
36
36
|
const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
37
37
|
|
|
38
38
|
function _getOutputParameters(stmt) {
|
|
@@ -48,32 +48,145 @@ function _getOutputParameters(stmt) {
|
|
|
48
48
|
return Object.keys(result).length > 0 ? result : undefined
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
const BINARY_TYPES = {
|
|
52
|
+
12: 'BINARY',
|
|
53
|
+
13: 'VARBINARY',
|
|
54
|
+
27: 'BLOB'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _getBinaries(stmt) {
|
|
58
|
+
// hdb vs. @sap/hana-client
|
|
59
|
+
const parameters = stmt.parameterMetadata || stmt.getParameterInfo()
|
|
60
|
+
const typeKey = stmt.parameterMetadata ? 'dataType' : 'nativeType'
|
|
61
|
+
return parameters.reduce((acc, cur, i) => {
|
|
62
|
+
if (BINARY_TYPES[cur[typeKey]]) acc.push(i)
|
|
63
|
+
return acc
|
|
64
|
+
}, [])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}={2})$/
|
|
68
|
+
|
|
69
|
+
function _isProcedureCall(sql) {
|
|
70
|
+
return sql.trim().match(/^call \s*"{0,1}\w*/i)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _getProcedureName(sql) {
|
|
74
|
+
const match = sql.trim().match(/^call \s*"{0,1}(\w*)/i)
|
|
75
|
+
return match && match[1]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _hdbGetResultForProcedure(rows, args, outParameters) {
|
|
79
|
+
// on hdb, rows already contains results for scalar params
|
|
80
|
+
const result = rows || {}
|
|
81
|
+
// merge table output params into scalar params
|
|
82
|
+
if (args && args.length && outParameters) {
|
|
83
|
+
const params = outParameters.filter(md => !(md.PARAMETER_NAME in rows))
|
|
84
|
+
for (let i = 0; i < args.length; i++) {
|
|
85
|
+
result[params[i].PARAMETER_NAME] = args[i]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function _hcGetResultForProcedure(stmt, resultSet, outParameters) {
|
|
92
|
+
const result = {}
|
|
93
|
+
// build result from scalar params
|
|
94
|
+
const paramInfo = stmt.getParameterInfo()
|
|
95
|
+
if (paramInfo.some(p => p.direction > 1)) {
|
|
96
|
+
for (let i = 0; i < paramInfo.length; i++) {
|
|
97
|
+
if (paramInfo[i].direction > 1) {
|
|
98
|
+
result[paramInfo[i].name] = stmt.getParameterValue(i)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// merge table output params into scalar params
|
|
103
|
+
if (outParameters && outParameters.length) {
|
|
104
|
+
const params = outParameters.filter(md => !(md.PARAMETER_NAME in result))
|
|
105
|
+
let i = 0
|
|
106
|
+
while (resultSet.next()) {
|
|
107
|
+
result[params[i].PARAMETER_NAME] = [resultSet.getValues()]
|
|
108
|
+
resultSet.nextResult()
|
|
109
|
+
i++
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function _getProcedureMetadata(procedureName, dbc) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
dbc.exec(
|
|
118
|
+
`SELECT PARAMETER_NAME FROM SYS.PROCEDURE_PARAMETERS WHERE SCHEMA_NAME = CURRENT_SCHEMA AND PROCEDURE_NAME = '${procedureName}' AND PARAMETER_TYPE IN ('OUT', 'INOUT') ORDER BY POSITION`,
|
|
119
|
+
(err, res) => {
|
|
120
|
+
if (err) reject(err)
|
|
121
|
+
else resolve(res)
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
51
127
|
function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
|
|
52
|
-
dbc.prepare(sql, function (err, stmt) {
|
|
128
|
+
dbc.prepare(sql, async function (err, stmt) {
|
|
53
129
|
if (err) {
|
|
54
130
|
err.query = sql
|
|
55
131
|
if (values) err.values = SANITIZE_VALUES ? ['***'] : values
|
|
56
132
|
return reject(err)
|
|
57
133
|
}
|
|
58
134
|
|
|
59
|
-
//
|
|
60
|
-
if (
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (stmt.parameterMetadata[i].dataType === 12 || stmt.parameterMetadata[i].dataType === 13) {
|
|
69
|
-
if (row[i] && !Buffer.isBuffer(row[i])) {
|
|
70
|
-
row[i] = Buffer.from(row[i].match(/.{1,2}/g).map(val => parseInt(val, 16)))
|
|
135
|
+
// convert binary strings to buffers
|
|
136
|
+
if (cds.env.hana.base64_to_buffer !== false && _hasValues(values)) {
|
|
137
|
+
const binaries = _getBinaries(stmt)
|
|
138
|
+
if (binaries.length) {
|
|
139
|
+
const vals = Array.isArray(values[0]) ? values : [values]
|
|
140
|
+
for (const i of binaries) {
|
|
141
|
+
for (const row of vals) {
|
|
142
|
+
if (row[i] && typeof row[i] === 'string' && row[i].match(BASE64)) {
|
|
143
|
+
row[i] = Buffer.from(row[i], 'base64')
|
|
71
144
|
}
|
|
72
145
|
}
|
|
73
146
|
}
|
|
74
147
|
}
|
|
75
148
|
}
|
|
76
149
|
|
|
150
|
+
if (cds.env.features.new_call_prodecure) {
|
|
151
|
+
// procedure call metadata
|
|
152
|
+
let outParameters
|
|
153
|
+
const isProcedureCall = _isProcedureCall(sql)
|
|
154
|
+
if (isProcedureCall) {
|
|
155
|
+
try {
|
|
156
|
+
const procedureName = _getProcedureName(sql)
|
|
157
|
+
outParameters = await _getProcedureMetadata(procedureName, dbc)
|
|
158
|
+
} catch (e) {
|
|
159
|
+
LOG._warn && LOG.warn('Unable to fetch procedure metadata due to error:', e)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// on @sap/hana-client, we need to use execQuery in case of calling procedures
|
|
164
|
+
stmt[isProcedureCall && dbc.name !== 'hdb' ? 'execQuery' : 'exec'](values, function (err, rows, ...args) {
|
|
165
|
+
if (err) {
|
|
166
|
+
stmt.drop(() => {})
|
|
167
|
+
err.query = sql
|
|
168
|
+
if (values) err.values = SANITIZE_VALUES ? ['***'] : values
|
|
169
|
+
return reject(err)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let result
|
|
173
|
+
if (isProcedureCall) {
|
|
174
|
+
result =
|
|
175
|
+
dbc.name === 'hdb'
|
|
176
|
+
? _hdbGetResultForProcedure(rows, args, outParameters)
|
|
177
|
+
: _hcGetResultForProcedure(stmt, rows, outParameters)
|
|
178
|
+
} else {
|
|
179
|
+
result = rows
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
stmt.drop(() => {})
|
|
183
|
+
|
|
184
|
+
resolve(result)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
77
190
|
stmt.exec(values, function (err, rows, procedureReturn) {
|
|
78
191
|
if (err) {
|
|
79
192
|
stmt.drop(() => {})
|
|
@@ -108,15 +221,15 @@ function _executeSimpleSQL(dbc, sql, values) {
|
|
|
108
221
|
values = Object.values(values)
|
|
109
222
|
}
|
|
110
223
|
// ensure that stored procedure with parameters is always executed as prepared
|
|
111
|
-
if (_hasValues(
|
|
224
|
+
if (_hasValues(values) || _isProcedureCall(sql)) {
|
|
112
225
|
_executeAsPreparedStatement(dbc, sql, values, reject, resolve)
|
|
113
226
|
} else {
|
|
114
|
-
dbc.exec(sql, function (err, result
|
|
227
|
+
dbc.exec(sql, function (err, result) {
|
|
115
228
|
if (err) {
|
|
116
229
|
err.query = sql
|
|
117
230
|
return reject(err)
|
|
118
231
|
}
|
|
119
|
-
resolve(
|
|
232
|
+
resolve(result)
|
|
120
233
|
})
|
|
121
234
|
}
|
|
122
235
|
})
|
|
@@ -11,9 +11,9 @@ function searchHandler(req) {
|
|
|
11
11
|
// REVISIT: remove feature toggle optimized_search after grace period
|
|
12
12
|
// inject the search2cqn4sql module into the rewrite handler only when
|
|
13
13
|
// the optimized search feature toggle is turned on
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (cds.env.features.optimized_search) {
|
|
15
|
+
_setSearchOptions(req.query, { search2cqn4sql, locale: req.locale })
|
|
16
|
+
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// handlers marked with `._initial = true` run in sequence
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { computeColumnsToBeSearched } = require('../cds-services/services/utils/columns')
|
|
2
2
|
const searchToLike = require('../common/utils/searchToLike')
|
|
3
3
|
const { isContainsPredicateSupported, searchToContains } = require('./searchToContains')
|
|
4
|
+
const { addAliasToExpression } = require('../db/utils/generateAliases')
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Computes a CQN expression for a search query.
|
|
@@ -33,8 +34,7 @@ const search2cqn4sql = (query, entity, options) => {
|
|
|
33
34
|
// suppress the localize handler from redirecting the query's target to the localized view
|
|
34
35
|
Object.defineProperty(query, '_suppressLocalization', { value: true })
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
if (resolveLocalizedDataAtRuntime && !query.SELECT.from.SELECT) {
|
|
37
|
+
if (resolveLocalizedDataAtRuntime) {
|
|
38
38
|
const onCondition = entity._relations[localizedAssociation.name].join(localizedAssociation.target, entity.name)
|
|
39
39
|
|
|
40
40
|
// replace $user_locale placeholder with the user locale or the HANA session context
|
|
@@ -46,56 +46,54 @@ const search2cqn4sql = (query, entity, options) => {
|
|
|
46
46
|
query.join(localizedEntityName).on(onCondition)
|
|
47
47
|
|
|
48
48
|
// prevent SQL ambiguity error for columns with the same name
|
|
49
|
-
columnsToBeSearched =
|
|
49
|
+
columnsToBeSearched = _addAliasToQuery(query, entity, columnsToBeSearched)
|
|
50
50
|
} // else --> resolve localized texts via localized view (default)
|
|
51
51
|
|
|
52
52
|
const useContains = isContainsPredicateSupported(query)
|
|
53
53
|
let expression
|
|
54
54
|
|
|
55
55
|
if (useContains) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const namedColumns = columnsToBeSearched.filter(col => !col.func)
|
|
57
|
+
expression = searchToContains(cqnSearchPhrase, namedColumns)
|
|
58
|
+
|
|
59
|
+
// func columns handling
|
|
60
|
+
if (columnsToBeSearched.length > namedColumns.length) {
|
|
61
|
+
const funcColumns = columnsToBeSearched.filter(col => col.func)
|
|
62
|
+
expression = [expression, 'or', ...searchToLike(cqnSearchPhrase, funcColumns)]
|
|
63
|
+
}
|
|
60
64
|
} else {
|
|
61
65
|
// No CONTAINS optimization possible. The search implementation for localized
|
|
62
66
|
// texts falls back to the LIKE predicate.
|
|
63
67
|
expression = searchToLike(cqnSearchPhrase, columnsToBeSearched)
|
|
64
68
|
}
|
|
69
|
+
|
|
65
70
|
// REVISIT: find out here if where or having must be used
|
|
66
71
|
query._aggregated || /* if new parser */ query.SELECT.groupBy ? query.having(expression) : query.where(expression)
|
|
67
72
|
return query
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
const _getLocalizedAssociation = entity =>
|
|
71
|
-
const associations = entity.associations
|
|
72
|
-
return associations && associations.localized
|
|
73
|
-
}
|
|
75
|
+
const _getLocalizedAssociation = entity => entity.associations && entity.associations.localized
|
|
74
76
|
|
|
75
77
|
// The inner join modifies the original SELECT ... FROM query and adds ambiguity,
|
|
76
78
|
// therefore add the table/entity name (as a preceding element) to the columns ref
|
|
77
79
|
// to prevent a SQL ambiguity error.
|
|
78
|
-
const
|
|
80
|
+
const _addAliasToQuery = (query, entity, columnsToBeSearched) => {
|
|
81
|
+
const SELECT = query.SELECT
|
|
79
82
|
const localizedEntityName = _getLocalizedAssociation(entity).target
|
|
80
83
|
const elements = entity.elements
|
|
81
84
|
const entityName = entity.name
|
|
82
|
-
const
|
|
83
|
-
const columnRef = column.ref
|
|
84
|
-
if (!columnRef) return column
|
|
85
|
+
const getEntityName = columnRef => {
|
|
85
86
|
const columnName = columnRef[0]
|
|
86
|
-
const localizedElement = elements[columnName].localized
|
|
87
|
+
const localizedElement = !!elements[columnName].localized
|
|
87
88
|
const targetEntityName = localizedElement ? localizedEntityName : entityName
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
query.SELECT.columns = query.SELECT.columns.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
92
|
-
const columns = columnsToBeSearched.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
93
|
-
|
|
94
|
-
if (query.SELECT.groupBy) {
|
|
95
|
-
query.SELECT.groupBy = query.SELECT.groupBy.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
89
|
+
return targetEntityName
|
|
96
90
|
}
|
|
97
91
|
|
|
98
|
-
|
|
92
|
+
SELECT.columns = addAliasToExpression(SELECT.columns, getEntityName)
|
|
93
|
+
columnsToBeSearched = addAliasToExpression(columnsToBeSearched, getEntityName)
|
|
94
|
+
SELECT.groupBy = addAliasToExpression(SELECT.groupBy, getEntityName)
|
|
95
|
+
SELECT.where = addAliasToExpression(SELECT.where, getEntityName)
|
|
96
|
+
return columnsToBeSearched
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
module.exports = search2cqn4sql
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* @returns {import("../types/api").searchContainsExp} The `CONTAINS` CQN expression
|
|
26
26
|
*/
|
|
27
27
|
const searchToContains = (cqnSearchPhrase, columns) => {
|
|
28
|
-
// serialize CQN search phrase
|
|
28
|
+
// serialize the CQN search phrase
|
|
29
29
|
const searchString = cqnSearchPhrase.reduce((searchStringAccumulator, currentValue) => {
|
|
30
30
|
// Multiple search terms separated by an space are automatically
|
|
31
31
|
// interpreted as an AND operator. Therefore, it is not mandatory
|
|
@@ -61,7 +61,7 @@ class AMQPWebhookMessaging extends MessagingService {
|
|
|
61
61
|
// In case of AMQP and Solace, the `failed` callback must be called
|
|
62
62
|
// with an error, otherwise there are problems with the redelivery count.
|
|
63
63
|
failed(new Error('processing failed'))
|
|
64
|
-
LOG.
|
|
64
|
+
LOG.error('ERROR occured in asynchronous event processing:', e)
|
|
65
65
|
}
|
|
66
66
|
})
|
|
67
67
|
}
|