ole32: Remove some assertions in the stuctured storage code by
[wine] / dlls / msi / where.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winerror.h"
26 #include "wine/debug.h"
27 #include "msi.h"
28 #include "msiquery.h"
29 #include "objbase.h"
30 #include "objidl.h"
31 #include "msipriv.h"
32 #include "winnls.h"
33
34 #include "query.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
37
38
39 /* below is the query interface to a table */
40
41 typedef struct tagMSIWHEREVIEW
42 {
43     MSIVIEW        view;
44     MSIDATABASE   *db;
45     MSIVIEW       *table;
46     UINT           row_count;
47     UINT          *reorder;
48     struct expr   *cond;
49 } MSIWHEREVIEW;
50
51 static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
52 {
53     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
54
55     TRACE("%p %d %d %p\n", wv, row, col, val );
56
57     if( !wv->table )
58         return ERROR_FUNCTION_FAILED;
59
60     if( row > wv->row_count )
61         return ERROR_NO_MORE_ITEMS;
62
63     row = wv->reorder[ row ];
64
65     return wv->table->ops->fetch_int( wv->table, row, col, val );
66 }
67
68 static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
69 {
70     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
71
72     TRACE("%p %d %d %p\n", wv, row, col, stm );
73
74     if( !wv->table )
75         return ERROR_FUNCTION_FAILED;
76
77     if( row > wv->row_count )
78         return ERROR_NO_MORE_ITEMS;
79
80     row = wv->reorder[ row ];
81
82     return wv->table->ops->fetch_stream( wv->table, row, col, stm );
83 }
84
85 static UINT WHERE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
86 {
87     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
88
89     TRACE("%p %d %d %04x\n", wv, row, col, val );
90
91     if( !wv->table )
92          return ERROR_FUNCTION_FAILED;
93     
94     if( row > wv->row_count )
95         return ERROR_NO_MORE_ITEMS;
96     
97     row = wv->reorder[ row ];
98     
99     return wv->table->ops->set_int( wv->table, row, col, val );
100 }
101
102 static INT INT_evaluate( INT lval, UINT op, INT rval )
103 {
104     switch( op )
105     {
106     case OP_EQ:
107         return ( lval == rval );
108     case OP_AND:
109         return ( lval && rval );
110     case OP_OR:
111         return ( lval || rval );
112     case OP_GT:
113         return ( lval > rval );
114     case OP_LT:
115         return ( lval < rval );
116     case OP_LE:
117         return ( lval <= rval );
118     case OP_GE:
119         return ( lval >= rval );
120     case OP_NE:
121         return ( lval != rval );
122     case OP_ISNULL:
123         return ( !lval );
124     case OP_NOTNULL:
125         return ( lval );
126     default:
127         ERR("Unknown operator %d\n", op );
128     }
129     return 0;
130 }
131
132 static const WCHAR *STRING_evaluate( string_table *st,
133               MSIVIEW *table, UINT row, struct expr *expr, MSIRECORD *record )
134 {
135     UINT val = 0, r;
136
137     switch( expr->type )
138     {
139     case EXPR_COL_NUMBER_STRING:
140         r = table->ops->fetch_int( table, row, expr->u.col_number, &val );
141         if( r != ERROR_SUCCESS )
142             return NULL;
143         return msi_string_lookup_id( st, val );
144
145     case EXPR_SVAL:
146         return expr->u.sval;
147
148     case EXPR_WILDCARD:
149         return MSI_RecordGetString( record, 1 );
150
151     default:
152         ERR("Invalid expression type\n");
153         break;
154     }
155     return NULL;
156 }
157
158 static UINT STRCMP_Evaluate( string_table *st, MSIVIEW *table, UINT row, 
159                              struct expr *cond, INT *val, MSIRECORD *record )
160 {
161     int sr;
162     const WCHAR *l_str, *r_str;
163
164     l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record );
165     r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record );
166     if( l_str == r_str )
167         sr = 0;
168     else if( l_str && ! r_str )
169         sr = 1;
170     else if( r_str && ! l_str )
171         sr = -1;
172     else
173         sr = lstrcmpW( l_str, r_str );
174
175     *val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
176            ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
177            ( cond->u.expr.op == OP_GT && ( sr > 0 ) );
178
179     return ERROR_SUCCESS;
180 }
181
182 static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row, 
183                              struct expr *cond, INT *val, MSIRECORD *record )
184 {
185     UINT r, tval;
186     INT lval, rval;
187
188     if( !cond )
189         return ERROR_SUCCESS;
190
191     switch( cond->type )
192     {
193     case EXPR_COL_NUMBER:
194         r = table->ops->fetch_int( table, row, cond->u.col_number, &tval );
195         *val = tval - 0x8000;
196         return ERROR_SUCCESS;
197
198     case EXPR_COL_NUMBER32:
199         r = table->ops->fetch_int( table, row, cond->u.col_number, &tval );
200         *val = tval - 0x80000000;
201         return r;
202
203     case EXPR_UVAL:
204         *val = cond->u.uval;
205         return ERROR_SUCCESS;
206
207     case EXPR_COMPLEX:
208         r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record );
209         if( r != ERROR_SUCCESS )
210             return r;
211         r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record );
212         if( r != ERROR_SUCCESS )
213             return r;
214         *val = INT_evaluate( lval, cond->u.expr.op, rval );
215         return ERROR_SUCCESS;
216
217     case EXPR_STRCMP:
218         return STRCMP_Evaluate( db->strings, table, row, cond, val, record );
219
220     case EXPR_WILDCARD:
221         *val = MSI_RecordGetInteger( record, 1 );
222         return ERROR_SUCCESS;
223
224     default:
225         ERR("Invalid expression type\n");
226         break;
227     } 
228
229     return ERROR_SUCCESS;
230
231 }
232
233 static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
234 {
235     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
236     UINT count = 0, r, i;
237     INT val;
238     MSIVIEW *table = wv->table;
239
240     TRACE("%p %p\n", wv, record);
241
242     if( !table )
243          return ERROR_FUNCTION_FAILED;
244
245     r = table->ops->execute( table, record );
246     if( r != ERROR_SUCCESS )
247         return r;
248
249     r = table->ops->get_dimensions( table, &count, NULL );
250     if( r != ERROR_SUCCESS )
251         return r;
252
253     msi_free( wv->reorder );
254     wv->reorder = msi_alloc( count*sizeof(UINT) );
255     if( !wv->reorder )
256         return ERROR_FUNCTION_FAILED;
257
258     wv->row_count = 0;
259     if (wv->cond->type == EXPR_STRCMP)
260     {
261         MSIITERHANDLE handle = NULL;
262         UINT row, value, col;
263         struct expr *col_cond = wv->cond->u.expr.left;
264         struct expr *val_cond = wv->cond->u.expr.right;
265
266         /* swap conditionals */
267         if (col_cond->type != EXPR_COL_NUMBER_STRING)
268         {
269             val_cond = wv->cond->u.expr.left;
270             col_cond = wv->cond->u.expr.right;
271         }
272
273         if ((col_cond->type == EXPR_COL_NUMBER_STRING) && (val_cond->type == EXPR_SVAL))
274         {
275             col = col_cond->u.col_number;
276             /* special case for "" - translate it into nil */
277             if (!val_cond->u.sval[0])
278                 value = 0;
279             else
280             {
281                 r = msi_string2idW(wv->db->strings, val_cond->u.sval, &value);
282                 if (r != ERROR_SUCCESS)
283                 {
284                     TRACE("no id for %s, assuming it doesn't exist in the table\n", debugstr_w(wv->cond->u.expr.right->u.sval));
285                     return ERROR_SUCCESS;
286                 }
287             }
288
289             do
290             {
291                 r = table->ops->find_matching_rows(table, col, value, &row, &handle);
292                 if (r == ERROR_SUCCESS)
293                     wv->reorder[ wv->row_count ++ ] = row;
294             } while (r == ERROR_SUCCESS);
295
296             if (r == ERROR_NO_MORE_ITEMS)
297                 return ERROR_SUCCESS;
298             else
299                 return r;
300         }
301         /* else fallback to slow case */
302     }
303
304     for( i=0; i<count; i++ )
305     {
306         val = 0;
307         r = WHERE_evaluate( wv->db, table, i, wv->cond, &val, record );
308         if( r != ERROR_SUCCESS )
309             return r;
310         if( val )
311             wv->reorder[ wv->row_count ++ ] = i;
312     }
313
314     return ERROR_SUCCESS;
315 }
316
317 static UINT WHERE_close( struct tagMSIVIEW *view )
318 {
319     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
320
321     TRACE("%p\n", wv );
322
323     if( !wv->table )
324          return ERROR_FUNCTION_FAILED;
325
326     msi_free( wv->reorder );
327     wv->reorder = NULL;
328
329     return wv->table->ops->close( wv->table );
330 }
331
332 static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
333 {
334     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
335
336     TRACE("%p %p %p\n", wv, rows, cols );
337
338     if( !wv->table )
339          return ERROR_FUNCTION_FAILED;
340
341     if( rows )
342     {
343         if( !wv->reorder )
344             return ERROR_FUNCTION_FAILED;
345         *rows = wv->row_count;
346     }
347
348     return wv->table->ops->get_dimensions( wv->table, NULL, cols );
349 }
350
351 static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
352                 UINT n, LPWSTR *name, UINT *type )
353 {
354     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
355
356     TRACE("%p %d %p %p\n", wv, n, name, type );
357
358     if( !wv->table )
359          return ERROR_FUNCTION_FAILED;
360
361     return wv->table->ops->get_column_info( wv->table, n, name, type );
362 }
363
364 static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
365                 MSIRECORD *rec )
366 {
367     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
368
369     TRACE("%p %d %p\n", wv, eModifyMode, rec );
370
371     if( !wv->table )
372          return ERROR_FUNCTION_FAILED;
373
374     return wv->table->ops->modify( wv->table, eModifyMode, rec );
375 }
376
377 static UINT WHERE_delete( struct tagMSIVIEW *view )
378 {
379     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
380
381     TRACE("%p\n", wv );
382
383     if( wv->table )
384         wv->table->ops->delete( wv->table );
385     wv->table = 0;
386
387     msi_free( wv->reorder );
388     wv->reorder = NULL;
389     wv->row_count = 0;
390
391     msiobj_release( &wv->db->hdr );
392     msi_free( wv );
393
394     return ERROR_SUCCESS;
395 }
396
397 static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
398     UINT val, UINT *row, MSIITERHANDLE *handle )
399 {
400     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
401     UINT r;
402
403     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
404
405     if( !wv->table )
406          return ERROR_FUNCTION_FAILED;
407
408     r = wv->table->ops->find_matching_rows( wv->table, col, val, row, handle );
409
410     if( *row > wv->row_count )
411         return ERROR_NO_MORE_ITEMS;
412
413     *row = wv->reorder[ *row ];
414
415     return r;
416 }
417
418
419 static const MSIVIEWOPS where_ops =
420 {
421     WHERE_fetch_int,
422     WHERE_fetch_stream,
423     WHERE_set_int,
424     NULL,
425     WHERE_execute,
426     WHERE_close,
427     WHERE_get_dimensions,
428     WHERE_get_column_info,
429     WHERE_modify,
430     WHERE_delete,
431     WHERE_find_matching_rows
432 };
433
434 static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
435                                    UINT *valid )
436 {
437     UINT r, val = 0;
438
439     switch( cond->type )
440     {
441     case EXPR_COLUMN:
442         r = VIEW_find_column( table, cond->u.column, &val );
443         if( r == ERROR_SUCCESS )
444         {
445             UINT type = 0;
446             r = table->ops->get_column_info( table, val, NULL, &type );
447             if( r == ERROR_SUCCESS )
448             {
449                 if (type&MSITYPE_STRING)
450                     cond->type = EXPR_COL_NUMBER_STRING;
451                 else if ((type&0xff) == 4)
452                     cond->type = EXPR_COL_NUMBER32;
453                 else
454                     cond->type = EXPR_COL_NUMBER;
455                 cond->u.col_number = val;
456                 *valid = 1;
457             }
458             else
459                 *valid = 0;
460         }
461         else
462         {
463             *valid = 0;
464             ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
465         }
466         break;
467     case EXPR_COMPLEX:
468         r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
469         if( r != ERROR_SUCCESS )
470             return r;
471         if( !*valid )
472             return ERROR_SUCCESS;
473         r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid );
474         if( r != ERROR_SUCCESS )
475             return r;
476
477         /* check the type of the comparison */
478         if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
479             ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
480             ( cond->u.expr.right->type == EXPR_SVAL ) ||
481             ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
482         {
483             switch( cond->u.expr.op )
484             {
485             case OP_EQ:
486             case OP_GT:
487             case OP_LT:
488                 break;
489             default:
490                 *valid = FALSE;
491                 return ERROR_INVALID_PARAMETER;
492             }
493
494             /* FIXME: check we're comparing a string to a column */
495
496             cond->type = EXPR_STRCMP;
497         }
498
499         break;
500     case EXPR_IVAL:
501         *valid = 1;
502         cond->type = EXPR_UVAL;
503         cond->u.uval = cond->u.ival;
504         break;
505     case EXPR_WILDCARD:
506         *valid = 1;
507         break;
508     case EXPR_SVAL:
509         *valid = 1;
510         break;
511     default:
512         ERR("Invalid expression type\n");
513         *valid = 0;
514         break;
515     } 
516
517     return ERROR_SUCCESS;
518 }
519
520 UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
521                        struct expr *cond )
522 {
523     MSIWHEREVIEW *wv = NULL;
524     UINT count = 0, r, valid = 0;
525
526     TRACE("%p\n", table );
527
528     r = table->ops->get_dimensions( table, NULL, &count );
529     if( r != ERROR_SUCCESS )
530     {
531         ERR("can't get table dimensions\n");
532         return r;
533     }
534
535     if( cond )
536     {
537         r = WHERE_VerifyCondition( db, table, cond, &valid );
538         if( r != ERROR_SUCCESS )
539             return r;
540         if( !valid )
541             return ERROR_FUNCTION_FAILED;
542     }
543
544     wv = msi_alloc_zero( sizeof *wv );
545     if( !wv )
546         return ERROR_FUNCTION_FAILED;
547     
548     /* fill the structure */
549     wv->view.ops = &where_ops;
550     msiobj_addref( &db->hdr );
551     wv->db = db;
552     wv->table = table;
553     wv->row_count = 0;
554     wv->reorder = NULL;
555     wv->cond = cond;
556     *view = (MSIVIEW*) wv;
557
558     return ERROR_SUCCESS;
559 }