Remove redundant check.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "wine/debug.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "objbase.h"
34 #include "objidl.h"
35 #include "msipriv.h"
36 #include "winnls.h"
37
38 #include "query.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41
42 typedef struct tagMSICOLUMNINFO
43 {
44     LPWSTR tablename;
45     UINT   number;
46     LPWSTR colname;
47     UINT   type;
48     UINT   offset;
49 } MSICOLUMNINFO;
50
51 struct tagMSITABLE
52 {
53     USHORT **data;
54     UINT ref_count;
55     UINT row_count;
56     struct tagMSITABLE *next;
57     struct tagMSITABLE *prev;
58     WCHAR name[1];
59 };
60
61 #define MAX_STREAM_NAME 0x1f
62
63 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
64        MSICOLUMNINFO **pcols, UINT *pcount );
65 static UINT get_tablecolumns( MSIDATABASE *db, 
66        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
67
68 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
69 {
70     if( col->type & MSITYPE_STRING )
71         return 2;
72     if( (col->type & 0xff) > 4 )
73         ERR("Invalid column size!\n");
74     return col->type & 0xff;
75 }
76
77 static int utf2mime(int x)
78 {
79     if( (x>='0') && (x<='9') )
80         return x-'0';
81     if( (x>='A') && (x<='Z') )
82         return x-'A'+10;
83     if( (x>='a') && (x<='z') )
84         return x-'a'+10+26;
85     if( x=='.' )
86         return 10+26+26;
87     if( x=='_' )
88         return 10+26+26+1;
89     return -1;
90 }
91
92 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
93 {
94     DWORD count = MAX_STREAM_NAME;
95     DWORD ch, next;
96     LPWSTR out, p;
97
98     if( !bTable )
99         count = lstrlenW( in )+2;
100     out = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
101     p = out;
102
103     if( bTable )
104     {
105          *p++ = 0x4840;
106          count --;
107     }
108     while( count -- ) 
109     {
110         ch = *in++;
111         if( !ch )
112         {
113             *p = ch;
114             return out;
115         }
116         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
117         {
118             ch = utf2mime(ch) + 0x4800;
119             next = *in;
120             if( next && (next<0x80) )
121             {
122                 next = utf2mime(next);
123                 if( next >= 0  )
124                 {
125                      next += 0x3ffffc0;
126                      ch += (next<<6);
127                      in++;
128                 }
129             }
130         }
131         *p++ = ch;
132     }
133     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
134     HeapFree( GetProcessHeap(), 0, out );
135     return NULL;
136 }
137
138 static int mime2utf(int x)
139 {
140     if( x<10 )
141         return x + '0';
142     if( x<(10+26))
143         return x - 10 + 'A';
144     if( x<(10+26+26))
145         return x - 10 - 26 + 'a';
146     if( x == (10+26+26) )
147         return '.';
148     return '_';
149 }
150
151 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
152 {
153     WCHAR ch;
154     DWORD count = 0;
155
156     while ( (ch = *in++) )
157     {
158         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
159         {
160             if( ch >= 0x4800 )
161                 ch = mime2utf(ch-0x4800);
162             else
163             {
164                 ch -= 0x3800;
165                 *out++ = mime2utf(ch&0x3f);
166                 count++;
167                 ch = mime2utf((ch>>6)&0x3f);
168             }
169         }
170         *out++ = ch;
171         count++;
172     }
173     *out = 0;
174     return count;
175 }
176
177 void enum_stream_names( IStorage *stg )
178 {
179     IEnumSTATSTG *stgenum = NULL;
180     HRESULT r;
181     STATSTG stat;
182     ULONG n, count;
183     WCHAR name[0x40];
184
185     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
186     if( FAILED( r ) )
187         return;
188
189     n = 0;
190     while( 1 )
191     {
192         count = 0;
193         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
194         if( FAILED( r ) || !count )
195             break;
196         decode_streamname( stat.pwcsName, name );
197         TRACE("stream %2ld -> %s %s\n", n, 
198               debugstr_w(stat.pwcsName), debugstr_w(name) );
199         n++;
200     }
201
202     IEnumSTATSTG_Release( stgenum );
203 }
204
205 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
206                               USHORT **pdata, UINT *psz )
207 {
208     HRESULT r;
209     UINT ret = ERROR_FUNCTION_FAILED;
210     VOID *data;
211     ULONG sz, count;
212     IStream *stm = NULL;
213     STATSTG stat;
214     LPWSTR encname;
215
216     encname = encode_streamname(TRUE, stname);
217
218     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
219
220     r = IStorage_OpenStream(stg, encname, NULL, 
221             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
222     HeapFree( GetProcessHeap(), 0, encname );
223     if( FAILED( r ) )
224     {
225         WARN("open stream failed r = %08lx - empty table?\n",r);
226         return ret;
227     }
228
229     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
230     if( FAILED( r ) )
231     {
232         WARN("open stream failed r = %08lx!\n",r);
233         goto end;
234     }
235
236     if( stat.cbSize.QuadPart >> 32 )
237     {
238         WARN("Too big!\n");
239         goto end;
240     }
241         
242     sz = stat.cbSize.QuadPart;
243     data = HeapAlloc( GetProcessHeap(), 0, sz );
244     if( !data )
245     {
246         WARN("couldn't allocate memory r=%08lx!\n",r);
247         ret = ERROR_NOT_ENOUGH_MEMORY;
248         goto end;
249     }
250         
251     r = IStream_Read(stm, data, sz, &count );
252     if( FAILED( r ) || ( count != sz ) )
253     {
254         HeapFree( GetProcessHeap(), 0, data );
255         WARN("read stream failed r = %08lx!\n",r);
256         goto end;
257     }
258
259     *pdata = data;
260     *psz = sz;
261     ret = ERROR_SUCCESS;
262
263 end:
264     IStream_Release( stm );
265
266     return ret;
267 }
268
269 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
270 {
271     LPWSTR encname;
272     HRESULT r;
273
274     encname = encode_streamname(FALSE, stname);
275
276     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
277
278     r = IStorage_OpenStream(db->storage, encname, NULL, 
279             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
280     HeapFree( GetProcessHeap(), 0, encname );
281     if( FAILED( r ) )
282     {
283         WARN("open stream failed r = %08lx - empty table?\n",r);
284         return ERROR_FUNCTION_FAILED;
285     }
286
287     return ERROR_SUCCESS;
288 }
289
290 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
291                               USHORT **pdata, UINT *psz )
292 {
293     HRESULT r;
294     UINT ret = ERROR_FUNCTION_FAILED;
295     VOID *data;
296     ULONG sz, count;
297     IStream *stm = NULL;
298     STATSTG stat;
299
300     r = db_get_raw_stream( db, stname, &stm );
301     if( r != ERROR_SUCCESS)
302         return ret;
303     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
304     if( FAILED( r ) )
305     {
306         WARN("open stream failed r = %08lx!\n",r);
307         goto end;
308     }
309
310     if( stat.cbSize.QuadPart >> 32 )
311     {
312         WARN("Too big!\n");
313         goto end;
314     }
315         
316     sz = stat.cbSize.QuadPart;
317     data = HeapAlloc( GetProcessHeap(), 0, sz );
318     if( !data )
319     {
320         WARN("couldn't allocate memory r=%08lx!\n",r);
321         ret = ERROR_NOT_ENOUGH_MEMORY;
322         goto end;
323     }
324         
325     r = IStream_Read(stm, data, sz, &count );
326     if( FAILED( r ) || ( count != sz ) )
327     {
328         HeapFree( GetProcessHeap(), 0, data );
329         WARN("read stream failed r = %08lx!\n",r);
330         goto end;
331     }
332
333     *pdata = data;
334     *psz = sz;
335     ret = ERROR_SUCCESS;
336
337 end:
338     IStream_Release( stm );
339
340     return ret;
341 }
342
343 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
344                                LPVOID data, UINT sz )
345 {
346     HRESULT r;
347     UINT ret = ERROR_FUNCTION_FAILED;
348     ULONG count;
349     IStream *stm = NULL;
350     ULARGE_INTEGER size;
351     LARGE_INTEGER pos;
352     LPWSTR encname;
353
354     encname = encode_streamname(TRUE, stname );
355     r = IStorage_OpenStream( stg, encname, NULL, 
356             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
357     if( FAILED(r) )
358     {
359         r = IStorage_CreateStream( stg, encname,
360                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
361     }
362     HeapFree( GetProcessHeap(), 0, encname );
363     if( FAILED( r ) )
364     {
365         WARN("open stream failed r = %08lx\n",r);
366         return ret;
367     }
368
369     size.QuadPart = sz;
370     r = IStream_SetSize( stm, size );
371     if( FAILED( r ) )
372     {
373         WARN("Failed to SetSize\n");
374         goto end;
375     }
376
377     pos.QuadPart = 0;
378     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
379     if( FAILED( r ) )
380     {
381         WARN("Failed to Seek\n");
382         goto end;
383     }
384
385     r = IStream_Write(stm, data, sz, &count );
386     if( FAILED( r ) || ( count != sz ) )
387     {
388         WARN("Failed to Write\n");
389         goto end;
390     }
391
392     ret = ERROR_SUCCESS;
393
394 end:
395     IStream_Release( stm );
396
397     return ret;
398 }
399
400 static UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
401 {
402     MSITABLE *t;
403     USHORT *rawdata = NULL;
404     UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
405     MSICOLUMNINFO *cols, *last_col;
406
407     TRACE("%s\n",debugstr_w(name));
408
409     /* nonexistent tables should be interpreted as empty tables */
410     t = HeapAlloc( GetProcessHeap(), 0, 
411                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
412     if( !t )
413         return ERROR_NOT_ENOUGH_MEMORY;
414
415     r = table_get_column_info( db, name, &cols, &num_cols );
416     if( r != ERROR_SUCCESS )
417     {
418         HeapFree( GetProcessHeap(), 0, t );
419         return r;
420     }
421     last_col = &cols[num_cols-1];
422     row_size = last_col->offset + bytes_per_column( last_col );
423
424     t->row_count = 0;
425     t->data = NULL;
426     lstrcpyW( t->name, name );
427     t->ref_count = 1;
428     *ptable = t;
429
430     /* if we can't read the table, just assume that it's empty */
431     read_stream_data( db->storage, name, &rawdata, &rawsize );
432     if( !rawdata )
433         return ERROR_SUCCESS;
434
435     TRACE("Read %d bytes\n", rawsize );
436
437     if( rawsize % row_size )
438     {
439         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
440         return ERROR_FUNCTION_FAILED;
441     }
442
443     t->row_count = rawsize / row_size;
444     t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
445                          t->row_count * sizeof (USHORT*) );
446     if( !t->data )
447         return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
448
449     /* transpose all the data */
450     TRACE("Transposing data from %d columns\n", t->row_count );
451     for( i=0; i<t->row_count; i++ )
452     {
453         t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
454         if( !t->data[i] )
455             return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
456         for( j=0; j<num_cols; j++ )
457         {
458             UINT ofs = cols[j].offset/2;
459             UINT n = bytes_per_column( &cols[j] );
460
461             switch( n )
462             {
463             case 2:
464                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
465                 break;
466             case 4:
467                 t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
468                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
469                 break;
470             default:
471                 ERR("oops - unknown column width %d\n", n);
472                 return ERROR_FUNCTION_FAILED;
473             }
474         }
475     }
476
477     HeapFree( GetProcessHeap(), 0, cols );
478     HeapFree( GetProcessHeap(), 0, rawdata );
479
480     return ERROR_SUCCESS;
481 }
482
483 /* add this table to the list of cached tables in the database */
484 void add_table(MSIDATABASE *db, MSITABLE *table)
485 {
486     table->next = db->first_table;
487     table->prev = NULL;
488     if( db->first_table )
489         db->first_table->prev = table;
490     else
491         db->last_table = table;
492     db->first_table = table;
493 }
494  
495 /* remove from the list of cached tables */
496 void remove_table( MSIDATABASE *db, MSITABLE *table )
497 {
498     if( table->next )
499         table->next->prev = table->prev;
500     else
501         db->last_table = table->prev;
502     if( table->prev )
503         table->prev->next = table->next;
504     else
505         db->first_table = table->next;
506     table->next = NULL;
507     table->prev = NULL;
508 }
509
510 static void release_table( MSIDATABASE *db, MSITABLE *table )
511 {
512     if( !table->ref_count )
513         ERR("Trying to destroy table with refcount 0\n");
514     table->ref_count --;
515     if( !table->ref_count )
516     {
517         remove_table( db, table );
518         HeapFree( GetProcessHeap(), 0, table->data );
519         HeapFree( GetProcessHeap(), 0, table );
520         TRACE("Destroyed table %s\n", debugstr_w(table->name));
521     }
522 }
523
524 void free_cached_tables( MSIDATABASE *db )
525 {
526     while( db->first_table )
527     {
528         MSITABLE *t = db->first_table;
529
530         if ( --t->ref_count )
531             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
532         remove_table( db, t );
533         HeapFree( GetProcessHeap(), 0, t->data );
534         HeapFree( GetProcessHeap(), 0, t );
535     }
536 }
537
538 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
539 {
540     MSITABLE *t;
541
542     for( t = db->first_table; t; t=t->next )
543     {
544         if( !lstrcmpW( name, t->name ) )
545         {
546             *ptable = t;
547             return ERROR_SUCCESS;
548         }
549     }
550
551     return ERROR_FUNCTION_FAILED;
552 }
553
554 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
555 {
556     UINT r, column_count;
557     MSICOLUMNINFO *columns;
558
559     /* get the number of columns in this table */
560     column_count = 0;
561     r = get_tablecolumns( db, name, NULL, &column_count );
562     if( r != ERROR_SUCCESS )
563         return r;
564
565     /* if there's no columns, there's no table */
566     if( column_count == 0 )
567         return ERROR_INVALID_PARAMETER;
568
569     TRACE("Table %s found\n", debugstr_w(name) );
570
571     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
572     if( !columns )
573         return ERROR_FUNCTION_FAILED;
574
575     r = get_tablecolumns( db, name, columns, &column_count );
576     if( r != ERROR_SUCCESS )
577     {
578         HeapFree( GetProcessHeap(), 0, columns );
579         return ERROR_FUNCTION_FAILED;
580     }
581
582     *pcols = columns;
583     *pcount = column_count;
584
585     return r;
586 }
587
588 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
589 {
590     UINT r;
591
592     *ptable = NULL;
593
594     /* first, see if the table is cached */
595     r = find_cached_table( db, name, ptable );
596     if( r == ERROR_SUCCESS )
597     {
598         (*ptable)->ref_count++;
599         return r;
600     }
601
602     r = read_table_from_storage( db, name, ptable );
603     if( r != ERROR_SUCCESS )
604         return r;
605
606     /* add the table to the list */
607     add_table( db, *ptable );
608     (*ptable)->ref_count++;
609
610     return ERROR_SUCCESS;
611 }
612
613 static UINT save_table( MSIDATABASE *db, MSITABLE *t )
614 {
615     USHORT *rawdata = NULL, *p;
616     UINT rawsize, r, i, j, row_size, num_cols = 0;
617     MSICOLUMNINFO *cols, *last_col;
618
619     TRACE("Saving %s\n", debugstr_w( t->name ) );
620
621     r = table_get_column_info( db, t->name, &cols, &num_cols );
622     if( r != ERROR_SUCCESS )
623         return r;
624     
625     last_col = &cols[num_cols-1];
626     row_size = last_col->offset + bytes_per_column( last_col );
627
628     rawsize = t->row_count * row_size;
629     rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
630     if( !rawdata )
631         return ERROR_NOT_ENOUGH_MEMORY;
632
633     p = rawdata;
634     for( i=0; i<num_cols; i++ )
635     {
636         for( j=0; j<t->row_count; j++ )
637         {
638             UINT offset = cols[i].offset;
639
640             *p++ = t->data[j][offset/2];
641             if( 4 == bytes_per_column( &cols[i] ) )
642                 *p++ = t->data[j][offset/2+1];
643         }
644     }
645
646     TRACE("writing %d bytes\n", rawsize);
647     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
648
649     HeapFree( GetProcessHeap(), 0, rawdata );
650
651     return r;
652 }
653
654 HRESULT init_string_table( IStorage *stg )
655 {
656     HRESULT r;
657     static const WCHAR szStringData[] = {
658         '_','S','t','r','i','n','g','D','a','t','a',0 };
659     static const WCHAR szStringPool[] = {
660         '_','S','t','r','i','n','g','P','o','o','l',0 };
661     USHORT zero[2] = { 0, 0 };
662     ULONG count = 0;
663     IStream *stm = NULL;
664     LPWSTR encname;
665
666     encname = encode_streamname(TRUE, szStringPool );
667
668     /* create the StringPool stream... add the zero string to it*/
669     r = IStorage_CreateStream( stg, encname,
670             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
671     HeapFree( GetProcessHeap(), 0, encname );
672     if( r ) 
673     {
674         TRACE("Failed\n");
675         return r;
676     }
677
678     r = IStream_Write(stm, zero, sizeof zero, &count );
679     IStream_Release( stm );
680
681     if( FAILED( r ) || ( count != sizeof zero ) )
682     {
683         TRACE("Failed\n");
684         return E_FAIL;
685     }
686
687     /* create the StringData stream... make it zero length */
688     encname = encode_streamname(TRUE, szStringData );
689     r = IStorage_CreateStream( stg, encname,
690             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
691     HeapFree( GetProcessHeap(), 0, encname );
692     if( r ) 
693     {
694         TRACE("Failed\n");
695         return E_FAIL;
696     }
697     IStream_Release( stm );
698
699     return r;
700 }
701
702 UINT load_string_table( MSIDATABASE *db )
703 {
704     CHAR *data;
705     USHORT *pool;
706     UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
707     DWORD i, count, offset, len, n;
708     static const WCHAR szStringData[] = {
709         '_','S','t','r','i','n','g','D','a','t','a',0 };
710     static const WCHAR szStringPool[] = {
711         '_','S','t','r','i','n','g','P','o','o','l',0 };
712
713     if( db->strings )
714     {
715         msi_destroy_stringtable( db->strings );
716         db->strings = NULL;
717     }
718
719     r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
720     if( r != ERROR_SUCCESS)
721         goto end;
722     r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
723     if( r != ERROR_SUCCESS)
724         goto end;
725
726     count = poolsize/4;
727     if( poolsize > 4 )
728         codepage = pool[0] | ( pool[1] << 16 );
729     else
730         codepage = CP_ACP;
731     db->strings = msi_init_stringtable( count, codepage );
732
733     offset = 0;
734     n = 1;
735     for( i=1; i<count; i++ )
736     {
737         len = pool[i*2];
738
739         /*
740          * If a string is over 64k, the previous string entry is made null
741          * and its the high word of the length is inserted in the null string's
742          * reference count field.
743          */
744         if( pool[i*2-2] == 0 )
745             len += pool[i*2-1] * 0x10000;
746
747         if( (offset + len) > datasize )
748         {
749             ERR("string table corrupt?\n");
750             break;
751         }
752
753         /* don't add the high word of a string's length as a string */
754         if ( len || !pool[i*2+1] )
755         {
756             r = msi_addstring( db->strings, n, data+offset, len, pool[i*2+1] );
757             if( r != n )
758                 ERR("Failed to add string %ld\n", n );
759             n++;
760         }
761
762         offset += len;
763     }
764
765     if ( datasize != offset )
766         ERR("string table load failed! (%08x != %08lx)\n", datasize, offset );
767
768     TRACE("Loaded %ld strings\n", count);
769
770     ret = ERROR_SUCCESS;
771
772 end:
773     HeapFree( GetProcessHeap(), 0, pool );
774     HeapFree( GetProcessHeap(), 0, data );
775
776     return ret;
777 }
778
779 static UINT save_string_table( MSIDATABASE *db )
780 {
781     UINT i, count, datasize, poolsize, sz, used, r, codepage;
782     UINT ret = ERROR_FUNCTION_FAILED;
783     static const WCHAR szStringData[] = {
784         '_','S','t','r','i','n','g','D','a','t','a',0 };
785     static const WCHAR szStringPool[] = {
786         '_','S','t','r','i','n','g','P','o','o','l',0 };
787     CHAR *data = NULL;
788     USHORT *pool = NULL;
789
790     TRACE("\n");
791
792     /* construct the new table in memory first */
793     datasize = msi_string_totalsize( db->strings, &count );
794     poolsize = count*2*sizeof(USHORT);
795
796     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
797     if( ! pool )
798     {
799         WARN("Failed to alloc pool %d bytes\n", poolsize );
800         goto err;
801     }
802     data = HeapAlloc( GetProcessHeap(), 0, datasize );
803     if( ! data )
804     {
805         WARN("Failed to alloc data %d bytes\n", poolsize );
806         goto err;
807     }
808
809     used = 0;
810     codepage = msi_string_get_codepage( db->strings );
811     pool[0]=codepage&0xffff;
812     pool[1]=(codepage>>16);
813     for( i=1; i<count; i++ )
814     {
815         sz = datasize - used;
816         r = msi_id2stringA( db->strings, i, data+used, &sz );
817         if( r != ERROR_SUCCESS )
818         {
819             ERR("failed to fetch string\n");
820             sz = 0;
821         }
822         if( sz && (sz < (datasize - used ) ) )
823             sz--;
824         TRACE("adding %u bytes %s\n", sz, data+used );
825         pool[ i*2 ] = sz;
826         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
827         used += sz;
828         if( used > datasize  )
829         {
830             ERR("oops overran %d >= %d\n", used, datasize);
831             goto err;
832         }
833     }
834
835     if( used != datasize )
836     {
837         ERR("oops used %d != datasize %d\n", used, datasize);
838         goto err;
839     }
840
841     /* write the streams */
842     r = write_stream_data( db->storage, szStringData, data, datasize );
843     TRACE("Wrote StringData r=%08x\n", r);
844     if( r )
845         goto err;
846     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
847     TRACE("Wrote StringPool r=%08x\n", r);
848     if( r )
849         goto err;
850
851     ret = ERROR_SUCCESS;
852
853 err:
854     HeapFree( GetProcessHeap(), 0, data );
855     HeapFree( GetProcessHeap(), 0, pool );
856
857     return ret;
858 }
859
860 /* information for default tables */
861 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
862 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
863 static const WCHAR szName[]    = { 'N','a','m','e',0 };
864 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
865 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
866 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
867 static const WCHAR szType[]    = { 'T','y','p','e',0 };
868
869 struct standard_table {
870     LPCWSTR tablename;
871     LPCWSTR columnname;
872     UINT number;
873     UINT type;
874 } MSI_standard_tables[] =
875 {
876   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
877   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
878   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
879   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
880   { szColumns, szType,   4, MSITYPE_VALID | 2},
881 };
882
883 #define STANDARD_TABLE_COUNT \
884      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
885
886 static UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
887 {
888     DWORD i, n=0;
889
890     for(i=0; i<STANDARD_TABLE_COUNT; i++)
891     {
892         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
893             continue;
894         if(colinfo && (n < *sz) )
895         {
896             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
897             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
898             colinfo[n].number = MSI_standard_tables[i].number;
899             colinfo[n].type = MSI_standard_tables[i].type;
900             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
901                     debugstr_w(colinfo[n].colname)); */
902             if( n )
903                 colinfo[n].offset = colinfo[n-1].offset
904                                   + bytes_per_column( &colinfo[n-1] );
905             else
906                 colinfo[n].offset = 0;
907         }
908         n++;
909         if( colinfo && (n >= *sz) )
910             break;
911     }
912     *sz = n;
913     return ERROR_SUCCESS;
914 }
915
916 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
917 {
918     UINT sz=0, r;
919     LPWSTR str;
920
921     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
922     if( r != ERROR_SUCCESS )
923         return NULL;
924     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
925     if( !str )
926         return str;
927     r = msi_id2stringW( db->strings, stringid, str, &sz );
928     if( r == ERROR_SUCCESS )
929         return str;
930     HeapFree(  GetProcessHeap(), 0, str );
931     return NULL;
932 }
933
934 static UINT get_tablecolumns( MSIDATABASE *db, 
935        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
936 {
937     UINT r, i, n=0, table_id, count, maxcount = *sz;
938     MSITABLE *table = NULL;
939     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
940
941     /* first check if there is a default table with that name */
942     r = get_defaulttablecolumns( szTableName, colinfo, sz );
943     if( ( r == ERROR_SUCCESS ) && *sz )
944         return r;
945
946     r = get_table( db, szColumns, &table);
947     if( r != ERROR_SUCCESS )
948     {
949         WARN("table %s not available\n", debugstr_w(szColumns));
950         return r;
951     }
952
953     /* convert table and column names to IDs from the string table */
954     r = msi_string2idW( db->strings, szTableName, &table_id );
955     if( r != ERROR_SUCCESS )
956     {
957         release_table( db, table );
958         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
959         return r;
960     }
961
962     TRACE("Table id is %d\n", table_id);
963
964     count = table->row_count;
965     for( i=0; i<count; i++ )
966     {
967         if( table->data[ i ][ 0 ] != table_id )
968             continue;
969         if( colinfo )
970         {
971             UINT id = table->data[ i ] [ 2 ];
972             colinfo[n].tablename = MSI_makestring( db, table_id );
973             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
974             colinfo[n].colname = MSI_makestring( db, id );
975             colinfo[n].type = table->data[ i ] [ 3 ];
976             /* this assumes that columns are in order in the table */
977             if( n )
978                 colinfo[n].offset = colinfo[n-1].offset
979                                   + bytes_per_column( &colinfo[n-1] );
980             else
981                 colinfo[n].offset = 0;
982             TRACE("table %s column %d is [%s] (%d) with type %08x "
983                   "offset %d at row %d\n", debugstr_w(szTableName),
984                    colinfo[n].number, debugstr_w(colinfo[n].colname),
985                    id, colinfo[n].type, colinfo[n].offset, i);
986             if( n != (colinfo[n].number-1) )
987             {
988                 ERR("oops. data in the _Columns table isn't in the right "
989                     "order for table %s\n", debugstr_w(szTableName));
990                 return ERROR_FUNCTION_FAILED;
991             }
992         }
993         n++;
994         if( colinfo && ( n >= maxcount ) )
995             break;
996     }
997     *sz = n;
998
999     release_table( db, table );
1000
1001     return ERROR_SUCCESS;
1002 }
1003
1004 /* try to find the table name in the _Tables table */
1005 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
1006 {
1007     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
1008     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
1009     UINT r, table_id = 0, i, count;
1010     MSITABLE *table = NULL;
1011
1012     if( !lstrcmpW( name, szTables ) )
1013         return TRUE;
1014     if( !lstrcmpW( name, szColumns ) )
1015         return TRUE;
1016
1017     r = msi_string2idW( db->strings, name, &table_id );
1018     if( r != ERROR_SUCCESS )
1019     {
1020         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1021         return FALSE;
1022     }
1023
1024     r = get_table( db, szTables, &table);
1025     if( r != ERROR_SUCCESS )
1026     {
1027         TRACE("table %s not available\n", debugstr_w(szTables));
1028         return FALSE;
1029     }
1030
1031     /* count = table->size/2; */
1032     count = table->row_count;
1033     for( i=0; i<count; i++ )
1034         if( table->data[ i ][ 0 ] == table_id )
1035             break;
1036
1037     release_table( db, table );
1038
1039     if (i!=count)
1040         return TRUE;
1041
1042     TRACE("Searched %d tables, but %d was not found\n", count, table_id );
1043
1044     return FALSE;
1045 }
1046
1047 /* below is the query interface to a table */
1048
1049 typedef struct tagMSITABLEVIEW
1050 {
1051     MSIVIEW        view;
1052     MSIDATABASE   *db;
1053     MSITABLE      *table;
1054     MSICOLUMNINFO *columns;
1055     UINT           num_cols;
1056     UINT           row_size;
1057     WCHAR          name[1];
1058 } MSITABLEVIEW;
1059
1060 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1061 {
1062     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1063     UINT offset, num_rows, n;
1064
1065     if( !tv->table )
1066         return ERROR_INVALID_PARAMETER;
1067
1068     if( (col==0) || (col>tv->num_cols) )
1069         return ERROR_INVALID_PARAMETER;
1070
1071     /* how many rows are there ? */
1072     num_rows = tv->table->row_count;
1073     if( row >= num_rows )
1074         return ERROR_NO_MORE_ITEMS;
1075
1076     if( tv->columns[col-1].offset >= tv->row_size )
1077     {
1078         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1079         ERR("%p %p\n", tv, tv->columns );
1080         return ERROR_FUNCTION_FAILED;
1081     }
1082
1083     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1084     n = bytes_per_column( &tv->columns[col-1] );
1085     switch( n )
1086     {
1087     case 4:
1088         offset = tv->columns[col-1].offset/2;
1089         *val = tv->table->data[row][offset] + 
1090                (tv->table->data[row][offset + 1] << 16);
1091         break;
1092     case 2:
1093         offset = tv->columns[col-1].offset/2;
1094         *val = tv->table->data[row][offset];
1095         break;
1096     default:
1097         ERR("oops! what is %d bytes per column?\n", n );
1098         return ERROR_FUNCTION_FAILED;
1099     }
1100
1101     /* TRACE("Data [%d][%d] = %d \n", row, col, *val ); */
1102
1103     return ERROR_SUCCESS;
1104 }
1105
1106 /*
1107  * We need a special case for streams, as we need to reference column with
1108  * the name of the stream in the same table, and the table name
1109  * which may not be available at higher levels of the query
1110  */
1111 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1112 {
1113     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1114     UINT ival = 0, refcol = 0, r;
1115     LPWSTR sval;
1116     LPWSTR full_name;
1117     DWORD len;
1118     static const WCHAR szDot[] = { '.', 0 };
1119
1120     if( !view->ops->fetch_int )
1121         return ERROR_INVALID_PARAMETER;
1122
1123     /*
1124      * The column marked with the type stream data seems to have a single number
1125      * which references the column containing the name of the stream data
1126      *
1127      * Fetch the column to reference first.
1128      */
1129     r = view->ops->fetch_int( view, row, col, &ival );
1130     if( r != ERROR_SUCCESS )
1131         return r;
1132
1133     /* now get the column with the name of the stream */
1134     r = view->ops->fetch_int( view, row, ival, &refcol );
1135     if( r != ERROR_SUCCESS )
1136         return r;
1137
1138     /* lookup the string value from the string table */
1139     sval = MSI_makestring( tv->db, refcol );
1140     if( !sval )
1141         return ERROR_INVALID_PARAMETER;
1142
1143     len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
1144     full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1145     lstrcpyW( full_name, tv->name );
1146     lstrcatW( full_name, szDot );
1147     lstrcatW( full_name, sval );
1148
1149     r = db_get_raw_stream( tv->db, full_name, stm );
1150     if( r )
1151         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1152     HeapFree( GetProcessHeap(), 0, full_name );
1153     HeapFree( GetProcessHeap(), 0, sval );
1154
1155     return r;
1156 }
1157
1158 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1159 {
1160     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1161     UINT offset, n;
1162
1163     if( !tv->table )
1164         return ERROR_INVALID_PARAMETER;
1165
1166     if( (col==0) || (col>tv->num_cols) )
1167         return ERROR_INVALID_PARAMETER;
1168
1169     if( tv->columns[col-1].offset >= tv->row_size )
1170     {
1171         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1172         ERR("%p %p\n", tv, tv->columns );
1173         return ERROR_FUNCTION_FAILED;
1174     }
1175
1176     n = bytes_per_column( &tv->columns[col-1] );
1177     switch( n )
1178     {
1179     case 4:
1180         offset = tv->columns[col-1].offset/2;
1181         tv->table->data[row][offset]     = val & 0xffff;
1182         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1183         break;
1184     case 2:
1185         offset = tv->columns[col-1].offset/2;
1186         tv->table->data[row][offset] = val;
1187         break;
1188     default:
1189         ERR("oops! what is %d bytes per column?\n", n );
1190         return ERROR_FUNCTION_FAILED;
1191     }
1192     return ERROR_SUCCESS;
1193 }
1194
1195 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num )
1196 {
1197     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1198     USHORT **p, *row;
1199     UINT sz;
1200
1201     TRACE("%p\n", view);
1202
1203     if( !tv->table )
1204         return ERROR_INVALID_PARAMETER;
1205
1206     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1207     if( !row )
1208         return ERROR_NOT_ENOUGH_MEMORY;
1209
1210     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1211     if( tv->table->data )
1212         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1213     else
1214         p = HeapAlloc( GetProcessHeap(), 0, sz );
1215     if( !p )
1216     {
1217         HeapFree( GetProcessHeap(), 0, row );
1218         return ERROR_NOT_ENOUGH_MEMORY;
1219     }
1220
1221     tv->table->data = p;
1222     tv->table->data[tv->table->row_count] = row;
1223     *num = tv->table->row_count;
1224     tv->table->row_count++;
1225
1226     return ERROR_SUCCESS;
1227 }
1228
1229 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1230 {
1231     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1232     UINT r;
1233
1234     TRACE("%p %p\n", tv, record);
1235
1236     if( tv->table )
1237     {
1238         release_table( tv->db, tv->table );
1239         tv->table = NULL;
1240     }
1241
1242     r = get_table( tv->db, tv->name, &tv->table );
1243     if( r != ERROR_SUCCESS )
1244         return r;
1245
1246     return ERROR_SUCCESS;
1247 }
1248
1249 static UINT TABLE_close( struct tagMSIVIEW *view )
1250 {
1251     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1252
1253     TRACE("%p\n", view );
1254
1255     if( !tv->table )
1256         return ERROR_FUNCTION_FAILED;
1257
1258     release_table( tv->db, tv->table );
1259     tv->table = NULL;
1260     
1261     return ERROR_SUCCESS;
1262 }
1263
1264 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1265 {
1266     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1267
1268     TRACE("%p %p %p\n", view, rows, cols );
1269
1270     if( cols )
1271         *cols = tv->num_cols;
1272     if( rows )
1273     {
1274         if( !tv->table )
1275             return ERROR_INVALID_PARAMETER;
1276         *rows = tv->table->row_count;
1277     }
1278
1279     return ERROR_SUCCESS;
1280 }
1281
1282 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1283                 UINT n, LPWSTR *name, UINT *type )
1284 {
1285     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1286
1287     TRACE("%p %d %p %p\n", tv, n, name, type );
1288
1289     if( ( n == 0 ) || ( n > tv->num_cols ) )
1290         return ERROR_INVALID_PARAMETER;
1291
1292     if( name )
1293     {
1294         *name = strdupW( tv->columns[n-1].colname );
1295         if( !*name )
1296             return ERROR_FUNCTION_FAILED;
1297     }
1298     if( type )
1299         *type = tv->columns[n-1].type;
1300
1301     return ERROR_SUCCESS;
1302 }
1303
1304 static UINT table_find_in_column( MSITABLEVIEW *tv, UINT col, UINT val, UINT *row )
1305 {
1306     UINT i, r, x;
1307
1308     for( i=0; i<tv->table->row_count; i++ )
1309     {
1310         r = TABLE_fetch_int( (struct tagMSIVIEW*) tv, i, col, &x );
1311         if ( r != ERROR_SUCCESS )
1312         {
1313             ERR("TABLE_fetch_int shouldn't fail here\n");
1314             break;
1315         }
1316         if ( x == val )
1317         {
1318             *row = i;
1319             return ERROR_SUCCESS;
1320         }
1321     }
1322     return ERROR_FUNCTION_FAILED;
1323 }
1324
1325 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
1326 {
1327     LPCWSTR str;
1328     UINT i, val, r, row;
1329     BOOL has_key = FALSE;
1330
1331     /* FIXME: set the MsiViewGetError value */
1332
1333     for( i = 0; i<tv->num_cols; i++ )
1334     {
1335         /* check for duplicate keys */
1336         if( !( tv->columns[i].type & MSITYPE_KEY ) )
1337             continue;
1338
1339         has_key = TRUE;
1340
1341         TRACE("column %d (%s.%s)is a key\n", i,
1342              debugstr_w(tv->columns[i].tablename),
1343              debugstr_w(tv->columns[i].colname) );
1344
1345         val = 0;
1346         if( tv->columns[i].type & MSITYPE_STRING )
1347         {
1348              /* keys can't be null */
1349              str = MSI_RecordGetString( rec, i+1 );
1350              if( !str )
1351                  return ERROR_INVALID_DATA;
1352
1353              /* if the string doesn't exist in the string table yet, it's OK */
1354              r = msi_string2idW( tv->db->strings, str, &val );
1355              if( ERROR_SUCCESS != r )
1356                  return ERROR_SUCCESS;
1357         }
1358         else
1359         {
1360             val = MSI_RecordGetInteger( rec, i+1 );
1361             val ^= 0x8000;
1362         }
1363
1364         /* if we find the same value in the table, it's a duplicate */
1365         row = 0;
1366         r = table_find_in_column( tv, i+1, val, &row );
1367         if( ERROR_SUCCESS != r )
1368             return ERROR_SUCCESS;
1369
1370         TRACE("found in row %d\n", row );
1371     }
1372
1373     if (has_key)
1374         return ERROR_INVALID_DATA;
1375
1376     return ERROR_SUCCESS;
1377 }
1378
1379 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
1380 {
1381     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1382     UINT n, type, val, r, row, col_count = 0;
1383
1384     r = TABLE_get_dimensions( view, NULL, &col_count );
1385     if( r )
1386         return r;
1387
1388     row = -1;
1389     r = table_create_new_row( view, &row );
1390     TRACE("insert_row returned %08x\n", r);
1391     if( r )
1392         return r;
1393
1394     for( n = 1; n <= col_count; n++ )
1395     {
1396         r = TABLE_get_column_info( view, n, NULL, &type );
1397         if( r )
1398             break;
1399
1400         if( type & MSITYPE_STRING )
1401         {
1402             const WCHAR *str = MSI_RecordGetString( rec, n );
1403             val = msi_addstringW( tv->db->strings, 0, str, -1, 1 );
1404         }
1405         else
1406         {
1407             val = MSI_RecordGetInteger( rec, n );
1408             val ^= 0x8000;
1409         }
1410         r = TABLE_set_int( view, row, n, val );
1411         if( r )
1412             break;
1413     }
1414
1415     return r;
1416 }
1417
1418 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1419                 MSIRECORD *rec)
1420 {
1421     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1422     UINT r;
1423
1424     TRACE("%p %d %p\n", view, eModifyMode, rec );
1425
1426     if (!tv->table)
1427     {
1428         r = TABLE_execute( view, NULL );
1429         if( r )
1430             return r;
1431     }
1432
1433     switch (eModifyMode)
1434     {
1435     case MSIMODIFY_VALIDATE_NEW:
1436         r = table_validate_new( tv, rec );
1437         break;
1438
1439     case MSIMODIFY_INSERT_TEMPORARY:
1440         r = table_validate_new( tv, rec );
1441         if (r != ERROR_SUCCESS)
1442             break;
1443         r = TABLE_insert_row( view, rec );
1444         break;
1445
1446     case MSIMODIFY_REFRESH:
1447     case MSIMODIFY_INSERT:
1448     case MSIMODIFY_UPDATE:
1449     case MSIMODIFY_ASSIGN:
1450     case MSIMODIFY_REPLACE:
1451     case MSIMODIFY_MERGE:
1452     case MSIMODIFY_DELETE:
1453     case MSIMODIFY_VALIDATE:
1454     case MSIMODIFY_VALIDATE_FIELD:
1455     case MSIMODIFY_VALIDATE_DELETE:
1456         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1457         r = ERROR_CALL_NOT_IMPLEMENTED;
1458         break;
1459
1460     default:
1461         r = ERROR_INVALID_DATA;
1462     }
1463
1464     return r;
1465 }
1466
1467 static UINT TABLE_delete( struct tagMSIVIEW *view )
1468 {
1469     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1470
1471     TRACE("%p\n", view );
1472
1473     if( tv->table )
1474         release_table( tv->db, tv->table );
1475     tv->table = NULL;
1476
1477     if( tv->columns )
1478     {
1479         UINT i;
1480         for( i=0; i<tv->num_cols; i++)
1481         {
1482             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1483             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1484         }
1485         HeapFree( GetProcessHeap(), 0, tv->columns );
1486     }
1487     tv->columns = NULL;
1488
1489     HeapFree( GetProcessHeap(), 0, tv );
1490
1491     return ERROR_SUCCESS;
1492 }
1493
1494
1495 MSIVIEWOPS table_ops =
1496 {
1497     TABLE_fetch_int,
1498     TABLE_fetch_stream,
1499     TABLE_set_int,
1500     TABLE_insert_row,
1501     TABLE_execute,
1502     TABLE_close,
1503     TABLE_get_dimensions,
1504     TABLE_get_column_info,
1505     TABLE_modify,
1506     TABLE_delete
1507 };
1508
1509 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1510 {
1511     MSITABLEVIEW *tv ;
1512     UINT r, sz, column_count;
1513     MSICOLUMNINFO *columns, *last_col;
1514
1515     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1516
1517     /* get the number of columns in this table */
1518     column_count = 0;
1519     r = get_tablecolumns( db, name, NULL, &column_count );
1520     if( r != ERROR_SUCCESS )
1521         return r;
1522
1523     /* if there's no columns, there's no table */
1524     if( column_count == 0 )
1525         return ERROR_INVALID_PARAMETER;
1526
1527     TRACE("Table found\n");
1528
1529     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1530     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1531     if( !tv )
1532         return ERROR_FUNCTION_FAILED;
1533     
1534     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1535     if( !columns )
1536     {
1537         HeapFree( GetProcessHeap(), 0, tv );
1538         return ERROR_FUNCTION_FAILED;
1539     }
1540
1541     r = get_tablecolumns( db, name, columns, &column_count );
1542     if( r != ERROR_SUCCESS )
1543     {
1544         HeapFree( GetProcessHeap(), 0, columns );
1545         HeapFree( GetProcessHeap(), 0, tv );
1546         return ERROR_FUNCTION_FAILED;
1547     }
1548
1549     TRACE("Table has %d columns\n", column_count);
1550
1551     last_col = &columns[column_count-1];
1552
1553     /* fill the structure */
1554     tv->view.ops = &table_ops;
1555     tv->db = db;
1556     tv->columns = columns;
1557     tv->num_cols = column_count;
1558     tv->table = NULL;
1559     tv->row_size = last_col->offset + bytes_per_column( last_col );
1560
1561     TRACE("one row is %d bytes\n", tv->row_size );
1562
1563     *view = (MSIVIEW*) tv;
1564     lstrcpyW( tv->name, name );
1565
1566     return ERROR_SUCCESS;
1567 }
1568
1569 UINT MSI_CommitTables( MSIDATABASE *db )
1570 {
1571     UINT r;
1572     MSITABLE *table = NULL;
1573
1574     TRACE("%p\n",db);
1575
1576     r = save_string_table( db );
1577     if( r != ERROR_SUCCESS )
1578     {
1579         WARN("failed to save string table r=%08x\n",r);
1580         return r;
1581     }
1582
1583     for( table = db->first_table; table; table = table->next )
1584     {
1585         r = save_table( db, table );
1586         if( r != ERROR_SUCCESS )
1587         {
1588             WARN("failed to save table %s (r=%08x)\n",
1589                   debugstr_w(table->name), r);
1590             return r;
1591         }
1592     }
1593
1594     /* force everything to reload next time */
1595     free_cached_tables( db );
1596
1597     return ERROR_SUCCESS;
1598 }