Fix handle allocation and the CREATE TABLE query.
[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 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "wine/debug.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(msi);
40
41 typedef struct tagMSICOLUMNINFO
42 {
43     LPWSTR tablename;
44     UINT   number;
45     LPWSTR colname;
46     UINT   type;
47     UINT   offset;
48 } MSICOLUMNINFO;
49
50 struct tagMSITABLE
51 {
52     USHORT **data;
53     UINT ref_count;
54     UINT row_count;
55     struct tagMSITABLE *next;
56     struct tagMSITABLE *prev;
57     WCHAR name[1];
58 };
59
60 #define MAX_STREAM_NAME 0x1f
61
62 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
63        MSICOLUMNINFO **pcols, UINT *pcount );
64 static UINT get_tablecolumns( MSIDATABASE *db, 
65        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
66
67 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
68 {
69     if( col->type & MSITYPE_STRING )
70         return 2;
71     if( (col->type & 0xff) > 4 )
72         ERR("Invalid column size!\n");
73     return col->type & 0xff;
74 }
75
76 static int utf2mime(int x)
77 {
78     if( (x>='0') && (x<='9') )
79         return x-'0';
80     if( (x>='A') && (x<='Z') )
81         return x-'A'+10;
82     if( (x>='a') && (x<='z') )
83         return x-'a'+10+26;
84     if( x=='.' )
85         return 10+26+26;
86     if( x=='_' )
87         return 10+26+26+1;
88     return -1;
89 }
90
91 static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out)
92 {
93     DWORD count = MAX_STREAM_NAME;
94     DWORD ch, next;
95
96     if( bTable )
97     {
98          *out++ = 0x4840;
99          count --;
100     }
101     while( count -- ) 
102     {
103         ch = *in++;
104         if( !ch )
105         {
106             *out = ch;
107             return TRUE;
108         }
109         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
110         {
111             ch = utf2mime(ch) + 0x4800;
112             next = *in;
113             if( next && (next<0x80) )
114             {
115                 next = utf2mime(next);
116                 if( next >= 0  )
117                 {
118                      next += 0x3ffffc0;
119                      ch += (next<<6);
120                      in++;
121                 }
122             }
123         }
124         *out++ = ch;
125     }
126     return FALSE;
127 }
128
129 #if 0
130 static int mime2utf(int x)
131 {
132     if( x<10 )
133         return x + '0';
134     if( x<(10+26))
135         return x - 10 + 'A';
136     if( x<(10+26+26))
137         return x - 10 - 26 + 'a';
138     if( x == (10+26+26) )
139         return '.';
140     return '_';
141 }
142
143 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
144 {
145     WCHAR ch;
146     DWORD count = 0;
147
148     while ( (ch = *in++) )
149     {
150         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
151         {
152             if( ch >= 0x4800 )
153                 ch = mime2utf(ch-0x4800);
154             else
155             {
156                 ch -= 0x3800;
157                 *out++ = mime2utf(ch&0x3f);
158                 count++;
159                 ch = mime2utf((ch>>6)&0x3f);
160             }
161         }
162         *out++ = ch;
163         count++;
164     }
165     *out = 0;
166     return count;
167 }
168 #endif
169
170 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
171                               USHORT **pdata, UINT *psz )
172 {
173     HRESULT r;
174     UINT ret = ERROR_FUNCTION_FAILED;
175     VOID *data;
176     ULONG sz, count;
177     IStream *stm = NULL;
178     STATSTG stat;
179     WCHAR encname[0x20];
180
181     encode_streamname(TRUE, stname, encname);
182
183     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
184
185     r = IStorage_OpenStream(stg, encname, NULL, 
186             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
187     if( FAILED( r ) )
188     {
189         WARN("open stream failed r = %08lx - empty table?\n",r);
190         return ret;
191     }
192
193     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
194     if( FAILED( r ) )
195     {
196         ERR("open stream failed r = %08lx!\n",r);
197         goto end;
198     }
199
200     if( stat.cbSize.QuadPart >> 32 )
201     {
202         ERR("Too big!\n");
203         goto end;
204     }
205         
206     sz = stat.cbSize.QuadPart;
207     data = HeapAlloc( GetProcessHeap(), 0, sz );
208     if( !data )
209     {
210         ERR("couldn't allocate memory r=%08lx!\n",r);
211         ret = ERROR_NOT_ENOUGH_MEMORY;
212         goto end;
213     }
214         
215     r = IStream_Read(stm, data, sz, &count );
216     if( FAILED( r ) || ( count != sz ) )
217     {
218         HeapFree( GetProcessHeap(), 0, data );
219         ERR("read stream failed r = %08lx!\n",r);
220         goto end;
221     }
222
223     *pdata = data;
224     *psz = sz;
225     ret = ERROR_SUCCESS;
226
227 end:
228     IStream_Release( stm );
229
230     return ret;
231 }
232
233 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
234                                LPVOID data, UINT sz )
235 {
236     HRESULT r;
237     UINT ret = ERROR_FUNCTION_FAILED;
238     ULONG count;
239     IStream *stm = NULL;
240     ULARGE_INTEGER size;
241     LARGE_INTEGER pos;
242
243     WCHAR encname[0x20];
244
245     encode_streamname(TRUE, stname, encname);
246     r = IStorage_OpenStream( stg, encname, NULL, 
247             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
248     if( FAILED(r) )
249     {
250         r = IStorage_CreateStream( stg, encname,
251                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
252     }
253     if( FAILED( r ) )
254     {
255         ERR("open stream failed r = %08lx\n",r);
256         return ret;
257     }
258
259     size.QuadPart = sz;
260     r = IStream_SetSize( stm, size );
261     if( FAILED( r ) )
262     {
263         ERR("Failed to SetSize\n");
264         goto end;
265     }
266
267     pos.QuadPart = 0;
268     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
269     if( FAILED( r ) )
270     {
271         ERR("Failed to Seek\n");
272         goto end;
273     }
274
275     r = IStream_Write(stm, data, sz, &count );
276     if( FAILED( r ) || ( count != sz ) )
277     {
278         ERR("Failed to Write\n");
279         goto end;
280     }
281
282     ret = ERROR_SUCCESS;
283
284 end:
285     IStream_Release( stm );
286
287     return ret;
288 }
289
290 UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
291 {
292     MSITABLE *t;
293     USHORT *rawdata = NULL;
294     UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
295     MSICOLUMNINFO *cols, *last_col;
296
297     TRACE("%s\n",debugstr_w(name));
298
299     /* non-existing tables should be interpretted as empty tables */
300     t = HeapAlloc( GetProcessHeap(), 0, 
301                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
302     if( !t )
303         return ERROR_NOT_ENOUGH_MEMORY;
304
305     r = table_get_column_info( db, name, &cols, &num_cols );
306     if( r != ERROR_SUCCESS )
307     {
308         HeapFree( GetProcessHeap(), 0, t );
309         return r;
310     }
311     last_col = &cols[num_cols-1];
312     row_size = last_col->offset + bytes_per_column( last_col );
313
314     t->row_count = 0;
315     t->data = NULL;
316     lstrcpyW( t->name, name );
317     t->ref_count = 1;
318     *ptable = t;
319
320     /* if we can't read the table, just assume that it's empty */
321     read_stream_data( db->storage, name, &rawdata, &rawsize );
322     if( !rawdata )
323         return ERROR_SUCCESS;
324
325     TRACE("Read %d bytes\n", rawsize );
326
327     if( rawsize % row_size )
328     {
329         ERR("Table size is invalid %d/%d\n", rawsize, row_size );
330         return ERROR_FUNCTION_FAILED;
331     }
332
333     t->row_count = rawsize / row_size;
334     t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
335                          t->row_count * sizeof (USHORT*) );
336     if( !t->data )
337         return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
338
339     /* transpose all the data */
340     TRACE("Transposing data from %d columns\n", t->row_count );
341     for( i=0; i<t->row_count; i++ )
342     {
343         t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
344         if( !t->data[i] )
345             return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
346         for( j=0; j<num_cols; j++ )
347         {
348             UINT ofs = cols[j].offset/2;
349             UINT n = bytes_per_column( &cols[j] );
350
351             switch( n )
352             {
353             case 2:
354                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
355                 break;
356             case 4:
357                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
358                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i + 1];
359                 break;
360             default:
361                 ERR("oops - unknown column width %d\n", n);
362                 return ERROR_FUNCTION_FAILED;
363             }
364         }
365     }
366
367     HeapFree( GetProcessHeap(), 0, cols );
368     HeapFree( GetProcessHeap(), 0, rawdata );
369
370     return ERROR_SUCCESS;
371 }
372
373 /* add this table to the list of cached tables in the database */
374 void add_table(MSIDATABASE *db, MSITABLE *table)
375 {
376     table->next = db->first_table;
377     table->prev = NULL;
378     if( db->first_table )
379         db->first_table->prev = table;
380     else
381         db->last_table = table;
382     db->first_table = table;
383 }
384  
385 /* remove from the list of cached tables */
386 void remove_table( MSIDATABASE *db, MSITABLE *table )
387 {
388     if( table->next )
389         table->next->prev = table->prev;
390     else
391         db->last_table = table->prev;
392     if( table->prev )
393         table->prev->next = table->next;
394     else
395         db->first_table = table->next;
396     table->next = NULL;
397     table->prev = NULL;
398 }
399
400 void release_table( MSIDATABASE *db, MSITABLE *table )
401 {
402     if( !table->ref_count )
403         ERR("Trying to destroy table with refcount 0\n");
404     table->ref_count --;
405     if( !table->ref_count )
406     {
407         remove_table( db, table );
408         HeapFree( GetProcessHeap(), 0, table->data );
409         HeapFree( GetProcessHeap(), 0, table );
410         TRACE("Destroyed table %s\n", debugstr_w(table->name));
411     }
412 }
413
414 void free_cached_tables( MSIDATABASE *db )
415 {
416     while( db->first_table )
417     {
418         MSITABLE *t = db->first_table;
419
420         if ( --t->ref_count )
421             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
422         remove_table( db, t );
423         HeapFree( GetProcessHeap(), 0, t->data );
424         HeapFree( GetProcessHeap(), 0, t );
425     }
426 }
427
428 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
429 {
430     MSITABLE *t;
431
432     for( t = db->first_table; t; t=t->next )
433     {
434         if( !lstrcmpW( name, t->name ) )
435         {
436             *ptable = t;
437             return ERROR_SUCCESS;
438         }
439     }
440
441     return ERROR_FUNCTION_FAILED;
442 }
443
444 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
445 {
446     UINT r, column_count;
447     MSICOLUMNINFO *columns;
448
449     /* get the number of columns in this table */
450     column_count = 0;
451     r = get_tablecolumns( db, name, NULL, &column_count );
452     if( r != ERROR_SUCCESS )
453         return r;
454
455     /* if there's no columns, there's no table */
456     if( column_count == 0 )
457         return ERROR_INVALID_PARAMETER;
458
459     TRACE("Table %s found\n", debugstr_w(name) );
460
461     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
462     if( !columns )
463         return ERROR_FUNCTION_FAILED;
464
465     r = get_tablecolumns( db, name, columns, &column_count );
466     if( r != ERROR_SUCCESS )
467     {
468         HeapFree( GetProcessHeap(), 0, columns );
469         return ERROR_FUNCTION_FAILED;
470     }
471
472     *pcols = columns;
473     *pcount = column_count;
474
475     return r;
476 }
477
478 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
479 {
480     UINT r;
481
482     *ptable = NULL;
483
484     /* first, see if the table is cached */
485     r = find_cached_table( db, name, ptable );
486     if( r == ERROR_SUCCESS )
487     {
488         (*ptable)->ref_count++;
489         return r;
490     }
491
492     r = read_table_from_storage( db, name, ptable );
493     if( r != ERROR_SUCCESS )
494         return r;
495
496     /* add the table to the list */
497     add_table( db, *ptable );
498     (*ptable)->ref_count++;
499
500     return ERROR_SUCCESS;
501 }
502
503 UINT save_table( MSIDATABASE *db, MSITABLE *t )
504 {
505     USHORT *rawdata = NULL, *p;
506     UINT rawsize, r, i, j, row_size, num_cols = 0;
507     MSICOLUMNINFO *cols, *last_col;
508
509     TRACE("Saving %s\n", debugstr_w( t->name ) );
510
511     r = table_get_column_info( db, t->name, &cols, &num_cols );
512     if( r != ERROR_SUCCESS )
513         return r;
514     
515     last_col = &cols[num_cols-1];
516     row_size = last_col->offset + bytes_per_column( last_col );
517
518     rawsize = t->row_count * row_size;
519     rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
520     if( !rawdata )
521         return ERROR_NOT_ENOUGH_MEMORY;
522
523     p = rawdata;
524     for( i=0; i<num_cols; i++ )
525     {
526         for( j=0; j<t->row_count; j++ )
527         {
528             UINT offset = cols[i].offset;
529
530             *p++ = t->data[j][offset/2];
531             if( 4 == bytes_per_column( &cols[i] ) )
532                 *p++ = t->data[j][offset/2+1];
533         }
534     }
535
536     TRACE("writing %d bytes\n", rawsize);
537     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
538
539     HeapFree( GetProcessHeap(), 0, rawdata );
540
541     return r;
542 }
543
544 HRESULT init_string_table( IStorage *stg )
545 {
546     HRESULT r;
547     const WCHAR szStringData[] = { 
548         '_','S','t','r','i','n','g','D','a','t','a',0 };
549     const WCHAR szStringPool[] = { 
550         '_','S','t','r','i','n','g','P','o','o','l',0 };
551     USHORT zero[2] = { 0, 0 };
552     ULONG count = 0;
553     IStream *stm = NULL;
554     WCHAR encname[0x20];
555
556     encode_streamname(TRUE, szStringPool, encname);
557
558     /* create the StringPool stream... add the zero string to it*/
559     r = IStorage_CreateStream( stg, encname,
560             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
561     if( r ) 
562     {
563         TRACE("Failed\n");
564         return r;
565     }
566
567     r = IStream_Write(stm, zero, sizeof zero, &count );
568     IStream_Release( stm );
569
570     if( FAILED( r ) || ( count != sizeof zero ) )
571     {
572         TRACE("Failed\n");
573         return E_FAIL;
574     }
575
576     /* create the StringData stream... make it zero length */
577     encode_streamname(TRUE, szStringData, encname);
578     r = IStorage_CreateStream( stg, encname,
579             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
580     if( r ) 
581     {
582         TRACE("Failed\n");
583         return E_FAIL;
584     }
585     IStream_Release( stm );
586
587     return r;
588 }
589
590 UINT load_string_table( MSIDATABASE *db )
591 {
592     CHAR *data;
593     USHORT *pool;
594     UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0;
595     DWORD i, count, offset, len, n;
596     const WCHAR szStringData[] = { 
597         '_','S','t','r','i','n','g','D','a','t','a',0 };
598     const WCHAR szStringPool[] = { 
599         '_','S','t','r','i','n','g','P','o','o','l',0 };
600
601     if( db->strings )
602     {
603         msi_destroy_stringtable( db->strings );
604         db->strings = NULL;
605     }
606
607     r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
608     if( r != ERROR_SUCCESS)
609         goto end;
610     r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
611     if( r != ERROR_SUCCESS)
612         goto end;
613
614     count = poolsize/4;
615     db->strings = msi_init_stringtable( count );
616
617     if( pool[0] || pool[1] )
618         ERR("The first string should be nul, but isn't\n");
619     offset = 0;
620     for( i=1; i<count; i++ )
621     {
622         len = pool[i*2];
623         n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
624         if( n != i )
625             ERR("Failed to add string %ld\n", i );
626         offset += len;
627     }
628
629     TRACE("Loaded %ld strings\n", count);
630
631     ret = ERROR_SUCCESS;
632
633 end:
634     if( pool )
635         HeapFree( GetProcessHeap(), 0, pool );
636     if( data )
637         HeapFree( GetProcessHeap(), 0, data );
638
639     return ret;
640 }
641
642 UINT save_string_table( MSIDATABASE *db )
643 {
644     UINT i, count, datasize, poolsize, sz, used, r;
645     UINT ret = ERROR_FUNCTION_FAILED;
646     const WCHAR szStringData[] = { 
647         '_','S','t','r','i','n','g','D','a','t','a',0 };
648     const WCHAR szStringPool[] = { 
649         '_','S','t','r','i','n','g','P','o','o','l',0 };
650     CHAR *data = NULL;
651     USHORT *pool = NULL;
652
653     TRACE("\n");
654
655     /* construct the new table in memory first */
656     count = msi_string_count( db->strings );
657     poolsize = count*2*sizeof(USHORT);
658     datasize = msi_string_totalsize( db->strings );
659
660     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
661     if( ! pool )
662     {
663         ERR("Failed to alloc pool %d bytes\n", poolsize );
664         goto err;
665     }
666     data = HeapAlloc( GetProcessHeap(), 0, datasize );
667     if( ! data )
668     {
669         ERR("Failed to alloc data %d bytes\n", poolsize );
670         goto err;
671     }
672
673     used = 0;
674     pool[0]=0;   /* the first element is always zero */
675     pool[1]=0;
676     for( i=1; i<count; i++ )
677     {
678         sz = datasize - used;
679         r = msi_id2stringA( db->strings, i, data+used, &sz );
680         if( r != ERROR_SUCCESS )
681         {
682             ERR("failed to fetch string\n");
683             sz = 0;
684         }
685         else
686             sz--;
687         pool[ i*2 ] = sz;
688         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
689         used += sz;
690         if( used > datasize  )
691         {
692             ERR("oops overran %d >= %d\n", used, datasize);
693             goto err;
694         }
695     }
696
697     if( used != datasize )
698     {
699         ERR("oops used %d != datasize %d\n", used, datasize);
700         goto err;
701     }
702
703     /* write the streams */
704     r = write_stream_data( db->storage, szStringData, data, datasize );
705     TRACE("Wrote StringData r=%08x\n", r);
706     if( r )
707         goto err;
708     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
709     TRACE("Wrote StringPool r=%08x\n", r);
710     if( r )
711         goto err;
712
713     ret = ERROR_SUCCESS;
714
715 err:
716     if( data )
717         HeapFree( GetProcessHeap(), 0, data );
718     if( pool )
719         HeapFree( GetProcessHeap(), 0, pool );
720
721     return ret;
722 }
723
724 static LPWSTR strdupW( LPCWSTR str )
725 {
726     UINT len = lstrlenW( str ) + 1;
727     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
728     if( ret )
729         lstrcpyW( ret, str );
730     return ret;
731 }
732
733 /* information for default tables */
734 const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
735 const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
736 const WCHAR szName[]    = { 'N','a','m','e',0 };
737 const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
738 const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
739 const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
740 const WCHAR szType[]    = { 'T','y','p','e',0 };
741
742 struct standard_table {
743     LPCWSTR tablename;
744     LPCWSTR columnname;
745     UINT number;
746     UINT type;
747 } MSI_standard_tables[] =
748 {
749   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
750   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
751   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
752   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
753   { szColumns, szType,   4, MSITYPE_VALID | 2},
754 };
755
756 #define STANDARD_TABLE_COUNT \
757      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
758
759 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
760 {
761     DWORD i, n=0;
762
763     for(i=0; i<STANDARD_TABLE_COUNT; i++)
764     {
765         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
766             continue;
767         if(colinfo && (n < *sz) )
768         {
769             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
770             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
771             colinfo[n].number = MSI_standard_tables[i].number;
772             colinfo[n].type = MSI_standard_tables[i].type;
773             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
774                     debugstr_w(colinfo[n].colname)); */
775             if( n )
776                 colinfo[n].offset = colinfo[n-1].offset
777                                   + bytes_per_column( &colinfo[n-1] );
778             else
779                 colinfo[n].offset = 0;
780         }
781         n++;
782         if( colinfo && (n >= *sz) )
783             break;
784     }
785     *sz = n;
786     return ERROR_SUCCESS;
787 }
788
789 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
790 {
791     UINT sz=0, r;
792     LPWSTR str;
793
794     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
795     if( r != ERROR_SUCCESS )
796         return NULL;
797     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
798     if( !str )
799         return str;
800     r = msi_id2stringW( db->strings, stringid, str, &sz );
801     if( r == ERROR_SUCCESS )
802         return str;
803     HeapFree(  GetProcessHeap(), 0, str );
804     return NULL;
805 }
806
807 static UINT get_tablecolumns( MSIDATABASE *db, 
808        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
809 {
810     UINT r, i, n=0, table_id, count, maxcount = *sz;
811     MSITABLE *table = NULL;
812     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
813
814     /* first check if there is a default table with that name */
815     r = get_defaulttablecolumns( szTableName, colinfo, sz );
816     if( ( r == ERROR_SUCCESS ) && *sz )
817         return r;
818
819     r = get_table( db, szColumns, &table);
820     if( r != ERROR_SUCCESS )
821     {
822         ERR("table %s not available\n", debugstr_w(szColumns));
823         return r;
824     }
825
826     /* convert table and column names to IDs from the string table */
827     r = msi_string2id( db->strings, szTableName, &table_id );
828     if( r != ERROR_SUCCESS )
829     {
830         release_table( db, table );
831         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
832         return r;
833     }
834
835     TRACE("Table id is %d\n", table_id);
836
837     count = table->row_count;
838     for( i=0; i<count; i++ )
839     {
840         if( table->data[ i ][ 0 ] != table_id )
841             continue;
842         if( colinfo )
843         {
844             UINT id = table->data[ i ] [ 2 ];
845             colinfo[n].tablename = MSI_makestring( db, table_id );
846             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
847             colinfo[n].colname = MSI_makestring( db, id );
848             colinfo[n].type = table->data[ i ] [ 3 ];
849             /* this assumes that columns are in order in the table */
850             if( n )
851                 colinfo[n].offset = colinfo[n-1].offset
852                                   + bytes_per_column( &colinfo[n-1] );
853             else
854                 colinfo[n].offset = 0;
855             TRACE("table %s column %d is [%s] (%d) with type %08x "
856                   "offset %d at row %d\n", debugstr_w(szTableName),
857                    colinfo[n].number, debugstr_w(colinfo[n].colname),
858                    id, colinfo[n].type, colinfo[n].offset, i);
859             if( n != (colinfo[n].number-1) )
860             {
861                 ERR("oops. data in the _Columns table isn't in the right "
862                     "order for table %s\n", debugstr_w(szTableName));
863                 return ERROR_FUNCTION_FAILED;
864             }
865         }
866         n++;
867         if( colinfo && ( n >= maxcount ) )
868             break;
869     }
870     *sz = n;
871
872     release_table( db, table );
873
874     return ERROR_SUCCESS;
875 }
876
877 /* try to find the table name in the _Tables table */
878 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
879 {
880     const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
881     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
882     UINT r, table_id = 0, i, count;
883     MSITABLE *table = NULL;
884
885     if( !lstrcmpW( name, szTables ) )
886         return TRUE;
887     if( !lstrcmpW( name, szColumns ) )
888         return TRUE;
889
890     r = msi_string2id( db->strings, name, &table_id );
891     if( r != ERROR_SUCCESS )
892     {
893         TRACE("Couldn't find id for %s\n", debugstr_w(name));
894         return FALSE;
895     }
896
897     r = get_table( db, szTables, &table);
898     if( r != ERROR_SUCCESS )
899     {
900         ERR("table %s not available\n", debugstr_w(szTables));
901         return FALSE;
902     }
903
904     /* count = table->size/2; */
905     count = table->row_count;
906     for( i=0; i<count; i++ )
907         if( table->data[ i ][ 0 ] == table_id )
908             break;
909
910     release_table( db, table );
911
912     if (i!=count)
913         return TRUE;
914
915     ERR("Searched %d tables, but %d was not found\n", count, table_id );
916
917     return FALSE;
918 }
919
920 /* below is the query interface to a table */
921
922 typedef struct tagMSITABLEVIEW
923 {
924     MSIVIEW        view;
925     MSIDATABASE   *db;
926     MSITABLE      *table;
927     MSICOLUMNINFO *columns;
928     UINT           num_cols;
929     UINT           row_size;
930     WCHAR          name[1];
931 } MSITABLEVIEW;
932
933 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
934 {
935     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
936     UINT offset, num_rows, n;
937
938     if( !tv->table )
939         return ERROR_INVALID_PARAMETER;
940
941     if( (col==0) || (col>tv->num_cols) )
942         return ERROR_INVALID_PARAMETER;
943
944     /* how many rows are there ? */
945     num_rows = tv->table->row_count;
946     if( row >= num_rows )
947         return ERROR_NO_MORE_ITEMS;
948
949     if( tv->columns[col-1].offset >= tv->row_size )
950     {
951         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
952         ERR("%p %p\n", tv, tv->columns );
953         return ERROR_FUNCTION_FAILED;
954     }
955
956     offset = row + (tv->columns[col-1].offset/2) * num_rows;
957     n = bytes_per_column( &tv->columns[col-1] );
958     switch( n )
959     {
960     case 4:
961         offset = tv->columns[col-1].offset/2;
962         *val = tv->table->data[row][offset] + 
963                (tv->table->data[row][offset + 1] << 16);
964         break;
965     case 2:
966         offset = tv->columns[col-1].offset/2;
967         *val = tv->table->data[row][offset];
968         break;
969     default:
970         ERR("oops! what is %d bytes per column?\n", n );
971         return ERROR_FUNCTION_FAILED;
972     }
973
974     TRACE("Data [%d][%d] = %d \n", row, col, *val );
975
976     return ERROR_SUCCESS;
977 }
978
979 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
980 {
981     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
982     UINT offset, num_rows, n;
983
984     if( !tv->table )
985         return ERROR_INVALID_PARAMETER;
986
987     if( (col==0) || (col>tv->num_cols) )
988         return ERROR_INVALID_PARAMETER;
989
990     if( tv->columns[col-1].offset >= tv->row_size )
991     {
992         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
993         ERR("%p %p\n", tv, tv->columns );
994         return ERROR_FUNCTION_FAILED;
995     }
996
997     offset = row + (tv->columns[col-1].offset/2) * num_rows;
998     n = bytes_per_column( &tv->columns[col-1] );
999     switch( n )
1000     {
1001     case 4:
1002         offset = tv->columns[col-1].offset/2;
1003         tv->table->data[row][offset]     = val & 0xffff;
1004         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1005         break;
1006     case 2:
1007         offset = tv->columns[col-1].offset/2;
1008         tv->table->data[row][offset] = val;
1009         break;
1010     default:
1011         ERR("oops! what is %d bytes per column?\n", n );
1012         return ERROR_FUNCTION_FAILED;
1013     }
1014     return ERROR_SUCCESS;
1015 }
1016
1017 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1018 {
1019     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1020     USHORT **p, *row;
1021     UINT sz;
1022
1023     TRACE("%p\n", view);
1024
1025     if( !tv->table )
1026         return ERROR_INVALID_PARAMETER;
1027
1028     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1029     if( !row )
1030         return ERROR_NOT_ENOUGH_MEMORY;
1031
1032     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1033     if( tv->table->data )
1034         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1035     else
1036         p = HeapAlloc( GetProcessHeap(), 0, sz );
1037     if( !p )
1038         return ERROR_NOT_ENOUGH_MEMORY;
1039
1040     tv->table->data = p;
1041     tv->table->data[tv->table->row_count] = row;
1042     *num = tv->table->row_count;
1043     tv->table->row_count++;
1044
1045     return ERROR_SUCCESS;
1046 }
1047
1048 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
1049 {
1050     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1051     UINT r;
1052
1053     TRACE("%p %ld\n", tv, record);
1054
1055     if( tv->table )
1056         return ERROR_FUNCTION_FAILED;
1057
1058     r = get_table( tv->db, tv->name, &tv->table );
1059     if( r != ERROR_SUCCESS )
1060         return r;
1061     
1062     return ERROR_SUCCESS;
1063 }
1064
1065 static UINT TABLE_close( struct tagMSIVIEW *view )
1066 {
1067     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1068
1069     TRACE("%p\n", view );
1070
1071     if( !tv->table )
1072         return ERROR_FUNCTION_FAILED;
1073
1074     release_table( tv->db, tv->table );
1075     tv->table = NULL;
1076     
1077     return ERROR_SUCCESS;
1078 }
1079
1080 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1081 {
1082     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1083
1084     TRACE("%p %p %p\n", view, rows, cols );
1085
1086     if( cols )
1087         *cols = tv->num_cols;
1088     if( rows )
1089     {
1090         if( !tv->table )
1091             return ERROR_INVALID_PARAMETER;
1092         *rows = tv->table->row_count;
1093     }
1094
1095     return ERROR_SUCCESS;
1096 }
1097
1098 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1099                 UINT n, LPWSTR *name, UINT *type )
1100 {
1101     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1102
1103     TRACE("%p %d %p %p\n", tv, n, name, type );
1104
1105     if( ( n == 0 ) || ( n > tv->num_cols ) )
1106         return ERROR_INVALID_PARAMETER;
1107
1108     if( name )
1109     {
1110         *name = strdupW( tv->columns[n-1].colname );
1111         if( !*name )
1112             return ERROR_FUNCTION_FAILED;
1113     }
1114     if( type )
1115         *type = tv->columns[n-1].type;
1116
1117     return ERROR_SUCCESS;
1118 }
1119
1120 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
1121 {
1122     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
1123     return ERROR_CALL_NOT_IMPLEMENTED;
1124 }
1125
1126 static UINT TABLE_delete( struct tagMSIVIEW *view )
1127 {
1128     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1129
1130     TRACE("%p\n", view );
1131
1132     if( tv->table )
1133         release_table( tv->db, tv->table );
1134     tv->table = NULL;
1135
1136     if( tv->columns )
1137     {
1138         UINT i;
1139         for( i=0; i<tv->num_cols; i++)
1140         {
1141             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1142             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1143         }
1144         HeapFree( GetProcessHeap(), 0, tv->columns );
1145     }
1146     tv->columns = NULL;
1147
1148     HeapFree( GetProcessHeap(), 0, tv );
1149
1150     return ERROR_SUCCESS;
1151 }
1152
1153
1154 MSIVIEWOPS table_ops =
1155 {
1156     TABLE_fetch_int,
1157     TABLE_set_int,
1158     TABLE_insert_row,
1159     TABLE_execute,
1160     TABLE_close,
1161     TABLE_get_dimensions,
1162     TABLE_get_column_info,
1163     TABLE_modify,
1164     TABLE_delete
1165 };
1166
1167 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1168 {
1169     MSITABLEVIEW *tv ;
1170     UINT r, sz, column_count;
1171     MSICOLUMNINFO *columns, *last_col;
1172
1173     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1174
1175     /* get the number of columns in this table */
1176     column_count = 0;
1177     r = get_tablecolumns( db, name, NULL, &column_count );
1178     if( r != ERROR_SUCCESS )
1179         return r;
1180
1181     /* if there's no columns, there's no table */
1182     if( column_count == 0 )
1183         return ERROR_INVALID_PARAMETER;
1184
1185     TRACE("Table found\n");
1186
1187     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1188     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1189     if( !tv )
1190         return ERROR_FUNCTION_FAILED;
1191     
1192     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1193     if( !columns )
1194     {
1195         HeapFree( GetProcessHeap(), 0, tv );
1196         return ERROR_FUNCTION_FAILED;
1197     }
1198
1199     r = get_tablecolumns( db, name, columns, &column_count );
1200     if( r != ERROR_SUCCESS )
1201     {
1202         HeapFree( GetProcessHeap(), 0, columns );
1203         HeapFree( GetProcessHeap(), 0, tv );
1204         return ERROR_FUNCTION_FAILED;
1205     }
1206
1207     TRACE("Table has %d columns\n", column_count);
1208
1209     last_col = &columns[column_count-1];
1210
1211     /* fill the structure */
1212     tv->view.ops = &table_ops;
1213     tv->db = db;
1214     tv->columns = columns;
1215     tv->num_cols = column_count;
1216     tv->table = NULL;
1217     tv->row_size = last_col->offset + bytes_per_column( last_col );
1218
1219     TRACE("one row is %d bytes\n", tv->row_size );
1220
1221     *view = (MSIVIEW*) tv;
1222     lstrcpyW( tv->name, name );
1223
1224     return ERROR_SUCCESS;
1225 }
1226
1227 UINT MSI_CommitTables( MSIDATABASE *db )
1228 {
1229     UINT r;
1230     MSITABLE *table = NULL;
1231
1232     TRACE("%p\n",db);
1233
1234     r = save_string_table( db );
1235     if( r != ERROR_SUCCESS )
1236     {
1237         ERR("failed to save string table r=%08x\n",r);
1238         return r;
1239     }
1240
1241     for( table = db->first_table; table; table = table->next )
1242     {
1243         r = save_table( db, table );
1244         if( r != ERROR_SUCCESS )
1245         {
1246             ERR("failed to save table %s (r=%08x)\n",
1247                   debugstr_w(table->name), r);
1248             return r;
1249         }
1250     }
1251
1252     /* force everything to reload next time */
1253     free_cached_tables( db );
1254
1255     return ERROR_SUCCESS;
1256 }