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