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 <stdio.h>
22 : #include <stdlib.h>
23 : #include <string.h>
24 : #include <ctype.h>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_multiproc.h"
28 : #include "swq.h"
29 :
30 : #ifndef SWQ_MALLOC
31 : #define SWQ_MALLOC(x) malloc(x)
32 : #define SWQ_FREE(x) free(x)
33 : #endif
34 :
35 : #ifdef _MSC_VER
36 : # define FORCE_CDECL __cdecl
37 : #else
38 : # define FORCE_CDECL
39 : #endif
40 :
41 : #ifndef TRUE
42 : # define TRUE 1
43 : #endif
44 :
45 : #ifndef FALSE
46 : # define FALSE 0
47 : #endif
48 :
49 : #if defined(_WIN32) && !defined(_WIN32_WCE)
50 : # define strcasecmp stricmp
51 : #elif defined(_WIN32_WCE)
52 : # define strcasecmp _stricmp
53 : #endif
54 :
55 :
56 : #define SWQ_OP_IS_LOGICAL(op) ((op) == SWQ_OR || (op) == SWQ_AND || (op) == SWQ_NOT)
57 : #define SWQ_OP_IS_POSTUNARY(op) ((op) == SWQ_ISNULL || (op) == SWQ_ISNOTNULL)
58 :
59 : /************************************************************************/
60 : /* swq_free() */
61 : /************************************************************************/
62 :
63 : void swq_free( void *pMemory )
64 :
65 10 : {
66 10 : SWQ_FREE( pMemory );
67 10 : }
68 :
69 : /************************************************************************/
70 : /* swq_malloc() */
71 : /************************************************************************/
72 :
73 : void *swq_malloc( int nSize )
74 :
75 163 : {
76 163 : return SWQ_MALLOC(nSize);
77 : }
78 :
79 : /************************************************************************/
80 : /* swq_realloc() */
81 : /************************************************************************/
82 :
83 : void *swq_realloc( void *old_mem, int old_size, int new_size )
84 :
85 130 : {
86 : void *new_mem;
87 :
88 130 : new_mem = swq_malloc( new_size );
89 :
90 130 : if( old_mem != NULL )
91 : {
92 54 : memcpy( new_mem, old_mem, old_size < new_size ? old_size : new_size);
93 54 : SWQ_FREE( old_mem );
94 : }
95 130 : if (old_size <= new_size )
96 130 : memset( ((char *) new_mem) + old_size, 0, new_size - old_size );
97 :
98 130 : return new_mem;
99 : }
100 :
101 : /************************************************************************/
102 : /* swq_get_errbuf() */
103 : /************************************************************************/
104 :
105 : #define SWQ_SIZEOF_ERRBUF 1024
106 :
107 : #define SNPRINTF_ERR1(x) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0)
108 : #define SNPRINTF_ERR2(x,y) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x, y); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0)
109 : #define SNPRINTF_ERR3(x,y,z) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x, y, z); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0)
110 :
111 : char *swq_get_errbuf()
112 :
113 2 : {
114 2 : char *pszStaticResult = (char *) CPLGetTLS( CTLS_SWQ_ERRBUF );
115 2 : if( pszStaticResult == NULL )
116 : {
117 1 : pszStaticResult = (char *) CPLMalloc(SWQ_SIZEOF_ERRBUF);
118 1 : CPLSetTLS( CTLS_SWQ_ERRBUF, pszStaticResult, TRUE );
119 : }
120 :
121 2 : return pszStaticResult;
122 : }
123 :
124 : /************************************************************************/
125 : /* swq_isalphanum() */
126 : /* */
127 : /* Is the passed character in the set of things that could */
128 : /* occur in an alphanumeric token, or a number? */
129 : /************************************************************************/
130 :
131 : static int swq_isalphanum( char c )
132 :
133 6241 : {
134 6241 : if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
135 : || (c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-'
136 : || c == '_' || c == '*' || ((unsigned char) c) > 127 )
137 5047 : return TRUE;
138 : else
139 1194 : return FALSE;
140 : }
141 :
142 : /************************************************************************/
143 : /* swq_token() */
144 : /************************************************************************/
145 :
146 : static char *swq_token( const char *expression, char **next, int *is_literal )
147 :
148 1482 : {
149 : char *token;
150 : int i_token;
151 :
152 1482 : if( is_literal != NULL )
153 627 : *is_literal = 0;
154 :
155 3892 : while( *expression == ' ' || *expression == '\t'
156 : || *expression == 10 || *expression == 13 )
157 928 : expression++;
158 :
159 1482 : if( *expression == '\0' )
160 : {
161 227 : *next = (char *) expression;
162 227 : return NULL;
163 : }
164 :
165 : /* -------------------------------------------------------------------- */
166 : /* Handle string constants. */
167 : /* -------------------------------------------------------------------- */
168 1316 : if( *expression == '"' || *expression == '\'' )
169 : {
170 61 : expression++;
171 :
172 61 : token = (char *) SWQ_MALLOC(strlen(expression)+1);
173 61 : i_token = 0;
174 :
175 600 : while( *expression != '\0' )
176 : {
177 539 : if( *expression == '\\' && expression[1] == '"' )
178 0 : expression++;
179 539 : else if( *expression == '\\' && expression[1] == '\'' )
180 0 : expression++;
181 539 : else if( *expression == '\'' && expression[1] == '\'' )
182 0 : expression++;
183 539 : else if( *expression == '"' )
184 : {
185 19 : expression++;
186 19 : break;
187 : }
188 520 : else if( *expression == '\'' )
189 : {
190 42 : expression++;
191 42 : break;
192 : }
193 :
194 478 : token[i_token++] = *(expression++);
195 : }
196 61 : token[i_token] = '\0';
197 :
198 61 : if( is_literal != NULL )
199 15 : *is_literal = 1;
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Handle alpha-numerics. */
204 : /* -------------------------------------------------------------------- */
205 1194 : else if( swq_isalphanum( *expression ) )
206 : {
207 910 : token = (char *) SWQ_MALLOC(strlen(expression)+1);
208 910 : i_token = 0;
209 :
210 5957 : while( swq_isalphanum( *expression ) )
211 : {
212 4137 : token[i_token++] = *(expression++);
213 : }
214 :
215 910 : token[i_token] = '\0';
216 : }
217 :
218 : /* -------------------------------------------------------------------- */
219 : /* Handle special tokens. */
220 : /* -------------------------------------------------------------------- */
221 : else
222 : {
223 284 : token = (char *) SWQ_MALLOC(3);
224 284 : token[0] = *expression;
225 284 : token[1] = '\0';
226 284 : expression++;
227 :
228 : /* special logic to group stuff like '>=' into one token. */
229 :
230 284 : if( (*token == '<' || *token == '>' || *token == '=' || *token == '!')
231 : && (*expression == '<' || *expression == '>' || *expression == '='))
232 : {
233 4 : token[1] = *expression;
234 4 : token[2] = '\0';
235 4 : expression++;
236 : }
237 : }
238 :
239 1255 : *next = (char *) expression;
240 :
241 1255 : return token;
242 : }
243 :
244 : /************************************************************************/
245 : /* swq_strdup() */
246 : /************************************************************************/
247 :
248 : static char *swq_strdup( const char *input )
249 :
250 490 : {
251 : char *result;
252 :
253 490 : result = (char *) SWQ_MALLOC(strlen(input)+1);
254 490 : strcpy( result, input );
255 :
256 490 : return result;
257 : }
258 :
259 : /************************************************************************/
260 : /* ==================================================================== */
261 : /* WHERE clause parsing */
262 : /* ==================================================================== */
263 : /************************************************************************/
264 :
265 : /************************************************************************/
266 : /* swq_test_like() */
267 : /* */
268 : /* Does input match pattern? */
269 : /************************************************************************/
270 :
271 : int swq_test_like( const char *input, const char *pattern )
272 :
273 90 : {
274 90 : if( input == NULL || pattern == NULL )
275 0 : return 0;
276 :
277 221 : while( *input != '\0' )
278 : {
279 129 : if( *pattern == '\0' )
280 0 : return 0;
281 :
282 129 : else if( *pattern == '_' )
283 : {
284 0 : input++;
285 0 : pattern++;
286 : }
287 129 : else if( *pattern == '%' )
288 : {
289 : int eat;
290 :
291 12 : if( pattern[1] == '\0' )
292 2 : return 1;
293 :
294 : /* try eating varying amounts of the input till we get a positive*/
295 87 : for( eat = 0; input[eat] != '\0'; eat++ )
296 : {
297 78 : if( swq_test_like(input+eat,pattern+1) )
298 1 : return 1;
299 : }
300 :
301 9 : return 0;
302 : }
303 : else
304 : {
305 117 : if( tolower(*pattern) != tolower(*input) )
306 76 : return 0;
307 : else
308 : {
309 41 : input++;
310 41 : pattern++;
311 : }
312 : }
313 : }
314 :
315 2 : if( *pattern != '\0' && strcmp(pattern,"%") != 0 )
316 1 : return 0;
317 : else
318 1 : return 1;
319 : }
320 :
321 : /************************************************************************/
322 : /* swq_identify_op() */
323 : /************************************************************************/
324 :
325 : static swq_op swq_identify_op( char **tokens, int *tokens_consumed )
326 :
327 218 : {
328 218 : const char *token = tokens[*tokens_consumed];
329 :
330 218 : if( strcasecmp(token,"OR") == 0 )
331 0 : return SWQ_OR;
332 :
333 218 : if( strcasecmp(token,"AND") == 0 )
334 27 : return SWQ_AND;
335 :
336 191 : if( strcasecmp(token,"NOT") == 0 )
337 : {
338 0 : if( tokens[*tokens_consumed+1] != NULL
339 : && (strcasecmp(tokens[*tokens_consumed+1],"LIKE") == 0
340 : || strcasecmp(tokens[*tokens_consumed+1],"ILIKE") == 0) )
341 : {
342 0 : *tokens_consumed += 1;
343 0 : return SWQ_NOTLIKE;
344 : }
345 0 : else if( tokens[*tokens_consumed+1] != NULL
346 : && strcasecmp(tokens[*tokens_consumed+1],"IN") == 0 )
347 : {
348 0 : *tokens_consumed += 1;
349 0 : return SWQ_NOTIN;
350 : }
351 : else
352 0 : return SWQ_NOT;
353 : }
354 :
355 191 : if( strcasecmp(token,"<=") == 0 )
356 0 : return SWQ_LE;
357 :
358 191 : if( strcasecmp(token,">=") == 0 )
359 0 : return SWQ_GE;
360 :
361 191 : if( strcasecmp(token,"=") == 0 )
362 109 : return SWQ_EQ;
363 :
364 82 : if( strcasecmp(token,"!=") == 0 )
365 3 : return SWQ_NE;
366 :
367 79 : if( strcasecmp(token,"<>") == 0 )
368 0 : return SWQ_NE;
369 :
370 79 : if( strcasecmp(token,"<") == 0 )
371 34 : return SWQ_LT;
372 :
373 45 : if( strcasecmp(token,">") == 0 )
374 34 : return SWQ_GT;
375 :
376 11 : if( strcasecmp(token,"LIKE") == 0 )
377 2 : return SWQ_LIKE;
378 :
379 9 : if( strcasecmp(token,"ILIKE") == 0 )
380 2 : return SWQ_LIKE;
381 :
382 7 : if( strcasecmp(token,"IN") == 0 )
383 7 : return SWQ_IN;
384 :
385 0 : if( strcasecmp(token,"IS") == 0 )
386 : {
387 0 : if( tokens[*tokens_consumed+1] == NULL )
388 0 : return SWQ_UNKNOWN;
389 0 : else if( strcasecmp(tokens[*tokens_consumed+1],"NULL") == 0 )
390 : {
391 0 : *tokens_consumed += 1;
392 0 : return SWQ_ISNULL;
393 : }
394 0 : else if( strcasecmp(tokens[*tokens_consumed+1],"NOT") == 0
395 : && tokens[*tokens_consumed+2] != NULL
396 : && strcasecmp(tokens[*tokens_consumed+2],"NULL") == 0 )
397 : {
398 0 : *tokens_consumed += 2;
399 0 : return SWQ_ISNOTNULL;
400 : }
401 : else
402 0 : return SWQ_UNKNOWN;
403 : }
404 :
405 0 : return SWQ_UNKNOWN;
406 : }
407 :
408 : /************************************************************************/
409 : /* swq_identify_field() */
410 : /************************************************************************/
411 :
412 : static int swq_identify_field( const char *token, swq_field_list *field_list,
413 : swq_field_type *this_type, int *table_id )
414 :
415 406 : {
416 : int i;
417 : char table_name[128];
418 406 : const char *field_token = token;
419 : int tables_enabled;
420 :
421 648 : if( field_list->table_count > 0 && field_list->table_ids != NULL )
422 242 : tables_enabled = TRUE;
423 : else
424 164 : tables_enabled = FALSE;
425 :
426 : /* -------------------------------------------------------------------- */
427 : /* Parse out table name if present, and table support enabled. */
428 : /* -------------------------------------------------------------------- */
429 406 : table_name[0] = '\0';
430 406 : if( tables_enabled && strchr(token, '.') != NULL )
431 : {
432 70 : int dot_offset = (int)(strchr(token,'.') - token);
433 :
434 70 : if( dot_offset < sizeof(table_name) )
435 : {
436 70 : strncpy( table_name, token, dot_offset );
437 70 : table_name[dot_offset] = '\0';
438 70 : field_token = token + dot_offset + 1;
439 : }
440 : }
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Search for matching field. */
444 : /* -------------------------------------------------------------------- */
445 995 : for( i = 0; i < field_list->count; i++ )
446 : {
447 994 : int t_id = 0;
448 :
449 994 : if( strcasecmp( field_list->names[i], field_token ) != 0 )
450 569 : continue;
451 :
452 : /* Do the table specifications match? */
453 425 : if( tables_enabled )
454 : {
455 261 : t_id = field_list->table_ids[i];
456 261 : if( table_name[0] != '\0'
457 : && strcasecmp(table_name,
458 : field_list->table_defs[t_id].table_alias) != 0 )
459 20 : continue;
460 :
461 : #ifdef notdef
462 : if( t_id != 0 && table_name[0] == '\0' )
463 : continue;
464 : #endif
465 : }
466 :
467 : /* We have a match, return various information */
468 405 : if( this_type != NULL )
469 : {
470 362 : if( field_list->types != NULL )
471 362 : *this_type = field_list->types[i];
472 : else
473 0 : *this_type = SWQ_OTHER;
474 : }
475 :
476 405 : if( table_id != NULL )
477 405 : *table_id = t_id;
478 :
479 405 : if( field_list->ids == NULL )
480 164 : return i;
481 : else
482 241 : return field_list->ids[i];
483 : }
484 :
485 : /* -------------------------------------------------------------------- */
486 : /* No match, return failure. */
487 : /* -------------------------------------------------------------------- */
488 1 : if( this_type != NULL )
489 1 : *this_type = SWQ_OTHER;
490 :
491 1 : if( table_id != NULL )
492 1 : *table_id = 0;
493 :
494 1 : return -1;
495 : }
496 :
497 : /************************************************************************/
498 : /* swq_parse_in_list() */
499 : /* */
500 : /* Parse the argument list to the IN predicate. Might be used */
501 : /* something like: */
502 : /* */
503 : /* WHERE color IN ('Red', 'Green', 'Blue') */
504 : /************************************************************************/
505 :
506 : static char *swq_parse_in_list( char **tokens, int *tokens_consumed )
507 :
508 7 : {
509 7 : int i, text_off = 2;
510 : char *result;
511 :
512 7 : if( tokens[*tokens_consumed] == NULL
513 : || strcasecmp(tokens[*tokens_consumed],"(") != 0 )
514 : {
515 0 : SNPRINTF_ERR1("IN argument doesn't start with '('." );
516 0 : return NULL;
517 : }
518 :
519 7 : *tokens_consumed += 1;
520 :
521 : /* Establish length of all tokens plus separators. */
522 :
523 7 : for( i = *tokens_consumed;
524 35 : tokens[i] != NULL && strcasecmp(tokens[i],")") != 0;
525 21 : i++ )
526 : {
527 21 : text_off += strlen(tokens[i]) + 1;
528 : }
529 :
530 7 : result = (char *) SWQ_MALLOC(text_off);
531 :
532 : /* Actually capture all the arguments. */
533 :
534 7 : text_off = 0;
535 28 : while( tokens[*tokens_consumed] != NULL
536 : && strcasecmp(tokens[*tokens_consumed],")") != 0 )
537 : {
538 14 : strcpy( result + text_off, tokens[*tokens_consumed] );
539 14 : text_off += strlen(tokens[*tokens_consumed]) + 1;
540 :
541 14 : *tokens_consumed += 1;
542 :
543 14 : if( strcasecmp(tokens[*tokens_consumed],",") != 0
544 : && strcasecmp(tokens[*tokens_consumed],")") != 0 )
545 : {
546 0 : SNPRINTF_ERR1("Contents of IN predicate missing comma or closing bracket." );
547 0 : SWQ_FREE( result );
548 0 : return NULL;
549 : }
550 14 : else if( strcasecmp(tokens[*tokens_consumed],",") == 0 )
551 7 : *tokens_consumed += 1;
552 : }
553 :
554 : /* add final extra terminating zero char */
555 7 : result[text_off] = '\0';
556 :
557 7 : if( tokens[*tokens_consumed] == NULL )
558 : {
559 0 : SNPRINTF_ERR1("Contents of IN predicate missing closing bracket." );
560 0 : SWQ_FREE( result );
561 0 : return NULL;
562 : }
563 :
564 7 : *tokens_consumed += 1;
565 :
566 7 : return result;
567 : }
568 :
569 :
570 : /************************************************************************/
571 : /* swq_subexpr_compile() */
572 : /************************************************************************/
573 :
574 : static const char *
575 : swq_subexpr_compile( char **tokens, swq_field_list *field_list,
576 : swq_expr **expr_out, int *tokens_consumed )
577 :
578 191 : {
579 : swq_expr *op;
580 : const char *error;
581 191 : int op_code = 0;
582 :
583 191 : *tokens_consumed = 0;
584 191 : *expr_out = NULL;
585 :
586 191 : if( tokens[0] == NULL || tokens[1] == NULL )
587 : {
588 0 : SNPRINTF_ERR1("Not enough tokens to complete expression." );
589 0 : return swq_get_errbuf();
590 : }
591 :
592 191 : op = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op));
593 191 : memset( op, 0, sizeof(swq_field_op) );
594 191 : op->field_index = -1;
595 :
596 191 : if( strcmp(tokens[0],"(") == 0 )
597 : {
598 0 : int sub_consumed = 0;
599 :
600 0 : error = swq_subexpr_compile( tokens + 1, field_list,
601 : (swq_expr **) &(op->first_sub_expr),
602 : &sub_consumed );
603 0 : if( error != NULL )
604 : {
605 0 : swq_expr_free( op );
606 0 : return error;
607 : }
608 :
609 0 : if( strcmp(tokens[sub_consumed+1],")") != 0 )
610 : {
611 0 : swq_expr_free( op );
612 0 : SNPRINTF_ERR1("Unclosed brackets, or incomplete expression.");
613 0 : return swq_get_errbuf();
614 : }
615 :
616 0 : *tokens_consumed += sub_consumed + 2;
617 :
618 : /* If we are at the end of the tokens, we should return our subnode */
619 0 : if( tokens[*tokens_consumed] == NULL
620 : || strcmp(tokens[*tokens_consumed],")") == 0 )
621 : {
622 0 : *expr_out = (swq_expr *) op->first_sub_expr;
623 0 : op->first_sub_expr = NULL;
624 0 : swq_expr_free( op );
625 0 : return NULL;
626 : }
627 : }
628 191 : else if( strcasecmp(tokens[0],"NOT") == 0 )
629 : {
630 : /* do nothing, the NOT will be collected as the operation */
631 : }
632 : else
633 : {
634 191 : op->field_index =
635 : swq_identify_field( tokens[*tokens_consumed], field_list,
636 : &(op->field_type),
637 : &(op->table_index) );
638 :
639 191 : if( op->field_index < 0 )
640 : {
641 0 : swq_expr_free( op );
642 0 : SNPRINTF_ERR2( "Failed to identify field:%s", tokens[*tokens_consumed] );
643 0 : return swq_get_errbuf();
644 : }
645 :
646 191 : (*tokens_consumed)++;
647 : }
648 :
649 : /*
650 : ** Identify the operation.
651 : */
652 191 : if( tokens[*tokens_consumed] == NULL || tokens[*tokens_consumed+1] == NULL)
653 : {
654 0 : swq_expr_free( op );
655 0 : SNPRINTF_ERR1( "Not enough tokens to complete expression." );
656 0 : return swq_get_errbuf();
657 : }
658 :
659 191 : op->operation = swq_identify_op( tokens, tokens_consumed );
660 191 : if( op->operation == SWQ_UNKNOWN )
661 : {
662 0 : swq_expr_free( op );
663 0 : SNPRINTF_ERR2( "Failed to identify operation:%s", tokens[*tokens_consumed] );
664 0 : return swq_get_errbuf();
665 : }
666 :
667 191 : if( SWQ_OP_IS_LOGICAL( op->operation )
668 : && op->first_sub_expr == NULL
669 : && op->operation != SWQ_NOT )
670 : {
671 0 : swq_expr_free( op );
672 0 : SNPRINTF_ERR1( "Used logical operation with non-logical operand.");
673 0 : return swq_get_errbuf();
674 : }
675 :
676 191 : if( op->field_index != -1 && op->field_type == SWQ_STRING
677 : && (op->operation != SWQ_EQ && op->operation != SWQ_NE
678 : && op->operation != SWQ_GT && op->operation != SWQ_LT
679 : && op->operation != SWQ_GE && op->operation != SWQ_LE
680 : && op->operation != SWQ_LIKE && op->operation != SWQ_NOTLIKE
681 : && op->operation != SWQ_IN && op->operation != SWQ_NOTIN
682 : && op->operation != SWQ_ISNULL && op->operation != SWQ_ISNOTNULL ))
683 : {
684 : /* NOTE: the use of names[] here is wrong. We should be looking
685 : up the field that matches op->field_index and op->table_index */
686 :
687 0 : SNPRINTF_ERR3(
688 : "Attempt to use STRING field `%s' with numeric comparison `%s'.",
689 : field_list->names[op->field_index], tokens[*tokens_consumed] );
690 0 : swq_expr_free( op );
691 0 : return swq_get_errbuf();
692 : }
693 :
694 191 : (*tokens_consumed)++;
695 :
696 : /*
697 : ** Collect the second operand as a subexpression.
698 : */
699 :
700 191 : if( SWQ_OP_IS_POSTUNARY(op->operation) )
701 : {
702 : /* we don't need another argument. */
703 : }
704 :
705 191 : else if( tokens[*tokens_consumed] == NULL )
706 : {
707 0 : SNPRINTF_ERR1( "Not enough tokens to complete expression." );
708 0 : swq_expr_free( op );
709 0 : return swq_get_errbuf();
710 : }
711 :
712 191 : else if( SWQ_OP_IS_LOGICAL( op->operation ) )
713 : {
714 0 : int sub_consumed = 0;
715 :
716 0 : error = swq_subexpr_compile( tokens + *tokens_consumed, field_list,
717 : (swq_expr **) &(op->second_sub_expr),
718 : &sub_consumed );
719 0 : if( error != NULL )
720 : {
721 0 : swq_expr_free( op );
722 0 : return error;
723 : }
724 :
725 0 : *tokens_consumed += sub_consumed;
726 : }
727 :
728 : /* The IN predicate has a complex argument syntax. */
729 198 : else if( op->operation == SWQ_IN || op->operation == SWQ_NOTIN )
730 : {
731 7 : op->string_value = swq_parse_in_list( tokens, tokens_consumed );
732 7 : if( op->string_value == NULL )
733 : {
734 0 : swq_expr_free( op );
735 0 : return swq_get_errbuf();
736 : }
737 : }
738 :
739 : /*
740 : ** Otherwise collect it as a literal value.
741 : */
742 : else
743 : {
744 184 : op->string_value = swq_strdup(tokens[*tokens_consumed]);
745 184 : op->int_value = atoi(op->string_value);
746 184 : op->float_value = CPLAtof(op->string_value);
747 :
748 184 : if( op->field_index != -1
749 : && (op->field_type == SWQ_INTEGER || op->field_type == SWQ_FLOAT)
750 : && op->string_value[0] != '-'
751 : && op->string_value[0] != '+'
752 : && op->string_value[0] != '.'
753 : && (op->string_value[0] < '0' || op->string_value[0] > '9') )
754 : {
755 : /* NOTE: the use of names[] here is wrong. We should be looking
756 : up the field that matches op->field_index and op->table_index */
757 :
758 0 : SNPRINTF_ERR3(
759 : "Attempt to compare numeric field `%s' to non-numeric"
760 : " value `%s' is illegal.",
761 : field_list->names[op->field_index], op->string_value );
762 0 : swq_expr_free( op );
763 0 : return swq_get_errbuf();
764 : }
765 :
766 184 : (*tokens_consumed)++;
767 : }
768 :
769 191 : *expr_out = op;
770 :
771 : /* Transform stuff like A NOT LIKE X into NOT (A LIKE X) */
772 191 : if( op->operation == SWQ_NOTLIKE
773 : || op->operation == SWQ_ISNOTNULL
774 : || op->operation == SWQ_NOTIN )
775 : {
776 0 : if( op->operation == SWQ_NOTLIKE )
777 0 : op->operation = SWQ_LIKE;
778 0 : else if( op->operation == SWQ_NOTIN )
779 0 : op->operation = SWQ_IN;
780 0 : else if( op->operation == SWQ_ISNOTNULL )
781 0 : op->operation = SWQ_ISNULL;
782 :
783 0 : op = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op));
784 0 : memset( op, 0, sizeof(swq_field_op) );
785 0 : op->field_index = -1;
786 0 : op->second_sub_expr = (struct swq_node_s *) *expr_out;
787 0 : op->operation = SWQ_NOT;
788 :
789 0 : *expr_out = op;
790 : }
791 :
792 191 : op = NULL;
793 :
794 : /*
795 : ** Are we part of an unparantized logical expression chain? If so,
796 : ** grab the remainder of the expression at "this level" and add to the
797 : ** local tree.
798 : */
799 191 : op_code = SWQ_UNKNOWN;
800 191 : if( tokens[*tokens_consumed] != NULL )
801 27 : op_code = swq_identify_op( tokens, tokens_consumed );
802 :
803 191 : if( SWQ_OP_IS_LOGICAL(op_code) )
804 : {
805 27 : swq_expr *remainder = NULL;
806 : swq_expr *parent;
807 : int sub_consumed;
808 :
809 27 : error = swq_subexpr_compile( tokens + *tokens_consumed + 1, field_list,
810 : &remainder, &sub_consumed );
811 27 : if( error != NULL )
812 : {
813 0 : swq_expr_free( *expr_out );
814 0 : *expr_out = NULL;
815 0 : return error;
816 : }
817 :
818 27 : parent = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op));
819 27 : memset( parent, 0, sizeof(swq_field_op) );
820 27 : parent->field_index = -1;
821 :
822 27 : parent->first_sub_expr = (struct swq_node_s *) *expr_out;
823 27 : parent->second_sub_expr = (struct swq_node_s *) remainder;
824 27 : parent->operation = op_code;
825 :
826 27 : *expr_out = parent;
827 :
828 27 : *tokens_consumed += sub_consumed + 1;
829 : }
830 :
831 191 : return NULL;
832 : }
833 :
834 : /************************************************************************/
835 : /* swq_expr_compile() */
836 : /************************************************************************/
837 :
838 : const char *swq_expr_compile( const char *where_clause,
839 : int field_count,
840 : char **field_names,
841 : swq_field_type *field_types,
842 : swq_expr **expr_out )
843 :
844 140 : {
845 : swq_field_list field_list;
846 :
847 140 : field_list.count = field_count;
848 140 : field_list.names = field_names;
849 140 : field_list.types = field_types;
850 140 : field_list.table_ids = NULL;
851 140 : field_list.ids = NULL;
852 :
853 140 : field_list.table_count = 0;
854 140 : field_list.table_defs = NULL;
855 :
856 140 : return swq_expr_compile2( where_clause, &field_list, expr_out );
857 : }
858 :
859 :
860 : /************************************************************************/
861 : /* swq_expr_compile2() */
862 : /************************************************************************/
863 :
864 : const char *swq_expr_compile2( const char *where_clause,
865 : swq_field_list *field_list,
866 : swq_expr **expr_out )
867 :
868 164 : {
869 : #define TOKEN_BLOCK_SIZE 1024
870 : char **token_list, *rest_of_expr;
871 164 : int token_count = 0;
872 : int tokens_consumed, i, token_list_size;
873 : const char *error;
874 :
875 : /*
876 : ** Collect token array.
877 : */
878 164 : token_list = (char **)SWQ_MALLOC(sizeof(char *) * TOKEN_BLOCK_SIZE);
879 164 : token_list_size = TOKEN_BLOCK_SIZE;
880 164 : rest_of_expr = (char *) where_clause;
881 956 : while( (token_list[token_count] =
882 : swq_token(rest_of_expr,&rest_of_expr,NULL)) != NULL )
883 : {
884 628 : token_count++;
885 628 : if (token_count == token_list_size)
886 : {
887 0 : token_list = (char **)swq_realloc(token_list,
888 : sizeof(char *) * token_list_size,
889 : sizeof(char *) * (token_list_size + TOKEN_BLOCK_SIZE));
890 0 : token_list_size += TOKEN_BLOCK_SIZE;
891 : }
892 : }
893 :
894 : /*
895 : ** Parse the expression.
896 : */
897 164 : *expr_out = NULL;
898 164 : error =
899 : swq_subexpr_compile( token_list, field_list, expr_out,
900 : &tokens_consumed );
901 :
902 792 : for( i = 0; i < token_count; i++ )
903 628 : SWQ_FREE( token_list[i] );
904 :
905 164 : SWQ_FREE(token_list);
906 :
907 164 : if( error != NULL )
908 0 : return error;
909 :
910 164 : if( tokens_consumed < token_count )
911 : {
912 0 : swq_expr_free( *expr_out );
913 0 : *expr_out = NULL;
914 0 : SNPRINTF_ERR2( "Syntax error, %d extra tokens",
915 : token_count - tokens_consumed );
916 0 : return swq_get_errbuf();
917 : }
918 :
919 164 : return NULL;
920 : }
921 :
922 : /************************************************************************/
923 : /* swq_expr_free() */
924 : /************************************************************************/
925 :
926 : void swq_expr_free( swq_expr *expr )
927 :
928 218 : {
929 218 : if( expr == NULL )
930 0 : return;
931 :
932 218 : if( expr->first_sub_expr != NULL )
933 27 : swq_expr_free( (swq_expr *) expr->first_sub_expr );
934 218 : if( expr->second_sub_expr != NULL )
935 27 : swq_expr_free( (swq_expr *) expr->second_sub_expr );
936 :
937 218 : if( expr->string_value != NULL )
938 191 : SWQ_FREE( expr->string_value );
939 :
940 218 : SWQ_FREE( expr );
941 : }
942 :
943 : /************************************************************************/
944 : /* swq_expr_evaluate() */
945 : /************************************************************************/
946 :
947 : int swq_expr_evaluate( swq_expr *expr, swq_op_evaluator fn_evaluator,
948 : void *record_handle )
949 :
950 1233 : {
951 1233 : if( expr->operation == SWQ_OR )
952 : {
953 0 : return swq_expr_evaluate( (swq_expr *) expr->first_sub_expr,
954 : fn_evaluator,
955 : record_handle)
956 : || swq_expr_evaluate( (swq_expr *) expr->second_sub_expr,
957 : fn_evaluator,
958 : record_handle);
959 : }
960 1233 : else if( expr->operation == SWQ_AND )
961 : {
962 81 : return swq_expr_evaluate( (swq_expr *) expr->first_sub_expr,
963 : fn_evaluator,
964 : record_handle)
965 : && swq_expr_evaluate( (swq_expr *) expr->second_sub_expr,
966 : fn_evaluator,
967 : record_handle);
968 : }
969 1152 : else if( expr->operation == SWQ_NOT )
970 : {
971 0 : return !swq_expr_evaluate( (swq_expr *) expr->second_sub_expr,
972 : fn_evaluator,
973 : record_handle);
974 : }
975 : else
976 : {
977 1152 : return fn_evaluator( expr, record_handle );
978 : }
979 : }
980 :
981 : /************************************************************************/
982 : /* swq_expr_dump() */
983 : /************************************************************************/
984 :
985 : void swq_expr_dump( swq_expr *expr, FILE * fp, int depth )
986 :
987 0 : {
988 : char spaces[60];
989 : int i;
990 0 : const char *op_name = "unknown";
991 :
992 0 : for( i = 0; i < depth*2 && i < sizeof(spaces); i++ )
993 0 : spaces[i] = ' ';
994 0 : spaces[i] = '\0';
995 :
996 : /*
997 : ** first term.
998 : */
999 0 : if( expr->first_sub_expr != NULL )
1000 0 : swq_expr_dump( (swq_expr *) expr->first_sub_expr, fp, depth + 1 );
1001 : else
1002 0 : fprintf( fp, "%s Field %d\n", spaces, expr->field_index );
1003 :
1004 : /*
1005 : ** Operation.
1006 : */
1007 0 : if( expr->operation == SWQ_OR )
1008 0 : op_name = "OR";
1009 0 : if( expr->operation == SWQ_AND )
1010 0 : op_name = "AND";
1011 0 : if( expr->operation == SWQ_NOT)
1012 0 : op_name = "NOT";
1013 0 : if( expr->operation == SWQ_GT )
1014 0 : op_name = ">";
1015 0 : if( expr->operation == SWQ_LT )
1016 0 : op_name = "<";
1017 0 : if( expr->operation == SWQ_EQ )
1018 0 : op_name = "=";
1019 0 : if( expr->operation == SWQ_NE )
1020 0 : op_name = "!=";
1021 0 : if( expr->operation == SWQ_GE )
1022 0 : op_name = ">=";
1023 0 : if( expr->operation == SWQ_LE )
1024 0 : op_name = "<=";
1025 0 : if( expr->operation == SWQ_LIKE )
1026 0 : op_name = "LIKE";
1027 0 : if( expr->operation == SWQ_ISNULL )
1028 0 : op_name = "IS NULL";
1029 0 : if( expr->operation == SWQ_IN )
1030 0 : op_name = "IN";
1031 :
1032 0 : fprintf( fp, "%s%s\n", spaces, op_name );
1033 :
1034 : /*
1035 : ** Second term.
1036 : */
1037 0 : if( expr->second_sub_expr != NULL )
1038 0 : swq_expr_dump( (swq_expr *) expr->second_sub_expr, fp, depth + 1 );
1039 0 : else if( expr->operation == SWQ_IN || expr->operation == SWQ_NOTIN )
1040 : {
1041 : const char *src;
1042 :
1043 0 : fprintf( fp, "%s (\"%s\"", spaces, expr->string_value );
1044 0 : src = expr->string_value + strlen(expr->string_value) + 1;
1045 0 : while( *src != '\0' )
1046 : {
1047 0 : fprintf( fp, ",\"%s\"", src );
1048 0 : src += strlen(src) + 1;
1049 : }
1050 :
1051 0 : fprintf( fp, ")\n" );
1052 : }
1053 0 : else if( expr->string_value != NULL )
1054 0 : fprintf( fp, "%s %s\n", spaces, expr->string_value );
1055 0 : }
1056 :
1057 : /************************************************************************/
1058 : /* ==================================================================== */
1059 : /* SELECT statement parsing */
1060 : /* ==================================================================== */
1061 : /************************************************************************/
1062 :
1063 : /*
1064 : Supported SQL Syntax:
1065 :
1066 : SELECT <field-list> FROM <table_def>
1067 : [LEFT JOIN <table_def>
1068 : ON [<table_ref>.]<key_field> = [<table_ref>.].<key_field>]*
1069 : [WHERE <where-expr>]
1070 : [ORDER BY <sort specification list>]
1071 :
1072 : <field-list> ::= <column-spec> [ { , <column-spec> }... ]
1073 :
1074 : <column-spec> ::= <field-spec> [ <as clause> ]
1075 : | CAST ( <field-spec> AS <data type> ) [ <as clause> ]
1076 :
1077 : <field-spec> ::= [DISTINCT] <field_ref>
1078 : | <field_func> ( [DISTINCT] <field-ref> )
1079 : | Count(*)
1080 :
1081 : <as clause> ::= [ AS ] <column_name>
1082 :
1083 : <data type> ::= character [ ( field_length ) ]
1084 : | float [ ( field_length ) ]
1085 : | numeric [ ( field_length [, field_precision ] ) ]
1086 : | integer [ ( field_length ) ]
1087 : | date [ ( field_length ) ]
1088 : | time [ ( field_length ) ]
1089 : | timestamp [ ( field_length ) ]
1090 :
1091 : <field-func> ::= AVG | MAX | MIN | SUM | COUNT
1092 :
1093 : <field_ref> ::= [<table_ref>.]field_name
1094 :
1095 : <sort specification list> ::=
1096 : <sort specification> [ { <comma> <sort specification> }... ]
1097 :
1098 : <sort specification> ::= <sort key> [ <ordering specification> ]
1099 :
1100 : <sort key> ::= <field_ref>
1101 :
1102 : <ordering specification> ::= ASC | DESC
1103 :
1104 : <table_def> ::= ['<datasource name>'.]table_name [table_alias]
1105 :
1106 : <table_ref> ::= table_name | table_alias
1107 : */
1108 :
1109 : static int swq_parse_table_def( swq_select *select_info,
1110 : int *is_literal,
1111 : char **token, char **input );
1112 :
1113 : static int swq_parse_typename( swq_col_def *col_def,
1114 : int *is_literal,
1115 : char **token, char **input );
1116 :
1117 : /************************************************************************/
1118 : /* swq_select_preparse() */
1119 : /************************************************************************/
1120 :
1121 : const char *swq_select_preparse( const char *select_statement,
1122 : swq_select **select_info_ret )
1123 :
1124 64 : {
1125 : swq_select *select_info;
1126 : char *token;
1127 : char *input;
1128 : int is_literal;
1129 : int type_cast;
1130 : swq_col_def *swq_cols;
1131 :
1132 : #define MAX_COLUMNS 250
1133 :
1134 64 : *select_info_ret = NULL;
1135 :
1136 64 : if (select_statement == NULL || select_statement[0] == '\0')
1137 : {
1138 1 : SNPRINTF_ERR1( "Empty SQL request string" );
1139 1 : return swq_get_errbuf();
1140 : }
1141 :
1142 : /* -------------------------------------------------------------------- */
1143 : /* Get first token. Ensure it is SELECT. */
1144 : /* -------------------------------------------------------------------- */
1145 63 : token = swq_token( select_statement, &input, NULL );
1146 63 : if( strcasecmp(token,"select") != 0 )
1147 : {
1148 0 : SWQ_FREE( token );
1149 0 : SNPRINTF_ERR1( "Missing keyword SELECT" );
1150 0 : return swq_get_errbuf();
1151 : }
1152 63 : SWQ_FREE( token );
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* allocate selection structure. */
1156 : /* -------------------------------------------------------------------- */
1157 63 : select_info = (swq_select *) SWQ_MALLOC(sizeof(swq_select));
1158 63 : memset( select_info, 0, sizeof(swq_select) );
1159 :
1160 63 : select_info->raw_select = swq_strdup( select_statement );
1161 :
1162 : /* -------------------------------------------------------------------- */
1163 : /* Allocate a big field list. */
1164 : /* -------------------------------------------------------------------- */
1165 63 : swq_cols = (swq_col_def *) SWQ_MALLOC(sizeof(swq_col_def) * MAX_COLUMNS);
1166 63 : memset( swq_cols, 0, sizeof(swq_col_def) * MAX_COLUMNS );
1167 :
1168 63 : select_info->column_defs = swq_cols;
1169 :
1170 : /* -------------------------------------------------------------------- */
1171 : /* Collect the field list, terminated by FROM keyword. */
1172 : /* -------------------------------------------------------------------- */
1173 63 : token = swq_token( input, &input, &is_literal );
1174 212 : while( token != NULL
1175 : && (is_literal || strcasecmp(token,"FROM") != 0) )
1176 : {
1177 : char *next_token;
1178 : int next_is_literal;
1179 :
1180 86 : if( select_info->result_columns == MAX_COLUMNS )
1181 : {
1182 0 : SWQ_FREE( token );
1183 0 : swq_select_free( select_info );
1184 0 : SNPRINTF_ERR2(
1185 : "More than MAX_COLUMNS (%d) columns in SELECT statement.",
1186 : MAX_COLUMNS );
1187 0 : return swq_get_errbuf();
1188 : }
1189 :
1190 : /* Ensure that we have a comma before fields other than the first. */
1191 :
1192 86 : if( select_info->result_columns > 0 )
1193 : {
1194 23 : if( strcasecmp(token,",") != 0 )
1195 : {
1196 0 : SNPRINTF_ERR2(
1197 : "Missing comma after column %s in SELECT statement.",
1198 : swq_cols[select_info->result_columns-1].field_name );
1199 0 : SWQ_FREE( token );
1200 0 : swq_select_free( select_info );
1201 0 : return swq_get_errbuf();
1202 : }
1203 :
1204 23 : SWQ_FREE( token );
1205 23 : token = swq_token( input, &input, &is_literal );
1206 : }
1207 :
1208 : /* set up some default values. */
1209 86 : swq_cols[select_info->result_columns].field_precision = -1;
1210 86 : swq_cols[select_info->result_columns].target_type = SWQ_OTHER;
1211 86 : select_info->result_columns++;
1212 :
1213 86 : next_token = swq_token( input, &input, &next_is_literal );
1214 :
1215 : /* Detect the type cast. */
1216 86 : type_cast = 0;
1217 86 : if (token != NULL && next_token != NULL &&strcasecmp(token,"CAST") == 0
1218 : && strcasecmp(next_token,"(") == 0)
1219 : {
1220 2 : type_cast = 1;
1221 2 : SWQ_FREE( token );
1222 2 : SWQ_FREE( next_token );
1223 2 : token = swq_token( input, &input, &is_literal );
1224 2 : next_token = swq_token( input, &input, &next_is_literal );
1225 : }
1226 :
1227 : /*
1228 : ** Handle function operators.
1229 : */
1230 89 : if( !is_literal && !next_is_literal && next_token != NULL
1231 : && strcasecmp(next_token,"(") == 0 )
1232 : {
1233 3 : SWQ_FREE( next_token );
1234 :
1235 3 : swq_cols[select_info->result_columns-1].col_func_name = token;
1236 :
1237 3 : token = swq_token( input, &input, &is_literal );
1238 :
1239 3 : if( token != NULL && !is_literal
1240 : && strcasecmp(token,"DISTINCT") == 0 )
1241 : {
1242 0 : swq_cols[select_info->result_columns-1].distinct_flag = 1;
1243 :
1244 0 : SWQ_FREE( token );
1245 0 : token = swq_token( input, &input, &is_literal );
1246 : }
1247 :
1248 3 : swq_cols[select_info->result_columns-1].field_name = token;
1249 :
1250 3 : token = swq_token( input, &input, &is_literal );
1251 :
1252 3 : if( token == NULL || strcasecmp(token,")") != 0 )
1253 : {
1254 0 : if( token != NULL )
1255 0 : SWQ_FREE( token );
1256 0 : swq_select_free( select_info );
1257 0 : return "Missing closing bracket in field function.";
1258 : }
1259 :
1260 3 : SWQ_FREE( token );
1261 3 : token = swq_token( input, &input, &is_literal );
1262 : }
1263 :
1264 : /*
1265 : ** Handle simple field.
1266 : */
1267 : else
1268 : {
1269 83 : if( token != NULL && !is_literal
1270 : && strcasecmp(token,"DISTINCT") == 0 )
1271 : {
1272 8 : swq_cols[select_info->result_columns-1].distinct_flag = 1;
1273 :
1274 8 : SWQ_FREE( token );
1275 8 : token = next_token;
1276 8 : is_literal = next_is_literal;
1277 :
1278 8 : next_token = swq_token( input, &input, &next_is_literal );
1279 : }
1280 :
1281 83 : swq_cols[select_info->result_columns-1].field_name = token;
1282 83 : token = next_token;
1283 83 : is_literal = next_is_literal;
1284 : }
1285 :
1286 : /* handle the type cast*/
1287 86 : if (type_cast && token != NULL)
1288 : {
1289 2 : if (strcasecmp(token,"AS") != 0)
1290 : {
1291 0 : SWQ_FREE( token );
1292 0 : swq_select_free( select_info );
1293 0 : return "Missing 'AS' keyword in the type cast in SELECT statement.";
1294 : }
1295 :
1296 2 : SWQ_FREE( token );
1297 2 : token = swq_token( input, &input, &is_literal );
1298 :
1299 : /* processing the typename */
1300 2 : if( swq_parse_typename( &swq_cols[select_info->result_columns-1], &is_literal, &token, &input) != 0 )
1301 : {
1302 0 : swq_select_free( select_info );
1303 0 : return swq_get_errbuf();
1304 : }
1305 :
1306 2 : if (token != NULL && strcasecmp(token,")") != 0)
1307 : {
1308 0 : if( token != NULL )
1309 0 : SWQ_FREE( token );
1310 0 : swq_select_free( select_info );
1311 0 : return "Missing closing bracket after the type cast in SELECT statement.";
1312 : }
1313 :
1314 2 : SWQ_FREE( token );
1315 2 : token = swq_token( input, &input, &is_literal );
1316 :
1317 2 : type_cast = 0;
1318 : }
1319 :
1320 : /* Handle the field alias */
1321 86 : if( token != NULL && strcasecmp(token,",") != 0 && strcasecmp(token,"from") != 0)
1322 : {
1323 : /* Skip field alias keyword. */
1324 0 : if (strcasecmp(token,"AS") == 0)
1325 : {
1326 0 : SWQ_FREE( token );
1327 0 : token = swq_token( input, &input, &is_literal );
1328 0 : if (token == NULL)
1329 : {
1330 0 : swq_select_free( select_info );
1331 0 : return "Unexpected terminator after the type cast in SELECT statement.";
1332 : }
1333 : }
1334 0 : swq_cols[select_info->result_columns-1].field_alias = token;
1335 0 : token = swq_token( input, &input, &is_literal );
1336 : }
1337 : }
1338 :
1339 : /* make a columns_def list that is just the right size. */
1340 63 : select_info->column_defs = (swq_col_def *)
1341 : SWQ_MALLOC(sizeof(swq_col_def) * select_info->result_columns);
1342 63 : memcpy( select_info->column_defs, swq_cols,
1343 : sizeof(swq_col_def) * select_info->result_columns );
1344 63 : SWQ_FREE( swq_cols );
1345 :
1346 : /* -------------------------------------------------------------------- */
1347 : /* Collect the table name from the FROM clause. */
1348 : /* -------------------------------------------------------------------- */
1349 63 : if( token == NULL || strcasecmp(token,"FROM") != 0 )
1350 : {
1351 0 : SNPRINTF_ERR1( "Missing FROM clause in SELECT statement." );
1352 0 : swq_select_free( select_info );
1353 0 : return swq_get_errbuf();
1354 : }
1355 :
1356 63 : SWQ_FREE( token );
1357 63 : token = swq_token( input, &input, &is_literal );
1358 :
1359 63 : if( token == NULL )
1360 : {
1361 0 : SNPRINTF_ERR1( "Missing table name in FROM clause." );
1362 0 : swq_select_free( select_info );
1363 0 : return swq_get_errbuf();
1364 : }
1365 :
1366 63 : if( swq_parse_table_def( select_info, &is_literal, &token, &input) != 0 )
1367 : {
1368 0 : swq_select_free( select_info );
1369 0 : return swq_get_errbuf();
1370 : }
1371 :
1372 : /* -------------------------------------------------------------------- */
1373 : /* Do we have a LEFT JOIN (or just JOIN) clause? */
1374 : /* -------------------------------------------------------------------- */
1375 140 : while( token != NULL
1376 : && (strcasecmp(token,"LEFT") == 0
1377 : || strcasecmp(token,"JOIN") == 0) )
1378 : {
1379 : swq_join_def *join_info;
1380 :
1381 14 : if( strcasecmp(token,"LEFT") == 0 )
1382 : {
1383 14 : SWQ_FREE( token );
1384 14 : token = swq_token( input, &input, &is_literal );
1385 :
1386 14 : if( token == NULL || strcasecmp(token,"JOIN") != 0 )
1387 : {
1388 0 : SNPRINTF_ERR1( "Missing JOIN keyword after LEFT." );
1389 0 : swq_select_free( select_info );
1390 0 : return swq_get_errbuf();
1391 : }
1392 : }
1393 :
1394 14 : SWQ_FREE( token );
1395 14 : token = swq_token( input, &input, &is_literal );
1396 :
1397 : /* Create join definition structure */
1398 14 : select_info->join_defs = (swq_join_def *)
1399 : swq_realloc( select_info->join_defs,
1400 : sizeof(swq_join_def) * (select_info->join_count),
1401 : sizeof(swq_join_def) * (select_info->join_count+1) );
1402 :
1403 14 : join_info = select_info->join_defs + select_info->join_count++;
1404 :
1405 : /* Parse out target table */
1406 14 : join_info->secondary_table =
1407 : swq_parse_table_def( select_info, &is_literal, &token, &input);
1408 :
1409 14 : if( join_info->secondary_table < 0 )
1410 : {
1411 0 : swq_select_free( select_info );
1412 0 : return swq_get_errbuf();
1413 : }
1414 :
1415 : /* Check for ON keyword */
1416 14 : if( token == NULL )
1417 0 : token = swq_token( input, &input, &is_literal );
1418 :
1419 14 : if( token == NULL || strcasecmp(token,"ON") != 0 )
1420 : {
1421 0 : swq_select_free( select_info );
1422 0 : SNPRINTF_ERR1( "Corrupt JOIN clause, expecting ON keyword." );
1423 0 : return swq_get_errbuf();
1424 : }
1425 :
1426 14 : SWQ_FREE( token );
1427 :
1428 14 : join_info->primary_field_name =
1429 : swq_token( input, &input, &is_literal );
1430 :
1431 14 : token = swq_token( input, &input, &is_literal );
1432 14 : if( token == NULL || strcasecmp(token,"=") != 0 )
1433 : {
1434 0 : swq_select_free( select_info );
1435 0 : SNPRINTF_ERR1( "Corrupt JOIN clause, expecting '=' condition.");
1436 0 : return swq_get_errbuf();
1437 : }
1438 :
1439 14 : SWQ_FREE( token );
1440 :
1441 14 : join_info->op = SWQ_EQ;
1442 :
1443 14 : join_info->secondary_field_name =
1444 : swq_token( input, &input, &is_literal );
1445 :
1446 14 : if( join_info->secondary_field_name == NULL )
1447 : {
1448 0 : swq_select_free( select_info );
1449 0 : SNPRINTF_ERR1( "Corrupt JOIN clause, missing secondary field.");
1450 0 : return swq_get_errbuf();
1451 : }
1452 :
1453 14 : token = swq_token( input, &input, &is_literal );
1454 : }
1455 :
1456 : /* -------------------------------------------------------------------- */
1457 : /* Do we have a WHERE clause? */
1458 : /* -------------------------------------------------------------------- */
1459 63 : if( token != NULL && strcasecmp(token,"WHERE") == 0 )
1460 : {
1461 24 : const char *where_base = input;
1462 :
1463 72 : while( *where_base == ' ' )
1464 24 : where_base++;
1465 :
1466 24 : SWQ_FREE( token );
1467 :
1468 24 : token = swq_token( input, &input, &is_literal );
1469 144 : while( token != NULL )
1470 : {
1471 101 : if( strcasecmp(token,"ORDER") == 0 && !is_literal )
1472 : {
1473 5 : break;
1474 : }
1475 :
1476 96 : if( token != NULL )
1477 : {
1478 96 : SWQ_FREE( token );
1479 :
1480 96 : token = swq_token( input, &input, &is_literal );
1481 : }
1482 : }
1483 :
1484 24 : select_info->whole_where_clause = swq_strdup(where_base);
1485 :
1486 24 : if( input != NULL )
1487 : {
1488 24 : if( token != NULL )
1489 5 : select_info->whole_where_clause[input - where_base - strlen(token)] = '\0';
1490 : else
1491 19 : select_info->whole_where_clause[input - where_base] = '\0';
1492 : }
1493 : }
1494 :
1495 : /* -------------------------------------------------------------------- */
1496 : /* Parse ORDER BY clause. */
1497 : /* -------------------------------------------------------------------- */
1498 63 : if( token != NULL && strcasecmp(token,"ORDER") == 0 )
1499 : {
1500 15 : SWQ_FREE( token );
1501 :
1502 15 : token = swq_token( input, &input, &is_literal );
1503 :
1504 15 : if( token == NULL || strcasecmp(token,"BY") != 0 )
1505 : {
1506 0 : if( token != NULL )
1507 0 : SWQ_FREE( token );
1508 :
1509 0 : SNPRINTF_ERR1( "ORDER BY clause missing BY keyword." );
1510 0 : swq_select_free( select_info );
1511 0 : return swq_get_errbuf();
1512 : }
1513 :
1514 15 : SWQ_FREE( token );
1515 15 : token = swq_token( input, &input, &is_literal );
1516 45 : while( token != NULL
1517 : && (select_info->order_specs == 0
1518 : || strcasecmp(token,",") == 0) )
1519 : {
1520 15 : swq_order_def *old_defs = select_info->order_defs;
1521 : swq_order_def *def;
1522 :
1523 15 : if( select_info->order_specs != 0 )
1524 : {
1525 0 : SWQ_FREE( token );
1526 0 : token = swq_token( input, &input, &is_literal );
1527 : }
1528 :
1529 15 : select_info->order_defs = (swq_order_def *)
1530 : SWQ_MALLOC(sizeof(swq_order_def)*(select_info->order_specs+1));
1531 :
1532 15 : if( old_defs != NULL )
1533 : {
1534 0 : memcpy( select_info->order_defs, old_defs,
1535 : sizeof(swq_order_def)*select_info->order_specs );
1536 0 : SWQ_FREE( old_defs );
1537 : }
1538 :
1539 15 : def = select_info->order_defs + select_info->order_specs;
1540 15 : def->field_name = token;
1541 15 : def->field_index = 0;
1542 15 : def->ascending_flag = 1;
1543 :
1544 15 : token = swq_token( input, &input, &is_literal );
1545 19 : if( token != NULL && strcasecmp(token,"DESC") == 0 )
1546 : {
1547 4 : SWQ_FREE( token );
1548 4 : token = swq_token( input, &input, &is_literal );
1549 :
1550 4 : def->ascending_flag = 0;
1551 : }
1552 11 : else if( token != NULL && strcasecmp(token,"ASC") == 0 )
1553 : {
1554 8 : SWQ_FREE( token );
1555 8 : token = swq_token( input, &input, &is_literal );
1556 : }
1557 :
1558 15 : select_info->order_specs++;
1559 : }
1560 : }
1561 :
1562 : /* -------------------------------------------------------------------- */
1563 : /* If we have anything left it indicates an error! */
1564 : /* -------------------------------------------------------------------- */
1565 63 : if( token != NULL )
1566 : {
1567 :
1568 0 : SNPRINTF_ERR2(
1569 : "Failed to parse SELECT statement, extra input at %s token.",
1570 : token );
1571 :
1572 0 : SWQ_FREE( token );
1573 0 : swq_select_free( select_info );
1574 0 : return swq_get_errbuf();
1575 : }
1576 :
1577 63 : *select_info_ret = select_info;
1578 :
1579 63 : return NULL;
1580 : }
1581 :
1582 : /************************************************************************/
1583 : /* swq_parse_typename() */
1584 : /************************************************************************/
1585 :
1586 : static int swq_parse_typename( swq_col_def *col_def,
1587 : int *is_literal,
1588 : char **token, char **input )
1589 :
1590 2 : {
1591 : int parse_length;
1592 : int parse_precision;
1593 :
1594 2 : if( *token == NULL )
1595 0 : *token = swq_token( *input, input, is_literal );
1596 :
1597 2 : if( *token == NULL )
1598 : {
1599 0 : SNPRINTF_ERR1( "Corrupt type name, insufficient tokens." );
1600 0 : return -1;
1601 : }
1602 :
1603 : /* -------------------------------------------------------------------- */
1604 : /* Check for the SQL92 typenames */
1605 : /* -------------------------------------------------------------------- */
1606 2 : parse_length = 0;
1607 2 : parse_precision = 0;
1608 2 : if( strcasecmp(*token,"character") == 0 )
1609 : {
1610 1 : col_def->target_type = SWQ_STRING;
1611 1 : col_def->field_length = 1;
1612 1 : parse_length = 1;
1613 : }
1614 1 : else if( strcasecmp(*token,"integer") == 0 )
1615 : {
1616 0 : col_def->target_type = SWQ_INTEGER;
1617 0 : parse_length = 1;
1618 : }
1619 1 : else if( strcasecmp(*token,"float") == 0 )
1620 : {
1621 0 : col_def->target_type = SWQ_FLOAT;
1622 0 : parse_length = 1;
1623 : }
1624 1 : else if( strcasecmp(*token,"numeric") == 0 )
1625 : {
1626 1 : col_def->target_type = SWQ_FLOAT;
1627 1 : parse_length = 1;
1628 1 : parse_precision = 1;
1629 : }
1630 0 : else if( strcasecmp(*token,"timestamp") == 0 )
1631 : {
1632 0 : col_def->target_type = SWQ_TIMESTAMP;
1633 0 : parse_length = 1;
1634 : }
1635 0 : else if( strcasecmp(*token,"date") == 0 )
1636 : {
1637 0 : col_def->target_type = SWQ_DATE;
1638 0 : parse_length = 1;
1639 : }
1640 0 : else if( strcasecmp(*token,"time") == 0 )
1641 : {
1642 0 : col_def->target_type = SWQ_TIME;
1643 0 : parse_length = 1;
1644 : }
1645 : else
1646 : {
1647 0 : SNPRINTF_ERR2( "Unrecognized typename %s.", *token );
1648 0 : SWQ_FREE( *token );
1649 0 : *token = NULL;
1650 0 : return -1;
1651 : }
1652 :
1653 2 : SWQ_FREE( *token );
1654 2 : *token = swq_token( *input, input, is_literal );
1655 :
1656 : /* -------------------------------------------------------------------- */
1657 : /* Check for the field length and precision */
1658 : /* -------------------------------------------------------------------- */
1659 2 : if (parse_length && *token != NULL && strcasecmp(*token,"(") == 0)
1660 : {
1661 2 : SWQ_FREE( *token );
1662 2 : *token = swq_token( *input, input, is_literal );
1663 :
1664 2 : if (*token != NULL)
1665 : {
1666 2 : col_def->field_length = atoi( *token );
1667 2 : SWQ_FREE( *token );
1668 2 : *token = swq_token( *input, input, is_literal );
1669 : }
1670 :
1671 2 : if (parse_precision && *token != NULL && strcasecmp(*token,",") == 0)
1672 : {
1673 1 : SWQ_FREE( *token );
1674 1 : *token = swq_token( *input, input, is_literal );
1675 1 : if (*token != NULL)
1676 : {
1677 1 : col_def->field_precision = atoi( *token );
1678 1 : SWQ_FREE( *token );
1679 1 : *token = swq_token( *input, input, is_literal );
1680 : }
1681 : }
1682 :
1683 2 : if (*token == NULL || strcasecmp(*token,")") != 0)
1684 : {
1685 0 : if (*token != NULL)
1686 : {
1687 0 : SWQ_FREE( *token );
1688 0 : *token = NULL;
1689 : }
1690 0 : SNPRINTF_ERR1( "Missing closing bracket in the field length specifier." );
1691 0 : return -1;
1692 : }
1693 :
1694 2 : SWQ_FREE( *token );
1695 2 : *token = swq_token( *input, input, is_literal );
1696 : }
1697 2 : return 0;
1698 : }
1699 :
1700 : /************************************************************************/
1701 : /* swq_parse_table_def() */
1702 : /* */
1703 : /* Supported table definition forms: */
1704 : /* */
1705 : /* <table_def> :== table_name */
1706 : /* | 'data_source'.table_name */
1707 : /* | table_name table_alias */
1708 : /* | 'data_source'.table_name table_alias */
1709 : /************************************************************************/
1710 :
1711 : static int swq_parse_table_def( swq_select *select_info,
1712 : int *is_literal,
1713 : char **token, char **input )
1714 :
1715 77 : {
1716 : int i;
1717 77 : char *datasource = NULL;
1718 77 : char *table = NULL;
1719 77 : char *alias = NULL;
1720 :
1721 77 : if( *token == NULL )
1722 0 : *token = swq_token( *input, input, is_literal );
1723 :
1724 77 : if( *token == NULL )
1725 : {
1726 0 : SNPRINTF_ERR1( "Corrupt table definition, insufficient tokens." );
1727 0 : return -1;
1728 : }
1729 :
1730 : /* -------------------------------------------------------------------- */
1731 : /* Do we have a datasource literal? */
1732 : /* -------------------------------------------------------------------- */
1733 77 : if( *token != NULL && *is_literal )
1734 : {
1735 5 : datasource = *token;
1736 5 : *token = swq_token( *input, input, is_literal );
1737 :
1738 5 : if( *token == NULL )
1739 : {
1740 0 : *token = datasource;
1741 0 : datasource = NULL;
1742 : }
1743 : }
1744 :
1745 : /* -------------------------------------------------------------------- */
1746 : /* Get the table name. Remove the '.' used to qualify it */
1747 : /* relative to the datasource name if found. */
1748 : /* -------------------------------------------------------------------- */
1749 78 : if( datasource != NULL && (*token)[0] != '.' )
1750 : {
1751 1 : table = datasource;
1752 1 : datasource = NULL;
1753 : }
1754 76 : else if( (*token)[0] == '.' )
1755 : {
1756 4 : table = swq_strdup( (*token) + 1 );
1757 4 : SWQ_FREE( *token );
1758 4 : *token = swq_token( *input, input, is_literal );
1759 : }
1760 : else
1761 : {
1762 72 : table = *token;
1763 72 : *token = swq_token( *input, input, is_literal );
1764 : }
1765 :
1766 : /* -------------------------------------------------------------------- */
1767 : /* Was an alias provided? */
1768 : /* -------------------------------------------------------------------- */
1769 77 : if( *token != NULL && ! *is_literal
1770 : && strcasecmp(*token,"ON") != 0
1771 : && strcasecmp(*token,"ORDER") != 0
1772 : && strcasecmp(*token,"WHERE") != 0
1773 : && strcasecmp(*token,"LEFT") != 0
1774 : && strcasecmp(*token,"JOIN") != 0 )
1775 : {
1776 15 : alias = *token;
1777 15 : *token = swq_token( *input, input, is_literal );
1778 : }
1779 :
1780 : /* -------------------------------------------------------------------- */
1781 : /* Does this match an existing table definition? */
1782 : /* -------------------------------------------------------------------- */
1783 92 : for( i = 0; i < select_info->table_count; i++ )
1784 : {
1785 15 : swq_table_def *table_def = select_info->table_defs + i;
1786 :
1787 15 : if( datasource == NULL
1788 : && alias == NULL
1789 : && strcasecmp(table_def->table_alias,table) == 0 )
1790 0 : return i;
1791 :
1792 15 : if( datasource != NULL && table_def->data_source != NULL
1793 : && strcasecmp(datasource,table_def->data_source) == 0
1794 : && strcasecmp(table,table_def->table_name) == 0 )
1795 0 : return i;
1796 : }
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Add a new entry to the tables table. */
1800 : /* -------------------------------------------------------------------- */
1801 77 : select_info->table_defs =
1802 : swq_realloc( select_info->table_defs,
1803 : sizeof(swq_table_def) * (select_info->table_count),
1804 : sizeof(swq_table_def) * (select_info->table_count+1) );
1805 :
1806 : /* -------------------------------------------------------------------- */
1807 : /* Populate the new entry. */
1808 : /* -------------------------------------------------------------------- */
1809 77 : if( alias == NULL )
1810 62 : alias = swq_strdup( table );
1811 :
1812 77 : select_info->table_defs[select_info->table_count].data_source = datasource;
1813 77 : select_info->table_defs[select_info->table_count].table_name = table;
1814 77 : select_info->table_defs[select_info->table_count].table_alias = alias;
1815 :
1816 77 : select_info->table_count++;
1817 :
1818 77 : return select_info->table_count - 1;
1819 : }
1820 :
1821 : /************************************************************************/
1822 : /* swq_select_expand_wildcard() */
1823 : /* */
1824 : /* This function replaces the '*' in a "SELECT *" with the list */
1825 : /* provided list of fields. Itis used by swq_select_parse(), */
1826 : /* but may be called in advance by applications wanting the */
1827 : /* "default" field list to be different than the full list of */
1828 : /* fields. */
1829 : /************************************************************************/
1830 :
1831 : const char *swq_select_expand_wildcard( swq_select *select_info,
1832 : swq_field_list *field_list )
1833 :
1834 126 : {
1835 : int isrc;
1836 :
1837 : /* ==================================================================== */
1838 : /* Check each pre-expansion field. */
1839 : /* ==================================================================== */
1840 511 : for( isrc = 0; isrc < select_info->result_columns; isrc++ )
1841 : {
1842 385 : const char *src_fieldname = select_info->column_defs[isrc].field_name;
1843 : int itable, new_fields, i, iout;
1844 :
1845 385 : if( src_fieldname[strlen(src_fieldname)-1] != '*' )
1846 342 : continue;
1847 :
1848 : /* We don't want to expand COUNT(*) */
1849 43 : if( select_info->column_defs[isrc].col_func_name != NULL )
1850 2 : continue;
1851 :
1852 : /* -------------------------------------------------------------------- */
1853 : /* Parse out the table name, verify it, and establish the */
1854 : /* number of fields to insert from it. */
1855 : /* -------------------------------------------------------------------- */
1856 41 : if( strcmp(src_fieldname,"*") == 0 )
1857 : {
1858 31 : itable = -1;
1859 31 : new_fields = field_list->count;
1860 : }
1861 10 : else if( strlen(src_fieldname) < 3
1862 : || src_fieldname[strlen(src_fieldname)-2] != '.' )
1863 : {
1864 0 : SNPRINTF_ERR2( "Ill formatted field definition '%s'.",
1865 : src_fieldname );
1866 0 : return swq_get_errbuf();
1867 : }
1868 : else
1869 : {
1870 10 : char *table_name = swq_strdup( src_fieldname );
1871 10 : table_name[strlen(src_fieldname)-2] = '\0';
1872 :
1873 12 : for( itable = 0; itable < field_list->table_count; itable++ )
1874 : {
1875 12 : if( strcasecmp(table_name,
1876 : field_list->table_defs[itable].table_alias ) == 0 )
1877 10 : break;
1878 : }
1879 :
1880 10 : if( itable == field_list->table_count )
1881 : {
1882 0 : SNPRINTF_ERR3(
1883 : "Table %s not recognised from %s definition.",
1884 : table_name, src_fieldname );
1885 0 : swq_free( table_name );
1886 0 : return swq_get_errbuf();
1887 : }
1888 10 : swq_free( table_name );
1889 :
1890 : /* count the number of fields in this table. */
1891 10 : new_fields = 0;
1892 66 : for( i = 0; i < field_list->count; i++ )
1893 : {
1894 56 : if( field_list->table_ids[i] == itable )
1895 30 : new_fields++;
1896 : }
1897 : }
1898 :
1899 41 : if (new_fields > 0)
1900 : {
1901 : /* -------------------------------------------------------------------- */
1902 : /* Reallocate the column list larger. */
1903 : /* -------------------------------------------------------------------- */
1904 39 : SWQ_FREE( select_info->column_defs[isrc].field_name );
1905 39 : select_info->column_defs = (swq_col_def *)
1906 : swq_realloc( select_info->column_defs,
1907 : sizeof(swq_col_def) * select_info->result_columns,
1908 : sizeof(swq_col_def) *
1909 : (select_info->result_columns + new_fields - 1 ) );
1910 :
1911 : /* -------------------------------------------------------------------- */
1912 : /* Push the old definitions that came after the one to be */
1913 : /* replaced further up in the array. */
1914 : /* -------------------------------------------------------------------- */
1915 39 : if (new_fields != 1)
1916 : {
1917 43 : for( i = select_info->result_columns-1; i > isrc; i-- )
1918 : {
1919 7 : memcpy( select_info->column_defs + i + new_fields - 1,
1920 : select_info->column_defs + i,
1921 : sizeof( swq_col_def ) );
1922 : }
1923 : }
1924 :
1925 39 : select_info->result_columns += (new_fields - 1 );
1926 :
1927 : /* -------------------------------------------------------------------- */
1928 : /* Zero out all the stuff in the target column definitions. */
1929 : /* -------------------------------------------------------------------- */
1930 39 : memset( select_info->column_defs + isrc, 0,
1931 : new_fields * sizeof(swq_col_def) );
1932 : }
1933 : else
1934 : {
1935 : /* -------------------------------------------------------------------- */
1936 : /* The wildcard expands to nothing */
1937 : /* -------------------------------------------------------------------- */
1938 2 : SWQ_FREE( select_info->column_defs[isrc].field_name );
1939 2 : memmove( select_info->column_defs + isrc,
1940 : select_info->column_defs + isrc + 1,
1941 : sizeof( swq_col_def ) * (select_info->result_columns-1-isrc) );
1942 :
1943 2 : select_info->result_columns --;
1944 : }
1945 :
1946 : /* -------------------------------------------------------------------- */
1947 : /* Assign the selected fields. */
1948 : /* -------------------------------------------------------------------- */
1949 41 : iout = isrc;
1950 :
1951 194 : for( i = 0; i < field_list->count; i++ )
1952 : {
1953 : swq_col_def *def;
1954 153 : int compose = itable != -1;
1955 :
1956 : /* skip this field if it isn't in the target table. */
1957 153 : if( itable != -1 && field_list->table_ids != NULL
1958 : && itable != field_list->table_ids[i] )
1959 26 : continue;
1960 :
1961 : /* set up some default values. */
1962 127 : def = select_info->column_defs + iout;
1963 127 : def->field_precision = -1;
1964 127 : def->target_type = SWQ_OTHER;
1965 :
1966 : /* does this field duplicate an earlier one? */
1967 127 : if( field_list->table_ids != NULL
1968 : && field_list->table_ids[i] != 0
1969 : && !compose )
1970 : {
1971 : int other;
1972 :
1973 71 : for( other = 0; other < i; other++ )
1974 : {
1975 59 : if( strcasecmp(field_list->names[i],
1976 : field_list->names[other]) == 0 )
1977 : {
1978 3 : compose = 1;
1979 3 : break;
1980 : }
1981 : }
1982 : }
1983 :
1984 127 : if( !compose )
1985 94 : def->field_name = swq_strdup( field_list->names[i] );
1986 : else
1987 : {
1988 33 : int itable = field_list->table_ids[i];
1989 : char *composed_name;
1990 33 : const char *field_name = field_list->names[i];
1991 : const char *table_alias =
1992 33 : field_list->table_defs[itable].table_alias;
1993 :
1994 33 : composed_name = (char *)
1995 : swq_malloc(strlen(field_name)+strlen(table_alias)+2);
1996 :
1997 33 : sprintf( composed_name, "%s.%s", table_alias, field_name );
1998 :
1999 33 : def->field_name = composed_name;
2000 : }
2001 :
2002 127 : iout++;
2003 :
2004 : /* All the other table info will be provided by the later
2005 : parse operation. */
2006 : }
2007 :
2008 : /* If there are several occurrences of '*', go on, but stay on the */
2009 : /* same index in case '*' is expanded to nothing */
2010 : /* (the -- is to compensate the fact that isrc will be incremented in */
2011 : /* the after statement of the for loop) */
2012 41 : isrc --;
2013 : }
2014 :
2015 :
2016 :
2017 126 : return NULL;
2018 : }
2019 :
2020 : /************************************************************************/
2021 : /* swq_select_parse() */
2022 : /************************************************************************/
2023 :
2024 : const char *swq_select_parse( swq_select *select_info,
2025 : swq_field_list *field_list,
2026 : int parse_flags )
2027 :
2028 63 : {
2029 : int i;
2030 : const char *error;
2031 :
2032 63 : error = swq_select_expand_wildcard( select_info, field_list );
2033 63 : if( error != NULL )
2034 0 : return error;
2035 :
2036 : /* -------------------------------------------------------------------- */
2037 : /* Identify field information. */
2038 : /* -------------------------------------------------------------------- */
2039 235 : for( i = 0; i < select_info->result_columns; i++ )
2040 : {
2041 172 : swq_col_def *def = select_info->column_defs + i;
2042 : swq_field_type this_type;
2043 :
2044 : /* identify field */
2045 172 : def->field_index = swq_identify_field( def->field_name, field_list,
2046 : &this_type,
2047 : &(def->table_index) );
2048 :
2049 : /* record field type */
2050 172 : def->field_type = this_type;
2051 :
2052 : /* identify column function if present */
2053 172 : if( def->col_func_name != NULL )
2054 : {
2055 3 : if( strcasecmp(def->col_func_name,"AVG") == 0 )
2056 0 : def->col_func = SWQCF_AVG;
2057 3 : else if( strcasecmp(def->col_func_name,"MIN") == 0 )
2058 0 : def->col_func = SWQCF_MIN;
2059 3 : else if( strcasecmp(def->col_func_name,"MAX") == 0 )
2060 2 : def->col_func = SWQCF_MAX;
2061 1 : else if( strcasecmp(def->col_func_name,"SUM") == 0 )
2062 0 : def->col_func = SWQCF_SUM;
2063 1 : else if( strcasecmp(def->col_func_name,"COUNT") == 0 )
2064 1 : def->col_func = SWQCF_COUNT;
2065 : else
2066 : {
2067 0 : def->col_func = SWQCF_CUSTOM;
2068 0 : if( !(parse_flags & SWQP_ALLOW_UNDEFINED_COL_FUNCS) )
2069 : {
2070 0 : SNPRINTF_ERR2( "Unrecognised field function %s.",
2071 : def->col_func_name );
2072 0 : return swq_get_errbuf();
2073 : }
2074 : }
2075 :
2076 3 : if( (def->col_func == SWQCF_MIN
2077 : || def->col_func == SWQCF_MAX
2078 : || def->col_func == SWQCF_AVG
2079 : || def->col_func == SWQCF_SUM)
2080 : && this_type == SWQ_STRING )
2081 : {
2082 0 : SNPRINTF_ERR3(
2083 : "Use of field function %s() on string field %s illegal.",
2084 : def->col_func_name, def->field_name );
2085 0 : return swq_get_errbuf();
2086 : }
2087 : }
2088 : else
2089 169 : def->col_func = SWQCF_NONE;
2090 :
2091 172 : if( def->field_index == -1 && def->col_func != SWQCF_COUNT )
2092 : {
2093 0 : SNPRINTF_ERR2( "Unrecognised field name %s.",
2094 : def->field_name );
2095 0 : return swq_get_errbuf();
2096 : }
2097 : }
2098 :
2099 : /* -------------------------------------------------------------------- */
2100 : /* Check if we are producing a one row summary result or a set */
2101 : /* of records. Generate an error if we get conflicting */
2102 : /* indications. */
2103 : /* -------------------------------------------------------------------- */
2104 63 : select_info->query_mode = -1;
2105 235 : for( i = 0; i < select_info->result_columns; i++ )
2106 : {
2107 172 : swq_col_def *def = select_info->column_defs + i;
2108 172 : int this_indicator = -1;
2109 :
2110 175 : if( def->col_func == SWQCF_MIN
2111 : || def->col_func == SWQCF_MAX
2112 : || def->col_func == SWQCF_AVG
2113 : || def->col_func == SWQCF_SUM
2114 : || def->col_func == SWQCF_COUNT )
2115 3 : this_indicator = SWQM_SUMMARY_RECORD;
2116 169 : else if( def->col_func == SWQCF_NONE )
2117 : {
2118 169 : if( def->distinct_flag )
2119 8 : this_indicator = SWQM_DISTINCT_LIST;
2120 : else
2121 161 : this_indicator = SWQM_RECORDSET;
2122 : }
2123 :
2124 172 : if( this_indicator != select_info->query_mode
2125 : && this_indicator != -1
2126 : && select_info->query_mode != -1 )
2127 : {
2128 0 : return "Field list implies mixture of regular recordset mode, summary mode or distinct field list mode.";
2129 : }
2130 :
2131 172 : if( this_indicator != -1 )
2132 172 : select_info->query_mode = this_indicator;
2133 : }
2134 :
2135 63 : if( select_info->result_columns > 1
2136 : && select_info->query_mode == SWQM_DISTINCT_LIST )
2137 : {
2138 0 : return "SELECTing more than one DISTINCT field is a query not supported.";
2139 : }
2140 63 : else if (select_info->result_columns == 0)
2141 : {
2142 1 : select_info->query_mode = SWQM_RECORDSET;
2143 : }
2144 :
2145 : /* -------------------------------------------------------------------- */
2146 : /* Process column names in JOIN specs. */
2147 : /* -------------------------------------------------------------------- */
2148 77 : for( i = 0; i < select_info->join_count; i++ )
2149 : {
2150 14 : swq_join_def *def = select_info->join_defs + i;
2151 : int table_id;
2152 :
2153 : /* identify primary field */
2154 14 : def->primary_field = swq_identify_field( def->primary_field_name,
2155 : field_list, NULL, &table_id );
2156 14 : if( def->primary_field == -1 )
2157 : {
2158 0 : SNPRINTF_ERR2(
2159 : "Unrecognised primary field %s in JOIN clause..",
2160 : def->primary_field_name );
2161 0 : return swq_get_errbuf();
2162 : }
2163 :
2164 14 : if( table_id != 0 )
2165 : {
2166 0 : SNPRINTF_ERR2(
2167 : "Currently the primary key must come from the primary table in\n"
2168 : "JOIN, %s is not from the primary table.",
2169 : def->primary_field_name );
2170 0 : return swq_get_errbuf();
2171 : }
2172 :
2173 : /* identify secondary field */
2174 14 : def->secondary_field = swq_identify_field( def->secondary_field_name,
2175 : field_list, NULL,&table_id);
2176 14 : if( def->secondary_field == -1 )
2177 : {
2178 0 : SNPRINTF_ERR2(
2179 : "Unrecognised secondary field %s in JOIN clause..",
2180 : def->primary_field_name );
2181 0 : return swq_get_errbuf();
2182 : }
2183 :
2184 14 : if( table_id != def->secondary_table )
2185 : {
2186 0 : SNPRINTF_ERR3(
2187 : "Currently the secondary key must come from the secondary table\n"
2188 : "listed in the JOIN. %s is not from table %s..",
2189 : def->primary_field_name,
2190 : select_info->table_defs[def->secondary_table].table_name);
2191 0 : return swq_get_errbuf();
2192 : }
2193 : }
2194 :
2195 : /* -------------------------------------------------------------------- */
2196 : /* Process column names in order specs. */
2197 : /* -------------------------------------------------------------------- */
2198 78 : for( i = 0; i < select_info->order_specs; i++ )
2199 : {
2200 15 : swq_order_def *def = select_info->order_defs + i;
2201 :
2202 : /* identify field */
2203 15 : def->field_index = swq_identify_field( def->field_name, field_list,
2204 : NULL, &(def->table_index) );
2205 15 : if( def->field_index == -1 )
2206 : {
2207 0 : SNPRINTF_ERR2( "Unrecognised field name %s in ORDER BY.",
2208 : def->field_name );
2209 0 : return swq_get_errbuf();
2210 : }
2211 : }
2212 :
2213 : /* -------------------------------------------------------------------- */
2214 : /* Parse the WHERE clause. */
2215 : /* -------------------------------------------------------------------- */
2216 63 : if( select_info->whole_where_clause != NULL )
2217 : {
2218 : const char *error;
2219 :
2220 24 : error = swq_expr_compile2( select_info->whole_where_clause,
2221 : field_list, &(select_info->where_expr) );
2222 :
2223 24 : if( error != NULL )
2224 0 : return error;
2225 : }
2226 :
2227 63 : return NULL;
2228 : }
2229 :
2230 : /************************************************************************/
2231 : /* swq_select_summarize() */
2232 : /************************************************************************/
2233 :
2234 : const char *
2235 : swq_select_summarize( swq_select *select_info,
2236 : int dest_column, const char *value )
2237 :
2238 180 : {
2239 180 : swq_col_def *def = select_info->column_defs + dest_column;
2240 : swq_summary *summary;
2241 :
2242 : /* -------------------------------------------------------------------- */
2243 : /* Do various checking. */
2244 : /* -------------------------------------------------------------------- */
2245 : if( !select_info->query_mode == SWQM_RECORDSET )
2246 : return "swq_select_summarize() called on non-summary query.";
2247 :
2248 180 : if( dest_column < 0 || dest_column >= select_info->result_columns )
2249 0 : return "dest_column out of range in swq_select_summarize().";
2250 :
2251 180 : if( def->col_func == SWQCF_NONE && !def->distinct_flag )
2252 0 : return NULL;
2253 :
2254 : /* -------------------------------------------------------------------- */
2255 : /* Create the summary information if this is the first row */
2256 : /* being processed. */
2257 : /* -------------------------------------------------------------------- */
2258 180 : if( select_info->column_summary == NULL )
2259 : {
2260 : int i;
2261 :
2262 8 : select_info->column_summary = (swq_summary *)
2263 : SWQ_MALLOC(sizeof(swq_summary) * select_info->result_columns);
2264 8 : memset( select_info->column_summary, 0,
2265 : sizeof(swq_summary) * select_info->result_columns );
2266 :
2267 16 : for( i = 0; i < select_info->result_columns; i++ )
2268 : {
2269 8 : select_info->column_summary[i].min = 1e20;
2270 8 : select_info->column_summary[i].max = -1e20;
2271 : }
2272 : }
2273 :
2274 : /* -------------------------------------------------------------------- */
2275 : /* If distinct processing is on, process that now. */
2276 : /* -------------------------------------------------------------------- */
2277 180 : summary = select_info->column_summary + dest_column;
2278 :
2279 180 : if( def->distinct_flag )
2280 : {
2281 : int i;
2282 :
2283 : /* This should be implemented with a much more complicated
2284 : data structure to achieve any sort of efficiency. */
2285 771 : for( i = 0; i < summary->count; i++ )
2286 : {
2287 722 : if( strcmp(value,summary->distinct_list[i]) == 0 )
2288 124 : break;
2289 : }
2290 :
2291 173 : if( i == summary->count )
2292 : {
2293 49 : char **old_list = summary->distinct_list;
2294 :
2295 49 : summary->distinct_list = (char **)
2296 : SWQ_MALLOC(sizeof(char *) * (summary->count+1));
2297 49 : memcpy( summary->distinct_list, old_list,
2298 : sizeof(char *) * summary->count );
2299 49 : summary->distinct_list[(summary->count)++] =
2300 : swq_strdup( value );
2301 :
2302 49 : SWQ_FREE(old_list);
2303 : }
2304 : }
2305 :
2306 : /* -------------------------------------------------------------------- */
2307 : /* Process various options. */
2308 : /* -------------------------------------------------------------------- */
2309 :
2310 180 : switch( def->col_func )
2311 : {
2312 : case SWQCF_MIN:
2313 0 : if( value != NULL && value[0] != '\0' )
2314 : {
2315 0 : double df_val = CPLAtof(value);
2316 0 : if( df_val < summary->min )
2317 0 : summary->min = df_val;
2318 : }
2319 0 : break;
2320 : case SWQCF_MAX:
2321 7 : if( value != NULL && value[0] != '\0' )
2322 : {
2323 7 : double df_val = CPLAtof(value);
2324 7 : if( df_val > summary->max )
2325 2 : summary->max = df_val;
2326 : }
2327 7 : break;
2328 : case SWQCF_AVG:
2329 : case SWQCF_SUM:
2330 0 : if( value != NULL && value[0] != '\0' )
2331 : {
2332 0 : summary->count++;
2333 0 : summary->sum += CPLAtof(value);
2334 : }
2335 0 : break;
2336 :
2337 : case SWQCF_COUNT:
2338 0 : if( value != NULL && !def->distinct_flag )
2339 0 : summary->count++;
2340 0 : break;
2341 :
2342 : case SWQCF_NONE:
2343 173 : break;
2344 :
2345 : case SWQCF_CUSTOM:
2346 0 : return "swq_select_summarize() called on custom field function.";
2347 :
2348 : default:
2349 0 : return "swq_select_summarize() - unexpected col_func";
2350 : }
2351 :
2352 180 : return NULL;
2353 : }
2354 : /************************************************************************/
2355 : /* sort comparison functions. */
2356 : /************************************************************************/
2357 :
2358 : static int FORCE_CDECL swq_compare_int( const void *item1, const void *item2 )
2359 50 : {
2360 : int v1, v2;
2361 :
2362 50 : v1 = atoi(*((const char **) item1));
2363 50 : v2 = atoi(*((const char **) item2));
2364 :
2365 50 : if( v1 < v2 )
2366 16 : return -1;
2367 34 : else if( v1 == v2 )
2368 0 : return 0;
2369 : else
2370 34 : return 1;
2371 : }
2372 :
2373 : static int FORCE_CDECL swq_compare_real( const void *item1, const void *item2 )
2374 23 : {
2375 : double v1, v2;
2376 :
2377 23 : v1 = CPLAtof(*((const char **) item1));
2378 23 : v2 = CPLAtof(*((const char **) item2));
2379 :
2380 23 : if( v1 < v2 )
2381 9 : return -1;
2382 14 : else if( v1 == v2 )
2383 0 : return 0;
2384 : else
2385 14 : return 1;
2386 : }
2387 :
2388 : static int FORCE_CDECL swq_compare_string( const void *item1, const void *item2 )
2389 12 : {
2390 12 : return strcmp( *((const char **) item1), *((const char **) item2) );
2391 : }
2392 :
2393 : /************************************************************************/
2394 : /* swq_select_finish_summarize() */
2395 : /* */
2396 : /* Call to complete summarize work. Does stuff like ordering */
2397 : /* the distinct list for instance. */
2398 : /************************************************************************/
2399 :
2400 : const char *swq_select_finish_summarize( swq_select *select_info )
2401 :
2402 10 : {
2403 : int (FORCE_CDECL *compare_func)(const void *, const void*);
2404 10 : int count = 0;
2405 10 : char **distinct_list = NULL;
2406 :
2407 10 : if( select_info->query_mode != SWQM_DISTINCT_LIST
2408 : || select_info->order_specs == 0 )
2409 5 : return NULL;
2410 :
2411 5 : if( select_info->order_specs > 1 )
2412 0 : return "Can't ORDER BY a DISTINCT list by more than one key.";
2413 :
2414 5 : if( select_info->order_defs[0].field_index !=
2415 : select_info->column_defs[0].field_index )
2416 0 : return "Only selected DISTINCT field can be used for ORDER BY.";
2417 :
2418 5 : if( select_info->column_summary == NULL )
2419 0 : return NULL;
2420 :
2421 5 : if( select_info->column_defs[0].field_type == SWQ_INTEGER )
2422 2 : compare_func = swq_compare_int;
2423 3 : else if( select_info->column_defs[0].field_type == SWQ_FLOAT )
2424 2 : compare_func = swq_compare_real;
2425 : else
2426 1 : compare_func = swq_compare_string;
2427 :
2428 5 : distinct_list = select_info->column_summary[0].distinct_list;
2429 5 : count = select_info->column_summary[0].count;
2430 :
2431 5 : qsort( distinct_list, count, sizeof(char *), compare_func );
2432 :
2433 : /* -------------------------------------------------------------------- */
2434 : /* Do we want the list ascending in stead of descending? */
2435 : /* -------------------------------------------------------------------- */
2436 5 : if( !select_info->order_defs[0].ascending_flag )
2437 : {
2438 : char *saved;
2439 : int i;
2440 :
2441 15 : for( i = 0; i < count/2; i++ )
2442 : {
2443 12 : saved = distinct_list[i];
2444 12 : distinct_list[i] = distinct_list[count-i-1];
2445 12 : distinct_list[count-i-1] = saved;
2446 : }
2447 : }
2448 :
2449 5 : return NULL;
2450 : }
2451 :
2452 : /************************************************************************/
2453 : /* swq_select_free() */
2454 : /************************************************************************/
2455 :
2456 : void swq_select_free( swq_select *select_info )
2457 :
2458 63 : {
2459 : int i;
2460 :
2461 63 : if( select_info == NULL )
2462 0 : return;
2463 :
2464 63 : if( select_info->where_expr != NULL )
2465 24 : swq_expr_free(select_info->where_expr);
2466 :
2467 63 : if( select_info->raw_select != NULL )
2468 63 : SWQ_FREE( select_info->raw_select );
2469 :
2470 63 : if( select_info->whole_where_clause != NULL )
2471 24 : SWQ_FREE( select_info->whole_where_clause );
2472 :
2473 140 : for( i = 0; i < select_info->table_count; i++ )
2474 : {
2475 77 : swq_table_def *table_def = select_info->table_defs + i;
2476 :
2477 77 : if( table_def->data_source != NULL )
2478 4 : SWQ_FREE( table_def->data_source );
2479 77 : SWQ_FREE( table_def->table_name );
2480 77 : SWQ_FREE( table_def->table_alias );
2481 : }
2482 63 : if( select_info->table_defs != NULL )
2483 63 : SWQ_FREE( select_info->table_defs );
2484 :
2485 235 : for( i = 0; i < select_info->result_columns; i++ )
2486 : {
2487 172 : if( select_info->column_defs[i].field_name != NULL )
2488 172 : SWQ_FREE( select_info->column_defs[i].field_name );
2489 172 : if( select_info->column_defs[i].field_alias != NULL )
2490 0 : SWQ_FREE( select_info->column_defs[i].field_alias );
2491 172 : if( select_info->column_defs[i].col_func_name != NULL )
2492 3 : SWQ_FREE( select_info->column_defs[i].col_func_name );
2493 :
2494 172 : if( select_info->column_summary != NULL
2495 : && select_info->column_summary[i].distinct_list != NULL )
2496 : {
2497 : int j;
2498 :
2499 56 : for( j = 0; j < select_info->column_summary[i].count; j++ )
2500 49 : SWQ_FREE( select_info->column_summary[i].distinct_list[j] );
2501 :
2502 7 : SWQ_FREE( select_info->column_summary[i].distinct_list );
2503 : }
2504 : }
2505 :
2506 63 : if( select_info->column_defs != NULL )
2507 63 : SWQ_FREE( select_info->column_defs );
2508 :
2509 63 : if( select_info->column_summary != NULL )
2510 8 : SWQ_FREE( select_info->column_summary );
2511 :
2512 78 : for( i = 0; i < select_info->order_specs; i++ )
2513 : {
2514 15 : if( select_info->order_defs[i].field_name != NULL )
2515 15 : SWQ_FREE( select_info->order_defs[i].field_name );
2516 : }
2517 :
2518 63 : if( select_info->order_defs != NULL )
2519 15 : SWQ_FREE( select_info->order_defs );
2520 :
2521 77 : for( i = 0; i < select_info->join_count; i++ )
2522 : {
2523 14 : SWQ_FREE( select_info->join_defs[i].primary_field_name );
2524 14 : if( select_info->join_defs[i].secondary_field_name != NULL )
2525 14 : SWQ_FREE( select_info->join_defs[i].secondary_field_name );
2526 : }
2527 63 : if( select_info->join_defs != NULL )
2528 13 : SWQ_FREE( select_info->join_defs );
2529 :
2530 63 : SWQ_FREE( select_info );
2531 : }
2532 :
2533 : /************************************************************************/
2534 : /* swq_reform_command() */
2535 : /* */
2536 : /* Rebuild the command string from the components in the */
2537 : /* swq_select structure. The where expression is taken from */
2538 : /* the whole_where_clause instead of being reformed. The */
2539 : /* results of forming the command are applied to the raw_select */
2540 : /* field. */
2541 : /************************************************************************/
2542 :
2543 : #define CHECK_COMMAND( new_bytes ) grow_command( &command, &max_cmd_size, &cmd_size, new_bytes );
2544 :
2545 : static void grow_command( char **p_command, int *max_cmd_size, int *cmd_size,
2546 : int new_bytes );
2547 :
2548 : const char *swq_reform_command( swq_select *select_info )
2549 :
2550 0 : {
2551 : char *command;
2552 0 : int max_cmd_size = 10;
2553 0 : int cmd_size = 0;
2554 : int i;
2555 :
2556 0 : command = SWQ_MALLOC(max_cmd_size);
2557 :
2558 0 : strcpy( command, "SELECT " );
2559 :
2560 : /* -------------------------------------------------------------------- */
2561 : /* Handle the field list. */
2562 : /* -------------------------------------------------------------------- */
2563 0 : for( i = 0; i < select_info->result_columns; i++ )
2564 : {
2565 0 : swq_col_def *def = select_info->column_defs + i;
2566 0 : const char *distinct = "";
2567 :
2568 0 : if( def->distinct_flag )
2569 0 : distinct = "DISTINCT ";
2570 :
2571 0 : if( i != 0 )
2572 : {
2573 0 : CHECK_COMMAND(3);
2574 0 : strcat( command + cmd_size, ", " );
2575 : }
2576 :
2577 0 : if( def->col_func_name != NULL )
2578 : {
2579 0 : CHECK_COMMAND( strlen(def->col_func_name)
2580 : + strlen(def->field_name) + 15 );
2581 0 : sprintf( command + cmd_size, "%s(%s%s)",
2582 : def->col_func_name, distinct, def->field_name );
2583 : }
2584 : else
2585 : {
2586 0 : CHECK_COMMAND( strlen(def->field_name) + 15 );
2587 0 : sprintf( command + cmd_size, "%s\"%s\"",
2588 : distinct, def->field_name );
2589 : }
2590 : }
2591 :
2592 : /* -------------------------------------------------------------------- */
2593 : /* Handle the FROM tablename. */
2594 : /* -------------------------------------------------------------------- */
2595 0 : if( select_info->table_count > 0 )
2596 : {
2597 0 : CHECK_COMMAND( 10 + strlen(select_info->table_defs[0].table_name) );
2598 0 : sprintf( command + cmd_size, " FROM \"%s\"",
2599 : select_info->table_defs[0].table_name );
2600 : }
2601 :
2602 : /* -------------------------------------------------------------------- */
2603 : /* Handle the JOIN clause(s). */
2604 : /* -------------------------------------------------------------------- */
2605 : /* TODO notdef */
2606 :
2607 : /* -------------------------------------------------------------------- */
2608 : /* Add WHERE statement if it exists. */
2609 : /* -------------------------------------------------------------------- */
2610 0 : if( select_info->whole_where_clause != NULL )
2611 : {
2612 0 : CHECK_COMMAND( 12 + strlen(select_info->whole_where_clause) );
2613 0 : sprintf( command + cmd_size, " WHERE %s",
2614 : select_info->whole_where_clause );
2615 : }
2616 :
2617 : /* -------------------------------------------------------------------- */
2618 : /* Add order by clause(s) if appropriate. */
2619 : /* -------------------------------------------------------------------- */
2620 0 : for( i = 0; i < select_info->order_specs; i++ )
2621 : {
2622 0 : swq_order_def *def = select_info->order_defs + i;
2623 :
2624 0 : if( i == 0 )
2625 : {
2626 0 : CHECK_COMMAND( 12 );
2627 0 : sprintf( command + cmd_size, " ORDER BY " );
2628 : }
2629 : else
2630 : {
2631 0 : CHECK_COMMAND( 3 );
2632 0 : sprintf( command + cmd_size, ", " );
2633 : }
2634 :
2635 0 : CHECK_COMMAND( strlen(def->field_name)+1 );
2636 0 : sprintf( command + cmd_size, "\"%s\"", def->field_name );
2637 :
2638 0 : CHECK_COMMAND( 6 );
2639 0 : if( def->ascending_flag )
2640 0 : strcat( command + cmd_size, " ASC" );
2641 : else
2642 0 : strcat( command + cmd_size, " DESC" );
2643 : }
2644 :
2645 : /* -------------------------------------------------------------------- */
2646 : /* Assign back to the select info. */
2647 : /* -------------------------------------------------------------------- */
2648 0 : SWQ_FREE( select_info->raw_select );
2649 0 : select_info->raw_select = command;
2650 :
2651 0 : return NULL;
2652 : }
2653 :
2654 : /* helper for the swq_reform_command() function. */
2655 :
2656 : static void grow_command( char **p_command, int *max_cmd_size, int *cmd_size,
2657 : int new_bytes )
2658 :
2659 0 : {
2660 : char *new_command;
2661 :
2662 0 : *cmd_size += strlen(*p_command + *cmd_size);
2663 :
2664 0 : if( *cmd_size + new_bytes < *max_cmd_size - 1 )
2665 0 : return;
2666 :
2667 0 : *max_cmd_size = 2 * *max_cmd_size;
2668 0 : if( *max_cmd_size < *cmd_size + new_bytes )
2669 0 : *max_cmd_size = *cmd_size + new_bytes + 100;
2670 :
2671 0 : new_command = SWQ_MALLOC(*max_cmd_size);
2672 :
2673 0 : strcpy( new_command, *p_command );
2674 0 : SWQ_FREE( *p_command );
2675 0 : *p_command = new_command;
2676 : }
|