wininet: Don't delete the cache file when closing a request.
[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 #define LONG_STR_BYTES 3
46
47 typedef struct tagMSICOLUMNHASHENTRY
48 {
49     struct tagMSICOLUMNHASHENTRY *next;
50     UINT value;
51     UINT row;
52 } MSICOLUMNHASHENTRY;
53
54 typedef struct tagMSICOLUMNINFO
55 {
56     LPWSTR tablename;
57     UINT   number;
58     LPWSTR colname;
59     UINT   type;
60     UINT   offset;
61     INT    ref_count;
62     BOOL   temporary;
63     MSICOLUMNHASHENTRY **hash_table;
64 } MSICOLUMNINFO;
65
66 typedef struct tagMSIORDERINFO
67 {
68     UINT *reorder;
69     UINT num_cols;
70     UINT cols[1];
71 } MSIORDERINFO;
72
73 struct tagMSITABLE
74 {
75     BYTE **data;
76     BOOL *data_persistent;
77     UINT row_count;
78     struct list entry;
79     MSICOLUMNINFO *colinfo;
80     UINT col_count;
81     MSICONDITION persistent;
82     INT ref_count;
83     WCHAR name[1];
84 };
85
86 typedef struct tagMSITRANSFORM {
87     struct list entry;
88     IStorage *stg;
89 } MSITRANSFORM;
90
91 static const WCHAR szStringData[] = {
92     '_','S','t','r','i','n','g','D','a','t','a',0 };
93 static const WCHAR szStringPool[] = {
94     '_','S','t','r','i','n','g','P','o','o','l',0 };
95
96 /* information for default tables */
97 static WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
98 static WCHAR szTable[]  = { 'T','a','b','l','e',0 };
99 static WCHAR szName[]    = { 'N','a','m','e',0 };
100 static WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
101 static WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
102 static WCHAR szType[]    = { 'T','y','p','e',0 };
103
104 static const MSICOLUMNINFO _Columns_cols[4] = {
105     { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
106     { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2,     2, 0, 0, NULL },
107     { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, 0, NULL },
108     { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6, 0, 0, NULL },
109 };
110
111 static const MSICOLUMNINFO _Tables_cols[1] = {
112     { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
113 };
114
115 #define MAX_STREAM_NAME 0x1f
116
117 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
118        MSICOLUMNINFO **pcols, UINT *pcount );
119 static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo,
120        DWORD count );
121 static UINT get_tablecolumns( MSIDATABASE *db,
122        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
123 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
124 static UINT table_find_insert_idx (MSIVIEW *view, LPCWSTR name, INT *pidx);
125
126 static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col )
127 {
128     if( MSITYPE_IS_BINARY(col->type) )
129         return 2;
130     if( col->type & MSITYPE_STRING )
131         return db->bytes_per_strref;
132     if( (col->type & 0xff) > 4 )
133         ERR("Invalid column size!\n");
134     return col->type & 0xff;
135 }
136
137 static int utf2mime(int x)
138 {
139     if( (x>='0') && (x<='9') )
140         return x-'0';
141     if( (x>='A') && (x<='Z') )
142         return x-'A'+10;
143     if( (x>='a') && (x<='z') )
144         return x-'a'+10+26;
145     if( x=='.' )
146         return 10+26+26;
147     if( x=='_' )
148         return 10+26+26+1;
149     return -1;
150 }
151
152 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
153 {
154     DWORD count = MAX_STREAM_NAME;
155     DWORD ch, next;
156     LPWSTR out, p;
157
158     if( !bTable )
159         count = lstrlenW( in )+2;
160     out = msi_alloc( count*sizeof(WCHAR) );
161     p = out;
162
163     if( bTable )
164     {
165          *p++ = 0x4840;
166          count --;
167     }
168     while( count -- ) 
169     {
170         ch = *in++;
171         if( !ch )
172         {
173             *p = ch;
174             return out;
175         }
176         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
177         {
178             ch = utf2mime(ch) + 0x4800;
179             next = *in;
180             if( next && (next<0x80) )
181             {
182                 next = utf2mime(next);
183                 if( next != -1 )
184                 {
185                      next += 0x3ffffc0;
186                      ch += (next<<6);
187                      in++;
188                 }
189             }
190         }
191         *p++ = ch;
192     }
193     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
194     msi_free( out );
195     return NULL;
196 }
197
198 static int mime2utf(int x)
199 {
200     if( x<10 )
201         return x + '0';
202     if( x<(10+26))
203         return x - 10 + 'A';
204     if( x<(10+26+26))
205         return x - 10 - 26 + 'a';
206     if( x == (10+26+26) )
207         return '.';
208     return '_';
209 }
210
211 BOOL decode_streamname(LPCWSTR in, LPWSTR out)
212 {
213     WCHAR ch;
214     DWORD count = 0;
215
216     while ( (ch = *in++) )
217     {
218         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
219         {
220             if( ch >= 0x4800 )
221                 ch = mime2utf(ch-0x4800);
222             else
223             {
224                 ch -= 0x3800;
225                 *out++ = mime2utf(ch&0x3f);
226                 count++;
227                 ch = mime2utf((ch>>6)&0x3f);
228             }
229         }
230         *out++ = ch;
231         count++;
232     }
233     *out = 0;
234     return count;
235 }
236
237 void enum_stream_names( IStorage *stg )
238 {
239     IEnumSTATSTG *stgenum = NULL;
240     HRESULT r;
241     STATSTG stat;
242     ULONG n, count;
243     WCHAR name[0x40];
244
245     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
246     if( FAILED( r ) )
247         return;
248
249     n = 0;
250     while( 1 )
251     {
252         count = 0;
253         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
254         if( FAILED( r ) || !count )
255             break;
256         decode_streamname( stat.pwcsName, name );
257         TRACE("stream %2d -> %s %s\n", n,
258               debugstr_w(stat.pwcsName), debugstr_w(name) );
259         CoTaskMemFree( stat.pwcsName );
260         n++;
261     }
262
263     IEnumSTATSTG_Release( stgenum );
264 }
265
266 UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
267                        BYTE **pdata, UINT *psz )
268 {
269     HRESULT r;
270     UINT ret = ERROR_FUNCTION_FAILED;
271     VOID *data;
272     ULONG sz, count;
273     IStream *stm = NULL;
274     STATSTG stat;
275     LPWSTR encname;
276
277     encname = encode_streamname(table, stname);
278
279     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
280
281     r = IStorage_OpenStream(stg, encname, NULL, 
282             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
283     msi_free( encname );
284     if( FAILED( r ) )
285     {
286         WARN("open stream failed r = %08x - empty table?\n", r);
287         return ret;
288     }
289
290     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
291     if( FAILED( r ) )
292     {
293         WARN("open stream failed r = %08x!\n", r);
294         goto end;
295     }
296
297     if( stat.cbSize.QuadPart >> 32 )
298     {
299         WARN("Too big!\n");
300         goto end;
301     }
302         
303     sz = stat.cbSize.QuadPart;
304     data = msi_alloc( sz );
305     if( !data )
306     {
307         WARN("couldn't allocate memory r=%08x!\n", r);
308         ret = ERROR_NOT_ENOUGH_MEMORY;
309         goto end;
310     }
311         
312     r = IStream_Read(stm, data, sz, &count );
313     if( FAILED( r ) || ( count != sz ) )
314     {
315         msi_free( data );
316         WARN("read stream failed r = %08x!\n", r);
317         goto end;
318     }
319
320     *pdata = data;
321     *psz = sz;
322     ret = ERROR_SUCCESS;
323
324 end:
325     IStream_Release( stm );
326
327     return ret;
328 }
329
330 static UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
331 {
332     LPWSTR encname;
333     HRESULT r;
334
335     encname = encode_streamname(FALSE, stname);
336
337     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
338
339     r = IStorage_OpenStream(db->storage, encname, NULL, 
340             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
341     if( FAILED( r ) )
342     {
343         MSITRANSFORM *transform;
344
345         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
346         {
347             TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
348             r = IStorage_OpenStream( transform->stg, encname, NULL, 
349                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
350             if (SUCCEEDED(r))
351                 break;
352         }
353     }
354
355     msi_free( encname );
356
357     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
358 }
359
360 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
361                               USHORT **pdata, UINT *psz )
362 {
363     HRESULT r;
364     UINT ret = ERROR_FUNCTION_FAILED;
365     VOID *data;
366     ULONG sz, count;
367     IStream *stm = NULL;
368     STATSTG stat;
369
370     r = db_get_raw_stream( db, stname, &stm );
371     if( r != ERROR_SUCCESS)
372         return ret;
373     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
374     if( FAILED( r ) )
375     {
376         WARN("open stream failed r = %08x!\n", r);
377         goto end;
378     }
379
380     if( stat.cbSize.QuadPart >> 32 )
381     {
382         WARN("Too big!\n");
383         goto end;
384     }
385         
386     sz = stat.cbSize.QuadPart;
387     data = msi_alloc( sz );
388     if( !data )
389     {
390         WARN("couldn't allocate memory r=%08x!\n", r);
391         ret = ERROR_NOT_ENOUGH_MEMORY;
392         goto end;
393     }
394         
395     r = IStream_Read(stm, data, sz, &count );
396     if( FAILED( r ) || ( count != sz ) )
397     {
398         msi_free( data );
399         WARN("read stream failed r = %08x!\n", r);
400         goto end;
401     }
402
403     *pdata = data;
404     *psz = sz;
405     ret = ERROR_SUCCESS;
406
407 end:
408     IStream_Release( stm );
409
410     return ret;
411 }
412
413 UINT write_stream_data( IStorage *stg, LPCWSTR stname,
414                         LPCVOID data, UINT sz, BOOL bTable )
415 {
416     HRESULT r;
417     UINT ret = ERROR_FUNCTION_FAILED;
418     ULONG count;
419     IStream *stm = NULL;
420     ULARGE_INTEGER size;
421     LARGE_INTEGER pos;
422     LPWSTR encname;
423
424     encname = encode_streamname(bTable, stname );
425     r = IStorage_OpenStream( stg, encname, NULL, 
426             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
427     if( FAILED(r) )
428     {
429         r = IStorage_CreateStream( stg, encname,
430                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
431     }
432     msi_free( encname );
433     if( FAILED( r ) )
434     {
435         WARN("open stream failed r = %08x\n", r);
436         return ret;
437     }
438
439     size.QuadPart = sz;
440     r = IStream_SetSize( stm, size );
441     if( FAILED( r ) )
442     {
443         WARN("Failed to SetSize\n");
444         goto end;
445     }
446
447     pos.QuadPart = 0;
448     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
449     if( FAILED( r ) )
450     {
451         WARN("Failed to Seek\n");
452         goto end;
453     }
454
455     if (sz)
456     {
457         r = IStream_Write(stm, data, sz, &count );
458         if( FAILED( r ) || ( count != sz ) )
459         {
460             WARN("Failed to Write\n");
461             goto end;
462         }
463     }
464
465     ret = ERROR_SUCCESS;
466
467 end:
468     IStream_Release( stm );
469
470     return ret;
471 }
472
473 static void free_table( MSITABLE *table )
474 {
475     UINT i;
476     for( i=0; i<table->row_count; i++ )
477         msi_free( table->data[i] );
478     msi_free( table->data );
479     msi_free( table->data_persistent );
480     msi_free_colinfo( table->colinfo, table->col_count );
481     msi_free( table->colinfo );
482     msi_free( table );
483 }
484
485 static UINT msi_table_get_row_size( MSIDATABASE *db,const MSICOLUMNINFO *cols,
486                                     UINT count )
487 {
488     const MSICOLUMNINFO *last_col = &cols[count-1];
489     if (!count)
490         return 0;
491     return last_col->offset + bytes_per_column( db, last_col );
492 }
493
494 /* add this table to the list of cached tables in the database */
495 static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg )
496 {
497     BYTE *rawdata = NULL;
498     UINT rawsize = 0, i, j, row_size = 0;
499
500     TRACE("%s\n",debugstr_w(t->name));
501
502     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count );
503
504     /* if we can't read the table, just assume that it's empty */
505     read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
506     if( !rawdata )
507         return ERROR_SUCCESS;
508
509     TRACE("Read %d bytes\n", rawsize );
510
511     if( rawsize % row_size )
512     {
513         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
514         goto err;
515     }
516
517     t->row_count = rawsize / row_size;
518     t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
519     if( !t->data )
520         goto err;
521     t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL));
522     if ( !t->data_persistent )
523         goto err;
524
525     /* transpose all the data */
526     TRACE("Transposing data from %d rows\n", t->row_count );
527     for( i=0; i<t->row_count; i++ )
528     {
529         t->data[i] = msi_alloc( row_size );
530         if( !t->data[i] )
531             goto err;
532         t->data_persistent[i] = TRUE;
533
534         for( j=0; j<t->col_count; j++ )
535         {
536             UINT ofs = t->colinfo[j].offset;
537             UINT n = bytes_per_column( db, &t->colinfo[j] );
538             UINT k;
539
540             if ( n != 2 && n != 3 && n != 4 )
541             {
542                 ERR("oops - unknown column width %d\n", n);
543                 goto err;
544             }
545
546             for ( k = 0; k < n; k++ )
547                 t->data[i][ofs + k] = rawdata[ofs*t->row_count + i * n + k];
548         }
549     }
550
551     msi_free( rawdata );
552     return ERROR_SUCCESS;
553 err:
554     msi_free( rawdata );
555     return ERROR_FUNCTION_FAILED;
556 }
557
558 void free_cached_tables( MSIDATABASE *db )
559 {
560     while( !list_empty( &db->tables ) )
561     {
562         MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
563
564         list_remove( &t->entry );
565         free_table( t );
566     }
567 }
568
569 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
570 {
571     MSITABLE *t;
572
573     LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
574         if( !lstrcmpW( name, t->name ) )
575             return t;
576
577     return NULL;
578 }
579
580 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
581 {
582     UINT r, column_count = 0;
583     MSICOLUMNINFO *columns;
584
585     /* get the number of columns in this table */
586     column_count = 0;
587     r = get_tablecolumns( db, name, NULL, &column_count );
588     if( r != ERROR_SUCCESS )
589         return r;
590
591     *pcount = column_count;
592
593     /* if there's no columns, there's no table */
594     if( column_count == 0 )
595         return ERROR_INVALID_PARAMETER;
596
597     TRACE("Table %s found\n", debugstr_w(name) );
598
599     columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
600     if( !columns )
601         return ERROR_FUNCTION_FAILED;
602
603     r = get_tablecolumns( db, name, columns, &column_count );
604     if( r != ERROR_SUCCESS )
605     {
606         msi_free( columns );
607         return ERROR_FUNCTION_FAILED;
608     }
609
610     *pcols = columns;
611
612     return r;
613 }
614
615 UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
616                        MSICONDITION persistent, MSITABLE **table_ret)
617 {
618     UINT r, nField;
619     MSIVIEW *tv = NULL;
620     MSIRECORD *rec = NULL;
621     column_info *col;
622     MSITABLE *table;
623     UINT i;
624     INT idx;
625
626     /* only add tables that don't exist already */
627     if( TABLE_Exists(db, name ) )
628     {
629         WARN("table %s exists\n", debugstr_w(name));
630         return ERROR_BAD_QUERY_SYNTAX;
631     }
632
633     table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
634     if( !table )
635         return ERROR_FUNCTION_FAILED;
636
637     table->ref_count = 1;
638     table->row_count = 0;
639     table->data = NULL;
640     table->data_persistent = NULL;
641     table->colinfo = NULL;
642     table->col_count = 0;
643     table->persistent = persistent;
644     lstrcpyW( table->name, name );
645
646     for( col = col_info; col; col = col->next )
647         table->col_count++;
648
649     table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
650     if (!table->colinfo)
651     {
652         free_table( table );
653         return ERROR_FUNCTION_FAILED;
654     }
655
656     for( i = 0, col = col_info; col; i++, col = col->next )
657     {
658         table->colinfo[ i ].tablename = strdupW( col->table );
659         table->colinfo[ i ].number = i + 1;
660         table->colinfo[ i ].colname = strdupW( col->column );
661         table->colinfo[ i ].type = col->type;
662         table->colinfo[ i ].offset = 0;
663         table->colinfo[ i ].ref_count = 0;
664         table->colinfo[ i ].hash_table = NULL;
665         table->colinfo[ i ].temporary = col->temporary;
666     }
667     table_calc_column_offsets( db, table->colinfo, table->col_count);
668
669     r = TABLE_CreateView( db, szTables, &tv );
670     TRACE("CreateView returned %x\n", r);
671     if( r )
672     {
673         free_table( table );
674         return r;
675     }
676
677     r = tv->ops->execute( tv, 0 );
678     TRACE("tv execute returned %x\n", r);
679     if( r )
680         goto err;
681
682     rec = MSI_CreateRecord( 1 );
683     if( !rec )
684         goto err;
685
686     r = MSI_RecordSetStringW( rec, 1, name );
687     if( r )
688         goto err;
689
690     r = table_find_insert_idx (tv, name, &idx);
691     if (r != ERROR_SUCCESS)
692        idx = -1;
693
694     r = tv->ops->insert_row( tv, rec, idx, persistent == MSICONDITION_FALSE );
695     TRACE("insert_row returned %x\n", r);
696     if( r )
697         goto err;
698
699     tv->ops->delete( tv );
700     tv = NULL;
701
702     msiobj_release( &rec->hdr );
703     rec = NULL;
704
705     if( persistent != MSICONDITION_FALSE )
706     {
707         /* add each column to the _Columns table */
708         r = TABLE_CreateView( db, szColumns, &tv );
709         if( r )
710             return r;
711
712         r = tv->ops->execute( tv, 0 );
713         TRACE("tv execute returned %x\n", r);
714         if( r )
715             goto err;
716
717         rec = MSI_CreateRecord( 4 );
718         if( !rec )
719             goto err;
720
721         r = MSI_RecordSetStringW( rec, 1, name );
722         if( r )
723             goto err;
724
725         /*
726          * need to set the table, column number, col name and type
727          * for each column we enter in the table
728          */
729         nField = 1;
730         for( col = col_info; col; col = col->next )
731         {
732             r = MSI_RecordSetInteger( rec, 2, nField );
733             if( r )
734                 goto err;
735
736             r = MSI_RecordSetStringW( rec, 3, col->column );
737             if( r )
738                 goto err;
739
740             r = MSI_RecordSetInteger( rec, 4, col->type );
741             if( r )
742                 goto err;
743
744             r = table_find_insert_idx (tv, name, &idx);
745             if (r != ERROR_SUCCESS)
746                 idx = -1;
747
748             r = tv->ops->insert_row( tv, rec, idx, FALSE );
749             if( r )
750                 goto err;
751
752             nField++;
753         }
754         if( !col )
755             r = ERROR_SUCCESS;
756     }
757
758 err:
759     if (rec)
760         msiobj_release( &rec->hdr );
761     /* FIXME: remove values from the string table on error */
762     if( tv )
763         tv->ops->delete( tv );
764
765     if (r == ERROR_SUCCESS)
766     {
767         list_add_head( &db->tables, &table->entry );
768         *table_ret = table;
769     }
770     else
771         free_table( table );
772
773     return r;
774 }
775
776 static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
777 {
778     MSITABLE *table;
779     UINT r;
780
781     /* first, see if the table is cached */
782     table = find_cached_table( db, name );
783     if( table )
784     {
785         *table_ret = table;
786         return ERROR_SUCCESS;
787     }
788
789     /* nonexistent tables should be interpreted as empty tables */
790     table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
791     if( !table )
792         return ERROR_FUNCTION_FAILED;
793
794     table->row_count = 0;
795     table->data = NULL;
796     table->data_persistent = NULL;
797     table->colinfo = NULL;
798     table->col_count = 0;
799     table->persistent = MSICONDITION_TRUE;
800     lstrcpyW( table->name, name );
801
802     if ( !lstrcmpW(name, szTables) || !lstrcmpW(name, szColumns) )
803         table->persistent = MSICONDITION_NONE;
804
805     r = table_get_column_info( db, name, &table->colinfo, &table->col_count);
806     if (r != ERROR_SUCCESS)
807     {
808         free_table ( table );
809         return r;
810     }
811
812     r = read_table_from_storage( db, table, db->storage );
813     if( r != ERROR_SUCCESS )
814     {
815         free_table( table );
816         return r;
817     }
818
819     list_add_head( &db->tables, &table->entry );
820     *table_ret = table;
821     return ERROR_SUCCESS;
822 }
823
824 static UINT save_table( MSIDATABASE *db, const MSITABLE *t )
825 {
826     BYTE *rawdata = NULL, *p;
827     UINT rawsize, r, i, j, row_size;
828
829     /* Nothing to do for non-persistent tables */
830     if( t->persistent == MSICONDITION_FALSE )
831         return ERROR_SUCCESS;
832
833     TRACE("Saving %s\n", debugstr_w( t->name ) );
834
835     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count );
836
837     rawsize = t->row_count * row_size;
838     rawdata = msi_alloc_zero( rawsize );
839     if( !rawdata )
840     {
841         r = ERROR_NOT_ENOUGH_MEMORY;
842         goto err;
843     }
844
845     rawsize = 0;
846     p = rawdata;
847     for( i=0; i<t->col_count; i++ )
848     {
849         for( j=0; j<t->row_count; j++ )
850         {
851             UINT offset = t->colinfo[i].offset;
852
853             if (!t->data_persistent[j]) continue;
854             if (i == 0)
855                 rawsize += row_size;
856
857             *p++ = t->data[j][offset];
858             *p++ = t->data[j][offset + 1];
859             if( 4 == bytes_per_column( db, &t->colinfo[i] ) )
860             {
861                 *p++ = t->data[j][offset + 2];
862                 *p++ = t->data[j][offset + 3];
863             }
864         }
865     }
866
867     TRACE("writing %d bytes\n", rawsize);
868     r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
869
870 err:
871     msi_free( rawdata );
872
873     return r;
874 }
875
876 static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo,
877                                        DWORD count )
878 {
879     DWORD i;
880
881     for( i=0; colinfo && (i<count); i++ )
882     {
883          assert( (i+1) == colinfo[ i ].number );
884          if (i)
885              colinfo[i].offset = colinfo[ i - 1 ].offset
886                                + bytes_per_column( db, &colinfo[ i - 1 ] );
887          else
888              colinfo[i].offset = 0;
889          TRACE("column %d is [%s] with type %08x ofs %d\n",
890                colinfo[i].number, debugstr_w(colinfo[i].colname),
891                colinfo[i].type, colinfo[i].offset);
892     }
893 }
894
895 static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name,
896                                      MSICOLUMNINFO *colinfo, UINT *sz)
897 {
898     const MSICOLUMNINFO *p;
899     DWORD i, n;
900
901     TRACE("%s\n", debugstr_w(name));
902
903     if (!lstrcmpW( name, szTables ))
904     {
905         p = _Tables_cols;
906         n = 1;
907     }
908     else if (!lstrcmpW( name, szColumns ))
909     {
910         p = _Columns_cols;
911         n = 4;
912     }
913     else
914         return ERROR_FUNCTION_FAILED;
915
916     /* duplicate the string data so we can free it in msi_free_colinfo */
917     for (i=0; i<n; i++)
918     {
919         if (colinfo && (i < *sz) )
920         {
921             colinfo[i] = p[i];
922             colinfo[i].tablename = strdupW( p[i].tablename );
923             colinfo[i].colname = strdupW( p[i].colname );
924         }
925         if( colinfo && (i >= *sz) )
926             break;
927     }
928     table_calc_column_offsets( db, colinfo, n );
929     *sz = n;
930     return ERROR_SUCCESS;
931 }
932
933 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
934 {
935     UINT i;
936
937     for( i=0; i<count; i++ )
938     {
939         msi_free( colinfo[i].tablename );
940         msi_free( colinfo[i].colname );
941         msi_free( colinfo[i].hash_table );
942     }
943 }
944
945 static LPWSTR msi_makestring( const MSIDATABASE *db, UINT stringid)
946 {
947     return strdupW(msi_string_lookup_id( db->strings, stringid ));
948 }
949
950 static UINT read_table_int(BYTE *const *data, UINT row, UINT col, UINT bytes)
951 {
952     UINT ret = 0, i;
953
954     for (i = 0; i < bytes; i++)
955         ret += (data[row][col + i] << i * 8);
956
957     return ret;
958 }
959
960 static UINT get_tablecolumns( MSIDATABASE *db,
961        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
962 {
963     UINT r, i, n=0, table_id, count, maxcount = *sz;
964     MSITABLE *table = NULL;
965
966     TRACE("%s\n", debugstr_w(szTableName));
967
968     /* first check if there is a default table with that name */
969     r = get_defaulttablecolumns( db, szTableName, colinfo, sz );
970     if( ( r == ERROR_SUCCESS ) && *sz )
971         return r;
972
973     r = get_table( db, szColumns, &table );
974     if( r != ERROR_SUCCESS )
975     {
976         ERR("couldn't load _Columns table\n");
977         return ERROR_FUNCTION_FAILED;
978     }
979
980     /* convert table and column names to IDs from the string table */
981     r = msi_string2idW( db->strings, szTableName, &table_id );
982     if( r != ERROR_SUCCESS )
983     {
984         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
985         return r;
986     }
987
988     TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
989
990     /* Note: _Columns table doesn't have non-persistent data */
991
992     /* if maxcount is non-zero, assume it's exactly right for this table */
993     memset( colinfo, 0, maxcount*sizeof(*colinfo) );
994     count = table->row_count;
995     for( i=0; i<count; i++ )
996     {
997         if( read_table_int(table->data, i, 0, db->bytes_per_strref) != table_id )
998             continue;
999         if( colinfo )
1000         {
1001             UINT id = read_table_int(table->data, i, table->colinfo[2].offset, db->bytes_per_strref);
1002             UINT col = read_table_int(table->data, i, table->colinfo[1].offset, sizeof(USHORT)) - (1<<15);
1003
1004             /* check the column number is in range */
1005             if (col<1 || col>maxcount)
1006             {
1007                 ERR("column %d out of range\n", col);
1008                 continue;
1009             }
1010
1011             /* check if this column was already set */
1012             if (colinfo[ col - 1 ].number)
1013             {
1014                 ERR("duplicate column %d\n", col);
1015                 continue;
1016             }
1017
1018             colinfo[ col - 1 ].tablename = msi_makestring( db, table_id );
1019             colinfo[ col - 1 ].number = col;
1020             colinfo[ col - 1 ].colname = msi_makestring( db, id );
1021             colinfo[ col - 1 ].type = read_table_int(table->data, i,
1022                                                      table->colinfo[3].offset,
1023                                                      sizeof(USHORT)) - (1<<15);
1024             colinfo[ col - 1 ].offset = 0;
1025             colinfo[ col - 1 ].ref_count = 0;
1026             colinfo[ col - 1 ].hash_table = NULL;
1027         }
1028         n++;
1029     }
1030
1031     TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
1032
1033     if (colinfo && n != maxcount)
1034     {
1035         ERR("missing column in table %s\n", debugstr_w(szTableName));
1036         msi_free_colinfo(colinfo, maxcount );
1037         return ERROR_FUNCTION_FAILED;
1038     }
1039
1040     table_calc_column_offsets( db, colinfo, n );
1041     *sz = n;
1042
1043     return ERROR_SUCCESS;
1044 }
1045
1046 static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name )
1047 {
1048     MSITABLE *table;
1049     UINT size, offset, old_count;
1050     UINT n;
1051
1052     table = find_cached_table( db, name );
1053     old_count = table->col_count;
1054     msi_free( table->colinfo );
1055     table->colinfo = NULL;
1056
1057     table_get_column_info( db, name, &table->colinfo, &table->col_count );
1058     if (!table->col_count)
1059         return;
1060
1061     size = msi_table_get_row_size( db, table->colinfo, table->col_count );
1062     offset = table->colinfo[table->col_count - 1].offset;
1063
1064     for ( n = 0; n < table->row_count; n++ )
1065     {
1066         table->data[n] = msi_realloc( table->data[n], size );
1067         if (old_count < table->col_count)
1068             memset( &table->data[n][offset], 0, size - offset );
1069     }
1070 }
1071
1072 /* try to find the table name in the _Tables table */
1073 BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
1074 {
1075     UINT r, table_id = 0, i, count;
1076     MSITABLE *table = NULL;
1077
1078     static const WCHAR szStreams[] = {'_','S','t','r','e','a','m','s',0};
1079     static const WCHAR szStorages[] = {'_','S','t','o','r','a','g','e','s',0};
1080
1081     if( !lstrcmpW( name, szTables ) || !lstrcmpW( name, szColumns ) ||
1082         !lstrcmpW( name, szStreams ) || !lstrcmpW( name, szStorages ) )
1083         return TRUE;
1084
1085     r = msi_string2idW( db->strings, name, &table_id );
1086     if( r != ERROR_SUCCESS )
1087     {
1088         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1089         return FALSE;
1090     }
1091
1092     r = get_table( db, szTables, &table );
1093     if( r != ERROR_SUCCESS )
1094     {
1095         ERR("table %s not available\n", debugstr_w(szTables));
1096         return FALSE;
1097     }
1098
1099     count = table->row_count;
1100     for( i=0; i<count; i++ )
1101         if( table->data[ i ][ 0 ] == table_id )
1102             return TRUE;
1103
1104     return FALSE;
1105 }
1106
1107 /* below is the query interface to a table */
1108
1109 typedef struct tagMSITABLEVIEW
1110 {
1111     MSIVIEW        view;
1112     MSIDATABASE   *db;
1113     MSITABLE      *table;
1114     MSICOLUMNINFO *columns;
1115     MSIORDERINFO  *order;
1116     UINT           num_cols;
1117     UINT           row_size;
1118     WCHAR          name[1];
1119 } MSITABLEVIEW;
1120
1121 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1122 {
1123     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1124     UINT offset, n;
1125
1126     if( !tv->table )
1127         return ERROR_INVALID_PARAMETER;
1128
1129     if( (col==0) || (col>tv->num_cols) )
1130         return ERROR_INVALID_PARAMETER;
1131
1132     /* how many rows are there ? */
1133     if( row >= tv->table->row_count )
1134         return ERROR_NO_MORE_ITEMS;
1135
1136     if( tv->columns[col-1].offset >= tv->row_size )
1137     {
1138         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1139         ERR("%p %p\n", tv, tv->columns );
1140         return ERROR_FUNCTION_FAILED;
1141     }
1142
1143     if (tv->order)
1144         row = tv->order->reorder[row];
1145
1146     n = bytes_per_column( tv->db, &tv->columns[col-1] );
1147     if (n != 2 && n != 3 && n != 4)
1148     {
1149         ERR("oops! what is %d bytes per column?\n", n );
1150         return ERROR_FUNCTION_FAILED;
1151     }
1152
1153     offset = tv->columns[col-1].offset;
1154     *val = read_table_int(tv->table->data, row, offset, n);
1155
1156     /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1157
1158     return ERROR_SUCCESS;
1159 }
1160
1161 static UINT msi_stream_name( const MSITABLEVIEW *tv, UINT row, LPWSTR *pstname )
1162 {
1163     LPWSTR p, stname = NULL;
1164     UINT i, r, type, ival;
1165     DWORD len;
1166     LPCWSTR sval;
1167     MSIVIEW *view = (MSIVIEW *) tv;
1168
1169     TRACE("%p %d\n", tv, row);
1170
1171     len = lstrlenW( tv->name ) + 1;
1172     stname = msi_alloc( len*sizeof(WCHAR) );
1173     if ( !stname )
1174     {
1175        r = ERROR_OUTOFMEMORY;
1176        goto err;
1177     }
1178
1179     lstrcpyW( stname, tv->name );
1180
1181     for ( i = 0; i < tv->num_cols; i++ )
1182     {
1183         type = tv->columns[i].type;
1184         if ( type & MSITYPE_KEY )
1185         {
1186             static const WCHAR szDot[] = { '.', 0 };
1187
1188             r = TABLE_fetch_int( view, row, i+1, &ival );
1189             if ( r != ERROR_SUCCESS )
1190                 goto err;
1191
1192             if ( tv->columns[i].type & MSITYPE_STRING )
1193             {
1194                 sval = msi_string_lookup_id( tv->db->strings, ival );
1195                 if ( !sval )
1196                 {
1197                     r = ERROR_INVALID_PARAMETER;
1198                     goto err;
1199                 }
1200             }
1201             else
1202             {
1203                 static const WCHAR fmt[] = { '%','d',0 };
1204                 WCHAR number[0x20];
1205                 UINT n = bytes_per_column( tv->db, &tv->columns[i] );
1206
1207                 switch( n )
1208                 {
1209                 case 2:
1210                     sprintfW( number, fmt, ival^0x8000 );
1211                     break;
1212                 case 4:
1213                     sprintfW( number, fmt, ival^0x80000000 );
1214                     break;
1215                 default:
1216                     ERR( "oops - unknown column width %d\n", n );
1217                     r = ERROR_FUNCTION_FAILED;
1218                     goto err;
1219                 }
1220                 sval = number;
1221             }
1222
1223             len += lstrlenW( szDot ) + lstrlenW( sval );
1224             p = msi_realloc ( stname, len*sizeof(WCHAR) );
1225             if ( !p )
1226             {
1227                 r = ERROR_OUTOFMEMORY;
1228                 goto err;
1229             }
1230             stname = p;
1231
1232             lstrcatW( stname, szDot );
1233             lstrcatW( stname, sval );
1234         }
1235         else
1236            continue;
1237     }
1238
1239     *pstname = stname;
1240     return ERROR_SUCCESS;
1241
1242 err:
1243     msi_free( stname );
1244     *pstname = NULL;
1245     return r;
1246 }
1247
1248 /*
1249  * We need a special case for streams, as we need to reference column with
1250  * the name of the stream in the same table, and the table name
1251  * which may not be available at higher levels of the query
1252  */
1253 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1254 {
1255     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1256     UINT r;
1257     LPWSTR full_name = NULL;
1258
1259     if( !view->ops->fetch_int )
1260         return ERROR_INVALID_PARAMETER;
1261
1262     r = msi_stream_name( tv, row, &full_name );
1263     if ( r != ERROR_SUCCESS )
1264     {
1265         ERR("fetching stream, error = %d\n", r);
1266         return r;
1267     }
1268
1269     r = db_get_raw_stream( tv->db, full_name, stm );
1270     if( r )
1271         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1272     msi_free( full_name );
1273
1274     return r;
1275 }
1276
1277 static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1278 {
1279     UINT offset, n, i;
1280
1281     if( !tv->table )
1282         return ERROR_INVALID_PARAMETER;
1283
1284     if( (col==0) || (col>tv->num_cols) )
1285         return ERROR_INVALID_PARAMETER;
1286
1287     if( row >= tv->table->row_count )
1288         return ERROR_INVALID_PARAMETER;
1289
1290     if( tv->columns[col-1].offset >= tv->row_size )
1291     {
1292         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1293         ERR("%p %p\n", tv, tv->columns );
1294         return ERROR_FUNCTION_FAILED;
1295     }
1296
1297     msi_free( tv->columns[col-1].hash_table );
1298     tv->columns[col-1].hash_table = NULL;
1299
1300     n = bytes_per_column( tv->db, &tv->columns[col-1] );
1301     if ( n != 2 && n != 3 && n != 4 )
1302     {
1303         ERR("oops! what is %d bytes per column?\n", n );
1304         return ERROR_FUNCTION_FAILED;
1305     }
1306
1307     offset = tv->columns[col-1].offset;
1308     for ( i = 0; i < n; i++ )
1309         tv->table->data[row][offset + i] = (val >> i * 8) & 0xff;
1310
1311     return ERROR_SUCCESS;
1312 }
1313
1314 static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
1315 {
1316     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1317
1318     if (!tv->table)
1319         return ERROR_INVALID_PARAMETER;
1320
1321     if (tv->order)
1322         row = tv->order->reorder[row];
1323
1324     return msi_view_get_row(tv->db, view, row, rec);
1325 }
1326
1327 static UINT msi_addstreamW( MSIDATABASE *db, LPCWSTR name, IStream *data )
1328 {
1329     UINT r;
1330     MSIQUERY *query = NULL;
1331     MSIRECORD *rec = NULL;
1332
1333     static const WCHAR insert[] = {
1334        'I','N','S','E','R','T',' ','I','N','T','O',' ',
1335           '`','_','S','t','r','e','a','m','s','`',' ',
1336          '(','`','N','a','m','e','`',',',
1337              '`','D','a','t','a','`',')',' ',
1338          'V','A','L','U','E','S',' ','(','?',',','?',')',0};
1339
1340     TRACE("%p %s %p\n", db, debugstr_w(name), data);
1341
1342     rec = MSI_CreateRecord( 2 );
1343     if ( !rec )
1344         return ERROR_OUTOFMEMORY;
1345
1346     r = MSI_RecordSetStringW( rec, 1, name );
1347     if ( r != ERROR_SUCCESS )
1348        goto err;
1349
1350     r = MSI_RecordSetIStream( rec, 2, data );
1351     if ( r != ERROR_SUCCESS )
1352        goto err;
1353
1354     r = MSI_DatabaseOpenViewW( db, insert, &query );
1355     if ( r != ERROR_SUCCESS )
1356        goto err;
1357
1358     r = MSI_ViewExecute( query, rec );
1359
1360 err:
1361     msiobj_release( &query->hdr );
1362     msiobj_release( &rec->hdr );
1363
1364     return r;
1365 }
1366
1367 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1368 {
1369     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1370     UINT i, val, r = ERROR_SUCCESS;
1371
1372     if ( !tv->table )
1373         return ERROR_INVALID_PARAMETER;
1374
1375     /* test if any of the mask bits are invalid */
1376     if ( mask >= (1<<tv->num_cols) )
1377         return ERROR_INVALID_PARAMETER;
1378
1379     for ( i = 0; i < tv->num_cols; i++ )
1380     {
1381         BOOL persistent;
1382
1383         /* only update the fields specified in the mask */
1384         if ( !(mask&(1<<i)) )
1385             continue;
1386
1387         persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
1388                      (tv->table->data_persistent[row]);
1389         /* FIXME: should we allow updating keys? */
1390
1391         val = 0;
1392         if ( !MSI_RecordIsNull( rec, i + 1 ) )
1393         {
1394             if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1395             {
1396                 IStream *stm;
1397                 LPWSTR stname;
1398
1399                 r = MSI_RecordGetIStream( rec, i + 1, &stm );
1400                 if ( r != ERROR_SUCCESS )
1401                     return r;
1402
1403                 r = msi_stream_name( tv, row, &stname );
1404                 if ( r != ERROR_SUCCESS )
1405                 {
1406                     IStream_Release( stm );
1407                     return r;
1408                 }
1409
1410                 r = msi_addstreamW( tv->db, stname, stm );
1411                 IStream_Release( stm );
1412                 msi_free ( stname );
1413
1414                 if ( r != ERROR_SUCCESS )
1415                     return r;
1416
1417                 val = 1; /* refers to the first key column */
1418             }
1419             else if ( tv->columns[i].type & MSITYPE_STRING )
1420             {
1421                 LPCWSTR sval = MSI_RecordGetString( rec, i + 1 );
1422                 UINT ival, x;
1423
1424                 r = msi_string2idW(tv->db->strings, sval, &ival);
1425                 if (r == ERROR_SUCCESS)
1426                 {
1427                     TABLE_fetch_int(&tv->view, row, i + 1, &x);
1428                     if (ival == x)
1429                         continue;
1430                 }
1431
1432                 val = msi_addstringW( tv->db->strings, 0, sval, -1, 1,
1433                                       persistent ? StringPersistent : StringNonPersistent );
1434             }
1435             else if ( 2 == bytes_per_column( tv->db, &tv->columns[ i ] ) )
1436             {
1437                 val = 0x8000 + MSI_RecordGetInteger( rec, i + 1 );
1438                 if ( val & 0xffff0000 )
1439                 {
1440                     ERR("field %u value %d out of range\n", i+1, val - 0x8000 );
1441                     return ERROR_FUNCTION_FAILED;
1442                 }
1443             }
1444             else
1445             {
1446                 INT ival = MSI_RecordGetInteger( rec, i + 1 );
1447                 val = ival ^ 0x80000000;
1448             }
1449         }
1450
1451         r = TABLE_set_int( tv, row, i+1, val );
1452         if ( r != ERROR_SUCCESS )
1453             break;
1454     }
1455     return r;
1456 }
1457
1458 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1459 {
1460     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1461     BYTE **p, *row;
1462     BOOL *b;
1463     UINT sz;
1464     BYTE ***data_ptr;
1465     BOOL **data_persist_ptr;
1466     UINT *row_count;
1467
1468     TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1469
1470     if( !tv->table )
1471         return ERROR_INVALID_PARAMETER;
1472
1473     row = msi_alloc_zero( tv->row_size );
1474     if( !row )
1475         return ERROR_NOT_ENOUGH_MEMORY;
1476
1477     row_count = &tv->table->row_count;
1478     data_ptr = &tv->table->data;
1479     data_persist_ptr = &tv->table->data_persistent;
1480     if (*num == -1)
1481         *num = tv->table->row_count;
1482
1483     sz = (*row_count + 1) * sizeof (BYTE*);
1484     if( *data_ptr )
1485         p = msi_realloc( *data_ptr, sz );
1486     else
1487         p = msi_alloc( sz );
1488     if( !p )
1489     {
1490         msi_free( row );
1491         return ERROR_NOT_ENOUGH_MEMORY;
1492     }
1493
1494     sz = (*row_count + 1) * sizeof (BOOL);
1495     if( *data_persist_ptr )
1496         b = msi_realloc( *data_persist_ptr, sz );
1497     else
1498         b = msi_alloc( sz );
1499     if( !b )
1500     {
1501         msi_free( row );
1502         msi_free( p );
1503         return ERROR_NOT_ENOUGH_MEMORY;
1504     }
1505
1506     *data_ptr = p;
1507     (*data_ptr)[*row_count] = row;
1508
1509     *data_persist_ptr = b;
1510     (*data_persist_ptr)[*row_count] = !temporary;
1511
1512     (*row_count)++;
1513
1514     return ERROR_SUCCESS;
1515 }
1516
1517 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1518 {
1519     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1520
1521     TRACE("%p %p\n", tv, record);
1522
1523     TRACE("There are %d columns\n", tv->num_cols );
1524
1525     return ERROR_SUCCESS;
1526 }
1527
1528 static UINT TABLE_close( struct tagMSIVIEW *view )
1529 {
1530     TRACE("%p\n", view );
1531     
1532     return ERROR_SUCCESS;
1533 }
1534
1535 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1536 {
1537     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1538
1539     TRACE("%p %p %p\n", view, rows, cols );
1540
1541     if( cols )
1542         *cols = tv->num_cols;
1543     if( rows )
1544     {
1545         if( !tv->table )
1546             return ERROR_INVALID_PARAMETER;
1547         *rows = tv->table->row_count;
1548     }
1549
1550     return ERROR_SUCCESS;
1551 }
1552
1553 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1554                 UINT n, LPWSTR *name, UINT *type, BOOL *temporary )
1555 {
1556     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1557
1558     TRACE("%p %d %p %p\n", tv, n, name, type );
1559
1560     if( ( n == 0 ) || ( n > tv->num_cols ) )
1561         return ERROR_INVALID_PARAMETER;
1562
1563     if( name )
1564     {
1565         *name = strdupW( tv->columns[n-1].colname );
1566         if( !*name )
1567             return ERROR_FUNCTION_FAILED;
1568     }
1569
1570     if( type )
1571         *type = tv->columns[n-1].type;
1572
1573     if( temporary )
1574         *temporary = tv->columns[n-1].temporary;
1575
1576     return ERROR_SUCCESS;
1577 }
1578
1579 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
1580
1581 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
1582 {
1583     UINT r, row, i;
1584
1585     /* check there's no null values where they're not allowed */
1586     for( i = 0; i < tv->num_cols; i++ )
1587     {
1588         if ( tv->columns[i].type & MSITYPE_NULLABLE )
1589             continue;
1590
1591         if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1592             TRACE("skipping binary column\n");
1593         else if ( tv->columns[i].type & MSITYPE_STRING )
1594         {
1595             LPCWSTR str;
1596
1597             str = MSI_RecordGetString( rec, i+1 );
1598             if (str == NULL || str[0] == 0)
1599                 return ERROR_INVALID_DATA;
1600         }
1601         else
1602         {
1603             UINT n;
1604
1605             n = MSI_RecordGetInteger( rec, i+1 );
1606             if (n == MSI_NULL_INTEGER)
1607                 return ERROR_INVALID_DATA;
1608         }
1609     }
1610
1611     /* check there's no duplicate keys */
1612     r = msi_table_find_row( tv, rec, &row );
1613     if (r == ERROR_SUCCESS)
1614         return ERROR_FUNCTION_FAILED;
1615
1616     return ERROR_SUCCESS;
1617 }
1618
1619 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
1620 {
1621     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1622     UINT i, r;
1623
1624     TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1625
1626     /* check that the key is unique - can we find a matching row? */
1627     r = table_validate_new( tv, rec );
1628     if( r != ERROR_SUCCESS )
1629         return ERROR_FUNCTION_FAILED;
1630
1631     r = table_create_new_row( view, &row, temporary );
1632     TRACE("insert_row returned %08x\n", r);
1633     if( r != ERROR_SUCCESS )
1634         return r;
1635
1636     /* shift the rows to make room for the new row */
1637     for (i = tv->table->row_count - 1; i > row; i--)
1638     {
1639         memmove(&(tv->table->data[i][0]),
1640                 &(tv->table->data[i - 1][0]), tv->row_size);
1641         tv->table->data_persistent[i] = tv->table->data_persistent[i - 1];
1642     }
1643
1644     /* Re-set the persistence flag */
1645     tv->table->data_persistent[row] = !temporary;
1646     return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1647 }
1648
1649 static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row )
1650 {
1651     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1652     UINT r, num_rows, num_cols, i;
1653
1654     TRACE("%p %d\n", tv, row);
1655
1656     if ( !tv->table )
1657         return ERROR_INVALID_PARAMETER;
1658
1659     r = TABLE_get_dimensions( view, &num_rows, &num_cols );
1660     if ( r != ERROR_SUCCESS )
1661         return r;
1662
1663     if ( row >= num_rows )
1664         return ERROR_FUNCTION_FAILED;
1665
1666     num_rows = tv->table->row_count;
1667     tv->table->row_count--;
1668
1669     /* reset the hash tables */
1670     for (i = 0; i < tv->num_cols; i++)
1671     {
1672         msi_free( tv->columns[i].hash_table );
1673         tv->columns[i].hash_table = NULL;
1674     }
1675
1676     if ( row == num_rows - 1 )
1677         return ERROR_SUCCESS;
1678
1679     for (i = row + 1; i < num_rows; i++)
1680     {
1681         memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size);
1682         tv->table->data_persistent[i - 1] = tv->table->data_persistent[i];
1683     }
1684
1685     return ERROR_SUCCESS;
1686 }
1687
1688 static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
1689 {
1690     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1691     UINT r, new_row;
1692
1693     /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1694      * sets the fetched record apart from other records
1695      */
1696
1697     if (!tv->table)
1698         return ERROR_INVALID_PARAMETER;
1699
1700     r = msi_table_find_row(tv, rec, &new_row);
1701     if (r != ERROR_SUCCESS)
1702     {
1703         ERR("can't find row to modify\n");
1704         return ERROR_FUNCTION_FAILED;
1705     }
1706
1707     /* the row cannot be changed */
1708     if (row != new_row + 1)
1709         return ERROR_FUNCTION_FAILED;
1710
1711     return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
1712 }
1713
1714 static UINT modify_delete_row( struct tagMSIVIEW *view, MSIRECORD *rec )
1715 {
1716     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1717     UINT row, r;
1718
1719     r = msi_table_find_row(tv, rec, &row);
1720     if (r != ERROR_SUCCESS)
1721         return r;
1722
1723     return TABLE_delete_row(view, row);
1724 }
1725
1726 static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row )
1727 {
1728     MSIRECORD *curr;
1729     UINT r, i, count;
1730
1731     r = TABLE_get_row(view, row - 1, &curr);
1732     if (r != ERROR_SUCCESS)
1733         return r;
1734
1735     count = MSI_RecordGetFieldCount(rec);
1736     for (i = 0; i < count; i++)
1737         MSI_RecordCopyField(curr, i + 1, rec, i + 1);
1738
1739     msiobj_release(&curr->hdr);
1740     return ERROR_SUCCESS;
1741 }
1742
1743 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1744                           MSIRECORD *rec, UINT row)
1745 {
1746     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1747     UINT r;
1748
1749     TRACE("%p %d %p\n", view, eModifyMode, rec );
1750
1751     switch (eModifyMode)
1752     {
1753     case MSIMODIFY_DELETE:
1754         r = modify_delete_row( view, rec );
1755         break;
1756     case MSIMODIFY_VALIDATE_NEW:
1757         r = table_validate_new( tv, rec );
1758         break;
1759
1760     case MSIMODIFY_INSERT:
1761         r = table_validate_new( tv, rec );
1762         if (r != ERROR_SUCCESS)
1763             break;
1764         r = TABLE_insert_row( view, rec, -1, FALSE );
1765         break;
1766
1767     case MSIMODIFY_INSERT_TEMPORARY:
1768         r = table_validate_new( tv, rec );
1769         if (r != ERROR_SUCCESS)
1770             break;
1771         r = TABLE_insert_row( view, rec, -1, TRUE );
1772         break;
1773
1774     case MSIMODIFY_REFRESH:
1775         r = msi_refresh_record( view, rec, row );
1776         break;
1777
1778     case MSIMODIFY_UPDATE:
1779         r = msi_table_update( view, rec, row );
1780         break;
1781
1782     case MSIMODIFY_ASSIGN:
1783     case MSIMODIFY_REPLACE:
1784     case MSIMODIFY_MERGE:
1785     case MSIMODIFY_VALIDATE:
1786     case MSIMODIFY_VALIDATE_FIELD:
1787     case MSIMODIFY_VALIDATE_DELETE:
1788         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1789         r = ERROR_CALL_NOT_IMPLEMENTED;
1790         break;
1791
1792     default:
1793         r = ERROR_INVALID_DATA;
1794     }
1795
1796     return r;
1797 }
1798
1799 static UINT TABLE_delete( struct tagMSIVIEW *view )
1800 {
1801     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1802
1803     TRACE("%p\n", view );
1804
1805     tv->table = NULL;
1806     tv->columns = NULL;
1807
1808     if (tv->order)
1809     {
1810         msi_free( tv->order->reorder );
1811         msi_free( tv->order );
1812         tv->order = NULL;
1813     }
1814
1815     msi_free( tv );
1816
1817     return ERROR_SUCCESS;
1818 }
1819
1820 static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
1821     UINT val, UINT *row, MSIITERHANDLE *handle )
1822 {
1823     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1824     const MSICOLUMNHASHENTRY *entry;
1825
1826     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
1827
1828     if( !tv->table )
1829         return ERROR_INVALID_PARAMETER;
1830
1831     if( (col==0) || (col > tv->num_cols) )
1832         return ERROR_INVALID_PARAMETER;
1833
1834     if( !tv->columns[col-1].hash_table )
1835     {
1836         UINT i;
1837         UINT num_rows = tv->table->row_count;
1838         MSICOLUMNHASHENTRY **hash_table;
1839         MSICOLUMNHASHENTRY *new_entry;
1840
1841         if( tv->columns[col-1].offset >= tv->row_size )
1842         {
1843             ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1844             ERR("%p %p\n", tv, tv->columns );
1845             return ERROR_FUNCTION_FAILED;
1846         }
1847
1848         /* allocate contiguous memory for the table and its entries so we
1849          * don't have to do an expensive cleanup */
1850         hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) +
1851             num_rows * sizeof(MSICOLUMNHASHENTRY));
1852         if (!hash_table)
1853             return ERROR_OUTOFMEMORY;
1854
1855         memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*));
1856         tv->columns[col-1].hash_table = hash_table;
1857
1858         new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE);
1859
1860         for (i = 0; i < num_rows; i++, new_entry++)
1861         {
1862             UINT row_value;
1863
1864             if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS)
1865                 continue;
1866
1867             new_entry->next = NULL;
1868             new_entry->value = row_value;
1869             new_entry->row = i;
1870             if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE])
1871             {
1872                 MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE];
1873                 while (prev_entry->next)
1874                     prev_entry = prev_entry->next;
1875                 prev_entry->next = new_entry;
1876             }
1877             else
1878                 hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry;
1879         }
1880     }
1881
1882     if( !*handle )
1883         entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE];
1884     else
1885         entry = (*handle)->next;
1886
1887     while (entry && entry->value != val)
1888         entry = entry->next;
1889
1890     *handle = entry;
1891     if (!entry)
1892         return ERROR_NO_MORE_ITEMS;
1893
1894     *row = entry->row;
1895
1896     return ERROR_SUCCESS;
1897 }
1898
1899 static UINT TABLE_add_ref(struct tagMSIVIEW *view)
1900 {
1901     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1902     UINT i;
1903
1904     TRACE("%p %d\n", view, tv->table->ref_count);
1905
1906     for (i = 0; i < tv->table->col_count; i++)
1907     {
1908         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
1909             InterlockedIncrement(&tv->table->colinfo[i].ref_count);
1910     }
1911
1912     return InterlockedIncrement(&tv->table->ref_count);
1913 }
1914
1915 static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number)
1916 {
1917     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1918     MSIRECORD *rec = NULL;
1919     MSIVIEW *columns = NULL;
1920     UINT row, r;
1921
1922     rec = MSI_CreateRecord(2);
1923     if (!rec)
1924         return ERROR_OUTOFMEMORY;
1925
1926     MSI_RecordSetStringW(rec, 1, table);
1927     MSI_RecordSetInteger(rec, 2, number);
1928
1929     r = TABLE_CreateView(tv->db, szColumns, &columns);
1930     if (r != ERROR_SUCCESS)
1931         return r;
1932
1933     r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row);
1934     if (r != ERROR_SUCCESS)
1935         goto done;
1936
1937     r = TABLE_delete_row(columns, row);
1938     if (r != ERROR_SUCCESS)
1939         goto done;
1940
1941     msi_update_table_columns(tv->db, table);
1942
1943 done:
1944     msiobj_release(&rec->hdr);
1945     columns->ops->delete(columns);
1946     return r;
1947 }
1948
1949 static UINT TABLE_release(struct tagMSIVIEW *view)
1950 {
1951     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1952     INT ref = tv->table->ref_count;
1953     UINT i, r;
1954
1955     TRACE("%p %d\n", view, ref);
1956
1957     for (i = 0; i < tv->table->col_count; i++)
1958     {
1959         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
1960         {
1961             ref = InterlockedDecrement(&tv->table->colinfo[i].ref_count);
1962             if (ref == 0)
1963             {
1964                 r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
1965                                         tv->table->colinfo[i].number);
1966                 if (r != ERROR_SUCCESS)
1967                     break;
1968             }
1969         }
1970     }
1971
1972     ref = InterlockedDecrement(&tv->table->ref_count);
1973     if (ref == 0)
1974     {
1975         if (!tv->table->row_count)
1976         {
1977             list_remove(&tv->table->entry);
1978             free_table(tv->table);
1979             TABLE_delete(view);
1980         }
1981     }
1982
1983     return ref;
1984 }
1985
1986 static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number,
1987                              LPCWSTR column, UINT type, BOOL hold)
1988 {
1989     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1990     MSITABLE *msitable;
1991     MSIRECORD *rec;
1992     UINT r, i;
1993
1994     rec = MSI_CreateRecord(4);
1995     if (!rec)
1996         return ERROR_OUTOFMEMORY;
1997
1998     MSI_RecordSetStringW(rec, 1, table);
1999     MSI_RecordSetInteger(rec, 2, number);
2000     MSI_RecordSetStringW(rec, 3, column);
2001     MSI_RecordSetInteger(rec, 4, type);
2002
2003     r = TABLE_insert_row(&tv->view, rec, -1, FALSE);
2004     if (r != ERROR_SUCCESS)
2005         goto done;
2006
2007     msi_update_table_columns(tv->db, table);
2008
2009     if (!hold)
2010         goto done;
2011
2012     msitable = find_cached_table(tv->db, table);
2013     for (i = 0; i < msitable->col_count; i++)
2014     {
2015         if (!lstrcmpW(msitable->colinfo[i].colname, column))
2016         {
2017             InterlockedIncrement(&msitable->colinfo[i].ref_count);
2018             break;
2019         }
2020     }
2021
2022 done:
2023     msiobj_release(&rec->hdr);
2024     return r;
2025 }
2026
2027 static UINT order_add_column(struct tagMSIVIEW *view, MSIORDERINFO *order, LPCWSTR name)
2028 {
2029     UINT n, r, count;
2030
2031     r = TABLE_get_dimensions(view, NULL, &count);
2032     if (r != ERROR_SUCCESS)
2033         return r;
2034
2035     if (order->num_cols >= count)
2036         return ERROR_FUNCTION_FAILED;
2037
2038     r = VIEW_find_column(view, name, &n);
2039     if (r != ERROR_SUCCESS)
2040         return r;
2041
2042     order->cols[order->num_cols] = n;
2043     TRACE("Ordering by column %s (%d)\n", debugstr_w(name), n);
2044
2045     order->num_cols++;
2046
2047     return ERROR_SUCCESS;
2048 }
2049
2050 static UINT order_compare(struct tagMSIVIEW *view, MSIORDERINFO *order,
2051                           UINT a, UINT b, UINT *swap)
2052 {
2053     UINT r, i, a_val = 0, b_val = 0;
2054
2055     *swap = 0;
2056     for (i = 0; i < order->num_cols; i++)
2057     {
2058         r = TABLE_fetch_int(view, a, order->cols[i], &a_val);
2059         if (r != ERROR_SUCCESS)
2060             return r;
2061
2062         r = TABLE_fetch_int(view, b, order->cols[i], &b_val);
2063         if (r != ERROR_SUCCESS)
2064             return r;
2065
2066         if (a_val != b_val)
2067         {
2068             if (a_val > b_val)
2069                 *swap = 1;
2070             break;
2071         }
2072     }
2073
2074     return ERROR_SUCCESS;
2075 }
2076
2077 static UINT order_mergesort(struct tagMSIVIEW *view, MSIORDERINFO *order,
2078                             UINT left, UINT right)
2079 {
2080     UINT r, i, j, temp;
2081     UINT swap = 0, center = (left + right) / 2;
2082     UINT *array = order->reorder;
2083
2084     if (left == right)
2085         return ERROR_SUCCESS;
2086
2087     /* sort the left half */
2088     r = order_mergesort(view, order, left, center);
2089     if (r != ERROR_SUCCESS)
2090         return r;
2091
2092     /* sort the right half */
2093     r = order_mergesort(view, order, center + 1, right);
2094     if (r != ERROR_SUCCESS)
2095         return r;
2096
2097     for (i = left, j = center + 1; (i <= center) && (j <= right); i++)
2098     {
2099         r = order_compare(view, order, array[i], array[j], &swap);
2100         if (r != ERROR_SUCCESS)
2101             return r;
2102
2103         if (swap)
2104         {
2105             temp = array[j];
2106             memmove(&array[i + 1], &array[i], (j - i) * sizeof(UINT));
2107             array[i] = temp;
2108             j++;
2109             center++;
2110         }
2111     }
2112
2113     return ERROR_SUCCESS;
2114 }
2115
2116 static UINT order_verify(struct tagMSIVIEW *view, MSIORDERINFO *order, UINT num_rows)
2117 {
2118     UINT i, swap, r;
2119
2120     for (i = 1; i < num_rows; i++)
2121     {
2122         r = order_compare(view, order, order->reorder[i - 1],
2123                           order->reorder[i], &swap);
2124         if (r != ERROR_SUCCESS)
2125             return r;
2126
2127         if (!swap)
2128             continue;
2129
2130         ERR("Bad order! %d\n", i);
2131         return ERROR_FUNCTION_FAILED;
2132     }
2133
2134     return ERROR_SUCCESS;
2135 }
2136
2137 static UINT TABLE_sort(struct tagMSIVIEW *view, column_info *columns)
2138 {
2139     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
2140     MSIORDERINFO *order;
2141     column_info *ptr;
2142     UINT r, i;
2143     UINT rows, cols;
2144
2145     TRACE("sorting table %s\n", debugstr_w(tv->name));
2146
2147     r = TABLE_get_dimensions(view, &rows, &cols);
2148     if (r != ERROR_SUCCESS)
2149         return r;
2150
2151     if (rows == 0)
2152         return ERROR_SUCCESS;
2153
2154     order = msi_alloc_zero(sizeof(MSIORDERINFO) + sizeof(UINT) * cols);
2155     if (!order)
2156         return ERROR_OUTOFMEMORY;
2157
2158     for (ptr = columns; ptr; ptr = ptr->next)
2159         order_add_column(view, order, ptr->column);
2160
2161     order->reorder = msi_alloc(rows * sizeof(UINT));
2162     if (!order->reorder)
2163         return ERROR_OUTOFMEMORY;
2164
2165     for (i = 0; i < rows; i++)
2166         order->reorder[i] = i;
2167
2168     r = order_mergesort(view, order, 0, rows - 1);
2169     if (r != ERROR_SUCCESS)
2170         return r;
2171
2172     r = order_verify(view, order, rows);
2173     if (r != ERROR_SUCCESS)
2174         return r;
2175
2176     tv->order = order;
2177
2178     return ERROR_SUCCESS;
2179 }
2180
2181 static UINT TABLE_drop(struct tagMSIVIEW *view)
2182 {
2183     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2184     MSIVIEW *tables = NULL;
2185     MSIRECORD *rec = NULL;
2186     UINT r, row;
2187     INT i;
2188
2189     TRACE("dropping table %s\n", debugstr_w(tv->name));
2190
2191     for (i = tv->table->col_count - 1; i >= 0; i--)
2192     {
2193         r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
2194                                 tv->table->colinfo[i].number);
2195         if (r != ERROR_SUCCESS)
2196             return r;
2197     }
2198
2199     rec = MSI_CreateRecord(1);
2200     if (!rec)
2201         return ERROR_OUTOFMEMORY;
2202
2203     MSI_RecordSetStringW(rec, 1, tv->name);
2204
2205     r = TABLE_CreateView(tv->db, szTables, &tables);
2206     if (r != ERROR_SUCCESS)
2207         return r;
2208
2209     r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row);
2210     if (r != ERROR_SUCCESS)
2211         goto done;
2212
2213     r = TABLE_delete_row(tables, row);
2214     if (r != ERROR_SUCCESS)
2215         goto done;
2216
2217     list_remove(&tv->table->entry);
2218     free_table(tv->table);
2219     TABLE_delete(view);
2220
2221 done:
2222     msiobj_release(&rec->hdr);
2223     tables->ops->delete(tables);
2224
2225     return r;
2226 }
2227
2228 static const MSIVIEWOPS table_ops =
2229 {
2230     TABLE_fetch_int,
2231     TABLE_fetch_stream,
2232     TABLE_get_row,
2233     TABLE_set_row,
2234     TABLE_insert_row,
2235     TABLE_delete_row,
2236     TABLE_execute,
2237     TABLE_close,
2238     TABLE_get_dimensions,
2239     TABLE_get_column_info,
2240     TABLE_modify,
2241     TABLE_delete,
2242     TABLE_find_matching_rows,
2243     TABLE_add_ref,
2244     TABLE_release,
2245     TABLE_add_column,
2246     TABLE_remove_column,
2247     TABLE_sort,
2248     TABLE_drop,
2249 };
2250
2251 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
2252 {
2253     MSITABLEVIEW *tv ;
2254     UINT r, sz;
2255
2256     static const WCHAR Streams[] = {'_','S','t','r','e','a','m','s',0};
2257     static const WCHAR Storages[] = {'_','S','t','o','r','a','g','e','s',0};
2258
2259     TRACE("%p %s %p\n", db, debugstr_w(name), view );
2260
2261     if ( !lstrcmpW( name, Streams ) )
2262         return STREAMS_CreateView( db, view );
2263     else if ( !lstrcmpW( name, Storages ) )
2264         return STORAGES_CreateView( db, view );
2265
2266     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
2267     tv = msi_alloc_zero( sz );
2268     if( !tv )
2269         return ERROR_FUNCTION_FAILED;
2270
2271     r = get_table( db, name, &tv->table );
2272     if( r != ERROR_SUCCESS )
2273     {
2274         msi_free( tv );
2275         WARN("table not found\n");
2276         return r;
2277     }
2278
2279     TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
2280
2281     /* fill the structure */
2282     tv->view.ops = &table_ops;
2283     tv->db = db;
2284     tv->columns = tv->table->colinfo;
2285     tv->num_cols = tv->table->col_count;
2286     tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count );
2287
2288     TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
2289
2290     *view = (MSIVIEW*) tv;
2291     lstrcpyW( tv->name, name );
2292
2293     return ERROR_SUCCESS;
2294 }
2295
2296 UINT MSI_CommitTables( MSIDATABASE *db )
2297 {
2298     UINT r;
2299     MSITABLE *table = NULL;
2300
2301     TRACE("%p\n",db);
2302
2303     r = msi_save_string_table( db->strings, db->storage );
2304     if( r != ERROR_SUCCESS )
2305     {
2306         WARN("failed to save string table r=%08x\n",r);
2307         return r;
2308     }
2309
2310     LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
2311     {
2312         r = save_table( db, table );
2313         if( r != ERROR_SUCCESS )
2314         {
2315             WARN("failed to save table %s (r=%08x)\n",
2316                   debugstr_w(table->name), r);
2317             return r;
2318         }
2319     }
2320
2321     /* force everything to reload next time */
2322     free_cached_tables( db );
2323
2324     return ERROR_SUCCESS;
2325 }
2326
2327 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
2328 {
2329     MSITABLE *t;
2330     UINT r;
2331
2332     TRACE("%p %s\n", db, debugstr_w(table));
2333
2334     if (!table)
2335         return MSICONDITION_ERROR;
2336
2337     r = get_table( db, table, &t );
2338     if (r != ERROR_SUCCESS)
2339         return MSICONDITION_NONE;
2340
2341     return t->persistent;
2342 }
2343
2344 static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
2345 {
2346     UINT ret = 0, i;
2347
2348     for (i = 0; i < bytes; i++)
2349         ret += (data[col + i] << i * 8);
2350
2351     return ret;
2352 }
2353
2354 static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname )
2355 {
2356     static const WCHAR szDot[] = { '.', 0 };
2357     LPWSTR stname = NULL, sval, p;
2358     DWORD len;
2359     UINT i, r;
2360
2361     TRACE("%p %p\n", tv, rec);
2362
2363     len = lstrlenW( tv->name ) + 1;
2364     stname = msi_alloc( len*sizeof(WCHAR) );
2365     if ( !stname )
2366     {
2367        r = ERROR_OUTOFMEMORY;
2368        goto err;
2369     }
2370
2371     lstrcpyW( stname, tv->name );
2372
2373     for ( i = 0; i < tv->num_cols; i++ )
2374     {
2375         if ( tv->columns[i].type & MSITYPE_KEY )
2376         {
2377             sval = msi_dup_record_field( rec, i + 1 );
2378             if ( !sval )
2379             {
2380                 r = ERROR_OUTOFMEMORY;
2381                 goto err;
2382             }
2383
2384             len += lstrlenW( szDot ) + lstrlenW ( sval );
2385             p = msi_realloc ( stname, len*sizeof(WCHAR) );
2386             if ( !p )
2387             {
2388                 r = ERROR_OUTOFMEMORY;
2389                 goto err;
2390             }
2391             stname = p;
2392
2393             lstrcatW( stname, szDot );
2394             lstrcatW( stname, sval );
2395
2396             msi_free( sval );
2397         }
2398         else
2399             continue;
2400     }
2401
2402     *pstname = encode_streamname( FALSE, stname );
2403     msi_free( stname );
2404
2405     return ERROR_SUCCESS;
2406
2407 err:
2408     msi_free ( stname );
2409     *pstname = NULL;
2410     return r;
2411 }
2412
2413 static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st,
2414                                             IStorage *stg,
2415                                             const BYTE *rawdata, UINT bytes_per_strref )
2416 {
2417     UINT i, val, ofs = 0;
2418     USHORT mask;
2419     MSICOLUMNINFO *columns = tv->columns;
2420     MSIRECORD *rec;
2421
2422     mask = rawdata[0] | (rawdata[1] << 8);
2423     rawdata += 2;
2424
2425     rec = MSI_CreateRecord( tv->num_cols );
2426     if( !rec )
2427         return rec;
2428
2429     TRACE("row ->\n");
2430     for( i=0; i<tv->num_cols; i++ )
2431     {
2432         if ( (mask&1) && (i>=(mask>>8)) )
2433             break;
2434         /* all keys must be present */
2435         if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
2436             continue;
2437
2438         if( MSITYPE_IS_BINARY(tv->columns[i].type) )
2439         {
2440             LPWSTR encname;
2441             IStream *stm = NULL;
2442             UINT r;
2443
2444             ofs += bytes_per_column( tv->db, &columns[i] );
2445
2446             r = msi_record_encoded_stream_name( tv, rec, &encname );
2447             if ( r != ERROR_SUCCESS )
2448                 return NULL;
2449
2450             r = IStorage_OpenStream( stg, encname, NULL,
2451                      STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
2452             msi_free( encname );
2453             if ( r != ERROR_SUCCESS )
2454                 return NULL;
2455
2456             MSI_RecordSetStream( rec, i+1, stm );
2457             TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
2458         }
2459         else if( columns[i].type & MSITYPE_STRING )
2460         {
2461             LPCWSTR sval;
2462
2463             val = read_raw_int(rawdata, ofs, bytes_per_strref);
2464             sval = msi_string_lookup_id( st, val );
2465             MSI_RecordSetStringW( rec, i+1, sval );
2466             TRACE(" field %d [%s]\n", i+1, debugstr_w(sval));
2467             ofs += bytes_per_strref;
2468         }
2469         else
2470         {
2471             UINT n = bytes_per_column( tv->db, &columns[i] );
2472             switch( n )
2473             {
2474             case 2:
2475                 val = read_raw_int(rawdata, ofs, n);
2476                 if (val)
2477                     MSI_RecordSetInteger( rec, i+1, val^0x8000 );
2478                 TRACE(" field %d [0x%04x]\n", i+1, val );
2479                 break;
2480             case 4:
2481                 val = read_raw_int(rawdata, ofs, n);
2482                 if (val)
2483                     MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
2484                 TRACE(" field %d [0x%08x]\n", i+1, val );
2485                 break;
2486             default:
2487                 ERR("oops - unknown column width %d\n", n);
2488                 break;
2489             }
2490             ofs += n;
2491         }
2492     }
2493     return rec;
2494 }
2495
2496 static void dump_record( MSIRECORD *rec )
2497 {
2498     UINT i, n;
2499
2500     n = MSI_RecordGetFieldCount( rec );
2501     for( i=1; i<=n; i++ )
2502     {
2503         LPCWSTR sval = MSI_RecordGetString( rec, i );
2504
2505         if( MSI_RecordIsNull( rec, i ) )
2506             TRACE("row -> []\n");
2507         else if( (sval = MSI_RecordGetString( rec, i )) )
2508             TRACE("row -> [%s]\n", debugstr_w(sval));
2509         else
2510             TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) );
2511     }
2512 }
2513
2514 static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
2515 {
2516     LPCWSTR sval;
2517     UINT i;
2518
2519     for( i=0; i<(rawsize/2); i++ )
2520     {
2521         sval = msi_string_lookup_id( st, rawdata[i] );
2522         MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
2523     }
2524 }
2525
2526 static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec )
2527 {
2528     LPCWSTR str;
2529     UINT i, r, *data;
2530
2531     data = msi_alloc( tv->num_cols *sizeof (UINT) );
2532     for( i=0; i<tv->num_cols; i++ )
2533     {
2534         data[i] = 0;
2535
2536         if ( ~tv->columns[i].type & MSITYPE_KEY )
2537             continue;
2538
2539         /* turn the transform column value into a row value */
2540         if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
2541              ! MSITYPE_IS_BINARY(tv->columns[i].type) )
2542         {
2543             str = MSI_RecordGetString( rec, i+1 );
2544             r = msi_string2idW( tv->db->strings, str, &data[i] );
2545
2546             /* if there's no matching string in the string table,
2547                these keys can't match any record, so fail now. */
2548             if( ERROR_SUCCESS != r )
2549             {
2550                 msi_free( data );
2551                 return NULL;
2552             }
2553         }
2554         else
2555         {
2556             data[i] = MSI_RecordGetInteger( rec, i+1 );
2557
2558             if (data[i] == MSI_NULL_INTEGER)
2559                 data[i] = 0;
2560             else if ((tv->columns[i].type&0xff) == 2)
2561                 data[i] += 0x8000;
2562             else
2563                 data[i] += 0x80000000;
2564         }
2565     }
2566     return data;
2567 }
2568
2569 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data )
2570 {
2571     UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
2572
2573     for( i=0; i<tv->num_cols; i++ )
2574     {
2575         if ( ~tv->columns[i].type & MSITYPE_KEY )
2576             continue;
2577
2578         /* turn the transform column value into a row value */
2579         r = TABLE_fetch_int( &tv->view, row, i+1, &x );
2580         if ( r != ERROR_SUCCESS )
2581         {
2582             ERR("TABLE_fetch_int shouldn't fail here\n");
2583             break;
2584         }
2585
2586         /* if this key matches, move to the next column */
2587         if ( x != data[i] )
2588         {
2589             ret = ERROR_FUNCTION_FAILED;
2590             break;
2591         }
2592
2593         ret = ERROR_SUCCESS;
2594     }
2595
2596     return ret;
2597 }
2598
2599 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
2600 {
2601     UINT i, r = ERROR_FUNCTION_FAILED, *data;
2602
2603     data = msi_record_to_row( tv, rec );
2604     if( !data )
2605         return r;
2606     for( i = 0; i < tv->table->row_count; i++ )
2607     {
2608         r = msi_row_matches( tv, i, data );
2609         if( r == ERROR_SUCCESS )
2610         {
2611             *row = i;
2612             break;
2613         }
2614     }
2615     msi_free( data );
2616     return r;
2617 }
2618
2619 typedef struct
2620 {
2621     struct list entry;
2622     LPWSTR name;
2623 } TRANSFORMDATA;
2624
2625 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
2626                                       string_table *st, TRANSFORMDATA *transform,
2627                                       UINT bytes_per_strref )
2628 {
2629     UINT rawsize = 0;
2630     BYTE *rawdata = NULL;
2631     MSITABLEVIEW *tv = NULL;
2632     UINT r, n, sz, i, mask;
2633     MSIRECORD *rec = NULL;
2634     UINT colcol = 0;
2635     WCHAR coltable[32];
2636     LPWSTR name;
2637
2638     if (!transform)
2639         return ERROR_SUCCESS;
2640
2641     name = transform->name;
2642
2643     coltable[0] = 0;
2644     TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
2645
2646     /* read the transform data */
2647     read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
2648     if ( !rawdata )
2649     {
2650         TRACE("table %s empty\n", debugstr_w(name) );
2651         return ERROR_INVALID_TABLE;
2652     }
2653
2654     /* create a table view */
2655     r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
2656     if( r != ERROR_SUCCESS )
2657         goto err;
2658
2659     r = tv->view.ops->execute( &tv->view, NULL );
2660     if( r != ERROR_SUCCESS )
2661         goto err;
2662
2663     TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
2664           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
2665
2666     /* interpret the data */
2667     r = ERROR_SUCCESS;
2668     for( n=0; n < rawsize;  )
2669     {
2670         mask = rawdata[n] | (rawdata[n+1] << 8);
2671
2672         if (mask&1)
2673         {
2674             /*
2675              * if the low bit is set, columns are continuous and
2676              * the number of columns is specified in the high byte
2677              */
2678             sz = 2;
2679             for( i=0; i<tv->num_cols; i++ )
2680             {
2681                 if( (tv->columns[i].type & MSITYPE_STRING) &&
2682                     ! MSITYPE_IS_BINARY(tv->columns[i].type) )
2683                     sz += bytes_per_strref;
2684                 else
2685                     sz += bytes_per_column( tv->db, &tv->columns[i] );
2686             }
2687         }
2688         else
2689         {
2690             /*
2691              * If the low bit is not set, mask is a bitmask.
2692              * Excepting for key fields, which are always present,
2693              *  each bit indicates that a field is present in the transform record.
2694              *
2695              * mask == 0 is a special case ... only the keys will be present
2696              * and it means that this row should be deleted.
2697              */
2698             sz = 2;
2699             for( i=0; i<tv->num_cols; i++ )
2700             {
2701                 if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
2702                 {
2703                     if( (tv->columns[i].type & MSITYPE_STRING) &&
2704                         ! MSITYPE_IS_BINARY(tv->columns[i].type) )
2705                         sz += bytes_per_strref;
2706                     else
2707                         sz += bytes_per_column( tv->db, &tv->columns[i] );
2708                 }
2709             }
2710         }
2711
2712         /* check we didn't run of the end of the table */
2713         if ( (n+sz) > rawsize )
2714         {
2715             ERR("borked.\n");
2716             dump_table( st, (USHORT *)rawdata, rawsize );
2717             break;
2718         }
2719
2720         rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
2721         if (rec)
2722         {
2723             if ( mask & 1 )
2724             {
2725                 WCHAR table[32];
2726                 DWORD sz = 32;
2727                 UINT number = MSI_NULL_INTEGER;
2728
2729                 TRACE("inserting record\n");
2730
2731                 if (!lstrcmpW(name, szColumns))
2732                 {
2733                     MSI_RecordGetStringW( rec, 1, table, &sz );
2734                     number = MSI_RecordGetInteger( rec, 2 );
2735
2736                     /*
2737                      * Native msi seems writes nul into the Number (2nd) column of
2738                      * the _Columns table, only when the columns are from a new table
2739                      */
2740                     if ( number == MSI_NULL_INTEGER )
2741                     {
2742                         /* reset the column number on a new table */
2743                         if ( lstrcmpW(coltable, table) )
2744                         {
2745                             colcol = 0;
2746                             lstrcpyW( coltable, table );
2747                         }
2748
2749                         /* fix nul column numbers */
2750                         MSI_RecordSetInteger( rec, 2, ++colcol );
2751                     }
2752                 }
2753
2754                 r = TABLE_insert_row( &tv->view, rec, -1, FALSE );
2755                 if (r != ERROR_SUCCESS)
2756                     ERR("insert row failed\n");
2757
2758                 if ( number != MSI_NULL_INTEGER && !lstrcmpW(name, szColumns) )
2759                     msi_update_table_columns( db, table );
2760             }
2761             else
2762             {
2763                 UINT row = 0;
2764
2765                 r = msi_table_find_row( tv, rec, &row );
2766                 if (r != ERROR_SUCCESS)
2767                     ERR("no matching row to transform\n");
2768                 else if ( mask )
2769                 {
2770                     TRACE("modifying row [%d]:\n", row);
2771                     TABLE_set_row( &tv->view, row, rec, mask );
2772                 }
2773                 else
2774                 {
2775                     TRACE("deleting row [%d]:\n", row);
2776                     TABLE_delete_row( &tv->view, row );
2777                 }
2778             }
2779             if( TRACE_ON(msidb) ) dump_record( rec );
2780             msiobj_release( &rec->hdr );
2781         }
2782
2783         n += sz;
2784     }
2785
2786 err:
2787     /* no need to free the table, it's associated with the database */
2788     msi_free( rawdata );
2789     if( tv )
2790         tv->view.ops->delete( &tv->view );
2791
2792     return ERROR_SUCCESS;
2793 }
2794
2795 /*
2796  * msi_table_apply_transform
2797  *
2798  * Enumerate the table transforms in a transform storage and apply each one.
2799  */
2800 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
2801 {
2802     struct list transforms;
2803     IEnumSTATSTG *stgenum = NULL;
2804     TRANSFORMDATA *transform;
2805     TRANSFORMDATA *tables = NULL, *columns = NULL;
2806     HRESULT r;
2807     STATSTG stat;
2808     string_table *strings;
2809     UINT ret = ERROR_FUNCTION_FAILED;
2810     UINT bytes_per_strref;
2811
2812     TRACE("%p %p\n", db, stg );
2813
2814     strings = msi_load_string_table( stg, &bytes_per_strref );
2815     if( !strings )
2816         goto end;
2817
2818     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
2819     if( FAILED( r ) )
2820         goto end;
2821
2822     list_init(&transforms);
2823
2824     while ( TRUE )
2825     {
2826         MSITABLEVIEW *tv = NULL;
2827         WCHAR name[0x40];
2828         ULONG count = 0;
2829
2830         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
2831         if ( FAILED( r ) || !count )
2832             break;
2833
2834         decode_streamname( stat.pwcsName, name );
2835         CoTaskMemFree( stat.pwcsName );
2836         if ( name[0] != 0x4840 )
2837             continue;
2838
2839         if ( !lstrcmpW( name+1, szStringPool ) ||
2840              !lstrcmpW( name+1, szStringData ) )
2841             continue;
2842
2843         transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
2844         if ( !transform )
2845             break;
2846
2847         list_add_tail( &transforms, &transform->entry );
2848
2849         transform->name = strdupW( name + 1 );
2850
2851         if ( !lstrcmpW( transform->name, szTables ) )
2852             tables = transform;
2853         else if (!lstrcmpW( transform->name, szColumns ) )
2854             columns = transform;
2855
2856         TRACE("transform contains stream %s\n", debugstr_w(name));
2857
2858         /* load the table */
2859         r = TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv );
2860         if( r != ERROR_SUCCESS )
2861             continue;
2862
2863         r = tv->view.ops->execute( &tv->view, NULL );
2864         if( r != ERROR_SUCCESS )
2865         {
2866             tv->view.ops->delete( &tv->view );
2867             continue;
2868         }
2869
2870         tv->view.ops->delete( &tv->view );
2871     }
2872
2873     /*
2874      * Apply _Tables and _Columns transforms first so that
2875      * the table metadata is correct, and empty tables exist.
2876      */
2877     ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref );
2878     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2879         goto end;
2880
2881     ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref );
2882     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2883         goto end;
2884
2885     ret = ERROR_SUCCESS;
2886
2887     while ( !list_empty( &transforms ) )
2888     {
2889         transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
2890
2891         if ( lstrcmpW( transform->name, szColumns ) &&
2892              lstrcmpW( transform->name, szTables ) &&
2893              ret == ERROR_SUCCESS )
2894         {
2895             ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref );
2896         }
2897
2898         list_remove( &transform->entry );
2899         msi_free( transform->name );
2900         msi_free( transform );
2901     }
2902
2903     if ( ret == ERROR_SUCCESS )
2904         append_storage_to_db( db, stg );
2905
2906 end:
2907     if ( stgenum )
2908         IEnumSTATSTG_Release( stgenum );
2909     if ( strings )
2910         msi_destroy_stringtable( strings );
2911
2912     return ret;
2913 }
2914
2915 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
2916 {
2917     MSITRANSFORM *t;
2918
2919     t = msi_alloc( sizeof *t );
2920     t->stg = stg;
2921     IStorage_AddRef( stg );
2922     list_add_tail( &db->transforms, &t->entry );
2923 }
2924
2925 void msi_free_transforms( MSIDATABASE *db )
2926 {
2927     while( !list_empty( &db->transforms ) )
2928     {
2929         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
2930                                       MSITRANSFORM, entry );
2931         list_remove( &t->entry );
2932         IStorage_Release( t->stg );
2933         msi_free( t );
2934     }
2935 }
2936
2937 static UINT table_find_insert_idx (MSIVIEW *view, LPCWSTR name, INT *pidx)
2938 {
2939     UINT r, name_id, row_id;
2940     INT idx;
2941     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
2942
2943     TRACE ("%p %s\n", view, debugstr_w(name));
2944
2945     r = msi_string2idW(tv->db->strings, name, &name_id);
2946     if (r != ERROR_SUCCESS)
2947     {
2948         *pidx = -1;
2949         return r;
2950     }
2951
2952     for( idx = 0; idx < tv->table->row_count; idx++ )
2953     {
2954         r = TABLE_fetch_int( &tv->view, idx, 1, &row_id );
2955         if (row_id > name_id)
2956             break;
2957     }
2958
2959     *pidx = idx;
2960     return ERROR_SUCCESS;
2961 }