@sap/cds 5.4.6 → 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 +208 -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 +44 -55
  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 +4 -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 +6 -22
  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 +102 -101
  127. package/libx/_runtime/common/utils/csn.js +47 -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 +223 -171
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +6 -12
  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 +22 -30
  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 +13 -20
  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 +241 -189
  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 +2 -2
  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 +23 -24
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +1 -1
  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
package/lib/ql/SELECT.js CHANGED
@@ -1,87 +1,120 @@
1
- const Whereable = require('./Whereable'), { parse, _where } = Whereable
2
- const $ = Object.assign
1
+ const Whereable = require('./Whereable'), { parse, predicate4 } = Whereable
2
+ const defaults = global.cds.env.sql
3
+
3
4
 
4
5
  module.exports = class SELECT extends Whereable {
5
6
 
6
- static get _() {
7
- return $((..._) => (new this).columns(..._), {
8
- from: (..._) => (new this).from(..._),
9
- distinct: $((...x) => $((new this).distinct.from(...x),
10
- { from: (..._) => (new this).distinct.from(..._).columns(...x) }
11
- ),{ from: (..._) => (new this).distinct.from(..._) }),
12
- one: $((...x) => $((new this).one.from(...x),
13
- { from: (..._) => (new this).one.from(..._).columns(...x) }
14
- ),{ from: (..._) => (new this).one.from(..._) }),
7
+ static _api() {
8
+ const $ = Object.assign
9
+ return $((..._) => new this()._select_or_from(..._), {
10
+ one: $((...x) => new this({one:true})._select_or_from(...x),{
11
+ columns: (..._) => new this({one:true}).columns(..._),
12
+ from: (..._) => new this({one:true}).from(..._),
13
+ }),
14
+ distinct: $((...x) => new this({distinct:true})._select_or_from(...x),{
15
+ columns: (..._) => new this({distinct:true}).columns(..._),
16
+ from: (..._) => new this({distinct:true}).from(..._),
17
+ }),
18
+ columns: (..._) => new this().columns(..._),
19
+ from: (..._) => new this().from(..._),
15
20
  })
16
21
  }
17
22
 
18
- get distinct() {
19
- this._.distinct = true
20
- return this
23
+ _select_or_from (cols, ...more) { // srv.read`title`.from`Books` or srv.read`Books` ?
24
+ if (!cols) return this
25
+ else if (is_number(cols)) return this.columns(...arguments) //> numbers can't be from
26
+ else if (cols.name) return this.from (...arguments) //> clearly a from
27
+ else if (cols.raw) { // tagged template string
28
+ if (cols[0].startsWith('from ')) { // SELECT`from ...`, with an arbitrary long CQL tail...
29
+ Object.assign (this.SELECT, SELECT_(' ',arguments))
30
+ return this
31
+ } else if (cols[0][0] === '{') { // SELECT`{a,b}`... -> it's columns
32
+ let {columns:c} = SELECT_('from X', arguments)
33
+ return this._add('columns',c)
34
+ } else { // SELECT`Foo` -> ambiguous -> try parsing as columns...
35
+ let {columns:c} = SELECT_('from X {', arguments, '}')
36
+ if (c.length > 1 || !c[0].ref) return this._add('columns',c)
37
+ // else cols = c[0] //> goes on below...
38
+ }
39
+ } else { // SELECT('Foo'|'*',[...]|(foo)=>{})
40
+ if (cols === '*') return this.columns(...arguments)
41
+ const c = _columns_or_not (cols)
42
+ if (c) return this._add('columns',c)
43
+ }
44
+
45
+ // return a proxy assuming it's a from and switching to
46
+ // columns on a subsequent call of .from, if any.
47
+ const {SELECT:_} = this, {one} = _
48
+ return Object.defineProperties (this.from (cols, ...more), {
49
+ from: { configurable:true, value:(...args) => { delete this.from
50
+ if (!one) delete _.one; delete _.columns; delete _.where
51
+ return this.from (...args) .columns (cols, ...more)
52
+ }}
53
+ })
21
54
  }
22
- get one() {
23
- this._.one = true
55
+
56
+ columns (...cols) {
57
+ if (cols[0]) this._add ('columns', _columns(cols))
24
58
  return this
25
59
  }
26
60
 
27
- from(entity, second, third) {
28
- this._.from = { ref: [this.target(entity).name] }
29
- if (is_projection(second)) this.columns(second)
30
- else if (second) {
31
- if (second) this.byKey(second)
61
+ from (target, second, third) {
62
+ this.SELECT.from = target === '*' || this._target_ref4 (...arguments)
63
+ if (!target.raw && second) {
64
+ const cols = _columns_or_not (second)
65
+ cols ? this._add('columns',cols) : this.byKey(second)
32
66
  if (third) this.columns(third)
33
67
  }
34
68
  return this
35
69
  }
36
70
 
37
- columns(...x) {
38
- const p = projection(x)
39
- this._.columns = this._.columns ? this._.columns.concat(p) : p
40
- return this
41
- }
42
-
43
- expand(ref, x, _kind = 'expand') {
44
- const cols = this._.columns || (this._.columns = [])
45
- cols.push({ ref: ref.split('.'), [_kind]: projection(x) })
46
- return this
71
+ fullJoin (other, as) { return this.join (other, as, 'full') }
72
+ leftJoin (other, as) { return this.join (other, as, 'left') }
73
+ rightJoin (other, as) { return this.join (other, as, 'right') }
74
+ innerJoin (other, as) { return this.join (other, as, 'inner') }
75
+ join (other, as, kind='inner') {
76
+ const [, target, alias = as] = /(\S+)(?:\s+(?:as)?\s+(\S+))?/i.exec(other)
77
+ const ref = { ref: [target] }
78
+ if (alias) ref.as = alias
79
+ this.SELECT.from = { join:kind, args: [this.SELECT.from, ref] }
80
+ return Object.defineProperty(this, '_where_or_having', { value: 'on', configurable: true })
47
81
  }
48
-
49
- inline(ref, cols) {
50
- return this.expand(ref, cols, 'inline')
82
+ on (...args) {
83
+ if (!this.SELECT.from || !this.SELECT.from.join)
84
+ throw new Error(`Invalid call of "SELECT.on()" without prior call of "SELECT.join()"`)
85
+ return this._where (args,'and','on')
51
86
  }
52
87
 
53
- excluding(...x) {
54
- this._.excluding = x
55
- return this
88
+ having(...x) {
89
+ return this._where (x,'and','having')
56
90
  }
57
91
 
58
- groupBy(...x) {
59
- this._.groupBy = x.map((x) => parse.expr(x))
60
- return this
92
+ groupBy (...args) {
93
+ if (!args[0]) return this
94
+ const cqn = args[0].raw ? SELECT_('from X group by', args).groupBy : args.map(parse.expr)
95
+ return this._add('groupBy',cqn)
61
96
  }
62
97
 
63
- having(...x) {
64
- this._.having = _where(x)
65
- return this
98
+ orderBy (...args) {
99
+ if (!args[0]) return this
100
+ return this._add('orderBy',_order_by(args))
66
101
  }
67
102
 
68
- orderBy(...x) {
69
- this._.orderBy = order_by(x)
103
+ limit (rows, offset) {
104
+ if (is_number(rows) || rows) this.SELECT.limit = rows.rows ? rows : { rows: {val:rows} }
105
+ if (is_number(offset)) this.SELECT.limit.offset = { val: offset }
70
106
  return this
71
107
  }
72
108
 
73
- limit(rows, offset) {
74
- const _ = this._.limit = { rows:{val:rows} }
75
- if (offset) _.offset = {val:offset}
109
+ forUpdate ({ of, wait = defaults.lock_acquire_timeout || -1 } = {}) {
110
+ const sfu = this.SELECT.forUpdate = {}
111
+ if (of) sfu.of = of.map (c => ({ref:c.split('.')}))
112
+ if (wait >= 0) sfu.wait = wait
76
113
  return this
77
114
  }
78
115
 
79
- redirectedTo(entity) {
80
- return this.clone().from(entity)
81
- }
82
-
83
- foreach(callback) {
84
- return this.then((rows) => rows.map(callback))
116
+ foreach (callback) {
117
+ return this.then(rows => rows.map(callback))
85
118
  }
86
119
 
87
120
  valueOf() {
@@ -89,89 +122,91 @@ module.exports = class SELECT extends Whereable {
89
122
  }
90
123
  }
91
124
 
92
- const is_projection = (x) => typeof x === 'function' || is_array(x)
93
- const column_expr = (x) => {
94
- if (x === '*' || x === undefined || x.xpr || x.ref || x.val || x.func || x.SELECT) return x
95
- if (typeof x === 'object') for (let one in x) {
96
- const r = parse.expr(one); r.as = x[one]
97
- return r
98
- }
99
- return typeof x === 'string' ? parse.column(x) : {val:x}
125
+
126
+ const _columns = (args) => {
127
+ if (args[0].raw) {
128
+ if (args[0][0] === '{') return SELECT_('from X ',args).columns
129
+ else return SELECT_('from X {',args,'}').columns
130
+ } else return _columns_or_not(args[0]) || args.map(_column_expr)
100
131
  }
101
132
 
102
- const projection = (x) => {
103
- if (is_projection(x[0])) x = x[0]
104
- if (!x || x === '*') return ['*']
105
- if (is_array(x)) return x.map(column_expr)
106
- if (typeof x === 'function') {
107
- const columns = []
108
- x(
109
- new Proxy(x, {
110
- // handle top-level projections or subselects such as
111
- // (foo)=>{ foo('*'), foo(SELECT...).as('bar') }
112
- apply: (_, __, [x]) => {
113
- columns.push(x)
114
- return { as: (alias) => (x.as = alias) }
115
- },
116
- // handle top-level paths like (foo)=>{ foo.bar }
133
+ const _columns_or_not = (x) => {
134
+ if (typeof x === 'function') return _projection4(x)
135
+ if (typeof x === 'string' && x[0] === '{') return parse.cql('SELECT from X '+ x).SELECT.columns
136
+ if (Array.isArray(x)) return x.map(_column_expr)
137
+ }
138
+
139
+ const _column_expr = (x) => {
140
+ if (is_cqn(x)) return x
141
+ if (typeof x === 'string') return parse.column(x)
142
+ if (typeof x === 'object') for (let one in x) return Object.assign(parse.expr(one),{as:x[one]})
143
+ else return {val:x}
144
+ }
145
+
146
+ const _projection4 = (fn) => {
147
+ const columns=[]; fn (new Proxy (fn,{
148
+ apply: (_, __, args) => { // handle top-level projections such as (foo)=>{ foo('*') }
149
+ if (!args.length) return columns.push('*')
150
+ let [x] = Array.isArray(args[0]) ? args[0] : args
151
+ columns.push (x === '*' || x === '.*' ? '*' : is_cqn(x) ? x : {ref:[x]})
152
+ return { as: (alias) => (x.as = alias) }
153
+ },
154
+ get: (_, p) => { // handle top-level paths like (foo)=>{ foo.bar }
155
+ const col = {ref:[p]}; columns.push(col)
156
+ const nested = new Proxy(fn,{ // handle n-fold paths like (foo)=>{ foo.bar.car }
117
157
  get: (_, p) => {
118
- const col = { ref: [p] }
119
- columns.push(col)
120
- const nested = new Proxy(x, {
121
- // handle n-fold paths like (foo)=>{ foo.bar.car }
122
- get: (_, p) => {
123
- if (p === 'where') return (x) => ((col.where = _where(x)), nested)
124
- if (p === 'as') return (alias) => ((col.as = alias), nested)
125
- else col.ref.push(p)
126
- return nested
127
- },
128
- // handle nested projections e.g. (foo)=>{ foo.bar (b=>{ ... }) }
129
- apply: (_, __, args) => {
130
- const [a, b] = args
131
- if (!a) col.expand = ['*']
132
- else if (a === '*') col.expand = ['*']
133
- else if (a === '.*') col.inline = ['*']
134
- else {
135
- let x = (col[/^\(?_\b/.test(a) ? 'inline' : 'expand'] = projection(args))
136
- if (b && b.levels)
137
- while (--b.levels) x.push({ ...col, expand: (x = [...x]) })
138
- }
139
- return nested
140
- },
141
- })
158
+ if (p === 'where') return (x) => ((col.where = predicate4([x])), nested)
159
+ if (p === 'as') return (alias) => ((col.as = alias), nested)
160
+ else return col.ref.push(p), nested
161
+ },
162
+ apply: (_, __, args) => { // handle nested projections e.g. (foo)=>{ foo.bar (b=>{ ... }) }
163
+ const [a, b] = args
164
+ if (!a) col.expand = ['*']
165
+ else if (a.raw) {
166
+ if (a[0] === '*') col.expand = ['*']
167
+ else if (a[0] === '.*') col.inline = ['*']
168
+ else {
169
+ let {columns} = SELECT_(col.ref[col.ref.length-1] +' ', args, ' from X')
170
+ Object.assign (col, columns[0])
171
+ }
172
+ }
173
+ else if (Array.isArray(a)) col.expand = _columns(a)
174
+ else if (a === '*') col.expand = ['*']
175
+ else if (a === '.*') col.inline = ['*']
176
+ else {
177
+ let x = (col[/^\(?_\b/.test(a) ? 'inline' : 'expand'] = _projection4(a))
178
+ if (b && b.levels) while (--b.levels) x.push({ ...col, expand: (x = [...x]) })
179
+ }
142
180
  return nested
143
181
  },
144
182
  })
145
- )
146
- return columns
147
- }
183
+ return nested
184
+ },
185
+ }))
186
+ return columns
148
187
  }
149
188
 
150
- const order_by = (args) => (is_object(args) ? _order_by_object : _order_by_fluid)(args)
151
-
152
- // e.g. .orderBy ({ x:'asc', y:'desc', y:-1 })
153
- const _order_by_object = ([arg]) => {
154
- const all = []
155
- for (let each in arg) {
156
- const order = parse.expr(each)
157
- all.push(order)
158
- const ad = arg[each]
159
- order[ad === 1 ? 'asc' : ad === -1 ? 'desc' : ad] = true
189
+ const _order_by = (args) => {
190
+ if (args[0].raw) return SELECT_('from X order by', args).orderBy
191
+ if (Array.isArray(args[0])) args = args[0]
192
+ const cqn=[], _add = (ref,ad) => {
193
+ const obx = parse.expr(ref); cqn.push(obx)
194
+ if (ad) obx.sort = ad == 1 ? 'asc' : ad == -1 ? 'desc' : ad
160
195
  }
161
- return all
162
- }
163
-
164
- // e.g. .orderBy ('x asc','y desc')
165
- const _order_by_fluid = (args) => {
166
- const all = []
167
196
  for (let each of args) {
168
- const order = parse.expr(each)
169
- all.push(order)
170
- if (each.endsWith(' asc')) order.asc = true
171
- if (each.endsWith(' desc')) order.desc = true
197
+ if (each.ref) cqn.push(each)
198
+ else if (typeof each === 'string') _add (...each.split(' '))
199
+ else for (let ref in each) _add (ref, each[ref])
172
200
  }
173
- return all
201
+ return cqn
202
+ }
203
+
204
+ const {CQL} = parse, SELECT_ = (prefix, [ strings, ...more ], suffix) => {
205
+ const tts = [...strings]; tts.raw = true
206
+ if (prefix) tts[0] = `SELECT ${prefix} ${tts[0]}`
207
+ if (suffix) tts[tts.length-1] += ` ${suffix}`
208
+ return CQL(tts,...more).SELECT
174
209
  }
175
210
 
176
- const is_object = (args) => args.length === 1 && typeof args[0] === 'object'
177
- const is_array = Array.isArray
211
+ const is_cqn = x => x === undefined || x === '*' || x.val !== undefined || x.xpr || x.ref || x.list || x.func || x.SELECT
212
+ const is_number = x => !isNaN(x)
package/lib/ql/UPDATE.js CHANGED
@@ -1,65 +1,97 @@
1
- const Whereable = require('./Whereable'), {parse} = Whereable
2
- const is_object = (args) => args.length === 1 && typeof args[0] === 'object'
3
- const is_array = Array.isArray
4
- const $ = Object.assign
1
+ const Whereable = require('./Whereable')
2
+ const { parse } = require('../index')
5
3
 
6
4
  module.exports = class UPDATE extends Whereable {
7
- static get _() {
8
- return $((..._) => new this().entity(..._), {
9
- entity: (..._) => new this().entity(..._),
5
+
6
+ static _api() {
7
+ return Object.assign ((..._) => (new this).entity(..._), {
8
+ entity: (..._) => (new this).entity(..._),
10
9
  })
11
10
  }
12
11
 
13
- entity(e, key) {
14
- this._.entity = this.target(e).name
12
+ entity (e, key) {
13
+ this.UPDATE.entity = this._target_name4 (e)
15
14
  if (key) this.byKey(key)
16
15
  return this
17
16
  }
18
17
 
19
- with(...args) {
20
- this._.with = (is_object(args) ? _with_object : _with_fluid)(args)
18
+ data (d) {
19
+ this.UPDATE.data = d
21
20
  return this
22
21
  }
23
22
 
24
- data(data) {
25
- this._.data = data
26
- return this
23
+ set (...args) { // .set() is an alias for .with()
24
+ return this.with (...args)
27
25
  }
28
26
 
29
- get _set() {
30
- //> .set is an alias to .with
31
- return this.with
32
- }
27
+ with (...args) {
28
+ if (args.length === 0) return this
29
+
30
+ // A tagged template string with a single expression, e.g. .with `my.stock -= 1`
31
+ if (args[0].raw) {
32
+ let [lhs,op,...rhs] = parse.CXL(...args).xpr || this._expected `${{args}} to contain expressions of form 'column = <expr>'`
33
+ _add (this, lhs.ref.join('.'), op, ...rhs)
34
+ }
35
+
36
+ // Alternating expr fragment / values args, e.g. .with ('my.stock -=',1, 'lastOrder =', '$now')
37
+ else if (args.length > 1) {
38
+ for (let i = 0; i < args.length; ++i) {
39
+ const [, col, op] = /\s*([\w.]+)\s*([%*+-]?=)/.exec(args[i])
40
+ _add (this, col, op, { val: args[++i] })
41
+ }
42
+ }
43
+
44
+ // A single string with comma-separated expressions, e.g. .with ('my.stock -= 1, lastOrder = $now')
45
+ else if (typeof args[0] === 'string') {
46
+ for (let each of _comma_separated_exprs(args[0])) {
47
+ let [lhs,op,...rhs] = parse.expr(each).xpr || this._expected `${{args}} to contain expressions of form 'column = <expr>'`
48
+ _add (this, lhs.ref.join('.'), op, ...rhs)
49
+ }
50
+ }
33
51
 
34
- valueOf() {
35
- return super.valueOf('UPDATE')
52
+ // A column - value / expr object, e.g. .with ({ stock:{'-=':1}, lastOrder:{'=':'$now'} })
53
+ else if (typeof args[0] === 'object') {
54
+ const o = args[0]
55
+ for (let col in o) {
56
+ let op = '=', v = o[col]
57
+ if (typeof v === 'object' && !(v === null || v.map || v.pipe || v instanceof Buffer || v instanceof Date)) {
58
+ let o = Object.keys(v)[0] || this._expected `${{v}} to be an object with an operator as single key`
59
+ if (o in operators) v = v[op=o]
60
+ }
61
+ _add (this, col, op, v && (v.val !== undefined || v.ref || v.xpr || v.func || v.SELECT) ? v : {val:v})
62
+ }
63
+ }
64
+
65
+ return this
36
66
  }
37
67
  }
38
68
 
39
- // .with ({ stock:{'-=':1}, lastOrder:{'=':'$now'} })
40
- const _with_object = ([arg]) => {
41
- const _set = {}
42
- for (let lhs in arg) {
43
- let rhs = arg[lhs]
44
- if (typeof rhs === 'object' && !is_array(rhs))
45
- for (let op in rhs) _set[lhs] = _value(lhs, op, rhs[op])
46
- else _set[lhs] = _value(lhs, '=', rhs)
47
- }
48
- return _set
69
+
70
+ const _add = (q, col, op, ...xpr) => {
71
+ const {UPDATE} = q, v =
72
+ op === '=' ? xpr.length === 1 ? xpr[0] : { xpr } :
73
+ op in operators ? { xpr: [ {ref:[col]}, op[0], ...xpr] } :
74
+ q._expected `${{op}} to be one of ${Object.keys(operators)}`
75
+ if ('val' in v) (UPDATE.data || (UPDATE.data={}))[col] = v.val
76
+ else (UPDATE.with || (UPDATE.with={}))[col] = v
49
77
  }
50
78
 
51
- // .with ('my.stock -=',1, 'lastOrder =', '$now')
52
- const _with_fluid = (args) => {
53
- const _set = {}
54
- for (let i = 0; i < args.length; ++i) {
55
- const [, lhs, op] = /\s*([\w.]+)\s*(=|-=|\+=)/i.exec(args[i])
56
- _set[lhs] = _value(lhs, op, args[++i])
79
+ const _comma_separated_exprs = (s) => {
80
+ let all=[], start=0, scope=0, close, stack = [close]
81
+ for (let i=0; i < s.length; ++i) {
82
+ const c = s[i]
83
+ if (c === "'") while (i < s.length) { if (s[++i] === "'") {
84
+ if (s[i+1] === "'") ++i // double '' is a quoted '
85
+ else break
86
+ }}
87
+ else if (c === ',' && scope === 0) { all.push(s.slice(start,i)); start = i+1 }
88
+ else if (c === '(') { scope++; stack.unshift(close = ')') }
89
+ else if (c === '[') { scope++; stack.unshift(close = ']') }
90
+ else if (c === '{') { scope++; stack.unshift(close = '}') }
91
+ else if (c === close) { scope--; stack.shift(); close = stack[0] }
57
92
  }
58
- return _set
93
+ all.push(s.slice(start))
94
+ return all
59
95
  }
60
96
 
61
- const _value = (lhs, op, rhs) => {
62
- if (op === '-=') return { xpr: [parse.expr(lhs), '-', parse.expr(rhs)] }
63
- if (op === '+=') return { xpr: [parse.expr(lhs), '+', parse.expr(rhs)] }
64
- else return parse.expr(rhs) || {val:rhs}
65
- }
97
+ const operators = { '=':1, '-=':2, '+=':2, '*=':2, '/=':2, '%=':2 }