@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.
- package/CHANGELOG.md +208 -2
- package/apis/ql.d.ts +17 -15
- package/app/index.js +1 -1
- package/bin/build/buildTaskEngine.js +26 -42
- package/bin/build/buildTaskFactory.js +6 -10
- package/bin/build/buildTaskHandler.js +2 -4
- package/bin/build/buildTaskProvider.js +3 -1
- package/bin/build/buildTaskProviderFactory.js +9 -15
- package/bin/build/constants.js +15 -3
- package/bin/build/index.js +5 -4
- package/bin/build/mtaUtil.js +8 -11
- package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
- package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
- package/bin/build/provider/buildTaskProviderInternal.js +16 -42
- package/bin/build/provider/fiori/index.js +13 -24
- package/bin/build/provider/hana/2migration.js +17 -15
- package/bin/build/provider/hana/2tabledata.js +52 -48
- package/bin/build/provider/hana/index.js +27 -25
- package/bin/build/provider/hana/migrationtable.js +91 -67
- package/bin/build/provider/java-cf/index.js +14 -24
- package/bin/build/provider/mtx/index.js +12 -14
- package/bin/build/provider/node-cf/index.js +18 -32
- package/bin/cds.js +5 -5
- package/bin/serve.js +29 -23
- package/bin/version.js +0 -1
- package/lib/compile/etc/_localized.js +4 -9
- package/lib/compile/for/sql.js +5 -2
- package/lib/compile/parse.js +25 -17
- package/lib/compile/to/srvinfo.js +2 -1
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +48 -49
- package/lib/core/classes.js +1 -1
- package/lib/core/reflect.js +10 -2
- package/lib/deploy.js +26 -23
- package/lib/env/defaults.js +13 -6
- package/lib/env/index.js +73 -78
- package/lib/env/requires.js +38 -19
- package/lib/index.js +9 -10
- package/lib/lazy.js +2 -2
- package/lib/log/index.js +33 -45
- package/lib/log/service/index.js +2 -2
- package/lib/ql/CREATE.js +14 -9
- package/lib/ql/DELETE.js +6 -5
- package/lib/ql/DROP.js +12 -9
- package/lib/ql/INSERT.js +40 -16
- package/lib/ql/Query.js +67 -40
- package/lib/ql/SELECT.js +162 -127
- package/lib/ql/UPDATE.js +74 -42
- package/lib/ql/Whereable.js +77 -87
- package/lib/ql/index.js +36 -24
- package/lib/ql/parse.js +35 -0
- package/lib/req/context.js +44 -8
- package/lib/req/locale.js +7 -7
- package/lib/serve/Service-api.js +21 -14
- package/lib/serve/Service-dispatch.js +28 -12
- package/lib/serve/Transaction.js +22 -10
- package/lib/serve/index.js +16 -11
- package/lib/utils/axios.js +23 -16
- package/lib/utils/data.js +35 -0
- package/lib/utils/tests.js +27 -18
- package/libx/_runtime/audit/generic/personal/access.js +81 -0
- package/libx/_runtime/audit/generic/personal/constants.js +4 -0
- package/libx/_runtime/audit/generic/personal/index.js +50 -0
- package/libx/_runtime/audit/generic/personal/modification.js +138 -0
- package/libx/_runtime/audit/generic/personal/utils.js +186 -0
- package/libx/_runtime/audit/utils/v2.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
- package/libx/_runtime/cds-services/services/Service.js +40 -5
- package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
- package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
- package/libx/_runtime/common/composition/data.js +44 -55
- package/libx/_runtime/common/composition/delete.js +97 -71
- package/libx/_runtime/common/composition/index.js +2 -1
- package/libx/_runtime/common/composition/insert.js +34 -11
- package/libx/_runtime/common/composition/tree.js +119 -92
- package/libx/_runtime/common/composition/update.js +4 -1
- package/libx/_runtime/common/composition/utils.js +1 -3
- package/libx/_runtime/common/constants/draft.js +12 -1
- package/libx/_runtime/common/generic/auth.js +6 -22
- package/libx/_runtime/common/generic/crud.js +14 -13
- package/libx/_runtime/common/generic/input.js +23 -26
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +16 -16
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +4 -0
- package/libx/_runtime/common/utils/backlinks.js +12 -5
- package/libx/_runtime/common/utils/cqn.js +6 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
- package/libx/_runtime/common/utils/csn.js +47 -4
- package/libx/_runtime/common/utils/data.js +0 -37
- package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
- package/libx/_runtime/common/utils/generateOnCond.js +11 -12
- package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
- package/libx/_runtime/common/utils/path.js +35 -0
- package/libx/_runtime/common/utils/postProcessing.js +86 -0
- package/libx/_runtime/common/utils/quotingStyles.js +37 -26
- package/libx/_runtime/common/utils/resolveView.js +223 -171
- package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
- package/libx/_runtime/common/utils/structured.js +6 -12
- package/libx/_runtime/common/utils/template.js +10 -5
- package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
- package/libx/_runtime/common/utils/templateProcessor.js +22 -30
- package/libx/_runtime/common/utils/union.js +31 -0
- package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
- package/libx/_runtime/db/Service.js +1 -1
- package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
- package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
- package/libx/_runtime/db/expand/index.js +3 -3
- package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
- package/libx/_runtime/db/generic/index.js +1 -1
- package/libx/_runtime/db/generic/input.js +5 -7
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/generic/rewrite.js +2 -10
- package/libx/_runtime/db/generic/update.js +13 -5
- package/libx/_runtime/db/generic/virtual.js +22 -58
- package/libx/_runtime/db/query/delete.js +7 -4
- package/libx/_runtime/db/query/insert.js +6 -4
- package/libx/_runtime/db/query/read.js +13 -20
- package/libx/_runtime/db/query/run.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -4
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
- package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
- package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
- package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
- package/libx/_runtime/db/utils/deep.js +8 -0
- package/libx/_runtime/db/utils/generateAliases.js +2 -1
- package/libx/_runtime/fiori/generic/activate.js +19 -15
- package/libx/_runtime/fiori/generic/before.js +3 -11
- package/libx/_runtime/fiori/generic/cancel.js +1 -1
- package/libx/_runtime/fiori/generic/delete.js +3 -1
- package/libx/_runtime/fiori/generic/edit.js +12 -2
- package/libx/_runtime/fiori/generic/new.js +5 -5
- package/libx/_runtime/fiori/generic/patch.js +0 -18
- package/libx/_runtime/fiori/generic/read.js +241 -189
- package/libx/_runtime/fiori/utils/delete.js +36 -7
- package/libx/_runtime/fiori/utils/handler.js +43 -44
- package/libx/_runtime/fiori/utils/where.js +30 -15
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
- package/libx/_runtime/hana/execute.js +2 -2
- package/libx/_runtime/hana/localized.js +4 -4
- package/libx/_runtime/hana/pool.js +29 -14
- package/libx/_runtime/hana/search2cqn4sql.js +2 -1
- package/libx/_runtime/hana/searchToContains.js +18 -14
- package/libx/_runtime/index.js +0 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
- package/libx/_runtime/messaging/service.js +7 -6
- package/libx/_runtime/odata/cqn2odata.js +110 -43
- package/libx/_runtime/odata/index.js +26 -48
- package/libx/_runtime/odata/odata2cqn.js +1 -6154
- package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
- package/libx/_runtime/odata/readToCqn.js +94 -64
- package/libx/_runtime/remote/Service.js +74 -21
- package/libx/_runtime/remote/cqn2odata/index.js +1 -5
- package/libx/_runtime/remote/utils/client.js +24 -101
- package/libx/_runtime/remote/utils/dataConversion.js +27 -12
- package/libx/_runtime/sqlite/Service.js +3 -5
- package/libx/_runtime/sqlite/execute.js +23 -24
- package/libx/_runtime/sqlite/localized.js +12 -7
- package/libx/_runtime/types/api.js +10 -0
- package/package.json +1 -1
- package/server.js +16 -2
- package/lib/ql/grammar.pegjs +0 -208
- package/lib/ql/parser.js +0 -1
- package/lib/ql/rt/DELETE.js +0 -29
- package/lib/ql/rt/INSERT.js +0 -23
- package/lib/ql/rt/Query.js +0 -84
- package/lib/ql/rt/SELECT.js +0 -174
- package/lib/ql/rt/UPDATE.js +0 -119
- package/lib/ql/rt/_helpers.js +0 -91
- package/lib/ql/rt/index.js +0 -32
- package/libx/_runtime/audit/generic/personal.js +0 -260
- package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
- package/libx/_runtime/cds-services/statements/Create.js +0 -57
- package/libx/_runtime/cds-services/statements/Delete.js +0 -33
- package/libx/_runtime/cds-services/statements/Drop.js +0 -42
- package/libx/_runtime/cds-services/statements/Insert.js +0 -201
- package/libx/_runtime/cds-services/statements/Select.js +0 -826
- package/libx/_runtime/cds-services/statements/Update.js +0 -181
- package/libx/_runtime/cds-services/statements/Where.js +0 -726
- package/libx/_runtime/cds-services/statements/index.js +0 -25
- package/libx/_runtime/common/generic/resolve-mock.js +0 -9
|
@@ -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
|
+
// ------------------------------------
|