@oino-ts/db 0.3.2 → 0.3.4
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/dist/cjs/OINODb.js +3 -3
- package/dist/cjs/OINODbSqlParams.js +6 -5
- package/dist/cjs/index.js +3 -1
- package/dist/esm/OINODb.js +3 -3
- package/dist/esm/OINODbSqlParams.js +6 -5
- package/dist/esm/index.js +1 -1
- package/dist/types/OINODbSqlParams.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/package.json +36 -36
- package/src/OINODb.ts +291 -291
- package/src/OINODbApi.test.ts +411 -411
- package/src/OINODbApi.ts +384 -384
- package/src/OINODbConfig.ts +95 -95
- package/src/OINODbDataField.ts +382 -382
- package/src/OINODbDataModel.ts +289 -289
- package/src/OINODbFactory.ts +124 -124
- package/src/OINODbModelSet.ts +298 -298
- package/src/OINODbParser.ts +457 -457
- package/src/OINODbSqlParams.ts +438 -437
- package/src/OINODbSwagger.ts +208 -208
- package/src/index.ts +130 -130
- package/README.md +0 -190
package/src/OINODbSqlParams.ts
CHANGED
|
@@ -1,437 +1,438 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
-
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { OINOStr, OINODbDataField, OINODbDataModel, OINO_ERROR_PREFIX, OINOLog } from "./index.js"
|
|
8
|
-
|
|
9
|
-
const OINO_FIELD_NAME_CHARS:string = "\\w\\s\\-\\_\\#\\¤"
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Supported logical conjunctions in filter predicates.
|
|
13
|
-
* @enum
|
|
14
|
-
*/
|
|
15
|
-
export enum OINODbSqlBooleanOperation { and = "and", or = "or", not = "not" }
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Supported logical conjunctions in filter predicates.
|
|
19
|
-
* @enum
|
|
20
|
-
*/
|
|
21
|
-
export enum OINODbSqlComparison { lt = "lt", le = "le", eq = "eq", ge = "ge", gt = "gt", like = "like" }
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Class for recursively parsing of filters and printing them as SQL conditions.
|
|
25
|
-
* Supports three types of statements
|
|
26
|
-
* - comparison: (field)-lt|le|eq|ge|gt|like(value)
|
|
27
|
-
* - negation: -not(filter)
|
|
28
|
-
* - conjunction/disjunction: (filter)-and|or(filter)
|
|
29
|
-
* Supported conditions are comparisons (<, <=, =, >=, >) and substring match (LIKE).
|
|
30
|
-
*
|
|
31
|
-
*/
|
|
32
|
-
export class OINODbSqlFilter {
|
|
33
|
-
private static _booleanOperationRegex = /^\s?\-(and|or)\s?$/i
|
|
34
|
-
private static _negationRegex = /^-(not|)\((.+)\)$/i
|
|
35
|
-
private static _filterComparisonRegex = /^\(([^'"\(\)]+)\)\s?\-(lt|le|eq|ge|gt|like)\s?\(([^'"\(\)]+)\)$/i
|
|
36
|
-
|
|
37
|
-
private _leftSide: OINODbSqlFilter | string
|
|
38
|
-
private _rightSide: OINODbSqlFilter | string
|
|
39
|
-
private _operator:OINODbSqlComparison|OINODbSqlBooleanOperation|null
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Constructor of `OINODbSqlFilter`
|
|
43
|
-
* @param leftSide left side of the filter, either another filter or a column name
|
|
44
|
-
* @param operation operation of the filter, either `OINODbSqlComparison` or `OINODbSqlBooleanOperation`
|
|
45
|
-
* @param rightSide right side of the filter, either another filter or a value
|
|
46
|
-
*/
|
|
47
|
-
constructor(leftSide:OINODbSqlFilter|string, operation:OINODbSqlComparison|OINODbSqlBooleanOperation|null, rightSide:OINODbSqlFilter|string) {
|
|
48
|
-
if (!(
|
|
49
|
-
((operation === null) && (leftSide == "") && (rightSide == "")) ||
|
|
50
|
-
((operation !== null) && (Object.values(OINODbSqlComparison).includes(operation as OINODbSqlComparison)) && (typeof(leftSide) == "string") && (leftSide != "") && (typeof(rightSide) == "string") && (rightSide != "")) ||
|
|
51
|
-
((operation == OINODbSqlBooleanOperation.not) && (leftSide == "") && (rightSide instanceof OINODbSqlFilter)) ||
|
|
52
|
-
(((operation == OINODbSqlBooleanOperation.and) || (operation == OINODbSqlBooleanOperation.or)) && (leftSide instanceof OINODbSqlFilter) && (rightSide instanceof OINODbSqlFilter))
|
|
53
|
-
)) {
|
|
54
|
-
OINOLog.debug("Unsupported OINODbSqlFilter format!", {leftSide:leftSide, operation:operation, rightSide:rightSide})
|
|
55
|
-
throw new Error(OINO_ERROR_PREFIX + ": Unsupported OINODbSqlFilter format!")
|
|
56
|
-
}
|
|
57
|
-
this._leftSide = leftSide
|
|
58
|
-
this._operator = operation
|
|
59
|
-
this._rightSide = rightSide
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Constructor for `OINODbSqlFilter` as parser of http parameter.
|
|
64
|
-
*
|
|
65
|
-
* @param filterString string representation of filter from HTTP-request
|
|
66
|
-
*
|
|
67
|
-
*/
|
|
68
|
-
static parse(filterString: string):OINODbSqlFilter {
|
|
69
|
-
// OINOLog_debug("OINODbSqlFilter.constructor", {filterString:filterString})
|
|
70
|
-
if (!filterString) {
|
|
71
|
-
return new OINODbSqlFilter("", null, "")
|
|
72
|
-
|
|
73
|
-
} else {
|
|
74
|
-
let match = OINODbSqlFilter._filterComparisonRegex.exec(filterString)
|
|
75
|
-
if (match != null) {
|
|
76
|
-
return new OINODbSqlFilter(match[1], match[2].toLowerCase() as OINODbSqlComparison, match[3])
|
|
77
|
-
} else {
|
|
78
|
-
let match = OINODbSqlFilter._negationRegex.exec(filterString)
|
|
79
|
-
if (match != null) {
|
|
80
|
-
return new OINODbSqlFilter("", OINODbSqlBooleanOperation.not, OINODbSqlFilter.parse(match[3]))
|
|
81
|
-
} else {
|
|
82
|
-
let boolean_parts = OINOStr.splitByBrackets(filterString, true, false, '(', ')')
|
|
83
|
-
// OINOLog_debug("OINODbSqlFilter.constructor", {boolean_parts:boolean_parts})
|
|
84
|
-
if (boolean_parts.length == 3 && (boolean_parts[1].match(OINODbSqlFilter._booleanOperationRegex))) {
|
|
85
|
-
return new OINODbSqlFilter(OINODbSqlFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1) as OINODbSqlBooleanOperation, OINODbSqlFilter.parse(boolean_parts[2]))
|
|
86
|
-
|
|
87
|
-
} else {
|
|
88
|
-
throw new Error(OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'") // invalid filter could be a security risk, stop processing
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Construct a new `OINODbSqlFilter` as combination of (boolean and/or) of two filters.
|
|
97
|
-
*
|
|
98
|
-
* @param leftSide left side to combine
|
|
99
|
-
* @param operation boolean operation to use in combination
|
|
100
|
-
* @param rightSide right side to combine
|
|
101
|
-
*
|
|
102
|
-
*/
|
|
103
|
-
static combine(leftSide:OINODbSqlFilter|undefined, operation:OINODbSqlBooleanOperation, rightSide:OINODbSqlFilter|undefined):OINODbSqlFilter|undefined {
|
|
104
|
-
if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
|
|
105
|
-
return new OINODbSqlFilter(leftSide, operation, rightSide)
|
|
106
|
-
|
|
107
|
-
} else if ((leftSide) && (!leftSide.isEmpty())) {
|
|
108
|
-
return leftSide
|
|
109
|
-
|
|
110
|
-
} else if ((rightSide) && (!rightSide.isEmpty())) {
|
|
111
|
-
return rightSide
|
|
112
|
-
|
|
113
|
-
} else {
|
|
114
|
-
return undefined
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
private _operatorToSql():string {
|
|
120
|
-
switch (this._operator) {
|
|
121
|
-
case "and": return " AND "
|
|
122
|
-
case "or": return " OR "
|
|
123
|
-
case "not": return "NOT "
|
|
124
|
-
case "lt": return " < "
|
|
125
|
-
case "le": return " <= "
|
|
126
|
-
case "eq": return " = "
|
|
127
|
-
case "ge": return " >= "
|
|
128
|
-
case "gt": return " > "
|
|
129
|
-
case "like": return " LIKE "
|
|
130
|
-
}
|
|
131
|
-
return " "
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Does filter contain any valid conditions.
|
|
136
|
-
*
|
|
137
|
-
*/
|
|
138
|
-
isEmpty():boolean {
|
|
139
|
-
return (this._leftSide == "") && (this._operator == null) && (this._rightSide == "")
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Print filter as SQL condition based on the datamodel of the API.
|
|
144
|
-
*
|
|
145
|
-
* @param dataModel data model (and database) to use for formatting of values
|
|
146
|
-
*
|
|
147
|
-
*/
|
|
148
|
-
toSql(dataModel:OINODbDataModel):string {
|
|
149
|
-
// OINOLog.debug("OINODbSqlFilter.toSql", {_leftSide:this._leftSide, _operator:this._operator, _rightSide:this._rightSide})
|
|
150
|
-
if (this.isEmpty()) {
|
|
151
|
-
return ""
|
|
152
|
-
}
|
|
153
|
-
let result:string = ""
|
|
154
|
-
let field:OINODbDataField|null = null
|
|
155
|
-
if (this._leftSide instanceof OINODbSqlFilter) {
|
|
156
|
-
result += this._leftSide.toSql(dataModel)
|
|
157
|
-
} else {
|
|
158
|
-
field = dataModel.findFieldByName(this._leftSide)
|
|
159
|
-
if (!field) {
|
|
160
|
-
OINOLog.error("OINODbSqlFilter.toSql: Invalid field!", {field:this._leftSide})
|
|
161
|
-
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid field '" + this._leftSide + "'") // invalid field name could be a security risk, stop processing
|
|
162
|
-
}
|
|
163
|
-
result += dataModel.api.db.printSqlColumnname(field?.name || this._leftSide)
|
|
164
|
-
}
|
|
165
|
-
result += this._operatorToSql()
|
|
166
|
-
if (this._rightSide instanceof OINODbSqlFilter) {
|
|
167
|
-
result += this._rightSide.toSql(dataModel)
|
|
168
|
-
} else {
|
|
169
|
-
const value = field!.deserializeCell(this._rightSide)
|
|
170
|
-
if (!value) {
|
|
171
|
-
OINOLog.error("OINODbSqlFilter.toSql: Invalid value!", {value:value})
|
|
172
|
-
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid value '" + value + "'") // invalid value could be a security risk, stop processing
|
|
173
|
-
}
|
|
174
|
-
result += field!.printCellAsSqlValue(value)
|
|
175
|
-
}
|
|
176
|
-
// OINOLog.debug("OINODbSqlFilter.toSql", {result:result})
|
|
177
|
-
return "(" + result + ")"
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Class for ordering select results on a number of columns.
|
|
183
|
-
*
|
|
184
|
-
*/
|
|
185
|
-
export class OINODbSqlOrder {
|
|
186
|
-
private static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC|\+|\-)?\s*?$/i
|
|
187
|
-
|
|
188
|
-
private _columns: string[]
|
|
189
|
-
private _descending: boolean[]
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Constructor for `OINODbSqlOrder`.
|
|
193
|
-
*
|
|
194
|
-
* @param column_or_array single or array of columns to order on
|
|
195
|
-
* @param descending_or_array single or array of booleans if ordes is descending
|
|
196
|
-
*
|
|
197
|
-
*/
|
|
198
|
-
constructor(column_or_array:string[]|string, descending_or_array:boolean[]|boolean) {
|
|
199
|
-
OINOLog.debug("OINODbSqlOrder.constructor", {columns:column_or_array, directions:descending_or_array})
|
|
200
|
-
if (Array.isArray(column_or_array)) {
|
|
201
|
-
this._columns = column_or_array
|
|
202
|
-
} else {
|
|
203
|
-
this._columns = [column_or_array]
|
|
204
|
-
}
|
|
205
|
-
if (Array.isArray(descending_or_array)) {
|
|
206
|
-
this._descending = descending_or_array
|
|
207
|
-
} else {
|
|
208
|
-
this._descending = [descending_or_array]
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Constructor for `OINODbSqlOrder` as parser of http parameter.
|
|
214
|
-
*
|
|
215
|
-
* @param orderString string representation of ordering from HTTP-request
|
|
216
|
-
*
|
|
217
|
-
*/
|
|
218
|
-
static parse(orderString: string):OINODbSqlOrder {
|
|
219
|
-
let columns:string[] = []
|
|
220
|
-
let directions:boolean[] = []
|
|
221
|
-
|
|
222
|
-
const column_strings = orderString.split(',')
|
|
223
|
-
|
|
224
|
-
for (let i=0; i<column_strings.length; i++) {
|
|
225
|
-
let match = OINODbSqlOrder._orderColumnRegex.exec(column_strings[i])
|
|
226
|
-
if (match != null) {
|
|
227
|
-
columns.push(match[1])
|
|
228
|
-
const dir:string = (match[2] || "ASC").toUpperCase()
|
|
229
|
-
directions.push((dir == "DESC") || (dir == "-"))
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return new OINODbSqlOrder(columns, directions)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Does filter contain any valid conditions.
|
|
237
|
-
*
|
|
238
|
-
*/
|
|
239
|
-
isEmpty():boolean {
|
|
240
|
-
return (this._columns.length == 0)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Print order as SQL condition based on the datamodel of the API.
|
|
245
|
-
*
|
|
246
|
-
* @param dataModel data model (and database) to use for formatting of values
|
|
247
|
-
*
|
|
248
|
-
*/
|
|
249
|
-
toSql(dataModel:OINODbDataModel):string {
|
|
250
|
-
if (this.isEmpty()) {
|
|
251
|
-
return ""
|
|
252
|
-
}
|
|
253
|
-
// OINOLog.debug("OINODbSqlOrder.toSql", {columns:this._columns, directions:this._directions})
|
|
254
|
-
let result:string = ""
|
|
255
|
-
for (let i=0; i<this._columns.length; i++) {
|
|
256
|
-
const field:OINODbDataField|null = dataModel.findFieldByName(this._columns[i])
|
|
257
|
-
if (!field) {
|
|
258
|
-
OINOLog.error("OINODbSqlOrder.toSql: Invalid field!", {field:this._columns[i]})
|
|
259
|
-
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlOrder.toSql - Invalid field '" + this._columns[i] + "'") // invalid field name could be a security risk, stop processing
|
|
260
|
-
}
|
|
261
|
-
if (result) {
|
|
262
|
-
result += ","
|
|
263
|
-
}
|
|
264
|
-
result += dataModel.api.db.printSqlColumnname(field.name) + " "
|
|
265
|
-
if (this._descending[i]) {
|
|
266
|
-
result += "DESC"
|
|
267
|
-
} else {
|
|
268
|
-
result += "ASC"
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
|
|
272
|
-
return result
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Class for limiting the number of results.
|
|
278
|
-
*
|
|
279
|
-
*/
|
|
280
|
-
export class OINODbSqlLimit {
|
|
281
|
-
private static _limitRegex = /^(\d+)(\spage\s)?(\d+)?$/i
|
|
282
|
-
|
|
283
|
-
private _limit: number
|
|
284
|
-
private _page: number
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Constructor for `OINODbSqlLimit`.
|
|
288
|
-
*
|
|
289
|
-
* @param limit maximum number of items to return
|
|
290
|
-
* @param page page number to return starting from 1
|
|
291
|
-
*
|
|
292
|
-
*/
|
|
293
|
-
constructor(limit: number, page: number = -1) {
|
|
294
|
-
this._limit = limit
|
|
295
|
-
this._page = page
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Constructor for `OINODbSqlLimit` as parser of http parameter.
|
|
299
|
-
*
|
|
300
|
-
* @param limitString string representation of limit from HTTP-request
|
|
301
|
-
*
|
|
302
|
-
*/
|
|
303
|
-
static parse(limitString: string):OINODbSqlLimit {
|
|
304
|
-
let match = OINODbSqlLimit._limitRegex.exec(limitString)
|
|
305
|
-
if ((match != null) && (match.length == 4)) {
|
|
306
|
-
return new OINODbSqlLimit(Number.parseInt(match[1]), Number.parseInt(match[3]))
|
|
307
|
-
} else if (match != null) {
|
|
308
|
-
return new OINODbSqlLimit(Number.parseInt(match[1]))
|
|
309
|
-
} else {
|
|
310
|
-
return new OINODbSqlLimit(-1)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Does filter contain any valid conditions.
|
|
316
|
-
*
|
|
317
|
-
*/
|
|
318
|
-
isEmpty():boolean {
|
|
319
|
-
return (this._limit <= 0)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Print order as SQL condition based on the datamodel of the API.
|
|
324
|
-
*
|
|
325
|
-
* @param dataModel data model (and database) to use for formatting of values
|
|
326
|
-
*
|
|
327
|
-
*/
|
|
328
|
-
toSql(dataModel:OINODbDataModel):string {
|
|
329
|
-
if (this.isEmpty()) {
|
|
330
|
-
return ""
|
|
331
|
-
}
|
|
332
|
-
let result:string = this._limit.toString()
|
|
333
|
-
if (this._page > 0) {
|
|
334
|
-
result += " OFFSET " + (this._limit * (this._page-1) + 1).toString()
|
|
335
|
-
}
|
|
336
|
-
return result
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Supported aggregation functions in OINODbSqlAggregate.
|
|
342
|
-
* @enum
|
|
343
|
-
*/
|
|
344
|
-
export enum OINODbSqlAggregateFunctions { count = "count", sum = "sum", avg = "avg", min = "min", max = "max" }
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Class for limiting the number of results.
|
|
348
|
-
*
|
|
349
|
-
*/
|
|
350
|
-
export class OINODbSqlAggregate {
|
|
351
|
-
private static _aggregateRegex:RegExp = new RegExp("^(count|sum|avg|min|max)\\(([" + OINO_FIELD_NAME_CHARS + "]+)\\)$", "mi")
|
|
352
|
-
|
|
353
|
-
private _functions: OINODbSqlAggregateFunctions[]
|
|
354
|
-
private _fields: string[]
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Constructor for `OINODbSqlAggregate`.
|
|
358
|
-
*
|
|
359
|
-
* @param
|
|
360
|
-
* @param fields fields to aggregate
|
|
361
|
-
*
|
|
362
|
-
*/
|
|
363
|
-
constructor(
|
|
364
|
-
this._functions =
|
|
365
|
-
this._fields = fields
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Constructor for `OINODbSqlAggregate` as parser of http parameter.
|
|
369
|
-
*
|
|
370
|
-
* @param aggregatorString string representation of limit from HTTP-request
|
|
371
|
-
*
|
|
372
|
-
*/
|
|
373
|
-
static parse(aggregatorString: string):OINODbSqlAggregate {
|
|
374
|
-
let funtions:OINODbSqlAggregateFunctions[] = []
|
|
375
|
-
let fields:string[] = []
|
|
376
|
-
const aggregator_parts = aggregatorString.split(',')
|
|
377
|
-
for (let i=0; i<aggregator_parts.length; i++) {
|
|
378
|
-
let match = OINODbSqlAggregate._aggregateRegex.exec(aggregator_parts[i])
|
|
379
|
-
// OINOLog.debug("OINODbSqlAggregate.parse - next aggregator", {aggregator: aggregator_parts[i], match:match})
|
|
380
|
-
if ((match != null) && (match.length == 3)) {
|
|
381
|
-
funtions.push(match[1] as OINODbSqlAggregateFunctions)
|
|
382
|
-
fields.push(match[2])
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return new OINODbSqlAggregate(funtions, fields)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Does filter contain any valid conditions.
|
|
390
|
-
*
|
|
391
|
-
*/
|
|
392
|
-
isEmpty():boolean {
|
|
393
|
-
return (this._functions.length <= 0)
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Print non-aggregated fields as SQL GROUP BY-condition based on the datamodel of the API.
|
|
398
|
-
*
|
|
399
|
-
* @param dataModel data model (and database) to use for formatting of values
|
|
400
|
-
*
|
|
401
|
-
*/
|
|
402
|
-
toSql(dataModel:OINODbDataModel):string {
|
|
403
|
-
if (this.isEmpty()) {
|
|
404
|
-
return ""
|
|
405
|
-
}
|
|
406
|
-
let result:string = ""
|
|
407
|
-
for (let i=0; i<dataModel.fields.length; i++) {
|
|
408
|
-
if (this._fields.includes(dataModel.fields[i].name) == false) {
|
|
409
|
-
result += dataModel.fields[i].printSqlColumnName() + ","
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
OINOLog.debug("OINODbSqlAggregate.toSql", {result:result})
|
|
413
|
-
return result.substring(0, result.length-1)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Print non-aggregated fields as SQL GROUP BY-condition based on the datamodel of the API.
|
|
418
|
-
*
|
|
419
|
-
* @param dataModel data model (and database) to use for formatting of values
|
|
420
|
-
*
|
|
421
|
-
*/
|
|
422
|
-
printSqlColumnNames(dataModel:OINODbDataModel):string {
|
|
423
|
-
let result:string = ""
|
|
424
|
-
for (let i=0; i<dataModel.fields.length; i++) {
|
|
425
|
-
const aggregate_index = this._fields.indexOf(dataModel.fields[i].name)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { OINOStr, OINODbDataField, OINODbDataModel, OINO_ERROR_PREFIX, OINOLog } from "./index.js"
|
|
8
|
+
|
|
9
|
+
const OINO_FIELD_NAME_CHARS:string = "\\w\\s\\-\\_\\#\\¤"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Supported logical conjunctions in filter predicates.
|
|
13
|
+
* @enum
|
|
14
|
+
*/
|
|
15
|
+
export enum OINODbSqlBooleanOperation { and = "and", or = "or", not = "not" }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Supported logical conjunctions in filter predicates.
|
|
19
|
+
* @enum
|
|
20
|
+
*/
|
|
21
|
+
export enum OINODbSqlComparison { lt = "lt", le = "le", eq = "eq", ge = "ge", gt = "gt", like = "like" }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Class for recursively parsing of filters and printing them as SQL conditions.
|
|
25
|
+
* Supports three types of statements
|
|
26
|
+
* - comparison: (field)-lt|le|eq|ge|gt|like(value)
|
|
27
|
+
* - negation: -not(filter)
|
|
28
|
+
* - conjunction/disjunction: (filter)-and|or(filter)
|
|
29
|
+
* Supported conditions are comparisons (<, <=, =, >=, >) and substring match (LIKE).
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export class OINODbSqlFilter {
|
|
33
|
+
private static _booleanOperationRegex = /^\s?\-(and|or)\s?$/i
|
|
34
|
+
private static _negationRegex = /^-(not|)\((.+)\)$/i
|
|
35
|
+
private static _filterComparisonRegex = /^\(([^'"\(\)]+)\)\s?\-(lt|le|eq|ge|gt|like)\s?\(([^'"\(\)]+)\)$/i
|
|
36
|
+
|
|
37
|
+
private _leftSide: OINODbSqlFilter | string
|
|
38
|
+
private _rightSide: OINODbSqlFilter | string
|
|
39
|
+
private _operator:OINODbSqlComparison|OINODbSqlBooleanOperation|null
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Constructor of `OINODbSqlFilter`
|
|
43
|
+
* @param leftSide left side of the filter, either another filter or a column name
|
|
44
|
+
* @param operation operation of the filter, either `OINODbSqlComparison` or `OINODbSqlBooleanOperation`
|
|
45
|
+
* @param rightSide right side of the filter, either another filter or a value
|
|
46
|
+
*/
|
|
47
|
+
constructor(leftSide:OINODbSqlFilter|string, operation:OINODbSqlComparison|OINODbSqlBooleanOperation|null, rightSide:OINODbSqlFilter|string) {
|
|
48
|
+
if (!(
|
|
49
|
+
((operation === null) && (leftSide == "") && (rightSide == "")) ||
|
|
50
|
+
((operation !== null) && (Object.values(OINODbSqlComparison).includes(operation as OINODbSqlComparison)) && (typeof(leftSide) == "string") && (leftSide != "") && (typeof(rightSide) == "string") && (rightSide != "")) ||
|
|
51
|
+
((operation == OINODbSqlBooleanOperation.not) && (leftSide == "") && (rightSide instanceof OINODbSqlFilter)) ||
|
|
52
|
+
(((operation == OINODbSqlBooleanOperation.and) || (operation == OINODbSqlBooleanOperation.or)) && (leftSide instanceof OINODbSqlFilter) && (rightSide instanceof OINODbSqlFilter))
|
|
53
|
+
)) {
|
|
54
|
+
OINOLog.debug("Unsupported OINODbSqlFilter format!", {leftSide:leftSide, operation:operation, rightSide:rightSide})
|
|
55
|
+
throw new Error(OINO_ERROR_PREFIX + ": Unsupported OINODbSqlFilter format!")
|
|
56
|
+
}
|
|
57
|
+
this._leftSide = leftSide
|
|
58
|
+
this._operator = operation
|
|
59
|
+
this._rightSide = rightSide
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Constructor for `OINODbSqlFilter` as parser of http parameter.
|
|
64
|
+
*
|
|
65
|
+
* @param filterString string representation of filter from HTTP-request
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
static parse(filterString: string):OINODbSqlFilter {
|
|
69
|
+
// OINOLog_debug("OINODbSqlFilter.constructor", {filterString:filterString})
|
|
70
|
+
if (!filterString) {
|
|
71
|
+
return new OINODbSqlFilter("", null, "")
|
|
72
|
+
|
|
73
|
+
} else {
|
|
74
|
+
let match = OINODbSqlFilter._filterComparisonRegex.exec(filterString)
|
|
75
|
+
if (match != null) {
|
|
76
|
+
return new OINODbSqlFilter(match[1], match[2].toLowerCase() as OINODbSqlComparison, match[3])
|
|
77
|
+
} else {
|
|
78
|
+
let match = OINODbSqlFilter._negationRegex.exec(filterString)
|
|
79
|
+
if (match != null) {
|
|
80
|
+
return new OINODbSqlFilter("", OINODbSqlBooleanOperation.not, OINODbSqlFilter.parse(match[3]))
|
|
81
|
+
} else {
|
|
82
|
+
let boolean_parts = OINOStr.splitByBrackets(filterString, true, false, '(', ')')
|
|
83
|
+
// OINOLog_debug("OINODbSqlFilter.constructor", {boolean_parts:boolean_parts})
|
|
84
|
+
if (boolean_parts.length == 3 && (boolean_parts[1].match(OINODbSqlFilter._booleanOperationRegex))) {
|
|
85
|
+
return new OINODbSqlFilter(OINODbSqlFilter.parse(boolean_parts[0]), boolean_parts[1].trim().toLowerCase().substring(1) as OINODbSqlBooleanOperation, OINODbSqlFilter.parse(boolean_parts[2]))
|
|
86
|
+
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error(OINO_ERROR_PREFIX + ": Invalid filter '" + filterString + "'") // invalid filter could be a security risk, stop processing
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Construct a new `OINODbSqlFilter` as combination of (boolean and/or) of two filters.
|
|
97
|
+
*
|
|
98
|
+
* @param leftSide left side to combine
|
|
99
|
+
* @param operation boolean operation to use in combination
|
|
100
|
+
* @param rightSide right side to combine
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
static combine(leftSide:OINODbSqlFilter|undefined, operation:OINODbSqlBooleanOperation, rightSide:OINODbSqlFilter|undefined):OINODbSqlFilter|undefined {
|
|
104
|
+
if ((leftSide) && (!leftSide.isEmpty()) && (rightSide) && (!rightSide.isEmpty())) {
|
|
105
|
+
return new OINODbSqlFilter(leftSide, operation, rightSide)
|
|
106
|
+
|
|
107
|
+
} else if ((leftSide) && (!leftSide.isEmpty())) {
|
|
108
|
+
return leftSide
|
|
109
|
+
|
|
110
|
+
} else if ((rightSide) && (!rightSide.isEmpty())) {
|
|
111
|
+
return rightSide
|
|
112
|
+
|
|
113
|
+
} else {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
private _operatorToSql():string {
|
|
120
|
+
switch (this._operator) {
|
|
121
|
+
case "and": return " AND "
|
|
122
|
+
case "or": return " OR "
|
|
123
|
+
case "not": return "NOT "
|
|
124
|
+
case "lt": return " < "
|
|
125
|
+
case "le": return " <= "
|
|
126
|
+
case "eq": return " = "
|
|
127
|
+
case "ge": return " >= "
|
|
128
|
+
case "gt": return " > "
|
|
129
|
+
case "like": return " LIKE "
|
|
130
|
+
}
|
|
131
|
+
return " "
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Does filter contain any valid conditions.
|
|
136
|
+
*
|
|
137
|
+
*/
|
|
138
|
+
isEmpty():boolean {
|
|
139
|
+
return (this._leftSide == "") && (this._operator == null) && (this._rightSide == "")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Print filter as SQL condition based on the datamodel of the API.
|
|
144
|
+
*
|
|
145
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
146
|
+
*
|
|
147
|
+
*/
|
|
148
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
149
|
+
// OINOLog.debug("OINODbSqlFilter.toSql", {_leftSide:this._leftSide, _operator:this._operator, _rightSide:this._rightSide})
|
|
150
|
+
if (this.isEmpty()) {
|
|
151
|
+
return ""
|
|
152
|
+
}
|
|
153
|
+
let result:string = ""
|
|
154
|
+
let field:OINODbDataField|null = null
|
|
155
|
+
if (this._leftSide instanceof OINODbSqlFilter) {
|
|
156
|
+
result += this._leftSide.toSql(dataModel)
|
|
157
|
+
} else {
|
|
158
|
+
field = dataModel.findFieldByName(this._leftSide)
|
|
159
|
+
if (!field) {
|
|
160
|
+
OINOLog.error("OINODbSqlFilter.toSql: Invalid field!", {field:this._leftSide})
|
|
161
|
+
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid field '" + this._leftSide + "'") // invalid field name could be a security risk, stop processing
|
|
162
|
+
}
|
|
163
|
+
result += dataModel.api.db.printSqlColumnname(field?.name || this._leftSide)
|
|
164
|
+
}
|
|
165
|
+
result += this._operatorToSql()
|
|
166
|
+
if (this._rightSide instanceof OINODbSqlFilter) {
|
|
167
|
+
result += this._rightSide.toSql(dataModel)
|
|
168
|
+
} else {
|
|
169
|
+
const value = field!.deserializeCell(this._rightSide)
|
|
170
|
+
if (!value) {
|
|
171
|
+
OINOLog.error("OINODbSqlFilter.toSql: Invalid value!", {value:value})
|
|
172
|
+
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlFilter.toSql - Invalid value '" + value + "'") // invalid value could be a security risk, stop processing
|
|
173
|
+
}
|
|
174
|
+
result += field!.printCellAsSqlValue(value)
|
|
175
|
+
}
|
|
176
|
+
// OINOLog.debug("OINODbSqlFilter.toSql", {result:result})
|
|
177
|
+
return "(" + result + ")"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Class for ordering select results on a number of columns.
|
|
183
|
+
*
|
|
184
|
+
*/
|
|
185
|
+
export class OINODbSqlOrder {
|
|
186
|
+
private static _orderColumnRegex = /^\s*(\w+)\s?(ASC|DESC|\+|\-)?\s*?$/i
|
|
187
|
+
|
|
188
|
+
private _columns: string[]
|
|
189
|
+
private _descending: boolean[]
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Constructor for `OINODbSqlOrder`.
|
|
193
|
+
*
|
|
194
|
+
* @param column_or_array single or array of columns to order on
|
|
195
|
+
* @param descending_or_array single or array of booleans if ordes is descending
|
|
196
|
+
*
|
|
197
|
+
*/
|
|
198
|
+
constructor(column_or_array:string[]|string, descending_or_array:boolean[]|boolean) {
|
|
199
|
+
OINOLog.debug("OINODbSqlOrder.constructor", {columns:column_or_array, directions:descending_or_array})
|
|
200
|
+
if (Array.isArray(column_or_array)) {
|
|
201
|
+
this._columns = column_or_array
|
|
202
|
+
} else {
|
|
203
|
+
this._columns = [column_or_array]
|
|
204
|
+
}
|
|
205
|
+
if (Array.isArray(descending_or_array)) {
|
|
206
|
+
this._descending = descending_or_array
|
|
207
|
+
} else {
|
|
208
|
+
this._descending = [descending_or_array]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Constructor for `OINODbSqlOrder` as parser of http parameter.
|
|
214
|
+
*
|
|
215
|
+
* @param orderString string representation of ordering from HTTP-request
|
|
216
|
+
*
|
|
217
|
+
*/
|
|
218
|
+
static parse(orderString: string):OINODbSqlOrder {
|
|
219
|
+
let columns:string[] = []
|
|
220
|
+
let directions:boolean[] = []
|
|
221
|
+
|
|
222
|
+
const column_strings = orderString.split(',')
|
|
223
|
+
|
|
224
|
+
for (let i=0; i<column_strings.length; i++) {
|
|
225
|
+
let match = OINODbSqlOrder._orderColumnRegex.exec(column_strings[i])
|
|
226
|
+
if (match != null) {
|
|
227
|
+
columns.push(match[1])
|
|
228
|
+
const dir:string = (match[2] || "ASC").toUpperCase()
|
|
229
|
+
directions.push((dir == "DESC") || (dir == "-"))
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return new OINODbSqlOrder(columns, directions)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Does filter contain any valid conditions.
|
|
237
|
+
*
|
|
238
|
+
*/
|
|
239
|
+
isEmpty():boolean {
|
|
240
|
+
return (this._columns.length == 0)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Print order as SQL condition based on the datamodel of the API.
|
|
245
|
+
*
|
|
246
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
247
|
+
*
|
|
248
|
+
*/
|
|
249
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
250
|
+
if (this.isEmpty()) {
|
|
251
|
+
return ""
|
|
252
|
+
}
|
|
253
|
+
// OINOLog.debug("OINODbSqlOrder.toSql", {columns:this._columns, directions:this._directions})
|
|
254
|
+
let result:string = ""
|
|
255
|
+
for (let i=0; i<this._columns.length; i++) {
|
|
256
|
+
const field:OINODbDataField|null = dataModel.findFieldByName(this._columns[i])
|
|
257
|
+
if (!field) {
|
|
258
|
+
OINOLog.error("OINODbSqlOrder.toSql: Invalid field!", {field:this._columns[i]})
|
|
259
|
+
throw new Error(OINO_ERROR_PREFIX + ": OINODbSqlOrder.toSql - Invalid field '" + this._columns[i] + "'") // invalid field name could be a security risk, stop processing
|
|
260
|
+
}
|
|
261
|
+
if (result) {
|
|
262
|
+
result += ","
|
|
263
|
+
}
|
|
264
|
+
result += dataModel.api.db.printSqlColumnname(field.name) + " "
|
|
265
|
+
if (this._descending[i]) {
|
|
266
|
+
result += "DESC"
|
|
267
|
+
} else {
|
|
268
|
+
result += "ASC"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// OINOLog.debug("OINODbSqlOrder.toSql", {result:result})
|
|
272
|
+
return result
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Class for limiting the number of results.
|
|
278
|
+
*
|
|
279
|
+
*/
|
|
280
|
+
export class OINODbSqlLimit {
|
|
281
|
+
private static _limitRegex = /^(\d+)(\spage\s)?(\d+)?$/i
|
|
282
|
+
|
|
283
|
+
private _limit: number
|
|
284
|
+
private _page: number
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Constructor for `OINODbSqlLimit`.
|
|
288
|
+
*
|
|
289
|
+
* @param limit maximum number of items to return
|
|
290
|
+
* @param page page number to return starting from 1
|
|
291
|
+
*
|
|
292
|
+
*/
|
|
293
|
+
constructor(limit: number, page: number = -1) {
|
|
294
|
+
this._limit = limit
|
|
295
|
+
this._page = page
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Constructor for `OINODbSqlLimit` as parser of http parameter.
|
|
299
|
+
*
|
|
300
|
+
* @param limitString string representation of limit from HTTP-request
|
|
301
|
+
*
|
|
302
|
+
*/
|
|
303
|
+
static parse(limitString: string):OINODbSqlLimit {
|
|
304
|
+
let match = OINODbSqlLimit._limitRegex.exec(limitString)
|
|
305
|
+
if ((match != null) && (match.length == 4)) {
|
|
306
|
+
return new OINODbSqlLimit(Number.parseInt(match[1]), Number.parseInt(match[3]))
|
|
307
|
+
} else if (match != null) {
|
|
308
|
+
return new OINODbSqlLimit(Number.parseInt(match[1]))
|
|
309
|
+
} else {
|
|
310
|
+
return new OINODbSqlLimit(-1)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Does filter contain any valid conditions.
|
|
316
|
+
*
|
|
317
|
+
*/
|
|
318
|
+
isEmpty():boolean {
|
|
319
|
+
return (this._limit <= 0)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Print order as SQL condition based on the datamodel of the API.
|
|
324
|
+
*
|
|
325
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
326
|
+
*
|
|
327
|
+
*/
|
|
328
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
329
|
+
if (this.isEmpty()) {
|
|
330
|
+
return ""
|
|
331
|
+
}
|
|
332
|
+
let result:string = this._limit.toString()
|
|
333
|
+
if (this._page > 0) {
|
|
334
|
+
result += " OFFSET " + (this._limit * (this._page-1) + 1).toString()
|
|
335
|
+
}
|
|
336
|
+
return result
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Supported aggregation functions in OINODbSqlAggregate.
|
|
342
|
+
* @enum
|
|
343
|
+
*/
|
|
344
|
+
export enum OINODbSqlAggregateFunctions { count = "count", sum = "sum", avg = "avg", min = "min", max = "max" }
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Class for limiting the number of results.
|
|
348
|
+
*
|
|
349
|
+
*/
|
|
350
|
+
export class OINODbSqlAggregate {
|
|
351
|
+
private static _aggregateRegex:RegExp = new RegExp("^(count|sum|avg|min|max)\\(([" + OINO_FIELD_NAME_CHARS + "]+)\\)$", "mi")
|
|
352
|
+
|
|
353
|
+
private _functions: OINODbSqlAggregateFunctions[]
|
|
354
|
+
private _fields: string[]
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Constructor for `OINODbSqlAggregate`.
|
|
358
|
+
*
|
|
359
|
+
* @param functions aggregate function to use
|
|
360
|
+
* @param fields fields to aggregate
|
|
361
|
+
*
|
|
362
|
+
*/
|
|
363
|
+
constructor(functions: OINODbSqlAggregateFunctions[], fields: string[]) {
|
|
364
|
+
this._functions = functions
|
|
365
|
+
this._fields = fields
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Constructor for `OINODbSqlAggregate` as parser of http parameter.
|
|
369
|
+
*
|
|
370
|
+
* @param aggregatorString string representation of limit from HTTP-request
|
|
371
|
+
*
|
|
372
|
+
*/
|
|
373
|
+
static parse(aggregatorString: string):OINODbSqlAggregate {
|
|
374
|
+
let funtions:OINODbSqlAggregateFunctions[] = []
|
|
375
|
+
let fields:string[] = []
|
|
376
|
+
const aggregator_parts = aggregatorString.split(',')
|
|
377
|
+
for (let i=0; i<aggregator_parts.length; i++) {
|
|
378
|
+
let match = OINODbSqlAggregate._aggregateRegex.exec(aggregator_parts[i])
|
|
379
|
+
// OINOLog.debug("OINODbSqlAggregate.parse - next aggregator", {aggregator: aggregator_parts[i], match:match})
|
|
380
|
+
if ((match != null) && (match.length == 3)) {
|
|
381
|
+
funtions.push(match[1] as OINODbSqlAggregateFunctions)
|
|
382
|
+
fields.push(match[2])
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return new OINODbSqlAggregate(funtions, fields)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Does filter contain any valid conditions.
|
|
390
|
+
*
|
|
391
|
+
*/
|
|
392
|
+
isEmpty():boolean {
|
|
393
|
+
return (this._functions.length <= 0)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Print non-aggregated fields as SQL GROUP BY-condition based on the datamodel of the API.
|
|
398
|
+
*
|
|
399
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
400
|
+
*
|
|
401
|
+
*/
|
|
402
|
+
toSql(dataModel:OINODbDataModel):string {
|
|
403
|
+
if (this.isEmpty()) {
|
|
404
|
+
return ""
|
|
405
|
+
}
|
|
406
|
+
let result:string = ""
|
|
407
|
+
for (let i=0; i<dataModel.fields.length; i++) {
|
|
408
|
+
if (this._fields.includes(dataModel.fields[i].name) == false) {
|
|
409
|
+
result += dataModel.fields[i].printSqlColumnName() + ","
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
OINOLog.debug("OINODbSqlAggregate.toSql", {result:result})
|
|
413
|
+
return result.substring(0, result.length-1)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Print non-aggregated fields as SQL GROUP BY-condition based on the datamodel of the API.
|
|
418
|
+
*
|
|
419
|
+
* @param dataModel data model (and database) to use for formatting of values
|
|
420
|
+
*
|
|
421
|
+
*/
|
|
422
|
+
printSqlColumnNames(dataModel:OINODbDataModel):string {
|
|
423
|
+
let result:string = ""
|
|
424
|
+
for (let i=0; i<dataModel.fields.length; i++) {
|
|
425
|
+
const aggregate_index = this._fields.indexOf(dataModel.fields[i].name)
|
|
426
|
+
const col_name = dataModel.fields[i].printSqlColumnName()
|
|
427
|
+
if (aggregate_index >= 0) {
|
|
428
|
+
result += this._functions[aggregate_index] + "(" + col_name + ") as " + col_name + ","
|
|
429
|
+
} else {
|
|
430
|
+
result += col_name + ","
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
OINOLog.debug("OINODbSqlAggregate.printSqlColumnNames", {result:result})
|
|
434
|
+
return result.substring(0, result.length-1)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
|