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