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 : *
10 : * Permission to use, copy, modify and distribute this software and
11 : * its documentation for any purpose and without fee is hereby granted,
12 : * provided that the above copyright notice appear in all copies, that
13 : * both the copyright notice and this permission notice appear in
14 : * supporting documentation, and that the name of 3i not be used
15 : * in advertising or publicity pertaining to distribution of the software
16 : * without specific, written prior permission. 3i makes no
17 : * representations about the suitability of this software for any purpose.
18 : * It is provided "as is" without express or implied warranty.
19 : ****************************************************************************/
20 :
21 : #include <ctype.h>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_multiproc.h"
25 : #include "swq.h"
26 : #include "swq_parser.hpp"
27 :
28 : #define YYSTYPE swq_expr_node*
29 :
30 : /************************************************************************/
31 : /* swqlex() */
32 : /* */
33 : /* Read back a token from the input. */
34 : /************************************************************************/
35 :
36 7404 : int swqlex( YYSTYPE *ppNode, swq_parse_context *context )
37 : {
38 7404 : const char *pszInput = context->pszNext;
39 :
40 7404 : *ppNode = NULL;
41 :
42 : /* -------------------------------------------------------------------- */
43 : /* Do we have a start symbol to return? */
44 : /* -------------------------------------------------------------------- */
45 7404 : if( context->nStartToken != 0 )
46 : {
47 641 : int nRet = context->nStartToken;
48 641 : context->nStartToken = 0;
49 641 : return nRet;
50 : }
51 :
52 : /* -------------------------------------------------------------------- */
53 : /* Skip white space. */
54 : /* -------------------------------------------------------------------- */
55 15983 : while( *pszInput == ' ' || *pszInput == '\t'
56 : || *pszInput == 10 || *pszInput == 13 )
57 2457 : pszInput++;
58 :
59 6763 : if( *pszInput == '\0' )
60 : {
61 599 : context->pszNext = pszInput;
62 599 : return EOF;
63 : }
64 :
65 : /* -------------------------------------------------------------------- */
66 : /* Handle string constants. */
67 : /* -------------------------------------------------------------------- */
68 6164 : if( *pszInput == '"' || *pszInput == '\'' )
69 : {
70 : char *token;
71 : int i_token;
72 :
73 218 : pszInput++;
74 :
75 218 : token = (char *) CPLMalloc(strlen(pszInput)+1);
76 218 : i_token = 0;
77 :
78 2339 : while( *pszInput != '\0' )
79 : {
80 2120 : if( *pszInput == '\\' && pszInput[1] == '"' )
81 0 : pszInput++;
82 2120 : else if( *pszInput == '\\' && pszInput[1] == '\'' )
83 0 : pszInput++;
84 2120 : else if( *pszInput == '\'' && pszInput[1] == '\'' )
85 0 : pszInput++;
86 2120 : else if( *pszInput == '"' )
87 : {
88 27 : pszInput++;
89 27 : break;
90 : }
91 2093 : else if( *pszInput == '\'' )
92 : {
93 190 : pszInput++;
94 190 : break;
95 : }
96 :
97 1903 : token[i_token++] = *(pszInput++);
98 : }
99 218 : token[i_token] = '\0';
100 :
101 218 : *ppNode = new swq_expr_node( token );
102 218 : CPLFree( token );
103 :
104 218 : context->pszNext = pszInput;
105 :
106 218 : return SWQT_STRING;
107 : }
108 :
109 : /* -------------------------------------------------------------------- */
110 : /* Handle numbers. */
111 : /* -------------------------------------------------------------------- */
112 5946 : else if( *pszInput >= '0' && *pszInput <= '9' )
113 : {
114 341 : CPLString osToken;
115 341 : const char *pszNext = pszInput + 1;
116 :
117 341 : osToken += *pszInput;
118 :
119 : // collect non-decimal part of number
120 1106 : while( *pszNext >= '0' && *pszNext <= '9' )
121 424 : osToken += *(pszNext++);
122 :
123 : // collect decimal places.
124 341 : if( *pszNext == '.' )
125 : {
126 27 : osToken += *(pszNext++);
127 105 : while( *pszNext >= '0' && *pszNext <= '9' )
128 51 : osToken += *(pszNext++);
129 : }
130 :
131 : // collect exponent
132 341 : if( *pszNext == 'e' || *pszNext == 'E' )
133 : {
134 1 : osToken += *(pszNext++);
135 1 : if( *pszNext == '-' || *pszNext == '+' )
136 1 : osToken += *(pszNext++);
137 3 : while( *pszNext >= '0' && *pszNext <= '9' )
138 1 : osToken += *(pszNext++);
139 : }
140 :
141 341 : context->pszNext = pszNext;
142 :
143 341 : if( strstr(osToken,".")
144 : || strstr(osToken,"e")
145 : || strstr(osToken,"E") )
146 : {
147 28 : *ppNode = new swq_expr_node( CPLAtof(osToken) );
148 : }
149 : else
150 : {
151 313 : *ppNode = new swq_expr_node( atoi(osToken) );
152 : }
153 :
154 341 : return SWQT_NUMBER;
155 : }
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Handle alpha-numerics. */
159 : /* -------------------------------------------------------------------- */
160 5605 : else if( isalnum( *pszInput ) )
161 : {
162 3229 : int nReturn = SWQT_IDENTIFIER;
163 3229 : CPLString osToken;
164 3229 : const char *pszNext = pszInput + 1;
165 :
166 3229 : osToken += *pszInput;
167 :
168 : // collect text characters
169 18838 : while( isalnum( *pszNext ) || *pszNext == '_'
170 : || ((unsigned char) *pszNext) > 127 )
171 12380 : osToken += *(pszNext++);
172 :
173 3229 : context->pszNext = pszNext;
174 :
175 3229 : if( EQUAL(osToken,"IN") )
176 36 : nReturn = SWQT_IN;
177 3193 : else if( EQUAL(osToken,"LIKE") )
178 12 : nReturn = SWQT_LIKE;
179 3181 : else if( EQUAL(osToken,"ILIKE") )
180 1 : nReturn = SWQT_LIKE;
181 3180 : else if( EQUAL(osToken,"ESCAPE") )
182 2 : nReturn = SWQT_ESCAPE;
183 3178 : else if( EQUAL(osToken,"NULL") )
184 15 : nReturn = SWQT_NULL;
185 3163 : else if( EQUAL(osToken,"IS") )
186 14 : nReturn = SWQT_IS;
187 3149 : else if( EQUAL(osToken,"NOT") )
188 16 : nReturn = SWQT_NOT;
189 3133 : else if( EQUAL(osToken,"AND") )
190 43 : nReturn = SWQT_AND;
191 3090 : else if( EQUAL(osToken,"OR") )
192 6 : nReturn = SWQT_OR;
193 3084 : else if( EQUAL(osToken,"BETWEEN") )
194 4 : nReturn = SWQT_BETWEEN;
195 3080 : else if( EQUAL(osToken,"SELECT") )
196 299 : nReturn = SWQT_SELECT;
197 2781 : else if( EQUAL(osToken,"LEFT") )
198 25 : nReturn = SWQT_LEFT;
199 2756 : else if( EQUAL(osToken,"JOIN") )
200 24 : nReturn = SWQT_JOIN;
201 2732 : else if( EQUAL(osToken,"WHERE") )
202 82 : nReturn = SWQT_WHERE;
203 2650 : else if( EQUAL(osToken,"ON") )
204 22 : nReturn = SWQT_ON;
205 2628 : else if( EQUAL(osToken,"ORDER") )
206 24 : nReturn = SWQT_ORDER;
207 2604 : else if( EQUAL(osToken,"BY") )
208 24 : nReturn = SWQT_BY;
209 2580 : else if( EQUAL(osToken,"FROM") )
210 264 : nReturn = SWQT_FROM;
211 2316 : else if( EQUAL(osToken,"AS") )
212 53 : nReturn = SWQT_AS;
213 2263 : else if( EQUAL(osToken,"ASC") )
214 11 : nReturn = SWQT_ASC;
215 2252 : else if( EQUAL(osToken,"DESC") )
216 7 : nReturn = SWQT_DESC;
217 2245 : else if( EQUAL(osToken,"DISTINCT") )
218 29 : nReturn = SWQT_DISTINCT;
219 2216 : else if( EQUAL(osToken,"CAST") )
220 43 : nReturn = SWQT_CAST;
221 : else
222 : {
223 2173 : *ppNode = new swq_expr_node( osToken );
224 2173 : nReturn = SWQT_IDENTIFIER;
225 : }
226 :
227 3229 : return nReturn;
228 : }
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Handle special tokens. */
232 : /* -------------------------------------------------------------------- */
233 : else
234 : {
235 2376 : context->pszNext = pszInput+1;
236 2376 : return *pszInput;
237 : }
238 : }
239 :
240 : /************************************************************************/
241 : /* swq_select_summarize() */
242 : /************************************************************************/
243 :
244 : const char *
245 244 : swq_select_summarize( swq_select *select_info,
246 : int dest_column, const char *value )
247 :
248 : {
249 244 : swq_col_def *def = select_info->column_defs + dest_column;
250 : swq_summary *summary;
251 :
252 : /* -------------------------------------------------------------------- */
253 : /* Do various checking. */
254 : /* -------------------------------------------------------------------- */
255 244 : if( select_info->query_mode == SWQM_RECORDSET )
256 0 : return "swq_select_summarize() called on non-summary query.";
257 :
258 244 : if( dest_column < 0 || dest_column >= select_info->result_columns )
259 0 : return "dest_column out of range in swq_select_summarize().";
260 :
261 244 : if( def->col_func == SWQCF_NONE && !def->distinct_flag )
262 0 : return NULL;
263 :
264 : /* -------------------------------------------------------------------- */
265 : /* Create the summary information if this is the first row */
266 : /* being processed. */
267 : /* -------------------------------------------------------------------- */
268 244 : if( select_info->column_summary == NULL && value != NULL )
269 : {
270 : int i;
271 :
272 : select_info->column_summary = (swq_summary *)
273 19 : CPLMalloc(sizeof(swq_summary) * select_info->result_columns);
274 : memset( select_info->column_summary, 0,
275 19 : sizeof(swq_summary) * select_info->result_columns );
276 :
277 46 : for( i = 0; i < select_info->result_columns; i++ )
278 : {
279 27 : select_info->column_summary[i].min = 1e20;
280 27 : select_info->column_summary[i].max = -1e20;
281 : }
282 : }
283 :
284 : /* -------------------------------------------------------------------- */
285 : /* If distinct processing is on, process that now. */
286 : /* -------------------------------------------------------------------- */
287 244 : summary = select_info->column_summary + dest_column;
288 :
289 244 : if( def->distinct_flag )
290 : {
291 : int i;
292 :
293 : /* This should be implemented with a much more complicated
294 : data structure to achieve any sort of efficiency. */
295 801 : for( i = 0; i < summary->count; i++ )
296 : {
297 735 : if( value == NULL )
298 : {
299 92 : if (summary->distinct_list[i] == NULL)
300 6 : break;
301 : }
302 1286 : else if( summary->distinct_list[i] != NULL &&
303 643 : strcmp(value,summary->distinct_list[i]) == 0 )
304 125 : break;
305 : }
306 :
307 197 : if( i == summary->count )
308 : {
309 66 : char **old_list = summary->distinct_list;
310 :
311 : summary->distinct_list = (char **)
312 66 : CPLMalloc(sizeof(char *) * (summary->count+1));
313 : memcpy( summary->distinct_list, old_list,
314 66 : sizeof(char *) * summary->count );
315 132 : summary->distinct_list[(summary->count)++] =
316 66 : (value != NULL) ? CPLStrdup( value ) : NULL;
317 :
318 66 : CPLFree(old_list);
319 : }
320 : }
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Process various options. */
324 : /* -------------------------------------------------------------------- */
325 :
326 244 : switch( def->col_func )
327 : {
328 : case SWQCF_MIN:
329 10 : if( value != NULL && value[0] != '\0' )
330 : {
331 10 : double df_val = CPLAtof(value);
332 10 : if( df_val < summary->min )
333 3 : summary->min = df_val;
334 : }
335 10 : break;
336 : case SWQCF_MAX:
337 7 : if( value != NULL && value[0] != '\0' )
338 : {
339 7 : double df_val = CPLAtof(value);
340 7 : if( df_val > summary->max )
341 2 : summary->max = df_val;
342 : }
343 7 : break;
344 : case SWQCF_AVG:
345 : case SWQCF_SUM:
346 2 : if( value != NULL && value[0] != '\0' )
347 : {
348 0 : summary->count++;
349 0 : summary->sum += CPLAtof(value);
350 : }
351 2 : break;
352 :
353 : case SWQCF_COUNT:
354 40 : if( value != NULL && !def->distinct_flag )
355 28 : summary->count++;
356 40 : break;
357 :
358 : case SWQCF_NONE:
359 185 : break;
360 :
361 : case SWQCF_CUSTOM:
362 0 : return "swq_select_summarize() called on custom field function.";
363 :
364 : default:
365 0 : return "swq_select_summarize() - unexpected col_func";
366 : }
367 :
368 244 : return NULL;
369 : }
370 : /************************************************************************/
371 : /* sort comparison functions. */
372 : /************************************************************************/
373 :
374 52 : static int FORCE_CDECL swq_compare_int( const void *item1, const void *item2 )
375 : {
376 : int v1, v2;
377 :
378 52 : const char* pszStr1 = *((const char **) item1);
379 52 : const char* pszStr2 = *((const char **) item2);
380 52 : if (pszStr1 == NULL)
381 0 : return (pszStr2 == NULL) ? 0 : -1;
382 52 : else if (pszStr2 == NULL)
383 10 : return 1;
384 :
385 42 : v1 = atoi(pszStr1);
386 42 : v2 = atoi(pszStr2);
387 :
388 42 : if( v1 < v2 )
389 16 : return -1;
390 26 : else if( v1 == v2 )
391 0 : return 0;
392 : else
393 26 : return 1;
394 : }
395 :
396 25 : static int FORCE_CDECL swq_compare_real( const void *item1, const void *item2 )
397 : {
398 : double v1, v2;
399 :
400 25 : const char* pszStr1 = *((const char **) item1);
401 25 : const char* pszStr2 = *((const char **) item2);
402 25 : if (pszStr1 == NULL)
403 0 : return (pszStr2 == NULL) ? 0 : -1;
404 25 : else if (pszStr2 == NULL)
405 2 : return 1;
406 :
407 23 : v1 = CPLAtof(pszStr1);
408 23 : v2 = CPLAtof(pszStr2);
409 :
410 23 : if( v1 < v2 )
411 9 : return -1;
412 14 : else if( v1 == v2 )
413 0 : return 0;
414 : else
415 14 : return 1;
416 : }
417 :
418 14 : static int FORCE_CDECL swq_compare_string( const void *item1, const void *item2 )
419 : {
420 14 : const char* pszStr1 = *((const char **) item1);
421 14 : const char* pszStr2 = *((const char **) item2);
422 14 : if (pszStr1 == NULL)
423 0 : return (pszStr2 == NULL) ? 0 : -1;
424 14 : else if (pszStr2 == NULL)
425 2 : return 1;
426 :
427 12 : return strcmp( pszStr1, pszStr2 );
428 : }
429 :
430 : /************************************************************************/
431 : /* swq_select_finish_summarize() */
432 : /* */
433 : /* Call to complete summarize work. Does stuff like ordering */
434 : /* the distinct list for instance. */
435 : /************************************************************************/
436 :
437 24 : const char *swq_select_finish_summarize( swq_select *select_info )
438 :
439 : {
440 : int (FORCE_CDECL *compare_func)(const void *, const void*);
441 24 : int count = 0;
442 24 : char **distinct_list = NULL;
443 :
444 24 : if( select_info->query_mode != SWQM_DISTINCT_LIST
445 : || select_info->order_specs == 0 )
446 13 : return NULL;
447 :
448 11 : if( select_info->order_specs > 1 )
449 0 : return "Can't ORDER BY a DISTINCT list by more than one key.";
450 :
451 22 : if( select_info->order_defs[0].field_index !=
452 11 : select_info->column_defs[0].field_index )
453 0 : return "Only selected DISTINCT field can be used for ORDER BY.";
454 :
455 11 : if( select_info->column_summary == NULL )
456 0 : return NULL;
457 :
458 11 : if( select_info->column_defs[0].field_type == SWQ_INTEGER )
459 4 : compare_func = swq_compare_int;
460 7 : else if( select_info->column_defs[0].field_type == SWQ_FLOAT )
461 4 : compare_func = swq_compare_real;
462 : else
463 3 : compare_func = swq_compare_string;
464 :
465 11 : distinct_list = select_info->column_summary[0].distinct_list;
466 11 : count = select_info->column_summary[0].count;
467 :
468 11 : qsort( distinct_list, count, sizeof(char *), compare_func );
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Do we want the list ascending in stead of descending? */
472 : /* -------------------------------------------------------------------- */
473 11 : if( !select_info->order_defs[0].ascending_flag )
474 : {
475 : char *saved;
476 : int i;
477 :
478 21 : for( i = 0; i < count/2; i++ )
479 : {
480 15 : saved = distinct_list[i];
481 15 : distinct_list[i] = distinct_list[count-i-1];
482 15 : distinct_list[count-i-1] = saved;
483 : }
484 : }
485 :
486 11 : return NULL;
487 : }
488 :
489 : /************************************************************************/
490 : /* swq_select_free() */
491 : /************************************************************************/
492 :
493 0 : void swq_select_free( swq_select *select_info )
494 :
495 : {
496 0 : delete select_info;
497 0 : }
498 :
499 : /************************************************************************/
500 : /* swq_identify_field() */
501 : /************************************************************************/
502 :
503 2134 : int swq_identify_field( const char *token, swq_field_list *field_list,
504 : swq_field_type *this_type, int *table_id )
505 :
506 : {
507 : int i;
508 : char table_name[128];
509 2134 : const char *field_token = token;
510 : int tables_enabled;
511 :
512 3724 : if( field_list->table_count > 0 && field_list->table_ids != NULL )
513 1590 : tables_enabled = TRUE;
514 : else
515 544 : tables_enabled = FALSE;
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* Parse out table name if present, and table support enabled. */
519 : /* -------------------------------------------------------------------- */
520 2134 : table_name[0] = '\0';
521 2134 : if( tables_enabled && strchr(token, '.') != NULL )
522 : {
523 93 : int dot_offset = (int)(strchr(token,'.') - token);
524 :
525 93 : if( dot_offset < (int) sizeof(table_name) )
526 : {
527 93 : strncpy( table_name, token, dot_offset );
528 93 : table_name[dot_offset] = '\0';
529 93 : field_token = token + dot_offset + 1;
530 : }
531 : }
532 :
533 : /* -------------------------------------------------------------------- */
534 : /* Search for matching field. */
535 : /* -------------------------------------------------------------------- */
536 6452 : for( i = 0; i < field_list->count; i++ )
537 : {
538 6172 : int t_id = 0;
539 :
540 6172 : if( !EQUAL( field_list->names[i], field_token ) )
541 4287 : continue;
542 :
543 : /* Do the table specifications match? */
544 1885 : if( tables_enabled )
545 : {
546 1501 : t_id = field_list->table_ids[i];
547 1622 : if( table_name[0] != '\0'
548 121 : && !EQUAL(table_name,field_list->table_defs[t_id].table_alias))
549 31 : continue;
550 :
551 : // if( t_id != 0 && table_name[0] == '\0' )
552 : // continue;
553 : }
554 :
555 : /* We have a match, return various information */
556 1854 : if( this_type != NULL )
557 : {
558 1797 : if( field_list->types != NULL )
559 1797 : *this_type = field_list->types[i];
560 : else
561 0 : *this_type = SWQ_OTHER;
562 : }
563 :
564 1854 : if( table_id != NULL )
565 1854 : *table_id = t_id;
566 :
567 1854 : if( field_list->ids == NULL )
568 384 : return i;
569 : else
570 1470 : return field_list->ids[i];
571 : }
572 :
573 : /* -------------------------------------------------------------------- */
574 : /* No match, return failure. */
575 : /* -------------------------------------------------------------------- */
576 280 : if( this_type != NULL )
577 277 : *this_type = SWQ_OTHER;
578 :
579 280 : if( table_id != NULL )
580 280 : *table_id = 0;
581 :
582 280 : return -1;
583 : }
584 :
585 : /************************************************************************/
586 : /* swq_expr_compile() */
587 : /************************************************************************/
588 :
589 338 : CPLErr swq_expr_compile( const char *where_clause,
590 : int field_count,
591 : char **field_names,
592 : swq_field_type *field_types,
593 : swq_expr_node **expr_out )
594 :
595 : {
596 : swq_field_list field_list;
597 :
598 338 : field_list.count = field_count;
599 338 : field_list.names = field_names;
600 338 : field_list.types = field_types;
601 338 : field_list.table_ids = NULL;
602 338 : field_list.ids = NULL;
603 :
604 338 : field_list.table_count = 0;
605 338 : field_list.table_defs = NULL;
606 :
607 338 : return swq_expr_compile2( where_clause, &field_list, expr_out );
608 : }
609 :
610 :
611 : /************************************************************************/
612 : /* swq_expr_compile2() */
613 : /************************************************************************/
614 :
615 338 : CPLErr swq_expr_compile2( const char *where_clause,
616 : swq_field_list *field_list,
617 : swq_expr_node **expr_out )
618 :
619 : {
620 :
621 338 : swq_parse_context context;
622 :
623 338 : context.pszInput = where_clause;
624 338 : context.pszNext = where_clause;
625 338 : context.nStartToken = SWQT_LOGICAL_START;
626 :
627 338 : if( swqparse( &context ) == 0
628 : && context.poRoot->Check( field_list ) != SWQ_ERROR )
629 : {
630 338 : *expr_out = context.poRoot;
631 :
632 338 : return CE_None;
633 : }
634 : else
635 : {
636 0 : delete context.poRoot;
637 0 : *expr_out = NULL;
638 0 : return CE_Failure;
639 : }
640 : }
641 :
642 : /************************************************************************/
643 : /* swq_is_reserved_keyword() */
644 : /************************************************************************/
645 :
646 : static const char* apszSQLReservedKeywords[] = {
647 : "OR",
648 : "AND",
649 : "NOT",
650 : "LIKE",
651 : "IS",
652 : "NULL",
653 : "IN",
654 : "BETWEEN",
655 : "CAST",
656 : "DISTINCT",
657 : "ESCAPE",
658 : "SELECT",
659 : "LEFT",
660 : "JOIN",
661 : "WHERE",
662 : "ON",
663 : "ORDER",
664 : "BY",
665 : "FROM",
666 : "AS",
667 : "ASC",
668 : "DESC"
669 : };
670 :
671 55 : int swq_is_reserved_keyword(const char* pszStr)
672 : {
673 1265 : for(int i = 0; i < (int)(sizeof(apszSQLReservedKeywords)/sizeof(char*)); i++)
674 : {
675 1210 : if (EQUAL(pszStr, apszSQLReservedKeywords[i]))
676 0 : return TRUE;
677 : }
678 55 : return FALSE;
679 : }
|