Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Component: OGDI Driver Support Library |
4 | | * Purpose: Generic SQL WHERE Expression Implementation. |
5 | | * Author: Frank Warmerdam <warmerdam@pobox.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (C) 2001 Information Interoperability Institute (3i) |
9 | | * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * Permission to use, copy, modify and distribute this software and |
12 | | * its documentation for any purpose and without fee is hereby granted, |
13 | | * provided that the above copyright notice appear in all copies, that |
14 | | * both the copyright notice and this permission notice appear in |
15 | | * supporting documentation, and that the name of 3i not be used |
16 | | * in advertising or publicity pertaining to distribution of the software |
17 | | * without specific, written prior permission. 3i makes no |
18 | | * representations about the suitability of this software for any purpose. |
19 | | * It is provided "as is" without express or implied warranty. |
20 | | ****************************************************************************/ |
21 | | |
22 | | #include "cpl_port.h" |
23 | | #include "ogr_swq.h" |
24 | | |
25 | | #include <cassert> |
26 | | #include <cctype> |
27 | | #include <cmath> |
28 | | #include <cstddef> |
29 | | #include <cstdlib> |
30 | | #include <cstring> |
31 | | #include <ctime> |
32 | | |
33 | | #include <algorithm> |
34 | | #include <limits> |
35 | | #include <string> |
36 | | |
37 | | #include "cpl_error.h" |
38 | | #include "cpl_multiproc.h" |
39 | | #include "cpl_time.h" |
40 | | #include "swq_parser.hpp" |
41 | | |
42 | | #define YYSTYPE swq_expr_node * |
43 | | |
44 | | /************************************************************************/ |
45 | | /* swqlex() */ |
46 | | /************************************************************************/ |
47 | | |
48 | | void swqerror(swq_parse_context *context, const char *msg) |
49 | 0 | { |
50 | 0 | CPLString osMsg; |
51 | 0 | osMsg.Printf("SQL Expression Parsing Error: %s. Occurred around :\n", msg); |
52 | |
|
53 | 0 | int n = static_cast<int>(context->pszLastValid - context->pszInput); |
54 | |
|
55 | 0 | for (int i = std::max(0, n - 40); |
56 | 0 | i < n + 40 && context->pszInput[i] != '\0'; i++) |
57 | 0 | osMsg += context->pszInput[i]; |
58 | 0 | osMsg += "\n"; |
59 | 0 | for (int i = 0; i < std::min(n, 40); i++) |
60 | 0 | osMsg += " "; |
61 | 0 | osMsg += "^"; |
62 | |
|
63 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", osMsg.c_str()); |
64 | 0 | } |
65 | | |
66 | | /************************************************************************/ |
67 | | /* swqlex() */ |
68 | | /* */ |
69 | | /* Read back a token from the input. */ |
70 | | /************************************************************************/ |
71 | | |
72 | | int swqlex(YYSTYPE *ppNode, swq_parse_context *context) |
73 | 0 | { |
74 | 0 | const char *pszInput = context->pszNext; |
75 | |
|
76 | 0 | *ppNode = nullptr; |
77 | | |
78 | | /* -------------------------------------------------------------------- */ |
79 | | /* Do we have a start symbol to return? */ |
80 | | /* -------------------------------------------------------------------- */ |
81 | 0 | if (context->nStartToken != 0) |
82 | 0 | { |
83 | 0 | int nRet = context->nStartToken; |
84 | 0 | context->nStartToken = 0; |
85 | 0 | return nRet; |
86 | 0 | } |
87 | | |
88 | | /* -------------------------------------------------------------------- */ |
89 | | /* Skip white space. */ |
90 | | /* -------------------------------------------------------------------- */ |
91 | 0 | while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 || |
92 | 0 | *pszInput == 13) |
93 | 0 | pszInput++; |
94 | |
|
95 | 0 | context->pszLastValid = pszInput; |
96 | |
|
97 | 0 | if (*pszInput == '\0') |
98 | 0 | { |
99 | 0 | context->pszNext = pszInput; |
100 | 0 | return EOF; |
101 | 0 | } |
102 | | |
103 | | /* -------------------------------------------------------------------- */ |
104 | | /* Handle string constants. */ |
105 | | /* -------------------------------------------------------------------- */ |
106 | 0 | if (*pszInput == '"' || *pszInput == '\'') |
107 | 0 | { |
108 | 0 | char chQuote = *pszInput; |
109 | 0 | bool bFoundEndQuote = false; |
110 | |
|
111 | 0 | int nRet = *pszInput == '"' ? SWQT_IDENTIFIER : SWQT_STRING; |
112 | |
|
113 | 0 | pszInput++; |
114 | |
|
115 | 0 | char *token = static_cast<char *>(CPLMalloc(strlen(pszInput) + 1)); |
116 | 0 | int i_token = 0; |
117 | |
|
118 | 0 | while (*pszInput != '\0') |
119 | 0 | { |
120 | | // Not totally sure we need to preserve this way of escaping for |
121 | | // strings between double-quotes |
122 | 0 | if (chQuote == '"' && *pszInput == '\\') |
123 | 0 | { |
124 | 0 | pszInput++; |
125 | 0 | if (*pszInput == '\0') |
126 | 0 | break; |
127 | 0 | } |
128 | 0 | else if (chQuote == '\'' && *pszInput == '\'' && |
129 | 0 | pszInput[1] == '\'') |
130 | 0 | pszInput++; |
131 | 0 | else if (*pszInput == chQuote) |
132 | 0 | { |
133 | 0 | pszInput++; |
134 | 0 | bFoundEndQuote = true; |
135 | 0 | break; |
136 | 0 | } |
137 | | |
138 | 0 | token[i_token++] = *(pszInput++); |
139 | 0 | } |
140 | 0 | token[i_token] = '\0'; |
141 | |
|
142 | 0 | if (!bFoundEndQuote) |
143 | 0 | { |
144 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
145 | 0 | "Did not find end-of-string character"); |
146 | 0 | CPLFree(token); |
147 | 0 | return YYerror; |
148 | 0 | } |
149 | | |
150 | 0 | *ppNode = new swq_expr_node(token); |
151 | 0 | CPLFree(token); |
152 | |
|
153 | 0 | context->pszNext = pszInput; |
154 | |
|
155 | 0 | return nRet; |
156 | 0 | } |
157 | | |
158 | | /* -------------------------------------------------------------------- */ |
159 | | /* Handle numbers. */ |
160 | | /* -------------------------------------------------------------------- */ |
161 | 0 | else if (*pszInput >= '0' && *pszInput <= '9') |
162 | 0 | { |
163 | 0 | CPLString osToken; |
164 | 0 | const char *pszNext = pszInput + 1; |
165 | |
|
166 | 0 | osToken += *pszInput; |
167 | | |
168 | | // collect non-decimal part of number |
169 | 0 | while (*pszNext >= '0' && *pszNext <= '9') |
170 | 0 | osToken += *(pszNext++); |
171 | | |
172 | | // collect decimal places. |
173 | 0 | if (*pszNext == '.') |
174 | 0 | { |
175 | 0 | osToken += *(pszNext++); |
176 | 0 | while (*pszNext >= '0' && *pszNext <= '9') |
177 | 0 | osToken += *(pszNext++); |
178 | 0 | } |
179 | | |
180 | | // collect exponent |
181 | 0 | if (*pszNext == 'e' || *pszNext == 'E') |
182 | 0 | { |
183 | 0 | osToken += *(pszNext++); |
184 | 0 | if (*pszNext == '-' || *pszNext == '+') |
185 | 0 | osToken += *(pszNext++); |
186 | 0 | while (*pszNext >= '0' && *pszNext <= '9') |
187 | 0 | osToken += *(pszNext++); |
188 | 0 | } |
189 | |
|
190 | 0 | context->pszNext = pszNext; |
191 | |
|
192 | 0 | if (strstr(osToken, ".") || strstr(osToken, "e") || |
193 | 0 | strstr(osToken, "E")) |
194 | 0 | { |
195 | 0 | *ppNode = new swq_expr_node(CPLAtof(osToken)); |
196 | 0 | return SWQT_FLOAT_NUMBER; |
197 | 0 | } |
198 | 0 | else |
199 | 0 | { |
200 | 0 | if (osToken.size() > 19 || |
201 | 0 | (osToken.size() >= 19 && osToken > "9223372036854775807")) |
202 | 0 | { |
203 | 0 | *ppNode = new swq_expr_node(CPLAtof(osToken)); |
204 | 0 | if (osToken == "9223372036854775808") |
205 | 0 | (*ppNode)->string_value = CPLStrdup(osToken); |
206 | 0 | return SWQT_FLOAT_NUMBER; |
207 | 0 | } |
208 | | |
209 | 0 | GIntBig nVal = CPLAtoGIntBig(osToken); |
210 | 0 | if (CPL_INT64_FITS_ON_INT32(nVal)) |
211 | 0 | *ppNode = new swq_expr_node(static_cast<int>(nVal)); |
212 | 0 | else |
213 | 0 | *ppNode = new swq_expr_node(nVal); |
214 | 0 | return SWQT_INTEGER_NUMBER; |
215 | 0 | } |
216 | 0 | } |
217 | | |
218 | | /* -------------------------------------------------------------------- */ |
219 | | /* Handle alpha-numerics. */ |
220 | | /* -------------------------------------------------------------------- */ |
221 | 0 | else if (isalnum(static_cast<unsigned char>(*pszInput))) |
222 | 0 | { |
223 | 0 | int nReturn = SWQT_IDENTIFIER; |
224 | 0 | CPLString osToken; |
225 | 0 | const char *pszNext = pszInput + 1; |
226 | |
|
227 | 0 | osToken += *pszInput; |
228 | | |
229 | | // collect text characters |
230 | 0 | while (isalnum(static_cast<unsigned char>(*pszNext)) || |
231 | 0 | *pszNext == '_' || static_cast<unsigned char>(*pszNext) > 127) |
232 | 0 | osToken += *(pszNext++); |
233 | |
|
234 | 0 | context->pszNext = pszNext; |
235 | |
|
236 | 0 | if (EQUAL(osToken, "IN")) |
237 | 0 | nReturn = SWQT_IN; |
238 | 0 | else if (EQUAL(osToken, "LIKE")) |
239 | 0 | nReturn = SWQT_LIKE; |
240 | 0 | else if (EQUAL(osToken, "ILIKE")) |
241 | 0 | nReturn = SWQT_ILIKE; |
242 | 0 | else if (EQUAL(osToken, "ESCAPE")) |
243 | 0 | nReturn = SWQT_ESCAPE; |
244 | 0 | else if (EQUAL(osToken, "EXCEPT")) |
245 | 0 | nReturn = SWQT_EXCEPT; |
246 | 0 | else if (EQUAL(osToken, "EXCLUDE")) |
247 | 0 | nReturn = SWQT_EXCLUDE; |
248 | 0 | else if (EQUAL(osToken, "NULL")) |
249 | 0 | nReturn = SWQT_NULL; |
250 | 0 | else if (EQUAL(osToken, "IS")) |
251 | 0 | nReturn = SWQT_IS; |
252 | 0 | else if (EQUAL(osToken, "NOT")) |
253 | 0 | nReturn = SWQT_NOT; |
254 | 0 | else if (EQUAL(osToken, "AND")) |
255 | 0 | nReturn = SWQT_AND; |
256 | 0 | else if (EQUAL(osToken, "OR")) |
257 | 0 | nReturn = SWQT_OR; |
258 | 0 | else if (EQUAL(osToken, "BETWEEN")) |
259 | 0 | nReturn = SWQT_BETWEEN; |
260 | 0 | else if (EQUAL(osToken, "SELECT")) |
261 | 0 | nReturn = SWQT_SELECT; |
262 | 0 | else if (EQUAL(osToken, "LEFT")) |
263 | 0 | nReturn = SWQT_LEFT; |
264 | 0 | else if (EQUAL(osToken, "JOIN")) |
265 | 0 | nReturn = SWQT_JOIN; |
266 | 0 | else if (EQUAL(osToken, "WHERE")) |
267 | 0 | nReturn = SWQT_WHERE; |
268 | 0 | else if (EQUAL(osToken, "ON")) |
269 | 0 | nReturn = SWQT_ON; |
270 | 0 | else if (EQUAL(osToken, "ORDER")) |
271 | 0 | nReturn = SWQT_ORDER; |
272 | 0 | else if (EQUAL(osToken, "BY")) |
273 | 0 | nReturn = SWQT_BY; |
274 | 0 | else if (EQUAL(osToken, "FROM")) |
275 | 0 | nReturn = SWQT_FROM; |
276 | 0 | else if (EQUAL(osToken, "AS")) |
277 | 0 | nReturn = SWQT_AS; |
278 | 0 | else if (EQUAL(osToken, "ASC")) |
279 | 0 | nReturn = SWQT_ASC; |
280 | 0 | else if (EQUAL(osToken, "DESC")) |
281 | 0 | nReturn = SWQT_DESC; |
282 | 0 | else if (EQUAL(osToken, "DISTINCT")) |
283 | 0 | nReturn = SWQT_DISTINCT; |
284 | 0 | else if (EQUAL(osToken, "CAST")) |
285 | 0 | nReturn = SWQT_CAST; |
286 | 0 | else if (EQUAL(osToken, "UNION")) |
287 | 0 | nReturn = SWQT_UNION; |
288 | 0 | else if (EQUAL(osToken, "ALL")) |
289 | 0 | nReturn = SWQT_ALL; |
290 | 0 | else if (EQUAL(osToken, "LIMIT")) |
291 | 0 | nReturn = SWQT_LIMIT; |
292 | 0 | else if (EQUAL(osToken, "OFFSET")) |
293 | 0 | nReturn = SWQT_OFFSET; |
294 | 0 | else if (EQUAL(osToken, "HIDDEN")) |
295 | 0 | { |
296 | 0 | *ppNode = new swq_expr_node(osToken); |
297 | 0 | nReturn = SWQT_HIDDEN; |
298 | 0 | } |
299 | | |
300 | | // Unhandled by OGR SQL. |
301 | 0 | else if (EQUAL(osToken, "OUTER") || EQUAL(osToken, "INNER")) |
302 | 0 | nReturn = SWQT_RESERVED_KEYWORD; |
303 | | |
304 | 0 | else |
305 | 0 | { |
306 | 0 | *ppNode = new swq_expr_node(osToken); |
307 | 0 | nReturn = SWQT_IDENTIFIER; |
308 | 0 | } |
309 | |
|
310 | 0 | return nReturn; |
311 | 0 | } |
312 | | |
313 | | /* -------------------------------------------------------------------- */ |
314 | | /* Handle special tokens. */ |
315 | | /* -------------------------------------------------------------------- */ |
316 | 0 | else |
317 | 0 | { |
318 | 0 | context->pszNext = pszInput + 1; |
319 | 0 | return *pszInput; |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | /************************************************************************/ |
324 | | /* swq_select_summarize() */ |
325 | | /************************************************************************/ |
326 | | |
327 | | const char *swq_select_summarize(swq_select *select_info, int dest_column, |
328 | | const char *pszValue, const double *pdfValue) |
329 | | |
330 | 0 | { |
331 | | /* -------------------------------------------------------------------- */ |
332 | | /* Do various checking. */ |
333 | | /* -------------------------------------------------------------------- */ |
334 | 0 | if (select_info->query_mode == SWQM_RECORDSET) |
335 | 0 | return "swq_select_summarize() called on non-summary query."; |
336 | | |
337 | 0 | if (dest_column < 0 || |
338 | 0 | dest_column >= static_cast<int>(select_info->column_defs.size())) |
339 | 0 | return "dest_column out of range in swq_select_summarize()."; |
340 | | |
341 | 0 | const swq_col_def *def = &select_info->column_defs[dest_column]; |
342 | 0 | if (def->col_func == SWQCF_NONE && !def->distinct_flag) |
343 | 0 | return nullptr; |
344 | | |
345 | 0 | if (select_info->query_mode == SWQM_DISTINCT_LIST && |
346 | 0 | select_info->order_specs > 0) |
347 | 0 | { |
348 | 0 | if (select_info->order_specs > 1) |
349 | 0 | return "Can't ORDER BY a DISTINCT list by more than one key."; |
350 | | |
351 | 0 | if (select_info->order_defs[0].field_index != |
352 | 0 | select_info->column_defs[0].field_index) |
353 | 0 | return "Only selected DISTINCT field can be used for ORDER BY."; |
354 | 0 | } |
355 | | |
356 | | /* -------------------------------------------------------------------- */ |
357 | | /* Create the summary information if this is the first row */ |
358 | | /* being processed. */ |
359 | | /* -------------------------------------------------------------------- */ |
360 | 0 | if (select_info->column_summary.empty()) |
361 | 0 | { |
362 | 0 | select_info->column_summary.resize(select_info->column_defs.size()); |
363 | 0 | for (std::size_t i = 0; i < select_info->column_defs.size(); i++) |
364 | 0 | { |
365 | 0 | if (def->distinct_flag) |
366 | 0 | { |
367 | 0 | swq_summary::Comparator oComparator; |
368 | 0 | if (select_info->order_specs > 0) |
369 | 0 | { |
370 | 0 | CPLAssert(select_info->order_specs == 1); |
371 | 0 | CPLAssert(select_info->column_defs.size() == 1); |
372 | 0 | oComparator.bSortAsc = |
373 | 0 | CPL_TO_BOOL(select_info->order_defs[0].ascending_flag); |
374 | 0 | } |
375 | 0 | if (select_info->column_defs[i].field_type == SWQ_INTEGER || |
376 | 0 | -select_info->column_defs[i].field_type == SWQ_INTEGER64) |
377 | 0 | { |
378 | 0 | oComparator.eType = SWQ_INTEGER64; |
379 | 0 | } |
380 | 0 | else if (select_info->column_defs[i].field_type == SWQ_FLOAT) |
381 | 0 | { |
382 | 0 | oComparator.eType = SWQ_FLOAT; |
383 | 0 | } |
384 | 0 | else |
385 | 0 | { |
386 | 0 | oComparator.eType = SWQ_STRING; |
387 | 0 | } |
388 | 0 | select_info->column_summary[i].oSetDistinctValues = |
389 | 0 | std::set<CPLString, swq_summary::Comparator>(oComparator); |
390 | 0 | } |
391 | 0 | select_info->column_summary[i].min = |
392 | 0 | std::numeric_limits<double>::infinity(); |
393 | 0 | select_info->column_summary[i].max = |
394 | 0 | -std::numeric_limits<double>::infinity(); |
395 | 0 | select_info->column_summary[i].osMin = "9999/99/99 99:99:99"; |
396 | 0 | select_info->column_summary[i].osMax = "0000/00/00 00:00:00"; |
397 | 0 | } |
398 | 0 | assert(!select_info->column_summary.empty()); |
399 | 0 | } |
400 | | |
401 | | /* -------------------------------------------------------------------- */ |
402 | | /* If distinct processing is on, process that now. */ |
403 | | /* -------------------------------------------------------------------- */ |
404 | 0 | swq_summary &summary = select_info->column_summary[dest_column]; |
405 | |
|
406 | 0 | if (def->distinct_flag) |
407 | 0 | { |
408 | 0 | if (pszValue == nullptr) |
409 | 0 | pszValue = SZ_OGR_NULL; |
410 | 0 | try |
411 | 0 | { |
412 | 0 | if (!cpl::contains(summary.oSetDistinctValues, pszValue)) |
413 | 0 | { |
414 | 0 | summary.oSetDistinctValues.insert(pszValue); |
415 | 0 | if (select_info->order_specs == 0) |
416 | 0 | { |
417 | | // If not sorted, keep values in their original order |
418 | 0 | summary.oVectorDistinctValues.emplace_back(pszValue); |
419 | 0 | } |
420 | 0 | summary.count++; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | catch (std::bad_alloc &) |
424 | 0 | { |
425 | 0 | return "Out of memory"; |
426 | 0 | } |
427 | | |
428 | 0 | return nullptr; |
429 | 0 | } |
430 | | |
431 | | /* -------------------------------------------------------------------- */ |
432 | | /* Process various options. */ |
433 | | /* -------------------------------------------------------------------- */ |
434 | | |
435 | 0 | switch (def->col_func) |
436 | 0 | { |
437 | 0 | case SWQCF_MIN: |
438 | 0 | if (pdfValue) |
439 | 0 | { |
440 | 0 | if (*pdfValue < summary.min) |
441 | 0 | summary.min = *pdfValue; |
442 | 0 | summary.count++; |
443 | 0 | } |
444 | 0 | else if (pszValue && pszValue[0] != '\0') |
445 | 0 | { |
446 | 0 | if (summary.count == 0 || strcmp(pszValue, summary.osMin) < 0) |
447 | 0 | { |
448 | 0 | summary.osMin = pszValue; |
449 | 0 | } |
450 | 0 | summary.count++; |
451 | 0 | } |
452 | 0 | break; |
453 | 0 | case SWQCF_MAX: |
454 | 0 | if (pdfValue) |
455 | 0 | { |
456 | 0 | if (*pdfValue > summary.max) |
457 | 0 | summary.max = *pdfValue; |
458 | 0 | summary.count++; |
459 | 0 | } |
460 | 0 | else if (pszValue && pszValue[0] != '\0') |
461 | 0 | { |
462 | 0 | if (summary.count == 0 || strcmp(pszValue, summary.osMax) > 0) |
463 | 0 | { |
464 | 0 | summary.osMax = pszValue; |
465 | 0 | } |
466 | 0 | summary.count++; |
467 | 0 | } |
468 | 0 | break; |
469 | 0 | case SWQCF_AVG: |
470 | 0 | case SWQCF_SUM: |
471 | 0 | if (pdfValue) |
472 | 0 | { |
473 | 0 | summary.count++; |
474 | | |
475 | | // Cf KahanBabushkaNeumaierSum of |
476 | | // https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements |
477 | | // We set a number of temporary variables as volatile, to |
478 | | // prevent potential undesired compiler optimizations. |
479 | |
|
480 | 0 | const double dfNewVal = *pdfValue; |
481 | 0 | const volatile double new_sum_acc = summary.sum_acc + dfNewVal; |
482 | 0 | if (summary.sum_only_finite_terms && std::isfinite(dfNewVal)) |
483 | 0 | { |
484 | 0 | if (std::fabs(summary.sum_acc) >= std::fabs(dfNewVal)) |
485 | 0 | { |
486 | 0 | const volatile double diff = |
487 | 0 | (summary.sum_acc - new_sum_acc); |
488 | 0 | summary.sum_correction += (diff + dfNewVal); |
489 | 0 | } |
490 | 0 | else |
491 | 0 | { |
492 | 0 | const volatile double diff = (dfNewVal - new_sum_acc); |
493 | 0 | summary.sum_correction += (diff + summary.sum_acc); |
494 | 0 | } |
495 | 0 | } |
496 | 0 | else |
497 | 0 | { |
498 | 0 | summary.sum_only_finite_terms = false; |
499 | 0 | } |
500 | 0 | summary.sum_acc = new_sum_acc; |
501 | 0 | } |
502 | 0 | else if (pszValue && pszValue[0] != '\0') |
503 | 0 | { |
504 | 0 | if (def->field_type == SWQ_DATE || |
505 | 0 | def->field_type == SWQ_TIME || |
506 | 0 | def->field_type == SWQ_TIMESTAMP) |
507 | 0 | { |
508 | 0 | OGRField sField; |
509 | 0 | if (OGRParseDate(pszValue, &sField, 0)) |
510 | 0 | { |
511 | 0 | struct tm brokendowntime; |
512 | 0 | brokendowntime.tm_year = sField.Date.Year - 1900; |
513 | 0 | brokendowntime.tm_mon = sField.Date.Month - 1; |
514 | 0 | brokendowntime.tm_mday = sField.Date.Day; |
515 | 0 | brokendowntime.tm_hour = sField.Date.Hour; |
516 | 0 | brokendowntime.tm_min = sField.Date.Minute; |
517 | 0 | brokendowntime.tm_sec = |
518 | 0 | static_cast<int>(sField.Date.Second); |
519 | 0 | summary.count++; |
520 | 0 | summary.sum_acc += CPLYMDHMSToUnixTime(&brokendowntime); |
521 | 0 | summary.sum_acc += |
522 | 0 | fmod(static_cast<double>(sField.Date.Second), 1.0); |
523 | 0 | } |
524 | 0 | } |
525 | 0 | else |
526 | 0 | { |
527 | 0 | return "swq_select_summarize() - AVG()/SUM() called on " |
528 | 0 | "unexpected field type"; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | break; |
532 | | |
533 | 0 | case SWQCF_COUNT: |
534 | 0 | if (pdfValue || pszValue) |
535 | 0 | summary.count++; |
536 | 0 | break; |
537 | | |
538 | 0 | case SWQCF_STDDEV_POP: |
539 | 0 | case SWQCF_STDDEV_SAMP: |
540 | 0 | { |
541 | 0 | const auto UpdateVariance = [&summary](double dfValue) |
542 | 0 | { |
543 | | // Welford's online algorithm for variance: |
544 | | // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm |
545 | 0 | summary.count++; |
546 | 0 | const double dfDelta = dfValue - summary.mean_for_variance; |
547 | 0 | summary.mean_for_variance += dfDelta / summary.count; |
548 | 0 | const double dfDelta2 = dfValue - summary.mean_for_variance; |
549 | 0 | summary.sq_dist_from_mean_acc += dfDelta * dfDelta2; |
550 | 0 | }; |
551 | |
|
552 | 0 | if (pdfValue) |
553 | 0 | { |
554 | 0 | UpdateVariance(*pdfValue); |
555 | 0 | } |
556 | 0 | else if (pszValue && pszValue[0] != '\0') |
557 | 0 | { |
558 | 0 | if (def->field_type == SWQ_DATE || |
559 | 0 | def->field_type == SWQ_TIME || |
560 | 0 | def->field_type == SWQ_TIMESTAMP) |
561 | 0 | { |
562 | 0 | OGRField sField; |
563 | 0 | if (OGRParseDate(pszValue, &sField, 0)) |
564 | 0 | { |
565 | 0 | struct tm brokendowntime; |
566 | 0 | brokendowntime.tm_year = sField.Date.Year - 1900; |
567 | 0 | brokendowntime.tm_mon = sField.Date.Month - 1; |
568 | 0 | brokendowntime.tm_mday = sField.Date.Day; |
569 | 0 | brokendowntime.tm_hour = sField.Date.Hour; |
570 | 0 | brokendowntime.tm_min = sField.Date.Minute; |
571 | 0 | brokendowntime.tm_sec = |
572 | 0 | static_cast<int>(sField.Date.Second); |
573 | |
|
574 | 0 | UpdateVariance(static_cast<double>( |
575 | 0 | CPLYMDHMSToUnixTime(&brokendowntime))); |
576 | 0 | } |
577 | 0 | } |
578 | 0 | else |
579 | 0 | { |
580 | 0 | return "swq_select_summarize() - STDDEV() called on " |
581 | 0 | "unexpected field type"; |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | 0 | break; |
586 | 0 | } |
587 | | |
588 | 0 | case SWQCF_NONE: |
589 | 0 | break; |
590 | | |
591 | 0 | case SWQCF_CUSTOM: |
592 | 0 | return "swq_select_summarize() called on custom field function."; |
593 | 0 | } |
594 | | |
595 | 0 | return nullptr; |
596 | 0 | } |
597 | | |
598 | | /************************************************************************/ |
599 | | /* sort comparison functions. */ |
600 | | /************************************************************************/ |
601 | | |
602 | | static bool Compare(swq_field_type eType, const CPLString &a, |
603 | | const CPLString &b) |
604 | 0 | { |
605 | 0 | if (a == SZ_OGR_NULL) |
606 | 0 | return b != SZ_OGR_NULL; |
607 | 0 | else if (b == SZ_OGR_NULL) |
608 | 0 | return false; |
609 | 0 | else |
610 | 0 | { |
611 | 0 | if (eType == SWQ_INTEGER64) |
612 | 0 | return CPLAtoGIntBig(a) < CPLAtoGIntBig(b); |
613 | 0 | else if (eType == SWQ_FLOAT) |
614 | 0 | return CPLAtof(a) < CPLAtof(b); |
615 | 0 | else if (eType == SWQ_STRING) |
616 | 0 | return a < b; |
617 | 0 | else |
618 | 0 | { |
619 | 0 | CPLAssert(false); |
620 | 0 | return false; |
621 | 0 | } |
622 | 0 | } |
623 | 0 | } |
624 | | |
625 | | #ifndef DOXYGEN_SKIP |
626 | | bool swq_summary::Comparator::operator()(const CPLString &a, |
627 | | const CPLString &b) const |
628 | 0 | { |
629 | 0 | if (bSortAsc) |
630 | 0 | { |
631 | 0 | return Compare(eType, a, b); |
632 | 0 | } |
633 | 0 | else |
634 | 0 | { |
635 | 0 | return Compare(eType, b, a); |
636 | 0 | } |
637 | 0 | } |
638 | | #endif |
639 | | |
640 | | /************************************************************************/ |
641 | | /* swq_identify_field() */ |
642 | | /************************************************************************/ |
643 | | int swq_identify_field_internal(const char *table_name, const char *field_token, |
644 | | swq_field_list *field_list, |
645 | | swq_field_type *this_type, int *table_id, |
646 | | int bOneMoreTimeOK); |
647 | | |
648 | | int swq_identify_field(const char *table_name, const char *field_token, |
649 | | swq_field_list *field_list, swq_field_type *this_type, |
650 | | int *table_id) |
651 | | |
652 | 0 | { |
653 | 0 | return swq_identify_field_internal(table_name, field_token, field_list, |
654 | 0 | this_type, table_id, TRUE); |
655 | 0 | } |
656 | | |
657 | | int swq_identify_field_internal(const char *table_name, const char *field_token, |
658 | | swq_field_list *field_list, |
659 | | swq_field_type *this_type, int *table_id, |
660 | | int bOneMoreTimeOK) |
661 | | |
662 | 0 | { |
663 | 0 | if (table_name == nullptr) |
664 | 0 | table_name = ""; |
665 | |
|
666 | 0 | int tables_enabled; |
667 | |
|
668 | 0 | if (field_list->table_count > 0 && field_list->table_ids != nullptr) |
669 | 0 | tables_enabled = TRUE; |
670 | 0 | else |
671 | 0 | tables_enabled = FALSE; |
672 | | |
673 | | /* -------------------------------------------------------------------- */ |
674 | | /* Search for matching field. */ |
675 | | /* -------------------------------------------------------------------- */ |
676 | 0 | for (int pass = 0; pass < 2; ++pass) |
677 | 0 | { |
678 | 0 | for (int i = 0; i < field_list->count; i++) |
679 | 0 | { |
680 | 0 | if ((pass == 0 && strcmp(field_list->names[i], field_token) != 0) || |
681 | 0 | (pass == 1 && !EQUAL(field_list->names[i], field_token))) |
682 | 0 | { |
683 | 0 | continue; |
684 | 0 | } |
685 | | |
686 | 0 | int t_id = 0; |
687 | | |
688 | | // Do the table specifications match?/ |
689 | 0 | if (tables_enabled) |
690 | 0 | { |
691 | 0 | t_id = field_list->table_ids[i]; |
692 | 0 | if (table_name[0] != '\0' && |
693 | 0 | !EQUAL(table_name, |
694 | 0 | field_list->table_defs[t_id].table_alias)) |
695 | 0 | continue; |
696 | | |
697 | | // if( t_id != 0 && table_name[0] == '\0' ) |
698 | | // continue; |
699 | 0 | } |
700 | 0 | else if (table_name[0] != '\0') |
701 | 0 | break; |
702 | | |
703 | | // We have a match, return various information. |
704 | 0 | if (this_type != nullptr) |
705 | 0 | { |
706 | 0 | if (field_list->types != nullptr) |
707 | 0 | *this_type = field_list->types[i]; |
708 | 0 | else |
709 | 0 | *this_type = SWQ_OTHER; |
710 | 0 | } |
711 | |
|
712 | 0 | if (table_id != nullptr) |
713 | 0 | *table_id = t_id; |
714 | |
|
715 | 0 | if (field_list->ids == nullptr) |
716 | 0 | return i; |
717 | 0 | else |
718 | 0 | return field_list->ids[i]; |
719 | 0 | } |
720 | 0 | } |
721 | | |
722 | | /* -------------------------------------------------------------------- */ |
723 | | /* When there is no ambiguity, try to accept quoting errors... */ |
724 | | /* -------------------------------------------------------------------- */ |
725 | 0 | if (bOneMoreTimeOK && |
726 | 0 | !CPLTestBool(CPLGetConfigOption("OGR_SQL_STRICT", "FALSE"))) |
727 | 0 | { |
728 | 0 | if (table_name[0]) |
729 | 0 | { |
730 | 0 | CPLString osAggregatedName( |
731 | 0 | CPLSPrintf("%s.%s", table_name, field_token)); |
732 | | |
733 | | // Check there's no table called table_name, or a field called with |
734 | | // the aggregated name. |
735 | 0 | int i = 0; // Used after for. |
736 | 0 | for (; i < field_list->count; i++) |
737 | 0 | { |
738 | 0 | if (tables_enabled) |
739 | 0 | { |
740 | 0 | int t_id = field_list->table_ids[i]; |
741 | 0 | if (EQUAL(table_name, |
742 | 0 | field_list->table_defs[t_id].table_alias)) |
743 | 0 | break; |
744 | 0 | } |
745 | 0 | } |
746 | 0 | if (i == field_list->count) |
747 | 0 | { |
748 | 0 | int ret = swq_identify_field_internal(nullptr, osAggregatedName, |
749 | 0 | field_list, this_type, |
750 | 0 | table_id, FALSE); |
751 | 0 | if (ret >= 0) |
752 | 0 | { |
753 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
754 | 0 | "Passed field name %s.%s should have been " |
755 | 0 | "surrounded by double quotes. " |
756 | 0 | "Accepted since there is no ambiguity...", |
757 | 0 | table_name, field_token); |
758 | 0 | } |
759 | 0 | return ret; |
760 | 0 | } |
761 | 0 | } |
762 | 0 | else |
763 | 0 | { |
764 | | // If the fieldname is a.b (and there's no . in b), then |
765 | | // it might be an error in providing it as being quoted where it |
766 | | // should not have been quoted. |
767 | 0 | const char *pszDot = strchr(field_token, '.'); |
768 | 0 | if (pszDot && strchr(pszDot + 1, '.') == nullptr) |
769 | 0 | { |
770 | 0 | CPLString osTableName(field_token); |
771 | 0 | osTableName.resize(pszDot - field_token); |
772 | 0 | CPLString osFieldName(pszDot + 1); |
773 | |
|
774 | 0 | int ret = swq_identify_field_internal(osTableName, osFieldName, |
775 | 0 | field_list, this_type, |
776 | 0 | table_id, FALSE); |
777 | 0 | if (ret >= 0) |
778 | 0 | { |
779 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
780 | 0 | "Passed field name %s should NOT have been " |
781 | 0 | "surrounded by double quotes. " |
782 | 0 | "Accepted since there is no ambiguity...", |
783 | 0 | field_token); |
784 | 0 | } |
785 | 0 | return ret; |
786 | 0 | } |
787 | 0 | } |
788 | 0 | } |
789 | | |
790 | | /* -------------------------------------------------------------------- */ |
791 | | /* No match, return failure. */ |
792 | | /* -------------------------------------------------------------------- */ |
793 | 0 | if (this_type != nullptr) |
794 | 0 | *this_type = SWQ_OTHER; |
795 | |
|
796 | 0 | if (table_id != nullptr) |
797 | 0 | *table_id = 0; |
798 | |
|
799 | 0 | return -1; |
800 | 0 | } |
801 | | |
802 | | /************************************************************************/ |
803 | | /* swq_expr_compile() */ |
804 | | /************************************************************************/ |
805 | | |
806 | | CPLErr swq_expr_compile(const char *where_clause, int field_count, |
807 | | char **field_names, swq_field_type *field_types, |
808 | | int bCheck, |
809 | | swq_custom_func_registrar *poCustomFuncRegistrar, |
810 | | swq_expr_node **expr_out) |
811 | | |
812 | 0 | { |
813 | 0 | swq_field_list field_list; |
814 | |
|
815 | 0 | field_list.count = field_count; |
816 | 0 | field_list.names = field_names; |
817 | 0 | field_list.types = field_types; |
818 | 0 | field_list.table_ids = nullptr; |
819 | 0 | field_list.ids = nullptr; |
820 | |
|
821 | 0 | field_list.table_count = 0; |
822 | 0 | field_list.table_defs = nullptr; |
823 | |
|
824 | 0 | return swq_expr_compile2(where_clause, &field_list, bCheck, |
825 | 0 | poCustomFuncRegistrar, expr_out); |
826 | 0 | } |
827 | | |
828 | | /************************************************************************/ |
829 | | /* swq_fixup_expression() */ |
830 | | /************************************************************************/ |
831 | | |
832 | | void swq_fixup(swq_parse_context *psParseContext) |
833 | 0 | { |
834 | 0 | if (psParseContext->poRoot) |
835 | 0 | { |
836 | 0 | psParseContext->poRoot->RebalanceAndOr(); |
837 | 0 | } |
838 | 0 | auto psSelect = psParseContext->poCurSelect; |
839 | 0 | while (psSelect) |
840 | 0 | { |
841 | 0 | if (psSelect->where_expr) |
842 | 0 | { |
843 | 0 | psSelect->where_expr->RebalanceAndOr(); |
844 | 0 | } |
845 | 0 | psSelect = psSelect->poOtherSelect; |
846 | 0 | } |
847 | 0 | } |
848 | | |
849 | | /************************************************************************/ |
850 | | /* swq_create_and_or_or() */ |
851 | | /************************************************************************/ |
852 | | |
853 | | swq_expr_node *swq_create_and_or_or(swq_op op, swq_expr_node *left, |
854 | | swq_expr_node *right) |
855 | 0 | { |
856 | 0 | auto poNode = new swq_expr_node(op); |
857 | 0 | poNode->field_type = SWQ_BOOLEAN; |
858 | |
|
859 | 0 | if (left->eNodeType == SNT_OPERATION && left->nOperation == op) |
860 | 0 | { |
861 | | // Temporary non-binary formulation |
862 | 0 | if (right->eNodeType == SNT_OPERATION && right->nOperation == op) |
863 | 0 | { |
864 | 0 | poNode->nSubExprCount = left->nSubExprCount + right->nSubExprCount; |
865 | 0 | poNode->papoSubExpr = static_cast<swq_expr_node **>( |
866 | 0 | CPLRealloc(left->papoSubExpr, |
867 | 0 | sizeof(swq_expr_node *) * poNode->nSubExprCount)); |
868 | 0 | memcpy(poNode->papoSubExpr + left->nSubExprCount, |
869 | 0 | right->papoSubExpr, |
870 | 0 | right->nSubExprCount * sizeof(swq_expr_node *)); |
871 | |
|
872 | 0 | right->nSubExprCount = 0; |
873 | 0 | CPLFree(right->papoSubExpr); |
874 | 0 | right->papoSubExpr = nullptr; |
875 | 0 | delete right; |
876 | 0 | } |
877 | 0 | else |
878 | 0 | { |
879 | 0 | poNode->nSubExprCount = left->nSubExprCount; |
880 | 0 | poNode->papoSubExpr = left->papoSubExpr; |
881 | 0 | poNode->PushSubExpression(right); |
882 | 0 | } |
883 | |
|
884 | 0 | left->nSubExprCount = 0; |
885 | 0 | left->papoSubExpr = nullptr; |
886 | 0 | delete left; |
887 | 0 | } |
888 | 0 | else if (right->eNodeType == SNT_OPERATION && right->nOperation == op) |
889 | 0 | { |
890 | | // Temporary non-binary formulation |
891 | 0 | poNode->nSubExprCount = right->nSubExprCount; |
892 | 0 | poNode->papoSubExpr = right->papoSubExpr; |
893 | 0 | poNode->PushSubExpression(left); |
894 | |
|
895 | 0 | right->nSubExprCount = 0; |
896 | 0 | right->papoSubExpr = nullptr; |
897 | 0 | delete right; |
898 | 0 | } |
899 | 0 | else |
900 | 0 | { |
901 | 0 | poNode->PushSubExpression(left); |
902 | 0 | poNode->PushSubExpression(right); |
903 | 0 | } |
904 | |
|
905 | 0 | return poNode; |
906 | 0 | } |
907 | | |
908 | | /************************************************************************/ |
909 | | /* swq_expr_compile2() */ |
910 | | /************************************************************************/ |
911 | | |
912 | | CPLErr swq_expr_compile2(const char *where_clause, swq_field_list *field_list, |
913 | | int bCheck, |
914 | | swq_custom_func_registrar *poCustomFuncRegistrar, |
915 | | swq_expr_node **expr_out) |
916 | | |
917 | 0 | { |
918 | 0 | swq_parse_context context; |
919 | |
|
920 | 0 | context.pszInput = where_clause; |
921 | 0 | context.pszNext = where_clause; |
922 | 0 | context.pszLastValid = where_clause; |
923 | 0 | context.nStartToken = SWQT_VALUE_START; |
924 | 0 | context.bAcceptCustomFuncs = poCustomFuncRegistrar != nullptr; |
925 | |
|
926 | 0 | if (swqparse(&context) == 0 && bCheck && |
927 | 0 | context.poRoot->Check(field_list, FALSE, FALSE, |
928 | 0 | poCustomFuncRegistrar) != SWQ_ERROR) |
929 | 0 | { |
930 | 0 | *expr_out = context.poRoot; |
931 | |
|
932 | 0 | return CE_None; |
933 | 0 | } |
934 | 0 | else |
935 | 0 | { |
936 | 0 | delete context.poRoot; |
937 | 0 | *expr_out = nullptr; |
938 | 0 | return CE_Failure; |
939 | 0 | } |
940 | 0 | } |
941 | | |
942 | | /************************************************************************/ |
943 | | /* swq_is_reserved_keyword() */ |
944 | | /************************************************************************/ |
945 | | |
946 | | static const char *const apszSQLReservedKeywords[] = { |
947 | | "OR", "AND", "NOT", "LIKE", "IS", "NULL", "IN", "BETWEEN", |
948 | | "CAST", "DISTINCT", "ESCAPE", "SELECT", "LEFT", "JOIN", "WHERE", "ON", |
949 | | "ORDER", "BY", "FROM", "AS", "ASC", "DESC", "UNION", "ALL"}; |
950 | | |
951 | | int swq_is_reserved_keyword(const char *pszStr) |
952 | 0 | { |
953 | 0 | for (const auto &pszKeyword : apszSQLReservedKeywords) |
954 | 0 | { |
955 | 0 | if (EQUAL(pszStr, pszKeyword)) |
956 | 0 | return TRUE; |
957 | 0 | } |
958 | 0 | return FALSE; |
959 | 0 | } |
960 | | |
961 | | /************************************************************************/ |
962 | | /* SWQFieldTypeToString() */ |
963 | | /************************************************************************/ |
964 | | |
965 | | const char *SWQFieldTypeToString(swq_field_type field_type) |
966 | 0 | { |
967 | 0 | switch (field_type) |
968 | 0 | { |
969 | 0 | case SWQ_INTEGER: |
970 | 0 | return "integer"; |
971 | 0 | case SWQ_INTEGER64: |
972 | 0 | return "bigint"; |
973 | 0 | case SWQ_FLOAT: |
974 | 0 | return "float"; |
975 | 0 | case SWQ_STRING: |
976 | 0 | return "string"; |
977 | 0 | case SWQ_BOOLEAN: |
978 | 0 | return "boolean"; |
979 | 0 | case SWQ_DATE: |
980 | 0 | return "date"; |
981 | 0 | case SWQ_TIME: |
982 | 0 | return "time"; |
983 | 0 | case SWQ_TIMESTAMP: |
984 | 0 | return "timestamp"; |
985 | 0 | case SWQ_GEOMETRY: |
986 | 0 | return "geometry"; |
987 | 0 | case SWQ_NULL: |
988 | 0 | return "null"; |
989 | 0 | default: |
990 | 0 | return "unknown"; |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | //! @cond Doxygen_Suppress |
995 | | |
996 | 0 | swq_custom_func_registrar::~swq_custom_func_registrar() = default; |
997 | | |
998 | | //! @endcond |