@sap/cds 5.5.2 → 5.6.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 +150 -17
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +31 -4
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +5 -3
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +9 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +14 -19
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +1 -12
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/toggles/alpha.js +1 -1
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +20 -10
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +11 -25
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +10 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/db/utils/deep.js +5 -7
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +9 -1
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +42 -29
- package/lib/req/cls.js +0 -39
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -2,6 +2,7 @@ const cds = require('../../cds')
|
|
|
2
2
|
let LOG = cds.log('app')
|
|
3
3
|
let _event
|
|
4
4
|
const PERSISTENCE_TABLE = '@cds.persistence.table'
|
|
5
|
+
const { rewriteAsterisks } = require('../../common/utils/rewriteAsterisks')
|
|
5
6
|
|
|
6
7
|
const getError = require('../error')
|
|
7
8
|
const { getEntityNameFromDeleteCQN, getEntityNameFromUpdateCQN } = require('../utils/cqn')
|
|
@@ -172,7 +173,7 @@ const _newColumns = (columns = [], transition, service, withAlias = false) => {
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
// ensure that renaming of a redirected assoc are also respected
|
|
175
|
-
if (column.expand) {
|
|
176
|
+
if (mapped && column.expand) {
|
|
176
177
|
// column.ref might be structured elements
|
|
177
178
|
let def
|
|
178
179
|
column.ref.forEach((ref, i) => {
|
|
@@ -186,9 +187,7 @@ const _newColumns = (columns = [], transition, service, withAlias = false) => {
|
|
|
186
187
|
// reuse _newColumns with new transition
|
|
187
188
|
const expandTarget = def._target
|
|
188
189
|
const subtransition = getTransition(expandTarget, service)
|
|
189
|
-
|
|
190
|
-
// REVISIT: in edom-retailer case, mapped was undefined which lead to a type error
|
|
191
|
-
if (mapped) mapped.transition = subtransition
|
|
190
|
+
mapped.transition = subtransition
|
|
192
191
|
|
|
193
192
|
newColumn.expand = _newColumns(column.expand, subtransition, service, withAlias)
|
|
194
193
|
}
|
|
@@ -338,8 +337,11 @@ const _newSelect = (query, transitions, service) => {
|
|
|
338
337
|
ref: _rewriteQueryPath(query.SELECT.from, transitions)
|
|
339
338
|
}
|
|
340
339
|
if (!newSelect.columns && targetTransition.mapping.size) newSelect.columns = _initialColumns(targetTransition)
|
|
341
|
-
if (newSelect.columns)
|
|
340
|
+
if (newSelect.columns) {
|
|
341
|
+
const isDB = service instanceof cds.DatabaseService
|
|
342
|
+
rewriteAsterisks({ SELECT: newSelect }, targetTransition.queryTarget, isDB)
|
|
342
343
|
newSelect.columns = _newColumns(newSelect.columns, targetTransition, service, service.kind !== 'app-service')
|
|
344
|
+
}
|
|
343
345
|
if (newSelect.having) newSelect.having = _newColumns(newSelect.having, targetTransition)
|
|
344
346
|
if (newSelect.groupBy) newSelect.groupBy = _newColumns(newSelect.groupBy, targetTransition)
|
|
345
347
|
if (newSelect.orderBy) newSelect.orderBy = _newColumns(newSelect.orderBy, targetTransition)
|
|
@@ -417,7 +419,7 @@ const _queryColumns = (target, columns = [], persistenceTable = false, force = f
|
|
|
417
419
|
if (!(target && target.query && target.query.SELECT)) return columns
|
|
418
420
|
const cqnColumns = target.query.SELECT.columns || []
|
|
419
421
|
const from = target.query.SELECT.from
|
|
420
|
-
const isTargetAliased = from.as &&
|
|
422
|
+
const isTargetAliased = from.as && cqnColumns.some(c => c.ref && c.ref[0] === from.as)
|
|
421
423
|
if (!columns.length) columns = Object.keys(target.elements).map(e => ({ ref: [e], as: e }))
|
|
422
424
|
return columns.reduce((res, column) => {
|
|
423
425
|
const renamed = _findRenamed(cqnColumns, column)
|
|
@@ -500,18 +502,26 @@ const _getTransitionData = (target, columns, service, skipForbiddenViewCheck) =>
|
|
|
500
502
|
if (!skipForbiddenViewCheck) _checkForForbiddenViews(target)
|
|
501
503
|
const targetStartsWithSrvName = service.namespace && target.name.startsWith(`${service.namespace}.`)
|
|
502
504
|
const persistenceTable = _isPersistenceTable(target)
|
|
503
|
-
columns = _queryColumns(
|
|
504
|
-
|
|
505
|
+
columns = _queryColumns(
|
|
506
|
+
target,
|
|
507
|
+
columns,
|
|
508
|
+
persistenceTable,
|
|
509
|
+
!(service instanceof cds.DatabaseService) && !targetStartsWithSrvName
|
|
510
|
+
)
|
|
511
|
+
if (persistenceTable && service instanceof cds.DatabaseService) {
|
|
505
512
|
return { target, transitionColumns: columns }
|
|
506
513
|
}
|
|
507
514
|
// stop projection resolving if it starts with the service name prefix
|
|
508
|
-
if (service
|
|
515
|
+
if (!(service instanceof cds.DatabaseService) && targetStartsWithSrvName) {
|
|
509
516
|
return { target, transitionColumns: columns }
|
|
510
517
|
}
|
|
511
518
|
// continue projection resolving if the target is a projection
|
|
512
519
|
if (target.query && target.query._target) {
|
|
513
520
|
const newTarget = target.query._target
|
|
514
|
-
if (
|
|
521
|
+
if (
|
|
522
|
+
service instanceof cds.DatabaseService ||
|
|
523
|
+
!(service.namespace && newTarget.name.startsWith(`${service.namespace}.`))
|
|
524
|
+
) {
|
|
515
525
|
return _getTransitionData(newTarget, columns, service, skipForbiddenViewCheck)
|
|
516
526
|
}
|
|
517
527
|
return { target: newTarget, transitionColumns: columns }
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const { getNavigationIfStruct } = require('./structured')
|
|
2
|
+
const getColumns = require('../../db/utils/columns')
|
|
3
|
+
const { ensureDraftsSuffix } = require('./draft')
|
|
4
|
+
|
|
5
|
+
const isAsteriskColumn = col => col === '*' || (col.ref && col.ref[0] === '*' && !col.expand)
|
|
6
|
+
|
|
7
|
+
const _isDuplicate = newColumn => column => {
|
|
8
|
+
if (newColumn.as) return column.as && column.as === newColumn.as
|
|
9
|
+
if (!column.ref) return
|
|
10
|
+
if (Array.isArray(newColumn)) newColumn = { ref: newColumn }
|
|
11
|
+
return newColumn.ref ? newColumn.ref.join('_') === column.ref.join('_') : newColumn === column.ref.join('_')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const _cqlDraftColumns = target => {
|
|
15
|
+
if (target.name.endsWith('DraftAdministrativeData')) return []
|
|
16
|
+
const draftName = ensureDraftsSuffix(target.name)
|
|
17
|
+
const subSelect = SELECT.from(draftName).columns([1])
|
|
18
|
+
for (const key in target.keys) {
|
|
19
|
+
if (key !== 'IsActiveEntity') subSelect.where([{ ref: [target.name, key] }, '=', { ref: [draftName, key] }])
|
|
20
|
+
}
|
|
21
|
+
return [
|
|
22
|
+
{ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
23
|
+
{ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
24
|
+
{
|
|
25
|
+
xpr: ['case', 'when', 'exists', subSelect, 'then', 'true', 'else', 'false', 'end'],
|
|
26
|
+
as: 'HasDraftEntity',
|
|
27
|
+
cast: { type: 'cds.Boolean' }
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const _expandColumn = (column, target, db) => {
|
|
33
|
+
if (!(column.ref && column.expand)) return
|
|
34
|
+
const nextTarget = getNavigationIfStruct(target, column.ref)
|
|
35
|
+
if (nextTarget && nextTarget._target && nextTarget._target.elements) _rewriteAsterisks(column, nextTarget._target, db)
|
|
36
|
+
return column
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const rewriteExpandAsterisk = (columns, target) => {
|
|
40
|
+
const expandAllColIdx = columns.findIndex(col => {
|
|
41
|
+
if (col.ref || !col.expand) return
|
|
42
|
+
return !Array.isArray(col.expand) ? col.expand === '*' : col.expand.indexOf('*') > -1
|
|
43
|
+
})
|
|
44
|
+
if (expandAllColIdx > -1) {
|
|
45
|
+
const { expand } = columns.splice(expandAllColIdx, 1)[0]
|
|
46
|
+
for (const elName in target.elements) {
|
|
47
|
+
if (target.elements[elName]._target && !columns.find(col => col.expand && col.ref && col.ref[0] === elName)) {
|
|
48
|
+
columns.push({ ref: [elName], expand: [...expand] })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const _rewriteAsterisk = (columns, target, db, isRoot) => {
|
|
55
|
+
const asteriskColumnIndex = columns.findIndex(col => isAsteriskColumn(col))
|
|
56
|
+
if (asteriskColumnIndex > -1) {
|
|
57
|
+
columns.splice(
|
|
58
|
+
asteriskColumnIndex,
|
|
59
|
+
1,
|
|
60
|
+
...getColumns(target, { db })
|
|
61
|
+
.map(c => ({ ref: [c.name] }))
|
|
62
|
+
.filter(c => !columns.find(_isDuplicate(c)) && (isRoot || c.ref[0] !== 'DraftAdministrativeData_DraftUUID'))
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const _rewriteAsterisks = (cqn, target, db, isRoot) => {
|
|
68
|
+
if (cqn.expand === '*') cqn.expand = ['*']
|
|
69
|
+
const columns = cqn.expand || cqn.columns
|
|
70
|
+
_rewriteAsterisk(columns, target, db, isRoot)
|
|
71
|
+
rewriteExpandAsterisk(columns, target)
|
|
72
|
+
for (const column of columns) {
|
|
73
|
+
_expandColumn(column, target, db)
|
|
74
|
+
}
|
|
75
|
+
return columns
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const rewriteAsterisks = (query, target, db = false, isDraft = false, onlyKeys = false) => {
|
|
79
|
+
if (!target || target.name.endsWith('_drafts')) return
|
|
80
|
+
if (!query.SELECT.columns || (query.SELECT.columns && !query.SELECT.columns.length)) {
|
|
81
|
+
if (isDraft || db) {
|
|
82
|
+
query.SELECT.columns = getColumns(target, { db, onlyKeys }).map(col => ({ ref: [col.name] }))
|
|
83
|
+
if (db && target._isDraftEnabled) query.SELECT.columns.push(..._cqlDraftColumns(target))
|
|
84
|
+
}
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
query.SELECT.columns = _rewriteAsterisks(query.SELECT, target, db, true)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
rewriteAsterisks,
|
|
92
|
+
isAsteriskColumn,
|
|
93
|
+
rewriteExpandAsterisk
|
|
94
|
+
}
|
|
@@ -3,23 +3,24 @@ const searchToLike = require('./searchToLike')
|
|
|
3
3
|
|
|
4
4
|
// convert $search system query option to WHERE/HAVING clause using
|
|
5
5
|
// the operator LIKE or CONTAINS
|
|
6
|
-
const search2cqn4sql = (
|
|
7
|
-
const { search2cqn4sql, targetName =
|
|
6
|
+
const search2cqn4sql = (query, model, options) => {
|
|
7
|
+
const { search2cqn4sql, targetName = query.SELECT.from.ref[0] } = options
|
|
8
8
|
const entity = model.definitions[targetName]
|
|
9
|
-
const columns = computeColumnsToBeSearched(
|
|
9
|
+
const columns = computeColumnsToBeSearched(query, entity)
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// Call custom (optimized search to cqn for sql implementation) that tries
|
|
12
12
|
// to optimize the search behavior for a specific database service.
|
|
13
|
-
|
|
13
|
+
// Note: $search query option combined with $filter is not currently optimized
|
|
14
|
+
if (typeof search2cqn4sql === 'function' && !query.SELECT.where) {
|
|
14
15
|
const search2cqnOptions = { columns, locale: options.locale }
|
|
15
|
-
return search2cqn4sql(
|
|
16
|
+
return search2cqn4sql(query, entity, search2cqnOptions)
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const cqnSearchPhrase =
|
|
19
|
+
const cqnSearchPhrase = query.SELECT.search
|
|
19
20
|
const expression = searchToLike(cqnSearchPhrase, columns)
|
|
20
21
|
|
|
21
22
|
// REVISIT: find out here if where or having must be used
|
|
22
|
-
|
|
23
|
+
query._aggregated ? query.having(expression) : query.where(expression)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
module.exports = search2cqn4sql
|
|
@@ -48,57 +48,60 @@ const _getNextTarget = (model, element, currentPath = []) => {
|
|
|
48
48
|
nextTargetName,
|
|
49
49
|
nextTarget: model.definitions[nextTargetName]
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (_isInlineStructured(element)) {
|
|
52
54
|
return {
|
|
53
55
|
nextTargetName: [...currentPath, element.name].join(DELIMITER),
|
|
54
56
|
nextTarget: element.items || element
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
|
|
57
60
|
return {}
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
61
64
|
*
|
|
62
|
-
* @param {CSN} model
|
|
63
|
-
* @param {
|
|
64
|
-
* @param {
|
|
65
|
-
* @param {
|
|
66
|
-
* @param {
|
|
67
|
-
* @param {
|
|
68
|
-
* @param {
|
|
69
|
-
* @param
|
|
70
|
-
* @param
|
|
71
|
-
* @param parent
|
|
72
|
-
* @param entityMap
|
|
65
|
+
* @param {import('@sap/cds-compiler/lib/api/main').CSN} model Model
|
|
66
|
+
* @param {Map} cache Internal - do not use
|
|
67
|
+
* @param {object} targetEntity The target entity which needs to be traversed
|
|
68
|
+
* @param {object} callbacks
|
|
69
|
+
* @param {function} callbacks.pick Callback function to pick elements. If it returns a truthy value, the element will be picked. The returned value is part of the template.
|
|
70
|
+
* @param {function} callbacks.ignore Callback function to ignore elements. If it returns a truthy value, the element will be ignored.
|
|
71
|
+
* @param {object} [parent=null] The parent entity
|
|
72
|
+
* @param {Map} [_entityMap] This parameter is an implementation side-effect — don't use it
|
|
73
|
+
* @param {array} [targetPath=[]]
|
|
73
74
|
*/
|
|
74
|
-
function _getTemplate(model, cache,
|
|
75
|
+
function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _entityMap = new Map(), targetPath = []) {
|
|
76
|
+
const { pick, ignore } = callbacks
|
|
75
77
|
const templateElements = new Map()
|
|
76
|
-
const template = { target, elements: templateElements }
|
|
77
|
-
const currentPath = [...targetPath,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
78
|
+
const template = { target: targetEntity, elements: templateElements }
|
|
79
|
+
const currentPath = [...targetPath, targetEntity.name]
|
|
80
|
+
_entityMap.set(currentPath.join(DELIMITER), { template })
|
|
81
|
+
if (!targetEntity.elements) return template
|
|
82
|
+
|
|
83
|
+
for (const elementName in targetEntity.elements) {
|
|
84
|
+
const element = targetEntity.elements[elementName]
|
|
85
|
+
if (ignore && ignore(element, targetEntity, parent)) continue
|
|
86
|
+
|
|
87
|
+
_pick(pick, element, targetEntity, parent, templateElements, elementName)
|
|
88
|
+
|
|
89
|
+
if (element.items) {
|
|
90
|
+
_pick(pick, element.items, targetEntity, parent, templateElements, ['_itemsOf', elementName].join(DELIMITER))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { nextTargetName, nextTarget } = _getNextTarget(model, element, currentPath)
|
|
94
|
+
const nextTargetCached = _entityMap.get(nextTargetName)
|
|
95
|
+
|
|
96
|
+
if (nextTargetCached) {
|
|
97
|
+
_addCacheToTemplateElements(templateElements, elementName, nextTargetCached)
|
|
98
|
+
} else if (nextTarget) {
|
|
99
|
+
// For associations and _typed_ structured elements, there's a (cacheable) target,
|
|
100
|
+
// inline structures must be handled separately.
|
|
101
|
+
const subTemplate = _isInlineStructured(element)
|
|
102
|
+
? _getTemplate(model, cache, nextTarget, { pick, ignore }, targetEntity, _entityMap, currentPath)
|
|
103
|
+
: cache.for(nextTarget, getTemplate(model, { pick, ignore }, targetEntity, _entityMap))
|
|
104
|
+
_addSubTemplate(templateElements, elementName, subTemplate)
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -112,11 +115,11 @@ const getTemplate =
|
|
|
112
115
|
|
|
113
116
|
const getCache = (anything, cache, newCacheFn) => {
|
|
114
117
|
let _cached = cache.get(anything)
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
if (_cached) return _cached
|
|
119
|
+
|
|
120
|
+
_cached = (typeof newCacheFn === 'function' && newCacheFn(anything, cache)) || new Map()
|
|
121
|
+
_cached.for = (_usecase, _newCacheFn) => getCache(_usecase, _cached, _newCacheFn)
|
|
122
|
+
cache.set(anything, _cached)
|
|
120
123
|
return _cached
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -124,11 +127,13 @@ module.exports = (usecase, tx, target, ...args) => {
|
|
|
124
127
|
// get model first as it may be added to tx (cf. "_ensureModel")
|
|
125
128
|
const model = tx.model
|
|
126
129
|
if (!model) return
|
|
130
|
+
|
|
127
131
|
// double-check with get target from model
|
|
128
132
|
// since target might come from anywhere like via cqn etc
|
|
129
133
|
if (!target) return
|
|
130
134
|
const root = (model && model.definitions[target.name]) || (target.elements && target)
|
|
131
135
|
if (!root) return
|
|
136
|
+
|
|
132
137
|
// tx could be the service itself
|
|
133
138
|
// prefer ApplicationService (i.e., tx.context._tx.__proto__)
|
|
134
139
|
// REVISIT: context._tx is not a stable API -> pls do not rely on that
|
|
@@ -136,10 +141,13 @@ module.exports = (usecase, tx, target, ...args) => {
|
|
|
136
141
|
? (tx.context._tx && Object.getPrototypeOf(tx.context._tx)) || Object.getPrototypeOf(tx)
|
|
137
142
|
: tx
|
|
138
143
|
if (!service) return
|
|
144
|
+
|
|
139
145
|
// cache templates at service for garbage collection
|
|
140
|
-
if (!service._templateCache) service._templateCache = new Map()
|
|
146
|
+
if (usecase && !service._templateCache) service._templateCache = new Map()
|
|
141
147
|
// model can be also a subset from tx
|
|
142
|
-
|
|
148
|
+
|
|
149
|
+
// if no usecase, don't save cache on the service object
|
|
150
|
+
return getCache(usecase, usecase ? service._templateCache : new Map())
|
|
143
151
|
.for(model)
|
|
144
152
|
.for(root, getTemplate(model, ...args))
|
|
145
153
|
}
|
|
@@ -109,6 +109,7 @@ class DatabaseService extends cds.Service {
|
|
|
109
109
|
if (query && (!streamQuery.SELECT.columns || streamQuery.SELECT.columns.length !== 0)) {
|
|
110
110
|
streamQuery.columns([query])
|
|
111
111
|
}
|
|
112
|
+
|
|
112
113
|
delete streamQuery.SELECT.one
|
|
113
114
|
streamQuery._streaming = true
|
|
114
115
|
|
|
@@ -119,12 +120,18 @@ class DatabaseService extends cds.Service {
|
|
|
119
120
|
}
|
|
120
121
|
})
|
|
121
122
|
|
|
122
|
-
if (
|
|
123
|
+
if (
|
|
124
|
+
!streamQuery.SELECT.where &&
|
|
125
|
+
!(
|
|
126
|
+
streamQuery.SELECT.from &&
|
|
127
|
+
streamQuery.SELECT.from.ref &&
|
|
128
|
+
streamQuery.SELECT.from.ref[streamQuery.SELECT.from.ref.length - 1].where
|
|
129
|
+
)
|
|
130
|
+
) {
|
|
123
131
|
return {
|
|
124
132
|
where: (...args) => {
|
|
125
133
|
streamQuery.where(...args)
|
|
126
134
|
this._runStream(streamQuery, result)
|
|
127
|
-
|
|
128
135
|
return result
|
|
129
136
|
}
|
|
130
137
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
3
|
const { getAllKeys } = require('../../cds-services/adapter/odata-v4/odata-to-cqn/utils')
|
|
4
|
-
const { getOnCond } = require('../../common/utils/generateOnCond')
|
|
5
4
|
|
|
6
5
|
const { getNavigationIfStruct } = require('../../common/utils/structured')
|
|
7
6
|
const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require('../../common/utils/draft')
|
|
8
7
|
const { filterKeys } = require('../../fiori/utils/handler')
|
|
8
|
+
const { isAsteriskColumn } = require('../../common/utils/rewriteAsterisks')
|
|
9
9
|
|
|
10
10
|
// Symbols are used to add extra information in response structure
|
|
11
11
|
const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
|
|
@@ -656,7 +656,7 @@ class JoinCQNFromExpanded {
|
|
|
656
656
|
SELECT: {
|
|
657
657
|
columns: cols,
|
|
658
658
|
from: unionFrom,
|
|
659
|
-
as:
|
|
659
|
+
as: tableAlias
|
|
660
660
|
}
|
|
661
661
|
}
|
|
662
662
|
}
|
|
@@ -822,23 +822,12 @@ class JoinCQNFromExpanded {
|
|
|
822
822
|
// No sub select
|
|
823
823
|
const subSelectColumns = this._getSubSelectColumns(readToOneCQN)
|
|
824
824
|
|
|
825
|
-
const navigation = getNavigationIfStruct(entity, tableAlias === columns[0] ? columns.slice(1) : columns)
|
|
826
|
-
const onConditionOptions = {
|
|
827
|
-
associationNames: columns,
|
|
828
|
-
csn: this._csn,
|
|
829
|
-
aliases: {
|
|
830
|
-
select: tableAlias,
|
|
831
|
-
join: parentAlias
|
|
832
|
-
},
|
|
833
|
-
resolveView: false
|
|
834
|
-
}
|
|
835
|
-
|
|
836
825
|
if (subSelectColumns.length === 0) {
|
|
837
|
-
return
|
|
826
|
+
return entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
|
|
838
827
|
}
|
|
839
828
|
|
|
840
829
|
const aliases = this._getAliases(subSelectColumns)
|
|
841
|
-
const on =
|
|
830
|
+
const on = entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
|
|
842
831
|
|
|
843
832
|
for (const element of on) {
|
|
844
833
|
if (element.ref && aliases[element.ref[0]] && aliases[element.ref[0]][element.ref[1]]) {
|
|
@@ -1069,15 +1058,7 @@ class JoinCQNFromExpanded {
|
|
|
1069
1058
|
!this._csn.definitions[colTarget]._isDraftEnabled
|
|
1070
1059
|
const ref = this._getJoinRef(entity.elements, colRef[0], expandActive, defaultLanguageThis)
|
|
1071
1060
|
const tableAlias = this._createAlias(toManyTree.concat(colRef).join(':'))
|
|
1072
|
-
const
|
|
1073
|
-
associationNames: colRef[0],
|
|
1074
|
-
csn: this._csn,
|
|
1075
|
-
aliases: {
|
|
1076
|
-
select: tableAlias,
|
|
1077
|
-
join: 'filterExpand'
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
const on = getOnCond(element, onConditionOptions)
|
|
1061
|
+
const on = entity._relations[colRef[0]].join(tableAlias, 'filterExpand')
|
|
1081
1062
|
const filterExpand = this._getFilterExpandCQN(readToOneCQN, on, parentAlias, entity.keys)
|
|
1082
1063
|
const expandedEntity = this._csn.definitions[colTarget]
|
|
1083
1064
|
const joinColumns = this._getJoinColumnsFromOnAddToMapping(mappings[colRef[0]], parentAlias, on, entity)
|
|
@@ -1529,8 +1510,13 @@ class JoinCQNFromExpanded {
|
|
|
1529
1510
|
}
|
|
1530
1511
|
|
|
1531
1512
|
_isNotIncludedIn(columns) {
|
|
1513
|
+
if (columns.some(column => isAsteriskColumn(column))) return _ => false
|
|
1532
1514
|
return entry =>
|
|
1533
|
-
!columns.some(
|
|
1515
|
+
!columns.some(
|
|
1516
|
+
column =>
|
|
1517
|
+
(typeof column === 'object' && column.ref && column.ref[1] === entry) ||
|
|
1518
|
+
('val' in column && column.as === entry)
|
|
1519
|
+
)
|
|
1534
1520
|
}
|
|
1535
1521
|
|
|
1536
1522
|
/**
|
|
@@ -95,7 +95,7 @@ class RawToExpanded {
|
|
|
95
95
|
|
|
96
96
|
// the expanded items may include the actives of the deleted drafts -> filter out
|
|
97
97
|
if (rootIsActiveEntity !== null) {
|
|
98
|
-
if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity
|
|
98
|
+
if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity !== false)
|
|
99
99
|
else expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === rootIsActiveEntity)
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -119,6 +119,7 @@ class RawToExpanded {
|
|
|
119
119
|
row[key] = parsed || null
|
|
120
120
|
} else if (rootIsActiveEntity !== null) {
|
|
121
121
|
if (mapping[TO_ACTIVE]) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
|
|
122
|
+
else if (rootIsActiveEntity) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
|
|
122
123
|
else row[key] = parsed && parsed.IsActiveEntity === rootIsActiveEntity ? parsed : null
|
|
123
124
|
}
|
|
124
125
|
} else {
|
|
@@ -13,8 +13,8 @@ const cds = require('../../cds')
|
|
|
13
13
|
|
|
14
14
|
const normalizeTimeData = require('../utils/normalizeTimeData')
|
|
15
15
|
|
|
16
|
-
const enrichDataWithKeysFromWhere = require('../../common/utils/
|
|
17
|
-
const {
|
|
16
|
+
const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
|
|
17
|
+
const { propagateForeignKeys } = require('../../common/utils/propagateForeignKeys')
|
|
18
18
|
const getTemplate = require('../../common/utils/template')
|
|
19
19
|
const templateProcessor = require('../../common/utils/templateProcessor')
|
|
20
20
|
|
|
@@ -25,13 +25,13 @@ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
|
25
25
|
const _isManaged = (category, event) =>
|
|
26
26
|
(category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
|
|
27
27
|
|
|
28
|
-
const _processComplexCategory = ({ row, key, val, category, req }) => {
|
|
28
|
+
const _processComplexCategory = ({ row, key, val, category, req, element }) => {
|
|
29
29
|
const categoryArgs = category.args
|
|
30
30
|
category = category.category
|
|
31
31
|
|
|
32
32
|
// propagate keys
|
|
33
33
|
if (category === 'propagateForeignKeys') {
|
|
34
|
-
propagateForeignKeys(key, row,
|
|
34
|
+
propagateForeignKeys(key, row, element._foreignKeys, element._isCompositionEffective)
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -63,7 +63,7 @@ const _processComplexCategory = ({ row, key, val, category, req }) => {
|
|
|
63
63
|
const _processCategory = ({ category, row, key, element, val, req }) => {
|
|
64
64
|
// use args only inside this if (sonar type error warning)
|
|
65
65
|
if (typeof category === 'object') {
|
|
66
|
-
_processComplexCategory({ category, row, key, val, req })
|
|
66
|
+
_processComplexCategory({ category, row, key, val, req, element })
|
|
67
67
|
return
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -136,12 +136,8 @@ const _pick = element => {
|
|
|
136
136
|
categories.push('associationEffective')
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
if (element.isAssociation) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (_foreignKeyPropagations) {
|
|
143
|
-
categories.push({ category: 'propagateForeignKeys', args: { foreignKeyPropagations: _foreignKeyPropagations } })
|
|
144
|
-
}
|
|
139
|
+
if (element.isAssociation && element._foreignKeys.length) {
|
|
140
|
+
categories.push({ category: 'propagateForeignKeys' })
|
|
145
141
|
}
|
|
146
142
|
|
|
147
143
|
// generate uuid
|
|
@@ -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 } = require('../../common/utils/cqn2cqn4sql')
|
|
5
5
|
const { getDependents } = require('../../common/utils/csn')
|
|
6
6
|
|
|
7
7
|
const CRUD = {
|
|
@@ -54,7 +54,7 @@ async function beforeDelete(req) {
|
|
|
54
54
|
select = select.where(req.query.DELETE.where)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
select = cqn2cqn4sql(select, this.model)
|
|
57
|
+
select = cqn2cqn4sql(select, this.model, { service: this })
|
|
58
58
|
|
|
59
59
|
req._beforeDeleteData = await this._read(this.model, this.dbc, select, req.context || req)
|
|
60
60
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
|
|
1
|
+
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
2
2
|
const generateAliases = require('../utils/generateAliases')
|
|
3
|
-
const rewriteAsterisk = require('../../common/utils/rewriteAsterisk')
|
|
4
3
|
const { restoreLink } = require('../../common/utils/resolveView')
|
|
5
4
|
|
|
6
5
|
const _isLinked = req => {
|
|
@@ -21,13 +20,11 @@ function handler(req) {
|
|
|
21
20
|
const streaming = req.query._streaming
|
|
22
21
|
const validationQuery = req.query._validationQuery
|
|
23
22
|
|
|
24
|
-
rewriteAsterisk(req)
|
|
25
|
-
|
|
26
23
|
// for restore link to req.data
|
|
27
24
|
const linked = _isLinked(req)
|
|
28
25
|
|
|
29
26
|
// convert to sql cqn
|
|
30
|
-
req.query = cqn2cqn4sql(req.query, this.model)
|
|
27
|
+
req.query = cqn2cqn4sql(req.query, this.model, { service: this })
|
|
31
28
|
|
|
32
29
|
// REVISIT: should not be necessary
|
|
33
30
|
// restore link to req.data
|
|
@@ -10,8 +10,8 @@ function _arrayWithCount(a, count) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function _createCountQuery(query) {
|
|
13
|
-
const _query = JSON.parse(JSON.stringify(query))
|
|
14
|
-
_query.SELECT.columns = [{ func: 'count', args: [{ val: 1 }], as: '
|
|
13
|
+
const _query = JSON.parse(JSON.stringify(query)) // REVISIT: Use query.clone() instead
|
|
14
|
+
_query.SELECT.columns = [{ func: 'count', args: [{ val: 1 }], as: '$count' }]
|
|
15
15
|
delete _query.SELECT.groupBy
|
|
16
16
|
delete _query.SELECT.limit
|
|
17
17
|
delete _query.SELECT.orderBy // not necessary to keep that
|
|
@@ -25,6 +25,11 @@ function _createCountQuery(query) {
|
|
|
25
25
|
return _query
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const countValue = countResult => {
|
|
29
|
+
if (countResult._counted_ != null) return countResult._counted_
|
|
30
|
+
if (countResult.$count != null) return countResult.$count
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) => {
|
|
29
34
|
const { user, locale, timestamp } = req
|
|
30
35
|
const isoTs = timestampToISO(timestamp)
|
|
@@ -37,7 +42,7 @@ const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) =>
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
// needed in case of expand
|
|
40
|
-
query.
|
|
45
|
+
if (query._target !== req.target) query._target = req.target
|
|
41
46
|
|
|
42
47
|
if (query.SELECT.count) {
|
|
43
48
|
if (query.SELECT.limit) {
|
|
@@ -45,11 +50,11 @@ const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) =>
|
|
|
45
50
|
const countResultPromise = executeSelectCQN(model, dbc, countQuery, user, locale, isoTs)
|
|
46
51
|
if (query.SELECT.limit.rows && query.SELECT.limit.rows.val === 0) {
|
|
47
52
|
// We don't need to perform our result query
|
|
48
|
-
return countResultPromise.then(countResult => _arrayWithCount([], countResult[0]
|
|
53
|
+
return countResultPromise.then(countResult => _arrayWithCount([], countValue(countResult[0])))
|
|
49
54
|
} else {
|
|
50
55
|
const resultPromise = executeSelectCQN(model, dbc, query, user, locale, isoTs)
|
|
51
56
|
return Promise.all([countResultPromise, resultPromise]).then(([countResult, result]) =>
|
|
52
|
-
_arrayWithCount(result, countResult[0]
|
|
57
|
+
_arrayWithCount(result, countValue(countResult[0]))
|
|
53
58
|
)
|
|
54
59
|
}
|
|
55
60
|
} else {
|
|
@@ -227,6 +227,12 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
227
227
|
* @private
|
|
228
228
|
*/
|
|
229
229
|
_addInOrNotIn(reference, operator, values) {
|
|
230
|
+
if (values.val === null) {
|
|
231
|
+
this._addToOutputObj(new this.ReferenceBuilder(reference, this._options, this._csn).build(), false)
|
|
232
|
+
this._outputObj.sql.push('is null')
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
|
|
230
236
|
if (Array.isArray(values.val)) {
|
|
231
237
|
this._addArrayForInQuery(reference, operator, values.val)
|
|
232
238
|
return true
|