msi: Mark fall-throughs in switch statements.
[wine] / dlls / msi / database.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002,2003,2004,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 <stdio.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "winnls.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "msi.h"
34 #include "msiquery.h"
35 #include "msipriv.h"
36 #include "objidl.h"
37 #include "objbase.h"
38 #include "msiserver.h"
39 #include "query.h"
40
41 #include "initguid.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44
45 /*
46  *  .MSI  file format
47  *
48  *  An .msi file is a structured storage file.
49  *  It contains a number of streams.
50  *  A stream for each table in the database.
51  *  Two streams for the string table in the database.
52  *  Any binary data in a table is a reference to a stream.
53  */
54
55 #define IS_INTMSIDBOPEN(x)      (((ULONG_PTR)(x) >> 16) == 0)
56
57 typedef struct tagMSITRANSFORM {
58     struct list entry;
59     IStorage *stg;
60 } MSITRANSFORM;
61
62 typedef struct tagMSISTREAM {
63     struct list entry;
64     IStorage *stg;
65     IStream *stm;
66 } MSISTREAM;
67
68 static UINT find_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm )
69 {
70     MSISTREAM *stream;
71
72     LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
73     {
74         HRESULT r;
75         STATSTG stat;
76
77         if (stream->stg != stg) continue;
78
79         r = IStream_Stat( stream->stm, &stat, 0 );
80         if( FAILED( r ) )
81         {
82             WARN("failed to stat stream r = %08x!\n", r);
83             continue;
84         }
85
86         if( !strcmpW( name, stat.pwcsName ) )
87         {
88             TRACE("found %s\n", debugstr_w(name));
89             *stm = stream->stm;
90             CoTaskMemFree( stat.pwcsName );
91             return ERROR_SUCCESS;
92         }
93
94         CoTaskMemFree( stat.pwcsName );
95     }
96
97     return ERROR_FUNCTION_FAILED;
98 }
99
100 UINT msi_clone_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm )
101 {
102     IStream *stream;
103
104     if (find_open_stream( db, stg, name, &stream ) == ERROR_SUCCESS)
105     {
106         HRESULT r;
107         LARGE_INTEGER pos;
108
109         r = IStream_Clone( stream, stm );
110         if( FAILED( r ) )
111         {
112             WARN("failed to clone stream r = %08x!\n", r);
113             return ERROR_FUNCTION_FAILED;
114         }
115
116         pos.QuadPart = 0;
117         r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
118         if( FAILED( r ) )
119         {
120             IStream_Release( *stm );
121             return ERROR_FUNCTION_FAILED;
122         }
123
124         return ERROR_SUCCESS;
125     }
126
127     return ERROR_FUNCTION_FAILED;
128 }
129
130 UINT msi_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
131 {
132     HRESULT r;
133     IStorage *stg;
134     WCHAR decoded[MAX_STREAM_NAME_LEN];
135
136     decode_streamname( stname, decoded );
137     TRACE("%s -> %s\n", debugstr_w(stname), debugstr_w(decoded));
138
139     if (msi_clone_open_stream( db, db->storage, stname, stm ) == ERROR_SUCCESS)
140         return ERROR_SUCCESS;
141
142     r = IStorage_OpenStream( db->storage, stname, NULL,
143                              STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
144     if( FAILED( r ) )
145     {
146         MSITRANSFORM *transform;
147
148         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
149         {
150             r = IStorage_OpenStream( transform->stg, stname, NULL,
151                                      STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
152             if (SUCCEEDED(r))
153             {
154                 stg = transform->stg;
155                 break;
156             }
157         }
158     }
159     else stg = db->storage;
160
161     if( SUCCEEDED(r) )
162     {
163         MSISTREAM *stream;
164
165         if (!(stream = msi_alloc( sizeof(MSISTREAM) ))) return ERROR_NOT_ENOUGH_MEMORY;
166         stream->stg = stg;
167         IStream_AddRef( stg );
168         stream->stm = *stm;
169         IStream_AddRef( *stm );
170         list_add_tail( &db->streams, &stream->entry );
171     }
172
173     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
174 }
175
176 static void free_transforms( MSIDATABASE *db )
177 {
178     while( !list_empty( &db->transforms ) )
179     {
180         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
181                                       MSITRANSFORM, entry );
182         list_remove( &t->entry );
183         IStorage_Release( t->stg );
184         msi_free( t );
185     }
186 }
187
188 void msi_destroy_stream( MSIDATABASE *db, const WCHAR *stname )
189 {
190     MSISTREAM *stream, *stream2;
191
192     LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry )
193     {
194         HRESULT r;
195         STATSTG stat;
196
197         r = IStream_Stat( stream->stm, &stat, 0 );
198         if (FAILED(r))
199         {
200             WARN("failed to stat stream r = %08x\n", r);
201             continue;
202         }
203
204         if (!strcmpW( stname, stat.pwcsName ))
205         {
206             TRACE("destroying %s\n", debugstr_w(stname));
207
208             list_remove( &stream->entry );
209             IStream_Release( stream->stm );
210             IStream_Release( stream->stg );
211             IStorage_DestroyElement( stream->stg, stname );
212             msi_free( stream );
213             CoTaskMemFree( stat.pwcsName );
214             break;
215         }
216         CoTaskMemFree( stat.pwcsName );
217     }
218 }
219
220 static void free_streams( MSIDATABASE *db )
221 {
222     while( !list_empty( &db->streams ) )
223     {
224         MSISTREAM *s = LIST_ENTRY(list_head( &db->streams ), MSISTREAM, entry);
225         list_remove( &s->entry );
226         IStream_Release( s->stm );
227         IStream_Release( s->stg );
228         msi_free( s );
229     }
230 }
231
232 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
233 {
234     MSITRANSFORM *t;
235
236     t = msi_alloc( sizeof *t );
237     t->stg = stg;
238     IStorage_AddRef( stg );
239     list_add_head( &db->transforms, &t->entry );
240
241     /* the transform may add or replace streams */
242     free_streams( db );
243 }
244
245 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
246 {
247     MSIDATABASE *db = (MSIDATABASE *) arg;
248
249     msi_free(db->path);
250     free_cached_tables( db );
251     free_streams( db );
252     free_transforms( db );
253     if (db->strings) msi_destroy_stringtable( db->strings );
254     IStorage_Release( db->storage );
255     if (db->deletefile)
256     {
257         DeleteFileW( db->deletefile );
258         msi_free( db->deletefile );
259     }
260 }
261
262 static HRESULT db_initialize( IStorage *stg, const GUID *clsid )
263 {
264     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
265     HRESULT hr;
266
267     hr = IStorage_SetClass( stg, clsid );
268     if (FAILED( hr ))
269     {
270         WARN("failed to set class id 0x%08x\n", hr);
271         return hr;
272     }
273
274     /* create the _Tables stream */
275     hr = write_stream_data( stg, szTables, NULL, 0, TRUE );
276     if (FAILED( hr ))
277     {
278         WARN("failed to create _Tables stream 0x%08x\n", hr);
279         return hr;
280     }
281
282     hr = msi_init_string_table( stg );
283     if (FAILED( hr ))
284     {
285         WARN("failed to initialize string table 0x%08x\n", hr);
286         return hr;
287     }
288
289     hr = IStorage_Commit( stg, 0 );
290     if (FAILED( hr ))
291     {
292         WARN("failed to commit changes 0x%08x\n", hr);
293         return hr;
294     }
295
296     return S_OK;
297 }
298
299 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
300 {
301     IStorage *stg = NULL;
302     HRESULT r;
303     MSIDATABASE *db = NULL;
304     UINT ret = ERROR_FUNCTION_FAILED;
305     LPCWSTR szMode, save_path;
306     STATSTG stat;
307     BOOL created = FALSE, patch = FALSE;
308     WCHAR path[MAX_PATH];
309
310     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
311
312     if( !pdb )
313         return ERROR_INVALID_PARAMETER;
314
315     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
316         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
317     {
318         TRACE("Database is a patch\n");
319         szPersist -= MSIDBOPEN_PATCHFILE;
320         patch = TRUE;
321     }
322
323     save_path = szDBPath;
324     szMode = szPersist;
325     if( !IS_INTMSIDBOPEN(szPersist) )
326     {
327         if (!CopyFileW( szDBPath, szPersist, FALSE ))
328             return ERROR_OPEN_FAILED;
329
330         szDBPath = szPersist;
331         szPersist = MSIDBOPEN_TRANSACT;
332         created = TRUE;
333     }
334
335     if( szPersist == MSIDBOPEN_READONLY )
336     {
337         r = StgOpenStorage( szDBPath, NULL,
338               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
339     }
340     else if( szPersist == MSIDBOPEN_CREATE )
341     {
342         r = StgCreateDocfile( szDBPath,
343               STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
344
345         if( SUCCEEDED(r) )
346             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
347         created = TRUE;
348     }
349     else if( szPersist == MSIDBOPEN_CREATEDIRECT )
350     {
351         r = StgCreateDocfile( szDBPath,
352               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
353
354         if( SUCCEEDED(r) )
355             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
356         created = TRUE;
357     }
358     else if( szPersist == MSIDBOPEN_TRANSACT )
359     {
360         r = StgOpenStorage( szDBPath, NULL,
361               STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
362     }
363     else if( szPersist == MSIDBOPEN_DIRECT )
364     {
365         r = StgOpenStorage( szDBPath, NULL,
366               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
367     }
368     else
369     {
370         ERR("unknown flag %p\n",szPersist);
371         return ERROR_INVALID_PARAMETER;
372     }
373
374     if( FAILED( r ) || !stg )
375     {
376         WARN("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
377         return ERROR_FUNCTION_FAILED;
378     }
379
380     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
381     if( FAILED( r ) )
382     {
383         FIXME("Failed to stat storage\n");
384         goto end;
385     }
386
387     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
388          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
389          !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
390     {
391         ERR("storage GUID is not a MSI database GUID %s\n",
392              debugstr_guid(&stat.clsid) );
393         goto end;
394     }
395
396     if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
397     {
398         ERR("storage GUID is not the MSI patch GUID %s\n",
399              debugstr_guid(&stat.clsid) );
400         ret = ERROR_OPEN_FAILED;
401         goto end;
402     }
403
404     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
405                               MSI_CloseDatabase );
406     if( !db )
407     {
408         FIXME("Failed to allocate a handle\n");
409         goto end;
410     }
411
412     if (!strchrW( save_path, '\\' ))
413     {
414         GetCurrentDirectoryW( MAX_PATH, path );
415         lstrcatW( path, szBackSlash );
416         lstrcatW( path, save_path );
417     }
418     else
419         lstrcpyW( path, save_path );
420
421     db->path = strdupW( path );
422     db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
423     db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
424
425     if( TRACE_ON( msi ) )
426         enum_stream_names( stg );
427
428     db->storage = stg;
429     db->mode = szMode;
430     if (created)
431         db->deletefile = strdupW( szDBPath );
432     list_init( &db->tables );
433     list_init( &db->transforms );
434     list_init( &db->streams );
435
436     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
437     if( !db->strings )
438         goto end;
439
440     ret = ERROR_SUCCESS;
441
442     msiobj_addref( &db->hdr );
443     IStorage_AddRef( stg );
444     *pdb = db;
445
446 end:
447     if( db )
448         msiobj_release( &db->hdr );
449     if( stg )
450         IStorage_Release( stg );
451
452     return ret;
453 }
454
455 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
456 {
457     MSIDATABASE *db;
458     UINT ret;
459
460     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
461
462     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
463     if( ret == ERROR_SUCCESS )
464     {
465         *phDB = alloc_msihandle( &db->hdr );
466         if (! *phDB)
467             ret = ERROR_NOT_ENOUGH_MEMORY;
468         msiobj_release( &db->hdr );
469     }
470
471     return ret;
472 }
473
474 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
475 {
476     HRESULT r = ERROR_FUNCTION_FAILED;
477     LPWSTR szwDBPath = NULL, szwPersist = NULL;
478
479     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
480
481     if( szDBPath )
482     {
483         szwDBPath = strdupAtoW( szDBPath );
484         if( !szwDBPath )
485             goto end;
486     }
487
488     if( !IS_INTMSIDBOPEN(szPersist) )
489     {
490         szwPersist = strdupAtoW( szPersist );
491         if( !szwPersist )
492             goto end;
493     }
494     else
495         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
496
497     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
498
499 end:
500     if( !IS_INTMSIDBOPEN(szPersist) )
501         msi_free( szwPersist );
502     msi_free( szwDBPath );
503
504     return r;
505 }
506
507 static LPWSTR msi_read_text_archive(LPCWSTR path, DWORD *len)
508 {
509     HANDLE file;
510     LPSTR data = NULL;
511     LPWSTR wdata = NULL;
512     DWORD read, size = 0;
513
514     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
515     if (file == INVALID_HANDLE_VALUE)
516         return NULL;
517
518     size = GetFileSize( file, NULL );
519     if (!(data = msi_alloc( size ))) goto done;
520
521     if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done;
522
523     while (!data[size - 1]) size--;
524     *len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 );
525     if ((wdata = msi_alloc( (*len + 1) * sizeof(WCHAR) )))
526     {
527         MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len );
528         wdata[*len] = 0;
529     }
530
531 done:
532     CloseHandle( file );
533     msi_free( data );
534     return wdata;
535 }
536
537 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries, DWORD *len)
538 {
539     LPWSTR ptr = *line, save;
540     DWORD i, count = 1, chars_left = *len;
541
542     *entries = NULL;
543
544     /* stay on this line */
545     while (chars_left && *ptr != '\n')
546     {
547         /* entries are separated by tabs */
548         if (*ptr == '\t')
549             count++;
550
551         ptr++;
552         chars_left--;
553     }
554
555     *entries = msi_alloc(count * sizeof(LPWSTR));
556     if (!*entries)
557         return;
558
559     /* store pointers into the data */
560     chars_left = *len;
561     for (i = 0, ptr = *line; i < count; i++)
562     {
563         while (chars_left && *ptr == '\r')
564         {
565             ptr++;
566             chars_left--;
567         }
568         save = ptr;
569
570         while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
571         {
572             if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */
573             if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11')
574             {
575                 *ptr = '\n';
576                 *(ptr - 1) = '\r';
577             }
578             ptr++;
579             chars_left--;
580         }
581
582         /* NULL-separate the data */
583         if (*ptr == '\n' || *ptr == '\r')
584         {
585             while (chars_left && (*ptr == '\n' || *ptr == '\r'))
586             {
587                 *(ptr++) = 0;
588                 chars_left--;
589             }
590         }
591         else if (*ptr)
592         {
593             *(ptr++) = 0;
594             chars_left--;
595         }
596         (*entries)[i] = save;
597     }
598
599     /* move to the next line if there's more, else EOF */
600     *line = ptr;
601     *len = chars_left;
602     if (num_entries)
603         *num_entries = count;
604 }
605
606 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
607 {
608     LPWSTR prelude;
609     DWORD size;
610
611     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
612
613     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
614     prelude = msi_alloc(size * sizeof(WCHAR));
615     if (!prelude)
616         return NULL;
617
618     sprintfW(prelude, create_fmt, table);
619     return prelude;
620 }
621
622 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
623 {
624     LPWSTR columns, p;
625     LPCWSTR type;
626     DWORD sql_size = 1, i, len;
627     WCHAR expanded[128], *ptr;
628     WCHAR size[10], comma[2], extra[30];
629
630     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
631     static const WCHAR size_fmt[] = {'(','%','s',')',0};
632     static const WCHAR type_char[] = {'C','H','A','R',0};
633     static const WCHAR type_int[] = {'I','N','T',0};
634     static const WCHAR type_long[] = {'L','O','N','G',0};
635     static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
636     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
637     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
638
639     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
640     if (!columns)
641         return NULL;
642
643     for (i = 0; i < num_columns; i++)
644     {
645         type = NULL;
646         comma[1] = size[0] = extra[0] = '\0';
647
648         if (i == num_columns - 1)
649             comma[0] = '\0';
650         else
651             comma[0] = ',';
652
653         ptr = &types[i][1];
654         len = atolW(ptr);
655         extra[0] = '\0';
656
657         switch (types[i][0])
658         {
659             case 'l':
660                 lstrcpyW(extra, type_notnull);
661                 /* fall through */
662             case 'L':
663                 lstrcatW(extra, localizable);
664                 type = type_char;
665                 sprintfW(size, size_fmt, ptr);
666                 break;
667             case 's':
668                 lstrcpyW(extra, type_notnull);
669                 /* fall through */
670             case 'S':
671                 type = type_char;
672                 sprintfW(size, size_fmt, ptr);
673                 break;
674             case 'i':
675                 lstrcpyW(extra, type_notnull);
676                 /* fall through */
677             case 'I':
678                 if (len <= 2)
679                     type = type_int;
680                 else if (len == 4)
681                     type = type_long;
682                 else
683                 {
684                     WARN("invalid int width %u\n", len);
685                     msi_free(columns);
686                     return NULL;
687                 }
688                 break;
689             case 'v':
690                 lstrcpyW(extra, type_notnull);
691                 /* fall through */
692             case 'V':
693                 type = type_object;
694                 break;
695             default:
696                 ERR("Unknown type: %c\n", types[i][0]);
697                 msi_free(columns);
698                 return NULL;
699         }
700
701         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
702         sql_size += lstrlenW(expanded);
703
704         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
705         if (!p)
706         {
707             msi_free(columns);
708             return NULL;
709         }
710         columns = p;
711
712         lstrcatW(columns, expanded);
713     }
714
715     return columns;
716 }
717
718 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
719 {
720     LPWSTR postlude, keys, ptr;
721     DWORD size, key_size, i;
722
723     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
724     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
725
726     for (i = 0, size = 1; i < num_keys; i++)
727         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
728
729     keys = msi_alloc(size * sizeof(WCHAR));
730     if (!keys)
731         return NULL;
732
733     for (i = 0, ptr = keys; i < num_keys; i++)
734     {
735         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
736         sprintfW(ptr, key_fmt, primary_keys[i]);
737         ptr += key_size;
738     }
739
740     /* remove final ', ' */
741     *(ptr - 2) = '\0';
742
743     size = lstrlenW(postlude_fmt) + size - 1;
744     postlude = msi_alloc(size * sizeof(WCHAR));
745     if (!postlude)
746         goto done;
747
748     sprintfW(postlude, postlude_fmt, keys);
749
750 done:
751     msi_free(keys);
752     return postlude;
753 }
754
755 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
756 {
757     UINT r = ERROR_OUTOFMEMORY;
758     DWORD size;
759     MSIQUERY *view;
760     LPWSTR create_sql = NULL;
761     LPWSTR prelude, columns_sql, postlude;
762
763     prelude = msi_build_createsql_prelude(labels[0]);
764     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
765     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
766
767     if (!prelude || !columns_sql || !postlude)
768         goto done;
769
770     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
771     create_sql = msi_alloc(size * sizeof(WCHAR));
772     if (!create_sql)
773         goto done;
774
775     lstrcpyW(create_sql, prelude);
776     lstrcatW(create_sql, columns_sql);
777     lstrcatW(create_sql, postlude);
778
779     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
780     if (r != ERROR_SUCCESS)
781         goto done;
782
783     r = MSI_ViewExecute(view, NULL);
784     MSI_ViewClose(view);
785     msiobj_release(&view->hdr);
786
787 done:
788     msi_free(prelude);
789     msi_free(columns_sql);
790     msi_free(postlude);
791     msi_free(create_sql);
792     return r;
793 }
794
795 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
796 {
797     DWORD len;
798     LPWSTR fullname, ptr;
799
800     len = lstrlenW(path) + lstrlenW(name) + 1;
801     fullname = msi_alloc(len*sizeof(WCHAR));
802     if (!fullname)
803        return NULL;
804
805     lstrcpyW( fullname, path );
806
807     /* chop off extension from path */
808     ptr = strrchrW(fullname, '.');
809     if (!ptr)
810     {
811         msi_free (fullname);
812         return NULL;
813     }
814     *ptr++ = '\\';
815     lstrcpyW( ptr, name );
816     return fullname;
817 }
818
819 static UINT construct_record(DWORD num_columns, LPWSTR *types,
820                              LPWSTR *data, LPWSTR path, MSIRECORD **rec)
821 {
822     UINT i;
823
824     *rec = MSI_CreateRecord(num_columns);
825     if (!*rec)
826         return ERROR_OUTOFMEMORY;
827
828     for (i = 0; i < num_columns; i++)
829     {
830         switch (types[i][0])
831         {
832             case 'L': case 'l': case 'S': case 's':
833                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
834                 break;
835             case 'I': case 'i':
836                 if (*data[i])
837                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
838                 break;
839             case 'V': case 'v':
840                 if (*data[i])
841                 {
842                     UINT r;
843                     LPWSTR file = msi_import_stream_filename(path, data[i]);
844                     if (!file)
845                         return ERROR_FUNCTION_FAILED;
846
847                     r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
848                     msi_free (file);
849                     if (r != ERROR_SUCCESS)
850                         return ERROR_FUNCTION_FAILED;
851                 }
852                 break;
853             default:
854                 ERR("Unhandled column type: %c\n", types[i][0]);
855                 msiobj_release(&(*rec)->hdr);
856                 return ERROR_FUNCTION_FAILED;
857         }
858     }
859
860     return ERROR_SUCCESS;
861 }
862
863 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
864                                      LPWSTR *labels, LPWSTR **records,
865                                      int num_columns, int num_records,
866                                      LPWSTR path)
867 {
868     UINT r;
869     int i;
870     MSIQUERY *view;
871     MSIRECORD *rec;
872
873     static const WCHAR select[] = {
874         'S','E','L','E','C','T',' ','*',' ',
875         'F','R','O','M',' ','`','%','s','`',0
876     };
877
878     r = MSI_OpenQuery(db, &view, select, labels[0]);
879     if (r != ERROR_SUCCESS)
880         return r;
881
882     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
883     {
884         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
885         msiobj_release(&rec->hdr);
886         if (r != ERROR_SUCCESS)
887             goto done;
888     }
889
890     for (i = 0; i < num_records; i++)
891     {
892         r = construct_record(num_columns, types, records[i], path, &rec);
893         if (r != ERROR_SUCCESS)
894             goto done;
895
896         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
897         if (r != ERROR_SUCCESS)
898         {
899             msiobj_release(&rec->hdr);
900             goto done;
901         }
902
903         msiobj_release(&rec->hdr);
904     }
905
906 done:
907     msiobj_release(&view->hdr);
908     return r;
909 }
910
911 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
912 {
913     UINT r;
914     DWORD len, i;
915     DWORD num_labels, num_types;
916     DWORD num_columns, num_records = 0;
917     LPWSTR *columns, *types, *labels;
918     LPWSTR path, ptr, data;
919     LPWSTR **records = NULL;
920     LPWSTR **temp_records;
921
922     static const WCHAR suminfo[] =
923         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
924     static const WCHAR forcecodepage[] =
925         {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
926
927     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
928
929     if( folder == NULL || file == NULL )
930         return ERROR_INVALID_PARAMETER;
931
932     len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
933     path = msi_alloc( len * sizeof(WCHAR) );
934     if (!path)
935         return ERROR_OUTOFMEMORY;
936
937     lstrcpyW( path, folder );
938     lstrcatW( path, szBackSlash );
939     lstrcatW( path, file );
940
941     data = msi_read_text_archive( path, &len );
942
943     ptr = data;
944     msi_parse_line( &ptr, &columns, &num_columns, &len );
945     msi_parse_line( &ptr, &types, &num_types, &len );
946     msi_parse_line( &ptr, &labels, &num_labels, &len );
947
948     if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
949         num_types == 2 && !strcmpW( types[1], forcecodepage ))
950     {
951         r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) );
952         goto done;
953     }
954
955     if (num_columns != num_types)
956     {
957         r = ERROR_FUNCTION_FAILED;
958         goto done;
959     }
960
961     records = msi_alloc(sizeof(LPWSTR *));
962     if (!records)
963     {
964         r = ERROR_OUTOFMEMORY;
965         goto done;
966     }
967
968     /* read in the table records */
969     while (len)
970     {
971         msi_parse_line( &ptr, &records[num_records], NULL, &len );
972
973         num_records++;
974         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
975         if (!temp_records)
976         {
977             r = ERROR_OUTOFMEMORY;
978             goto done;
979         }
980         records = temp_records;
981     }
982
983     if (!strcmpW(labels[0], suminfo))
984     {
985         r = msi_add_suminfo( db, records, num_records, num_columns );
986         if (r != ERROR_SUCCESS)
987         {
988             r = ERROR_FUNCTION_FAILED;
989             goto done;
990         }
991     }
992     else
993     {
994         if (!TABLE_Exists(db, labels[0]))
995         {
996             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
997             if (r != ERROR_SUCCESS)
998             {
999                 r = ERROR_FUNCTION_FAILED;
1000                 goto done;
1001             }
1002         }
1003
1004         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
1005     }
1006
1007 done:
1008     msi_free(path);
1009     msi_free(data);
1010     msi_free(columns);
1011     msi_free(types);
1012     msi_free(labels);
1013
1014     for (i = 0; i < num_records; i++)
1015         msi_free(records[i]);
1016
1017     msi_free(records);
1018
1019     return r;
1020 }
1021
1022 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
1023 {
1024     MSIDATABASE *db;
1025     UINT r;
1026
1027     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
1028
1029     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1030     if( !db )
1031     {
1032         IWineMsiRemoteDatabase *remote_database;
1033
1034         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1035         if ( !remote_database )
1036             return ERROR_INVALID_HANDLE;
1037
1038         IWineMsiRemoteDatabase_Release( remote_database );
1039         WARN("MsiDatabaseImport not allowed during a custom action!\n");
1040
1041         return ERROR_SUCCESS;
1042     }
1043
1044     r = MSI_DatabaseImport( db, szFolder, szFilename );
1045     msiobj_release( &db->hdr );
1046     return r;
1047 }
1048
1049 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
1050                LPCSTR szFolder, LPCSTR szFilename )
1051 {
1052     LPWSTR path = NULL, file = NULL;
1053     UINT r = ERROR_OUTOFMEMORY;
1054
1055     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
1056
1057     if( szFolder )
1058     {
1059         path = strdupAtoW( szFolder );
1060         if( !path )
1061             goto end;
1062     }
1063
1064     if( szFilename )
1065     {
1066         file = strdupAtoW( szFilename );
1067         if( !file )
1068             goto end;
1069     }
1070
1071     r = MsiDatabaseImportW( handle, path, file );
1072
1073 end:
1074     msi_free( path );
1075     msi_free( file );
1076
1077     return r;
1078 }
1079
1080 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1081 {
1082     UINT i, count, len, r = ERROR_SUCCESS;
1083     const char *sep;
1084     char *buffer;
1085     DWORD sz;
1086
1087     len = 0x100;
1088     buffer = msi_alloc( len );
1089     if ( !buffer )
1090         return ERROR_OUTOFMEMORY;
1091
1092     count = MSI_RecordGetFieldCount( row );
1093     for ( i=start; i<=count; i++ )
1094     {
1095         sz = len;
1096         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1097         if (r == ERROR_MORE_DATA)
1098         {
1099             char *p = msi_realloc( buffer, sz + 1 );
1100             if (!p)
1101                 break;
1102             len = sz + 1;
1103             buffer = p;
1104         }
1105         sz = len;
1106         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1107         if (r != ERROR_SUCCESS)
1108             break;
1109
1110         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1111         {
1112             r = ERROR_FUNCTION_FAILED;
1113             break;
1114         }
1115
1116         sep = (i < count) ? "\t" : "\r\n";
1117         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1118         {
1119             r = ERROR_FUNCTION_FAILED;
1120             break;
1121         }
1122     }
1123     msi_free( buffer );
1124     return r;
1125 }
1126
1127 static UINT msi_export_row( MSIRECORD *row, void *arg )
1128 {
1129     return msi_export_record( arg, row, 1 );
1130 }
1131
1132 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
1133 {
1134     static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1135     char data[sizeof(fmt) + 10];
1136     DWORD sz;
1137
1138     sprintf( data, fmt, codepage );
1139
1140     sz = lstrlenA(data) + 1;
1141     if (!WriteFile(handle, data, sz, &sz, NULL))
1142         return ERROR_FUNCTION_FAILED;
1143
1144     return ERROR_SUCCESS;
1145 }
1146
1147 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1148                LPCWSTR folder, LPCWSTR file )
1149 {
1150     static const WCHAR query[] = {
1151         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1152     static const WCHAR forcecodepage[] = {
1153         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1154     MSIRECORD *rec = NULL;
1155     MSIQUERY *view = NULL;
1156     LPWSTR filename;
1157     HANDLE handle;
1158     UINT len, r;
1159
1160     TRACE("%p %s %s %s\n", db, debugstr_w(table),
1161           debugstr_w(folder), debugstr_w(file) );
1162
1163     if( folder == NULL || file == NULL )
1164         return ERROR_INVALID_PARAMETER;
1165
1166     len = lstrlenW(folder) + lstrlenW(file) + 2;
1167     filename = msi_alloc(len * sizeof (WCHAR));
1168     if (!filename)
1169         return ERROR_OUTOFMEMORY;
1170
1171     lstrcpyW( filename, folder );
1172     lstrcatW( filename, szBackSlash );
1173     lstrcatW( filename, file );
1174
1175     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1176                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1177     msi_free( filename );
1178     if (handle == INVALID_HANDLE_VALUE)
1179         return ERROR_FUNCTION_FAILED;
1180
1181     if (!strcmpW( table, forcecodepage ))
1182     {
1183         UINT codepage = msi_get_string_table_codepage( db->strings );
1184         r = msi_export_forcecodepage( handle, codepage );
1185         goto done;
1186     }
1187
1188     r = MSI_OpenQuery( db, &view, query, table );
1189     if (r == ERROR_SUCCESS)
1190     {
1191         /* write out row 1, the column names */
1192         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1193         if (r == ERROR_SUCCESS)
1194         {
1195             msi_export_record( handle, rec, 1 );
1196             msiobj_release( &rec->hdr );
1197         }
1198
1199         /* write out row 2, the column types */
1200         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1201         if (r == ERROR_SUCCESS)
1202         {
1203             msi_export_record( handle, rec, 1 );
1204             msiobj_release( &rec->hdr );
1205         }
1206
1207         /* write out row 3, the table name + keys */
1208         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1209         if (r == ERROR_SUCCESS)
1210         {
1211             MSI_RecordSetStringW( rec, 0, table );
1212             msi_export_record( handle, rec, 0 );
1213             msiobj_release( &rec->hdr );
1214         }
1215
1216         /* write out row 4 onwards, the data */
1217         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1218         msiobj_release( &view->hdr );
1219     }
1220
1221 done:
1222     CloseHandle( handle );
1223     return r;
1224 }
1225
1226 /***********************************************************************
1227  * MsiExportDatabaseW        [MSI.@]
1228  *
1229  * Writes a file containing the table data as tab separated ASCII.
1230  *
1231  * The format is as follows:
1232  *
1233  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1234  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1235  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1236  *
1237  * Followed by the data, starting at row 1 with one row per line
1238  *
1239  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1240  */
1241 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1242                LPCWSTR szFolder, LPCWSTR szFilename )
1243 {
1244     MSIDATABASE *db;
1245     UINT r;
1246
1247     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1248           debugstr_w(szFolder), debugstr_w(szFilename));
1249
1250     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1251     if( !db )
1252     {
1253         IWineMsiRemoteDatabase *remote_database;
1254
1255         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1256         if ( !remote_database )
1257             return ERROR_INVALID_HANDLE;
1258
1259         IWineMsiRemoteDatabase_Release( remote_database );
1260         WARN("MsiDatabaseExport not allowed during a custom action!\n");
1261
1262         return ERROR_SUCCESS;
1263     }
1264
1265     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1266     msiobj_release( &db->hdr );
1267     return r;
1268 }
1269
1270 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1271                LPCSTR szFolder, LPCSTR szFilename )
1272 {
1273     LPWSTR path = NULL, file = NULL, table = NULL;
1274     UINT r = ERROR_OUTOFMEMORY;
1275
1276     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1277           debugstr_a(szFolder), debugstr_a(szFilename));
1278
1279     if( szTable )
1280     {
1281         table = strdupAtoW( szTable );
1282         if( !table )
1283             goto end;
1284     }
1285
1286     if( szFolder )
1287     {
1288         path = strdupAtoW( szFolder );
1289         if( !path )
1290             goto end;
1291     }
1292
1293     if( szFilename )
1294     {
1295         file = strdupAtoW( szFilename );
1296         if( !file )
1297             goto end;
1298     }
1299
1300     r = MsiDatabaseExportW( handle, table, path, file );
1301
1302 end:
1303     msi_free( table );
1304     msi_free( path );
1305     msi_free( file );
1306
1307     return r;
1308 }
1309
1310 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1311                               LPCSTR szTableName)
1312 {
1313     UINT r;
1314     LPWSTR table;
1315
1316     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1317           debugstr_a(szTableName));
1318
1319     table = strdupAtoW(szTableName);
1320     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1321
1322     msi_free(table);
1323     return r;
1324 }
1325
1326 typedef struct _tagMERGETABLE
1327 {
1328     struct list entry;
1329     struct list rows;
1330     LPWSTR name;
1331     DWORD numconflicts;
1332     LPWSTR *columns;
1333     DWORD numcolumns;
1334     LPWSTR *types;
1335     DWORD numtypes;
1336     LPWSTR *labels;
1337     DWORD numlabels;
1338 } MERGETABLE;
1339
1340 typedef struct _tagMERGEROW
1341 {
1342     struct list entry;
1343     MSIRECORD *data;
1344 } MERGEROW;
1345
1346 typedef struct _tagMERGEDATA
1347 {
1348     MSIDATABASE *db;
1349     MSIDATABASE *merge;
1350     MERGETABLE *curtable;
1351     MSIQUERY *curview;
1352     struct list *tabledata;
1353 } MERGEDATA;
1354
1355 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1356 {
1357     if (((type1[0] == 'l') || (type1[0] == 's')) &&
1358         ((type2[0] == 'l') || (type2[0] == 's')))
1359         return TRUE;
1360
1361     if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1362         ((type2[0] == 'L') || (type2[0] == 'S')))
1363         return TRUE;
1364
1365     return !strcmpW( type1, type2 );
1366 }
1367
1368 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1369 {
1370     MSIRECORD *dbrec, *mergerec;
1371     UINT r, i, count;
1372
1373     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1374     if (r != ERROR_SUCCESS)
1375         return r;
1376
1377     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1378     if (r != ERROR_SUCCESS)
1379         return r;
1380
1381     count = MSI_RecordGetFieldCount(dbrec);
1382     for (i = 1; i <= count; i++)
1383     {
1384         if (!MSI_RecordGetString(mergerec, i))
1385             break;
1386
1387         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1388         {
1389             r = ERROR_DATATYPE_MISMATCH;
1390             goto done;
1391         }
1392     }
1393
1394     msiobj_release(&dbrec->hdr);
1395     msiobj_release(&mergerec->hdr);
1396     dbrec = mergerec = NULL;
1397
1398     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1399     if (r != ERROR_SUCCESS)
1400         return r;
1401
1402     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1403     if (r != ERROR_SUCCESS)
1404         return r;
1405
1406     count = MSI_RecordGetFieldCount(dbrec);
1407     for (i = 1; i <= count; i++)
1408     {
1409         if (!MSI_RecordGetString(mergerec, i))
1410             break;
1411
1412         if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1413                      MSI_RecordGetString(mergerec, i)))
1414         {
1415             r = ERROR_DATATYPE_MISMATCH;
1416             break;
1417         }
1418     }
1419
1420 done:
1421     msiobj_release(&dbrec->hdr);
1422     msiobj_release(&mergerec->hdr);
1423
1424     return r;
1425 }
1426
1427 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1428                                       LPCWSTR table)
1429 {
1430     MSIRECORD *dbrec, *mergerec = NULL;
1431     UINT r, i, count;
1432
1433     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1434     if (r != ERROR_SUCCESS)
1435         return r;
1436
1437     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1438     if (r != ERROR_SUCCESS)
1439         goto done;
1440
1441     count = MSI_RecordGetFieldCount(dbrec);
1442     if (count != MSI_RecordGetFieldCount(mergerec))
1443     {
1444         r = ERROR_DATATYPE_MISMATCH;
1445         goto done;
1446     }
1447
1448     for (i = 1; i <= count; i++)
1449     {
1450         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1451         {
1452             r = ERROR_DATATYPE_MISMATCH;
1453             goto done;
1454         }
1455     }
1456
1457 done:
1458     msiobj_release(&dbrec->hdr);
1459     msiobj_release(&mergerec->hdr);
1460
1461     return r;
1462 }
1463
1464 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1465 {
1466     MSIRECORD *colnames;
1467     LPWSTR str, val;
1468     UINT r, i = 0, sz = 0;
1469     int cmp;
1470
1471     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1472     if (r != ERROR_SUCCESS)
1473         return NULL;
1474
1475     do
1476     {
1477         str = msi_dup_record_field(colnames, ++i);
1478         cmp = strcmpW( key, str );
1479         msi_free(str);
1480     } while (cmp);
1481
1482     msiobj_release(&colnames->hdr);
1483
1484     r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1485     if (r != ERROR_SUCCESS)
1486         return NULL;
1487     sz++;
1488
1489     if (MSI_RecordGetString(rec, i))  /* check record field is a string */
1490     {
1491         /* quote string record fields */
1492         const WCHAR szQuote[] = {'\'', 0};
1493         sz += 2;
1494         val = msi_alloc(sz*sizeof(WCHAR));
1495         if (!val)
1496             return NULL;
1497
1498         lstrcpyW(val, szQuote);
1499         r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1500         lstrcpyW(val+1+sz, szQuote);
1501     }
1502     else
1503     {
1504         /* do not quote integer record fields */
1505         val = msi_alloc(sz*sizeof(WCHAR));
1506         if (!val)
1507             return NULL;
1508
1509         r = MSI_RecordGetStringW(rec, i, val, &sz);
1510     }
1511
1512     if (r != ERROR_SUCCESS)
1513     {
1514         ERR("failed to get string!\n");
1515         msi_free(val);
1516         return NULL;
1517     }
1518
1519     return val;
1520 }
1521
1522 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1523                                     LPWSTR table, MSIRECORD *rec)
1524 {
1525     LPWSTR query = NULL, clause = NULL, val;
1526     LPCWSTR setptr, key;
1527     DWORD size, oldsize;
1528     MSIRECORD *keys;
1529     UINT r, i, count;
1530
1531     static const WCHAR keyset[] = {
1532         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1533     static const WCHAR lastkeyset[] = {
1534         '`','%','s','`',' ','=',' ','%','s',' ',0};
1535     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1536         'F','R','O','M',' ','`','%','s','`',' ',
1537         'W','H','E','R','E',' ','%','s',0};
1538
1539     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1540     if (r != ERROR_SUCCESS)
1541         return NULL;
1542
1543     clause = msi_alloc_zero(sizeof(WCHAR));
1544     if (!clause)
1545         goto done;
1546
1547     size = 1;
1548     count = MSI_RecordGetFieldCount(keys);
1549     for (i = 1; i <= count; i++)
1550     {
1551         key = MSI_RecordGetString(keys, i);
1552         val = get_key_value(view, key, rec);
1553
1554         if (i == count)
1555             setptr = lastkeyset;
1556         else
1557             setptr = keyset;
1558
1559         oldsize = size;
1560         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1561         clause = msi_realloc(clause, size * sizeof (WCHAR));
1562         if (!clause)
1563         {
1564             msi_free(val);
1565             goto done;
1566         }
1567
1568         sprintfW(clause + oldsize - 1, setptr, key, val);
1569         msi_free(val);
1570     }
1571
1572     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1573     query = msi_alloc(size * sizeof(WCHAR));
1574     if (!query)
1575         goto done;
1576
1577     sprintfW(query, fmt, table, clause);
1578
1579 done:
1580     msi_free(clause);
1581     msiobj_release(&keys->hdr);
1582     return query;
1583 }
1584
1585 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1586 {
1587     MERGEDATA *data = param;
1588     MERGETABLE *table = data->curtable;
1589     MERGEROW *mergerow;
1590     MSIQUERY *dbview = NULL;
1591     MSIRECORD *row = NULL;
1592     LPWSTR query = NULL;
1593     UINT r = ERROR_SUCCESS;
1594
1595     if (TABLE_Exists(data->db, table->name))
1596     {
1597         query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1598         if (!query)
1599             return ERROR_OUTOFMEMORY;
1600
1601         r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1602         if (r != ERROR_SUCCESS)
1603             goto done;
1604
1605         r = MSI_ViewExecute(dbview, NULL);
1606         if (r != ERROR_SUCCESS)
1607             goto done;
1608
1609         r = MSI_ViewFetch(dbview, &row);
1610         if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1611         {
1612             table->numconflicts++;
1613             goto done;
1614         }
1615         else if (r != ERROR_NO_MORE_ITEMS)
1616             goto done;
1617
1618         r = ERROR_SUCCESS;
1619     }
1620
1621     mergerow = msi_alloc(sizeof(MERGEROW));
1622     if (!mergerow)
1623     {
1624         r = ERROR_OUTOFMEMORY;
1625         goto done;
1626     }
1627
1628     mergerow->data = MSI_CloneRecord(rec);
1629     if (!mergerow->data)
1630     {
1631         r = ERROR_OUTOFMEMORY;
1632         msi_free(mergerow);
1633         goto done;
1634     }
1635
1636     list_add_tail(&table->rows, &mergerow->entry);
1637
1638 done:
1639     msi_free(query);
1640     msiobj_release(&row->hdr);
1641     msiobj_release(&dbview->hdr);
1642     return r;
1643 }
1644
1645 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1646 {
1647     UINT r, i, count;
1648     MSIRECORD *prec = NULL;
1649
1650     r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1651     if (r != ERROR_SUCCESS)
1652         return r;
1653
1654     count = MSI_RecordGetFieldCount(prec);
1655     *numlabels = count + 1;
1656     *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1657     if (!*labels)
1658     {
1659         r = ERROR_OUTOFMEMORY;
1660         goto end;
1661     }
1662
1663     (*labels)[0] = strdupW(table);
1664     for (i=1; i<=count; i++ )
1665     {
1666         (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1667     }
1668
1669 end:
1670     msiobj_release( &prec->hdr );
1671     return r;
1672 }
1673
1674 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1675 {
1676     UINT r, i, count;
1677     MSIRECORD *prec = NULL;
1678
1679     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1680     if (r != ERROR_SUCCESS)
1681         return r;
1682
1683     count = MSI_RecordGetFieldCount(prec);
1684     *columns = msi_alloc(count*sizeof(LPWSTR));
1685     if (!*columns)
1686     {
1687         r = ERROR_OUTOFMEMORY;
1688         goto end;
1689     }
1690
1691     for (i=1; i<=count; i++ )
1692     {
1693         (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1694     }
1695
1696     *numcolumns = count;
1697
1698 end:
1699     msiobj_release( &prec->hdr );
1700     return r;
1701 }
1702
1703 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1704 {
1705     UINT r, i, count;
1706     MSIRECORD *prec = NULL;
1707
1708     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1709     if (r != ERROR_SUCCESS)
1710         return r;
1711
1712     count = MSI_RecordGetFieldCount(prec);
1713     *types = msi_alloc(count*sizeof(LPWSTR));
1714     if (!*types)
1715     {
1716         r = ERROR_OUTOFMEMORY;
1717         goto end;
1718     }
1719
1720     *numtypes = count;
1721     for (i=1; i<=count; i++ )
1722     {
1723         (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1724     }
1725
1726 end:
1727     msiobj_release( &prec->hdr );
1728     return r;
1729 }
1730
1731 static void merge_free_rows(MERGETABLE *table)
1732 {
1733     struct list *item, *cursor;
1734
1735     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1736     {
1737         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1738
1739         list_remove(&row->entry);
1740         msiobj_release(&row->data->hdr);
1741         msi_free(row);
1742     }
1743 }
1744
1745 static void free_merge_table(MERGETABLE *table)
1746 {
1747     UINT i;
1748
1749     if (table->labels != NULL)
1750     {
1751         for (i = 0; i < table->numlabels; i++)
1752             msi_free(table->labels[i]);
1753
1754         msi_free(table->labels);
1755     }
1756
1757     if (table->columns != NULL)
1758     {
1759         for (i = 0; i < table->numcolumns; i++)
1760             msi_free(table->columns[i]);
1761
1762         msi_free(table->columns);
1763     }
1764
1765     if (table->types != NULL)
1766     {
1767         for (i = 0; i < table->numtypes; i++)
1768             msi_free(table->types[i]);
1769
1770         msi_free(table->types);
1771     }
1772
1773     msi_free(table->name);
1774     merge_free_rows(table);
1775
1776     msi_free(table);
1777 }
1778
1779 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1780 {
1781     UINT r;
1782     MERGETABLE *table;
1783     MSIQUERY *mergeview = NULL;
1784
1785     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1786         'F','R','O','M',' ','`','%','s','`',0};
1787
1788     table = msi_alloc_zero(sizeof(MERGETABLE));
1789     if (!table)
1790     {
1791        *ptable = NULL;
1792        return ERROR_OUTOFMEMORY;
1793     }
1794
1795     r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1796     if (r != ERROR_SUCCESS)
1797         goto err;
1798
1799     r = MSI_OpenQuery(db, &mergeview, query, name);
1800     if (r != ERROR_SUCCESS)
1801         goto err;
1802
1803     r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1804     if (r != ERROR_SUCCESS)
1805         goto err;
1806
1807     r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1808     if (r != ERROR_SUCCESS)
1809         goto err;
1810
1811     list_init(&table->rows);
1812
1813     table->name = strdupW(name);
1814     table->numconflicts = 0;
1815
1816     msiobj_release(&mergeview->hdr);
1817     *ptable = table;
1818     return ERROR_SUCCESS;
1819
1820 err:
1821     msiobj_release(&mergeview->hdr);
1822     free_merge_table(table);
1823     *ptable = NULL;
1824     return r;
1825 }
1826
1827 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1828 {
1829     MERGEDATA *data = param;
1830     MERGETABLE *table;
1831     MSIQUERY *dbview = NULL;
1832     MSIQUERY *mergeview = NULL;
1833     LPCWSTR name;
1834     UINT r;
1835
1836     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1837         'F','R','O','M',' ','`','%','s','`',0};
1838
1839     name = MSI_RecordGetString(rec, 1);
1840
1841     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1842     if (r != ERROR_SUCCESS)
1843         goto done;
1844
1845     if (TABLE_Exists(data->db, name))
1846     {
1847         r = MSI_OpenQuery(data->db, &dbview, query, name);
1848         if (r != ERROR_SUCCESS)
1849             goto done;
1850
1851         r = merge_verify_colnames(dbview, mergeview);
1852         if (r != ERROR_SUCCESS)
1853             goto done;
1854
1855         r = merge_verify_primary_keys(data->db, data->merge, name);
1856         if (r != ERROR_SUCCESS)
1857             goto done;
1858     }
1859
1860     r = msi_get_merge_table(data->merge, name, &table);
1861     if (r != ERROR_SUCCESS)
1862         goto done;
1863
1864     data->curtable = table;
1865     data->curview = mergeview;
1866     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1867     if (r != ERROR_SUCCESS)
1868     {
1869         free_merge_table(table);
1870         goto done;
1871     }
1872
1873     list_add_tail(data->tabledata, &table->entry);
1874
1875 done:
1876     msiobj_release(&dbview->hdr);
1877     msiobj_release(&mergeview->hdr);
1878     return r;
1879 }
1880
1881 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1882                               struct list *tabledata)
1883 {
1884     static const WCHAR query[] = {
1885         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1886         '`','_','T','a','b','l','e','s','`',0};
1887     MSIQUERY *view;
1888     MERGEDATA data;
1889     UINT r;
1890
1891     r = MSI_DatabaseOpenViewW(merge, query, &view);
1892     if (r != ERROR_SUCCESS)
1893         return r;
1894
1895     data.db = db;
1896     data.merge = merge;
1897     data.tabledata = tabledata;
1898     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1899     msiobj_release(&view->hdr);
1900     return r;
1901 }
1902
1903 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1904 {
1905     UINT r;
1906     MERGEROW *row;
1907     MSIVIEW *tv;
1908
1909     if (!TABLE_Exists(db, table->name))
1910     {
1911         r = msi_add_table_to_db(db, table->columns, table->types,
1912                table->labels, table->numlabels, table->numcolumns);
1913         if (r != ERROR_SUCCESS)
1914            return ERROR_FUNCTION_FAILED;
1915     }
1916
1917     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1918     {
1919         r = TABLE_CreateView(db, table->name, &tv);
1920         if (r != ERROR_SUCCESS)
1921             return r;
1922
1923         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1924         tv->ops->delete(tv);
1925
1926         if (r != ERROR_SUCCESS)
1927             return r;
1928     }
1929
1930     return ERROR_SUCCESS;
1931 }
1932
1933 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1934                                 LPWSTR table, DWORD numconflicts)
1935 {
1936     UINT r;
1937     MSIQUERY *view;
1938
1939     static const WCHAR create[] = {
1940         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1941         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1942         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1943         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1944         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1945         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1946         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1947     static const WCHAR insert[] = {
1948         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1949         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1950         '`','N','u','m','R','o','w','M','e','r','g','e',
1951         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1952         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1953
1954     if (!TABLE_Exists(db, error))
1955     {
1956         r = MSI_OpenQuery(db, &view, create, error);
1957         if (r != ERROR_SUCCESS)
1958             return r;
1959
1960         r = MSI_ViewExecute(view, NULL);
1961         msiobj_release(&view->hdr);
1962         if (r != ERROR_SUCCESS)
1963             return r;
1964     }
1965
1966     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1967     if (r != ERROR_SUCCESS)
1968         return r;
1969
1970     r = MSI_ViewExecute(view, NULL);
1971     msiobj_release(&view->hdr);
1972     return r;
1973 }
1974
1975 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1976                               LPCWSTR szTableName)
1977 {
1978     struct list tabledata = LIST_INIT(tabledata);
1979     struct list *item, *cursor;
1980     MSIDATABASE *db, *merge;
1981     MERGETABLE *table;
1982     BOOL conflicts;
1983     UINT r;
1984
1985     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1986           debugstr_w(szTableName));
1987
1988     if (szTableName && !*szTableName)
1989         return ERROR_INVALID_TABLE;
1990
1991     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1992     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1993     if (!db || !merge)
1994     {
1995         r = ERROR_INVALID_HANDLE;
1996         goto done;
1997     }
1998
1999     r = gather_merge_data(db, merge, &tabledata);
2000     if (r != ERROR_SUCCESS)
2001         goto done;
2002
2003     conflicts = FALSE;
2004     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
2005     {
2006         if (table->numconflicts)
2007         {
2008             conflicts = TRUE;
2009
2010             r = update_merge_errors(db, szTableName, table->name,
2011                                     table->numconflicts);
2012             if (r != ERROR_SUCCESS)
2013                 break;
2014         }
2015         else
2016         {
2017             r = merge_table(db, table);
2018             if (r != ERROR_SUCCESS)
2019                 break;
2020         }
2021     }
2022
2023     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
2024     {
2025         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
2026         list_remove(&table->entry);
2027         free_merge_table(table);
2028     }
2029
2030     if (conflicts)
2031         r = ERROR_FUNCTION_FAILED;
2032
2033 done:
2034     msiobj_release(&db->hdr);
2035     msiobj_release(&merge->hdr);
2036     return r;
2037 }
2038
2039 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
2040 {
2041     MSIDBSTATE ret = MSIDBSTATE_READ;
2042     MSIDATABASE *db;
2043
2044     TRACE("%d\n", handle);
2045
2046     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
2047     if( !db )
2048     {
2049         IWineMsiRemoteDatabase *remote_database;
2050
2051         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
2052         if ( !remote_database )
2053             return MSIDBSTATE_ERROR;
2054
2055         IWineMsiRemoteDatabase_Release( remote_database );
2056         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
2057
2058         return MSIDBSTATE_READ;
2059     }
2060
2061     if (db->mode != MSIDBOPEN_READONLY )
2062         ret = MSIDBSTATE_WRITE;
2063     msiobj_release( &db->hdr );
2064
2065     return ret;
2066 }
2067
2068 typedef struct _msi_remote_database_impl {
2069     IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface;
2070     MSIHANDLE database;
2071     LONG refs;
2072 } msi_remote_database_impl;
2073
2074 static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface )
2075 {
2076     return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface);
2077 }
2078
2079 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2080                                           REFIID riid,LPVOID *ppobj)
2081 {
2082     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2083         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2084     {
2085         IUnknown_AddRef( iface );
2086         *ppobj = iface;
2087         return S_OK;
2088     }
2089
2090     return E_NOINTERFACE;
2091 }
2092
2093 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2094 {
2095     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2096
2097     return InterlockedIncrement( &This->refs );
2098 }
2099
2100 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2101 {
2102     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2103     ULONG r;
2104
2105     r = InterlockedDecrement( &This->refs );
2106     if (r == 0)
2107     {
2108         MsiCloseHandle( This->database );
2109         msi_free( This );
2110     }
2111     return r;
2112 }
2113
2114 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2115                                              LPCWSTR table, MSICONDITION *persistent )
2116 {
2117     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2118     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2119     return S_OK;
2120 }
2121
2122 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2123                                           LPCWSTR table, MSIHANDLE *keys )
2124 {
2125     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2126     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2127     return HRESULT_FROM_WIN32(r);
2128 }
2129
2130 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2131                                                 UINT updatecount, MSIHANDLE *suminfo )
2132 {
2133     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2134     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2135     return HRESULT_FROM_WIN32(r);
2136 }
2137
2138 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2139                                     LPCWSTR query, MSIHANDLE *view )
2140 {
2141     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2142     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2143     return HRESULT_FROM_WIN32(r);
2144 }
2145
2146 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2147 {
2148     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2149     This->database = handle;
2150     return S_OK;
2151 }
2152
2153 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2154 {
2155     mrd_QueryInterface,
2156     mrd_AddRef,
2157     mrd_Release,
2158     mrd_IsTablePersistent,
2159     mrd_GetPrimaryKeys,
2160     mrd_GetSummaryInformation,
2161     mrd_OpenView,
2162     mrd_SetMsiHandle,
2163 };
2164
2165 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2166 {
2167     msi_remote_database_impl *This;
2168
2169     This = msi_alloc( sizeof *This );
2170     if (!This)
2171         return E_OUTOFMEMORY;
2172
2173     This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl;
2174     This->database = 0;
2175     This->refs = 1;
2176
2177     *ppObj = This;
2178
2179     return S_OK;
2180 }