@sap/cds 5.8.2 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +214 -78
- package/app/fiori/preview.js +16 -11
- package/app/fiori/routes.js +3 -0
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +20 -17
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +12 -5
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +26 -3
- package/lib/compile/etc/_localized.js +36 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/for/odata.js +1 -1
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +120 -49
- package/lib/index.js +1 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +7 -5
- package/lib/serve/index.js +24 -12
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +13 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/UriHelper.js +4 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +50 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +296 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +14 -10
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/index.js +2 -31
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +9 -6
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -417
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +2 -2
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +29 -75
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +25 -13
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/create.js +6 -1
- package/libx/gql/resolvers/crud/delete.js +6 -1
- package/libx/gql/resolvers/crud/read.js +6 -1
- package/libx/gql/resolvers/crud/update.js +9 -1
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +1 -1
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
|
@@ -5,7 +5,7 @@ const onDraftActivate = require('./activate')._handler
|
|
|
5
5
|
const { isNavigationToMany } = require('../utils/req')
|
|
6
6
|
const { getKeysCondition } = require('../utils/where')
|
|
7
7
|
const { removeDraftUUIDIfNecessary, ensureDraftsSuffix } = require('../utils/handler')
|
|
8
|
-
const {
|
|
8
|
+
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
9
9
|
|
|
10
10
|
const _getUpdateDraftAdminCQN = ({ user, timestamp }, draftUUID) => {
|
|
11
11
|
return UPDATE('DRAFT.DraftAdministrativeData')
|
|
@@ -60,7 +60,7 @@ const _handler = async function (req, next) {
|
|
|
60
60
|
const elements = req.target.elements
|
|
61
61
|
for (const column in elements) {
|
|
62
62
|
const col = elements[column]
|
|
63
|
-
if (col.default !== undefined && !
|
|
63
|
+
if (col.default !== undefined && !(column in DRAFT_COLUMNS_MAP)) {
|
|
64
64
|
if ('val' in col.default) req.data[col.name] = col.default.val
|
|
65
65
|
else if ('ref' in col.default) req.data[col.name] = col.default.ref[0]
|
|
66
66
|
else req.data[col.name] = col.default
|
|
@@ -91,7 +91,7 @@ const _handler = async function (req, next) {
|
|
|
91
91
|
req.reject(404)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
removeDraftUUIDIfNecessary(result[0]
|
|
94
|
+
removeDraftUUIDIfNecessary(req)(result[0])
|
|
95
95
|
|
|
96
96
|
return result[0]
|
|
97
97
|
}
|
|
@@ -82,7 +82,7 @@ const _handler = async function (req) {
|
|
|
82
82
|
await Promise.all([dbtx.run(updateDraftCQN), dbtx.run(updateDraftAdminCQN)])
|
|
83
83
|
result = await dbtx.run(_getSelectCQN(this.model, req, keysCondition, false))
|
|
84
84
|
if (result.length === 0) req.reject(404)
|
|
85
|
-
removeDraftUUIDIfNecessary(result[0]
|
|
85
|
+
removeDraftUUIDIfNecessary(req)(result[0])
|
|
86
86
|
return result[0]
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -3,7 +3,7 @@ const { SELECT } = cds.ql
|
|
|
3
3
|
|
|
4
4
|
const { cqn2cqn4sql, convertWhereExists } = require('../../common/utils/cqn2cqn4sql')
|
|
5
5
|
const { getElementDeep } = require('../../common/utils/csn')
|
|
6
|
-
const {
|
|
6
|
+
const { DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
|
|
7
7
|
const {
|
|
8
8
|
addColumnAlias,
|
|
9
9
|
draftIsLocked,
|
|
@@ -786,7 +786,7 @@ const _getOrderByEnrichedColumns = (orderBy, columns, entity) => {
|
|
|
786
786
|
// For associations we need to 'materialise' the resulting field, otherwise we cannot access it in an outer SELECT.
|
|
787
787
|
if (entity && entity.elements[el.ref[0]] && entity.elements[el.ref[0]].isAssociation) {
|
|
788
788
|
enrichedCol.push({ ref: [...el.ref], as: _poorMansAlias4(el) })
|
|
789
|
-
} else if (!
|
|
789
|
+
} else if (!(el.ref[el.ref.length - 1] in DRAFT_COLUMNS_MAP) && !colNames.includes(el.ref[el.ref.length - 1])) {
|
|
790
790
|
enrichedCol.push({ ref: [...el.ref] })
|
|
791
791
|
}
|
|
792
792
|
}
|
|
@@ -844,7 +844,9 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model, ent
|
|
|
844
844
|
for (const col of enrichedColumns) {
|
|
845
845
|
// if we have columns for outer order by that may also be needed for joins, we need to duplicate them
|
|
846
846
|
const element = getElementDeep(req.target, col.ref)
|
|
847
|
-
if (element && element
|
|
847
|
+
if (element && element._foreignKey4) {
|
|
848
|
+
columns.push({ ref: [...col.ref] })
|
|
849
|
+
}
|
|
848
850
|
|
|
849
851
|
col.as = _poorMansAlias4(col)
|
|
850
852
|
// add alias to outer order by
|
|
@@ -1059,8 +1061,7 @@ const _getColumns = ({ query: { SELECT }, target }, model) => {
|
|
|
1059
1061
|
return SELECT.columns
|
|
1060
1062
|
? SELECT.columns.filter(
|
|
1061
1063
|
col =>
|
|
1062
|
-
(col.ref && !
|
|
1063
|
-
(!col.ref && !DRAFT_COLUMNS.includes(col))
|
|
1064
|
+
(col.ref && !(col.ref[col.ref.length - 1] in DRAFT_COLUMNS_MAP)) || (!col.ref && !(col in DRAFT_COLUMNS_MAP))
|
|
1064
1065
|
)
|
|
1065
1066
|
: getColumns(target, { onlyNames: true, removeIgnore: true })
|
|
1066
1067
|
}
|
|
@@ -1100,20 +1101,14 @@ const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
|
|
|
1100
1101
|
}
|
|
1101
1102
|
}
|
|
1102
1103
|
|
|
1103
|
-
const _calculateDraftAdminColumns = (result, user) => {
|
|
1104
|
-
if (
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
)
|
|
1104
|
+
const _calculateDraftAdminColumns = (result, user, deleteLastChangeDateTime) => {
|
|
1105
|
+
if (!result) return
|
|
1106
|
+
if ('InProcessByUser' in result && !draftIsLocked(result.LastChangeDateTime)) result.InProcessByUser = ''
|
|
1107
|
+
if (deleteLastChangeDateTime) delete result.LastChangeDateTime
|
|
1108
|
+
if ('DraftIsCreatedByMe' in result && 'CreatedByUser' in result)
|
|
1108
1109
|
result.DraftIsCreatedByMe = result.CreatedByUser === user
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
if (
|
|
1112
|
-
Object.prototype.hasOwnProperty.call(result, 'DraftIsProcessedByMe') &&
|
|
1113
|
-
Object.prototype.hasOwnProperty.call(result, 'InProcessByUser')
|
|
1114
|
-
) {
|
|
1110
|
+
if ('DraftIsProcessedByMe' in result && 'InProcessByUser' in result)
|
|
1115
1111
|
result.DraftIsProcessedByMe = result.InProcessByUser === user
|
|
1116
|
-
}
|
|
1117
1112
|
}
|
|
1118
1113
|
|
|
1119
1114
|
const _adaptDraftColumnsForSiblingEntity = (result, isSiblingActive) => {
|
|
@@ -1145,30 +1140,6 @@ const _adaptAnnotationAliases = cqn => {
|
|
|
1145
1140
|
_collectAliases(cqn.SELECT.from, aliases)
|
|
1146
1141
|
}
|
|
1147
1142
|
|
|
1148
|
-
const calculateDraftTimeout = (scenario, result, deleteLastChangeDateTime) => {
|
|
1149
|
-
if (scenario === SCENARIO.DRAFT_ADMIN) {
|
|
1150
|
-
if (!draftIsLocked(result[0].LastChangeDateTime)) {
|
|
1151
|
-
result[0].InProcessByUser = ''
|
|
1152
|
-
}
|
|
1153
|
-
if (deleteLastChangeDateTime) delete result[0].LastChangeDateTime
|
|
1154
|
-
|
|
1155
|
-
return
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
// non empty result that and DraftAdministrativeData was expanded
|
|
1159
|
-
if (result.length && Object.prototype.hasOwnProperty.call(result[0], 'DraftAdministrativeData')) {
|
|
1160
|
-
result.forEach(row => {
|
|
1161
|
-
if (!row.DraftAdministrativeData) return
|
|
1162
|
-
if (Object.prototype.hasOwnProperty.call(row.DraftAdministrativeData, 'InProcessByUser')) {
|
|
1163
|
-
if (!draftIsLocked(row.DraftAdministrativeData.LastChangeDateTime)) {
|
|
1164
|
-
row.DraftAdministrativeData.InProcessByUser = ''
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
if (deleteLastChangeDateTime) delete row.DraftAdministrativeData.LastChangeDateTime
|
|
1168
|
-
})
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
1143
|
const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
|
|
1173
1144
|
if (scenario !== SCENARIO.DRAFT_ADMIN) {
|
|
1174
1145
|
const draftAdmin = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'DraftAdministrativeData')
|
|
@@ -1212,6 +1183,51 @@ const _adaptDraftAdminExpand = cqn => {
|
|
|
1212
1183
|
}
|
|
1213
1184
|
}
|
|
1214
1185
|
|
|
1186
|
+
const _getOriginalColumns = req => {
|
|
1187
|
+
const originalColumns = {}
|
|
1188
|
+
// expanded columns are handled generically in db
|
|
1189
|
+
for (const c of req.query.SELECT.columns) {
|
|
1190
|
+
originalColumns[c.ref ? c.ref[c.ref.length - 1] : c.as || c] = true
|
|
1191
|
+
}
|
|
1192
|
+
return originalColumns
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
const _postProcess = (result, req, cqnScenario, deleteLastChangeDateTime) => {
|
|
1196
|
+
const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
|
|
1197
|
+
|
|
1198
|
+
if (!result || !resultAsArray.length) return result
|
|
1199
|
+
|
|
1200
|
+
if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
|
|
1201
|
+
if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) return []
|
|
1202
|
+
delete resultAsArray[0].draftAdmin_inProcessByUser
|
|
1203
|
+
_adaptDraftColumnsForSiblingEntity(resultAsArray[0], cqnScenario.isSiblingActive)
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const removeDraftUUIDIfNecessaryFn = removeDraftUUIDIfNecessary(req)
|
|
1207
|
+
let notRequestedColumns
|
|
1208
|
+
// REVISIT: remove flag cds.env.features.auto_fetch_expand_keys after two month grace period
|
|
1209
|
+
if (!req.query.SELECT._4odata && !cds.env.features.auto_fetch_expand_keys) {
|
|
1210
|
+
const originalColumns = _getOriginalColumns(req)
|
|
1211
|
+
notRequestedColumns = originalColumns && Object.keys(resultAsArray[0]).filter(key => !originalColumns[key])
|
|
1212
|
+
}
|
|
1213
|
+
if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
|
|
1214
|
+
_calculateDraftAdminColumns(resultAsArray[0], req.user.id, deleteLastChangeDateTime)
|
|
1215
|
+
if (notRequestedColumns) {
|
|
1216
|
+
for (const key of notRequestedColumns) delete resultAsArray[0][key]
|
|
1217
|
+
}
|
|
1218
|
+
} else {
|
|
1219
|
+
for (const row of resultAsArray) {
|
|
1220
|
+
removeDraftUUIDIfNecessaryFn(row)
|
|
1221
|
+
_calculateDraftAdminColumns(row.DraftAdministrativeData, req.user.id, deleteLastChangeDateTime)
|
|
1222
|
+
if (notRequestedColumns) {
|
|
1223
|
+
for (const key of notRequestedColumns) delete row[key]
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
return result
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1215
1231
|
/**
|
|
1216
1232
|
* Generic Handler for READ requests in the context of draft.
|
|
1217
1233
|
*
|
|
@@ -1274,32 +1290,8 @@ const _handler = async function (req) {
|
|
|
1274
1290
|
req.target = this.model.definitions[ensureUnlocalized(req.target.name)]
|
|
1275
1291
|
|
|
1276
1292
|
const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
|
|
1277
|
-
const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
|
|
1278
|
-
removeDraftUUIDIfNecessary(resultAsArray, req)
|
|
1279
|
-
|
|
1280
|
-
if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
|
|
1281
|
-
if (!result || (Array.isArray(result) && !result.length)) return result
|
|
1282
1293
|
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
calculateDraftTimeout(cqnScenario.scenario, resultAsArray, enhancedWithLastChangeDateTime)
|
|
1287
|
-
|
|
1288
|
-
if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
|
|
1289
|
-
if (!result || (Array.isArray(result) && !result.length)) return result
|
|
1290
|
-
if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) return []
|
|
1291
|
-
|
|
1292
|
-
delete resultAsArray[0].draftAdmin_inProcessByUser
|
|
1293
|
-
_adaptDraftColumnsForSiblingEntity(resultAsArray[0], cqnScenario.isSiblingActive)
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
if (resultAsArray.length && Object.prototype.hasOwnProperty.call(resultAsArray[0], 'DraftAdministrativeData')) {
|
|
1297
|
-
resultAsArray.forEach(row => {
|
|
1298
|
-
row.DraftAdministrativeData && _calculateDraftAdminColumns(row.DraftAdministrativeData, req.user.id)
|
|
1299
|
-
})
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
return result
|
|
1294
|
+
return _postProcess(result, req, cqnScenario, enhancedWithLastChangeDateTime)
|
|
1303
1295
|
}
|
|
1304
1296
|
|
|
1305
1297
|
module.exports = cds.service.impl(function () {
|
|
@@ -3,6 +3,7 @@ const { SELECT } = cds.ql
|
|
|
3
3
|
const { getEnrichedCQN, hasDraft, ensureDraftsSuffix } = require('../utils/handler')
|
|
4
4
|
const { readAndDeleteKeywords } = require('../utils/where')
|
|
5
5
|
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
6
|
+
const { isActiveEntityRequested } = require('../../../_runtime/fiori/utils/where')
|
|
6
7
|
|
|
7
8
|
const _modifyCQN = (cqnDraft, where, context) => {
|
|
8
9
|
const whereDraft = [...where]
|
|
@@ -10,9 +11,8 @@ const _modifyCQN = (cqnDraft, where, context) => {
|
|
|
10
11
|
cqnDraft.where(whereDraft)
|
|
11
12
|
|
|
12
13
|
if (result && result.value.val === false) {
|
|
13
|
-
cqnDraft.SELECT.from.ref
|
|
14
|
-
|
|
15
|
-
)
|
|
14
|
+
const fromRef = cqnDraft.SELECT.from.ref
|
|
15
|
+
cqnDraft.SELECT.from.ref[fromRef.length - 1] = ensureDraftsSuffix(fromRef[fromRef.length - 1])
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
for (let i = 0; i < cqnDraft.SELECT.where.length; i++) {
|
|
@@ -33,32 +33,89 @@ const _modifyCQN = (cqnDraft, where, context) => {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const _hasNavToNonDraftEnclosedAssoc = (pathSegments, definitions, excludeAssoc) => {
|
|
37
|
+
if (pathSegments.length < 2) return false
|
|
38
|
+
const entity = definitions[pathSegments[0]]
|
|
39
|
+
const nav = entity.elements[pathSegments[1]]
|
|
40
|
+
|
|
41
|
+
if (nav._isAssociationStrict) {
|
|
42
|
+
if (nav['@odata.draft.enclosed']) return false
|
|
43
|
+
if (excludeAssoc == null) return true
|
|
44
|
+
if (!excludeAssoc(nav)) return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// At this point we know that nav is a composition, so we have to recursively
|
|
48
|
+
// follow the navigation until we reach the end or find an association.
|
|
49
|
+
pathSegments.shift()
|
|
50
|
+
pathSegments[0] = nav.target
|
|
51
|
+
return _hasNavToNonDraftEnclosedAssoc(pathSegments, definitions, excludeAssoc)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const _shouldReadOverDraft = (req, definitions) => {
|
|
55
|
+
const SELECT = req.query.SELECT
|
|
56
|
+
const fromRef = SELECT.from.ref
|
|
57
|
+
|
|
58
|
+
if (!fromRef) return false
|
|
59
|
+
|
|
60
|
+
// read over the draft if the navigation target is non-draft-enabled
|
|
61
|
+
if (!req.target._isDraftEnabled) return true
|
|
62
|
+
|
|
63
|
+
// read over the draft only for navigation scenarios
|
|
64
|
+
if (fromRef.length === 1) return false
|
|
65
|
+
|
|
66
|
+
const firstFromRef = fromRef[0]
|
|
67
|
+
const rootEntityName = typeof firstFromRef === 'string' ? firstFromRef : firstFromRef.id
|
|
68
|
+
const rootEntity = definitions[rootEntityName]
|
|
69
|
+
|
|
70
|
+
// read over the draft only if the root entity is draft-enabled and
|
|
71
|
+
// the navigation starts with a draft entity (IsActiveEntity=false)
|
|
72
|
+
if (!!rootEntity._isDraftEnabled && isActiveEntityRequested(firstFromRef.where)) return false
|
|
73
|
+
|
|
74
|
+
// read over the draft if the navigation target is an association and
|
|
75
|
+
// only if it isn't annotated with @odata.draft.enclosed
|
|
76
|
+
const pathSegments = fromRef.map(path => (typeof path === 'string' ? path : path.id))
|
|
77
|
+
|
|
78
|
+
const excludeAssoc = assoc => {
|
|
79
|
+
if (assoc.name === 'DraftAdministrativeData' || assoc.name === 'SiblingEntity') return true
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return _hasNavToNonDraftEnclosedAssoc(pathSegments, definitions, excludeAssoc)
|
|
84
|
+
}
|
|
85
|
+
|
|
36
86
|
/**
|
|
37
87
|
* Generic Handler for READ requests.
|
|
38
88
|
*
|
|
39
|
-
* @param req
|
|
89
|
+
* @param {import('../../cds-services/adapter/odata-v4/ODataRequest')} req
|
|
90
|
+
* @param next
|
|
40
91
|
* @returns {Promise<Array>}
|
|
41
92
|
*/
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
93
|
+
const _readOverDraftHandler = async function (req, next) {
|
|
94
|
+
const definitions = this.model.definitions
|
|
95
|
+
|
|
96
|
+
// determine whether the request is handled here (read over draft handler),
|
|
97
|
+
// or whether it is passed to the next registered handler/route
|
|
98
|
+
if (!_shouldReadOverDraft(req, definitions)) return next()
|
|
99
|
+
|
|
100
|
+
const rows = req.query.SELECT.limit && req.query.SELECT.limit.rows
|
|
101
|
+
if (rows && rows.val === 0) return Promise.resolve([])
|
|
46
102
|
|
|
47
103
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
48
|
-
const sqlQuery = cqn2cqn4sql(req.query, this.model, { _4fiori: true })
|
|
104
|
+
const sqlQuery = cqn2cqn4sql(req.query, this.model, { _4db: req.target._isDraftEnabled, _4fiori: true })
|
|
105
|
+
|
|
49
106
|
if (req.query._streaming) {
|
|
50
107
|
sqlQuery._streaming = true
|
|
51
108
|
}
|
|
52
109
|
|
|
53
|
-
const hasDraftEntity = hasDraft(
|
|
110
|
+
const hasDraftEntity = hasDraft(definitions, sqlQuery)
|
|
54
111
|
|
|
55
|
-
if (hasDraftEntity && sqlQuery.SELECT.where && sqlQuery.SELECT.where.length
|
|
112
|
+
if (hasDraftEntity && sqlQuery.SELECT.where && sqlQuery.SELECT.where.length > 0) {
|
|
56
113
|
let cqnDraft = SELECT.from({
|
|
57
114
|
ref: [...sqlQuery.SELECT.from.ref],
|
|
58
115
|
as: sqlQuery.SELECT.from.as
|
|
59
116
|
})
|
|
60
|
-
cqnDraft.SELECT.columns = sqlQuery.SELECT.columns
|
|
61
117
|
|
|
118
|
+
cqnDraft.SELECT.columns = sqlQuery.SELECT.columns
|
|
62
119
|
_modifyCQN(cqnDraft, sqlQuery.SELECT.where, req)
|
|
63
120
|
cqnDraft = getEnrichedCQN(cqnDraft, sqlQuery.SELECT, [])
|
|
64
121
|
return cds.tx(req).run(cqnDraft)
|
|
@@ -67,8 +124,6 @@ const _handler = async function (req) {
|
|
|
67
124
|
return cds.tx(req).run(sqlQuery)
|
|
68
125
|
}
|
|
69
126
|
|
|
70
|
-
module.exports = cds.service.impl(function () {
|
|
71
|
-
|
|
72
|
-
this.on('READ', entity, _handler)
|
|
73
|
-
}
|
|
127
|
+
module.exports = cds.service.impl(function readOverDraft() {
|
|
128
|
+
this.on('READ', '*', _readOverDraftHandler)
|
|
74
129
|
})
|
|
@@ -4,7 +4,7 @@ const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
|
4
4
|
const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require('../../common/utils/draft')
|
|
5
5
|
const getTemplate = require('../../common/utils/template')
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
8
8
|
|
|
9
9
|
// unofficial config!
|
|
10
10
|
const MAX_RECURSION_DEPTH = (cds.env.features.recursion_depth && Number(cds.env.features.recursion_depth)) || 2
|
|
@@ -157,7 +157,7 @@ const getEnrichedCQN = (cqn, select, draftWhere, scenarioAlias, addLimitOrder =
|
|
|
157
157
|
const _aliasRef = (ref, alias) => {
|
|
158
158
|
const newRef = [...ref]
|
|
159
159
|
// we skip draft columns because they are mostly calculated later on
|
|
160
|
-
if (alias && !
|
|
160
|
+
if (alias && !(ref[ref.length - 1] in DRAFT_COLUMNS_MAP)) {
|
|
161
161
|
newRef.unshift(alias)
|
|
162
162
|
}
|
|
163
163
|
return newRef
|
|
@@ -184,17 +184,10 @@ const setStatusCodeAndHeader = (response, keys, entityName, isActiveEntity) => {
|
|
|
184
184
|
response.setHeader('location', `../${entityName}(${keysString},IsActiveEntity=${isActiveEntity})`)
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
const removeDraftUUIDIfNecessary =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
for (const row of result) {
|
|
192
|
-
delete row.DraftAdministrativeData_DraftUUID
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
delete result.DraftAdministrativeData_DraftUUID
|
|
196
|
-
}
|
|
197
|
-
}
|
|
187
|
+
const removeDraftUUIDIfNecessary = req =>
|
|
188
|
+
req._.req && req._.req.headers && req._.req.headers['x-cds-odata-version'] === 'v2'
|
|
189
|
+
? () => {}
|
|
190
|
+
: result => delete result.DraftAdministrativeData_DraftUUID
|
|
198
191
|
|
|
199
192
|
const isDraftActivateAction = req => {
|
|
200
193
|
// REVISIT: get rid of getUrlObject
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const AND_OR = { and: 1, or: 1 }
|
|
2
|
+
|
|
1
3
|
const _removeMultipleBrackets = (index, whereCondition, isXpr) => {
|
|
2
4
|
if (isXpr) return index
|
|
3
5
|
|
|
@@ -20,19 +22,18 @@ const _removeMultipleBrackets = (index, whereCondition, isXpr) => {
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const _calculateSpliceArgs = (index, whereCondition, isXpr = false) => {
|
|
23
|
-
const AND_OR = ['and', 'or']
|
|
24
25
|
const len = isXpr ? 1 : 3
|
|
25
|
-
if (
|
|
26
|
+
if (whereCondition[index - 1] in AND_OR) {
|
|
26
27
|
return { index: index - 1, count: 1 + len }
|
|
27
28
|
}
|
|
28
|
-
if (
|
|
29
|
+
if (whereCondition[index + len] in AND_OR) {
|
|
29
30
|
return { index: index, count: len + 1 }
|
|
30
31
|
}
|
|
31
32
|
if (whereCondition[index - 1] === '(' && whereCondition[index + len] === ')') {
|
|
32
|
-
if (
|
|
33
|
+
if (whereCondition[index - 2] in AND_OR) {
|
|
33
34
|
return { index: index - 2, count: len + 3 }
|
|
34
35
|
}
|
|
35
|
-
if (
|
|
36
|
+
if (whereCondition[index + len + 1] in AND_OR) {
|
|
36
37
|
return { index: index - 1, count: len + 3 }
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -107,17 +107,12 @@ class HanaDatabase extends DatabaseService {
|
|
|
107
107
|
|
|
108
108
|
_registerAfterHandlers() {
|
|
109
109
|
// REVISIT: after phase runs in parallel -> side effects possible!
|
|
110
|
-
const { effective } = cds.env
|
|
110
|
+
const { effective, features } = cds.env
|
|
111
111
|
|
|
112
|
-
if (effective.odata.structs) {
|
|
112
|
+
if (effective.odata.structs && !features.ucsn_struct_conversion) {
|
|
113
113
|
// REVISIT: only register for entities that contain structured or navigation to it
|
|
114
114
|
this.after(['READ'], '*', this._structured)
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
if (effective.odata.version !== 'v2') {
|
|
118
|
-
// REVISIT: only register for entities that contain arrayed or navigation to it
|
|
119
|
-
this.after(['READ'], '*', this._arrayed)
|
|
120
|
-
}
|
|
121
116
|
}
|
|
122
117
|
|
|
123
118
|
/*
|
|
@@ -125,14 +120,13 @@ class HanaDatabase extends DatabaseService {
|
|
|
125
120
|
*/
|
|
126
121
|
// eslint-disable-next-line complexity
|
|
127
122
|
async acquire(arg) {
|
|
128
|
-
// REVISIT: remove fallback arg.user.tenant with cds^6
|
|
123
|
+
// REVISIT: remove fallback arg.user.tenant with cds^6 (still needed for cds-mtx)
|
|
129
124
|
const tenant = (typeof arg === 'string' ? arg : arg.tenant || (arg.user && arg.user.tenant)) || 'anonymous'
|
|
130
125
|
const dbc = await pool.acquire(tenant, this)
|
|
131
126
|
|
|
132
127
|
if (typeof arg !== 'string') {
|
|
133
128
|
_setSessionContext(dbc, 'APPLICATIONUSER', arg.user.id || 'ANONYMOUS')
|
|
134
|
-
|
|
135
|
-
_setSessionContext(dbc, 'LOCALE', arg.locale || (arg.user && arg.user.locale) || 'en')
|
|
129
|
+
_setSessionContext(dbc, 'LOCALE', arg.locale || 'en')
|
|
136
130
|
// REVISIT: stable access
|
|
137
131
|
const validFrom = (arg.context && arg.context._ && arg.context._['VALID-FROM']) || (arg._ && arg._['VALID-FROM'])
|
|
138
132
|
const validto = (arg.context && arg.context._ && arg.context._['VALID-TO']) || (arg._ && arg._['VALID-TO'])
|
|
@@ -48,7 +48,7 @@ class CustomSelectBuilder extends SelectBuilder {
|
|
|
48
48
|
select.from.ref &&
|
|
49
49
|
select.from.ref.length === 1 &&
|
|
50
50
|
// REVISIT this does not work with join and draft!
|
|
51
|
-
this._csn.definitions[select.from.ref[0]]
|
|
51
|
+
this._csn.definitions[select.from.ref[0].id || select.from.ref[0]]
|
|
52
52
|
// TODO FIXME
|
|
53
53
|
skip =
|
|
54
54
|
!select.orderBy ||
|
|
@@ -56,7 +56,7 @@ class CustomSelectBuilder extends SelectBuilder {
|
|
|
56
56
|
select.orderBy.every(o => {
|
|
57
57
|
const k = o.ref && o.ref.length === 1 && o.ref[0]
|
|
58
58
|
const element = (k && entity.elements[k]) || {}
|
|
59
|
-
return element.
|
|
59
|
+
return element._type !== 'cds.String'
|
|
60
60
|
}))
|
|
61
61
|
} catch (e) {
|
|
62
62
|
if (LOG._warn) {
|
|
@@ -50,7 +50,7 @@ function _connectHdb(creds, tenant) {
|
|
|
50
50
|
if (err) {
|
|
51
51
|
err = _ensureError(err)
|
|
52
52
|
err.message = `Could not establish connection for tenant "${tenant}" due to error: ` + err.message
|
|
53
|
-
LOG.
|
|
53
|
+
LOG.error(err)
|
|
54
54
|
|
|
55
55
|
// error on .connect shall lead to pool drain
|
|
56
56
|
err._connectError = true
|
|
@@ -109,7 +109,7 @@ function _connectHanaClient(creds, tenant) {
|
|
|
109
109
|
if (err) {
|
|
110
110
|
err = _ensureError(err)
|
|
111
111
|
err.message = `Could not establish connection for tenant "${tenant}" due to error: ` + err.message
|
|
112
|
-
LOG.
|
|
112
|
+
LOG.error(err)
|
|
113
113
|
|
|
114
114
|
// error on .connect shall lead to pool drain
|
|
115
115
|
err._connectError = true
|
|
@@ -4,12 +4,7 @@ const LOG = cds.log('hana|db|sql')
|
|
|
4
4
|
const { HANA_TYPE_CONVERSION_MAP } = require('./conversion')
|
|
5
5
|
const CustomBuilder = require('./customBuilder')
|
|
6
6
|
const { sqlFactory } = require('../db/sql-builder/')
|
|
7
|
-
const {
|
|
8
|
-
getPostProcessMapper,
|
|
9
|
-
getPropertyMapper,
|
|
10
|
-
getStructMapper,
|
|
11
|
-
postProcess
|
|
12
|
-
} = require('../db/data-conversion/post-processing')
|
|
7
|
+
const { getPostProcessMapper, postProcess } = require('../db/data-conversion/post-processing')
|
|
13
8
|
const { createJoinCQNFromExpanded, hasExpand, rawToExpanded, expandV2 } = require('../db/expand')
|
|
14
9
|
const {
|
|
15
10
|
hasStreamInsert,
|
|
@@ -35,23 +30,11 @@ function _cqnToSQL(model, query, user, locale, txTimestamp) {
|
|
|
35
30
|
|
|
36
31
|
const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
37
32
|
|
|
38
|
-
function _getOutputParameters(stmt) {
|
|
39
|
-
const result = {}
|
|
40
|
-
const info = stmt.getParameterInfo()
|
|
41
|
-
for (let i = 0; i < info.length; i++) {
|
|
42
|
-
const param = info[i]
|
|
43
|
-
if (param.direction === 2) {
|
|
44
|
-
result[param.name] = stmt.getParameterValue(i)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return Object.keys(result).length > 0 ? result : undefined
|
|
49
|
-
}
|
|
50
|
-
|
|
51
33
|
const BINARY_TYPES = {
|
|
52
34
|
12: 'BINARY',
|
|
53
35
|
13: 'VARBINARY',
|
|
54
|
-
27: 'BLOB'
|
|
36
|
+
27: 'BLOB',
|
|
37
|
+
33: 'BSTRING'
|
|
55
38
|
}
|
|
56
39
|
|
|
57
40
|
function _getBinaries(stmt) {
|
|
@@ -147,47 +130,20 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
|
|
|
147
130
|
}
|
|
148
131
|
}
|
|
149
132
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
LOG._warn && LOG.warn('Unable to fetch procedure metadata due to error:', e)
|
|
160
|
-
}
|
|
133
|
+
// procedure call metadata
|
|
134
|
+
let outParameters
|
|
135
|
+
const isProcedureCall = _isProcedureCall(sql)
|
|
136
|
+
if (isProcedureCall) {
|
|
137
|
+
try {
|
|
138
|
+
const procedureName = _getProcedureName(sql)
|
|
139
|
+
outParameters = await _getProcedureMetadata(procedureName, dbc)
|
|
140
|
+
} catch (e) {
|
|
141
|
+
LOG._warn && LOG.warn('Unable to fetch procedure metadata due to error:', e)
|
|
161
142
|
}
|
|
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
143
|
}
|
|
189
144
|
|
|
190
|
-
|
|
145
|
+
// on @sap/hana-client, we need to use execQuery in case of calling procedures
|
|
146
|
+
stmt[isProcedureCall && dbc.name !== 'hdb' ? 'execQuery' : 'exec'](values, function (err, rows, ...args) {
|
|
191
147
|
if (err) {
|
|
192
148
|
stmt.drop(() => {})
|
|
193
149
|
err.query = sql
|
|
@@ -195,13 +151,19 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
|
|
|
195
151
|
return reject(err)
|
|
196
152
|
}
|
|
197
153
|
|
|
198
|
-
let result
|
|
199
|
-
if (
|
|
200
|
-
result =
|
|
154
|
+
let result
|
|
155
|
+
if (isProcedureCall) {
|
|
156
|
+
result =
|
|
157
|
+
dbc.name === 'hdb'
|
|
158
|
+
? _hdbGetResultForProcedure(rows, args, outParameters)
|
|
159
|
+
: _hcGetResultForProcedure(stmt, rows, outParameters)
|
|
160
|
+
} else {
|
|
161
|
+
result = rows
|
|
201
162
|
}
|
|
202
163
|
|
|
203
164
|
stmt.drop(() => {})
|
|
204
|
-
|
|
165
|
+
|
|
166
|
+
resolve(result)
|
|
205
167
|
})
|
|
206
168
|
})
|
|
207
169
|
}
|
|
@@ -235,13 +197,13 @@ function _executeSimpleSQL(dbc, sql, values) {
|
|
|
235
197
|
})
|
|
236
198
|
}
|
|
237
199
|
|
|
238
|
-
function _executeSelectSQL(dbc, sql, values, isOne, postMapper
|
|
200
|
+
function _executeSelectSQL(dbc, sql, values, isOne, postMapper) {
|
|
239
201
|
return _executeSimpleSQL(dbc, sql, values).then(result => {
|
|
240
202
|
if (isOne) {
|
|
241
203
|
result = result.length > 0 ? result[0] : null
|
|
242
204
|
}
|
|
243
205
|
|
|
244
|
-
return postProcess(result, postMapper
|
|
206
|
+
return postProcess(result, postMapper)
|
|
245
207
|
})
|
|
246
208
|
}
|
|
247
209
|
|
|
@@ -272,17 +234,9 @@ function executeSelectCQN(model, dbc, query, user, locale, txTimestamp) {
|
|
|
272
234
|
}
|
|
273
235
|
|
|
274
236
|
const { sql, values = [] } = _cqnToSQL(model, query, user, locale, txTimestamp)
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
return _executeSelectSQL(
|
|
278
|
-
dbc,
|
|
279
|
-
sql,
|
|
280
|
-
values,
|
|
281
|
-
query.SELECT.one,
|
|
282
|
-
getPostProcessMapper(HANA_TYPE_CONVERSION_MAP, model, query),
|
|
283
|
-
propertyMapper,
|
|
284
|
-
getStructMapper(model, query, propertyMapper)
|
|
285
|
-
)
|
|
237
|
+
const postProcessMapper = getPostProcessMapper(HANA_TYPE_CONVERSION_MAP, model, query)
|
|
238
|
+
|
|
239
|
+
return _executeSelectSQL(dbc, sql, values, query.SELECT.one, postProcessMapper)
|
|
286
240
|
}
|
|
287
241
|
|
|
288
242
|
function _getValuesProxy(values) {
|
|
@@ -97,7 +97,7 @@ function factory4(creds, tenant) {
|
|
|
97
97
|
/*
|
|
98
98
|
* default generic-pool config
|
|
99
99
|
*/
|
|
100
|
-
const defaultConfig = { min: 0, max: 100, testOnBorrow: true }
|
|
100
|
+
const defaultConfig = { min: 0, max: 100, testOnBorrow: true, fifo: false }
|
|
101
101
|
|
|
102
102
|
const _getPoolConfig = function () {
|
|
103
103
|
const { pool: poolConfig } = cds.env.requires.db
|
|
@@ -18,7 +18,8 @@ const streamExtension = _loadStreamExtensionIfNeeded()
|
|
|
18
18
|
|
|
19
19
|
function hasStreamInsert(insert, model) {
|
|
20
20
|
if (!model) return true
|
|
21
|
-
const
|
|
21
|
+
const name = insert.into.ref ? insert.into.ref[0] : insert.into
|
|
22
|
+
const into = model.definitions[ensureNoDraftsSuffix(name)]
|
|
22
23
|
if (!into) return false
|
|
23
24
|
|
|
24
25
|
if (insert.entries && insert.entries.length > 0) {
|