@sap/cds 5.4.3 → 5.5.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 +239 -2
- package/apis/ql.d.ts +17 -15
- package/app/index.js +1 -1
- package/bin/build/buildTaskEngine.js +26 -42
- package/bin/build/buildTaskFactory.js +6 -10
- package/bin/build/buildTaskHandler.js +2 -4
- package/bin/build/buildTaskProvider.js +3 -1
- package/bin/build/buildTaskProviderFactory.js +9 -15
- package/bin/build/constants.js +15 -3
- package/bin/build/index.js +5 -4
- package/bin/build/mtaUtil.js +8 -11
- package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
- package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
- package/bin/build/provider/buildTaskProviderInternal.js +16 -42
- package/bin/build/provider/fiori/index.js +13 -24
- package/bin/build/provider/hana/2migration.js +17 -15
- package/bin/build/provider/hana/2tabledata.js +52 -48
- package/bin/build/provider/hana/index.js +27 -25
- package/bin/build/provider/hana/migrationtable.js +91 -67
- package/bin/build/provider/java-cf/index.js +14 -24
- package/bin/build/provider/mtx/index.js +12 -14
- package/bin/build/provider/node-cf/index.js +18 -32
- package/bin/cds.js +5 -5
- package/bin/serve.js +29 -23
- package/bin/version.js +0 -1
- package/lib/compile/etc/_localized.js +4 -9
- package/lib/compile/for/sql.js +5 -2
- package/lib/compile/parse.js +25 -17
- package/lib/compile/to/srvinfo.js +2 -1
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +48 -49
- package/lib/core/classes.js +1 -1
- package/lib/core/reflect.js +10 -2
- package/lib/deploy.js +26 -23
- package/lib/env/defaults.js +13 -6
- package/lib/env/index.js +73 -78
- package/lib/env/requires.js +38 -19
- package/lib/index.js +9 -10
- package/lib/lazy.js +2 -2
- package/lib/log/index.js +33 -45
- package/lib/log/service/index.js +2 -2
- package/lib/ql/CREATE.js +14 -9
- package/lib/ql/DELETE.js +6 -5
- package/lib/ql/DROP.js +12 -9
- package/lib/ql/INSERT.js +40 -16
- package/lib/ql/Query.js +67 -40
- package/lib/ql/SELECT.js +162 -127
- package/lib/ql/UPDATE.js +74 -42
- package/lib/ql/Whereable.js +77 -87
- package/lib/ql/index.js +36 -24
- package/lib/ql/parse.js +35 -0
- package/lib/req/context.js +44 -8
- package/lib/req/locale.js +7 -7
- package/lib/serve/Service-api.js +21 -14
- package/lib/serve/Service-dispatch.js +28 -12
- package/lib/serve/Transaction.js +22 -10
- package/lib/serve/index.js +16 -11
- package/lib/utils/axios.js +23 -16
- package/lib/utils/data.js +35 -0
- package/lib/utils/tests.js +27 -18
- package/libx/_runtime/audit/generic/personal/access.js +81 -0
- package/libx/_runtime/audit/generic/personal/constants.js +4 -0
- package/libx/_runtime/audit/generic/personal/index.js +50 -0
- package/libx/_runtime/audit/generic/personal/modification.js +138 -0
- package/libx/_runtime/audit/generic/personal/utils.js +186 -0
- package/libx/_runtime/audit/utils/v2.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
- 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/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
- package/libx/_runtime/cds-services/services/Service.js +40 -5
- package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
- package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
- package/libx/_runtime/common/composition/data.js +66 -63
- package/libx/_runtime/common/composition/delete.js +97 -71
- package/libx/_runtime/common/composition/index.js +2 -1
- package/libx/_runtime/common/composition/insert.js +34 -11
- package/libx/_runtime/common/composition/tree.js +119 -92
- package/libx/_runtime/common/composition/update.js +12 -1
- package/libx/_runtime/common/composition/utils.js +1 -3
- package/libx/_runtime/common/constants/draft.js +12 -1
- package/libx/_runtime/common/generic/auth.js +53 -31
- package/libx/_runtime/common/generic/crud.js +14 -13
- package/libx/_runtime/common/generic/input.js +23 -26
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +16 -16
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +4 -0
- package/libx/_runtime/common/utils/backlinks.js +12 -5
- package/libx/_runtime/common/utils/cqn.js +6 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +123 -108
- package/libx/_runtime/common/utils/csn.js +56 -4
- package/libx/_runtime/common/utils/data.js +0 -37
- package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
- package/libx/_runtime/common/utils/generateOnCond.js +11 -12
- package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
- package/libx/_runtime/common/utils/path.js +35 -0
- package/libx/_runtime/common/utils/postProcessing.js +86 -0
- package/libx/_runtime/common/utils/quotingStyles.js +37 -26
- package/libx/_runtime/common/utils/resolveView.js +227 -173
- package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
- package/libx/_runtime/common/utils/structured.js +13 -13
- package/libx/_runtime/common/utils/template.js +10 -5
- package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
- package/libx/_runtime/common/utils/templateProcessor.js +28 -72
- package/libx/_runtime/common/utils/union.js +31 -0
- package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
- package/libx/_runtime/db/Service.js +1 -1
- package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
- package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
- package/libx/_runtime/db/expand/index.js +3 -3
- package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
- package/libx/_runtime/db/generic/index.js +1 -1
- package/libx/_runtime/db/generic/input.js +5 -7
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/generic/rewrite.js +2 -10
- package/libx/_runtime/db/generic/update.js +13 -5
- package/libx/_runtime/db/generic/virtual.js +22 -58
- package/libx/_runtime/db/query/delete.js +7 -4
- package/libx/_runtime/db/query/insert.js +6 -4
- package/libx/_runtime/db/query/read.js +21 -8
- package/libx/_runtime/db/query/run.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -4
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
- package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
- package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
- package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
- package/libx/_runtime/db/utils/deep.js +8 -0
- package/libx/_runtime/db/utils/generateAliases.js +2 -1
- package/libx/_runtime/fiori/generic/activate.js +19 -15
- package/libx/_runtime/fiori/generic/before.js +3 -11
- package/libx/_runtime/fiori/generic/cancel.js +1 -1
- package/libx/_runtime/fiori/generic/delete.js +3 -1
- package/libx/_runtime/fiori/generic/edit.js +12 -2
- package/libx/_runtime/fiori/generic/new.js +5 -5
- package/libx/_runtime/fiori/generic/patch.js +0 -18
- package/libx/_runtime/fiori/generic/read.js +261 -205
- package/libx/_runtime/fiori/utils/delete.js +36 -7
- package/libx/_runtime/fiori/utils/handler.js +43 -44
- package/libx/_runtime/fiori/utils/where.js +30 -15
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
- package/libx/_runtime/hana/execute.js +3 -3
- package/libx/_runtime/hana/localized.js +4 -4
- package/libx/_runtime/hana/pool.js +29 -14
- package/libx/_runtime/hana/search2cqn4sql.js +2 -1
- package/libx/_runtime/hana/searchToContains.js +18 -14
- package/libx/_runtime/index.js +0 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
- package/libx/_runtime/messaging/service.js +7 -6
- package/libx/_runtime/odata/cqn2odata.js +110 -43
- package/libx/_runtime/odata/index.js +26 -48
- package/libx/_runtime/odata/odata2cqn.js +1 -6154
- package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
- package/libx/_runtime/odata/readToCqn.js +94 -64
- package/libx/_runtime/remote/Service.js +74 -21
- package/libx/_runtime/remote/cqn2odata/index.js +1 -5
- package/libx/_runtime/remote/utils/client.js +24 -101
- package/libx/_runtime/remote/utils/dataConversion.js +27 -12
- package/libx/_runtime/sqlite/Service.js +3 -5
- package/libx/_runtime/sqlite/execute.js +33 -27
- package/libx/_runtime/sqlite/localized.js +12 -7
- package/libx/_runtime/types/api.js +10 -0
- package/package.json +2 -2
- package/server.js +16 -2
- package/lib/ql/grammar.pegjs +0 -208
- package/lib/ql/parser.js +0 -1
- package/lib/ql/rt/DELETE.js +0 -29
- package/lib/ql/rt/INSERT.js +0 -23
- package/lib/ql/rt/Query.js +0 -84
- package/lib/ql/rt/SELECT.js +0 -174
- package/lib/ql/rt/UPDATE.js +0 -119
- package/lib/ql/rt/_helpers.js +0 -91
- package/lib/ql/rt/index.js +0 -32
- package/libx/_runtime/audit/generic/personal.js +0 -260
- package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
- package/libx/_runtime/cds-services/statements/Create.js +0 -57
- package/libx/_runtime/cds-services/statements/Delete.js +0 -33
- package/libx/_runtime/cds-services/statements/Drop.js +0 -42
- package/libx/_runtime/cds-services/statements/Insert.js +0 -201
- package/libx/_runtime/cds-services/statements/Select.js +0 -826
- package/libx/_runtime/cds-services/statements/Update.js +0 -181
- package/libx/_runtime/cds-services/statements/Where.js +0 -726
- package/libx/_runtime/cds-services/statements/index.js +0 -25
- package/libx/_runtime/common/generic/resolve-mock.js +0 -9
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { hasExpand, createJoinCQNFromExpanded } = require('./expandCQNToJoin')
|
|
2
2
|
const rawToExpanded = require('./rawToExpanded')
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
|
-
hasExpand
|
|
6
|
-
createJoinCQNFromExpanded
|
|
5
|
+
hasExpand,
|
|
6
|
+
createJoinCQNFromExpanded,
|
|
7
7
|
rawToExpanded
|
|
8
8
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
const EXPAND = Symbol.for('sap.cds.expand')
|
|
2
2
|
const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
|
|
3
3
|
const TO_MANY = Symbol.for('sap.cds.toMany')
|
|
4
|
+
const TO_ACTIVE = Symbol.for('sap.cds.toActive')
|
|
4
5
|
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
5
6
|
|
|
6
7
|
class RawToExpanded {
|
|
7
|
-
constructor(configs, queries, one) {
|
|
8
|
+
constructor(configs, queries, one, rootEntity) {
|
|
8
9
|
this._one = one
|
|
9
10
|
this._toManyResults = { expand: {} }
|
|
10
11
|
this._result = []
|
|
11
12
|
this._configs = configs
|
|
12
13
|
this._queries = queries
|
|
14
|
+
this._rootEntity = rootEntity
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -66,20 +68,40 @@ class RawToExpanded {
|
|
|
66
68
|
* @returns {object}
|
|
67
69
|
* @private
|
|
68
70
|
*/
|
|
71
|
+
// eslint-disable-next-line complexity
|
|
69
72
|
_parseRaw({ mappings, toManyTree, conversionMapper, entry }) {
|
|
70
73
|
let isEntityNull
|
|
71
74
|
|
|
72
75
|
const row = {}
|
|
73
76
|
|
|
77
|
+
const rootIsActiveEntity = this._rootEntity._isDraftEnabled
|
|
78
|
+
? mappings.IsActiveEntity
|
|
79
|
+
? entry[mappings.IsActiveEntity] === null
|
|
80
|
+
? null
|
|
81
|
+
: !!entry[mappings.IsActiveEntity]
|
|
82
|
+
: 'IsActiveEntity' in entry
|
|
83
|
+
? entry.IsActiveEntity === null
|
|
84
|
+
? null
|
|
85
|
+
: !!entry.IsActiveEntity
|
|
86
|
+
: null
|
|
87
|
+
: null
|
|
88
|
+
|
|
74
89
|
// A raw row contains more elements than the config. Iterating over config is faster.
|
|
75
90
|
for (const key in mappings) {
|
|
76
91
|
// To many entries have been already processed and cached
|
|
77
92
|
const mapping = mappings[key]
|
|
78
93
|
if (mapping[TO_MANY]) {
|
|
79
|
-
|
|
94
|
+
let expandedItems = this._getResultCache(toManyTree.concat(key))[mapping[GET_KEY_VALUE](false, entry)] || []
|
|
95
|
+
|
|
96
|
+
// the expanded items may include the actives of the deleted drafts -> filter out
|
|
97
|
+
if (rootIsActiveEntity !== null) {
|
|
98
|
+
if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === true)
|
|
99
|
+
else expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === rootIsActiveEntity)
|
|
100
|
+
}
|
|
80
101
|
|
|
81
|
-
|
|
102
|
+
row[key] = expandedItems
|
|
82
103
|
} else if (typeof mapping === 'object') {
|
|
104
|
+
// > Will be true in case of 1:1 expands
|
|
83
105
|
// check if the expanded entry doesn't exists
|
|
84
106
|
if (this._isExpandEmpty(mapping, entry)) {
|
|
85
107
|
row[key] = null
|
|
@@ -92,9 +114,15 @@ class RawToExpanded {
|
|
|
92
114
|
entry: entry
|
|
93
115
|
})
|
|
94
116
|
|
|
95
|
-
|
|
96
|
-
|
|
117
|
+
// the expanded items may include the actives of the deleted drafts -> filter out
|
|
118
|
+
if (rootIsActiveEntity === null || key === 'DraftAdministrativeData') {
|
|
119
|
+
row[key] = parsed || null
|
|
120
|
+
} else if (rootIsActiveEntity !== null) {
|
|
121
|
+
if (mapping[TO_ACTIVE]) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
|
|
122
|
+
else row[key] = parsed && parsed.IsActiveEntity === rootIsActiveEntity ? parsed : null
|
|
123
|
+
}
|
|
97
124
|
} else {
|
|
125
|
+
// > No expand convert the result directly.
|
|
98
126
|
const rawValue = entry[mapping]
|
|
99
127
|
// Assume a DB will not return undefined, but always null
|
|
100
128
|
row[key] = this._convertValue(rawValue, conversionMapper.get(mapping))
|
|
@@ -195,10 +223,11 @@ class RawToExpanded {
|
|
|
195
223
|
* @param {Array} configs - Array of instructions how to combine and expand the 1 to N results
|
|
196
224
|
* @param {Array} queries - Same amount of queries as configs. Contains one query for each to many expand.
|
|
197
225
|
* @param {boolean} one - SELECT.one has been used
|
|
226
|
+
* @param {object} rootEntity - the root entity
|
|
198
227
|
* @returns {Promise<Array>} The complete expanded result set.
|
|
199
228
|
*/
|
|
200
|
-
const rawToExpanded = (configs, queries, one) => {
|
|
201
|
-
return new RawToExpanded(configs, queries, one).toExpanded().catch(err => {
|
|
229
|
+
const rawToExpanded = (configs, queries, one, rootEntity) => {
|
|
230
|
+
return new RawToExpanded(configs, queries, one, rootEntity).toExpanded().catch(err => {
|
|
202
231
|
Promise.all(queries).catch(() => {})
|
|
203
232
|
throw err
|
|
204
233
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const rewrite = require('./rewrite')
|
|
3
3
|
const input = require('./input')
|
|
4
4
|
const integrity = require('./integrity')
|
|
5
|
-
const {
|
|
5
|
+
const { convertVirtuals: virtual } = require('./virtual')
|
|
6
6
|
// on
|
|
7
7
|
const CREATE = require('./create')
|
|
8
8
|
const READ = require('./read')
|
|
@@ -31,7 +31,7 @@ const _processComplexCategory = ({ row, key, val, category, req }) => {
|
|
|
31
31
|
|
|
32
32
|
// propagate keys
|
|
33
33
|
if (category === 'propagateForeignKeys') {
|
|
34
|
-
propagateForeignKeys(key, row, categoryArgs.foreignKeyPropagations)
|
|
34
|
+
propagateForeignKeys(key, row, categoryArgs.foreignKeyPropagations, { onlyWriteCompositionEffective: true })
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -60,10 +60,10 @@ const _processComplexCategory = ({ row, key, val, category, req }) => {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const _processCategory = ({ row, key,
|
|
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({ row, key, val,
|
|
66
|
+
_processComplexCategory({ category, row, key, val, req })
|
|
67
67
|
return
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -75,7 +75,6 @@ const _processCategory = ({ row, key, val, category, req, element, suppressError
|
|
|
75
75
|
|
|
76
76
|
// not null without default (for better error message)
|
|
77
77
|
if (category === '!default' && val == null && req.event === 'CREATE') {
|
|
78
|
-
if (suppressErrorPropagation) return
|
|
79
78
|
req.error(400, 'ASSERT_NOT_NULL', key, [key])
|
|
80
79
|
return
|
|
81
80
|
}
|
|
@@ -87,19 +86,18 @@ const _processCategory = ({ row, key, val, category, req, element, suppressError
|
|
|
87
86
|
|
|
88
87
|
// check for forbidden deep operations for association
|
|
89
88
|
if (category === 'associationEffective' && (req.event === 'CREATE' || req.event === 'UPDATE')) {
|
|
90
|
-
if (suppressErrorPropagation) return
|
|
91
89
|
checkIfAssocDeep(element, val, req)
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
const processorFn =
|
|
96
94
|
req =>
|
|
97
|
-
(row, key, element, plain
|
|
95
|
+
({ row, key, element, plain }) => {
|
|
98
96
|
const categories = plain.categories
|
|
99
97
|
const val = row[key]
|
|
100
98
|
|
|
101
99
|
for (const category of categories) {
|
|
102
|
-
_processCategory({ row, key,
|
|
100
|
+
_processCategory({ category, row, key, element, val, req })
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
|
|
@@ -48,7 +48,7 @@ async function beforeDelete(req) {
|
|
|
48
48
|
const dependents = getDependents(target, this.model)
|
|
49
49
|
if (!dependents) return
|
|
50
50
|
|
|
51
|
-
const keys = Object.keys(target.keys).filter(k => _isPrimitiveKey(target.elements[k]))
|
|
51
|
+
const keys = Object.keys(target.keys).filter(k => _isPrimitiveKey(target.elements[k]) && k !== 'IsActiveEntity')
|
|
52
52
|
let select = SELECT(keys).from(req.target.name)
|
|
53
53
|
if (req.query.DELETE.where) {
|
|
54
54
|
select = select.where(req.query.DELETE.where)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
|
|
2
2
|
const generateAliases = require('../utils/generateAliases')
|
|
3
3
|
const rewriteAsterisk = require('../../common/utils/rewriteAsterisk')
|
|
4
|
+
const { restoreLink } = require('../../common/utils/resolveView')
|
|
4
5
|
|
|
5
6
|
const _isLinked = req => {
|
|
6
7
|
if (req.query.INSERT && req.query.INSERT.entries) {
|
|
@@ -11,15 +12,6 @@ const _isLinked = req => {
|
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const _restoreLink = req => {
|
|
15
|
-
if (req.query.INSERT && req.query.INSERT.entries) {
|
|
16
|
-
if (Array.isArray(req.query.INSERT.entries)) req.data = req.query.INSERT.entries[0]
|
|
17
|
-
else req.data = req.query.INSERT.entries
|
|
18
|
-
} else if (req.query.UPDATE && req.query.UPDATE.data) {
|
|
19
|
-
req.data = req.query.UPDATE.data
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
function handler(req) {
|
|
24
16
|
// REVISIT: req.target._unresolved for join queries
|
|
25
17
|
if (!this.model || typeof req.query === 'string' /* || !req.target || req.target._unresolved */) {
|
|
@@ -39,7 +31,7 @@ function handler(req) {
|
|
|
39
31
|
|
|
40
32
|
// REVISIT: should not be necessary
|
|
41
33
|
// restore link to req.data
|
|
42
|
-
if (linked)
|
|
34
|
+
if (linked) restoreLink(req)
|
|
43
35
|
|
|
44
36
|
if (streaming) req.query._streaming = streaming
|
|
45
37
|
if (validationQuery) req.query._validationQuery = validationQuery
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// const UpdateResult = require('../result/UpdateResult')
|
|
3
3
|
|
|
4
4
|
const { allKeysAreProvided } = require('../../cds-services/services/utils/handlerUtils')
|
|
5
|
+
const onlyKeysRemain = require('../../common/utils/onlyKeysRemain')
|
|
5
6
|
|
|
6
7
|
const _enrichKeysFromOldData = (req, oldData) => {
|
|
7
8
|
const data = req.data && (Array.isArray(req.data) ? req.data : [req.data])
|
|
@@ -27,7 +28,7 @@ const _addKeysToQuery = req => {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const _targetKeys = target => {
|
|
30
|
-
return Object.values(target.keys)
|
|
31
|
+
return Object.values(target.keys || {})
|
|
31
32
|
.filter(k => !(k.is2one || k.is2many))
|
|
32
33
|
.map(k => ({ ref: [k.name] }))
|
|
33
34
|
}
|
|
@@ -47,20 +48,27 @@ module.exports = async function (req) {
|
|
|
47
48
|
|
|
48
49
|
if (req.target && !req.target._unresolved && req.target._isSingleton) {
|
|
49
50
|
if (!allKeysAreProvided(req)) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
// REVISIT: There can also be renaming... we better use resolveView here.
|
|
52
|
+
const targetKeys = _targetKeys(req.target)
|
|
53
|
+
const readKeysCQN =
|
|
54
|
+
req.target.query && req.target.query.SELECT
|
|
55
|
+
? {
|
|
56
|
+
SELECT: Object.assign({ columns: targetKeys, one: true }, req.target.query.SELECT)
|
|
57
|
+
}
|
|
58
|
+
: SELECT.from(req.target).columns(targetKeys)
|
|
53
59
|
|
|
54
60
|
// REVISIT: avoid additional read
|
|
55
61
|
const current = await this._read(this.model, this.dbc, readKeysCQN, req)
|
|
56
62
|
_enrichKeysFromOldData(req, current)
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
if (!req.query.UPDATE.where) {
|
|
65
|
+
if (!req.query.UPDATE.where && req.target.keys) {
|
|
60
66
|
_addKeysToQuery(req)
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
if (onlyKeysRemain(req)) return
|
|
71
|
+
|
|
64
72
|
try {
|
|
65
73
|
const result = await this._update(this.model, this.dbc, req.query, req)
|
|
66
74
|
return result
|
|
@@ -1,80 +1,44 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* handler for
|
|
2
|
+
* handler for converting virtual fields to { val: null, as: 'myVirtualField' } by READ
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
6
|
+
|
|
7
|
+
const _convert = (columns, target, model) => {
|
|
8
|
+
if (!target) return
|
|
9
|
+
for (const col of columns) {
|
|
10
10
|
const element = col.ref && target.elements[col.ref[col.ref.length - 1]]
|
|
11
11
|
if (element) {
|
|
12
12
|
if (element.virtual) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
virtuals.push(element.name)
|
|
13
|
+
col.as = col.as || col.ref[col.ref.length - 1]
|
|
14
|
+
delete col.ref
|
|
15
|
+
col.val = (element.default && element.default.val) || null
|
|
18
16
|
}
|
|
19
17
|
if (col.expand && element.isAssociation) {
|
|
20
|
-
|
|
21
|
-
if (_virtuals.length) virtuals.push({ name: element.name, virtuals: _virtuals })
|
|
18
|
+
_convert(col.expand, model.definitions[element.target], model)
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
|
-
i++
|
|
25
21
|
}
|
|
26
|
-
return virtuals
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const _handleVirtuals = (req, model, remove) => {
|
|
24
|
+
const convertVirtuals = function (req, _model) {
|
|
25
|
+
const model = this.model || _model
|
|
32
26
|
// target.name ensures it is not a union or join
|
|
33
|
-
if (typeof req.query === 'string' || !req.target || typeof req.target.name !== 'string' || !model) return
|
|
34
|
-
const target = (!req.target._unresolved && req.target) || model.definitions[req.target.name
|
|
27
|
+
if (typeof req.query === 'string' || !req.target || typeof req.target.name !== 'string' || !model) return
|
|
28
|
+
const target = (!req.target._unresolved && req.target) || model.definitions[ensureNoDraftsSuffix(req.target.name)]
|
|
35
29
|
const columns = (req.query && req.query.SELECT && req.query.SELECT.columns) || []
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
for (const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const _handlerFn = (!remove && _find) || _filter
|
|
43
|
-
return _handlerFn(columns, target, model)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const getVirtuals = (req, model) => _handleVirtuals(req, model)
|
|
47
|
-
|
|
48
|
-
const removeVirtuals = function (req, model) {
|
|
49
|
-
return _handleVirtuals(req, this.model || model, true)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const postProcessVirtuals = (virtuals, result) => {
|
|
53
|
-
if (!result) return
|
|
54
|
-
result = result.value && Object.keys(result).filter(k => !k.match(/^\W/)).length === 1 ? result.value : result
|
|
55
|
-
const rows = Array.isArray(result) ? result : [result]
|
|
56
|
-
for (const virtual of virtuals) {
|
|
57
|
-
for (const row of rows) {
|
|
58
|
-
if (typeof row !== 'object') continue
|
|
59
|
-
if (virtual.name && row[virtual.name]) postProcessVirtuals(virtual.virtuals, row[virtual.name])
|
|
60
|
-
else if (!(virtual in row)) row[virtual] = null
|
|
30
|
+
_convert(columns, target, model)
|
|
31
|
+
if (req.query.SELECT.from && req.query.SELECT.from.SET) {
|
|
32
|
+
for (const arg of req.query.SELECT.from.SET.args) {
|
|
33
|
+
const target = model.definitions[ensureNoDraftsSuffix(arg._target.name)]
|
|
34
|
+
const columns = (arg.SELECT && arg.SELECT.columns) || []
|
|
35
|
+
_convert(columns, target, model, true)
|
|
61
36
|
}
|
|
62
37
|
}
|
|
63
38
|
}
|
|
64
39
|
|
|
65
|
-
|
|
66
|
-
const virtuals = []
|
|
67
|
-
for (const k in target.elements) {
|
|
68
|
-
if (target.elements[k].virtual) virtuals.push(k)
|
|
69
|
-
}
|
|
70
|
-
return virtuals
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
removeVirtuals._initial = true
|
|
40
|
+
convertVirtuals._initial = true
|
|
74
41
|
|
|
75
42
|
module.exports = {
|
|
76
|
-
|
|
77
|
-
getVirtualFromTarget,
|
|
78
|
-
postProcessVirtuals,
|
|
79
|
-
getVirtuals
|
|
43
|
+
convertVirtuals
|
|
80
44
|
}
|
|
@@ -3,7 +3,8 @@ const { timestampToISO } = require('../data-conversion/timestamp')
|
|
|
3
3
|
const { hasDeepDelete, getDeepDeleteCQNs } = require('../../common/composition')
|
|
4
4
|
|
|
5
5
|
const deleteFn = executeDeleteCQN => async (model, dbc, query, req) => {
|
|
6
|
-
const
|
|
6
|
+
const { user, locale, timestamp } = req
|
|
7
|
+
const isoTs = timestampToISO(timestamp)
|
|
7
8
|
|
|
8
9
|
let result
|
|
9
10
|
if (hasDeepDelete(model && model.definitions, query)) {
|
|
@@ -11,17 +12,19 @@ const deleteFn = executeDeleteCQN => async (model, dbc, query, req) => {
|
|
|
11
12
|
|
|
12
13
|
// the delete chunks, i.e., how many deletes can be processed in parallel
|
|
13
14
|
const chunks = []
|
|
14
|
-
for (const each of cqns)
|
|
15
|
+
for (const each of cqns) {
|
|
16
|
+
if (each.length) chunks.push(each.length)
|
|
17
|
+
}
|
|
15
18
|
|
|
16
19
|
cqns = getFlatArray(cqns)
|
|
17
20
|
|
|
18
21
|
if (cqns.length === 0) return 0
|
|
19
22
|
|
|
20
|
-
const results = await processCQNs(executeDeleteCQN, cqns, model, dbc,
|
|
23
|
+
const results = await processCQNs(executeDeleteCQN, cqns, model, dbc, user, locale, isoTs, chunks)
|
|
21
24
|
// return number of affected rows of "root cqn"
|
|
22
25
|
result = results[results.length - 1]
|
|
23
26
|
} else {
|
|
24
|
-
result = await executeDeleteCQN(model, dbc, query,
|
|
27
|
+
result = await executeDeleteCQN(model, dbc, query, user, locale, isoTs)
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
return result
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
const { hasDeepInsert, getDeepInsertCQNs } = require('../../common/composition')
|
|
1
|
+
const { hasDeepInsert, getDeepInsertCQNs, cleanEmptyCompositionsOfMany } = require('../../common/composition')
|
|
2
2
|
const { getFlatArray, processCQNs } = require('../utils/deep')
|
|
3
3
|
const { timestampToISO } = require('../data-conversion/timestamp')
|
|
4
4
|
|
|
5
5
|
const insert = executeInsertCQN => async (model, dbc, query, req) => {
|
|
6
|
-
const
|
|
6
|
+
const { user, locale, timestamp } = req
|
|
7
|
+
const isoTs = timestampToISO(timestamp)
|
|
7
8
|
|
|
8
9
|
if (hasDeepInsert(model && model.definitions, query)) {
|
|
9
10
|
const cqns = getFlatArray(getDeepInsertCQNs(model && model.definitions, query))
|
|
10
11
|
|
|
11
12
|
// return array of individual results
|
|
12
13
|
if (cqns.length === 0) return []
|
|
13
|
-
const results = await processCQNs(executeInsertCQN, cqns, model, dbc,
|
|
14
|
+
const results = await processCQNs(executeInsertCQN, cqns, model, dbc, user, locale, isoTs)
|
|
14
15
|
return getFlatArray(results)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
cleanEmptyCompositionsOfMany(model && model.definitions, query)
|
|
19
|
+
return executeInsertCQN(model, dbc, query, user, locale, isoTs)
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
module.exports = insert
|
|
@@ -21,32 +21,45 @@ function _createCountQuery(query) {
|
|
|
21
21
|
subCountQuery.SELECT.columns = [{ val: 1 }]
|
|
22
22
|
})
|
|
23
23
|
}
|
|
24
|
+
if (query.SELECT._4odata) _query.SELECT._4odata = true
|
|
24
25
|
return _query
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) => {
|
|
29
|
+
const { user, locale, timestamp } = req
|
|
30
|
+
const isoTs = timestampToISO(timestamp)
|
|
31
|
+
|
|
28
32
|
if (query._streaming) {
|
|
29
33
|
if (!query.SELECT || (query.SELECT && (!query.SELECT.columns || query.SELECT.columns.length !== 1))) {
|
|
30
34
|
req.reject(400)
|
|
31
35
|
}
|
|
32
|
-
return executeStreamCQN(model, dbc, query,
|
|
36
|
+
return executeStreamCQN(model, dbc, query, user, locale, isoTs)
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
// needed in case of expand
|
|
40
|
+
query._rootEntity = req.target
|
|
41
|
+
|
|
35
42
|
if (query.SELECT.count) {
|
|
36
43
|
if (query.SELECT.limit) {
|
|
37
44
|
const countQuery = _createCountQuery(query)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const countResultPromise = executeSelectCQN(model, dbc, countQuery, user, locale, isoTs)
|
|
46
|
+
if (query.SELECT.limit.rows && query.SELECT.limit.rows.val === 0) {
|
|
47
|
+
// We don't need to perform our result query
|
|
48
|
+
return countResultPromise.then(countResult => _arrayWithCount([], countResult[0]._counted_))
|
|
49
|
+
} else {
|
|
50
|
+
const resultPromise = executeSelectCQN(model, dbc, query, user, locale, isoTs)
|
|
51
|
+
return Promise.all([countResultPromise, resultPromise]).then(([countResult, result]) =>
|
|
52
|
+
_arrayWithCount(result, countResult[0]._counted_)
|
|
53
|
+
)
|
|
54
|
+
}
|
|
42
55
|
} else {
|
|
43
|
-
return executeSelectCQN(model, dbc, query,
|
|
44
|
-
|
|
56
|
+
return executeSelectCQN(model, dbc, query, user, locale, isoTs).then(result =>
|
|
57
|
+
_arrayWithCount(result, result.length)
|
|
45
58
|
)
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
return executeSelectCQN(model, dbc, query,
|
|
62
|
+
return executeSelectCQN(model, dbc, query, user, locale, isoTs)
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
module.exports = read
|
|
@@ -21,7 +21,10 @@ const run = (insert, read, update, deleet, cqn, sql) => (model, dbc, query, req,
|
|
|
21
21
|
return update(model, dbc, query, req)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const { user, locale, timestamp } = req
|
|
25
|
+
const isoTs = timestampToISO(timestamp)
|
|
26
|
+
|
|
27
|
+
return cqn(model, dbc, query, user, locale, isoTs)
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
module.exports = run
|
|
@@ -48,14 +48,15 @@ const _getFilteredCqns = (cqns, model) => {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const update = (executeUpdateCQN, executeSelectCQN) => async (model, dbc, query, req) => {
|
|
51
|
-
const
|
|
51
|
+
const { user, locale, timestamp } = req
|
|
52
|
+
const isoTs = timestampToISO(timestamp)
|
|
52
53
|
|
|
53
54
|
if (hasDeepUpdate(model && model.definitions, query)) {
|
|
54
55
|
// REVISIT: _activeData gets set in case of draftActivate for performance, but this is a layer violation
|
|
55
56
|
let selectData = req._ && req._.query && req._.query._activeData
|
|
56
57
|
if (!selectData) {
|
|
57
58
|
// REVISIT: avoid additional read
|
|
58
|
-
selectData = await selectDeepUpdateData(model && model.definitions, query, req)
|
|
59
|
+
selectData = await selectDeepUpdateData(model && model.definitions, query, req, false, false, cds.db)
|
|
59
60
|
} else {
|
|
60
61
|
selectData = [selectData]
|
|
61
62
|
}
|
|
@@ -70,14 +71,14 @@ const update = (executeUpdateCQN, executeSelectCQN) => async (model, dbc, query,
|
|
|
70
71
|
cqns = _getFilteredCqns(getFlatArray(cqns), model)
|
|
71
72
|
|
|
72
73
|
if (cqns.length === 0) return 0
|
|
73
|
-
const results = await processCQNs(executeUpdateCQN, cqns, model, dbc,
|
|
74
|
+
const results = await processCQNs(executeUpdateCQN, cqns, model, dbc, user, locale, isoTs, chunks)
|
|
74
75
|
// return number of affected rows of "root cqn", if an update, 1 otherwise (as not update of root but its children)
|
|
75
76
|
if (cqns[0].UPDATE) return results[0]
|
|
76
77
|
return 1
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
// REVISIT: don't invoke setters if not needed
|
|
80
|
-
return executeUpdateCQN(model, dbc, query,
|
|
81
|
+
return executeUpdateCQN(model, dbc, query, user, locale, isoTs)
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
module.exports = update
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
|
|
1
3
|
const BaseBuilder = require('./BaseBuilder')
|
|
2
4
|
const { flattenStructuredWhereHaving } = require('../../common/utils/structured')
|
|
3
5
|
|
|
6
|
+
const SQLITE_DATETIME_FUNCTIONS = new Set(['year', 'month', 'day', 'second', 'hour', 'minute'])
|
|
7
|
+
const OPERATORS = new Set(['=', '!=', '<>', '<', '>', '<=', '>='])
|
|
8
|
+
|
|
9
|
+
function _fillAfterDot(val) {
|
|
10
|
+
const [beforeDot, afterDot = ''] = val.split('.')
|
|
11
|
+
return `${beforeDot}.${afterDot.padEnd(3, '0')}`
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
/**
|
|
5
15
|
* ExpressionBuilder is used to take a part of a CQN object as an input and to build an object representing an expression
|
|
6
16
|
* with SQL string and values to be used with a prepared statement.
|
|
@@ -127,7 +137,30 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
127
137
|
* @returns {number}
|
|
128
138
|
* @private
|
|
129
139
|
*/
|
|
140
|
+
// eslint-disable-next-line complexity
|
|
130
141
|
_reseverdKeyWords(objects, i) {
|
|
142
|
+
if (objects[i] === 'not' && objects[i + 1].func) {
|
|
143
|
+
objects[i + 1].func = `not ${objects[i + 1].func}`
|
|
144
|
+
return 1
|
|
145
|
+
}
|
|
146
|
+
if (objects[i].func || (objects[i + 2] && objects[i + 2].func)) {
|
|
147
|
+
// sqlite requires leading 0 for numbers in datetime functions
|
|
148
|
+
const f = objects[i].func ? i : OPERATORS.has(objects[i + 1]) ? i + 2 : i - 2
|
|
149
|
+
const v = objects[i].val ? i : OPERATORS.has(objects[i + 1]) ? i + 2 : i - 2
|
|
150
|
+
if (objects[f] && SQLITE_DATETIME_FUNCTIONS.has(objects[f].func) && cds.db && cds.db.kind === 'sqlite') {
|
|
151
|
+
if (objects[v] && objects[v].val !== undefined && typeof objects[v].val === 'number') {
|
|
152
|
+
objects[v] = { val: `${objects[v].val < 10 ? 0 : ''}${objects[v].val}` }
|
|
153
|
+
if (objects[f].func === 'second') objects[v].val = _fillAfterDot(objects[v].val)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// odata indexof function returns the zero-based character position of the first occurrence
|
|
157
|
+
if (this._options._4odata && objects[i].func && objects[i].func === 'indexof') {
|
|
158
|
+
if (objects[i + 2] && objects[i + 2].val !== undefined) objects[i + 2].val++
|
|
159
|
+
else if (objects[i - 2] && objects[i - 2].val !== undefined) this._outputObj.sql[i - 2]++
|
|
160
|
+
}
|
|
161
|
+
return 0
|
|
162
|
+
}
|
|
163
|
+
|
|
131
164
|
if ((objects[i + 1] === '=' || objects[i + 1] === '!=') && objects[i + 2] && objects[i + 2].val === null) {
|
|
132
165
|
this._addNullOrNotNull(objects[i], objects[i + 1])
|
|
133
166
|
return 3
|
|
@@ -304,8 +337,8 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
304
337
|
|
|
305
338
|
_xprOutputFromElement(element) {
|
|
306
339
|
this._options.objectKey = 'xpr'
|
|
307
|
-
|
|
308
|
-
this._addToOutputObj(new
|
|
340
|
+
// new instance of subclass builder
|
|
341
|
+
this._addToOutputObj(new this.constructor(element, this._options, this._csn).build(), true)
|
|
309
342
|
}
|
|
310
343
|
|
|
311
344
|
/**
|
|
@@ -6,7 +6,8 @@ const cqn2sqlFunc = {
|
|
|
6
6
|
indexof: 'locate',
|
|
7
7
|
day: 'dayofmonth',
|
|
8
8
|
date: 'to_date',
|
|
9
|
-
time: 'to_time'
|
|
9
|
+
time: 'to_time',
|
|
10
|
+
average: 'avg'
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -66,6 +67,7 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
66
67
|
_handleFunction() {
|
|
67
68
|
const functionName = this._functionName()
|
|
68
69
|
const args = this._functionArgs()
|
|
70
|
+
|
|
69
71
|
if (!args) {
|
|
70
72
|
// > arg-less func such as current_date
|
|
71
73
|
this._outputObj.sql.push(functionName)
|
|
@@ -89,12 +91,24 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
89
91
|
return
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
if (functionName === 'countdistinct') {
|
|
95
|
+
this._handleCountdistinct(args)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
this._outputObj.sql.push(functionName, '(')
|
|
93
100
|
if (typeof args === 'string') this._outputObj.sql.push(args)
|
|
94
101
|
else this._addFunctionArgs(args)
|
|
95
102
|
this._outputObj.sql.push(')')
|
|
96
103
|
}
|
|
97
104
|
|
|
105
|
+
_handleCountdistinct(args) {
|
|
106
|
+
this._outputObj.sql.push('count', '(', 'DISTINCT')
|
|
107
|
+
if (typeof args === 'string') this._outputObj.sql.push(args)
|
|
108
|
+
else this._addFunctionArgs(args)
|
|
109
|
+
this._outputObj.sql.push(')')
|
|
110
|
+
}
|
|
111
|
+
|
|
98
112
|
_handleContains(args) {
|
|
99
113
|
this._handleLikewiseFunc(args)
|
|
100
114
|
}
|
|
@@ -165,7 +179,8 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
165
179
|
res.push(sql)
|
|
166
180
|
this._outputObj.values.push(...values)
|
|
167
181
|
} else if (arg.func) {
|
|
168
|
-
|
|
182
|
+
// new instance of subclass builder
|
|
183
|
+
const { sql, values } = new this.constructor(arg, this._options, this._csn).build()
|
|
169
184
|
res.push(sql)
|
|
170
185
|
this._outputObj.values.push(...values)
|
|
171
186
|
} else if (arg.xpr) {
|