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