SetMenu should always call SetWindowPos whether the window is visible
[wine] / dlls / msi / sql.y
1 %{
2
3 /*
4  * Implementation of the Microsoft Installer (msi.dll)
5  *
6  * Copyright 2002 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 yyerror(const char *str);
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 typedef struct tag_yyinput
44 {
45     MSIDATABASE *db;
46     LPCWSTR command;
47     DWORD n, len;
48     MSIVIEW **view;  /* view structure for the resulting query */
49 } yyinput;
50
51 struct string_list
52 {
53     LPWSTR string;
54     struct string_list *next;
55 };
56
57 static LPWSTR yygetstring( yyinput *info );
58 static INT yygetint( yyinput *sql );
59 static int yylex( void *yylval, yyinput *info);
60
61 static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
62                         struct string_list *columns );
63 static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
64                                struct string_list *columns );
65
66 static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r );
67 static struct expr * EXPR_column( LPWSTR column );
68 static struct expr * EXPR_ival( INT ival );
69 static struct expr * EXPR_sval( LPWSTR string );
70
71 %}
72
73 %pure-parser
74
75 %union
76 {
77     LPWSTR string;
78     struct string_list *column_list;
79     MSIVIEW *table;
80     struct expr *expr;
81 }
82
83 %token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC
84 %token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY
85 %token TK_CASCADE TK_CASE TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN TK_COMMA
86 %token TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT 
87 %token TK_CONSTRAINT TK_COPY TK_CREATE
88 %token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC
89 %token TK_DISTINCT TK_DOT TK_DROP TK_EACH
90 %token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN
91 %token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION
92 %token TK_GE TK_GLOB TK_GROUP TK_GT
93 %token TK_HAVING
94 %token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
95 %token <string> TK_ID 
96 %token TK_INSERT TK_INSTEAD TK_INTEGER TK_INTERSECT TK_INTO TK_IS TK_ISNULL
97 %token TK_JOIN TK_JOIN_KW
98 %token TK_KEY
99 %token TK_LE TK_LIKE TK_LIMIT TK_LP TK_LSHIFT TK_LT
100 %token TK_MATCH TK_MINUS
101 %token TK_NE TK_NOT TK_NOTNULL TK_NULL
102 %token TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER
103 %token TK_PLUS TK_PRAGMA TK_PRIMARY
104 %token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK
105 %token TK_ROW TK_RP TK_RSHIFT
106 %token TK_SELECT TK_SEMI TK_SET TK_SLASH TK_SPACE TK_STAR TK_STATEMENT 
107 %token <string> TK_STRING
108 %token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER
109 %token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE
110 %token TK_UPDATE TK_UPLUS TK_USING
111 %token TK_VACUUM TK_VALUES TK_VIEW
112 %token TK_WHEN TK_WHERE
113
114 /*
115  * These are extra tokens used by the lexer but never seen by the
116  * parser.  We put them in a rule so that the parser generator will
117  * add them to the parse.h output file.
118  *
119  */
120 %nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
121           COLUMN AGG_FUNCTION.
122
123 %type <query> oneselect
124 %type <string> column table string_or_id
125 %type <column_list> selcollist
126 %type <table> from unorderedsel
127 %type <expr> expr val column_val
128
129 %%
130
131 oneselect:
132     unorderedsel TK_ORDER TK_BY selcollist
133         {
134             yyinput* sql = (yyinput*) info;
135
136             if( !$1 )
137                 YYABORT;
138             if( $4 )
139                 *sql->view = do_order_by( sql->db, $1, $4 );
140             else
141                 *sql->view = $1;
142         }
143   | unorderedsel
144         {
145             yyinput* sql = (yyinput*) info;
146
147             *sql->view = $1;
148         }
149     ;
150
151 unorderedsel:
152     TK_SELECT selcollist from 
153         {
154             yyinput* sql = (yyinput*) info;
155             if( !$3 )
156                 YYABORT;
157             if( $2 )
158                 $$ = do_one_select( sql->db, $3, $2 );
159             else
160                 $$ = $3;
161         }
162   | TK_SELECT TK_DISTINCT selcollist from 
163         {
164             yyinput* sql = (yyinput*) info;
165             MSIVIEW *view = $4;
166
167             if( !view )
168                 YYABORT;
169             if( $3 )
170                 view = do_one_select( sql->db, view, $3 );
171             DISTINCT_CreateView( sql->db, & $$, view );
172         }
173     ;
174
175 selcollist:
176     column 
177         { 
178             struct string_list *list;
179
180             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
181             if( !list )
182                 YYABORT;
183             list->string = $1;
184             list->next = NULL;
185
186             $$ = list;
187             TRACE("Collist %s\n",debugstr_w($$->string));
188         }
189   | column TK_COMMA selcollist
190         { 
191             struct string_list *list;
192
193             list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
194             if( !list )
195                 YYABORT;
196             list->string = $1;
197             list->next = $3;
198
199             $$ = list;
200             TRACE("From table: %s\n",debugstr_w($$->string));
201         }
202   | TK_STAR
203         {
204             $$ = NULL;
205         }
206     ;
207
208 from:
209     TK_FROM table
210         { 
211             yyinput* sql = (yyinput*) info;
212
213             $$ = NULL;
214             TRACE("From table: %s\n",debugstr_w($2));
215             TABLE_CreateView( sql->db, $2, & $$ );
216         }
217   | TK_FROM table TK_WHERE expr
218         { 
219             yyinput* sql = (yyinput*) info;
220             MSIVIEW *view = NULL;
221             UINT r;
222
223             $$ = NULL;
224             TRACE("From table: %s\n",debugstr_w($2));
225             r = TABLE_CreateView( sql->db, $2, &view );
226             if( r != ERROR_SUCCESS )
227                 YYABORT;
228             r = WHERE_CreateView( sql->db, &view, view );
229             if( r != ERROR_SUCCESS )
230                 YYABORT;
231             r = WHERE_AddCondition( view, $4 );
232             if( r != ERROR_SUCCESS )
233                 YYABORT;
234             $$ = view;
235         }
236     ;
237
238 expr:
239     TK_LP expr TK_RP
240         {
241             $$ = $2;
242         }
243   | column_val TK_EQ column_val
244         {
245             $$ = EXPR_complex( $1, OP_EQ, $3 );
246         }
247   | expr TK_AND expr
248         {
249             $$ = EXPR_complex( $1, OP_AND, $3 );
250         }
251   | expr TK_OR expr
252         {
253             $$ = EXPR_complex( $1, OP_OR, $3 );
254         }
255   | column_val TK_EQ val
256         {
257             $$ = EXPR_complex( $1, OP_EQ, $3 );
258         }
259   | column_val TK_GT val
260         {
261             $$ = EXPR_complex( $1, OP_GT, $3 );
262         }
263   | column_val TK_LT val
264         {
265             $$ = EXPR_complex( $1, OP_LT, $3 );
266         }
267   | column_val TK_LE val
268         {
269             $$ = EXPR_complex( $1, OP_LE, $3 );
270         }
271   | column_val TK_GE val
272         {
273             $$ = EXPR_complex( $1, OP_GE, $3 );
274         }
275   | column_val TK_NE val
276         {
277             $$ = EXPR_complex( $1, OP_NE, $3 );
278         }
279   | column_val TK_IS TK_NULL
280         {
281             $$ = EXPR_complex( $1, OP_ISNULL, NULL );
282         }
283   | column_val TK_IS TK_NOT TK_NULL
284         {
285             $$ = EXPR_complex( $1, OP_NOTNULL, NULL );
286         }
287     ;
288
289 val:
290     column_val
291         {
292             $$ = $1;
293         }
294   | TK_INTEGER
295         {
296             yyinput* sql = (yyinput*) info;
297             $$ = EXPR_ival( yygetint(sql) );
298         }
299   | TK_STRING
300         {
301             $$ = EXPR_sval( $1 );
302         }
303     ;
304
305 column_val:
306     column 
307         {
308             $$ = EXPR_column( $1 );
309         }
310     ;
311
312 column:
313     table TK_DOT string_or_id
314         {
315             $$ = $3;  /* FIXME */
316         }
317   | string_or_id
318         {
319             $$ = $1;
320         }
321     ;
322
323 table:
324     string_or_id
325         {
326             $$ = $1;
327         }
328     ;
329
330 string_or_id:
331     TK_ID
332         {
333             yyinput* sql = (yyinput*) info;
334             $$ = yygetstring(sql);
335         }
336   | TK_STRING
337         {
338             yyinput* sql = (yyinput*) info;
339             $$ = yygetstring(sql);
340         }
341     ;
342
343 %%
344
345 int yylex( void *yylval, yyinput *sql)
346 {
347     int token;
348
349     do
350     {
351         sql->n += sql->len;
352         if( ! sql->command[sql->n] )
353             return 0;  /* end of input */
354
355         TRACE("string : %s\n", debugstr_w(&sql->command[sql->n]));
356         sql->len = sqliteGetToken( &sql->command[sql->n], &token );
357         if( sql->len==0 )
358             break;
359     }
360     while( token == TK_SPACE );
361
362     TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
363     
364     return token;
365 }
366
367 LPWSTR yygetstring( yyinput *sql )
368 {
369     LPCWSTR p = &sql->command[sql->n];
370     LPWSTR str;
371     UINT len = sql->len;
372
373     /* if there's quotes, remove them */
374     if( (p[0]=='`') && (p[len-1]=='`') )
375     {
376         p++;
377         len -= 2;
378     }
379     str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
380     if(!str )
381         return str;
382     memcpy(str, p, len*sizeof(WCHAR) );
383     str[len]=0;
384
385     return str;
386 }
387
388 INT yygetint( yyinput *sql )
389 {
390     LPCWSTR p = &sql->command[sql->n];
391
392     return atoiW( p );
393 }
394
395 int yyerror(const char *str)
396 {
397     return 0;
398 }
399
400 static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
401                                struct string_list *columns )
402 {
403     MSIVIEW *view = NULL;
404
405     SELECT_CreateView( db, &view, in );
406     if( view )
407     {
408         struct string_list *x = columns;
409
410         while( x )
411         {
412             struct string_list *t = x->next;
413             SELECT_AddColumn( view, x->string );
414             HeapFree( GetProcessHeap(), 0, x->string );
415             HeapFree( GetProcessHeap(), 0, x );
416             x = t;
417         }
418     }
419     else
420         ERR("Error creating select query\n");
421     return view;
422 }
423
424 static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
425                                struct string_list *columns )
426 {
427     MSIVIEW *view = NULL;
428
429     ORDER_CreateView( db, &view, in );
430     if( view )
431     {
432         struct string_list *x = columns;
433
434         while( x )
435         {
436             struct string_list *t = x->next;
437             ORDER_AddColumn( view, x->string );
438             HeapFree( GetProcessHeap(), 0, x->string );
439             HeapFree( GetProcessHeap(), 0, x );
440             x = t;
441         }
442     }
443     else
444         ERR("Error creating select query\n");
445     return view;
446 }
447
448 static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r )
449 {
450     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
451     if( e )
452     {
453         e->type = EXPR_COMPLEX;
454         e->u.expr.left = l;
455         e->u.expr.op = op;
456         e->u.expr.right = r;
457     }
458     return e;
459 }
460
461 static struct expr * EXPR_column( LPWSTR column )
462 {
463     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
464     if( e )
465     {
466         e->type = EXPR_COLUMN;
467         e->u.column = column;
468     }
469     return e;
470 }
471
472 static struct expr * EXPR_ival( INT ival )
473 {
474     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
475     if( e )
476     {
477         e->type = EXPR_IVAL;
478         e->u.ival = ival;
479     }
480     return e;
481 }
482
483 static struct expr * EXPR_sval( LPWSTR string )
484 {
485     struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
486     if( e )
487     {
488         e->type = EXPR_SVAL;
489         e->u.sval = string;
490     }
491     return e;
492 }
493
494 UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
495 {
496     yyinput sql;
497     int r;
498
499     *phview = NULL;
500
501     sql.db = db;
502     sql.command = command;
503     sql.n = 0;
504     sql.len = 0;
505     sql.view = phview;
506
507     r = yyparse(&sql);
508
509     TRACE("Parse returned %d\n", r);
510     if( r )
511     {
512         if( *sql.view )
513             (*sql.view)->ops->delete( *sql.view );
514         return ERROR_BAD_QUERY_SYNTAX;
515     }
516
517     return ERROR_SUCCESS;
518 }