- action.c is getting too big, so split out all the handling of
[wine] / dlls / msi / sql.y
1 %{
2
3 /*
4  * Implementation of the Microsoft Installer (msi.dll)
5  *
6  * Copyright 2002-2004 Mike McCormack for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23
24 #include "config.h"
25
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "query.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35
36 #define YYLEX_PARAM info
37 #define YYPARSE_PARAM info
38
39 extern int SQL_error(const char *str);
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 typedef struct tag_SQL_input
44 {
45     MSIDATABASE *db;
46     LPCWSTR command;
47     DWORD n, len;
48     MSIVIEW **view;  /* view structure for the resulting query */
49 } SQL_input;
50
51 static LPWSTR SQL_getstring( struct sql_str *str );
52 static INT SQL_getint( SQL_input *sql );
53 static int SQL_lex( void *SQL_lval, SQL_input *info);
54
55 static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
56                                string_list *columns );
57 static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
58                              string_list *columns );
59
60 static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
61                                  string_list *keys);
62
63 static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r );
64 static struct expr * EXPR_column( LPWSTR );
65 static struct expr * EXPR_ival( struct sql_str *, int sign);
66 static struct expr * EXPR_sval( struct sql_str *);
67 static struct expr * EXPR_wildcard();
68
69 %}
70
71 %pure-parser
72
73 %union
74 {
75     struct sql_str str;
76     LPWSTR string;
77     string_list *column_list;
78     value_list *val_list;
79     MSIVIEW *query;
80     struct expr *expr;
81     USHORT column_type;
82     create_col_info *column_info;
83     column_assignment update_col_info;
84 }
85
86 %token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC
87 %token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY
88 %token TK_CASCADE TK_CASE TK_CHAR TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN
89 %token TK_COMMA TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT 
90 %token TK_CONSTRAINT TK_COPY TK_CREATE
91 %token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC
92 %token TK_DISTINCT TK_DOT TK_DROP TK_EACH
93 %token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN
94 %token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION
95 %token TK_GE TK_GLOB TK_GROUP TK_GT
96 %token TK_HAVING TK_HOLD
97 %token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
98 %token <str> TK_ID 
99 %token TK_INSERT TK_INSTEAD TK_INT 
100 %token <str> TK_INTEGER
101 %token TK_INTERSECT TK_INTO TK_IS
102 %token TK_ISNULL
103 %token TK_JOIN TK_JOIN_KW
104 %token TK_KEY
105 %token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT
106 %token TK_LOCALIZABLE
107 %token TK_MATCH TK_MINUS
108 %token TK_NE TK_NOT TK_NOTNULL TK_NULL
109 %token TK_OBJECT TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER
110 %token TK_PLUS TK_PRAGMA TK_PRIMARY
111 %token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK
112 %token TK_ROW TK_RP TK_RSHIFT
113 %token TK_SELECT TK_SEMI TK_SET TK_SHORT TK_SLASH TK_SPACE TK_STAR TK_STATEMENT 
114 %token <str> TK_STRING
115 %token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER
116 %token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE
117 %token TK_UPDATE TK_UPLUS TK_USING
118 %token TK_VACUUM TK_VALUES TK_VIEW
119 %token TK_WHEN TK_WHERE TK_WILDCARD
120
121 /*
122  * These are extra tokens used by the lexer but never seen by the
123  * parser.  We put them in a rule so that the parser generator will
124  * add them to the parse.h output file.
125  *
126  */
127 %nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
128           COLUMN AGG_FUNCTION.
129
130 %type <string> column table string_or_id
131 %type <column_list> selcollist
132 %type <query> from unorderedsel oneselect onequery onecreate oneinsert oneupdate
133 %type <expr> expr val column_val const_val
134 %type <column_type> column_type data_type data_type_l data_count
135 %type <column_info> column_def table_def
136 %type <val_list> constlist
137 %type <update_col_info> column_assignment update_assign_list
138
139 %%
140
141 onequery:
142     oneselect
143     {
144         SQL_input* sql = (SQL_input*) info;
145         *sql->view = $1;
146     }
147   | onecreate
148     {
149         SQL_input* sql = (SQL_input*) info;
150         *sql->view = $1;
151     }
152   | oneinsert
153     {
154         SQL_input* sql = (SQL_input*) info;
155         *sql->view = $1;
156     }
157   | oneupdate
158     {
159         SQL_input* sql = (SQL_input*) info;
160         *sql->view = $1;
161     }
162     ;
163
164 oneinsert:
165     TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP
166     {
167         SQL_input *sql = (SQL_input*) info;
168         MSIVIEW *insert = NULL; 
169
170         INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); 
171         $$ = insert;
172     }
173   | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP
174     {
175         SQL_input *sql = (SQL_input*) info;
176         MSIVIEW *insert = NULL; 
177
178         INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); 
179         $$ = insert;
180     }
181     ;
182
183 onecreate:
184     TK_CREATE TK_TABLE table TK_LP table_def TK_RP
185         {
186             SQL_input* sql = (SQL_input*) info;
187             MSIVIEW *create = NULL; 
188
189             if( !$5 )
190                 YYABORT;
191             CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
192             $$ = create;
193         }
194   | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD
195         {
196             SQL_input* sql = (SQL_input*) info;
197             MSIVIEW *create = NULL; 
198
199             if( !$5 )
200                 YYABORT;
201             CREATE_CreateView( sql->db, &create, $3, $5, TRUE );
202             $$ = create;
203         }
204     ;
205
206 oneupdate:
207     TK_UPDATE table TK_SET update_assign_list TK_WHERE expr
208         {
209             SQL_input* sql = (SQL_input*) info;
210             MSIVIEW *update = NULL; 
211
212             UPDATE_CreateView( sql->db, &update, $2, &$4, $6 );
213             $$ = update;
214         }
215     ;
216
217 table_def:
218     column_def TK_PRIMARY TK_KEY selcollist
219         {
220             if( SQL_MarkPrimaryKeys( $1, $4 ) )
221                 $$ = $1;
222             else
223                 $$ = NULL;
224         }
225     ;
226
227 column_def:
228     column_def TK_COMMA column column_type
229         {
230             create_col_info *ci;
231
232             for( ci = $1; ci->next; ci = ci->next )
233                 ;
234
235             ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
236             if( !ci->next )
237             {
238                 /* FIXME: free $1 */
239                 YYABORT;
240             }
241             ci->next->colname = $3;
242             ci->next->type = $4;
243             ci->next->next = NULL;
244
245             $$ = $1;
246         }
247   | column column_type
248         {
249             $$ = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
250             if( ! $$ )
251                 YYABORT;
252             $$->colname = $1;
253             $$->type = $2;
254             $$->next = NULL;
255         }
256     ;
257
258 column_type:
259     data_type_l
260         {
261             $$ = $1 | MSITYPE_VALID;
262         }
263   | data_type_l TK_LOCALIZABLE
264         {
265             FIXME("LOCALIZABLE ignored\n");
266             $$ = $1 | MSITYPE_VALID;
267         }
268     ;
269
270 data_type_l:
271     data_type
272         {
273             $$ |= MSITYPE_NULLABLE;
274         }
275   | data_type TK_NOT TK_NULL
276         {
277             $$ = $1;
278         }
279     ;
280
281 data_type:
282     TK_CHAR
283         {
284             $$ = MSITYPE_STRING | 1;
285         }
286   | TK_CHAR TK_LP data_count TK_RP
287         {
288             $$ = MSITYPE_STRING | 0x400 | $3;
289         }
290   | TK_LONGCHAR
291         {
292             $$ = 2;
293         }
294   | TK_SHORT
295         {
296             $$ = 2;
297         }
298   | TK_INT
299         {
300             $$ = 2;
301         }
302   | TK_LONG
303         {
304             $$ = 4;
305         }
306   | TK_OBJECT
307         {
308             $$ = 0;
309         }
310     ;
311
312 data_count:
313     TK_INTEGER
314         {
315             SQL_input* sql = (SQL_input*) info;
316             int val = SQL_getint(sql);
317             if( ( val > 255 ) || ( val < 0 ) )
318                 YYABORT;
319             $$ = val;
320         }
321     ;
322
323 oneselect:
324     unorderedsel TK_ORDER TK_BY selcollist
325         {
326             SQL_input* sql = (SQL_input*) info;
327
328             if( !$1 )
329                 YYABORT;
330             if( $4 )
331                 $$ = do_order_by( sql->db, $1, $4 );
332             else
333                 $$ = $1;
334         }
335   | unorderedsel
336     ;
337
338 unorderedsel:
339     TK_SELECT selcollist from 
340         {
341             SQL_input* sql = (SQL_input*) info;
342             if( !$3 )
343                 YYABORT;
344             if( $2 )
345             {
346                 $$ = do_one_select( sql->db, $3, $2 );
347                 if( !$$ )
348                     YYABORT;
349             }
350             else
351                 $$ = $3;
352         }
353   | TK_SELECT TK_DISTINCT selcollist from 
354         {
355             SQL_input* sql = (SQL_input*) info;
356             MSIVIEW *view = $4;
357
358             if( !view )
359                 YYABORT;
360             if( $3 )
361             {
362                 view = do_one_select( sql->db, view, $3 );
363                 if( !view )
364                     YYABORT;
365             }
366             DISTINCT_CreateView( sql->db, & $$, view );
367         }
368     ;
369
370 selcollist:
371     column 
372         { 
373             string_list *list;
374
375             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
376             if( !list )
377                 YYABORT;
378             list->string = $1;
379             list->next = NULL;
380
381             $$ = list;
382             TRACE("Collist %s\n",debugstr_w($$->string));
383         }
384   | column TK_COMMA selcollist
385         { 
386             string_list *list;
387
388             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
389             if( !list )
390                 YYABORT;
391             list->string = $1;
392             list->next = $3;
393
394             $$ = list;
395             TRACE("From table: %s\n",debugstr_w($$->string));
396         }
397   | TK_STAR
398         {
399             $$ = NULL;
400         }
401     ;
402
403 from:
404     TK_FROM table
405         { 
406             SQL_input* sql = (SQL_input*) info;
407             UINT r;
408
409             $$ = NULL;
410             TRACE("From table: %s\n",debugstr_w($2));
411             r = TABLE_CreateView( sql->db, $2, & $$ );
412             if( r != ERROR_SUCCESS )
413                 YYABORT;
414         }
415   | TK_FROM table TK_WHERE expr
416         { 
417             SQL_input* sql = (SQL_input*) info;
418             MSIVIEW *view = NULL;
419             UINT r;
420
421             $$ = NULL;
422             TRACE("From table: %s\n",debugstr_w($2));
423             r = TABLE_CreateView( sql->db, $2, &view );
424             if( r != ERROR_SUCCESS )
425                 YYABORT;
426             r = WHERE_CreateView( sql->db, &view, view, $4 );
427             if( r != ERROR_SUCCESS )
428                 YYABORT;
429             $$ = view;
430         }
431     ;
432
433 expr:
434     TK_LP expr TK_RP
435         {
436             $$ = $2;
437         }
438   | column_val TK_EQ column_val
439         {
440             $$ = EXPR_complex( $1, OP_EQ, $3 );
441         }
442   | expr TK_AND expr
443         {
444             $$ = EXPR_complex( $1, OP_AND, $3 );
445         }
446   | expr TK_OR expr
447         {
448             $$ = EXPR_complex( $1, OP_OR, $3 );
449         }
450   | column_val TK_EQ val
451         {
452             $$ = EXPR_complex( $1, OP_EQ, $3 );
453         }
454   | column_val TK_GT val
455         {
456             $$ = EXPR_complex( $1, OP_GT, $3 );
457         }
458   | column_val TK_LT val
459         {
460             $$ = EXPR_complex( $1, OP_LT, $3 );
461         }
462   | column_val TK_LE val
463         {
464             $$ = EXPR_complex( $1, OP_LE, $3 );
465         }
466   | column_val TK_GE val
467         {
468             $$ = EXPR_complex( $1, OP_GE, $3 );
469         }
470   | column_val TK_NE val
471         {
472             $$ = EXPR_complex( $1, OP_NE, $3 );
473         }
474   | column_val TK_IS TK_NULL
475         {
476             $$ = EXPR_complex( $1, OP_ISNULL, NULL );
477         }
478   | column_val TK_IS TK_NOT TK_NULL
479         {
480             $$ = EXPR_complex( $1, OP_NOTNULL, NULL );
481         }
482     ;
483
484 val:
485     column_val
486   | const_val
487     ;
488
489 constlist:
490     const_val
491         {
492             value_list *vals;
493
494             vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
495             if( vals )
496             {
497                 vals->val = $1;
498                 vals->next = NULL;
499             }
500             $$ = vals;
501         }
502   | const_val TK_COMMA constlist
503         {
504             value_list *vals;
505
506             vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
507             if( vals )
508             {
509                 vals->val = $1;
510                 vals->next = $3;
511             }
512             $$ = vals;
513         }
514     ;
515
516 update_assign_list:
517     column_assignment
518   | column_assignment TK_COMMA update_assign_list
519         {
520             $1.col_list->next = $3.col_list;
521             $1.val_list->next = $3.val_list;
522             $$ = $1;
523         }
524     ;
525
526 column_assignment:
527     column TK_EQ const_val
528         {
529             $$.col_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.col_list );
530             if( !$$.col_list )
531                 YYABORT;
532             $$.col_list->string = $1;
533             $$.col_list->next = NULL;
534             $$.val_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.val_list );
535             if( !$$.val_list )
536                 YYABORT;
537             $$.val_list->val = $3;
538             $$.val_list->next = 0;
539         }
540     ;
541
542 const_val:
543     TK_INTEGER
544         {
545             $$ = EXPR_ival( &$1, 1 );
546         }
547   | TK_MINUS  TK_INTEGER
548         {
549             $$ = EXPR_ival( &$2, -1 );
550         }
551   | TK_STRING
552         {
553             $$ = EXPR_sval( &$1 );
554         }
555   | TK_WILDCARD
556         {
557             $$ = EXPR_wildcard();
558         }
559     ;
560
561 column_val:
562     column 
563         {
564             $$ = EXPR_column( $1 );
565         }
566     ;
567
568 column:
569     table TK_DOT string_or_id
570         {
571             $$ = $3;  /* FIXME */
572         }
573   | string_or_id
574         {
575             $$ = $1;
576         }
577     ;
578
579 table:
580     string_or_id
581         {
582             $$ = $1;
583         }
584     ;
585
586 string_or_id:
587     TK_ID
588         {
589             $$ = SQL_getstring( &$1 );
590         }
591   | TK_STRING
592         {
593             $$ = SQL_getstring( &$1 );
594         }
595     ;
596
597 %%
598
599 int SQL_lex( void *SQL_lval, SQL_input *sql)
600 {
601     int token;
602     struct sql_str * str = SQL_lval;
603
604     do
605     {
606         sql->n += sql->len;
607         if( ! sql->command[sql->n] )
608             return 0;  /* end of input */
609
610         TRACE("string : %s\n", debugstr_w(&sql->command[sql->n]));
611         sql->len = sqliteGetToken( &sql->command[sql->n], &token );
612         if( sql->len==0 )
613             break;
614         str->data = &sql->command[sql->n];
615         str->len = sql->len;
616     }
617     while( token == TK_SPACE );
618
619     TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
620     
621     return token;
622 }
623
624 LPWSTR SQL_getstring( struct sql_str *strdata)
625 {
626     LPCWSTR p = strdata->data;
627     UINT len = strdata->len;
628     LPWSTR str;
629
630     /* if there's quotes, remove them */
631     if( ( (p[0]=='`') && (p[len-1]=='`') ) || 
632         ( (p[0]=='\'') && (p[len-1]=='\'') ) )
633     {
634         p++;
635         len -= 2;
636     }
637     str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
638     if(!str )
639         return str;
640     memcpy(str, p, len*sizeof(WCHAR) );
641     str[len]=0;
642
643     return str;
644 }
645
646 INT SQL_getint( SQL_input *sql )
647 {
648     LPCWSTR p = &sql->command[sql->n];
649
650     return atoiW( p );
651 }
652
653 int SQL_error(const char *str)
654 {
655     return 0;
656 }
657
658 static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
659                                string_list *columns )
660 {
661     MSIVIEW *view = NULL;
662
663     SELECT_CreateView( db, &view, in, columns );
664     delete_string_list( columns );
665     if( !view )
666         ERR("Error creating select query\n");
667     return view;
668 }
669
670 static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
671                              string_list *columns )
672 {
673     MSIVIEW *view = NULL;
674
675     ORDER_CreateView( db, &view, in );
676     if( view )
677     {
678         string_list *x = columns;
679
680         for( x = columns; x ; x = x->next )
681             ORDER_AddColumn( view, x->string );
682     }
683     else
684         ERR("Error creating select query\n");
685     delete_string_list( columns );
686     return view;
687 }
688
689 static struct expr * EXPR_wildcard()
690 {
691     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
692     if( e )
693     {
694         e->type = EXPR_WILDCARD;
695     }
696     return e;
697 }
698
699 static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r )
700 {
701     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
702     if( e )
703     {
704         e->type = EXPR_COMPLEX;
705         e->u.expr.left = l;
706         e->u.expr.op = op;
707         e->u.expr.right = r;
708     }
709     return e;
710 }
711
712 static struct expr * EXPR_column( LPWSTR str )
713 {
714     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
715     if( e )
716     {
717         e->type = EXPR_COLUMN;
718         e->u.sval = str;
719     }
720     return e;
721 }
722
723 static struct expr * EXPR_ival( struct sql_str *str , int sign)
724 {
725     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
726     if( e )
727     {
728         e->type = EXPR_IVAL;
729         e->u.ival = atoiW( str->data ) * sign;
730     }
731     return e;
732 }
733
734 static struct expr * EXPR_sval( struct sql_str *str )
735 {
736     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
737     if( e )
738     {
739         e->type = EXPR_SVAL;
740         e->u.sval = SQL_getstring( str );
741     }
742     return e;
743 }
744
745 void delete_expr( struct expr *e )
746 {
747     if( !e )
748         return;
749     if( e->type == EXPR_COMPLEX )
750     {
751         delete_expr( e->u.expr.left );
752         delete_expr( e->u.expr.right );
753     }
754     else if( e->type == EXPR_UTF8 )
755         HeapFree( GetProcessHeap(), 0, e->u.utf8 );
756     else if( e->type == EXPR_SVAL )
757         HeapFree( GetProcessHeap(), 0, e->u.sval );
758     HeapFree( GetProcessHeap(), 0, e );
759 }
760
761 void delete_string_list( string_list *sl )
762 {
763     while( sl )
764     {
765         string_list *t = sl->next;
766         HeapFree( GetProcessHeap(), 0, sl->string );
767         HeapFree( GetProcessHeap(), 0, sl );
768         sl = t;
769     }
770 }
771
772 void delete_value_list( value_list *vl )
773 {
774     while( vl )
775     {
776         value_list *t = vl->next;
777         delete_expr( vl->val );
778         HeapFree( GetProcessHeap(), 0, vl );
779         vl = t;
780     }
781 }
782
783 static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
784                                  string_list *keys )
785 {
786     string_list *k;
787     BOOL found = TRUE;
788
789     for( k = keys; k && found; k = k->next )
790     {
791         create_col_info *c;
792
793         found = FALSE;
794         for( c = cols; c && !found; c = c->next )
795         {
796              if( lstrcmpW( k->string, c->colname ) )
797                  continue;
798              c->type |= MSITYPE_KEY;
799              found = TRUE;
800         }
801     }
802
803     return found;
804 }
805
806 UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
807 {
808     SQL_input sql;
809     int r;
810
811     *phview = NULL;
812
813     sql.db = db;
814     sql.command = command;
815     sql.n = 0;
816     sql.len = 0;
817     sql.view = phview;
818
819     r = SQL_parse(&sql);
820
821     TRACE("Parse returned %d\n", r);
822     if( r )
823     {
824         if( *sql.view )
825             (*sql.view)->ops->delete( *sql.view );
826         *sql.view = NULL;
827         return ERROR_BAD_QUERY_SYNTAX;
828     }
829
830     return ERROR_SUCCESS;
831 }