dinput: Fix printing NULL strings.
[wine] / dlls / msi / where.c
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             break;
209         }
210     }
211     while ((table = table->next));
212
213     WARN("Couldn't find column %s.%s\n", debugstr_w( column->unparsed.table ), debugstr_w( column->unparsed.column ) );
214     return ERROR_BAD_QUERY_SYNTAX;
215 }
216
217 static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
218 {
219     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
220     JOINTABLE *table;
221     UINT *rows;
222     UINT r;
223
224     TRACE("%p %d %d %p\n", wv, row, col, val );
225
226     if( !wv->tables )
227         return ERROR_FUNCTION_FAILED;
228
229     r = find_row(wv, row, &rows);
230     if (r != ERROR_SUCCESS)
231         return r;
232
233     table = find_table(wv, col, &col);
234     if (!table)
235         return ERROR_FUNCTION_FAILED;
236
237     return table->view->ops->fetch_int(table->view, rows[table->table_index], col, val);
238 }
239
240 static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
241 {
242     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
243     JOINTABLE *table;
244     UINT *rows;
245     UINT r;
246
247     TRACE("%p %d %d %p\n", wv, row, col, stm );
248
249     if( !wv->tables )
250         return ERROR_FUNCTION_FAILED;
251
252     r = find_row(wv, row, &rows);
253     if (r != ERROR_SUCCESS)
254         return r;
255
256     table = find_table(wv, col, &col);
257     if (!table)
258         return ERROR_FUNCTION_FAILED;
259
260     return table->view->ops->fetch_stream( table->view, rows[table->table_index], col, stm );
261 }
262
263 static UINT WHERE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
264 {
265     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
266
267     TRACE("%p %d %p\n", wv, row, rec );
268
269     if (!wv->tables)
270         return ERROR_FUNCTION_FAILED;
271
272     return msi_view_get_row( wv->db, view, row, rec );
273 }
274
275 static UINT WHERE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
276 {
277     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
278     UINT i, r, offset = 0;
279     JOINTABLE *table = wv->tables;
280     UINT *rows;
281     UINT mask_copy = mask;
282
283     TRACE("%p %d %p %08x\n", wv, row, rec, mask );
284
285     if( !wv->tables )
286          return ERROR_FUNCTION_FAILED;
287
288     r = find_row(wv, row, &rows);
289     if (r != ERROR_SUCCESS)
290         return r;
291
292     if (mask >= 1 << wv->col_count)
293         return ERROR_INVALID_PARAMETER;
294
295     do
296     {
297         for (i = 0; i < table->col_count; i++) {
298             UINT type;
299
300             if (!(mask_copy & (1 << i)))
301                 continue;
302             r = table->view->ops->get_column_info(table->view, i + 1, NULL,
303                                             &type, NULL, NULL );
304             if (r != ERROR_SUCCESS)
305                 return r;
306             if (type & MSITYPE_KEY)
307                 return ERROR_FUNCTION_FAILED;
308         }
309         mask_copy >>= table->col_count;
310     }
311     while (mask_copy && (table = table->next));
312
313     table = wv->tables;
314
315     do
316     {
317         const UINT col_count = table->col_count;
318         UINT i;
319         MSIRECORD *reduced;
320         UINT reduced_mask = (mask >> offset) & ((1 << col_count) - 1);
321
322         if (!reduced_mask)
323         {
324             offset += col_count;
325             continue;
326         }
327
328         reduced = MSI_CreateRecord(col_count);
329         if (!reduced)
330             return ERROR_FUNCTION_FAILED;
331
332         for (i = 1; i <= col_count; i++)
333         {
334             r = MSI_RecordCopyField(rec, i + offset, reduced, i);
335             if (r != ERROR_SUCCESS)
336                 break;
337         }
338
339         offset += col_count;
340
341         if (r == ERROR_SUCCESS)
342             r = table->view->ops->set_row(table->view, rows[table->table_index], reduced, reduced_mask);
343
344         msiobj_release(&reduced->hdr);
345     }
346     while ((table = table->next));
347     return r;
348 }
349
350 static UINT WHERE_delete_row(struct tagMSIVIEW *view, UINT row)
351 {
352     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
353     UINT r;
354     UINT *rows;
355
356     TRACE("(%p %d)\n", view, row);
357
358     if (!wv->tables)
359         return ERROR_FUNCTION_FAILED;
360
361     r = find_row(wv, row, &rows);
362     if ( r != ERROR_SUCCESS )
363         return r;
364
365     if (wv->table_count > 1)
366         return ERROR_CALL_NOT_IMPLEMENTED;
367
368     return wv->tables->view->ops->delete_row(wv->tables->view, rows[0]);
369 }
370
371 static INT INT_evaluate_binary( MSIWHEREVIEW *wv, const UINT rows[],
372                                 const struct complex_expr *expr, INT *val, MSIRECORD *record )
373 {
374     UINT rl, rr;
375     INT lval, rval;
376
377     rl = WHERE_evaluate(wv, rows, expr->left, &lval, record);
378     if (rl != ERROR_SUCCESS && rl != ERROR_CONTINUE)
379         return rl;
380     rr = WHERE_evaluate(wv, rows, expr->right, &rval, record);
381     if (rr != ERROR_SUCCESS && rr != ERROR_CONTINUE)
382         return rr;
383
384     if (rl == ERROR_CONTINUE || rr == ERROR_CONTINUE)
385     {
386         if (rl == rr)
387         {
388             *val = TRUE;
389             return ERROR_CONTINUE;
390         }
391
392         if (expr->op == OP_AND)
393         {
394             if ((rl == ERROR_CONTINUE && !rval) || (rr == ERROR_CONTINUE && !lval))
395             {
396                 *val = FALSE;
397                 return ERROR_SUCCESS;
398             }
399         }
400         else if (expr->op == OP_OR)
401         {
402             if ((rl == ERROR_CONTINUE && rval) || (rr == ERROR_CONTINUE && lval))
403             {
404                 *val = TRUE;
405                 return ERROR_SUCCESS;
406             }
407         }
408
409         *val = TRUE;
410         return ERROR_CONTINUE;
411     }
412
413     switch( expr->op )
414     {
415     case OP_EQ:
416         *val = ( lval == rval );
417         break;
418     case OP_AND:
419         *val = ( lval && rval );
420         break;
421     case OP_OR:
422         *val = ( lval || rval );
423         break;
424     case OP_GT:
425         *val = ( lval > rval );
426         break;
427     case OP_LT:
428         *val = ( lval < rval );
429         break;
430     case OP_LE:
431         *val = ( lval <= rval );
432         break;
433     case OP_GE:
434         *val = ( lval >= rval );
435         break;
436     case OP_NE:
437         *val = ( lval != rval );
438         break;
439     default:
440         ERR("Unknown operator %d\n", expr->op );
441         return ERROR_FUNCTION_FAILED;
442     }
443
444     return ERROR_SUCCESS;
445 }
446
447 static inline UINT expr_fetch_value(const union ext_column *expr, const UINT rows[], UINT *val)
448 {
449     JOINTABLE *table = expr->parsed.table;
450
451     if( rows[table->table_index] == INVALID_ROW_INDEX )
452     {
453         *val = 1;
454         return ERROR_CONTINUE;
455     }
456     return table->view->ops->fetch_int(table->view, rows[table->table_index],
457                                         expr->parsed.column, val);
458 }
459
460
461 static UINT INT_evaluate_unary( MSIWHEREVIEW *wv, const UINT rows[],
462                                 const struct complex_expr *expr, INT *val, MSIRECORD *record )
463 {
464     UINT r;
465     UINT lval;
466
467     r = expr_fetch_value(&expr->left->u.column, rows, &lval);
468     if(r != ERROR_SUCCESS)
469         return r;
470
471     switch( expr->op )
472     {
473     case OP_ISNULL:
474         *val = !lval;
475         break;
476     case OP_NOTNULL:
477         *val = lval;
478         break;
479     default:
480         ERR("Unknown operator %d\n", expr->op );
481         return ERROR_FUNCTION_FAILED;
482     }
483     return ERROR_SUCCESS;
484 }
485
486 static UINT STRING_evaluate( MSIWHEREVIEW *wv, const UINT rows[],
487                                      const struct expr *expr,
488                                      const MSIRECORD *record,
489                                      const WCHAR **str )
490 {
491     UINT val = 0, r = ERROR_SUCCESS;
492
493     switch( expr->type )
494     {
495     case EXPR_COL_NUMBER_STRING:
496         r = expr_fetch_value(&expr->u.column, rows, &val);
497         if (r == ERROR_SUCCESS)
498             *str =  msi_string_lookup_id(wv->db->strings, val);
499         else
500             *str = NULL;
501         break;
502
503     case EXPR_SVAL:
504         *str = expr->u.sval;
505         break;
506
507     case EXPR_WILDCARD:
508         *str = MSI_RecordGetString(record, ++wv->rec_index);
509         break;
510
511     default:
512         ERR("Invalid expression type\n");
513         r = ERROR_FUNCTION_FAILED;
514         *str = NULL;
515         break;
516     }
517     return r;
518 }
519
520 static UINT STRCMP_Evaluate( MSIWHEREVIEW *wv, const UINT rows[], const struct complex_expr *expr,
521                              INT *val, const MSIRECORD *record )
522 {
523     int sr;
524     const WCHAR *l_str, *r_str;
525     UINT r;
526
527     *val = TRUE;
528     r = STRING_evaluate(wv, rows, expr->left, record, &l_str);
529     if (r == ERROR_CONTINUE)
530         return r;
531     r = STRING_evaluate(wv, rows, expr->right, record, &r_str);
532     if (r == ERROR_CONTINUE)
533         return r;
534
535     if( l_str == r_str ||
536         ((!l_str || !*l_str) && (!r_str || !*r_str)) )
537         sr = 0;
538     else if( l_str && ! r_str )
539         sr = 1;
540     else if( r_str && ! l_str )
541         sr = -1;
542     else
543         sr = strcmpW( l_str, r_str );
544
545     *val = ( expr->op == OP_EQ && ( sr == 0 ) ) ||
546            ( expr->op == OP_NE && ( sr != 0 ) );
547
548     return ERROR_SUCCESS;
549 }
550
551 static UINT WHERE_evaluate( MSIWHEREVIEW *wv, const UINT rows[],
552                             struct expr *cond, INT *val, MSIRECORD *record )
553 {
554     UINT r, tval;
555
556     if( !cond )
557     {
558         *val = TRUE;
559         return ERROR_SUCCESS;
560     }
561
562     switch( cond->type )
563     {
564     case EXPR_COL_NUMBER:
565         r = expr_fetch_value(&cond->u.column, rows, &tval);
566         if( r != ERROR_SUCCESS )
567             return r;
568         *val = tval - 0x8000;
569         return ERROR_SUCCESS;
570
571     case EXPR_COL_NUMBER32:
572         r = expr_fetch_value(&cond->u.column, rows, &tval);
573         if( r != ERROR_SUCCESS )
574             return r;
575         *val = tval - 0x80000000;
576         return r;
577
578     case EXPR_UVAL:
579         *val = cond->u.uval;
580         return ERROR_SUCCESS;
581
582     case EXPR_COMPLEX:
583         return INT_evaluate_binary(wv, rows, &cond->u.expr, val, record);
584
585     case EXPR_UNARY:
586         return INT_evaluate_unary( wv, rows, &cond->u.expr, val, record );
587
588     case EXPR_STRCMP:
589         return STRCMP_Evaluate( wv, rows, &cond->u.expr, val, record );
590
591     case EXPR_WILDCARD:
592         *val = MSI_RecordGetInteger( record, ++wv->rec_index );
593         return ERROR_SUCCESS;
594
595     default:
596         ERR("Invalid expression type\n");
597         break;
598     }
599
600     return ERROR_SUCCESS;
601 }
602
603 static UINT check_condition( MSIWHEREVIEW *wv, MSIRECORD *record, JOINTABLE *table,
604                              UINT table_rows[] )
605 {
606     UINT r = ERROR_FUNCTION_FAILED;
607     INT val;
608
609     for (table_rows[table->table_index] = 0; table_rows[table->table_index] < table->row_count;
610          table_rows[table->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 (table->next)
620             {
621                 r = check_condition(wv, record, table->next, 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[table->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 UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
685 {
686     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
687     UINT r;
688     JOINTABLE *table = wv->tables;
689     UINT *rows;
690     int i;
691
692     TRACE("%p %p\n", wv, record);
693
694     if( !table )
695          return ERROR_FUNCTION_FAILED;
696
697     r = init_reorder(wv);
698     if (r != ERROR_SUCCESS)
699         return r;
700
701     do
702     {
703         table->view->ops->execute(table->view, NULL);
704
705         r = table->view->ops->get_dimensions(table->view, &table->row_count, NULL);
706         if (r != ERROR_SUCCESS)
707         {
708             ERR("failed to get table dimensions\n");
709             return r;
710         }
711
712         /* each table must have at least one row */
713         if (table->row_count == 0)
714             return ERROR_SUCCESS;
715     }
716     while ((table = table->next));
717
718     rows = msi_alloc( wv->table_count * sizeof(*rows) );
719     for (i = 0; i < wv->table_count; i++)
720         rows[i] = INVALID_ROW_INDEX;
721
722     r =  check_condition(wv, record, wv->tables, rows);
723
724     if (wv->order_info)
725         wv->order_info->error = ERROR_SUCCESS;
726
727     qsort(wv->reorder, wv->row_count, sizeof(MSIROWENTRY *), compare_entry);
728
729     if (wv->order_info)
730         r = wv->order_info->error;
731
732     msi_free( rows );
733     return r;
734 }
735
736 static UINT WHERE_close( struct tagMSIVIEW *view )
737 {
738     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
739     JOINTABLE *table = wv->tables;
740
741     TRACE("%p\n", wv );
742
743     if (!table)
744         return ERROR_FUNCTION_FAILED;
745
746     do
747         table->view->ops->close(table->view);
748     while ((table = table->next));
749
750     return ERROR_SUCCESS;
751 }
752
753 static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
754 {
755     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
756
757     TRACE("%p %p %p\n", wv, rows, cols );
758
759     if(!wv->tables)
760          return ERROR_FUNCTION_FAILED;
761
762     if (rows)
763     {
764         if (!wv->reorder)
765             return ERROR_FUNCTION_FAILED;
766         *rows = wv->row_count;
767     }
768
769     if (cols)
770         *cols = wv->col_count;
771
772     return ERROR_SUCCESS;
773 }
774
775 static UINT WHERE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
776                                    UINT *type, BOOL *temporary, LPCWSTR *table_name )
777 {
778     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
779     JOINTABLE *table;
780
781     TRACE("%p %d %p %p %p %p\n", wv, n, name, type, temporary, table_name );
782
783     if(!wv->tables)
784          return ERROR_FUNCTION_FAILED;
785
786     table = find_table(wv, n, &n);
787     if (!table)
788         return ERROR_FUNCTION_FAILED;
789
790     return table->view->ops->get_column_info(table->view, n, name,
791                                             type, temporary, table_name);
792 }
793
794 static UINT join_find_row( MSIWHEREVIEW *wv, MSIRECORD *rec, UINT *row )
795 {
796     LPCWSTR str;
797     UINT r, i, id, data;
798
799     str = MSI_RecordGetString( rec, 1 );
800     r = msi_string2idW( wv->db->strings, str, &id );
801     if (r != ERROR_SUCCESS)
802         return r;
803
804     for (i = 0; i < wv->row_count; i++)
805     {
806         WHERE_fetch_int( &wv->view, i, 1, &data );
807
808         if (data == id)
809         {
810             *row = i;
811             return ERROR_SUCCESS;
812         }
813     }
814
815     return ERROR_FUNCTION_FAILED;
816 }
817
818 static UINT join_modify_update( struct tagMSIVIEW *view, MSIRECORD *rec )
819 {
820     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
821     UINT r, row, i, mask = 0;
822     MSIRECORD *current;
823
824
825     r = join_find_row( wv, rec, &row );
826     if (r != ERROR_SUCCESS)
827         return r;
828
829     r = msi_view_get_row( wv->db, view, row, &current );
830     if (r != ERROR_SUCCESS)
831         return r;
832
833     assert(MSI_RecordGetFieldCount(rec) == MSI_RecordGetFieldCount(current));
834
835     for (i = MSI_RecordGetFieldCount(rec); i > 0; i--)
836     {
837         if (!MSI_RecordsAreFieldsEqual(rec, current, i))
838             mask |= 1 << (i - 1);
839     }
840      msiobj_release(&current->hdr);
841
842     return WHERE_set_row( view, row, rec, mask );
843 }
844
845 static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
846                           MSIRECORD *rec, UINT row )
847 {
848     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
849     JOINTABLE *table = wv->tables;
850     UINT r;
851
852     TRACE("%p %d %p\n", wv, eModifyMode, rec);
853
854     if (!table)
855         return ERROR_FUNCTION_FAILED;
856
857     if (!table->next)
858     {
859         UINT *rows;
860
861         if (find_row(wv, row - 1, &rows) == ERROR_SUCCESS)
862             row = rows[0] + 1;
863         else
864             row = -1;
865
866         return table->view->ops->modify(table->view, eModifyMode, rec, row);
867     }
868
869     switch (eModifyMode)
870     {
871     case MSIMODIFY_UPDATE:
872         return join_modify_update( view, rec );
873
874     case MSIMODIFY_ASSIGN:
875     case MSIMODIFY_DELETE:
876     case MSIMODIFY_INSERT:
877     case MSIMODIFY_INSERT_TEMPORARY:
878     case MSIMODIFY_MERGE:
879     case MSIMODIFY_REPLACE:
880     case MSIMODIFY_SEEK:
881     case MSIMODIFY_VALIDATE:
882     case MSIMODIFY_VALIDATE_DELETE:
883     case MSIMODIFY_VALIDATE_FIELD:
884     case MSIMODIFY_VALIDATE_NEW:
885         r = ERROR_FUNCTION_FAILED;
886         break;
887
888     case MSIMODIFY_REFRESH:
889         r = ERROR_CALL_NOT_IMPLEMENTED;
890         break;
891
892     default:
893         WARN("%p %d %p %u - unknown mode\n", view, eModifyMode, rec, row );
894         r = ERROR_INVALID_PARAMETER;
895         break;
896     }
897
898     return r;
899 }
900
901 static UINT WHERE_delete( struct tagMSIVIEW *view )
902 {
903     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
904     JOINTABLE *table = wv->tables;
905
906     TRACE("%p\n", wv );
907
908     while(table)
909     {
910         JOINTABLE *next;
911
912         table->view->ops->delete(table->view);
913         table->view = NULL;
914         next = table->next;
915         msi_free(table);
916         table = next;
917     }
918     wv->tables = NULL;
919     wv->table_count = 0;
920
921     free_reorder(wv);
922
923     if (wv->order_info)
924     {
925         msi_free(wv->order_info);
926         wv->order_info = NULL;
927     }
928
929     msiobj_release( &wv->db->hdr );
930     msi_free( wv );
931
932     return ERROR_SUCCESS;
933 }
934
935 static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
936     UINT val, UINT *row, MSIITERHANDLE *handle )
937 {
938     MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
939     UINT i, row_value;
940
941     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
942
943     if (!wv->tables)
944          return ERROR_FUNCTION_FAILED;
945
946     if (col == 0 || col > wv->col_count)
947         return ERROR_INVALID_PARAMETER;
948
949     for (i = PtrToUlong(*handle); i < wv->row_count; i++)
950     {
951         if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS)
952             continue;
953
954         if (row_value == val)
955         {
956             *row = i;
957             *handle = UlongToPtr(i + 1);
958             return ERROR_SUCCESS;
959         }
960     }
961
962     return ERROR_NO_MORE_ITEMS;
963 }
964
965 static UINT WHERE_sort(struct tagMSIVIEW *view, column_info *columns)
966 {
967     MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view;
968     JOINTABLE *table = wv->tables;
969     column_info *column = columns;
970     MSIORDERINFO *orderinfo;
971     UINT r, count = 0;
972     int i;
973
974     TRACE("%p %p\n", view, columns);
975
976     if (!table)
977         return ERROR_FUNCTION_FAILED;
978
979     while (column)
980     {
981         count++;
982         column = column->next;
983     }
984
985     if (count == 0)
986         return ERROR_SUCCESS;
987
988     orderinfo = msi_alloc(sizeof(MSIORDERINFO) + (count - 1) * sizeof(union ext_column));
989     if (!orderinfo)
990         return ERROR_OUTOFMEMORY;
991
992     orderinfo->col_count = count;
993
994     column = columns;
995
996     for (i = 0; i < count; i++)
997     {
998         orderinfo->columns[i].unparsed.column = column->column;
999         orderinfo->columns[i].unparsed.table = column->table;
1000
1001         r = parse_column(wv, &orderinfo->columns[i], NULL);
1002         if (r != ERROR_SUCCESS)
1003             goto error;
1004     }
1005
1006     wv->order_info = orderinfo;
1007
1008     return ERROR_SUCCESS;
1009 error:
1010     msi_free(orderinfo);
1011     return r;
1012 }
1013
1014 static const MSIVIEWOPS where_ops =
1015 {
1016     WHERE_fetch_int,
1017     WHERE_fetch_stream,
1018     WHERE_get_row,
1019     WHERE_set_row,
1020     NULL,
1021     WHERE_delete_row,
1022     WHERE_execute,
1023     WHERE_close,
1024     WHERE_get_dimensions,
1025     WHERE_get_column_info,
1026     WHERE_modify,
1027     WHERE_delete,
1028     WHERE_find_matching_rows,
1029     NULL,
1030     NULL,
1031     NULL,
1032     NULL,
1033     WHERE_sort,
1034     NULL,
1035 };
1036
1037 static UINT WHERE_VerifyCondition( MSIWHEREVIEW *wv, struct expr *cond,
1038                                    UINT *valid )
1039 {
1040     UINT r;
1041
1042     switch( cond->type )
1043     {
1044     case EXPR_COLUMN:
1045     {
1046         UINT type;
1047
1048         *valid = FALSE;
1049
1050         r = parse_column(wv, &cond->u.column, &type);
1051         if (r != ERROR_SUCCESS)
1052             break;
1053
1054         if (type&MSITYPE_STRING)
1055             cond->type = EXPR_COL_NUMBER_STRING;
1056         else if ((type&0xff) == 4)
1057             cond->type = EXPR_COL_NUMBER32;
1058         else
1059             cond->type = EXPR_COL_NUMBER;
1060
1061         *valid = TRUE;
1062         break;
1063     }
1064     case EXPR_COMPLEX:
1065         r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid );
1066         if( r != ERROR_SUCCESS )
1067             return r;
1068         if( !*valid )
1069             return ERROR_SUCCESS;
1070         r = WHERE_VerifyCondition( wv, cond->u.expr.right, valid );
1071         if( r != ERROR_SUCCESS )
1072             return r;
1073
1074         /* check the type of the comparison */
1075         if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
1076             ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
1077             ( cond->u.expr.right->type == EXPR_SVAL ) ||
1078             ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
1079         {
1080             switch( cond->u.expr.op )
1081             {
1082             case OP_EQ:
1083             case OP_NE:
1084                 break;
1085             default:
1086                 *valid = FALSE;
1087                 return ERROR_INVALID_PARAMETER;
1088             }
1089
1090             /* FIXME: check we're comparing a string to a column */
1091
1092             cond->type = EXPR_STRCMP;
1093         }
1094
1095         break;
1096     case EXPR_UNARY:
1097         if ( cond->u.expr.left->type != EXPR_COLUMN )
1098         {
1099             *valid = FALSE;
1100             return ERROR_INVALID_PARAMETER;
1101         }
1102         r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid );
1103         if( r != ERROR_SUCCESS )
1104             return r;
1105         break;
1106     case EXPR_IVAL:
1107         *valid = 1;
1108         cond->type = EXPR_UVAL;
1109         cond->u.uval = cond->u.ival;
1110         break;
1111     case EXPR_WILDCARD:
1112         *valid = 1;
1113         break;
1114     case EXPR_SVAL:
1115         *valid = 1;
1116         break;
1117     default:
1118         ERR("Invalid expression type\n");
1119         *valid = 0;
1120         break;
1121     }
1122
1123     return ERROR_SUCCESS;
1124 }
1125
1126 UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR tables,
1127                        struct expr *cond )
1128 {
1129     MSIWHEREVIEW *wv = NULL;
1130     UINT r, valid = 0;
1131     WCHAR *ptr;
1132
1133     TRACE("(%s)\n", debugstr_w(tables) );
1134
1135     wv = msi_alloc_zero( sizeof *wv );
1136     if( !wv )
1137         return ERROR_FUNCTION_FAILED;
1138     
1139     /* fill the structure */
1140     wv->view.ops = &where_ops;
1141     msiobj_addref( &db->hdr );
1142     wv->db = db;
1143     wv->cond = cond;
1144
1145     while (*tables)
1146     {
1147         JOINTABLE *table;
1148
1149         if ((ptr = strchrW(tables, ' ')))
1150             *ptr = '\0';
1151
1152         table = msi_alloc(sizeof(JOINTABLE));
1153         if (!table)
1154         {
1155             r = ERROR_OUTOFMEMORY;
1156             goto end;
1157         }
1158
1159         r = TABLE_CreateView(db, tables, &table->view);
1160         if (r != ERROR_SUCCESS)
1161         {
1162             WARN("can't create table: %s\n", debugstr_w(tables));
1163             msi_free(table);
1164             r = ERROR_BAD_QUERY_SYNTAX;
1165             goto end;
1166         }
1167
1168         r = table->view->ops->get_dimensions(table->view, NULL,
1169                                              &table->col_count);
1170         if (r != ERROR_SUCCESS)
1171         {
1172             ERR("can't get table dimensions\n");
1173             goto end;
1174         }
1175
1176         wv->col_count += table->col_count;
1177         table->table_index = wv->table_count++;
1178
1179         table->next = wv->tables;
1180         wv->tables = table;
1181
1182         if (!ptr)
1183             break;
1184
1185         tables = ptr + 1;
1186     }
1187
1188     if( cond )
1189     {
1190         r = WHERE_VerifyCondition( wv, cond, &valid );
1191         if( r != ERROR_SUCCESS )
1192             goto end;
1193         if( !valid ) {
1194             r = ERROR_FUNCTION_FAILED;
1195             goto end;
1196         }
1197     }
1198
1199     *view = (MSIVIEW*) wv;
1200
1201     return ERROR_SUCCESS;
1202 end:
1203     WHERE_delete(&wv->view);
1204
1205     return r;
1206 }