quartz: Use proper alloc/free functions for COM objects.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2005 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <assert.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "objbase.h"
34 #include "objidl.h"
35 #include "winnls.h"
36 #include "msipriv.h"
37 #include "query.h"
38
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
43
44 #define MSITABLE_HASH_TABLE_SIZE 37
45
46 typedef struct tagMSICOLUMNHASHENTRY
47 {
48     struct tagMSICOLUMNHASHENTRY *next;
49     UINT value;
50     UINT row;
51 } MSICOLUMNHASHENTRY;
52
53 typedef struct tagMSICOLUMNINFO
54 {
55     LPCWSTR tablename;
56     UINT   number;
57     LPCWSTR colname;
58     UINT   type;
59     UINT   offset;
60     MSICOLUMNHASHENTRY **hash_table;
61 } MSICOLUMNINFO;
62
63 struct tagMSITABLE
64 {
65     USHORT **data;
66     UINT row_count;
67     struct list entry;
68     WCHAR name[1];
69 };
70
71 typedef struct tagMSITRANSFORM {
72     struct list entry;
73     IStorage *stg;
74 } MSITRANSFORM;
75
76 static const WCHAR szStringData[] = {
77     '_','S','t','r','i','n','g','D','a','t','a',0 };
78 static const WCHAR szStringPool[] = {
79     '_','S','t','r','i','n','g','P','o','o','l',0 };
80
81 #define MAX_STREAM_NAME 0x1f
82
83 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
84        MSICOLUMNINFO **pcols, UINT *pcount );
85 static UINT get_tablecolumns( MSIDATABASE *db,
86        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
87 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
88
89 static inline UINT bytes_per_column( const MSICOLUMNINFO *col )
90 {
91     if( col->type & MSITYPE_STRING )
92         return 2;
93     if( (col->type & 0xff) > 4 )
94         ERR("Invalid column size!\n");
95     return col->type & 0xff;
96 }
97
98 static int utf2mime(int x)
99 {
100     if( (x>='0') && (x<='9') )
101         return x-'0';
102     if( (x>='A') && (x<='Z') )
103         return x-'A'+10;
104     if( (x>='a') && (x<='z') )
105         return x-'a'+10+26;
106     if( x=='.' )
107         return 10+26+26;
108     if( x=='_' )
109         return 10+26+26+1;
110     return -1;
111 }
112
113 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
114 {
115     DWORD count = MAX_STREAM_NAME;
116     DWORD ch, next;
117     LPWSTR out, p;
118
119     if( !bTable )
120         count = lstrlenW( in )+2;
121     out = msi_alloc( count*sizeof(WCHAR) );
122     p = out;
123
124     if( bTable )
125     {
126          *p++ = 0x4840;
127          count --;
128     }
129     while( count -- ) 
130     {
131         ch = *in++;
132         if( !ch )
133         {
134             *p = ch;
135             return out;
136         }
137         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
138         {
139             ch = utf2mime(ch) + 0x4800;
140             next = *in;
141             if( next && (next<0x80) )
142             {
143                 next = utf2mime(next);
144                 if( next >= 0  )
145                 {
146                      next += 0x3ffffc0;
147                      ch += (next<<6);
148                      in++;
149                 }
150             }
151         }
152         *p++ = ch;
153     }
154     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
155     msi_free( out );
156     return NULL;
157 }
158
159 static int mime2utf(int x)
160 {
161     if( x<10 )
162         return x + '0';
163     if( x<(10+26))
164         return x - 10 + 'A';
165     if( x<(10+26+26))
166         return x - 10 - 26 + 'a';
167     if( x == (10+26+26) )
168         return '.';
169     return '_';
170 }
171
172 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
173 {
174     WCHAR ch;
175     DWORD count = 0;
176
177     while ( (ch = *in++) )
178     {
179         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
180         {
181             if( ch >= 0x4800 )
182                 ch = mime2utf(ch-0x4800);
183             else
184             {
185                 ch -= 0x3800;
186                 *out++ = mime2utf(ch&0x3f);
187                 count++;
188                 ch = mime2utf((ch>>6)&0x3f);
189             }
190         }
191         *out++ = ch;
192         count++;
193     }
194     *out = 0;
195     return count;
196 }
197
198 void enum_stream_names( IStorage *stg )
199 {
200     IEnumSTATSTG *stgenum = NULL;
201     HRESULT r;
202     STATSTG stat;
203     ULONG n, count;
204     WCHAR name[0x40];
205
206     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
207     if( FAILED( r ) )
208         return;
209
210     n = 0;
211     while( 1 )
212     {
213         count = 0;
214         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
215         if( FAILED( r ) || !count )
216             break;
217         decode_streamname( stat.pwcsName, name );
218         TRACE("stream %2d -> %s %s\n", n,
219               debugstr_w(stat.pwcsName), debugstr_w(name) );
220         n++;
221     }
222
223     IEnumSTATSTG_Release( stgenum );
224 }
225
226 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
227                               USHORT **pdata, UINT *psz )
228 {
229     HRESULT r;
230     UINT ret = ERROR_FUNCTION_FAILED;
231     VOID *data;
232     ULONG sz, count;
233     IStream *stm = NULL;
234     STATSTG stat;
235     LPWSTR encname;
236
237     encname = encode_streamname(TRUE, stname);
238
239     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
240
241     r = IStorage_OpenStream(stg, encname, NULL, 
242             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
243     msi_free( encname );
244     if( FAILED( r ) )
245     {
246         WARN("open stream failed r = %08x - empty table?\n", r);
247         return ret;
248     }
249
250     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
251     if( FAILED( r ) )
252     {
253         WARN("open stream failed r = %08x!\n", r);
254         goto end;
255     }
256
257     if( stat.cbSize.QuadPart >> 32 )
258     {
259         WARN("Too big!\n");
260         goto end;
261     }
262         
263     sz = stat.cbSize.QuadPart;
264     data = msi_alloc( sz );
265     if( !data )
266     {
267         WARN("couldn't allocate memory r=%08x!\n", r);
268         ret = ERROR_NOT_ENOUGH_MEMORY;
269         goto end;
270     }
271         
272     r = IStream_Read(stm, data, sz, &count );
273     if( FAILED( r ) || ( count != sz ) )
274     {
275         msi_free( data );
276         WARN("read stream failed r = %08x!\n", r);
277         goto end;
278     }
279
280     *pdata = data;
281     *psz = sz;
282     ret = ERROR_SUCCESS;
283
284 end:
285     IStream_Release( stm );
286
287     return ret;
288 }
289
290 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
291 {
292     LPWSTR encname;
293     HRESULT r;
294
295     encname = encode_streamname(FALSE, stname);
296
297     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
298
299     r = IStorage_OpenStream(db->storage, encname, NULL, 
300             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
301     if( FAILED( r ) )
302     {
303         MSITRANSFORM *transform;
304
305         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
306         {
307             TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
308             r = IStorage_OpenStream( transform->stg, encname, NULL, 
309                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
310             if (SUCCEEDED(r))
311                 break;
312         }
313     }
314
315     msi_free( encname );
316
317     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
318 }
319
320 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
321                               USHORT **pdata, UINT *psz )
322 {
323     HRESULT r;
324     UINT ret = ERROR_FUNCTION_FAILED;
325     VOID *data;
326     ULONG sz, count;
327     IStream *stm = NULL;
328     STATSTG stat;
329
330     r = db_get_raw_stream( db, stname, &stm );
331     if( r != ERROR_SUCCESS)
332         return ret;
333     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
334     if( FAILED( r ) )
335     {
336         WARN("open stream failed r = %08x!\n", r);
337         goto end;
338     }
339
340     if( stat.cbSize.QuadPart >> 32 )
341     {
342         WARN("Too big!\n");
343         goto end;
344     }
345         
346     sz = stat.cbSize.QuadPart;
347     data = msi_alloc( sz );
348     if( !data )
349     {
350         WARN("couldn't allocate memory r=%08x!\n", r);
351         ret = ERROR_NOT_ENOUGH_MEMORY;
352         goto end;
353     }
354         
355     r = IStream_Read(stm, data, sz, &count );
356     if( FAILED( r ) || ( count != sz ) )
357     {
358         msi_free( data );
359         WARN("read stream failed r = %08x!\n", r);
360         goto end;
361     }
362
363     *pdata = data;
364     *psz = sz;
365     ret = ERROR_SUCCESS;
366
367 end:
368     IStream_Release( stm );
369
370     return ret;
371 }
372
373 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
374                                LPVOID data, UINT sz )
375 {
376     HRESULT r;
377     UINT ret = ERROR_FUNCTION_FAILED;
378     ULONG count;
379     IStream *stm = NULL;
380     ULARGE_INTEGER size;
381     LARGE_INTEGER pos;
382     LPWSTR encname;
383
384     encname = encode_streamname(TRUE, stname );
385     r = IStorage_OpenStream( stg, encname, NULL, 
386             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
387     if( FAILED(r) )
388     {
389         r = IStorage_CreateStream( stg, encname,
390                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
391     }
392     msi_free( encname );
393     if( FAILED( r ) )
394     {
395         WARN("open stream failed r = %08x\n", r);
396         return ret;
397     }
398
399     size.QuadPart = sz;
400     r = IStream_SetSize( stm, size );
401     if( FAILED( r ) )
402     {
403         WARN("Failed to SetSize\n");
404         goto end;
405     }
406
407     pos.QuadPart = 0;
408     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
409     if( FAILED( r ) )
410     {
411         WARN("Failed to Seek\n");
412         goto end;
413     }
414
415     r = IStream_Write(stm, data, sz, &count );
416     if( FAILED( r ) || ( count != sz ) )
417     {
418         WARN("Failed to Write\n");
419         goto end;
420     }
421
422     ret = ERROR_SUCCESS;
423
424 end:
425     IStream_Release( stm );
426
427     return ret;
428 }
429
430 static void free_table( MSITABLE *table )
431 {
432     int i;
433     for( i=0; i<table->row_count; i++ )
434         msi_free( table->data[i] );
435     msi_free( table->data );
436     msi_free( table );
437 }
438
439 static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count )
440 {
441     const MSICOLUMNINFO *last_col = &cols[count-1];
442     if (!count)
443         return 0;
444     return last_col->offset + bytes_per_column( last_col );
445 }
446
447 /* add this table to the list of cached tables in the database */
448 static MSITABLE *read_table_from_storage( IStorage *stg, LPCWSTR name,
449                                     const MSICOLUMNINFO *cols, UINT num_cols )
450 {
451     MSITABLE *t;
452     USHORT *rawdata = NULL;
453     UINT rawsize = 0, i, j, row_size = 0;
454
455     TRACE("%s\n",debugstr_w(name));
456
457     /* nonexistent tables should be interpreted as empty tables */
458     t = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
459     if( !t )
460         return t;
461
462     row_size = msi_table_get_row_size( cols, num_cols );
463
464     t->row_count = 0;
465     t->data = NULL;
466     lstrcpyW( t->name, name );
467
468     /* if we can't read the table, just assume that it's empty */
469     read_stream_data( stg, name, &rawdata, &rawsize );
470     if( !rawdata )
471         return t;
472
473     TRACE("Read %d bytes\n", rawsize );
474
475     if( rawsize % row_size )
476     {
477         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
478         goto err;
479     }
480
481     t->row_count = rawsize / row_size;
482     t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
483     if( !t->data )
484         goto err;
485
486     /* transpose all the data */
487     TRACE("Transposing data from %d rows\n", t->row_count );
488     for( i=0; i<t->row_count; i++ )
489     {
490         t->data[i] = msi_alloc( row_size );
491         if( !t->data[i] )
492             goto err;
493
494         for( j=0; j<num_cols; j++ )
495         {
496             UINT ofs = cols[j].offset/2;
497             UINT n = bytes_per_column( &cols[j] );
498
499             switch( n )
500             {
501             case 2:
502                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
503                 break;
504             case 4:
505                 t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
506                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
507                 break;
508             default:
509                 ERR("oops - unknown column width %d\n", n);
510                 goto err;
511             }
512         }
513     }
514
515     msi_free( rawdata );
516     return t;
517 err:
518     msi_free( rawdata );
519     free_table( t );
520     return NULL;
521 }
522
523 void free_cached_tables( MSIDATABASE *db )
524 {
525     while( !list_empty( &db->tables ) )
526     {
527         MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
528
529         list_remove( &t->entry );
530         free_table( t );
531     }
532 }
533
534 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
535 {
536     MSITABLE *t;
537
538     LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
539         if( !lstrcmpW( name, t->name ) )
540             return t;
541
542     return NULL;
543 }
544
545 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
546 {
547     UINT r, column_count = 0;
548     MSICOLUMNINFO *columns;
549
550     /* get the number of columns in this table */
551     column_count = 0;
552     r = get_tablecolumns( db, name, NULL, &column_count );
553     if( r != ERROR_SUCCESS )
554         return r;
555
556     /* if there's no columns, there's no table */
557     if( column_count == 0 )
558         return ERROR_INVALID_PARAMETER;
559
560     TRACE("Table %s found\n", debugstr_w(name) );
561
562     columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
563     if( !columns )
564         return ERROR_FUNCTION_FAILED;
565
566     r = get_tablecolumns( db, name, columns, &column_count );
567     if( r != ERROR_SUCCESS )
568     {
569         msi_free( columns );
570         return ERROR_FUNCTION_FAILED;
571     }
572
573     *pcols = columns;
574     *pcount = column_count;
575
576     return r;
577 }
578
579 static MSITABLE *get_table( MSIDATABASE *db, LPCWSTR name,
580                             const MSICOLUMNINFO *cols, UINT num_cols )
581 {
582     MSITABLE *table;
583
584     /* first, see if the table is cached */
585     table = find_cached_table( db, name );
586     if( table )
587         return table;
588
589     table = read_table_from_storage( db->storage, name, cols, num_cols );
590     if( table )
591         list_add_head( &db->tables, &table->entry );
592
593     return table;
594 }
595
596 static UINT save_table( MSIDATABASE *db, MSITABLE *t )
597 {
598     USHORT *rawdata = NULL, *p;
599     UINT rawsize, r, i, j, row_size, num_cols = 0;
600     MSICOLUMNINFO *cols = NULL;
601
602     TRACE("Saving %s\n", debugstr_w( t->name ) );
603
604     r = table_get_column_info( db, t->name, &cols, &num_cols );
605     if( r != ERROR_SUCCESS )
606         return r;
607     
608     row_size = msi_table_get_row_size( cols, num_cols );
609
610     rawsize = t->row_count * row_size;
611     rawdata = msi_alloc_zero( rawsize );
612     if( !rawdata )
613     {
614         r = ERROR_NOT_ENOUGH_MEMORY;
615         goto err;
616     }
617
618     p = rawdata;
619     for( i=0; i<num_cols; i++ )
620     {
621         for( j=0; j<t->row_count; j++ )
622         {
623             UINT offset = cols[i].offset;
624
625             *p++ = t->data[j][offset/2];
626             if( 4 == bytes_per_column( &cols[i] ) )
627                 *p++ = t->data[j][offset/2+1];
628         }
629     }
630
631     TRACE("writing %d bytes\n", rawsize);
632     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
633
634 err:
635     msi_free_colinfo( cols, num_cols );
636     msi_free( cols );
637     msi_free( rawdata );
638
639     return r;
640 }
641
642 HRESULT init_string_table( IStorage *stg )
643 {
644     HRESULT r;
645     USHORT zero[2] = { 0, 0 };
646     ULONG count = 0;
647     IStream *stm = NULL;
648     LPWSTR encname;
649
650     encname = encode_streamname(TRUE, szStringPool );
651
652     /* create the StringPool stream... add the zero string to it*/
653     r = IStorage_CreateStream( stg, encname,
654             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
655     msi_free( encname );
656     if( r ) 
657     {
658         TRACE("Failed\n");
659         return r;
660     }
661
662     r = IStream_Write(stm, zero, sizeof zero, &count );
663     IStream_Release( stm );
664
665     if( FAILED( r ) || ( count != sizeof zero ) )
666     {
667         TRACE("Failed\n");
668         return E_FAIL;
669     }
670
671     /* create the StringData stream... make it zero length */
672     encname = encode_streamname(TRUE, szStringData );
673     r = IStorage_CreateStream( stg, encname,
674             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
675     msi_free( encname );
676     if( r ) 
677     {
678         TRACE("Failed\n");
679         return E_FAIL;
680     }
681     IStream_Release( stm );
682
683     return r;
684 }
685
686 string_table *load_string_table( IStorage *stg )
687 {
688     string_table *st = NULL;
689     CHAR *data = NULL;
690     USHORT *pool = NULL;
691     UINT r, datasize = 0, poolsize = 0, codepage;
692     DWORD i, count, offset, len, n, refs;
693
694     r = read_stream_data( stg, szStringPool, &pool, &poolsize );
695     if( r != ERROR_SUCCESS)
696         goto end;
697     r = read_stream_data( stg, szStringData, (USHORT**)&data, &datasize );
698     if( r != ERROR_SUCCESS)
699         goto end;
700
701     count = poolsize/4;
702     if( poolsize > 4 )
703         codepage = pool[0] | ( pool[1] << 16 );
704     else
705         codepage = CP_ACP;
706     st = msi_init_stringtable( count, codepage );
707
708     offset = 0;
709     n = 1;
710     i = 1;
711     while( i<count )
712     {
713         /* the string reference count is always the second word */
714         refs = pool[i*2+1];
715
716         /* empty entries have two zeros, still have a string id */
717         if (pool[i*2] == 0 && refs == 0)
718         {
719             i++;
720             n++;
721             continue;
722         }
723
724         /*
725          * If a string is over 64k, the previous string entry is made null
726          * and its the high word of the length is inserted in the null string's
727          * reference count field.
728          */
729         if( pool[i*2] == 0)
730         {
731             len = (pool[i*2+3] << 16) + pool[i*2+2];
732             i += 2;
733         }
734         else
735         {
736             len = pool[i*2];
737             i += 1;
738         }
739
740         if ( (offset + len) > datasize )
741         {
742             ERR("string table corrupt?\n");
743             break;
744         }
745
746         r = msi_addstring( st, n, data+offset, len, refs );
747         if( r != n )
748             ERR("Failed to add string %d\n", n );
749         n++;
750         offset += len;
751     }
752
753     if ( datasize != offset )
754         ERR("string table load failed! (%08x != %08x), please report\n", datasize, offset );
755
756     TRACE("Loaded %d strings\n", count);
757
758 end:
759     msi_free( pool );
760     msi_free( data );
761
762     return st;
763 }
764
765 static UINT save_string_table( MSIDATABASE *db )
766 {
767     UINT i, count, datasize = 0, poolsize = 0, sz, used, r, codepage, n;
768     UINT ret = ERROR_FUNCTION_FAILED;
769     CHAR *data = NULL;
770     USHORT *pool = NULL;
771
772     TRACE("\n");
773
774     /* construct the new table in memory first */
775     count = msi_string_totalsize( db->strings, &datasize, &poolsize );
776
777     TRACE("%u %u %u\n", count, datasize, poolsize );
778
779     pool = msi_alloc( poolsize );
780     if( ! pool )
781     {
782         WARN("Failed to alloc pool %d bytes\n", poolsize );
783         goto err;
784     }
785     data = msi_alloc( datasize );
786     if( ! data )
787     {
788         WARN("Failed to alloc data %d bytes\n", poolsize );
789         goto err;
790     }
791
792     used = 0;
793     codepage = msi_string_get_codepage( db->strings );
794     pool[0]=codepage&0xffff;
795     pool[1]=(codepage>>16);
796     n = 1;
797     for( i=1; i<count; i++ )
798     {
799         sz = datasize - used;
800         r = msi_id2stringA( db->strings, i, data+used, &sz );
801         if( r != ERROR_SUCCESS )
802         {
803             ERR("failed to fetch string\n");
804             sz = 0;
805         }
806         if( sz && (sz < (datasize - used ) ) )
807             sz--;
808
809         if (sz)
810             pool[ n*2 + 1 ] = msi_id_refcount( db->strings, i );
811         else
812             pool[ n*2 + 1 ] = 0;
813         if (sz < 0x10000)
814         {
815             pool[ n*2 ] = sz;
816             n++;
817         }
818         else
819         {
820             pool[ n*2 ] = 0;
821             pool[ n*2 + 2 ] = sz&0xffff;
822             pool[ n*2 + 3 ] = (sz>>16);
823             n += 2;
824         }
825         used += sz;
826         if( used > datasize  )
827         {
828             ERR("oops overran %d >= %d\n", used, datasize);
829             goto err;
830         }
831     }
832
833     if( used != datasize )
834     {
835         ERR("oops used %d != datasize %d\n", used, datasize);
836         goto err;
837     }
838
839     /* write the streams */
840     r = write_stream_data( db->storage, szStringData, data, datasize );
841     TRACE("Wrote StringData r=%08x\n", r);
842     if( r )
843         goto err;
844     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
845     TRACE("Wrote StringPool r=%08x\n", r);
846     if( r )
847         goto err;
848
849     ret = ERROR_SUCCESS;
850
851 err:
852     msi_free( data );
853     msi_free( pool );
854
855     return ret;
856 }
857
858 /* information for default tables */
859 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
860 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
861 static const WCHAR szName[]    = { 'N','a','m','e',0 };
862 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
863 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
864 static const WCHAR szType[]    = { 'T','y','p','e',0 };
865
866 static const MSICOLUMNINFO _Columns_cols[4] = {
867     { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
868     { szColumns, 2, szNumber, MSITYPE_VALID | 2,                   2 },
869     { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
870     { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6 },
871 };
872 static const MSICOLUMNINFO _Tables_cols[1] = {
873     { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
874 };
875
876 static void table_calc_column_offsets( MSICOLUMNINFO *colinfo, DWORD count )
877 {
878     DWORD i;
879
880     for( i=0; colinfo && (i<count); i++ )
881     {
882          assert( (i+1) == colinfo[ i ].number );
883          if (i)
884              colinfo[i].offset = colinfo[ i - 1 ].offset
885                                + bytes_per_column( &colinfo[ i - 1 ] );
886          else
887              colinfo[i].offset = 0;
888          TRACE("column %d is [%s] with type %08x ofs %d\n",
889                colinfo[i].number, debugstr_w(colinfo[i].colname),
890                colinfo[i].type, colinfo[i].offset);
891     }
892 }
893
894 static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
895 {
896     const MSICOLUMNINFO *p;
897     DWORD i, n;
898
899     TRACE("%s\n", debugstr_w(name));
900
901     if (!lstrcmpW( name, szTables ))
902     {
903         p = _Tables_cols;
904         n = 1;
905     }
906     else if (!lstrcmpW( name, szColumns ))
907     {
908         p = _Columns_cols;
909         n = 4;
910     }
911     else
912         return ERROR_FUNCTION_FAILED;
913
914     /* duplicate the string data so we can free it in msi_free_colinfo */
915     for (i=0; i<n; i++)
916     {
917         if (colinfo && (i < *sz) )
918         {
919             memcpy( &colinfo[i], &p[i], sizeof(MSICOLUMNINFO) );
920             colinfo[i].tablename = strdupW( p[i].tablename );
921             colinfo[i].colname = strdupW( p[i].colname );
922         }
923         if( colinfo && (i >= *sz) )
924             break;
925     }
926     table_calc_column_offsets( colinfo, n );
927     *sz = n;
928     return ERROR_SUCCESS;
929 }
930
931 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
932 {
933     UINT i;
934
935     for( i=0; i<count; i++ )
936     {
937         msi_free( (LPWSTR) colinfo[i].tablename );
938         msi_free( (LPWSTR) colinfo[i].colname );
939         msi_free( colinfo[i].hash_table );
940     }
941 }
942
943 static LPWSTR msi_makestring( MSIDATABASE *db, UINT stringid)
944 {
945     return strdupW(msi_string_lookup_id( db->strings, stringid ));
946 }
947
948 static UINT get_tablecolumns( MSIDATABASE *db,
949        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
950 {
951     UINT r, i, n=0, table_id, count, maxcount = *sz;
952     MSITABLE *table = NULL;
953
954     TRACE("%s\n", debugstr_w(szTableName));
955
956     /* first check if there is a default table with that name */
957     r = get_defaulttablecolumns( szTableName, colinfo, sz );
958     if( ( r == ERROR_SUCCESS ) && *sz )
959         return r;
960
961     table = get_table( db, szColumns, _Columns_cols, 4 );
962     if( !table )
963     {
964         ERR("couldn't load _Columns table\n");
965         return ERROR_FUNCTION_FAILED;
966     }
967
968     /* convert table and column names to IDs from the string table */
969     r = msi_string2idW( db->strings, szTableName, &table_id );
970     if( r != ERROR_SUCCESS )
971     {
972         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
973         return r;
974     }
975
976     TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
977
978     /* if maxcount is non-zero, assume it's exactly right for this table */
979     memset( colinfo, 0, maxcount*sizeof(*colinfo) );
980     count = table->row_count;
981     for( i=0; i<count; i++ )
982     {
983         if( table->data[ i ][ 0 ] != table_id )
984             continue;
985         if( colinfo )
986         {
987             UINT id = table->data[ i ] [ 2 ];
988             UINT col = table->data[ i ][ 1 ] - (1<<15);
989
990             /* check the column number is in range */
991             if (col<1 || col>maxcount)
992             {
993                 ERR("column %d out of range\n", col);
994                 continue;
995             }
996
997             /* check if this column was already set */
998             if (colinfo[ col - 1 ].number)
999             {
1000                 ERR("duplicate column %d\n", col);
1001                 continue;
1002             }
1003
1004             colinfo[ col - 1 ].tablename = msi_makestring( db, table_id );
1005             colinfo[ col - 1 ].number = col;
1006             colinfo[ col - 1 ].colname = msi_makestring( db, id );
1007             colinfo[ col - 1 ].type = table->data[ i ] [ 3 ] - (1<<15);
1008             colinfo[ col - 1 ].offset = 0;
1009             colinfo[ col - 1 ].hash_table = NULL;
1010         }
1011         n++;
1012     }
1013
1014     TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
1015
1016     if (colinfo && n != maxcount)
1017     {
1018         ERR("missing column in table %s\n", debugstr_w(szTableName));
1019         msi_free_colinfo(colinfo, maxcount );
1020         return ERROR_FUNCTION_FAILED;
1021     }
1022
1023     table_calc_column_offsets( colinfo, n );
1024     *sz = n;
1025
1026     return ERROR_SUCCESS;
1027 }
1028
1029 /* try to find the table name in the _Tables table */
1030 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
1031 {
1032     UINT r, table_id = 0, i, count;
1033     MSITABLE *table = NULL;
1034
1035     if( !lstrcmpW( name, szTables ) )
1036         return TRUE;
1037     if( !lstrcmpW( name, szColumns ) )
1038         return TRUE;
1039
1040     r = msi_string2idW( db->strings, name, &table_id );
1041     if( r != ERROR_SUCCESS )
1042     {
1043         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1044         return FALSE;
1045     }
1046
1047     table = get_table( db, szTables, _Tables_cols, 1 );
1048     if( !table )
1049     {
1050         TRACE("table %s not available\n", debugstr_w(szTables));
1051         return FALSE;
1052     }
1053
1054     /* count = table->size/2; */
1055     count = table->row_count;
1056     for( i=0; i<count; i++ )
1057         if( table->data[ i ][ 0 ] == table_id )
1058             break;
1059
1060     if (i!=count)
1061         return TRUE;
1062
1063     TRACE("Searched %d tables, but %d was not found\n", count, table_id );
1064
1065     return FALSE;
1066 }
1067
1068 /* below is the query interface to a table */
1069
1070 typedef struct tagMSITABLEVIEW
1071 {
1072     MSIVIEW        view;
1073     MSIDATABASE   *db;
1074     MSITABLE      *table;
1075     MSICOLUMNINFO *columns;
1076     UINT           num_cols;
1077     UINT           row_size;
1078     WCHAR          name[1];
1079 } MSITABLEVIEW;
1080
1081 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1082 {
1083     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1084     UINT offset, num_rows, n;
1085
1086     if( !tv->table )
1087         return ERROR_INVALID_PARAMETER;
1088
1089     if( (col==0) || (col>tv->num_cols) )
1090         return ERROR_INVALID_PARAMETER;
1091
1092     /* how many rows are there ? */
1093     num_rows = tv->table->row_count;
1094     if( row >= num_rows )
1095         return ERROR_NO_MORE_ITEMS;
1096
1097     if( tv->columns[col-1].offset >= tv->row_size )
1098     {
1099         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1100         ERR("%p %p\n", tv, tv->columns );
1101         return ERROR_FUNCTION_FAILED;
1102     }
1103
1104     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1105     n = bytes_per_column( &tv->columns[col-1] );
1106     switch( n )
1107     {
1108     case 4:
1109         offset = tv->columns[col-1].offset/2;
1110         *val = tv->table->data[row][offset] +
1111                (tv->table->data[row][offset + 1] << 16);
1112         break;
1113     case 2:
1114         offset = tv->columns[col-1].offset/2;
1115         *val = tv->table->data[row][offset];
1116         break;
1117     default:
1118         ERR("oops! what is %d bytes per column?\n", n );
1119         return ERROR_FUNCTION_FAILED;
1120     }
1121
1122     /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1123
1124     return ERROR_SUCCESS;
1125 }
1126
1127 /*
1128  * We need a special case for streams, as we need to reference column with
1129  * the name of the stream in the same table, and the table name
1130  * which may not be available at higher levels of the query
1131  */
1132 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1133 {
1134     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1135     UINT ival = 0, refcol = 0, r;
1136     LPCWSTR sval;
1137     LPWSTR full_name;
1138     DWORD len;
1139     static const WCHAR szDot[] = { '.', 0 };
1140     WCHAR number[0x20];
1141
1142     if( !view->ops->fetch_int )
1143         return ERROR_INVALID_PARAMETER;
1144
1145     /*
1146      * The column marked with the type stream data seems to have a single number
1147      * which references the column containing the name of the stream data
1148      *
1149      * Fetch the column to reference first.
1150      */
1151     r = view->ops->fetch_int( view, row, col, &ival );
1152     if( r != ERROR_SUCCESS )
1153         return r;
1154
1155     /* check the column value is in range */
1156     if (ival < 0 || ival > tv->num_cols || ival == col)
1157     {
1158         ERR("bad column ref (%u) for stream\n", ival);
1159         return ERROR_FUNCTION_FAILED;
1160     }
1161
1162     if ( tv->columns[ival - 1].type & MSITYPE_STRING )
1163     {
1164         /* now get the column with the name of the stream */
1165         r = view->ops->fetch_int( view, row, ival, &refcol );
1166         if ( r != ERROR_SUCCESS )
1167             return r;
1168
1169         /* lookup the string value from the string table */
1170         sval = msi_string_lookup_id( tv->db->strings, refcol );
1171         if ( !sval )
1172             return ERROR_INVALID_PARAMETER;
1173     }
1174     else
1175     {
1176         static const WCHAR fmt[] = { '%','d',0 };
1177         sprintfW( number, fmt, ival );
1178         sval = number;
1179     }
1180
1181     len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
1182     full_name = msi_alloc( len*sizeof(WCHAR) );
1183     lstrcpyW( full_name, tv->name );
1184     lstrcatW( full_name, szDot );
1185     lstrcatW( full_name, sval );
1186
1187     r = db_get_raw_stream( tv->db, full_name, stm );
1188     if( r )
1189         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1190     msi_free( full_name );
1191
1192     return r;
1193 }
1194
1195 static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1196 {
1197     UINT offset, n;
1198
1199     if( !tv->table )
1200         return ERROR_INVALID_PARAMETER;
1201
1202     if( (col==0) || (col>tv->num_cols) )
1203         return ERROR_INVALID_PARAMETER;
1204
1205     if( tv->columns[col-1].offset >= tv->row_size )
1206     {
1207         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1208         ERR("%p %p\n", tv, tv->columns );
1209         return ERROR_FUNCTION_FAILED;
1210     }
1211
1212     n = bytes_per_column( &tv->columns[col-1] );
1213     switch( n )
1214     {
1215     case 4:
1216         offset = tv->columns[col-1].offset/2;
1217         tv->table->data[row][offset]     = val & 0xffff;
1218         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1219         break;
1220     case 2:
1221         offset = tv->columns[col-1].offset/2;
1222         tv->table->data[row][offset] = val;
1223         break;
1224     default:
1225         ERR("oops! what is %d bytes per column?\n", n );
1226         return ERROR_FUNCTION_FAILED;
1227     }
1228     return ERROR_SUCCESS;
1229 }
1230
1231 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1232 {
1233     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1234     UINT i, val, r = ERROR_SUCCESS;
1235
1236     if ( !tv->table )
1237         return ERROR_INVALID_PARAMETER;
1238
1239     /* test if any of the mask bits are invalid */
1240     if ( mask >= (1<<tv->num_cols) )
1241         return ERROR_INVALID_PARAMETER;
1242
1243     for ( i = 0; i < tv->num_cols; i++ )
1244     {
1245         /* only update the fields specified in the mask */
1246         if ( !(mask&(1<<i)) )
1247             continue;
1248
1249         /* FIXME: should we allow updating keys? */
1250
1251         val = 0;
1252         if ( !MSI_RecordIsNull( rec, i + 1 ) )
1253         {
1254             if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1255             {
1256                 val = 1; /* refers to the first key column */
1257             }
1258             else if ( tv->columns[i].type & MSITYPE_STRING )
1259             {
1260                 LPCWSTR sval = MSI_RecordGetString( rec, i + 1 );
1261                 val = msi_addstringW( tv->db->strings, 0, sval, -1, 1 );
1262             }
1263             else if ( 2 == bytes_per_column( &tv->columns[ i ] ) )
1264             {
1265                 val = 0x8000 + MSI_RecordGetInteger( rec, i + 1 );
1266                 if ( val & 0xffff0000 )
1267                 {
1268                     ERR("field %u value %d out of range\n", i+1, val - 0x8000 );
1269                     return ERROR_FUNCTION_FAILED;
1270                 }
1271             }
1272             else
1273             {
1274                 INT ival = MSI_RecordGetInteger( rec, i + 1 );
1275                 val = ival ^ 0x80000000;
1276             }
1277         }
1278
1279         r = TABLE_set_int( tv, row, i+1, val );
1280         if ( r != ERROR_SUCCESS )
1281             break;
1282     }
1283     return r;
1284 }
1285
1286 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num )
1287 {
1288     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1289     USHORT **p, *row;
1290     UINT sz;
1291
1292     TRACE("%p\n", view);
1293
1294     if( !tv->table )
1295         return ERROR_INVALID_PARAMETER;
1296
1297     row = msi_alloc_zero( tv->row_size );
1298     if( !row )
1299         return ERROR_NOT_ENOUGH_MEMORY;
1300
1301     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1302     if( tv->table->data )
1303         p = msi_realloc( tv->table->data, sz );
1304     else
1305         p = msi_alloc( sz );
1306     if( !p )
1307     {
1308         msi_free( row );
1309         return ERROR_NOT_ENOUGH_MEMORY;
1310     }
1311
1312     tv->table->data = p;
1313     tv->table->data[tv->table->row_count] = row;
1314     *num = tv->table->row_count;
1315     tv->table->row_count++;
1316
1317     return ERROR_SUCCESS;
1318 }
1319
1320 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1321 {
1322     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1323
1324     TRACE("%p %p\n", tv, record);
1325
1326     TRACE("There are %d columns\n", tv->num_cols );
1327     tv->table = get_table( tv->db, tv->name, tv->columns, tv->num_cols );
1328     if( !tv->table )
1329         return ERROR_FUNCTION_FAILED;
1330
1331     return ERROR_SUCCESS;
1332 }
1333
1334 static UINT TABLE_close( struct tagMSIVIEW *view )
1335 {
1336     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1337
1338     TRACE("%p\n", view );
1339
1340     if( !tv->table )
1341         return ERROR_FUNCTION_FAILED;
1342
1343     tv->table = NULL;
1344     
1345     return ERROR_SUCCESS;
1346 }
1347
1348 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1349 {
1350     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1351
1352     TRACE("%p %p %p\n", view, rows, cols );
1353
1354     if( cols )
1355         *cols = tv->num_cols;
1356     if( rows )
1357     {
1358         if( !tv->table )
1359             return ERROR_INVALID_PARAMETER;
1360         *rows = tv->table->row_count;
1361     }
1362
1363     return ERROR_SUCCESS;
1364 }
1365
1366 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1367                 UINT n, LPWSTR *name, UINT *type )
1368 {
1369     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1370
1371     TRACE("%p %d %p %p\n", tv, n, name, type );
1372
1373     if( ( n == 0 ) || ( n > tv->num_cols ) )
1374         return ERROR_INVALID_PARAMETER;
1375
1376     if( name )
1377     {
1378         *name = strdupW( tv->columns[n-1].colname );
1379         if( !*name )
1380             return ERROR_FUNCTION_FAILED;
1381     }
1382     if( type )
1383         *type = tv->columns[n-1].type;
1384
1385     return ERROR_SUCCESS;
1386 }
1387
1388 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
1389
1390 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
1391 {
1392     UINT r, row, i;
1393
1394     /* check there's no null values where they're not allowed */
1395     for( i = 0; i < tv->num_cols; i++ )
1396     {
1397         if ( tv->columns[i].type & MSITYPE_NULLABLE )
1398             continue;
1399
1400         if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1401             TRACE("skipping binary column\n");
1402         else if ( tv->columns[i].type & MSITYPE_STRING )
1403         {
1404             LPCWSTR str;
1405
1406             str = MSI_RecordGetString( rec, i+1 );
1407             if (str == NULL || str[0] == 0)
1408                 return ERROR_INVALID_DATA;
1409         }
1410         else
1411         {
1412             UINT n;
1413
1414             n = MSI_RecordGetInteger( rec, i+1 );
1415             if (n == MSI_NULL_INTEGER)
1416                 return ERROR_INVALID_DATA;
1417         }
1418     }
1419
1420     /* check there's no duplicate keys */
1421     r = msi_table_find_row( tv, rec, &row );
1422     if (r == ERROR_SUCCESS)
1423         return ERROR_INVALID_DATA;
1424
1425     return ERROR_SUCCESS;
1426 }
1427
1428 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
1429 {
1430     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1431     UINT r, row = -1;
1432
1433     TRACE("%p %p\n", tv, rec );
1434
1435     /* check that the key is unique - can we find a matching row? */
1436     r = table_validate_new( tv, rec );
1437     if( r != ERROR_SUCCESS )
1438         return ERROR_FUNCTION_FAILED;
1439
1440     r = table_create_new_row( view, &row );
1441     TRACE("insert_row returned %08x\n", r);
1442     if( r != ERROR_SUCCESS )
1443         return r;
1444
1445     return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1446 }
1447
1448 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1449                 MSIRECORD *rec)
1450 {
1451     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1452     UINT r;
1453
1454     TRACE("%p %d %p\n", view, eModifyMode, rec );
1455
1456     if (!tv->table)
1457     {
1458         r = TABLE_execute( view, NULL );
1459         if( r )
1460             return r;
1461     }
1462
1463     switch (eModifyMode)
1464     {
1465     case MSIMODIFY_VALIDATE_NEW:
1466         r = table_validate_new( tv, rec );
1467         break;
1468
1469     case MSIMODIFY_INSERT_TEMPORARY:
1470         r = table_validate_new( tv, rec );
1471         if (r != ERROR_SUCCESS)
1472             break;
1473         r = TABLE_insert_row( view, rec );
1474         break;
1475
1476     case MSIMODIFY_REFRESH:
1477     case MSIMODIFY_INSERT:
1478     case MSIMODIFY_UPDATE:
1479     case MSIMODIFY_ASSIGN:
1480     case MSIMODIFY_REPLACE:
1481     case MSIMODIFY_MERGE:
1482     case MSIMODIFY_DELETE:
1483     case MSIMODIFY_VALIDATE:
1484     case MSIMODIFY_VALIDATE_FIELD:
1485     case MSIMODIFY_VALIDATE_DELETE:
1486         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1487         r = ERROR_CALL_NOT_IMPLEMENTED;
1488         break;
1489
1490     default:
1491         r = ERROR_INVALID_DATA;
1492     }
1493
1494     return r;
1495 }
1496
1497 static UINT TABLE_delete( struct tagMSIVIEW *view )
1498 {
1499     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1500
1501     TRACE("%p\n", view );
1502
1503     tv->table = NULL;
1504
1505     if( tv->columns )
1506     {
1507         msi_free_colinfo( tv->columns, tv->num_cols );
1508         msi_free( tv->columns );
1509     }
1510     tv->columns = NULL;
1511
1512     msi_free( tv );
1513
1514     return ERROR_SUCCESS;
1515 }
1516
1517 static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
1518     UINT val, UINT *row, MSIITERHANDLE *handle )
1519 {
1520     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1521     const MSICOLUMNHASHENTRY *entry;
1522
1523     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
1524
1525     if( !tv->table )
1526         return ERROR_INVALID_PARAMETER;
1527
1528     if( (col==0) || (col > tv->num_cols) )
1529         return ERROR_INVALID_PARAMETER;
1530
1531     if( !tv->columns[col-1].hash_table )
1532     {
1533         UINT i;
1534         UINT num_rows = tv->table->row_count;
1535         MSICOLUMNHASHENTRY **hash_table;
1536         MSICOLUMNHASHENTRY *new_entry;
1537
1538         if( tv->columns[col-1].offset >= tv->row_size )
1539         {
1540             ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1541             ERR("%p %p\n", tv, tv->columns );
1542             return ERROR_FUNCTION_FAILED;
1543         }
1544
1545         /* allocate contiguous memory for the table and its entries so we
1546          * don't have to do an expensive cleanup */
1547         hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) +
1548             num_rows * sizeof(MSICOLUMNHASHENTRY));
1549         if (!hash_table)
1550             return ERROR_OUTOFMEMORY;
1551
1552         memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*));
1553         tv->columns[col-1].hash_table = hash_table;
1554
1555         new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE);
1556
1557         for (i = 0; i < num_rows; i++, new_entry++)
1558         {
1559             UINT row_value, n;
1560             UINT offset = i + (tv->columns[col-1].offset/2) * num_rows;
1561             n = bytes_per_column( &tv->columns[col-1] );
1562             switch( n )
1563             {
1564             case 4:
1565                 offset = tv->columns[col-1].offset/2;
1566                 row_value = tv->table->data[i][offset] + 
1567                     (tv->table->data[i][offset + 1] << 16);
1568                 break;
1569             case 2:
1570                 offset = tv->columns[col-1].offset/2;
1571                 row_value = tv->table->data[i][offset];
1572                 break;
1573             default:
1574                 ERR("oops! what is %d bytes per column?\n", n );
1575                 continue;
1576             }
1577
1578             new_entry->next = NULL;
1579             new_entry->value = row_value;
1580             new_entry->row = i;
1581             if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE])
1582             {
1583                 MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE];
1584                 while (prev_entry->next)
1585                     prev_entry = prev_entry->next;
1586                 prev_entry->next = new_entry;
1587             }
1588             else
1589                 hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry;
1590         }
1591     }
1592
1593     if( !*handle )
1594         entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE];
1595     else
1596         entry = (*handle)->next;
1597
1598     while (entry && entry->value != val)
1599         entry = entry->next;
1600
1601     *handle = entry;
1602     if (!entry)
1603         return ERROR_NO_MORE_ITEMS;
1604
1605     *row = entry->row;
1606     return ERROR_SUCCESS;
1607 }
1608
1609
1610 static const MSIVIEWOPS table_ops =
1611 {
1612     TABLE_fetch_int,
1613     TABLE_fetch_stream,
1614     TABLE_set_row,
1615     TABLE_insert_row,
1616     TABLE_execute,
1617     TABLE_close,
1618     TABLE_get_dimensions,
1619     TABLE_get_column_info,
1620     TABLE_modify,
1621     TABLE_delete,
1622     TABLE_find_matching_rows
1623 };
1624
1625 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1626 {
1627     MSITABLEVIEW *tv ;
1628     UINT r, sz, column_count;
1629     MSICOLUMNINFO *columns;
1630
1631     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1632
1633     /* get the number of columns in this table */
1634     column_count = 0;
1635     r = get_tablecolumns( db, name, NULL, &column_count );
1636     if( r != ERROR_SUCCESS )
1637         return r;
1638
1639     /* if there's no columns, there's no table */
1640     if( column_count == 0 )
1641         return ERROR_INVALID_PARAMETER;
1642
1643     TRACE("Table found\n");
1644
1645     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1646     tv = msi_alloc_zero( sz );
1647     if( !tv )
1648         return ERROR_FUNCTION_FAILED;
1649     
1650     columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO));
1651     if( !columns )
1652     {
1653         msi_free( tv );
1654         return ERROR_FUNCTION_FAILED;
1655     }
1656
1657     r = get_tablecolumns( db, name, columns, &column_count );
1658     if( r != ERROR_SUCCESS )
1659     {
1660         msi_free( columns );
1661         msi_free( tv );
1662         return ERROR_FUNCTION_FAILED;
1663     }
1664
1665     TRACE("Table has %d columns\n", column_count);
1666
1667     /* fill the structure */
1668     tv->view.ops = &table_ops;
1669     tv->db = db;
1670     tv->columns = columns;
1671     tv->num_cols = column_count;
1672     tv->table = NULL;
1673     tv->row_size = msi_table_get_row_size( columns, column_count );
1674
1675     TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
1676
1677     *view = (MSIVIEW*) tv;
1678     lstrcpyW( tv->name, name );
1679
1680     return ERROR_SUCCESS;
1681 }
1682
1683 UINT MSI_CommitTables( MSIDATABASE *db )
1684 {
1685     UINT r;
1686     MSITABLE *table = NULL;
1687
1688     TRACE("%p\n",db);
1689
1690     r = save_string_table( db );
1691     if( r != ERROR_SUCCESS )
1692     {
1693         WARN("failed to save string table r=%08x\n",r);
1694         return r;
1695     }
1696
1697     LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
1698     {
1699         r = save_table( db, table );
1700         if( r != ERROR_SUCCESS )
1701         {
1702             WARN("failed to save table %s (r=%08x)\n",
1703                   debugstr_w(table->name), r);
1704             return r;
1705         }
1706     }
1707
1708     /* force everything to reload next time */
1709     free_cached_tables( db );
1710
1711     return ERROR_SUCCESS;
1712 }
1713
1714 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
1715 {
1716     if (!table)
1717         return MSICONDITION_ERROR;
1718
1719     return MSICONDITION_FALSE;
1720 }
1721
1722 static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
1723 {
1724     UINT i, val, ofs = 0;
1725     USHORT mask = *rawdata++;
1726     MSICOLUMNINFO *columns = tv->columns;
1727     MSIRECORD *rec;
1728
1729     rec = MSI_CreateRecord( tv->num_cols );
1730     if( !rec )
1731         return rec;
1732
1733     TRACE("row ->\n");
1734     for( i=0; i<tv->num_cols; i++ )
1735     {
1736         UINT n = bytes_per_column( &columns[i] );
1737
1738         if ( (mask&1) && (i>=(mask>>8)) )
1739             break;
1740         /* all keys must be present */
1741         if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
1742             continue;
1743
1744         switch( n )
1745         {
1746         case 2:
1747             val = rawdata[ofs];
1748             if( (columns[i].type & MSITYPE_STRING) &&
1749                 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1750             {
1751                 LPCWSTR sval = msi_string_lookup_id( st, val );
1752                 MSI_RecordSetStringW( rec, i+1, sval );
1753                 TRACE(" field %d [%s]\n", i+1, debugstr_w(sval));
1754             }
1755             else
1756             {
1757                 if (val)
1758                     MSI_RecordSetInteger( rec, i+1, val^0x8000 );
1759                 TRACE(" field %d [0x%04x]\n", i+1, val );
1760             }
1761             break;
1762         case 4:
1763             val = (rawdata[ofs] + (rawdata[ofs + 1]<<16));
1764             if (val)
1765                 MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
1766             TRACE(" field %d [0x%08x]\n", i+1, val );
1767             break;
1768         default:
1769             ERR("oops - unknown column width %d\n", n);
1770             break;
1771         }
1772         ofs += n/2;
1773     }
1774     return rec;
1775 }
1776
1777 static void dump_record( MSIRECORD *rec )
1778 {
1779     UINT i, n;
1780
1781     n = MSI_RecordGetFieldCount( rec );
1782     for( i=1; i<=n; i++ )
1783     {
1784         LPCWSTR sval = MSI_RecordGetString( rec, i );
1785
1786         if( MSI_RecordIsNull( rec, i ) )
1787             TRACE("row -> []\n");
1788         else if( (sval = MSI_RecordGetString( rec, i )) )
1789             TRACE("row -> [%s]\n", debugstr_w(sval));
1790         else
1791             TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) );
1792     }
1793 }
1794
1795 static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
1796 {
1797     LPCWSTR sval;
1798     UINT i;
1799
1800     for( i=0; i<(rawsize/2); i++ )
1801     {
1802         sval = msi_string_lookup_id( st, rawdata[i] );
1803         MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
1804     }
1805 }
1806
1807 static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
1808 {
1809     LPCWSTR str;
1810     UINT i, r, *data;
1811
1812     data = msi_alloc( tv->num_cols *sizeof (UINT) );
1813     for( i=0; i<tv->num_cols; i++ )
1814     {
1815         data[i] = 0;
1816
1817         if ( ~tv->columns[i].type & MSITYPE_KEY )
1818             continue;
1819
1820         /* turn the transform column value into a row value */
1821         if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
1822              ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1823         {
1824             str = MSI_RecordGetString( rec, i+1 );
1825             r = msi_string2idW( tv->db->strings, str, &data[i] );
1826
1827             /* if there's no matching string in the string table,
1828                these keys can't match any record, so fail now. */
1829             if( ERROR_SUCCESS != r )
1830             {
1831                 msi_free( data );
1832                 return NULL;
1833             }
1834         }
1835         else
1836         {
1837             data[i] = MSI_RecordGetInteger( rec, i+1 );
1838             if ((tv->columns[i].type&0xff) == 2)
1839                 data[i] += 0x8000;
1840             else
1841                 data[i] += 0x80000000;
1842         }
1843     }
1844     return data;
1845 }
1846
1847 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
1848 {
1849     UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
1850
1851     for( i=0; i<tv->num_cols; i++ )
1852     {
1853         if ( ~tv->columns[i].type & MSITYPE_KEY )
1854             continue;
1855
1856         /* turn the transform column value into a row value */
1857         r = TABLE_fetch_int( &tv->view, row, i+1, &x );
1858         if ( r != ERROR_SUCCESS )
1859         {
1860             ERR("TABLE_fetch_int shouldn't fail here\n");
1861             break;
1862         }
1863
1864         /* if this key matches, move to the next column */
1865         if ( x != data[i] )
1866         {
1867             ret = ERROR_FUNCTION_FAILED;
1868             break;
1869         }
1870
1871         ret = ERROR_SUCCESS;
1872     }
1873
1874     return ret;
1875 }
1876
1877 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
1878 {
1879     UINT i, r = ERROR_FUNCTION_FAILED, *data;
1880
1881     data = msi_record_to_row( tv, rec );
1882     if( !data )
1883         return r;
1884     for( i=0; i<tv->table->row_count; i++ )
1885     {
1886         r = msi_row_matches( tv, i, data );
1887         if( r == ERROR_SUCCESS )
1888         {
1889             *row = i;
1890             break;
1891         }
1892     }
1893     msi_free( data );
1894     return r;
1895 }
1896
1897 static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
1898 {
1899     UINT i;
1900
1901     for( i=1; i<=tv->num_cols; i++ )
1902         TABLE_set_int( tv, row, i, 0 );
1903     return ERROR_SUCCESS;
1904 }
1905
1906 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
1907                                       string_table *st, LPCWSTR name )
1908 {
1909     UINT rawsize = 0;
1910     USHORT *rawdata = NULL;
1911     MSITABLEVIEW *tv = NULL;
1912     UINT r, n, sz, i, mask;
1913     MSIRECORD *rec = NULL;
1914     UINT colcol = 0;
1915     WCHAR coltable[32];
1916
1917     coltable[0] = 0;
1918     TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
1919
1920     /* read the transform data */
1921     read_stream_data( stg, name, &rawdata, &rawsize );
1922     if ( !rawdata )
1923     {
1924         TRACE("table %s empty\n", debugstr_w(name) );
1925         return ERROR_INVALID_TABLE;
1926     }
1927
1928     /* create a table view */
1929     r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
1930     if( r != ERROR_SUCCESS )
1931         goto err;
1932
1933     r = tv->view.ops->execute( &tv->view, NULL );
1934     if( r != ERROR_SUCCESS )
1935         goto err;
1936
1937     TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
1938           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
1939
1940     /* interpret the data */
1941     r = ERROR_SUCCESS;
1942     for( n=0; n < (rawsize/2);  )
1943     {
1944         mask = rawdata[n];
1945
1946         if (mask&1)
1947         {
1948             /*
1949              * if the low bit is set, columns are continuous and
1950              * the number of columns is specified in the high byte
1951              */
1952             sz = 2 + tv->row_size;
1953         }
1954         else
1955         {
1956             /*
1957              * If the low bit is not set, rowdata[n] is a bitmask.
1958              * Excepting for key fields, which are always present,
1959              *  each bit indicates that a field is present in the transform record.
1960              *
1961              * rawdata[n] == 0 is a special case ... only the keys will be present
1962              * and it means that this row should be deleted.
1963              */
1964             sz = 2;
1965             for( i=0; i<tv->num_cols; i++ )
1966             {
1967                 if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
1968                     sz += bytes_per_column( &tv->columns[i] );
1969             }
1970         }
1971
1972         /* check we didn't run of the end of the table */
1973         if ( (n+sz) > rawsize )
1974         {
1975             ERR("borked.\n");
1976             dump_table( st, rawdata, rawsize );
1977             break;
1978         }
1979
1980         rec = msi_get_transform_record( tv, st, &rawdata[n] );
1981         if (rec)
1982         {
1983             if ( mask & 1 )
1984             {
1985                 TRACE("inserting record\n");
1986
1987                 /*
1988                  * Native msi seems writes nul into the
1989                  * Number (2nd) column of the _Columns table.
1990                  * Not sure that it's deliberate...
1991                  */
1992                 if (!lstrcmpW(name, szColumns))
1993                 {
1994                     WCHAR table[32];
1995                     DWORD sz = 32;
1996
1997                     MSI_RecordGetStringW( rec, 1, table, &sz );
1998
1999                     /* reset the column number on a new table */
2000                     if ( lstrcmpW(coltable, table) )
2001                     {
2002                         colcol = 0;
2003                         lstrcpyW( coltable, table );
2004                     }
2005
2006                     /* fix nul column numbers */
2007                     MSI_RecordSetInteger( rec, 2, ++colcol );
2008                 }
2009
2010                 r = TABLE_insert_row( &tv->view, rec );
2011                 if (r != ERROR_SUCCESS)
2012                     ERR("insert row failed\n");
2013             }
2014             else
2015             {
2016                 UINT row = 0;
2017
2018                 r = msi_table_find_row( tv, rec, &row );
2019                 if (r != ERROR_SUCCESS)
2020                     ERR("no matching row to transform\n");
2021                 else if ( mask )
2022                 {
2023                     TRACE("modifying row [%d]:\n", row);
2024                     TABLE_set_row( &tv->view, row, rec, mask );
2025                 }
2026                 else
2027                 {
2028                     TRACE("deleting row [%d]:\n", row);
2029                     msi_delete_row( tv, row );
2030                 }
2031             }
2032             if( TRACE_ON(msidb) ) dump_record( rec );
2033             msiobj_release( &rec->hdr );
2034         }
2035
2036         n += sz/2;
2037     }
2038
2039 err:
2040     /* no need to free the table, it's associated with the database */
2041     msi_free( rawdata );
2042     if( tv )
2043         tv->view.ops->delete( &tv->view );
2044
2045     return ERROR_SUCCESS;
2046 }
2047
2048 /*
2049  * msi_table_apply_transform
2050  *
2051  * Enumerate the table transforms in a transform storage and apply each one.
2052  */
2053 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
2054 {
2055     IEnumSTATSTG *stgenum = NULL;
2056     HRESULT r;
2057     STATSTG stat;
2058     ULONG count;
2059     WCHAR name[0x40];
2060     string_table *strings;
2061     UINT ret = ERROR_FUNCTION_FAILED;
2062
2063     TRACE("%p %p\n", db, stg );
2064
2065     strings = load_string_table( stg );
2066     if( !strings )
2067         goto end;
2068
2069     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
2070     if( FAILED( r ) )
2071         goto end;
2072
2073     /*
2074      * Apply _Tables and _Columns transforms first so that
2075      * the table metadata is correct, and empty tables exist.
2076      */
2077     ret = msi_table_load_transform( db, stg, strings, szTables );
2078     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2079         goto end;
2080
2081     ret = msi_table_load_transform( db, stg, strings, szColumns );
2082     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2083         goto end;
2084
2085     ret = ERROR_SUCCESS;
2086
2087     while( r == ERROR_SUCCESS )
2088     {
2089         count = 0;
2090         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
2091         if( FAILED( r ) || !count )
2092             break;
2093
2094         decode_streamname( stat.pwcsName, name );
2095         if ( name[0] != 0x4840 )
2096             continue;
2097
2098         TRACE("transform contains stream %s\n", debugstr_w(name));
2099
2100         if ( !lstrcmpW( name+1, szStringPool ) ||
2101              !lstrcmpW( name+1, szStringData ) ||
2102              !lstrcmpW( name+1, szColumns ) ||
2103              !lstrcmpW( name+1, szTables ) )
2104             continue;
2105
2106         ret = msi_table_load_transform( db, stg, strings, name+1 );
2107     }
2108
2109     if ( ret == ERROR_SUCCESS )
2110         append_storage_to_db( db, stg );
2111
2112 end:
2113     if ( stgenum )
2114         IEnumSTATSTG_Release( stgenum );
2115     if ( strings )
2116         msi_destroy_stringtable( strings );
2117
2118     return ret;
2119 }
2120
2121 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
2122 {
2123     MSITRANSFORM *t;
2124
2125     t = msi_alloc( sizeof *t );
2126     t->stg = stg;
2127     IStorage_AddRef( stg );
2128     list_add_tail( &db->transforms, &t->entry );
2129 }
2130
2131 void msi_free_transforms( MSIDATABASE *db )
2132 {
2133     while( !list_empty( &db->transforms ) )
2134     {
2135         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
2136                                       MSITRANSFORM, entry );
2137         list_remove( &t->entry );
2138         IStorage_Release( t->stg );
2139         msi_free( t );
2140     }
2141 }