xref: /reactos/dll/win32/msi/where.c (revision 8a978a17)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002 Mike McCormack for CodeWeavers
5  * Copyright 2011 Bernhard Loos
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include <assert.h>
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "msipriv.h"
35 #include "winnls.h"
36 
37 #include "query.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
40 
41 /* below is the query interface to a table */
42 typedef struct tagMSIROWENTRY
43 {
44     struct tagMSIWHEREVIEW *wv; /* used during sorting */
45     UINT values[1];
46 } MSIROWENTRY;
47 
48 typedef struct tagJOINTABLE
49 {
50     struct tagJOINTABLE *next;
51     MSIVIEW *view;
52     UINT col_count;
53     UINT row_count;
54     UINT table_index;
55 } JOINTABLE;
56 
57 typedef struct tagMSIORDERINFO
58 {
59     UINT col_count;
60     UINT error;
61     union ext_column columns[1];
62 } MSIORDERINFO;
63 
64 typedef struct tagMSIWHEREVIEW
65 {
66     MSIVIEW        view;
67     MSIDATABASE   *db;
68     JOINTABLE     *tables;
69     UINT           row_count;
70     UINT           col_count;
71     UINT           table_count;
72     MSIROWENTRY  **reorder;
73     UINT           reorder_size; /* number of entries available in reorder */
74     struct expr   *cond;
75     UINT           rec_index;
76     MSIORDERINFO  *order_info;
77 } MSIWHEREVIEW;
78 
79 static UINT WHERE_evaluate( MSIWHEREVIEW *wv, const UINT rows[],
80                             struct expr *cond, INT *val, MSIRECORD *record );
81 
82 #define INITIAL_REORDER_SIZE 16
83 
84 #define INVALID_ROW_INDEX (-1)
85 
86 static void free_reorder(MSIWHEREVIEW *wv)
87 {
88     UINT i;
89 
90     if (!wv->reorder)
91         return;
92 
93     for (i = 0; i < wv->row_count; i++)
94         msi_free(wv->reorder[i]);
95 
96     msi_free( wv->reorder );
97     wv->reorder = NULL;
98     wv->reorder_size = 0;
99     wv->row_count = 0;
100 }
101 
102 static UINT init_reorder(MSIWHEREVIEW *wv)
103 {
104     MSIROWENTRY **new = msi_alloc_zero(sizeof(MSIROWENTRY *) * INITIAL_REORDER_SIZE);
105     if (!new)
106         return ERROR_OUTOFMEMORY;
107 
108     free_reorder(wv);
109 
110     wv->reorder = new;
111     wv->reorder_size = INITIAL_REORDER_SIZE;
112 
113     return ERROR_SUCCESS;
114 }
115 
116 static inline UINT find_row(MSIWHEREVIEW *wv, UINT row, UINT *(values[]))
117 {
118     if (row >= wv->row_count)
119         return ERROR_NO_MORE_ITEMS;
120 
121     *values = wv->reorder[row]->values;
122 
123     return ERROR_SUCCESS;
124 }
125 
126 static UINT add_row(MSIWHEREVIEW *wv, UINT vals[])
127 {
128     MSIROWENTRY *new;
129 
130     if (wv->reorder_size <= wv->row_count)
131     {
132         MSIROWENTRY **new_reorder;
133         UINT newsize = wv->reorder_size * 2;
134 
135         new_reorder = msi_realloc_zero(wv->reorder, sizeof(MSIROWENTRY *) * newsize);
136         if (!new_reorder)
137             return ERROR_OUTOFMEMORY;
138 
139         wv->reorder = new_reorder;
140         wv->reorder_size = newsize;
141     }
142 
143     new = msi_alloc(FIELD_OFFSET( MSIROWENTRY, values[wv->table_count] ));
144 
145     if (!new)
146         return ERROR_OUTOFMEMORY;
147 
148     wv->reorder[wv->row_count++] = new;
149 
150     memcpy(new->values, vals, wv->table_count * sizeof(UINT));
151     new->wv = wv;
152 
153     return ERROR_SUCCESS;
154 }
155 
156 static JOINTABLE *find_table(MSIWHEREVIEW *wv, UINT col, UINT *table_col)
157 {
158     JOINTABLE *table = wv->tables;
159 
160     if(col == 0 || col > wv->col_count)
161          return NULL;
162 
163     while (col > table->col_count)
164     {
165         col -= table->col_count;
166         table = table->next;
167         assert(table);
168     }
169 
170     *table_col = col;
171     return table;
172 }
173 
174 static UINT parse_column(MSIWHEREVIEW *wv, union ext_column *column,
175                          UINT *column_type)
176 {
177     JOINTABLE *table = wv->tables;
178     UINT i, r;
179 
180     do
181     {
182         LPCWSTR table_name;
183 
184         if (column->unparsed.table)
185         {
186             r = table->view->ops->get_column_info(table->view, 1, NULL, NULL,
187                                                   NULL, &table_name);
188             if (r != ERROR_SUCCESS)
189                 return r;
190             if (strcmpW(table_name, column->unparsed.table) != 0)
191                 continue;
192         }
193 
194         for(i = 1; i <= table->col_count; i++)
195         {
196             LPCWSTR col_name;
197 
198             r = table->view->ops->get_column_info(table->view, i, &col_name, column_type,
199                                                   NULL, NULL);
200             if(r != ERROR_SUCCESS )
201                 return r;
202 
203             if(strcmpW(col_name, column->unparsed.column))
204                 continue;
205             column->parsed.column = i;
206             column->parsed.table = table;
207             return ERROR_SUCCESS;
208         }
209     }
210     while ((table = table->next));
211 
212     WARN("Couldn't find column %s.%s\n", debugstr_w( column->unparsed.table ), debugstr_w( column->unparsed.column ) );
213     return ERROR_BAD_QUERY_SYNTAX;
214 }
215 
216 static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
217 {
218     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
219     JOINTABLE *table;
220     UINT *rows;
221     UINT r;
222 
223     TRACE("%p %d %d %p\n", wv, row, col, val );
224 
225     if( !wv->tables )
226         return ERROR_FUNCTION_FAILED;
227 
228     r = find_row(wv, row, &rows);
229     if (r != ERROR_SUCCESS)
230         return r;
231 
232     table = find_table(wv, col, &col);
233     if (!table)
234         return ERROR_FUNCTION_FAILED;
235 
236     return table->view->ops->fetch_int(table->view, rows[table->table_index], col, val);
237 }
238 
239 static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
240 {
241     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
242     JOINTABLE *table;
243     UINT *rows;
244     UINT r;
245 
246     TRACE("%p %d %d %p\n", wv, row, col, stm );
247 
248     if( !wv->tables )
249         return ERROR_FUNCTION_FAILED;
250 
251     r = find_row(wv, row, &rows);
252     if (r != ERROR_SUCCESS)
253         return r;
254 
255     table = find_table(wv, col, &col);
256     if (!table)
257         return ERROR_FUNCTION_FAILED;
258 
259     return table->view->ops->fetch_stream( table->view, rows[table->table_index], col, stm );
260 }
261 
262 static UINT WHERE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
263 {
264     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
265 
266     TRACE("%p %d %p\n", wv, row, rec );
267 
268     if (!wv->tables)
269         return ERROR_FUNCTION_FAILED;
270 
271     return msi_view_get_row( wv->db, view, row, rec );
272 }
273 
274 static UINT WHERE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
275 {
276     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
277     UINT i, r, offset = 0;
278     JOINTABLE *table = wv->tables;
279     UINT *rows;
280     UINT mask_copy = mask;
281 
282     TRACE("%p %d %p %08x\n", wv, row, rec, mask );
283 
284     if( !wv->tables )
285          return ERROR_FUNCTION_FAILED;
286 
287     r = find_row(wv, row, &rows);
288     if (r != ERROR_SUCCESS)
289         return r;
290 
291     if (mask >= 1 << wv->col_count)
292         return ERROR_INVALID_PARAMETER;
293 
294     do
295     {
296         for (i = 0; i < table->col_count; i++) {
297             UINT type;
298 
299             if (!(mask_copy & (1 << i)))
300                 continue;
301             r = table->view->ops->get_column_info(table->view, i + 1, NULL,
302                                             &type, NULL, NULL );
303             if (r != ERROR_SUCCESS)
304                 return r;
305             if (type & MSITYPE_KEY)
306                 return ERROR_FUNCTION_FAILED;
307         }
308         mask_copy >>= table->col_count;
309     }
310     while (mask_copy && (table = table->next));
311 
312     table = wv->tables;
313 
314     do
315     {
316         const UINT col_count = table->col_count;
317         UINT i;
318         MSIRECORD *reduced;
319         UINT reduced_mask = (mask >> offset) & ((1 << col_count) - 1);
320 
321         if (!reduced_mask)
322         {
323             offset += col_count;
324             continue;
325         }
326 
327         reduced = MSI_CreateRecord(col_count);
328         if (!reduced)
329             return ERROR_FUNCTION_FAILED;
330 
331         for (i = 1; i <= col_count; i++)
332         {
333             r = MSI_RecordCopyField(rec, i + offset, reduced, i);
334             if (r != ERROR_SUCCESS)
335                 break;
336         }
337 
338         offset += col_count;
339 
340         if (r == ERROR_SUCCESS)
341             r = table->view->ops->set_row(table->view, rows[table->table_index], reduced, reduced_mask);
342 
343         msiobj_release(&reduced->hdr);
344     }
345     while ((table = table->next));
346     return r;
347 }
348 
349 static UINT WHERE_delete_row(struct tagMSIVIEW *view, UINT row)
350 {
351     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
352     UINT r;
353     UINT *rows;
354 
355     TRACE("(%p %d)\n", view, row);
356 
357     if (!wv->tables)
358         return ERROR_FUNCTION_FAILED;
359 
360     r = find_row(wv, row, &rows);
361     if ( r != ERROR_SUCCESS )
362         return r;
363 
364     if (wv->table_count > 1)
365         return ERROR_CALL_NOT_IMPLEMENTED;
366 
367     return wv->tables->view->ops->delete_row(wv->tables->view, rows[0]);
368 }
369 
370 static INT INT_evaluate_binary( MSIWHEREVIEW *wv, const UINT rows[],
371                                 const struct complex_expr *expr, INT *val, MSIRECORD *record )
372 {
373     UINT rl, rr;
374     INT lval, rval;
375 
376     rl = WHERE_evaluate(wv, rows, expr->left, &lval, record);
377     if (rl != ERROR_SUCCESS && rl != ERROR_CONTINUE)
378         return rl;
379     rr = WHERE_evaluate(wv, rows, expr->right, &rval, record);
380     if (rr != ERROR_SUCCESS && rr != ERROR_CONTINUE)
381         return rr;
382 
383     if (rl == ERROR_CONTINUE || rr == ERROR_CONTINUE)
384     {
385         if (rl == rr)
386         {
387             *val = TRUE;
388             return ERROR_CONTINUE;
389         }
390 
391         if (expr->op == OP_AND)
392         {
393             if ((rl == ERROR_CONTINUE && !rval) || (rr == ERROR_CONTINUE && !lval))
394             {
395                 *val = FALSE;
396                 return ERROR_SUCCESS;
397             }
398         }
399         else if (expr->op == OP_OR)
400         {
401             if ((rl == ERROR_CONTINUE && rval) || (rr == ERROR_CONTINUE && lval))
402             {
403                 *val = TRUE;
404                 return ERROR_SUCCESS;
405             }
406         }
407 
408         *val = TRUE;
409         return ERROR_CONTINUE;
410     }
411 
412     switch( expr->op )
413     {
414     case OP_EQ:
415         *val = ( lval == rval );
416         break;
417     case OP_AND:
418         *val = ( lval && rval );
419         break;
420     case OP_OR:
421         *val = ( lval || rval );
422         break;
423     case OP_GT:
424         *val = ( lval > rval );
425         break;
426     case OP_LT:
427         *val = ( lval < rval );
428         break;
429     case OP_LE:
430         *val = ( lval <= rval );
431         break;
432     case OP_GE:
433         *val = ( lval >= rval );
434         break;
435     case OP_NE:
436         *val = ( lval != rval );
437         break;
438     default:
439         ERR("Unknown operator %d\n", expr->op );
440         return ERROR_FUNCTION_FAILED;
441     }
442 
443     return ERROR_SUCCESS;
444 }
445 
446 static inline UINT expr_fetch_value(const union ext_column *expr, const UINT rows[], UINT *val)
447 {
448     JOINTABLE *table = expr->parsed.table;
449 
450     if( rows[table->table_index] == INVALID_ROW_INDEX )
451     {
452         *val = 1;
453         return ERROR_CONTINUE;
454     }
455     return table->view->ops->fetch_int(table->view, rows[table->table_index],
456                                         expr->parsed.column, val);
457 }
458 
459 
460 static UINT INT_evaluate_unary( MSIWHEREVIEW *wv, const UINT rows[],
461                                 const struct complex_expr *expr, INT *val, MSIRECORD *record )
462 {
463     UINT r;
464     UINT lval;
465 
466     r = expr_fetch_value(&expr->left->u.column, rows, &lval);
467     if(r != ERROR_SUCCESS)
468         return r;
469 
470     switch( expr->op )
471     {
472     case OP_ISNULL:
473         *val = !lval;
474         break;
475     case OP_NOTNULL:
476         *val = lval;
477         break;
478     default:
479         ERR("Unknown operator %d\n", expr->op );
480         return ERROR_FUNCTION_FAILED;
481     }
482     return ERROR_SUCCESS;
483 }
484 
485 static UINT STRING_evaluate( MSIWHEREVIEW *wv, const UINT rows[],
486                                      const struct expr *expr,
487                                      const MSIRECORD *record,
488                                      const WCHAR **str )
489 {
490     UINT val = 0, r = ERROR_SUCCESS;
491 
492     switch( expr->type )
493     {
494     case EXPR_COL_NUMBER_STRING:
495         r = expr_fetch_value(&expr->u.column, rows, &val);
496         if (r == ERROR_SUCCESS)
497             *str =  msi_string_lookup(wv->db->strings, val, NULL);
498         else
499             *str = NULL;
500         break;
501 
502     case EXPR_SVAL:
503         *str = expr->u.sval;
504         break;
505 
506     case EXPR_WILDCARD:
507         *str = MSI_RecordGetString(record, ++wv->rec_index);
508         break;
509 
510     default:
511         ERR("Invalid expression type\n");
512         r = ERROR_FUNCTION_FAILED;
513         *str = NULL;
514         break;
515     }
516     return r;
517 }
518 
519 static UINT STRCMP_Evaluate( MSIWHEREVIEW *wv, const UINT rows[], const struct complex_expr *expr,
520                              INT *val, const MSIRECORD *record )
521 {
522     int sr;
523     const WCHAR *l_str, *r_str;
524     UINT r;
525 
526     *val = TRUE;
527     r = STRING_evaluate(wv, rows, expr->left, record, &l_str);
528     if (r == ERROR_CONTINUE)
529         return r;
530     r = STRING_evaluate(wv, rows, expr->right, record, &r_str);
531     if (r == ERROR_CONTINUE)
532         return r;
533 
534     if( l_str == r_str ||
535         ((!l_str || !*l_str) && (!r_str || !*r_str)) )
536         sr = 0;
537     else if( l_str && ! r_str )
538         sr = 1;
539     else if( r_str && ! l_str )
540         sr = -1;
541     else
542         sr = strcmpW( l_str, r_str );
543 
544     *val = ( expr->op == OP_EQ && ( sr == 0 ) ) ||
545            ( expr->op == OP_NE && ( sr != 0 ) );
546 
547     return ERROR_SUCCESS;
548 }
549 
550 static UINT WHERE_evaluate( MSIWHEREVIEW *wv, const UINT rows[],
551                             struct expr *cond, INT *val, MSIRECORD *record )
552 {
553     UINT r, tval;
554 
555     if( !cond )
556     {
557         *val = TRUE;
558         return ERROR_SUCCESS;
559     }
560 
561     switch( cond->type )
562     {
563     case EXPR_COL_NUMBER:
564         r = expr_fetch_value(&cond->u.column, rows, &tval);
565         if( r != ERROR_SUCCESS )
566             return r;
567         *val = tval - 0x8000;
568         return ERROR_SUCCESS;
569 
570     case EXPR_COL_NUMBER32:
571         r = expr_fetch_value(&cond->u.column, rows, &tval);
572         if( r != ERROR_SUCCESS )
573             return r;
574         *val = tval - 0x80000000;
575         return r;
576 
577     case EXPR_UVAL:
578         *val = cond->u.uval;
579         return ERROR_SUCCESS;
580 
581     case EXPR_COMPLEX:
582         return INT_evaluate_binary(wv, rows, &cond->u.expr, val, record);
583 
584     case EXPR_UNARY:
585         return INT_evaluate_unary( wv, rows, &cond->u.expr, val, record );
586 
587     case EXPR_STRCMP:
588         return STRCMP_Evaluate( wv, rows, &cond->u.expr, val, record );
589 
590     case EXPR_WILDCARD:
591         *val = MSI_RecordGetInteger( record, ++wv->rec_index );
592         return ERROR_SUCCESS;
593 
594     default:
595         ERR("Invalid expression type\n");
596         return ERROR_FUNCTION_FAILED;
597     }
598 
599     return ERROR_SUCCESS;
600 }
601 
602 static UINT check_condition( MSIWHEREVIEW *wv, MSIRECORD *record, JOINTABLE **tables,
603                              UINT table_rows[] )
604 {
605     UINT r = ERROR_FUNCTION_FAILED;
606     INT val;
607 
608     for (table_rows[(*tables)->table_index] = 0;
609          table_rows[(*tables)->table_index] < (*tables)->row_count;
610          table_rows[(*tables)->table_index]++)
611     {
612         val = 0;
613         wv->rec_index = 0;
614         r = WHERE_evaluate( wv, table_rows, wv->cond, &val, record );
615         if (r != ERROR_SUCCESS && r != ERROR_CONTINUE)
616             break;
617         if (val)
618         {
619             if (*(tables + 1))
620             {
621                 r = check_condition(wv, record, tables + 1, table_rows);
622                 if (r != ERROR_SUCCESS)
623                     break;
624             }
625             else
626             {
627                 if (r != ERROR_SUCCESS)
628                     break;
629                 add_row (wv, table_rows);
630             }
631         }
632     }
633     table_rows[(*tables)->table_index] = INVALID_ROW_INDEX;
634     return r;
635 }
636 
637 static int compare_entry( const void *left, const void *right )
638 {
639     const MSIROWENTRY *le = *(const MSIROWENTRY**)left;
640     const MSIROWENTRY *re = *(const MSIROWENTRY**)right;
641     const MSIWHEREVIEW *wv = le->wv;
642     MSIORDERINFO *order = wv->order_info;
643     UINT i, j, r, l_val, r_val;
644 
645     assert(le->wv == re->wv);
646 
647     if (order)
648     {
649         for (i = 0; i < order->col_count; i++)
650         {
651             const union ext_column *column = &order->columns[i];
652 
653             r = column->parsed.table->view->ops->fetch_int(column->parsed.table->view,
654                           le->values[column->parsed.table->table_index],
655                           column->parsed.column, &l_val);
656             if (r != ERROR_SUCCESS)
657             {
658                 order->error = r;
659                 return 0;
660             }
661 
662             r = column->parsed.table->view->ops->fetch_int(column->parsed.table->view,
663                           re->values[column->parsed.table->table_index],
664                           column->parsed.column, &r_val);
665             if (r != ERROR_SUCCESS)
666             {
667                 order->error = r;
668                 return 0;
669             }
670 
671             if (l_val != r_val)
672                 return l_val < r_val ? -1 : 1;
673         }
674     }
675 
676     for (j = 0; j < wv->table_count; j++)
677     {
678         if (le->values[j] != re->values[j])
679             return le->values[j] < re->values[j] ? -1 : 1;
680     }
681     return 0;
682 }
683 
684 static void add_to_array( JOINTABLE **array, JOINTABLE *elem )
685 {
686     while (*array && *array != elem)
687         array++;
688     if (!*array)
689         *array = elem;
690 }
691 
692 static BOOL in_array( JOINTABLE **array, JOINTABLE *elem )
693 {
694     while (*array && *array != elem)
695         array++;
696     return *array != NULL;
697 }
698 
699 #define CONST_EXPR 1 /* comparison to a constant value */
700 #define JOIN_TO_CONST_EXPR 0x10000 /* comparison to a table involved with
701                                       a CONST_EXPR comaprison */
702 
703 static UINT reorder_check( const struct expr *expr, JOINTABLE **ordered_tables,
704                            BOOL process_joins, JOINTABLE **lastused )
705 {
706     UINT res = 0;
707 
708     switch (expr->type)
709     {
710         case EXPR_WILDCARD:
711         case EXPR_SVAL:
712         case EXPR_UVAL:
713             return 0;
714         case EXPR_COL_NUMBER:
715         case EXPR_COL_NUMBER32:
716         case EXPR_COL_NUMBER_STRING:
717             if (in_array(ordered_tables, expr->u.column.parsed.table))
718                 return JOIN_TO_CONST_EXPR;
719             *lastused = expr->u.column.parsed.table;
720             return CONST_EXPR;
721         case EXPR_STRCMP:
722         case EXPR_COMPLEX:
723             res = reorder_check(expr->u.expr.right, ordered_tables, process_joins, lastused);
724             /* fall through */
725         case EXPR_UNARY:
726             res += reorder_check(expr->u.expr.left, ordered_tables, process_joins, lastused);
727             if (res == 0)
728                 return 0;
729             if (res == CONST_EXPR)
730                 add_to_array(ordered_tables, *lastused);
731             if (process_joins && res == JOIN_TO_CONST_EXPR + CONST_EXPR)
732                 add_to_array(ordered_tables, *lastused);
733             return res;
734         default:
735             ERR("Unknown expr type: %i\n", expr->type);
736             assert(0);
737             return 0x1000000;
738     }
739 }
740 
741 /* reorders the tablelist in a way to evaluate the condition as fast as possible */
742 static JOINTABLE **ordertables( MSIWHEREVIEW *wv )
743 {
744     JOINTABLE *table;
745     JOINTABLE **tables;
746 
747     tables = msi_alloc_zero( (wv->table_count + 1) * sizeof(*tables) );
748 
749     if (wv->cond)
750     {
751         table = NULL;
752         reorder_check(wv->cond, tables, FALSE, &table);
753         table = NULL;
754         reorder_check(wv->cond, tables, TRUE, &table);
755     }
756 
757     table = wv->tables;
758     while (table)
759     {
760         add_to_array(tables, table);
761         table = table->next;
762     }
763     return tables;
764 }
765 
766 static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
767 {
768     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
769     UINT r;
770     JOINTABLE *table = wv->tables;
771     UINT *rows;
772     JOINTABLE **ordered_tables;
773     UINT i = 0;
774 
775     TRACE("%p %p\n", wv, record);
776 
777     if( !table )
778          return ERROR_FUNCTION_FAILED;
779 
780     r = init_reorder(wv);
781     if (r != ERROR_SUCCESS)
782         return r;
783 
784     do
785     {
786         table->view->ops->execute(table->view, NULL);
787 
788         r = table->view->ops->get_dimensions(table->view, &table->row_count, NULL);
789         if (r != ERROR_SUCCESS)
790         {
791             ERR("failed to get table dimensions\n");
792             return r;
793         }
794 
795         /* each table must have at least one row */
796         if (table->row_count == 0)
797             return ERROR_SUCCESS;
798     }
799     while ((table = table->next));
800 
801     ordered_tables = ordertables( wv );
802 
803     rows = msi_alloc( wv->table_count * sizeof(*rows) );
804     for (i = 0; i < wv->table_count; i++)
805         rows[i] = INVALID_ROW_INDEX;
806 
807     r =  check_condition(wv, record, ordered_tables, rows);
808 
809     if (wv->order_info)
810         wv->order_info->error = ERROR_SUCCESS;
811 
812     qsort(wv->reorder, wv->row_count, sizeof(MSIROWENTRY *), compare_entry);
813 
814     if (wv->order_info)
815         r = wv->order_info->error;
816 
817     msi_free( rows );
818     msi_free( ordered_tables );
819     return r;
820 }
821 
822 static UINT WHERE_close( struct tagMSIVIEW *view )
823 {
824     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
825     JOINTABLE *table = wv->tables;
826 
827     TRACE("%p\n", wv );
828 
829     if (!table)
830         return ERROR_FUNCTION_FAILED;
831 
832     do
833         table->view->ops->close(table->view);
834     while ((table = table->next));
835 
836     return ERROR_SUCCESS;
837 }
838 
839 static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
840 {
841     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
842 
843     TRACE("%p %p %p\n", wv, rows, cols );
844 
845     if(!wv->tables)
846          return ERROR_FUNCTION_FAILED;
847 
848     if (rows)
849     {
850         if (!wv->reorder)
851             return ERROR_FUNCTION_FAILED;
852         *rows = wv->row_count;
853     }
854 
855     if (cols)
856         *cols = wv->col_count;
857 
858     return ERROR_SUCCESS;
859 }
860 
861 static UINT WHERE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
862                                    UINT *type, BOOL *temporary, LPCWSTR *table_name )
863 {
864     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
865     JOINTABLE *table;
866 
867     TRACE("%p %d %p %p %p %p\n", wv, n, name, type, temporary, table_name );
868 
869     if(!wv->tables)
870          return ERROR_FUNCTION_FAILED;
871 
872     table = find_table(wv, n, &n);
873     if (!table)
874         return ERROR_FUNCTION_FAILED;
875 
876     return table->view->ops->get_column_info(table->view, n, name,
877                                             type, temporary, table_name);
878 }
879 
880 static UINT join_find_row( MSIWHEREVIEW *wv, MSIRECORD *rec, UINT *row )
881 {
882     LPCWSTR str;
883     UINT r, i, id, data;
884 
885     str = MSI_RecordGetString( rec, 1 );
886     r = msi_string2id( wv->db->strings, str, -1, &id );
887     if (r != ERROR_SUCCESS)
888         return r;
889 
890     for (i = 0; i < wv->row_count; i++)
891     {
892         WHERE_fetch_int( &wv->view, i, 1, &data );
893 
894         if (data == id)
895         {
896             *row = i;
897             return ERROR_SUCCESS;
898         }
899     }
900 
901     return ERROR_FUNCTION_FAILED;
902 }
903 
904 static UINT join_modify_update( struct tagMSIVIEW *view, MSIRECORD *rec )
905 {
906     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
907     UINT r, row, i, mask = 0;
908     MSIRECORD *current;
909 
910 
911     r = join_find_row( wv, rec, &row );
912     if (r != ERROR_SUCCESS)
913         return r;
914 
915     r = msi_view_get_row( wv->db, view, row, &current );
916     if (r != ERROR_SUCCESS)
917         return r;
918 
919     assert(MSI_RecordGetFieldCount(rec) == MSI_RecordGetFieldCount(current));
920 
921     for (i = MSI_RecordGetFieldCount(rec); i > 0; i--)
922     {
923         if (!MSI_RecordsAreFieldsEqual(rec, current, i))
924             mask |= 1 << (i - 1);
925     }
926      msiobj_release(&current->hdr);
927 
928     return WHERE_set_row( view, row, rec, mask );
929 }
930 
931 static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
932                           MSIRECORD *rec, UINT row )
933 {
934     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
935     JOINTABLE *table = wv->tables;
936     UINT r;
937 
938     TRACE("%p %d %p\n", wv, eModifyMode, rec);
939 
940     if (!table)
941         return ERROR_FUNCTION_FAILED;
942 
943     if (!table->next)
944     {
945         UINT *rows;
946 
947         if (find_row(wv, row - 1, &rows) == ERROR_SUCCESS)
948             row = rows[0] + 1;
949         else
950             row = -1;
951 
952         return table->view->ops->modify(table->view, eModifyMode, rec, row);
953     }
954 
955     switch (eModifyMode)
956     {
957     case MSIMODIFY_UPDATE:
958         return join_modify_update( view, rec );
959 
960     case MSIMODIFY_ASSIGN:
961     case MSIMODIFY_DELETE:
962     case MSIMODIFY_INSERT:
963     case MSIMODIFY_INSERT_TEMPORARY:
964     case MSIMODIFY_MERGE:
965     case MSIMODIFY_REPLACE:
966     case MSIMODIFY_SEEK:
967     case MSIMODIFY_VALIDATE:
968     case MSIMODIFY_VALIDATE_DELETE:
969     case MSIMODIFY_VALIDATE_FIELD:
970     case MSIMODIFY_VALIDATE_NEW:
971         r = ERROR_FUNCTION_FAILED;
972         break;
973 
974     case MSIMODIFY_REFRESH:
975         r = ERROR_CALL_NOT_IMPLEMENTED;
976         break;
977 
978     default:
979         WARN("%p %d %p %u - unknown mode\n", view, eModifyMode, rec, row );
980         r = ERROR_INVALID_PARAMETER;
981         break;
982     }
983 
984     return r;
985 }
986 
987 static UINT WHERE_delete( struct tagMSIVIEW *view )
988 {
989     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
990     JOINTABLE *table = wv->tables;
991 
992     TRACE("%p\n", wv );
993 
994     while(table)
995     {
996         JOINTABLE *next;
997 
998         table->view->ops->delete(table->view);
999         table->view = NULL;
1000         next = table->next;
1001         msi_free(table);
1002         table = next;
1003     }
1004     wv->tables = NULL;
1005     wv->table_count = 0;
1006 
1007     free_reorder(wv);
1008 
1009     msi_free(wv->order_info);
1010     wv->order_info = NULL;
1011 
1012     msiobj_release( &wv->db->hdr );
1013     msi_free( wv );
1014 
1015     return ERROR_SUCCESS;
1016 }
1017 
1018 static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
1019     UINT val, UINT *row, MSIITERHANDLE *handle )
1020 {
1021     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
1022     UINT i, row_value;
1023 
1024     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
1025 
1026     if (!wv->tables)
1027          return ERROR_FUNCTION_FAILED;
1028 
1029     if (col == 0 || col > wv->col_count)
1030         return ERROR_INVALID_PARAMETER;
1031 
1032     for (i = PtrToUlong(*handle); i < wv->row_count; i++)
1033     {
1034         if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS)
1035             continue;
1036 
1037         if (row_value == val)
1038         {
1039             *row = i;
1040             *handle = UlongToPtr(i + 1);
1041             return ERROR_SUCCESS;
1042         }
1043     }
1044 
1045     return ERROR_NO_MORE_ITEMS;
1046 }
1047 
1048 static UINT WHERE_sort(struct tagMSIVIEW *view, column_info *columns)
1049 {
1050     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
1051     JOINTABLE *table = wv->tables;
1052     column_info *column = columns;
1053     MSIORDERINFO *orderinfo;
1054     UINT r, count = 0;
1055     UINT i;
1056 
1057     TRACE("%p %p\n", view, columns);
1058 
1059     if (!table)
1060         return ERROR_FUNCTION_FAILED;
1061 
1062     while (column)
1063     {
1064         count++;
1065         column = column->next;
1066     }
1067 
1068     if (count == 0)
1069         return ERROR_SUCCESS;
1070 
1071     orderinfo = msi_alloc(FIELD_OFFSET(MSIORDERINFO, columns[count]));
1072     if (!orderinfo)
1073         return ERROR_OUTOFMEMORY;
1074 
1075     orderinfo->col_count = count;
1076 
1077     column = columns;
1078 
1079     for (i = 0; i < count; i++)
1080     {
1081         orderinfo->columns[i].unparsed.column = column->column;
1082         orderinfo->columns[i].unparsed.table = column->table;
1083 
1084         r = parse_column(wv, &orderinfo->columns[i], NULL);
1085         if (r != ERROR_SUCCESS)
1086             goto error;
1087     }
1088 
1089     wv->order_info = orderinfo;
1090 
1091     return ERROR_SUCCESS;
1092 error:
1093     msi_free(orderinfo);
1094     return r;
1095 }
1096 
1097 static const MSIVIEWOPS where_ops =
1098 {
1099     WHERE_fetch_int,
1100     WHERE_fetch_stream,
1101     WHERE_get_row,
1102     WHERE_set_row,
1103     NULL,
1104     WHERE_delete_row,
1105     WHERE_execute,
1106     WHERE_close,
1107     WHERE_get_dimensions,
1108     WHERE_get_column_info,
1109     WHERE_modify,
1110     WHERE_delete,
1111     WHERE_find_matching_rows,
1112     NULL,
1113     NULL,
1114     NULL,
1115     NULL,
1116     WHERE_sort,
1117     NULL,
1118 };
1119 
1120 static UINT WHERE_VerifyCondition( MSIWHEREVIEW *wv, struct expr *cond,
1121                                    UINT *valid )
1122 {
1123     UINT r;
1124 
1125     switch( cond->type )
1126     {
1127     case EXPR_COLUMN:
1128     {
1129         UINT type;
1130 
1131         *valid = FALSE;
1132 
1133         r = parse_column(wv, &cond->u.column, &type);
1134         if (r != ERROR_SUCCESS)
1135             break;
1136 
1137         if (type&MSITYPE_STRING)
1138             cond->type = EXPR_COL_NUMBER_STRING;
1139         else if ((type&0xff) == 4)
1140             cond->type = EXPR_COL_NUMBER32;
1141         else
1142             cond->type = EXPR_COL_NUMBER;
1143 
1144         *valid = TRUE;
1145         break;
1146     }
1147     case EXPR_COMPLEX:
1148         r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid );
1149         if( r != ERROR_SUCCESS )
1150             return r;
1151         if( !*valid )
1152             return ERROR_SUCCESS;
1153         r = WHERE_VerifyCondition( wv, cond->u.expr.right, valid );
1154         if( r != ERROR_SUCCESS )
1155             return r;
1156 
1157         /* check the type of the comparison */
1158         if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
1159             ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
1160             ( cond->u.expr.right->type == EXPR_SVAL ) ||
1161             ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
1162         {
1163             switch( cond->u.expr.op )
1164             {
1165             case OP_EQ:
1166             case OP_NE:
1167                 break;
1168             default:
1169                 *valid = FALSE;
1170                 return ERROR_INVALID_PARAMETER;
1171             }
1172 
1173             /* FIXME: check we're comparing a string to a column */
1174 
1175             cond->type = EXPR_STRCMP;
1176         }
1177 
1178         break;
1179     case EXPR_UNARY:
1180         if ( cond->u.expr.left->type != EXPR_COLUMN )
1181         {
1182             *valid = FALSE;
1183             return ERROR_INVALID_PARAMETER;
1184         }
1185         r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid );
1186         if( r != ERROR_SUCCESS )
1187             return r;
1188         break;
1189     case EXPR_IVAL:
1190         *valid = 1;
1191         cond->type = EXPR_UVAL;
1192         cond->u.uval = cond->u.ival;
1193         break;
1194     case EXPR_WILDCARD:
1195         *valid = 1;
1196         break;
1197     case EXPR_SVAL:
1198         *valid = 1;
1199         break;
1200     default:
1201         ERR("Invalid expression type\n");
1202         *valid = 0;
1203         break;
1204     }
1205 
1206     return ERROR_SUCCESS;
1207 }
1208 
1209 UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR tables,
1210                        struct expr *cond )
1211 {
1212     MSIWHEREVIEW *wv = NULL;
1213     UINT r, valid = 0;
1214     WCHAR *ptr;
1215 
1216     TRACE("(%s)\n", debugstr_w(tables) );
1217 
1218     wv = msi_alloc_zero( sizeof *wv );
1219     if( !wv )
1220         return ERROR_FUNCTION_FAILED;
1221 
1222     /* fill the structure */
1223     wv->view.ops = &where_ops;
1224     msiobj_addref( &db->hdr );
1225     wv->db = db;
1226     wv->cond = cond;
1227 
1228     while (*tables)
1229     {
1230         JOINTABLE *table;
1231 
1232         if ((ptr = strchrW(tables, ' ')))
1233             *ptr = '\0';
1234 
1235         table = msi_alloc(sizeof(JOINTABLE));
1236         if (!table)
1237         {
1238             r = ERROR_OUTOFMEMORY;
1239             goto end;
1240         }
1241 
1242         r = TABLE_CreateView(db, tables, &table->view);
1243         if (r != ERROR_SUCCESS)
1244         {
1245             WARN("can't create table: %s\n", debugstr_w(tables));
1246             msi_free(table);
1247             r = ERROR_BAD_QUERY_SYNTAX;
1248             goto end;
1249         }
1250 
1251         r = table->view->ops->get_dimensions(table->view, NULL,
1252                                              &table->col_count);
1253         if (r != ERROR_SUCCESS)
1254         {
1255             ERR("can't get table dimensions\n");
1256             table->view->ops->delete(table->view);
1257             msi_free(table);
1258             goto end;
1259         }
1260 
1261         wv->col_count += table->col_count;
1262         table->table_index = wv->table_count++;
1263 
1264         table->next = wv->tables;
1265         wv->tables = table;
1266 
1267         if (!ptr)
1268             break;
1269 
1270         tables = ptr + 1;
1271     }
1272 
1273     if( cond )
1274     {
1275         r = WHERE_VerifyCondition( wv, cond, &valid );
1276         if( r != ERROR_SUCCESS )
1277             goto end;
1278         if( !valid ) {
1279             r = ERROR_FUNCTION_FAILED;
1280             goto end;
1281         }
1282     }
1283 
1284     *view = (MSIVIEW*) wv;
1285 
1286     return ERROR_SUCCESS;
1287 end:
1288     WHERE_delete(&wv->view);
1289 
1290     return r;
1291 }
1292