dinput: Silence incorrect warning and move it to a valid place.
[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 #define MSI_HASH_TABLE_SIZE 37
39
40 typedef struct tagMSIHASHENTRY
41 {
42     struct tagMSIHASHENTRY *next;
43     UINT value;
44     UINT row;
45 } MSIHASHENTRY;
46
47 /* below is the query interface to a table */
48
49 typedef struct tagMSIWHEREVIEW
50 {
51     MSIVIEW        view;
52     MSIDATABASE   *db;
53     MSIVIEW       *table;
54     UINT           row_count;
55     MSIHASHENTRY **reorder;
56     struct expr   *cond;
57     UINT           rec_index;
58 } MSIWHEREVIEW;
59
60 static void free_hash_table(MSIHASHENTRY **table)
61 {
62     MSIHASHENTRY *new, *old;
63     int i;
64
65     if (!table)
66         return;
67
68     for (i = 0; i < MSI_HASH_TABLE_SIZE; i++)
69     {
70         new = table[i];
71
72         while (new)
73         {
74             old = new;
75             new = old->next;
76             msi_free(old);
77         }
78
79         table[i] = NULL;
80     }
81
82     msi_free(table);
83 }
84
85 static UINT find_entry_in_hash(MSIHASHENTRY **table, UINT row, UINT *val)
86 {
87     MSIHASHENTRY *entry;
88
89     if (!(entry = table[row % MSI_HASH_TABLE_SIZE]))
90     {
91         ERR("Row not found in hash table!\n");
92         return ERROR_FUNCTION_FAILED;
93     }
94
95     while (entry && entry->row != row)
96         entry = entry->next;
97
98     if (entry) *val = entry->value;
99     return ERROR_SUCCESS;
100 }
101
102 static UINT add_entry_to_hash(MSIHASHENTRY **table, UINT row, UINT val)
103 {
104     MSIHASHENTRY *new = msi_alloc(sizeof(MSIHASHENTRY));
105     MSIHASHENTRY *prev;
106
107     if (!new)
108         return ERROR_OUTOFMEMORY;
109
110     new->next = NULL;
111     new->value = val;
112     new->row = row;
113
114     prev = table[row % MSI_HASH_TABLE_SIZE];
115     if (prev)
116         new->next = prev;
117
118     table[row % MSI_HASH_TABLE_SIZE] = new;
119
120     return ERROR_SUCCESS;
121 }
122
123 static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
124 {
125     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
126     UINT r;
127
128     TRACE("%p %d %d %p\n", wv, row, col, val );
129
130     if( !wv->table )
131         return ERROR_FUNCTION_FAILED;
132
133     if( row > wv->row_count )
134         return ERROR_NO_MORE_ITEMS;
135
136     r = find_entry_in_hash(wv->reorder, row, &row);
137     if (r != ERROR_SUCCESS)
138         return r;
139
140     return wv->table->ops->fetch_int( wv->table, row, col, val );
141 }
142
143 static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
144 {
145     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
146     UINT r;
147
148     TRACE("%p %d %d %p\n", wv, row, col, stm );
149
150     if( !wv->table )
151         return ERROR_FUNCTION_FAILED;
152
153     if( row > wv->row_count )
154         return ERROR_NO_MORE_ITEMS;
155
156     r = find_entry_in_hash(wv->reorder, row, &row);
157     if (r != ERROR_SUCCESS)
158         return r;
159
160     return wv->table->ops->fetch_stream( wv->table, row, col, stm );
161 }
162
163 static UINT WHERE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
164 {
165     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
166     UINT r;
167
168     TRACE("%p %d %p\n", wv, row, rec );
169
170     if (!wv->table)
171         return ERROR_FUNCTION_FAILED;
172
173     if (row > wv->row_count)
174         return ERROR_NO_MORE_ITEMS;
175
176     r = find_entry_in_hash(wv->reorder, row, &row);
177     if (r != ERROR_SUCCESS)
178         return r;
179
180     return wv->table->ops->get_row(view, row, rec);
181 }
182
183 static UINT WHERE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
184 {
185     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
186     UINT r;
187
188     TRACE("%p %d %p %08x\n", wv, row, rec, mask );
189
190     if( !wv->table )
191          return ERROR_FUNCTION_FAILED;
192
193     if( row > wv->row_count )
194         return ERROR_NO_MORE_ITEMS;
195
196     r = find_entry_in_hash(wv->reorder, row, &row);
197     if (r != ERROR_SUCCESS)
198         return r;
199
200     return wv->table->ops->set_row( wv->table, row, rec, mask );
201 }
202
203 static INT INT_evaluate_binary( INT lval, UINT op, INT rval )
204 {
205     switch( op )
206     {
207     case OP_EQ:
208         return ( lval == rval );
209     case OP_AND:
210         return ( lval && rval );
211     case OP_OR:
212         return ( lval || rval );
213     case OP_GT:
214         return ( lval > rval );
215     case OP_LT:
216         return ( lval < rval );
217     case OP_LE:
218         return ( lval <= rval );
219     case OP_GE:
220         return ( lval >= rval );
221     case OP_NE:
222         return ( lval != rval );
223     default:
224         ERR("Unknown operator %d\n", op );
225     }
226     return 0;
227 }
228
229 static INT INT_evaluate_unary( INT lval, UINT op )
230 {
231     switch( op )
232     {
233     case OP_ISNULL:
234         return ( !lval );
235     case OP_NOTNULL:
236         return ( lval );
237     default:
238         ERR("Unknown operator %d\n", op );
239     }
240     return 0;
241 }
242
243 static const WCHAR *STRING_evaluate( MSIWHEREVIEW *wv, UINT row,
244                                      const struct expr *expr,
245                                      const MSIRECORD *record )
246 {
247     UINT val = 0, r;
248
249     switch( expr->type )
250     {
251     case EXPR_COL_NUMBER_STRING:
252         r = wv->table->ops->fetch_int( wv->table, row, expr->u.col_number, &val );
253         if( r != ERROR_SUCCESS )
254             return NULL;
255         return msi_string_lookup_id( wv->db->strings, val );
256
257     case EXPR_SVAL:
258         return expr->u.sval;
259
260     case EXPR_WILDCARD:
261         return MSI_RecordGetString( record, ++wv->rec_index );
262
263     default:
264         ERR("Invalid expression type\n");
265         break;
266     }
267     return NULL;
268 }
269
270 static UINT STRCMP_Evaluate( MSIWHEREVIEW *wv, UINT row, const struct expr *cond,
271                              INT *val, const MSIRECORD *record )
272 {
273     int sr;
274     const WCHAR *l_str, *r_str;
275
276     l_str = STRING_evaluate( wv, row, cond->u.expr.left, record );
277     r_str = STRING_evaluate( wv, row, cond->u.expr.right, record );
278     if( l_str == r_str ||
279         ((!l_str || !*l_str) && (!r_str || !*r_str)) )
280         sr = 0;
281     else if( l_str && ! r_str )
282         sr = 1;
283     else if( r_str && ! l_str )
284         sr = -1;
285     else
286         sr = lstrcmpW( l_str, r_str );
287
288     *val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
289            ( cond->u.expr.op == OP_NE && ( sr != 0 ) ) ||
290            ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
291            ( cond->u.expr.op == OP_GT && ( sr > 0 ) );
292
293     return ERROR_SUCCESS;
294 }
295
296 static UINT WHERE_evaluate( MSIWHEREVIEW *wv, UINT row,
297                             struct expr *cond, INT *val, MSIRECORD *record )
298 {
299     UINT r, tval;
300     INT lval, rval;
301
302     if( !cond )
303         return ERROR_SUCCESS;
304
305     switch( cond->type )
306     {
307     case EXPR_COL_NUMBER:
308         r = wv->table->ops->fetch_int( wv->table, row, cond->u.col_number, &tval );
309         *val = tval - 0x8000;
310         return ERROR_SUCCESS;
311
312     case EXPR_COL_NUMBER32:
313         r = wv->table->ops->fetch_int( wv->table, row, cond->u.col_number, &tval );
314         *val = tval - 0x80000000;
315         return r;
316
317     case EXPR_UVAL:
318         *val = cond->u.uval;
319         return ERROR_SUCCESS;
320
321     case EXPR_COMPLEX:
322         r = WHERE_evaluate( wv, row, cond->u.expr.left, &lval, record );
323         if( r != ERROR_SUCCESS )
324             return r;
325         r = WHERE_evaluate( wv, row, cond->u.expr.right, &rval, record );
326         if( r != ERROR_SUCCESS )
327             return r;
328         *val = INT_evaluate_binary( lval, cond->u.expr.op, rval );
329         return ERROR_SUCCESS;
330
331     case EXPR_UNARY:
332         r = wv->table->ops->fetch_int( wv->table, row, cond->u.expr.left->u.col_number, &tval );
333         if( r != ERROR_SUCCESS )
334             return r;
335         *val = INT_evaluate_unary( tval, cond->u.expr.op );
336         return ERROR_SUCCESS;
337
338     case EXPR_STRCMP:
339         return STRCMP_Evaluate( wv, row, cond, val, record );
340
341     case EXPR_WILDCARD:
342         *val = MSI_RecordGetInteger( record, ++wv->rec_index );
343         return ERROR_SUCCESS;
344
345     default:
346         ERR("Invalid expression type\n");
347         break;
348     }
349
350     return ERROR_SUCCESS;
351 }
352
353 static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
354 {
355     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
356     UINT count = 0, r, i;
357     INT val;
358     MSIVIEW *table = wv->table;
359
360     TRACE("%p %p\n", wv, record);
361
362     if( !table )
363          return ERROR_FUNCTION_FAILED;
364
365     r = table->ops->execute( table, record );
366     if( r != ERROR_SUCCESS )
367         return r;
368
369     r = table->ops->get_dimensions( table, &count, NULL );
370     if( r != ERROR_SUCCESS )
371         return r;
372
373     free_hash_table(wv->reorder);
374     wv->reorder = msi_alloc_zero(MSI_HASH_TABLE_SIZE * sizeof(MSIHASHENTRY *));
375     if( !wv->reorder )
376         return ERROR_OUTOFMEMORY;
377
378     wv->row_count = 0;
379     if (wv->cond->type == EXPR_STRCMP)
380     {
381         MSIITERHANDLE handle = NULL;
382         UINT row, value, col;
383         struct expr *col_cond = wv->cond->u.expr.left;
384         struct expr *val_cond = wv->cond->u.expr.right;
385
386         /* swap conditionals */
387         if (col_cond->type != EXPR_COL_NUMBER_STRING)
388         {
389             val_cond = wv->cond->u.expr.left;
390             col_cond = wv->cond->u.expr.right;
391         }
392
393         if ((col_cond->type == EXPR_COL_NUMBER_STRING) && (val_cond->type == EXPR_SVAL))
394         {
395             col = col_cond->u.col_number;
396             /* special case for "" - translate it into nil */
397             if (!val_cond->u.sval[0])
398                 value = 0;
399             else
400             {
401                 r = msi_string2idW(wv->db->strings, val_cond->u.sval, &value);
402                 if (r != ERROR_SUCCESS)
403                 {
404                     TRACE("no id for %s, assuming it doesn't exist in the table\n", debugstr_w(wv->cond->u.expr.right->u.sval));
405                     return ERROR_SUCCESS;
406                 }
407             }
408
409             do
410             {
411                 r = table->ops->find_matching_rows(table, col, value, &row, &handle);
412                 if (r == ERROR_SUCCESS)
413                     add_entry_to_hash(wv->reorder, wv->row_count++, row);
414             } while (r == ERROR_SUCCESS);
415
416             if (r == ERROR_NO_MORE_ITEMS)
417                 return ERROR_SUCCESS;
418             else
419                 return r;
420         }
421         /* else fallback to slow case */
422     }
423
424     for( i=0; i<count; i++ )
425     {
426         val = 0;
427         wv->rec_index = 0;
428         r = WHERE_evaluate( wv, i, wv->cond, &val, record );
429         if( r != ERROR_SUCCESS )
430             return r;
431         if( val )
432             add_entry_to_hash( wv->reorder, wv->row_count++, i );
433     }
434
435     return ERROR_SUCCESS;
436 }
437
438 static UINT WHERE_close( struct tagMSIVIEW *view )
439 {
440     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
441
442     TRACE("%p\n", wv );
443
444     if( !wv->table )
445         return ERROR_FUNCTION_FAILED;
446
447     return wv->table->ops->close( wv->table );
448 }
449
450 static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
451 {
452     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
453
454     TRACE("%p %p %p\n", wv, rows, cols );
455
456     if( !wv->table )
457          return ERROR_FUNCTION_FAILED;
458
459     if( rows )
460     {
461         if( !wv->reorder )
462             return ERROR_FUNCTION_FAILED;
463         *rows = wv->row_count;
464     }
465
466     return wv->table->ops->get_dimensions( wv->table, NULL, cols );
467 }
468
469 static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
470                 UINT n, LPWSTR *name, UINT *type )
471 {
472     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
473
474     TRACE("%p %d %p %p\n", wv, n, name, type );
475
476     if( !wv->table )
477          return ERROR_FUNCTION_FAILED;
478
479     return wv->table->ops->get_column_info( wv->table, n, name, type );
480 }
481
482 static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
483                           MSIRECORD *rec, UINT row )
484 {
485     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
486
487     TRACE("%p %d %p\n", wv, eModifyMode, rec );
488
489     if( !wv->table )
490          return ERROR_FUNCTION_FAILED;
491
492     return wv->table->ops->modify( wv->table, eModifyMode, rec, row );
493 }
494
495 static UINT WHERE_delete( struct tagMSIVIEW *view )
496 {
497     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
498
499     TRACE("%p\n", wv );
500
501     if( wv->table )
502         wv->table->ops->delete( wv->table );
503     wv->table = 0;
504
505     free_hash_table(wv->reorder);
506     wv->reorder = NULL;
507     wv->row_count = 0;
508
509     msiobj_release( &wv->db->hdr );
510     msi_free( wv );
511
512     return ERROR_SUCCESS;
513 }
514
515 static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
516     UINT val, UINT *row, MSIITERHANDLE *handle )
517 {
518     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
519     UINT r;
520
521     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
522
523     if( !wv->table )
524          return ERROR_FUNCTION_FAILED;
525
526     r = wv->table->ops->find_matching_rows( wv->table, col, val, row, handle );
527     if (r != ERROR_SUCCESS)
528         return r;
529
530     if( *row > wv->row_count )
531         return ERROR_NO_MORE_ITEMS;
532
533     return find_entry_in_hash(wv->reorder, *row, row);
534 }
535
536 static UINT WHERE_sort(struct tagMSIVIEW *view, column_info *columns)
537 {
538     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
539
540     TRACE("%p %p\n", view, columns);
541
542     return wv->table->ops->sort(wv->table, columns);
543 }
544
545 static const MSIVIEWOPS where_ops =
546 {
547     WHERE_fetch_int,
548     WHERE_fetch_stream,
549     WHERE_get_row,
550     WHERE_set_row,
551     NULL,
552     NULL,
553     WHERE_execute,
554     WHERE_close,
555     WHERE_get_dimensions,
556     WHERE_get_column_info,
557     WHERE_modify,
558     WHERE_delete,
559     WHERE_find_matching_rows,
560     NULL,
561     NULL,
562     NULL,
563     NULL,
564     WHERE_sort,
565 };
566
567 static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
568                                    UINT *valid )
569 {
570     UINT r, val = 0;
571
572     switch( cond->type )
573     {
574     case EXPR_COLUMN:
575         r = VIEW_find_column( table, cond->u.column, &val );
576         if( r == ERROR_SUCCESS )
577         {
578             UINT type = 0;
579             r = table->ops->get_column_info( table, val, NULL, &type );
580             if( r == ERROR_SUCCESS )
581             {
582                 if (type&MSITYPE_STRING)
583                     cond->type = EXPR_COL_NUMBER_STRING;
584                 else if ((type&0xff) == 4)
585                     cond->type = EXPR_COL_NUMBER32;
586                 else
587                     cond->type = EXPR_COL_NUMBER;
588                 cond->u.col_number = val;
589                 *valid = 1;
590             }
591             else
592                 *valid = 0;
593         }
594         else
595         {
596             *valid = 0;
597             WARN("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
598         }
599         break;
600     case EXPR_COMPLEX:
601         r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
602         if( r != ERROR_SUCCESS )
603             return r;
604         if( !*valid )
605             return ERROR_SUCCESS;
606         r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid );
607         if( r != ERROR_SUCCESS )
608             return r;
609
610         /* check the type of the comparison */
611         if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
612             ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
613             ( cond->u.expr.right->type == EXPR_SVAL ) ||
614             ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
615         {
616             switch( cond->u.expr.op )
617             {
618             case OP_EQ:
619             case OP_GT:
620             case OP_LT:
621             case OP_NE:
622                 break;
623             default:
624                 *valid = FALSE;
625                 return ERROR_INVALID_PARAMETER;
626             }
627
628             /* FIXME: check we're comparing a string to a column */
629
630             cond->type = EXPR_STRCMP;
631         }
632
633         break;
634     case EXPR_UNARY:
635         if ( cond->u.expr.left->type != EXPR_COLUMN )
636         {
637             *valid = FALSE;
638             return ERROR_INVALID_PARAMETER;
639         }
640         r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
641         if( r != ERROR_SUCCESS )
642             return r;
643         break;
644     case EXPR_IVAL:
645         *valid = 1;
646         cond->type = EXPR_UVAL;
647         cond->u.uval = cond->u.ival;
648         break;
649     case EXPR_WILDCARD:
650         *valid = 1;
651         break;
652     case EXPR_SVAL:
653         *valid = 1;
654         break;
655     default:
656         ERR("Invalid expression type\n");
657         *valid = 0;
658         break;
659     }
660
661     return ERROR_SUCCESS;
662 }
663
664 UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
665                        struct expr *cond )
666 {
667     MSIWHEREVIEW *wv = NULL;
668     UINT count = 0, r, valid = 0;
669
670     TRACE("%p\n", table );
671
672     r = table->ops->get_dimensions( table, NULL, &count );
673     if( r != ERROR_SUCCESS )
674     {
675         ERR("can't get table dimensions\n");
676         return r;
677     }
678
679     if( cond )
680     {
681         r = WHERE_VerifyCondition( db, table, cond, &valid );
682         if( r != ERROR_SUCCESS )
683             return r;
684         if( !valid )
685             return ERROR_FUNCTION_FAILED;
686     }
687
688     wv = msi_alloc_zero( sizeof *wv );
689     if( !wv )
690         return ERROR_FUNCTION_FAILED;
691     
692     /* fill the structure */
693     wv->view.ops = &where_ops;
694     msiobj_addref( &db->hdr );
695     wv->db = db;
696     wv->table = table;
697     wv->row_count = 0;
698     wv->reorder = NULL;
699     wv->cond = cond;
700     wv->rec_index = 0;
701     *view = (MSIVIEW*) wv;
702
703     return ERROR_SUCCESS;
704 }