@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.
Files changed (228) hide show
  1. package/CHANGELOG.md +239 -2
  2. package/apis/ql.d.ts +17 -15
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskEngine.js +26 -42
  5. package/bin/build/buildTaskFactory.js +6 -10
  6. package/bin/build/buildTaskHandler.js +2 -4
  7. package/bin/build/buildTaskProvider.js +3 -1
  8. package/bin/build/buildTaskProviderFactory.js +9 -15
  9. package/bin/build/constants.js +15 -3
  10. package/bin/build/index.js +5 -4
  11. package/bin/build/mtaUtil.js +8 -11
  12. package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
  13. package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
  14. package/bin/build/provider/buildTaskProviderInternal.js +16 -42
  15. package/bin/build/provider/fiori/index.js +13 -24
  16. package/bin/build/provider/hana/2migration.js +17 -15
  17. package/bin/build/provider/hana/2tabledata.js +52 -48
  18. package/bin/build/provider/hana/index.js +27 -25
  19. package/bin/build/provider/hana/migrationtable.js +91 -67
  20. package/bin/build/provider/java-cf/index.js +14 -24
  21. package/bin/build/provider/mtx/index.js +12 -14
  22. package/bin/build/provider/node-cf/index.js +18 -32
  23. package/bin/cds.js +5 -5
  24. package/bin/serve.js +29 -23
  25. package/bin/version.js +0 -1
  26. package/lib/compile/etc/_localized.js +4 -9
  27. package/lib/compile/for/sql.js +5 -2
  28. package/lib/compile/parse.js +25 -17
  29. package/lib/compile/to/srvinfo.js +2 -1
  30. package/lib/connect/bindings.js +2 -1
  31. package/lib/connect/index.js +48 -49
  32. package/lib/core/classes.js +1 -1
  33. package/lib/core/reflect.js +10 -2
  34. package/lib/deploy.js +26 -23
  35. package/lib/env/defaults.js +13 -6
  36. package/lib/env/index.js +73 -78
  37. package/lib/env/requires.js +38 -19
  38. package/lib/index.js +9 -10
  39. package/lib/lazy.js +2 -2
  40. package/lib/log/index.js +33 -45
  41. package/lib/log/service/index.js +2 -2
  42. package/lib/ql/CREATE.js +14 -9
  43. package/lib/ql/DELETE.js +6 -5
  44. package/lib/ql/DROP.js +12 -9
  45. package/lib/ql/INSERT.js +40 -16
  46. package/lib/ql/Query.js +67 -40
  47. package/lib/ql/SELECT.js +162 -127
  48. package/lib/ql/UPDATE.js +74 -42
  49. package/lib/ql/Whereable.js +77 -87
  50. package/lib/ql/index.js +36 -24
  51. package/lib/ql/parse.js +35 -0
  52. package/lib/req/context.js +44 -8
  53. package/lib/req/locale.js +7 -7
  54. package/lib/serve/Service-api.js +21 -14
  55. package/lib/serve/Service-dispatch.js +28 -12
  56. package/lib/serve/Transaction.js +22 -10
  57. package/lib/serve/index.js +16 -11
  58. package/lib/utils/axios.js +23 -16
  59. package/lib/utils/data.js +35 -0
  60. package/lib/utils/tests.js +27 -18
  61. package/libx/_runtime/audit/generic/personal/access.js +81 -0
  62. package/libx/_runtime/audit/generic/personal/constants.js +4 -0
  63. package/libx/_runtime/audit/generic/personal/index.js +50 -0
  64. package/libx/_runtime/audit/generic/personal/modification.js +138 -0
  65. package/libx/_runtime/audit/generic/personal/utils.js +186 -0
  66. package/libx/_runtime/audit/utils/v2.js +10 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
  68. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
  74. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
  75. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
  76. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
  78. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
  79. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
  80. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
  81. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
  85. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
  86. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
  87. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  89. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
  91. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
  99. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
  101. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
  102. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
  103. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
  104. package/libx/_runtime/cds-services/services/Service.js +40 -5
  105. package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
  106. package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
  107. package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
  108. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
  109. package/libx/_runtime/common/composition/data.js +66 -63
  110. package/libx/_runtime/common/composition/delete.js +97 -71
  111. package/libx/_runtime/common/composition/index.js +2 -1
  112. package/libx/_runtime/common/composition/insert.js +34 -11
  113. package/libx/_runtime/common/composition/tree.js +119 -92
  114. package/libx/_runtime/common/composition/update.js +12 -1
  115. package/libx/_runtime/common/composition/utils.js +1 -3
  116. package/libx/_runtime/common/constants/draft.js +12 -1
  117. package/libx/_runtime/common/generic/auth.js +53 -31
  118. package/libx/_runtime/common/generic/crud.js +14 -13
  119. package/libx/_runtime/common/generic/input.js +23 -26
  120. package/libx/_runtime/common/generic/put.js +1 -1
  121. package/libx/_runtime/common/generic/sorting.js +16 -16
  122. package/libx/_runtime/common/i18n/index.js +1 -1
  123. package/libx/_runtime/common/i18n/messages.properties +4 -0
  124. package/libx/_runtime/common/utils/backlinks.js +12 -5
  125. package/libx/_runtime/common/utils/cqn.js +6 -1
  126. package/libx/_runtime/common/utils/cqn2cqn4sql.js +123 -108
  127. package/libx/_runtime/common/utils/csn.js +56 -4
  128. package/libx/_runtime/common/utils/data.js +0 -37
  129. package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
  130. package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
  131. package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
  132. package/libx/_runtime/common/utils/generateOnCond.js +11 -12
  133. package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
  134. package/libx/_runtime/common/utils/path.js +35 -0
  135. package/libx/_runtime/common/utils/postProcessing.js +86 -0
  136. package/libx/_runtime/common/utils/quotingStyles.js +37 -26
  137. package/libx/_runtime/common/utils/resolveView.js +227 -173
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +13 -13
  140. package/libx/_runtime/common/utils/template.js +10 -5
  141. package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
  142. package/libx/_runtime/common/utils/templateProcessor.js +28 -72
  143. package/libx/_runtime/common/utils/union.js +31 -0
  144. package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
  145. package/libx/_runtime/db/Service.js +1 -1
  146. package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
  147. package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
  148. package/libx/_runtime/db/expand/index.js +3 -3
  149. package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
  150. package/libx/_runtime/db/generic/index.js +1 -1
  151. package/libx/_runtime/db/generic/input.js +5 -7
  152. package/libx/_runtime/db/generic/integrity.js +1 -1
  153. package/libx/_runtime/db/generic/rewrite.js +2 -10
  154. package/libx/_runtime/db/generic/update.js +13 -5
  155. package/libx/_runtime/db/generic/virtual.js +22 -58
  156. package/libx/_runtime/db/query/delete.js +7 -4
  157. package/libx/_runtime/db/query/insert.js +6 -4
  158. package/libx/_runtime/db/query/read.js +21 -8
  159. package/libx/_runtime/db/query/run.js +4 -1
  160. package/libx/_runtime/db/query/update.js +5 -4
  161. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
  162. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
  163. package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
  164. package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
  165. package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
  166. package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
  167. package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
  168. package/libx/_runtime/db/utils/deep.js +8 -0
  169. package/libx/_runtime/db/utils/generateAliases.js +2 -1
  170. package/libx/_runtime/fiori/generic/activate.js +19 -15
  171. package/libx/_runtime/fiori/generic/before.js +3 -11
  172. package/libx/_runtime/fiori/generic/cancel.js +1 -1
  173. package/libx/_runtime/fiori/generic/delete.js +3 -1
  174. package/libx/_runtime/fiori/generic/edit.js +12 -2
  175. package/libx/_runtime/fiori/generic/new.js +5 -5
  176. package/libx/_runtime/fiori/generic/patch.js +0 -18
  177. package/libx/_runtime/fiori/generic/read.js +261 -205
  178. package/libx/_runtime/fiori/utils/delete.js +36 -7
  179. package/libx/_runtime/fiori/utils/handler.js +43 -44
  180. package/libx/_runtime/fiori/utils/where.js +30 -15
  181. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
  182. package/libx/_runtime/hana/execute.js +3 -3
  183. package/libx/_runtime/hana/localized.js +4 -4
  184. package/libx/_runtime/hana/pool.js +29 -14
  185. package/libx/_runtime/hana/search2cqn4sql.js +2 -1
  186. package/libx/_runtime/hana/searchToContains.js +18 -14
  187. package/libx/_runtime/index.js +0 -5
  188. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
  189. package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
  190. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
  191. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  192. package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
  193. package/libx/_runtime/messaging/service.js +7 -6
  194. package/libx/_runtime/odata/cqn2odata.js +110 -43
  195. package/libx/_runtime/odata/index.js +26 -48
  196. package/libx/_runtime/odata/odata2cqn.js +1 -6154
  197. package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
  198. package/libx/_runtime/odata/readToCqn.js +94 -64
  199. package/libx/_runtime/remote/Service.js +74 -21
  200. package/libx/_runtime/remote/cqn2odata/index.js +1 -5
  201. package/libx/_runtime/remote/utils/client.js +24 -101
  202. package/libx/_runtime/remote/utils/dataConversion.js +27 -12
  203. package/libx/_runtime/sqlite/Service.js +3 -5
  204. package/libx/_runtime/sqlite/execute.js +33 -27
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +2 -2
  208. package/server.js +16 -2
  209. package/lib/ql/grammar.pegjs +0 -208
  210. package/lib/ql/parser.js +0 -1
  211. package/lib/ql/rt/DELETE.js +0 -29
  212. package/lib/ql/rt/INSERT.js +0 -23
  213. package/lib/ql/rt/Query.js +0 -84
  214. package/lib/ql/rt/SELECT.js +0 -174
  215. package/lib/ql/rt/UPDATE.js +0 -119
  216. package/lib/ql/rt/_helpers.js +0 -91
  217. package/lib/ql/rt/index.js +0 -32
  218. package/libx/_runtime/audit/generic/personal.js +0 -260
  219. package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
  220. package/libx/_runtime/cds-services/statements/Create.js +0 -57
  221. package/libx/_runtime/cds-services/statements/Delete.js +0 -33
  222. package/libx/_runtime/cds-services/statements/Drop.js +0 -42
  223. package/libx/_runtime/cds-services/statements/Insert.js +0 -201
  224. package/libx/_runtime/cds-services/statements/Select.js +0 -826
  225. package/libx/_runtime/cds-services/statements/Update.js +0 -181
  226. package/libx/_runtime/cds-services/statements/Where.js +0 -726
  227. package/libx/_runtime/cds-services/statements/index.js +0 -25
  228. 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