@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
@@ -0,0 +1,559 @@
1
+ /** ------------------------------------------
2
+ * This is a peg.js adaptation of the https://github.com/oasis-tcs/odata-abnf/blob/master/abnf/odata-abnf-construction-rules.txt
3
+ * which directly constructs CQN out of parsed sources.
4
+ *
5
+ * NOTE:
6
+ * In contrast to the OData ABNF source, which uses very detailed semantic rules,
7
+ * this adaptation uses rather generic syntactic rules only, e.g. NOT distinguishing
8
+ * between Collection Navigation or NOT knowing individual function names.
9
+ * This is to be open to future enhancements of the OData standard, as well as
10
+ * to improve error messages. For example a typo in a function name could be
11
+ * reported specifically instead of throwing a generic parser error.
12
+ *
13
+ * See also: https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview
14
+ * Future test cases http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/abnf/odata-abnf-testcases.xml
15
+ *
16
+ * Limitations: Type, Geo functions are NOT supported,
17
+ * maxdatetime, mindatetime, fractionalseconds,
18
+ * totaloffsetminutes, date, totalseconds,
19
+ * floor, ceiling also are NOT supported by CAP
20
+ *
21
+ * Examples:
22
+ * Books
23
+ * Books/201
24
+ * Books?$select=ID,title&$expand=author($select=name)&$filter=stock gt 1&$orderby=title
25
+ */
26
+
27
+ // ---------- JavaScript Helpers -------------
28
+ {
29
+ const $ = Object.assign
30
+ const { strict, minimal } = options
31
+ const stack=[]
32
+ let SELECT
33
+ const TECHNICAL_OPTS = ['$value'] // odata parts to be handled somewhere else
34
+
35
+ // we keep that here to allow for usage in https://pegjs.org/online
36
+ const safeNumber = options.safeNumber || function (str) {
37
+ const n = Number(str)
38
+ return Number.isSafeInteger(n) ? n : str
39
+ }
40
+
41
+ // check whether we have any
42
+ // - $select
43
+ // - aggregate(... as someProp)
44
+ const hasNOTAny$select = (columns) => {
45
+ return columns.filter(cur => cur.ref ? !cur.expand : cur.as).length === 0;
46
+ }
47
+
48
+ const adaptColumns = (SELECT) => {
49
+ const columns = SELECT.columns
50
+ if (!columns) return SELECT
51
+
52
+ // when $select and $expand was NOT defined
53
+ if (!hasNOTAny$select(columns)) {
54
+ const columnsMap = new Map()
55
+
56
+ columns.forEach(column => {
57
+
58
+ if (column === '*') {
59
+ columnsMap.set(column, column)
60
+ return
61
+ }
62
+
63
+ if (!column.ref) {
64
+ columnsMap.set(column.as, column)
65
+ return
66
+ }
67
+
68
+ const columnRefName = column.ref.join('/')
69
+
70
+ if (!columnsMap.has(columnRefName) || column.expand) {
71
+ columnsMap.set(columnRefName, column)
72
+ }
73
+ })
74
+
75
+ if (columnsMap.size > 0) {
76
+ SELECT.columns = Array.from(columnsMap.values())
77
+ }
78
+
79
+ return SELECT
80
+ }
81
+
82
+ if (columns.length === 1 && columns[0] === '*') {
83
+ delete SELECT.columns
84
+ return SELECT
85
+ }
86
+
87
+ // in case when have $expand but have NOT $select
88
+ if (!columns.includes('*')) {
89
+ SELECT.columns.unshift('*')
90
+ }
91
+
92
+ return SELECT
93
+ }
94
+
95
+ // NOTe: mutation of the object property, it's NOT a pure function
96
+ const correctAggAwayWhere = (where, colNames) => {
97
+ const changedWhere = [...where];
98
+
99
+ for (const item of changedWhere) {
100
+ if (item.xpr) {
101
+ item.xpr = correctAggAwayWhere(item.xpr, colNames)
102
+ }
103
+
104
+ if (item.args) {
105
+ item.args = correctAggAwayWhere(item.args, colNames)
106
+ }
107
+
108
+ // $filter ohne $apply -> input set = entity -> kein null setzen
109
+ // $apply mit filter transformation -> wie oben
110
+ // $apply mit filter transformation + $filter -> filter in where, $filter in having
111
+ // $apply mit filter transformation + groupby/aggregate/select + $filter -> filter in where, $filter in having
112
+
113
+ // TODO fix this for $apply
114
+ if(item.ref && !colNames.includes(item.ref.join(''))) {
115
+ // item.ref = null;
116
+ }
117
+ // REVISIT: { val:null } for should be also implemented
118
+ }
119
+
120
+ return changedWhere;
121
+ }
122
+
123
+ const correctAggAwayColumns = (SELECT) => {
124
+ const groupBy = SELECT.groupBy;
125
+ const where = SELECT.where;
126
+ const columns = SELECT.columns || [];
127
+ const aggregates = columns.filter((cur) => cur.as);
128
+
129
+ let fromAggregate = [];
130
+ let fromGroupBy = [];
131
+
132
+ // handle $apply=aggregate(... as someProp)&$select=someProp,?...
133
+ if (aggregates.length !== 0) {
134
+ fromAggregate = columns.filter((cur) =>
135
+ cur.ref ? aggregates.includes(cur.ref.join('')) : true
136
+ );
137
+ }
138
+
139
+ // handle $apply=groupby((someProp,?...))&$select=?...
140
+ if (groupBy) {
141
+ const allowedNames = groupBy.map(({ ref }) => ref.join(''));
142
+ const allowedColumns = columns.filter((cur) =>
143
+ cur.ref && allowedNames.includes(cur.ref.join(''))
144
+ );
145
+ fromGroupBy = allowedColumns.length === 0 ? [...groupBy] : allowedColumns;
146
+ }
147
+
148
+ const newColumns = fromAggregate.length !== 0 || fromGroupBy.length !== 0
149
+ ? [...fromGroupBy, ...fromAggregate]
150
+ : SELECT.columns;
151
+
152
+ let result = { ...SELECT, columns: newColumns }
153
+ let newWhere = [];
154
+
155
+ if (where && (groupBy || aggregates.length !== 0)) {
156
+ // changing { ref: null } for aggregated-away props
157
+ const colNames = columns.map((cur) => cur.ref && cur.ref.join('') || cur.as);
158
+ result = { ...result, where: correctAggAwayWhere(where, colNames) }
159
+ }
160
+
161
+ return result;
162
+ }
163
+ }
164
+
165
+ // ---------- Entity Paths ---------------
166
+
167
+ ODataRelativeURI // Note: case-sensitive!
168
+ = '/'? (p:path { SELECT = p })
169
+ ( o"?"o QueryOption ( o'&'o QueryOption )* )? o {
170
+ if (SELECT.columns) {
171
+ // columns set because of $count: ignore $select, $expand, $top, $skip, $orderby
172
+ // REVISIT: don't ignore query options but throw bad request (as okra did)?
173
+ delete SELECT.expand
174
+ delete SELECT.limit
175
+ delete SELECT.orderBy
176
+ return { SELECT }
177
+ }
178
+ if (SELECT.expand) {
179
+ SELECT.columns = SELECT.expand
180
+ delete SELECT.expand
181
+ }
182
+
183
+ SELECT = correctAggAwayColumns(SELECT)
184
+ SELECT = adaptColumns(SELECT)
185
+
186
+ // REVISIT: shouldn't be necessary
187
+ if (!SELECT.columns) delete SELECT.columns
188
+
189
+ return { SELECT }
190
+ }
191
+
192
+ path
193
+ = "$count" {return {columns:[{args: ["*"], as: "$count", func: "count"}]}}
194
+ / rv:$("$ref"/"$value") {return !TECHNICAL_OPTS.includes(rv) && {from: {ref: [rv]}}}
195
+ / head:(identifier/val) filter:(OPEN args CLOSE)? tail:( '/' p:path {return p} )? {
196
+ // minimal: val also as path segment
197
+ const ref = [ filter ? { id:head, where:filter[1] } : (minimal ? `${head.val || head}` : head) ]
198
+ if (tail && tail.from) {
199
+ const more = tail.from.ref, [{val}] = more
200
+ if (val) ref[ref.length-1] = { id:ref[ref.length-1], where:[more.shift()] }
201
+ ref.push (...more)
202
+ }
203
+
204
+ const res = {from: {ref}}
205
+ if (tail) res.columns = tail.columns
206
+ return res
207
+ }
208
+
209
+ args
210
+ = val:val {return [val]}
211
+ / ref:ref o"="o val:(val/w:word{return {val: w}}) more:( COMMA args )? {
212
+ const args = [ ref, '=', val ]
213
+ if (more) args.push ('and', ...more[1])
214
+ return args
215
+ }
216
+
217
+ //
218
+ // ---------- Query Options ------------
219
+
220
+ QueryOption = ExpandOption
221
+ ExpandOption =
222
+ "$select=" o select ( COMMA select )* /
223
+ "$expand=" o expand ( COMMA expand )* /
224
+ "$filter=" o filter /
225
+ "$orderby=" o orderby ( COMMA orderby )* /
226
+ "$top=" o top /
227
+ "$skip=" o skip /
228
+ "$search=" o search /
229
+ "$count=" o count /
230
+ "$apply=" o apply /
231
+ custom
232
+
233
+
234
+ select
235
+ = col:(
236
+ ref /
237
+ ('*' { return '*' })
238
+ ) {
239
+ SELECT.expand = SELECT.expand || []
240
+ SELECT.expand.push(col)
241
+ return col
242
+ }
243
+
244
+ expand =
245
+ ( c:select { c.expand='*' } )
246
+ ( // --- nested query options, if any
247
+ (OPEN {
248
+ stack.push (SELECT)
249
+ SELECT = SELECT.expand[SELECT.expand.length - 1]
250
+ SELECT.expand = ['*'] // by default expand everything
251
+ })(
252
+ expandOption:ExpandOption ( o";"o ExpandOption)*
253
+ {
254
+ // if there is a $select remove the '*' which is by default element[0]
255
+ if (expandOption[0] === '$select=') SELECT.expand.shift();
256
+ }
257
+ )(CLOSE {
258
+ SELECT = stack.pop()
259
+ })
260
+ )? // --- end of nested query options
261
+ ( COMMA expand )?
262
+
263
+ top
264
+ = val:integer {
265
+ (SELECT.limit || (SELECT.limit={})).rows = {val}
266
+ }
267
+
268
+ skip
269
+ = val:integer {
270
+ (SELECT.limit || (SELECT.limit={})).offset = {val}
271
+ }
272
+
273
+ search
274
+ = p:search_clause {SELECT.search = p}
275
+
276
+ search_clause
277
+ = p:( n:NOT? {return n?[n]:[]} )(
278
+ OPEN xpr:search_clause CLOSE {p.push({xpr})}
279
+ / (
280
+ val:doubleQuotedString {p.push({val})} /
281
+ val:string {p.push({val})} /
282
+ val:word {p.push({val})}
283
+ )
284
+ )( ao:(AND/OR/AND_SPACE) more:search_clause {p.push(ao,...more)} )*
285
+ {return p}
286
+
287
+ filter
288
+ = p:where_clause {SELECT.where = p}
289
+
290
+ where_clause = p:( n:NOT? {return n?[n]:[]} )(
291
+ OPEN xpr:where_clause CLOSE {p.push({xpr})}
292
+ / comp:comparison {p.push(...comp)}
293
+ / lambda:lambda {p.push(...lambda)}
294
+ / func:boolish {p.push(func)}
295
+ )( ao:(AND/OR) more:where_clause {p.push(ao,...more)} )*
296
+ {return p}
297
+
298
+ lambda =
299
+ nav:( n:identifier {return[n]} ) '/' ( n:identifier '/' {nav.push(n)} )*
300
+ xpr:(
301
+ any:any {
302
+ let id = nav.pop()
303
+ if (!any) return ['exists', { ref: [...nav, { id }] }]
304
+ let xpr = []
305
+ for (let i=0, k=0; i<any.length; ++i) {
306
+ let each = any[i]
307
+ if (each.ref && each.ref.length === 0 && any[i+1] === '=') {
308
+ xpr[k++] = { func:'contains', args:[{ref:id}, any[i+=2]] }
309
+ } else {
310
+ xpr[k++] = each
311
+ }
312
+ }
313
+
314
+ if (xpr.length < any.length) {
315
+ id = nav.pop()
316
+ return ['exists', { ref: [...nav, { id, where: xpr }] }]
317
+ } else {
318
+ return ['exists', { ref: [...nav, { id, where: any }] }]
319
+ }
320
+ }
321
+ / all:all {
322
+ let id = nav.pop()
323
+ return ['not', 'exists', { ref: [...nav, { id, where: ['not', { xpr: [...all] }] }] }]
324
+ }
325
+ )
326
+ { return xpr }
327
+
328
+ inner_lambda =
329
+ p:( n:NOT? { return n ? [n] : [] } )(
330
+ OPEN xpr:inner_lambda CLOSE { p.push('(', ...xpr, ')') }
331
+ / comp:comparison { p.push(...comp) }
332
+ / lambda:lambda { p.push(...lambda)}
333
+ )
334
+ ( ao:(AND/OR) more:inner_lambda { p.push(ao, ...more) } )*
335
+ { return p }
336
+
337
+ lambda_clause = prefix:identifier ":" inner:inner_lambda {
338
+ for (const e of inner) {
339
+ // remove the prefix identifier
340
+ if (e.ref && e.ref[0] === prefix) e.ref.shift()
341
+ }
342
+
343
+ return inner
344
+ }
345
+
346
+ any = "any" OPEN p:lambda_clause? CLOSE { return p }
347
+
348
+ all = "all" OPEN p:lambda_clause CLOSE { return p }
349
+
350
+ orderby
351
+ = ref:(function/ref) sort:( _ s:$("asc"/"desc") {return s})? {
352
+ const appendObj = $(ref, sort && {sort});
353
+ SELECT.orderBy = SELECT.orderBy ?
354
+ [...SELECT.orderBy, appendObj] :
355
+ [appendObj]
356
+ }
357
+
358
+ count
359
+ = val:bool { if(val) SELECT.count = true }
360
+
361
+ apply
362
+ = applyTrafo ("/" applyTrafo)*
363
+
364
+ custom = [a-zA-Z] [a-zA-Z0-9-]* "=" [^&]*
365
+
366
+ //
367
+ // ---------- Expressions ------------
368
+
369
+
370
+ comparison "a comparison"
371
+ = a:operand _ o:$("eq"/"ne"/"lt"/"gt"/"le"/"ge") _ b:operand {
372
+ const op = { eq:'=', ne:'!=', lt:'<', gt:'>', le:'<=', ge:'>=' }[o]||o
373
+ return [ a, op, b ]
374
+ }
375
+
376
+ mathCalc
377
+ = operand (_ ("add" / "sub" / "mul" / "div" / "mod") _ operand)*
378
+
379
+ operand "an operand"
380
+ = function / ref / val / jsonObject / jsonArray / list
381
+
382
+ ref "a reference"
383
+ = head:identifier tail:( '/' n:identifier {return n})*
384
+ {
385
+ if (head === "null") {
386
+ return { val: null }
387
+ }
388
+
389
+ return { ref:[ head, ...tail ] }
390
+ }
391
+
392
+ val
393
+ = val:(bool / date) {return {val}}
394
+ / guid
395
+ / val:number {return typeof val === 'number' ? {val} : { val, literal:'number' }}
396
+ / val:string {return {val}}
397
+
398
+ jsonObject = val:$("{" (jsonObject / [^}])* "}") {return {val}}
399
+
400
+ jsonArray = val:$("[" o "]" / "[" o "{" (jsonArray / [^\]])* "]") {return {val}}
401
+
402
+ list
403
+ = "[" any:$([^\]])* "]" // > needs improvment
404
+ { return { list: any.replace(/"/g,'').split(',').map(ele => ({ val: ele })) } }
405
+
406
+ function "a function call"
407
+ = func:$[a-z]+ OPEN a:operand more:( COMMA o:operand {return o} )* CLOSE {
408
+ if (strict && !(func in strict.functions)) error("'"+ func +"' is an unknown function in OData URL spec (strict mode)")
409
+ return { func, args:[a,...more] }
410
+ }
411
+
412
+ boolish "a boolean function"
413
+ = func:("contains"/"endswith"/"startswith") OPEN a:operand COMMA b:operand CLOSE
414
+ { return { func, args:[a,b] }}
415
+
416
+ NOT = o "NOT"i _ {return 'not'}
417
+ AND = _ "AND"i _ {return 'and'}
418
+ AND_SPACE = _ {return 'and'}
419
+ OR = _ "OR"i _ {return 'or'}
420
+
421
+
422
+ //
423
+ // ---------- Transformations ------------
424
+
425
+ applyTrafo
426
+ = (
427
+ "aggregate" aggregateTrafo /
428
+ "groupby" groupbyTrafo /
429
+ "filter" filterTrafo /
430
+ countTrafo /
431
+
432
+ // REVISIT: All transformations below need improvment
433
+ // and should supported by CAP
434
+ "expand" expandTrafo /
435
+ "search" searchTrafo /
436
+ "concat" concatTrafo /
437
+ "compute" computeTrafo /
438
+ "bottompercent" commonFuncTrafo /
439
+ "bottomsum" commonFuncTrafo /
440
+ "toppercent" commonFuncTrafo /
441
+ "topsum" commonFuncTrafo /
442
+ identityTrafo
443
+ // customFunction
444
+ )
445
+
446
+ aggregateTrafo
447
+ = OPEN o aggregateItem (o COMMA o aggregateItem)* o CLOSE
448
+ aggregateItem
449
+ = res:("$count" alias:asAlias { return { func: 'count', args: ['*'], as: alias } }
450
+ / aggregateExpr
451
+ ) { (SELECT.expand || (SELECT.expand = [])).push(res) }
452
+ aggregateExpr
453
+ = path:(
454
+ ref
455
+ // / mathCalc - needs CAP support
456
+ )
457
+ func:aggregateWith aggregateFrom? alias:asAlias
458
+ { return { func, args: [ path ], as: alias } }
459
+ / identifier OPEN aggregateExpr CLOSE // needs CAP support
460
+ // / customAggregate // needs CAP support
461
+ aggregateWith
462
+ = _ "with" _ func:$[a-z]+ { return func; }
463
+ aggregateFrom
464
+ = _ "from" _ ref aggregateWith aggregateFrom? // needs CAP support
465
+ asAlias
466
+ = _ "as" _ alias:identifier { return alias; }
467
+
468
+ groupbyTrafo
469
+ = OPEN o (OPEN groupByElem (COMMA o groupByElem)* CLOSE) (COMMA o apply)? o CLOSE
470
+ groupByElem
471
+ = val:(rollupSpec / ref)
472
+ { (SELECT.groupBy || (SELECT.groupBy = [])).push(val) }
473
+ rollupSpec // neeed CAP support
474
+ = "rollup" OPEN o ('$all' / ref) (o COMMA ref)+ o CLOSE
475
+
476
+ filterTrafo = OPEN o filter o CLOSE
477
+
478
+ countTrafo
479
+ = trafo:("topcount" / "bottomcount") OPEN o val:number o COMMA o ref:ref o CLOSE
480
+ {
481
+ const oredrObj = { ...ref, sort: trafo === 'topcount' ? 'desc' : 'asc' };
482
+ SELECT.orderBy = SELECT.orderBy ? [...SELECT.orderBy, oredrObj] : [oredrObj];
483
+ (SELECT.limit || (SELECT.limit={})).rows = {val};
484
+ }
485
+
486
+
487
+ // All transformations below need improvment
488
+ // and should supported by CAP
489
+ expandTrafo
490
+ = OPEN o ref o COMMA o
491
+ ( expandTrafo (o COMMA expandTrafo)*
492
+ / filterTrafo (o COMMA expandTrafo)*
493
+ ) o CLOSE
494
+
495
+ searchTrafo = OPEN o search o CLOSE
496
+
497
+ concatTrafo = OPEN o apply (o COMMA o apply)+ o CLOSE
498
+
499
+ computeTrafo = OPEN o computeExpr (o COMMA o computeExpr)* o CLOSE
500
+ computeExpr = where_clause asAlias
501
+
502
+ commonFuncTrafo = OPEN o operand o COMMA o operand o CLOSE
503
+
504
+ identityTrafo = "identity"
505
+
506
+
507
+ //
508
+ // ---------- Literals -----------
509
+
510
+ bool = b:("true" / "false") { return b === 'true'}
511
+
512
+ string "Edm.String"
513
+ = "'" s:$("''"/[^'])* "'"
514
+ {return s.replace(/''/g,"'")}
515
+
516
+ doubleQuotedString
517
+ = '"' s:$('\\"'/[^"])* '"'
518
+ {return s.replace(/\\\\/g,"\\").replace(/\\"/g,'"')}
519
+
520
+ word
521
+ = s:$([+"-"a-zA-Z0-9"."]+)
522
+
523
+ date
524
+ = s:$( [0-9]+"-"[0-9][0-9]"-"[0-9][0-9] // date
525
+ ("T"[0-9][0-9]":"[0-9][0-9](":"[0-9][0-9]("."[0-9]+)?)? // time
526
+ ( "Z" / (("+" / "-")[0-9][0-9]":"[0-9][0-9]) )? // timezone (Z or +-hh:mm)
527
+ )?)
528
+
529
+ number
530
+ = s:$( [+-]? [0-9]+ ("."[0-9]+)? ("e"[0-9]+)? )
531
+ {return safeNumber(s)}
532
+
533
+ integer
534
+ = s:$( [+-]? [0-9]+ )
535
+ {return parseInt(s)}
536
+
537
+ identifier
538
+ = !bool !guid s:$([_a-zA-Z][_a-zA-Z0-9"."]*) {return s}
539
+
540
+ guid = val:$([0-9a-zA-Z]+ "-" ([0-9a-zA-Z]+ "-"?)+)
541
+ {return {val}}
542
+
543
+ //
544
+ // ---------- Punctuation ----------
545
+
546
+ COLON = o":"o
547
+ COMMA = o","o
548
+ SEMI = o";"o
549
+ OPEN = o"("o
550
+ CLOSE = o")"
551
+
552
+ //
553
+ // ---------- Whitespaces -----------
554
+
555
+ o "optional whitespaces" = $[ \t\n]*
556
+ _ "mandatory whitespaces" = $[ \t\n]+
557
+
558
+ //
559
+ // ------------------------------------