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