Reverse the order for deleting the items in resetcontent to correctly
[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
133 %type <query> oneupdate onedelete
134 %type <expr> expr val column_val const_val
135 %type <column_type> column_type data_type data_type_l data_count
136 %type <column_info> column_def table_def
137 %type <val_list> constlist
138 %type <update_col_info> column_assignment update_assign_list
139
140 %%
141
142 onequery:
143     oneselect
144     {
145         SQL_input* sql = (SQL_input*) info;
146         *sql->view = $1;
147     }
148   | onecreate
149     {
150         SQL_input* sql = (SQL_input*) info;
151         *sql->view = $1;
152     }
153   | oneinsert
154     {
155         SQL_input* sql = (SQL_input*) info;
156         *sql->view = $1;
157     }
158   | oneupdate
159     {
160         SQL_input* sql = (SQL_input*) info;
161         *sql->view = $1;
162     }
163   | onedelete
164     {
165         SQL_input* sql = (SQL_input*) info;
166         *sql->view = $1;
167     }
168     ;
169
170 oneinsert:
171     TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP
172     {
173         SQL_input *sql = (SQL_input*) info;
174         MSIVIEW *insert = NULL; 
175
176         INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); 
177         $$ = insert;
178     }
179   | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP
180     {
181         SQL_input *sql = (SQL_input*) info;
182         MSIVIEW *insert = NULL; 
183
184         INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); 
185         $$ = insert;
186     }
187     ;
188
189 onecreate:
190     TK_CREATE TK_TABLE table TK_LP table_def TK_RP
191         {
192             SQL_input* sql = (SQL_input*) info;
193             MSIVIEW *create = NULL; 
194
195             if( !$5 )
196                 YYABORT;
197             CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
198             $$ = create;
199         }
200   | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD
201         {
202             SQL_input* sql = (SQL_input*) info;
203             MSIVIEW *create = NULL; 
204
205             if( !$5 )
206                 YYABORT;
207             CREATE_CreateView( sql->db, &create, $3, $5, TRUE );
208             $$ = create;
209         }
210     ;
211
212 oneupdate:
213     TK_UPDATE table TK_SET update_assign_list TK_WHERE expr
214         {
215             SQL_input* sql = (SQL_input*) info;
216             MSIVIEW *update = NULL; 
217
218             UPDATE_CreateView( sql->db, &update, $2, &$4, $6 );
219             $$ = update;
220         }
221     ;
222
223 onedelete:
224     TK_DELETE from
225         {
226             SQL_input* sql = (SQL_input*) info;
227             MSIVIEW *delete = NULL; 
228
229             DELETE_CreateView( sql->db, &delete, $2 );
230             $$ = delete;
231         }
232     ;
233
234 table_def:
235     column_def TK_PRIMARY TK_KEY selcollist
236         {
237             if( SQL_MarkPrimaryKeys( $1, $4 ) )
238                 $$ = $1;
239             else
240                 $$ = NULL;
241         }
242     ;
243
244 column_def:
245     column_def TK_COMMA column column_type
246         {
247             create_col_info *ci;
248
249             for( ci = $1; ci->next; ci = ci->next )
250                 ;
251
252             ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
253             if( !ci->next )
254             {
255                 /* FIXME: free $1 */
256                 YYABORT;
257             }
258             ci->next->colname = $3;
259             ci->next->type = $4;
260             ci->next->next = NULL;
261
262             $$ = $1;
263         }
264   | column column_type
265         {
266             $$ = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
267             if( ! $$ )
268                 YYABORT;
269             $$->colname = $1;
270             $$->type = $2;
271             $$->next = NULL;
272         }
273     ;
274
275 column_type:
276     data_type_l
277         {
278             $$ = $1 | MSITYPE_VALID;
279         }
280   | data_type_l TK_LOCALIZABLE
281         {
282             FIXME("LOCALIZABLE ignored\n");
283             $$ = $1 | MSITYPE_VALID;
284         }
285     ;
286
287 data_type_l:
288     data_type
289         {
290             $$ |= MSITYPE_NULLABLE;
291         }
292   | data_type TK_NOT TK_NULL
293         {
294             $$ = $1;
295         }
296     ;
297
298 data_type:
299     TK_CHAR
300         {
301             $$ = MSITYPE_STRING | 1;
302         }
303   | TK_CHAR TK_LP data_count TK_RP
304         {
305             $$ = MSITYPE_STRING | 0x400 | $3;
306         }
307   | TK_LONGCHAR
308         {
309             $$ = 2;
310         }
311   | TK_SHORT
312         {
313             $$ = 2;
314         }
315   | TK_INT
316         {
317             $$ = 2;
318         }
319   | TK_LONG
320         {
321             $$ = 4;
322         }
323   | TK_OBJECT
324         {
325             $$ = 0;
326         }
327     ;
328
329 data_count:
330     TK_INTEGER
331         {
332             SQL_input* sql = (SQL_input*) info;
333             int val = SQL_getint(sql);
334             if( ( val > 255 ) || ( val < 0 ) )
335                 YYABORT;
336             $$ = val;
337         }
338     ;
339
340 oneselect:
341     unorderedsel TK_ORDER TK_BY selcollist
342         {
343             SQL_input* sql = (SQL_input*) info;
344
345             if( !$1 )
346                 YYABORT;
347             if( $4 )
348                 $$ = do_order_by( sql->db, $1, $4 );
349             else
350                 $$ = $1;
351         }
352   | unorderedsel
353     ;
354
355 unorderedsel:
356     TK_SELECT selcollist from 
357         {
358             SQL_input* sql = (SQL_input*) info;
359             if( !$3 )
360                 YYABORT;
361             if( $2 )
362             {
363                 $$ = do_one_select( sql->db, $3, $2 );
364                 if( !$$ )
365                     YYABORT;
366             }
367             else
368                 $$ = $3;
369         }
370   | TK_SELECT TK_DISTINCT selcollist from 
371         {
372             SQL_input* sql = (SQL_input*) info;
373             MSIVIEW *view = $4;
374
375             if( !view )
376                 YYABORT;
377             if( $3 )
378             {
379                 view = do_one_select( sql->db, view, $3 );
380                 if( !view )
381                     YYABORT;
382             }
383             DISTINCT_CreateView( sql->db, & $$, view );
384         }
385     ;
386
387 selcollist:
388     column 
389         { 
390             string_list *list;
391
392             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
393             if( !list )
394                 YYABORT;
395             list->string = $1;
396             list->next = NULL;
397
398             $$ = list;
399             TRACE("Collist %s\n",debugstr_w($$->string));
400         }
401   | column TK_COMMA selcollist
402         { 
403             string_list *list;
404
405             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
406             if( !list )
407                 YYABORT;
408             list->string = $1;
409             list->next = $3;
410
411             $$ = list;
412             TRACE("From table: %s\n",debugstr_w($$->string));
413         }
414   | TK_STAR
415         {
416             $$ = NULL;
417         }
418     ;
419
420 from:
421     TK_FROM table
422         { 
423             SQL_input* sql = (SQL_input*) info;
424             UINT r;
425
426             $$ = NULL;
427             TRACE("From table: %s\n",debugstr_w($2));
428             r = TABLE_CreateView( sql->db, $2, & $$ );
429             if( r != ERROR_SUCCESS )
430                 YYABORT;
431         }
432   | TK_FROM table TK_WHERE expr
433         { 
434             SQL_input* sql = (SQL_input*) info;
435             MSIVIEW *view = NULL;
436             UINT r;
437
438             $$ = NULL;
439             TRACE("From table: %s\n",debugstr_w($2));
440             r = TABLE_CreateView( sql->db, $2, &view );
441             if( r != ERROR_SUCCESS )
442                 YYABORT;
443             r = WHERE_CreateView( sql->db, &view, view, $4 );
444             if( r != ERROR_SUCCESS )
445                 YYABORT;
446             $$ = view;
447         }
448     ;
449
450 expr:
451     TK_LP expr TK_RP
452         {
453             $$ = $2;
454         }
455   | column_val TK_EQ column_val
456         {
457             $$ = EXPR_complex( $1, OP_EQ, $3 );
458         }
459   | expr TK_AND expr
460         {
461             $$ = EXPR_complex( $1, OP_AND, $3 );
462         }
463   | expr TK_OR expr
464         {
465             $$ = EXPR_complex( $1, OP_OR, $3 );
466         }
467   | column_val TK_EQ val
468         {
469             $$ = EXPR_complex( $1, OP_EQ, $3 );
470         }
471   | column_val TK_GT val
472         {
473             $$ = EXPR_complex( $1, OP_GT, $3 );
474         }
475   | column_val TK_LT val
476         {
477             $$ = EXPR_complex( $1, OP_LT, $3 );
478         }
479   | column_val TK_LE val
480         {
481             $$ = EXPR_complex( $1, OP_LE, $3 );
482         }
483   | column_val TK_GE val
484         {
485             $$ = EXPR_complex( $1, OP_GE, $3 );
486         }
487   | column_val TK_NE val
488         {
489             $$ = EXPR_complex( $1, OP_NE, $3 );
490         }
491   | column_val TK_IS TK_NULL
492         {
493             $$ = EXPR_complex( $1, OP_ISNULL, NULL );
494         }
495   | column_val TK_IS TK_NOT TK_NULL
496         {
497             $$ = EXPR_complex( $1, OP_NOTNULL, NULL );
498         }
499     ;
500
501 val:
502     column_val
503   | const_val
504     ;
505
506 constlist:
507     const_val
508         {
509             value_list *vals;
510
511             vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
512             if( vals )
513             {
514                 vals->val = $1;
515                 vals->next = NULL;
516             }
517             $$ = vals;
518         }
519   | const_val TK_COMMA constlist
520         {
521             value_list *vals;
522
523             vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
524             if( vals )
525             {
526                 vals->val = $1;
527                 vals->next = $3;
528             }
529             $$ = vals;
530         }
531     ;
532
533 update_assign_list:
534     column_assignment
535   | column_assignment TK_COMMA update_assign_list
536         {
537             $1.col_list->next = $3.col_list;
538             $1.val_list->next = $3.val_list;
539             $$ = $1;
540         }
541     ;
542
543 column_assignment:
544     column TK_EQ const_val
545         {
546             $$.col_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.col_list );
547             if( !$$.col_list )
548                 YYABORT;
549             $$.col_list->string = $1;
550             $$.col_list->next = NULL;
551             $$.val_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.val_list );
552             if( !$$.val_list )
553                 YYABORT;
554             $$.val_list->val = $3;
555             $$.val_list->next = 0;
556         }
557     ;
558
559 const_val:
560     TK_INTEGER
561         {
562             $$ = EXPR_ival( &$1, 1 );
563         }
564   | TK_MINUS  TK_INTEGER
565         {
566             $$ = EXPR_ival( &$2, -1 );
567         }
568   | TK_STRING
569         {
570             $$ = EXPR_sval( &$1 );
571         }
572   | TK_WILDCARD
573         {
574             $$ = EXPR_wildcard();
575         }
576     ;
577
578 column_val:
579     column 
580         {
581             $$ = EXPR_column( $1 );
582         }
583     ;
584
585 column:
586     table TK_DOT string_or_id
587         {
588             $$ = $3;  /* FIXME */
589         }
590   | string_or_id
591         {
592             $$ = $1;
593         }
594     ;
595
596 table:
597     string_or_id
598         {
599             $$ = $1;
600         }
601     ;
602
603 string_or_id:
604     TK_ID
605         {
606             $$ = SQL_getstring( &$1 );
607         }
608   | TK_STRING
609         {
610             $$ = SQL_getstring( &$1 );
611         }
612     ;
613
614 %%
615
616 int SQL_lex( void *SQL_lval, SQL_input *sql)
617 {
618     int token;
619     struct sql_str * str = SQL_lval;
620
621     do
622     {
623         sql->n += sql->len;
624         if( ! sql->command[sql->n] )
625             return 0;  /* end of input */
626
627         TRACE("string : %s\n", debugstr_w(&sql->command[sql->n]));
628         sql->len = sqliteGetToken( &sql->command[sql->n], &token );
629         if( sql->len==0 )
630             break;
631         str->data = &sql->command[sql->n];
632         str->len = sql->len;
633     }
634     while( token == TK_SPACE );
635
636     TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
637     
638     return token;
639 }
640
641 LPWSTR SQL_getstring( struct sql_str *strdata)
642 {
643     LPCWSTR p = strdata->data;
644     UINT len = strdata->len;
645     LPWSTR str;
646
647     /* if there's quotes, remove them */
648     if( ( (p[0]=='`') && (p[len-1]=='`') ) || 
649         ( (p[0]=='\'') && (p[len-1]=='\'') ) )
650     {
651         p++;
652         len -= 2;
653     }
654     str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
655     if(!str )
656         return str;
657     memcpy(str, p, len*sizeof(WCHAR) );
658     str[len]=0;
659
660     return str;
661 }
662
663 INT SQL_getint( SQL_input *sql )
664 {
665     LPCWSTR p = &sql->command[sql->n];
666
667     return atoiW( p );
668 }
669
670 int SQL_error(const char *str)
671 {
672     return 0;
673 }
674
675 static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
676                                string_list *columns )
677 {
678     MSIVIEW *view = NULL;
679
680     SELECT_CreateView( db, &view, in, columns );
681     delete_string_list( columns );
682     if( !view )
683         ERR("Error creating select query\n");
684     return view;
685 }
686
687 static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
688                              string_list *columns )
689 {
690     MSIVIEW *view = NULL;
691
692     ORDER_CreateView( db, &view, in );
693     if( view )
694     {
695         string_list *x = columns;
696
697         for( x = columns; x ; x = x->next )
698             ORDER_AddColumn( view, x->string );
699     }
700     else
701         ERR("Error creating select query\n");
702     delete_string_list( columns );
703     return view;
704 }
705
706 static struct expr * EXPR_wildcard()
707 {
708     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
709     if( e )
710     {
711         e->type = EXPR_WILDCARD;
712     }
713     return e;
714 }
715
716 static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r )
717 {
718     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
719     if( e )
720     {
721         e->type = EXPR_COMPLEX;
722         e->u.expr.left = l;
723         e->u.expr.op = op;
724         e->u.expr.right = r;
725     }
726     return e;
727 }
728
729 static struct expr * EXPR_column( LPWSTR str )
730 {
731     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
732     if( e )
733     {
734         e->type = EXPR_COLUMN;
735         e->u.sval = str;
736     }
737     return e;
738 }
739
740 static struct expr * EXPR_ival( struct sql_str *str , int sign)
741 {
742     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
743     if( e )
744     {
745         e->type = EXPR_IVAL;
746         e->u.ival = atoiW( str->data ) * sign;
747     }
748     return e;
749 }
750
751 static struct expr * EXPR_sval( struct sql_str *str )
752 {
753     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
754     if( e )
755     {
756         e->type = EXPR_SVAL;
757         e->u.sval = SQL_getstring( str );
758     }
759     return e;
760 }
761
762 void delete_expr( struct expr *e )
763 {
764     if( !e )
765         return;
766     if( e->type == EXPR_COMPLEX )
767     {
768         delete_expr( e->u.expr.left );
769         delete_expr( e->u.expr.right );
770     }
771     else if( e->type == EXPR_UTF8 )
772         HeapFree( GetProcessHeap(), 0, e->u.utf8 );
773     else if( e->type == EXPR_SVAL )
774         HeapFree( GetProcessHeap(), 0, e->u.sval );
775     HeapFree( GetProcessHeap(), 0, e );
776 }
777
778 void delete_string_list( string_list *sl )
779 {
780     while( sl )
781     {
782         string_list *t = sl->next;
783         HeapFree( GetProcessHeap(), 0, sl->string );
784         HeapFree( GetProcessHeap(), 0, sl );
785         sl = t;
786     }
787 }
788
789 void delete_value_list( value_list *vl )
790 {
791     while( vl )
792     {
793         value_list *t = vl->next;
794         delete_expr( vl->val );
795         HeapFree( GetProcessHeap(), 0, vl );
796         vl = t;
797     }
798 }
799
800 static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
801                                  string_list *keys )
802 {
803     string_list *k;
804     BOOL found = TRUE;
805
806     for( k = keys; k && found; k = k->next )
807     {
808         create_col_info *c;
809
810         found = FALSE;
811         for( c = cols; c && !found; c = c->next )
812         {
813              if( lstrcmpW( k->string, c->colname ) )
814                  continue;
815              c->type |= MSITYPE_KEY;
816              found = TRUE;
817         }
818     }
819
820     return found;
821 }
822
823 UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
824 {
825     SQL_input sql;
826     int r;
827
828     *phview = NULL;
829
830     sql.db = db;
831     sql.command = command;
832     sql.n = 0;
833     sql.len = 0;
834     sql.view = phview;
835
836     r = SQL_parse(&sql);
837
838     TRACE("Parse returned %d\n", r);
839     if( r )
840     {
841         if( *sql.view )
842             (*sql.view)->ops->delete( *sql.view );
843         *sql.view = NULL;
844         return ERROR_BAD_QUERY_SYNTAX;
845     }
846
847     return ERROR_SUCCESS;
848 }