@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,826 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const Where = require('./Where')
|
|
3
|
-
const { hasBeenCalledError, unexpectedFunctionCallError, invalidFunctionArgumentError } = require('../util/errors')
|
|
4
|
-
const { getEntityNameFromCQN } = require('../../common/utils/entityFromCqn')
|
|
5
|
-
|
|
6
|
-
const MODEL = Symbol.for('sap.cds.model')
|
|
7
|
-
const fnChain = Symbol.for('sap.cds.fnChain')
|
|
8
|
-
const addedColumns = Symbol.for('sap.cds.addedColumns')
|
|
9
|
-
const ON = Symbol.for('cqn.from.on')
|
|
10
|
-
|
|
11
|
-
const ORDERS = new Set(['asc', 'desc'])
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* SELECT statement selects values or rows from a specific table.
|
|
15
|
-
*
|
|
16
|
-
* @extends {Where}
|
|
17
|
-
*/
|
|
18
|
-
class Select extends Where {
|
|
19
|
-
constructor() {
|
|
20
|
-
super('SELECT')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Wrapper that allows usages like:
|
|
25
|
-
* SELECT(['column']).from('Table')
|
|
26
|
-
* SELECT('column').from('Table')
|
|
27
|
-
* SELECT.from('Table', ['column'])
|
|
28
|
-
*
|
|
29
|
-
* @returns {function(*): Select}
|
|
30
|
-
*/
|
|
31
|
-
static get select() {
|
|
32
|
-
const fn = (...args) => {
|
|
33
|
-
if (Array.isArray(args[0])) {
|
|
34
|
-
args = args[0]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
from: (entity, columns, model) => {
|
|
39
|
-
return Select.from(entity, columns && columns.length !== 0 ? args.concat(columns) : args, model)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
fn.from = Select.from
|
|
45
|
-
fn.one = Select.one
|
|
46
|
-
fn.distinct = Select.distinct
|
|
47
|
-
|
|
48
|
-
return fn
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Select distinct entries of an entity.
|
|
53
|
-
*
|
|
54
|
-
* @param {string|object} entity - entity name or an entity from reflection API or CQN
|
|
55
|
-
* @param {object} model the reflected CSN model
|
|
56
|
-
* @returns {Select} this object instance for chaining.
|
|
57
|
-
* @throws Error if no valid argument entity provided
|
|
58
|
-
*/
|
|
59
|
-
static distinct(entity, model) {
|
|
60
|
-
if (!entity) {
|
|
61
|
-
throw invalidFunctionArgumentError(this[fnChain])
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const cqn = new Select()
|
|
65
|
-
cqn[fnChain] = cqn[fnChain].concat('.distinct()')
|
|
66
|
-
if (model) {
|
|
67
|
-
Object.defineProperty(cqn, MODEL, { value: model })
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
cqn._parseEntity(entity)
|
|
71
|
-
return cqn.distinct()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Select entries of an entity.
|
|
76
|
-
*
|
|
77
|
-
* @param {string|object} entity - entity name or an entity from reflection API or CQN
|
|
78
|
-
* @param {Array} [columns] Can be either an array or an array in string notation that specifies a list of column names or a function
|
|
79
|
-
* @param {object} [model] the reflected CSN model
|
|
80
|
-
* @returns {Select} this object instance for chaining.
|
|
81
|
-
* @throws Error if no valid argument entity provided
|
|
82
|
-
*/
|
|
83
|
-
static from(entity, columns, model) {
|
|
84
|
-
if (!entity) {
|
|
85
|
-
throw invalidFunctionArgumentError('SELECT.from()')
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const cqn = new Select()
|
|
89
|
-
cqn[fnChain] = cqn[fnChain].concat('.from()')
|
|
90
|
-
|
|
91
|
-
if (model) Object.defineProperty(cqn, MODEL, { value: model })
|
|
92
|
-
cqn._parseEntity(entity)
|
|
93
|
-
cqn._parseColumns(columns, entity)
|
|
94
|
-
|
|
95
|
-
return cqn
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Select one entry of the entity. Can be used in combination with where() or byId().
|
|
100
|
-
*
|
|
101
|
-
* @param {string|object} entity - entity name or an entity from reflection API or CQN
|
|
102
|
-
* @param {Array} columns Can be either an array that specifies a list of column names or a function
|
|
103
|
-
* @returns {Select} this object instance for chaining.
|
|
104
|
-
*/
|
|
105
|
-
static one(entity, columns) {
|
|
106
|
-
return Select.from(entity, columns)._one()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
_one() {
|
|
110
|
-
this[fnChain] = this[fnChain].concat('.one()')
|
|
111
|
-
this.SELECT.one = true
|
|
112
|
-
return this
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
_checkHasDraftEntity(entity) {
|
|
116
|
-
const draftName = `${entity.name}_drafts`
|
|
117
|
-
const keys = Object.keys(entity.keys)
|
|
118
|
-
let subSelect = Select.from(draftName).columns([1])
|
|
119
|
-
subSelect = keys.reduce((select, key) => {
|
|
120
|
-
if (key !== 'IsActiveEntity') {
|
|
121
|
-
return subSelect.where([
|
|
122
|
-
{ ref: [entity.name, key] },
|
|
123
|
-
'=',
|
|
124
|
-
{
|
|
125
|
-
ref: [draftName, key]
|
|
126
|
-
}
|
|
127
|
-
])
|
|
128
|
-
}
|
|
129
|
-
return subSelect
|
|
130
|
-
}, subSelect)
|
|
131
|
-
return subSelect
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
_addSelectColumns(entity) {
|
|
135
|
-
if (!entity) {
|
|
136
|
-
entity = getEntityNameFromCQN(this.SELECT.from)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (entity && entity.elements) {
|
|
140
|
-
this[addedColumns] = true
|
|
141
|
-
const addAliasToColumns = []
|
|
142
|
-
for (const column in entity.elements) {
|
|
143
|
-
if (column === 'DraftAdministrativeData_DraftUUID') continue
|
|
144
|
-
const e = entity.elements[column]
|
|
145
|
-
if (e.isAssociation || e.virtual) continue
|
|
146
|
-
else if (entity._isDraftEnabled && column === 'IsActiveEntity') {
|
|
147
|
-
addAliasToColumns.push({ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } })
|
|
148
|
-
} else if (entity._isDraftEnabled && column === 'HasActiveEntity') {
|
|
149
|
-
addAliasToColumns.push({ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } })
|
|
150
|
-
} else if (entity._isDraftEnabled && column === 'HasDraftEntity') {
|
|
151
|
-
addAliasToColumns.push({
|
|
152
|
-
xpr: ['case', 'when', 'exists', this._checkHasDraftEntity(entity), 'then', 'true', 'else', 'false', 'end'],
|
|
153
|
-
as: 'HasDraftEntity',
|
|
154
|
-
cast: { type: 'cds.Boolean' }
|
|
155
|
-
})
|
|
156
|
-
} else addAliasToColumns.push({ [column]: column })
|
|
157
|
-
}
|
|
158
|
-
if (addAliasToColumns.length > 0) this._parseColumns(addAliasToColumns, entity)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
_parseColumnsArray(columns, entity) {
|
|
163
|
-
if (columns.length === 0) {
|
|
164
|
-
this._addSelectColumns(entity)
|
|
165
|
-
}
|
|
166
|
-
this._parseArray(columns)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
_parseColumns(columns, entity) {
|
|
170
|
-
if (columns) {
|
|
171
|
-
if (Array.isArray(columns)) {
|
|
172
|
-
this._parseColumnsArray(columns, entity)
|
|
173
|
-
} else if (typeof columns === 'object' && !this[MODEL]) {
|
|
174
|
-
Object.defineProperty(this, MODEL, { value: columns })
|
|
175
|
-
} else if (typeof columns === 'string' && columns.includes('{') && columns.includes('}')) {
|
|
176
|
-
this.SELECT.columns = []
|
|
177
|
-
if (columns.includes('.{')) {
|
|
178
|
-
columns = `{${columns.replace(/{/g, '').replace(/}/g, '')}}`
|
|
179
|
-
}
|
|
180
|
-
const res = cds.parse.cql(`SELECT from Entity ${columns.replace(/'/g, '')}`)
|
|
181
|
-
this._parseArray(res.SELECT.columns)
|
|
182
|
-
} else {
|
|
183
|
-
throw invalidFunctionArgumentError(this[fnChain], columns)
|
|
184
|
-
}
|
|
185
|
-
} else {
|
|
186
|
-
this._addSelectColumns(entity)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
_parseEntity(entity) {
|
|
191
|
-
const hasOwnProperty = Object.prototype.hasOwnProperty
|
|
192
|
-
|
|
193
|
-
if (typeof entity === 'string' && (entity.includes(':') || entity.includes('['))) {
|
|
194
|
-
this.SELECT.from = Where.convertPathExpression(entity)
|
|
195
|
-
} else if (typeof entity === 'string') {
|
|
196
|
-
this._parseString(entity)
|
|
197
|
-
} else if (
|
|
198
|
-
typeof entity === 'object' &&
|
|
199
|
-
hasOwnProperty.call(entity, 'params') &&
|
|
200
|
-
Object.keys(entity.params).length !== 0
|
|
201
|
-
) {
|
|
202
|
-
this._fromViewWithParams(entity)
|
|
203
|
-
} else if (typeof entity === 'object' && hasOwnProperty.call(entity, 'name')) {
|
|
204
|
-
this._from(entity.name)
|
|
205
|
-
} else if (
|
|
206
|
-
typeof entity === 'object' &&
|
|
207
|
-
(hasOwnProperty.call(entity, 'SELECT') || hasOwnProperty.call(entity, 'SET'))
|
|
208
|
-
) {
|
|
209
|
-
this._fromCQN(entity)
|
|
210
|
-
} else if (typeof entity === 'object') {
|
|
211
|
-
this._fromObject(entity)
|
|
212
|
-
} else {
|
|
213
|
-
throw invalidFunctionArgumentError(this[fnChain], entity)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
_fromObject(entity) {
|
|
218
|
-
const key = this._getKey(entity)
|
|
219
|
-
if (key === 'ref') {
|
|
220
|
-
// TODO: support further types of partial cqns like join.
|
|
221
|
-
this.SELECT.from = entity
|
|
222
|
-
} else {
|
|
223
|
-
this._from(entity[key], key)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Group result by column names
|
|
229
|
-
*
|
|
230
|
-
* @param {...string} columns - String representing a column name.
|
|
231
|
-
* @returns {Select} this object instance for chaining.
|
|
232
|
-
* @throws Error if invalid parameter columns provided
|
|
233
|
-
*/
|
|
234
|
-
groupBy(...columns) {
|
|
235
|
-
this[fnChain] = this[fnChain].concat('.groupBy()')
|
|
236
|
-
if (columns.length === 0) {
|
|
237
|
-
throw invalidFunctionArgumentError(this[fnChain])
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
columns.forEach(column => {
|
|
241
|
-
this._addColumnToGroupBy(column)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
return this
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Used to specify column names in SELECT
|
|
249
|
-
*
|
|
250
|
-
* @param {Array | string} columns Can be either a list of arguments, an array or a string for inline structure
|
|
251
|
-
* @returns {Select} this object instance for chaining.
|
|
252
|
-
* @throws Error if invalid parameter columns provided
|
|
253
|
-
*/
|
|
254
|
-
columns(columns) {
|
|
255
|
-
if (this[addedColumns]) {
|
|
256
|
-
this.SELECT.columns = []
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
this[fnChain] = this[fnChain].concat('.columns()')
|
|
260
|
-
if (columns) {
|
|
261
|
-
if (Array.isArray(columns) || (typeof columns === 'string' && columns.startsWith('{'))) {
|
|
262
|
-
this._parseColumns(columns)
|
|
263
|
-
} else {
|
|
264
|
-
this._parseColumns([...arguments])
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return this
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Orders result by column names
|
|
272
|
-
*
|
|
273
|
-
* @param {string|object} columnName - Column name or object describing the order
|
|
274
|
-
* @param {string} [order] - sorting order, defaults to asc
|
|
275
|
-
* @example
|
|
276
|
-
* orderBy('Author', 'asc')
|
|
277
|
-
* orderBy('Author asc')
|
|
278
|
-
* orderBy({Author: 'asc'})
|
|
279
|
-
* orderBy([{ref: ['Author'], sort: 'asc'}])
|
|
280
|
-
* orderBy(<any>, ...)
|
|
281
|
-
* orderBy([<any>, ...])
|
|
282
|
-
* @returns {Select} this object instance for chaining.
|
|
283
|
-
* @throws Error if no valid parameter columnName provided
|
|
284
|
-
*/
|
|
285
|
-
orderBy(columnName, order, ...args) {
|
|
286
|
-
if (typeof columnName === 'string' && columnName.match(/,/)) {
|
|
287
|
-
columnName = columnName.split(',').map(ele => ele.trim())
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (Array.isArray(columnName) && columnName.length > 1) {
|
|
291
|
-
for (const each of columnName) this.orderBy(each)
|
|
292
|
-
return this
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (args && args.length > 0) {
|
|
296
|
-
// > more than two args
|
|
297
|
-
this.orderBy(columnName)
|
|
298
|
-
this.orderBy(order)
|
|
299
|
-
for (const each of args) this.orderBy(each)
|
|
300
|
-
return this
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (order && !ORDERS.has(order)) {
|
|
304
|
-
// > second arg also an ordering
|
|
305
|
-
this.orderBy(columnName)
|
|
306
|
-
this.orderBy(order)
|
|
307
|
-
return this
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// cqn partial must be in array
|
|
311
|
-
if (columnName.ref) columnName = [columnName]
|
|
312
|
-
|
|
313
|
-
this[fnChain] = this[fnChain].concat('.orderBy()')
|
|
314
|
-
if (typeof columnName === 'string') {
|
|
315
|
-
if (columnName.match(/ /)) [columnName, order] = [...columnName.split(' ')]
|
|
316
|
-
this._addColumnToOrderBy(columnName, order)
|
|
317
|
-
} else if (Array.isArray(columnName)) {
|
|
318
|
-
// cqn partial
|
|
319
|
-
this.SELECT.orderBy = this.SELECT.orderBy || []
|
|
320
|
-
this.SELECT.orderBy.push(...columnName)
|
|
321
|
-
} else if (typeof columnName === 'object') {
|
|
322
|
-
this._addColumnToOrderByAsObject(columnName)
|
|
323
|
-
} else {
|
|
324
|
-
throw invalidFunctionArgumentError(this[fnChain], columnName)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return this
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Add inner join.
|
|
332
|
-
*
|
|
333
|
-
* @param tableName - Table name to be used for join.
|
|
334
|
-
* @param as
|
|
335
|
-
* @returns {Select} this object instance for chaining.
|
|
336
|
-
* @throws Error - If where or having was already called.
|
|
337
|
-
*/
|
|
338
|
-
join(tableName, as) {
|
|
339
|
-
this[fnChain] = this[fnChain].concat('.join()')
|
|
340
|
-
return this._join(tableName, as, 'inner')
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Add left join.
|
|
345
|
-
*
|
|
346
|
-
* @param tableName - Table name to be used for join.
|
|
347
|
-
* @param as
|
|
348
|
-
* @returns {Select} this object instance for chaining.
|
|
349
|
-
* @throws Error - If where or having was already called.
|
|
350
|
-
*/
|
|
351
|
-
leftJoin(tableName, as) {
|
|
352
|
-
this[fnChain] = this[fnChain].concat('.leftJoin()')
|
|
353
|
-
return this._join(tableName, as, 'left')
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Add right join.
|
|
358
|
-
*
|
|
359
|
-
* @param tableName - Table name to be used for join.
|
|
360
|
-
* @param as
|
|
361
|
-
* @returns {Select} this object instance for chaining.
|
|
362
|
-
* @throws Error - If where or having was already called.
|
|
363
|
-
*/
|
|
364
|
-
rightJoin(tableName, as) {
|
|
365
|
-
this[fnChain] = this[fnChain].concat('.rightJoin()')
|
|
366
|
-
return this._join(tableName, as, 'right')
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Add full join.
|
|
371
|
-
*
|
|
372
|
-
* @param tableName - Table name to be used for join.
|
|
373
|
-
* @param as
|
|
374
|
-
* @returns {Select} this object instance for chaining.
|
|
375
|
-
* @throws Error - If where or having was already called.
|
|
376
|
-
*/
|
|
377
|
-
fullJoin(tableName, as) {
|
|
378
|
-
this[fnChain] = this[fnChain].concat('.fullJoin()')
|
|
379
|
-
return this._join(tableName, as, 'full')
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* .on can only be used after .join has been called.
|
|
384
|
-
*
|
|
385
|
-
* @param {Array | object} arg1 Can be object predicate if argument is passed as an object. Or can be array of partial CQNs.
|
|
386
|
-
* @param {*} [args] Can be strings, values or objects. To be parsed as fluent expression.
|
|
387
|
-
* @returns {Select} this object instance for chaining.
|
|
388
|
-
* @throws Error - If called without calling join before.
|
|
389
|
-
*/
|
|
390
|
-
on(...args) {
|
|
391
|
-
this[fnChain] = this[fnChain].concat('.on()')
|
|
392
|
-
if (!Object.prototype.hasOwnProperty.call(this.cqn.from, 'join')) {
|
|
393
|
-
throw unexpectedFunctionCallError('.on()', '.join()')
|
|
394
|
-
}
|
|
395
|
-
this.cqn.from.on = []
|
|
396
|
-
// eslint-disable-next-line dot-notation
|
|
397
|
-
if (!this.cqn[ON]) {
|
|
398
|
-
const that = this
|
|
399
|
-
Object.defineProperty(this.cqn, ON, {
|
|
400
|
-
get: () => that.cqn.from.on
|
|
401
|
-
})
|
|
402
|
-
}
|
|
403
|
-
return this._condition(ON, ...args)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Add having. Can not be called before .where.
|
|
408
|
-
*
|
|
409
|
-
* Possible uses:
|
|
410
|
-
* having('ID', '<operator>', <value>)
|
|
411
|
-
* having('ID', 'between', <value>, <value>)
|
|
412
|
-
* having('Association.name', '<operator>', <value>)
|
|
413
|
-
* having('lower(column)', '<operator>', <value>)
|
|
414
|
-
* having(<object>)
|
|
415
|
-
* Fluid usage with alternating string value arguments
|
|
416
|
-
* having(arg1, arg2, arg3, ...)
|
|
417
|
-
* Array with partial CQNs
|
|
418
|
-
* having([arg1, arg2, arg3, ...])
|
|
419
|
-
*
|
|
420
|
-
* @param arg1
|
|
421
|
-
* @param arg2
|
|
422
|
-
* @param arg3
|
|
423
|
-
* @param arg4
|
|
424
|
-
* @example
|
|
425
|
-
* having('ID', '>', 7411)
|
|
426
|
-
* having({ ID: 7411})
|
|
427
|
-
* having({ or: [{ ID: 7411}, { ID: 2511}]})
|
|
428
|
-
* Fluid usage:
|
|
429
|
-
* having(`name like`, 'foo', `and ( ratio between`, 0.1, `and`, 0.3, `or ratio >`, 0.9, ')')
|
|
430
|
-
* Array with partial CQNs
|
|
431
|
-
* having([{ref: ['x']}, '=', {val: 1}])
|
|
432
|
-
*
|
|
433
|
-
* @returns {Select} this object instance for chaining.
|
|
434
|
-
* @throws Error - If called more than once or with no arguments or in the wrong context
|
|
435
|
-
*/
|
|
436
|
-
having(arg1, arg2, arg3, arg4) {
|
|
437
|
-
if (!arg1) {
|
|
438
|
-
throw invalidFunctionArgumentError(this[fnChain].concat('.having()'))
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const cqn = this.cqn
|
|
442
|
-
if (cqn.having) {
|
|
443
|
-
return this._andHaving(...arguments)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
this[fnChain] = this[fnChain].concat('.having()')
|
|
447
|
-
cqn.having = []
|
|
448
|
-
return this._having(...arguments)
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
_having(...args) {
|
|
452
|
-
return this._condition('having', ...args)
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
_andHaving(...args) {
|
|
456
|
-
return this._setAndOrBracket('and', 'having', ...args)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Add distinct.
|
|
461
|
-
*
|
|
462
|
-
* @returns {Select} this object instance for chaining.
|
|
463
|
-
* @throws Error - If called more than once
|
|
464
|
-
*/
|
|
465
|
-
distinct() {
|
|
466
|
-
this[fnChain] = this[fnChain].concat('.distinct()')
|
|
467
|
-
const cqn = this.cqn
|
|
468
|
-
if (cqn.distinct) {
|
|
469
|
-
throw hasBeenCalledError('distinct()', this[fnChain])
|
|
470
|
-
}
|
|
471
|
-
cqn.distinct = true
|
|
472
|
-
|
|
473
|
-
return this
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Locks the entity until the transaction is ended.
|
|
478
|
-
* Timeout will unlock entity in case select for update fails and entity is not unlocked on error.
|
|
479
|
-
*
|
|
480
|
-
* @param {object} [args={}]
|
|
481
|
-
* @param {Array} [args.of] Array of string values, that specifies the columns/tables that should be locked
|
|
482
|
-
* @param {number} [args.wait] An integer value, that specifies when to return an error if a lock can't be obtained on
|
|
483
|
-
* a record
|
|
484
|
-
* @returns {this}
|
|
485
|
-
*/
|
|
486
|
-
forUpdate({ of, wait } = {}) {
|
|
487
|
-
const cqn = this.cqn
|
|
488
|
-
cqn.forUpdate = {}
|
|
489
|
-
|
|
490
|
-
if (of) {
|
|
491
|
-
cqn.forUpdate.of = this._getSelectForUpdateOf(of)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (Number.isInteger(wait)) {
|
|
495
|
-
cqn.forUpdate.wait = wait
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return this
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Add limit.
|
|
503
|
-
*
|
|
504
|
-
* @param {number} rows - the number of records to return
|
|
505
|
-
* @param {number} [offset] - skip that many rows before beginning to return rows
|
|
506
|
-
* @returns {Select} this object instance for chaining.
|
|
507
|
-
* @throws Error - If called more than once or if invalid parameter rows provided
|
|
508
|
-
*/
|
|
509
|
-
limit(rows, offset) {
|
|
510
|
-
this[fnChain] = this[fnChain].concat('.limit()')
|
|
511
|
-
if (!rows && rows !== 0) {
|
|
512
|
-
throw invalidFunctionArgumentError(this[fnChain], rows)
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const cqn = this.cqn
|
|
516
|
-
|
|
517
|
-
if (cqn.limit) {
|
|
518
|
-
throw hasBeenCalledError('limit()', this[fnChain])
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
cqn.limit = { rows: { val: rows } }
|
|
522
|
-
if (offset) {
|
|
523
|
-
cqn.limit.offset = { val: offset }
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return this
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
_join(tableName, as, kind) {
|
|
530
|
-
if (!tableName) {
|
|
531
|
-
throw invalidFunctionArgumentError(this[fnChain])
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
if (this.cqn.where) {
|
|
535
|
-
throw unexpectedFunctionCallError('.where()', '.join()')
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (this.cqn.having) {
|
|
539
|
-
throw unexpectedFunctionCallError('.having()', '.join()')
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const parts = tableName.split(' ')
|
|
543
|
-
let ref
|
|
544
|
-
|
|
545
|
-
if (parts.length === 3 && parts[1].toUpperCase() === 'AS') {
|
|
546
|
-
ref = this._parseCastedElement(parts[0], parts[2])
|
|
547
|
-
} else {
|
|
548
|
-
ref = { ref: [parts[0]] }
|
|
549
|
-
|
|
550
|
-
if (as) {
|
|
551
|
-
ref.as = as
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (Object.prototype.hasOwnProperty.call(this.cqn.from, 'join')) {
|
|
556
|
-
this.cqn.from = { join: kind, args: [this.cqn.from, ref] }
|
|
557
|
-
return this
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const from = this.cqn.from
|
|
561
|
-
this.cqn.from = { join: kind, args: [from, ref] }
|
|
562
|
-
|
|
563
|
-
return this
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
_getSelectForUpdateOf(columns) {
|
|
567
|
-
this[fnChain] = this[fnChain].concat('.forUpdate()')
|
|
568
|
-
|
|
569
|
-
if (!Array.isArray(columns)) {
|
|
570
|
-
throw invalidFunctionArgumentError(this[fnChain], columns)
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const ofCQN = []
|
|
574
|
-
for (const column of columns) {
|
|
575
|
-
if (typeof column === 'string') {
|
|
576
|
-
ofCQN.push(column.includes('.') ? this._parseElementWithDot(column) : { ref: [column] })
|
|
577
|
-
} else {
|
|
578
|
-
throw invalidFunctionArgumentError(this[fnChain], columns)
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
return ofCQN
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
_parseString(entity) {
|
|
586
|
-
if (entity === '') {
|
|
587
|
-
throw invalidFunctionArgumentError(this[fnChain])
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
const parsed = this._parseStringElement(entity)
|
|
591
|
-
this._from(parsed.ref[0], parsed.as)
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
_from(entityName, asName) {
|
|
595
|
-
this.SELECT.from = { ref: [entityName] }
|
|
596
|
-
|
|
597
|
-
if (asName) {
|
|
598
|
-
this.SELECT.from.as = asName
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
_fromCQN(cqn) {
|
|
603
|
-
this.SELECT.from = cqn
|
|
604
|
-
|
|
605
|
-
if (cqn.SET && cqn.SET.as) {
|
|
606
|
-
this.SELECT.as = cqn.SET.as
|
|
607
|
-
delete cqn.SET.as
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
_fromViewWithParams(entity, asName) {
|
|
612
|
-
this.SELECT.from = { ref: [{ id: entity.name }] }
|
|
613
|
-
|
|
614
|
-
if (asName) {
|
|
615
|
-
this.SELECT.from.as = asName
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
_addColumnToOrderByAsObject(columnObject) {
|
|
620
|
-
this._extractKeysAndValuesFromObject(columnObject).forEach(item => {
|
|
621
|
-
this._addColumnToOrderBy(item.key, item.value)
|
|
622
|
-
})
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
_extractKeysAndValuesFromObject(object) {
|
|
626
|
-
const array = []
|
|
627
|
-
|
|
628
|
-
for (const key in object) {
|
|
629
|
-
array.push({ key: key, value: object[key] })
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
return array
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
_addColumnToOrderBy(column, order = 'asc') {
|
|
636
|
-
let res
|
|
637
|
-
|
|
638
|
-
if (this._isFunction(column)) {
|
|
639
|
-
res = this._parseFunction(column)
|
|
640
|
-
} else {
|
|
641
|
-
res = column.includes('.') ? this._parseElementWithDot(column) : { ref: [column] }
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
if (Object.prototype.hasOwnProperty.call(this.SELECT, 'orderBy')) {
|
|
645
|
-
this.SELECT.orderBy.push(res)
|
|
646
|
-
} else {
|
|
647
|
-
this.SELECT.orderBy = [res]
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
if (order === 'desc') {
|
|
651
|
-
this.SELECT.orderBy[this.SELECT.orderBy.length - 1].sort = 'desc'
|
|
652
|
-
} else if (order === 'asc') {
|
|
653
|
-
this.SELECT.orderBy[this.SELECT.orderBy.length - 1].sort = 'asc'
|
|
654
|
-
} else {
|
|
655
|
-
throw invalidFunctionArgumentError(this[fnChain], column)
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
_addColumnToGroupBy(column) {
|
|
660
|
-
const res =
|
|
661
|
-
typeof column === 'string' && column.includes('.') ? this._parseElementWithDot(column) : { ref: [column] }
|
|
662
|
-
|
|
663
|
-
if (Object.prototype.hasOwnProperty.call(this.SELECT, 'groupBy')) {
|
|
664
|
-
this.SELECT.groupBy.push(res)
|
|
665
|
-
} else {
|
|
666
|
-
this.SELECT.groupBy = [res]
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
_parseArray(columns) {
|
|
671
|
-
if (!this.SELECT.columns) {
|
|
672
|
-
this.SELECT.columns = []
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
for (let i = 0, length = columns.length; i < length; i++) {
|
|
676
|
-
if (typeof columns[i] === 'string' && Array.isArray(columns[i + 1])) {
|
|
677
|
-
this.SELECT.columns.push(this._parseElement({ [`expand(${columns[i]})`]: columns[i + 1] }))
|
|
678
|
-
i++
|
|
679
|
-
} else if (typeof columns[i] === 'string' && columns[i].includes('.')) {
|
|
680
|
-
this.SELECT.columns.push(this._parseElementWithDot(columns[i]))
|
|
681
|
-
} else {
|
|
682
|
-
this.SELECT.columns.push(this._parseElement(columns[i]))
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
_parseElement(element) {
|
|
688
|
-
if (typeof element === 'string') {
|
|
689
|
-
return this._parseStringElement(element)
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
if (typeof element === 'object') {
|
|
693
|
-
return this._parseObjectElement(element)
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
if (this._isNumber(element)) {
|
|
697
|
-
return this._parseNonCastedElement(element)
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
throw invalidFunctionArgumentError(this[fnChain], element)
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* @param element
|
|
705
|
-
* @example
|
|
706
|
-
* COUNT(1) AS count
|
|
707
|
-
* SUM(number) AS number
|
|
708
|
-
* COUNT(1)
|
|
709
|
-
* @private
|
|
710
|
-
* @returns {object}
|
|
711
|
-
*/
|
|
712
|
-
_parseStringElement(element) {
|
|
713
|
-
const parts = element.split(' ')
|
|
714
|
-
|
|
715
|
-
if (parts.length === 1) {
|
|
716
|
-
// cannot be zero
|
|
717
|
-
return this._parseNonCastedElement(element)
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
if (parts.length === 3 && parts[1].toUpperCase() === 'AS') {
|
|
721
|
-
return this._parseCastedElement(parts[0], parts[2])
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
throw invalidFunctionArgumentError(this[fnChain], element)
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* @param object
|
|
729
|
-
* @example
|
|
730
|
-
* {amount: 'COUNT(1)'}
|
|
731
|
-
* {amount: 'SUM(number)'}
|
|
732
|
-
* @private
|
|
733
|
-
* @returns {object}
|
|
734
|
-
*/
|
|
735
|
-
_parseObjectElement(object) {
|
|
736
|
-
const hasOwnProperty = Object.prototype.hasOwnProperty
|
|
737
|
-
// partial CQN: function calls and binding parameters are refs
|
|
738
|
-
if (
|
|
739
|
-
hasOwnProperty.call(object, 'val') ||
|
|
740
|
-
hasOwnProperty.call(object, 'func') ||
|
|
741
|
-
hasOwnProperty.call(object, 'ref') ||
|
|
742
|
-
hasOwnProperty.call(object, 'xpr') ||
|
|
743
|
-
hasOwnProperty.call(object, 'SELECT')
|
|
744
|
-
) {
|
|
745
|
-
return object
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
const key = this._getKey(object)
|
|
749
|
-
|
|
750
|
-
if (key.startsWith('expand(') && Array.isArray(object[key])) {
|
|
751
|
-
return this._parseExpand(key, object[key])
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
return this._parseCastedElement(key, object[key])
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
_getKey(object) {
|
|
758
|
-
const key = Object.keys(object)[0]
|
|
759
|
-
|
|
760
|
-
if (!key) {
|
|
761
|
-
throw invalidFunctionArgumentError(this[fnChain], object)
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
return key
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
_parseExpand(expr, elements) {
|
|
768
|
-
const ref = {
|
|
769
|
-
ref: [expr.replace(/(^expand\(|\)$)/g, '')]
|
|
770
|
-
}
|
|
771
|
-
ref.expand = []
|
|
772
|
-
|
|
773
|
-
for (const element of elements) {
|
|
774
|
-
ref.expand.push(this._parseElement(element))
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
return ref
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
_parseElementWithDot(columnString) {
|
|
781
|
-
const parsedColumn = {}
|
|
782
|
-
// split by ` as ` case insensitive and multiple whitespaces
|
|
783
|
-
const [column, alias] = columnString.split(/\s+as\s+/gi)
|
|
784
|
-
|
|
785
|
-
parsedColumn.ref = column.includes('.{') ? this._matchInline(column) : column.split(/\./)
|
|
786
|
-
|
|
787
|
-
if (alias) {
|
|
788
|
-
parsedColumn.as = alias
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
return parsedColumn
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
_parseCastedElement(element, castedName) {
|
|
795
|
-
const castedElement = this._parseNonCastedElement(element)
|
|
796
|
-
castedElement.as = castedName
|
|
797
|
-
return castedElement
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* @param column
|
|
802
|
-
* @example
|
|
803
|
-
* COUNT(1) => {func: 'COUNT', args: '1'}
|
|
804
|
-
* SUM(number) => {func: 'SUM', args: [{ref: ['number']}]}
|
|
805
|
-
*
|
|
806
|
-
* @private
|
|
807
|
-
* @returns {object}
|
|
808
|
-
*/
|
|
809
|
-
_parseNonCastedElement(column) {
|
|
810
|
-
if (this._isNumber(column)) {
|
|
811
|
-
return { val: column }
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
if (this._isFunction(column)) {
|
|
815
|
-
return this._parseFunction(column)
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return { ref: [column] }
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
valueOf() {
|
|
822
|
-
return `SELECT * FROM ${Select._quoteElement(this.SELECT.from.ref.join('.'))} `
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
module.exports = Select
|