msi: Fix printing NULL strings.
[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             case 'L':
662                 lstrcatW(extra, localizable);
663                 type = type_char;
664                 sprintfW(size, size_fmt, ptr);
665                 break;
666             case 's':
667                 lstrcpyW(extra, type_notnull);
668             case 'S':
669                 type = type_char;
670                 sprintfW(size, size_fmt, ptr);
671                 break;
672             case 'i':
673                 lstrcpyW(extra, type_notnull);
674             case 'I':
675                 if (len <= 2)
676                     type = type_int;
677                 else if (len == 4)
678                     type = type_long;
679                 else
680                 {
681                     WARN("invalid int width %u\n", len);
682                     msi_free(columns);
683                     return NULL;
684                 }
685                 break;
686             case 'v':
687                 lstrcpyW(extra, type_notnull);
688             case 'V':
689                 type = type_object;
690                 break;
691             default:
692                 ERR("Unknown type: %c\n", types[i][0]);
693                 msi_free(columns);
694                 return NULL;
695         }
696
697         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
698         sql_size += lstrlenW(expanded);
699
700         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
701         if (!p)
702         {
703             msi_free(columns);
704             return NULL;
705         }
706         columns = p;
707
708         lstrcatW(columns, expanded);
709     }
710
711     return columns;
712 }
713
714 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
715 {
716     LPWSTR postlude, keys, ptr;
717     DWORD size, key_size, i;
718
719     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
720     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
721
722     for (i = 0, size = 1; i < num_keys; i++)
723         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
724
725     keys = msi_alloc(size * sizeof(WCHAR));
726     if (!keys)
727         return NULL;
728
729     for (i = 0, ptr = keys; i < num_keys; i++)
730     {
731         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
732         sprintfW(ptr, key_fmt, primary_keys[i]);
733         ptr += key_size;
734     }
735
736     /* remove final ', ' */
737     *(ptr - 2) = '\0';
738
739     size = lstrlenW(postlude_fmt) + size - 1;
740     postlude = msi_alloc(size * sizeof(WCHAR));
741     if (!postlude)
742         goto done;
743
744     sprintfW(postlude, postlude_fmt, keys);
745
746 done:
747     msi_free(keys);
748     return postlude;
749 }
750
751 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
752 {
753     UINT r = ERROR_OUTOFMEMORY;
754     DWORD size;
755     MSIQUERY *view;
756     LPWSTR create_sql = NULL;
757     LPWSTR prelude, columns_sql, postlude;
758
759     prelude = msi_build_createsql_prelude(labels[0]);
760     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
761     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
762
763     if (!prelude || !columns_sql || !postlude)
764         goto done;
765
766     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
767     create_sql = msi_alloc(size * sizeof(WCHAR));
768     if (!create_sql)
769         goto done;
770
771     lstrcpyW(create_sql, prelude);
772     lstrcatW(create_sql, columns_sql);
773     lstrcatW(create_sql, postlude);
774
775     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
776     if (r != ERROR_SUCCESS)
777         goto done;
778
779     r = MSI_ViewExecute(view, NULL);
780     MSI_ViewClose(view);
781     msiobj_release(&view->hdr);
782
783 done:
784     msi_free(prelude);
785     msi_free(columns_sql);
786     msi_free(postlude);
787     msi_free(create_sql);
788     return r;
789 }
790
791 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
792 {
793     DWORD len;
794     LPWSTR fullname, ptr;
795
796     len = lstrlenW(path) + lstrlenW(name) + 1;
797     fullname = msi_alloc(len*sizeof(WCHAR));
798     if (!fullname)
799        return NULL;
800
801     lstrcpyW( fullname, path );
802
803     /* chop off extension from path */
804     ptr = strrchrW(fullname, '.');
805     if (!ptr)
806     {
807         msi_free (fullname);
808         return NULL;
809     }
810     *ptr++ = '\\';
811     lstrcpyW( ptr, name );
812     return fullname;
813 }
814
815 static UINT construct_record(DWORD num_columns, LPWSTR *types,
816                              LPWSTR *data, LPWSTR path, MSIRECORD **rec)
817 {
818     UINT i;
819
820     *rec = MSI_CreateRecord(num_columns);
821     if (!*rec)
822         return ERROR_OUTOFMEMORY;
823
824     for (i = 0; i < num_columns; i++)
825     {
826         switch (types[i][0])
827         {
828             case 'L': case 'l': case 'S': case 's':
829                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
830                 break;
831             case 'I': case 'i':
832                 if (*data[i])
833                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
834                 break;
835             case 'V': case 'v':
836                 if (*data[i])
837                 {
838                     UINT r;
839                     LPWSTR file = msi_import_stream_filename(path, data[i]);
840                     if (!file)
841                         return ERROR_FUNCTION_FAILED;
842
843                     r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
844                     msi_free (file);
845                     if (r != ERROR_SUCCESS)
846                         return ERROR_FUNCTION_FAILED;
847                 }
848                 break;
849             default:
850                 ERR("Unhandled column type: %c\n", types[i][0]);
851                 msiobj_release(&(*rec)->hdr);
852                 return ERROR_FUNCTION_FAILED;
853         }
854     }
855
856     return ERROR_SUCCESS;
857 }
858
859 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
860                                      LPWSTR *labels, LPWSTR **records,
861                                      int num_columns, int num_records,
862                                      LPWSTR path)
863 {
864     UINT r;
865     int i;
866     MSIQUERY *view;
867     MSIRECORD *rec;
868
869     static const WCHAR select[] = {
870         'S','E','L','E','C','T',' ','*',' ',
871         'F','R','O','M',' ','`','%','s','`',0
872     };
873
874     r = MSI_OpenQuery(db, &view, select, labels[0]);
875     if (r != ERROR_SUCCESS)
876         return r;
877
878     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
879     {
880         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
881         msiobj_release(&rec->hdr);
882         if (r != ERROR_SUCCESS)
883             goto done;
884     }
885
886     for (i = 0; i < num_records; i++)
887     {
888         r = construct_record(num_columns, types, records[i], path, &rec);
889         if (r != ERROR_SUCCESS)
890             goto done;
891
892         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
893         if (r != ERROR_SUCCESS)
894         {
895             msiobj_release(&rec->hdr);
896             goto done;
897         }
898
899         msiobj_release(&rec->hdr);
900     }
901
902 done:
903     msiobj_release(&view->hdr);
904     return r;
905 }
906
907 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
908 {
909     UINT r;
910     DWORD len, i;
911     DWORD num_labels, num_types;
912     DWORD num_columns, num_records = 0;
913     LPWSTR *columns, *types, *labels;
914     LPWSTR path, ptr, data;
915     LPWSTR **records = NULL;
916     LPWSTR **temp_records;
917
918     static const WCHAR suminfo[] =
919         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
920     static const WCHAR forcecodepage[] =
921         {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
922
923     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
924
925     if( folder == NULL || file == NULL )
926         return ERROR_INVALID_PARAMETER;
927
928     len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
929     path = msi_alloc( len * sizeof(WCHAR) );
930     if (!path)
931         return ERROR_OUTOFMEMORY;
932
933     lstrcpyW( path, folder );
934     lstrcatW( path, szBackSlash );
935     lstrcatW( path, file );
936
937     data = msi_read_text_archive( path, &len );
938
939     ptr = data;
940     msi_parse_line( &ptr, &columns, &num_columns, &len );
941     msi_parse_line( &ptr, &types, &num_types, &len );
942     msi_parse_line( &ptr, &labels, &num_labels, &len );
943
944     if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
945         num_types == 2 && !strcmpW( types[1], forcecodepage ))
946     {
947         r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) );
948         goto done;
949     }
950
951     if (num_columns != num_types)
952     {
953         r = ERROR_FUNCTION_FAILED;
954         goto done;
955     }
956
957     records = msi_alloc(sizeof(LPWSTR *));
958     if (!records)
959     {
960         r = ERROR_OUTOFMEMORY;
961         goto done;
962     }
963
964     /* read in the table records */
965     while (len)
966     {
967         msi_parse_line( &ptr, &records[num_records], NULL, &len );
968
969         num_records++;
970         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
971         if (!temp_records)
972         {
973             r = ERROR_OUTOFMEMORY;
974             goto done;
975         }
976         records = temp_records;
977     }
978
979     if (!strcmpW(labels[0], suminfo))
980     {
981         r = msi_add_suminfo( db, records, num_records, num_columns );
982         if (r != ERROR_SUCCESS)
983         {
984             r = ERROR_FUNCTION_FAILED;
985             goto done;
986         }
987     }
988     else
989     {
990         if (!TABLE_Exists(db, labels[0]))
991         {
992             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
993             if (r != ERROR_SUCCESS)
994             {
995                 r = ERROR_FUNCTION_FAILED;
996                 goto done;
997             }
998         }
999
1000         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
1001     }
1002
1003 done:
1004     msi_free(path);
1005     msi_free(data);
1006     msi_free(columns);
1007     msi_free(types);
1008     msi_free(labels);
1009
1010     for (i = 0; i < num_records; i++)
1011         msi_free(records[i]);
1012
1013     msi_free(records);
1014
1015     return r;
1016 }
1017
1018 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
1019 {
1020     MSIDATABASE *db;
1021     UINT r;
1022
1023     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
1024
1025     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1026     if( !db )
1027     {
1028         IWineMsiRemoteDatabase *remote_database;
1029
1030         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1031         if ( !remote_database )
1032             return ERROR_INVALID_HANDLE;
1033
1034         IWineMsiRemoteDatabase_Release( remote_database );
1035         WARN("MsiDatabaseImport not allowed during a custom action!\n");
1036
1037         return ERROR_SUCCESS;
1038     }
1039
1040     r = MSI_DatabaseImport( db, szFolder, szFilename );
1041     msiobj_release( &db->hdr );
1042     return r;
1043 }
1044
1045 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
1046                LPCSTR szFolder, LPCSTR szFilename )
1047 {
1048     LPWSTR path = NULL, file = NULL;
1049     UINT r = ERROR_OUTOFMEMORY;
1050
1051     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
1052
1053     if( szFolder )
1054     {
1055         path = strdupAtoW( szFolder );
1056         if( !path )
1057             goto end;
1058     }
1059
1060     if( szFilename )
1061     {
1062         file = strdupAtoW( szFilename );
1063         if( !file )
1064             goto end;
1065     }
1066
1067     r = MsiDatabaseImportW( handle, path, file );
1068
1069 end:
1070     msi_free( path );
1071     msi_free( file );
1072
1073     return r;
1074 }
1075
1076 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1077 {
1078     UINT i, count, len, r = ERROR_SUCCESS;
1079     const char *sep;
1080     char *buffer;
1081     DWORD sz;
1082
1083     len = 0x100;
1084     buffer = msi_alloc( len );
1085     if ( !buffer )
1086         return ERROR_OUTOFMEMORY;
1087
1088     count = MSI_RecordGetFieldCount( row );
1089     for ( i=start; i<=count; i++ )
1090     {
1091         sz = len;
1092         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1093         if (r == ERROR_MORE_DATA)
1094         {
1095             char *p = msi_realloc( buffer, sz + 1 );
1096             if (!p)
1097                 break;
1098             len = sz + 1;
1099             buffer = p;
1100         }
1101         sz = len;
1102         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1103         if (r != ERROR_SUCCESS)
1104             break;
1105
1106         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1107         {
1108             r = ERROR_FUNCTION_FAILED;
1109             break;
1110         }
1111
1112         sep = (i < count) ? "\t" : "\r\n";
1113         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1114         {
1115             r = ERROR_FUNCTION_FAILED;
1116             break;
1117         }
1118     }
1119     msi_free( buffer );
1120     return r;
1121 }
1122
1123 static UINT msi_export_row( MSIRECORD *row, void *arg )
1124 {
1125     return msi_export_record( arg, row, 1 );
1126 }
1127
1128 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
1129 {
1130     static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1131     char data[sizeof(fmt) + 10];
1132     DWORD sz;
1133
1134     sprintf( data, fmt, codepage );
1135
1136     sz = lstrlenA(data) + 1;
1137     if (!WriteFile(handle, data, sz, &sz, NULL))
1138         return ERROR_FUNCTION_FAILED;
1139
1140     return ERROR_SUCCESS;
1141 }
1142
1143 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1144                LPCWSTR folder, LPCWSTR file )
1145 {
1146     static const WCHAR query[] = {
1147         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1148     static const WCHAR forcecodepage[] = {
1149         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1150     MSIRECORD *rec = NULL;
1151     MSIQUERY *view = NULL;
1152     LPWSTR filename;
1153     HANDLE handle;
1154     UINT len, r;
1155
1156     TRACE("%p %s %s %s\n", db, debugstr_w(table),
1157           debugstr_w(folder), debugstr_w(file) );
1158
1159     if( folder == NULL || file == NULL )
1160         return ERROR_INVALID_PARAMETER;
1161
1162     len = lstrlenW(folder) + lstrlenW(file) + 2;
1163     filename = msi_alloc(len * sizeof (WCHAR));
1164     if (!filename)
1165         return ERROR_OUTOFMEMORY;
1166
1167     lstrcpyW( filename, folder );
1168     lstrcatW( filename, szBackSlash );
1169     lstrcatW( filename, file );
1170
1171     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1172                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1173     msi_free( filename );
1174     if (handle == INVALID_HANDLE_VALUE)
1175         return ERROR_FUNCTION_FAILED;
1176
1177     if (!strcmpW( table, forcecodepage ))
1178     {
1179         UINT codepage = msi_get_string_table_codepage( db->strings );
1180         r = msi_export_forcecodepage( handle, codepage );
1181         goto done;
1182     }
1183
1184     r = MSI_OpenQuery( db, &view, query, table );
1185     if (r == ERROR_SUCCESS)
1186     {
1187         /* write out row 1, the column names */
1188         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1189         if (r == ERROR_SUCCESS)
1190         {
1191             msi_export_record( handle, rec, 1 );
1192             msiobj_release( &rec->hdr );
1193         }
1194
1195         /* write out row 2, the column types */
1196         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1197         if (r == ERROR_SUCCESS)
1198         {
1199             msi_export_record( handle, rec, 1 );
1200             msiobj_release( &rec->hdr );
1201         }
1202
1203         /* write out row 3, the table name + keys */
1204         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1205         if (r == ERROR_SUCCESS)
1206         {
1207             MSI_RecordSetStringW( rec, 0, table );
1208             msi_export_record( handle, rec, 0 );
1209             msiobj_release( &rec->hdr );
1210         }
1211
1212         /* write out row 4 onwards, the data */
1213         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1214         msiobj_release( &view->hdr );
1215     }
1216
1217 done:
1218     CloseHandle( handle );
1219     return r;
1220 }
1221
1222 /***********************************************************************
1223  * MsiExportDatabaseW        [MSI.@]
1224  *
1225  * Writes a file containing the table data as tab separated ASCII.
1226  *
1227  * The format is as follows:
1228  *
1229  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1230  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1231  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1232  *
1233  * Followed by the data, starting at row 1 with one row per line
1234  *
1235  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1236  */
1237 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1238                LPCWSTR szFolder, LPCWSTR szFilename )
1239 {
1240     MSIDATABASE *db;
1241     UINT r;
1242
1243     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1244           debugstr_w(szFolder), debugstr_w(szFilename));
1245
1246     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1247     if( !db )
1248     {
1249         IWineMsiRemoteDatabase *remote_database;
1250
1251         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1252         if ( !remote_database )
1253             return ERROR_INVALID_HANDLE;
1254
1255         IWineMsiRemoteDatabase_Release( remote_database );
1256         WARN("MsiDatabaseExport not allowed during a custom action!\n");
1257
1258         return ERROR_SUCCESS;
1259     }
1260
1261     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1262     msiobj_release( &db->hdr );
1263     return r;
1264 }
1265
1266 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1267                LPCSTR szFolder, LPCSTR szFilename )
1268 {
1269     LPWSTR path = NULL, file = NULL, table = NULL;
1270     UINT r = ERROR_OUTOFMEMORY;
1271
1272     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1273           debugstr_a(szFolder), debugstr_a(szFilename));
1274
1275     if( szTable )
1276     {
1277         table = strdupAtoW( szTable );
1278         if( !table )
1279             goto end;
1280     }
1281
1282     if( szFolder )
1283     {
1284         path = strdupAtoW( szFolder );
1285         if( !path )
1286             goto end;
1287     }
1288
1289     if( szFilename )
1290     {
1291         file = strdupAtoW( szFilename );
1292         if( !file )
1293             goto end;
1294     }
1295
1296     r = MsiDatabaseExportW( handle, table, path, file );
1297
1298 end:
1299     msi_free( table );
1300     msi_free( path );
1301     msi_free( file );
1302
1303     return r;
1304 }
1305
1306 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1307                               LPCSTR szTableName)
1308 {
1309     UINT r;
1310     LPWSTR table;
1311
1312     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1313           debugstr_a(szTableName));
1314
1315     table = strdupAtoW(szTableName);
1316     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1317
1318     msi_free(table);
1319     return r;
1320 }
1321
1322 typedef struct _tagMERGETABLE
1323 {
1324     struct list entry;
1325     struct list rows;
1326     LPWSTR name;
1327     DWORD numconflicts;
1328     LPWSTR *columns;
1329     DWORD numcolumns;
1330     LPWSTR *types;
1331     DWORD numtypes;
1332     LPWSTR *labels;
1333     DWORD numlabels;
1334 } MERGETABLE;
1335
1336 typedef struct _tagMERGEROW
1337 {
1338     struct list entry;
1339     MSIRECORD *data;
1340 } MERGEROW;
1341
1342 typedef struct _tagMERGEDATA
1343 {
1344     MSIDATABASE *db;
1345     MSIDATABASE *merge;
1346     MERGETABLE *curtable;
1347     MSIQUERY *curview;
1348     struct list *tabledata;
1349 } MERGEDATA;
1350
1351 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1352 {
1353     if (((type1[0] == 'l') || (type1[0] == 's')) &&
1354         ((type2[0] == 'l') || (type2[0] == 's')))
1355         return TRUE;
1356
1357     if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1358         ((type2[0] == 'L') || (type2[0] == 'S')))
1359         return TRUE;
1360
1361     return !strcmpW( type1, type2 );
1362 }
1363
1364 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1365 {
1366     MSIRECORD *dbrec, *mergerec;
1367     UINT r, i, count;
1368
1369     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1370     if (r != ERROR_SUCCESS)
1371         return r;
1372
1373     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1374     if (r != ERROR_SUCCESS)
1375         return r;
1376
1377     count = MSI_RecordGetFieldCount(dbrec);
1378     for (i = 1; i <= count; i++)
1379     {
1380         if (!MSI_RecordGetString(mergerec, i))
1381             break;
1382
1383         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1384         {
1385             r = ERROR_DATATYPE_MISMATCH;
1386             goto done;
1387         }
1388     }
1389
1390     msiobj_release(&dbrec->hdr);
1391     msiobj_release(&mergerec->hdr);
1392     dbrec = mergerec = NULL;
1393
1394     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1395     if (r != ERROR_SUCCESS)
1396         return r;
1397
1398     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1399     if (r != ERROR_SUCCESS)
1400         return r;
1401
1402     count = MSI_RecordGetFieldCount(dbrec);
1403     for (i = 1; i <= count; i++)
1404     {
1405         if (!MSI_RecordGetString(mergerec, i))
1406             break;
1407
1408         if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1409                      MSI_RecordGetString(mergerec, i)))
1410         {
1411             r = ERROR_DATATYPE_MISMATCH;
1412             break;
1413         }
1414     }
1415
1416 done:
1417     msiobj_release(&dbrec->hdr);
1418     msiobj_release(&mergerec->hdr);
1419
1420     return r;
1421 }
1422
1423 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1424                                       LPCWSTR table)
1425 {
1426     MSIRECORD *dbrec, *mergerec = NULL;
1427     UINT r, i, count;
1428
1429     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1430     if (r != ERROR_SUCCESS)
1431         return r;
1432
1433     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1434     if (r != ERROR_SUCCESS)
1435         goto done;
1436
1437     count = MSI_RecordGetFieldCount(dbrec);
1438     if (count != MSI_RecordGetFieldCount(mergerec))
1439     {
1440         r = ERROR_DATATYPE_MISMATCH;
1441         goto done;
1442     }
1443
1444     for (i = 1; i <= count; i++)
1445     {
1446         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1447         {
1448             r = ERROR_DATATYPE_MISMATCH;
1449             goto done;
1450         }
1451     }
1452
1453 done:
1454     msiobj_release(&dbrec->hdr);
1455     msiobj_release(&mergerec->hdr);
1456
1457     return r;
1458 }
1459
1460 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1461 {
1462     MSIRECORD *colnames;
1463     LPWSTR str, val;
1464     UINT r, i = 0, sz = 0;
1465     int cmp;
1466
1467     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1468     if (r != ERROR_SUCCESS)
1469         return NULL;
1470
1471     do
1472     {
1473         str = msi_dup_record_field(colnames, ++i);
1474         cmp = strcmpW( key, str );
1475         msi_free(str);
1476     } while (cmp);
1477
1478     msiobj_release(&colnames->hdr);
1479
1480     r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1481     if (r != ERROR_SUCCESS)
1482         return NULL;
1483     sz++;
1484
1485     if (MSI_RecordGetString(rec, i))  /* check record field is a string */
1486     {
1487         /* quote string record fields */
1488         const WCHAR szQuote[] = {'\'', 0};
1489         sz += 2;
1490         val = msi_alloc(sz*sizeof(WCHAR));
1491         if (!val)
1492             return NULL;
1493
1494         lstrcpyW(val, szQuote);
1495         r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1496         lstrcpyW(val+1+sz, szQuote);
1497     }
1498     else
1499     {
1500         /* do not quote integer record fields */
1501         val = msi_alloc(sz*sizeof(WCHAR));
1502         if (!val)
1503             return NULL;
1504
1505         r = MSI_RecordGetStringW(rec, i, val, &sz);
1506     }
1507
1508     if (r != ERROR_SUCCESS)
1509     {
1510         ERR("failed to get string!\n");
1511         msi_free(val);
1512         return NULL;
1513     }
1514
1515     return val;
1516 }
1517
1518 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1519                                     LPWSTR table, MSIRECORD *rec)
1520 {
1521     LPWSTR query = NULL, clause = NULL, val;
1522     LPCWSTR setptr, key;
1523     DWORD size, oldsize;
1524     MSIRECORD *keys;
1525     UINT r, i, count;
1526
1527     static const WCHAR keyset[] = {
1528         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1529     static const WCHAR lastkeyset[] = {
1530         '`','%','s','`',' ','=',' ','%','s',' ',0};
1531     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1532         'F','R','O','M',' ','`','%','s','`',' ',
1533         'W','H','E','R','E',' ','%','s',0};
1534
1535     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1536     if (r != ERROR_SUCCESS)
1537         return NULL;
1538
1539     clause = msi_alloc_zero(sizeof(WCHAR));
1540     if (!clause)
1541         goto done;
1542
1543     size = 1;
1544     count = MSI_RecordGetFieldCount(keys);
1545     for (i = 1; i <= count; i++)
1546     {
1547         key = MSI_RecordGetString(keys, i);
1548         val = get_key_value(view, key, rec);
1549
1550         if (i == count)
1551             setptr = lastkeyset;
1552         else
1553             setptr = keyset;
1554
1555         oldsize = size;
1556         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1557         clause = msi_realloc(clause, size * sizeof (WCHAR));
1558         if (!clause)
1559         {
1560             msi_free(val);
1561             goto done;
1562         }
1563
1564         sprintfW(clause + oldsize - 1, setptr, key, val);
1565         msi_free(val);
1566     }
1567
1568     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1569     query = msi_alloc(size * sizeof(WCHAR));
1570     if (!query)
1571         goto done;
1572
1573     sprintfW(query, fmt, table, clause);
1574
1575 done:
1576     msi_free(clause);
1577     msiobj_release(&keys->hdr);
1578     return query;
1579 }
1580
1581 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1582 {
1583     MERGEDATA *data = param;
1584     MERGETABLE *table = data->curtable;
1585     MERGEROW *mergerow;
1586     MSIQUERY *dbview = NULL;
1587     MSIRECORD *row = NULL;
1588     LPWSTR query = NULL;
1589     UINT r = ERROR_SUCCESS;
1590
1591     if (TABLE_Exists(data->db, table->name))
1592     {
1593         query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1594         if (!query)
1595             return ERROR_OUTOFMEMORY;
1596
1597         r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1598         if (r != ERROR_SUCCESS)
1599             goto done;
1600
1601         r = MSI_ViewExecute(dbview, NULL);
1602         if (r != ERROR_SUCCESS)
1603             goto done;
1604
1605         r = MSI_ViewFetch(dbview, &row);
1606         if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1607         {
1608             table->numconflicts++;
1609             goto done;
1610         }
1611         else if (r != ERROR_NO_MORE_ITEMS)
1612             goto done;
1613
1614         r = ERROR_SUCCESS;
1615     }
1616
1617     mergerow = msi_alloc(sizeof(MERGEROW));
1618     if (!mergerow)
1619     {
1620         r = ERROR_OUTOFMEMORY;
1621         goto done;
1622     }
1623
1624     mergerow->data = MSI_CloneRecord(rec);
1625     if (!mergerow->data)
1626     {
1627         r = ERROR_OUTOFMEMORY;
1628         msi_free(mergerow);
1629         goto done;
1630     }
1631
1632     list_add_tail(&table->rows, &mergerow->entry);
1633
1634 done:
1635     msi_free(query);
1636     msiobj_release(&row->hdr);
1637     msiobj_release(&dbview->hdr);
1638     return r;
1639 }
1640
1641 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1642 {
1643     UINT r, i, count;
1644     MSIRECORD *prec = NULL;
1645
1646     r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1647     if (r != ERROR_SUCCESS)
1648         return r;
1649
1650     count = MSI_RecordGetFieldCount(prec);
1651     *numlabels = count + 1;
1652     *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1653     if (!*labels)
1654     {
1655         r = ERROR_OUTOFMEMORY;
1656         goto end;
1657     }
1658
1659     (*labels)[0] = strdupW(table);
1660     for (i=1; i<=count; i++ )
1661     {
1662         (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1663     }
1664
1665 end:
1666     msiobj_release( &prec->hdr );
1667     return r;
1668 }
1669
1670 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1671 {
1672     UINT r, i, count;
1673     MSIRECORD *prec = NULL;
1674
1675     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1676     if (r != ERROR_SUCCESS)
1677         return r;
1678
1679     count = MSI_RecordGetFieldCount(prec);
1680     *columns = msi_alloc(count*sizeof(LPWSTR));
1681     if (!*columns)
1682     {
1683         r = ERROR_OUTOFMEMORY;
1684         goto end;
1685     }
1686
1687     for (i=1; i<=count; i++ )
1688     {
1689         (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1690     }
1691
1692     *numcolumns = count;
1693
1694 end:
1695     msiobj_release( &prec->hdr );
1696     return r;
1697 }
1698
1699 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1700 {
1701     UINT r, i, count;
1702     MSIRECORD *prec = NULL;
1703
1704     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1705     if (r != ERROR_SUCCESS)
1706         return r;
1707
1708     count = MSI_RecordGetFieldCount(prec);
1709     *types = msi_alloc(count*sizeof(LPWSTR));
1710     if (!*types)
1711     {
1712         r = ERROR_OUTOFMEMORY;
1713         goto end;
1714     }
1715
1716     *numtypes = count;
1717     for (i=1; i<=count; i++ )
1718     {
1719         (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1720     }
1721
1722 end:
1723     msiobj_release( &prec->hdr );
1724     return r;
1725 }
1726
1727 static void merge_free_rows(MERGETABLE *table)
1728 {
1729     struct list *item, *cursor;
1730
1731     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1732     {
1733         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1734
1735         list_remove(&row->entry);
1736         msiobj_release(&row->data->hdr);
1737         msi_free(row);
1738     }
1739 }
1740
1741 static void free_merge_table(MERGETABLE *table)
1742 {
1743     UINT i;
1744
1745     if (table->labels != NULL)
1746     {
1747         for (i = 0; i < table->numlabels; i++)
1748             msi_free(table->labels[i]);
1749
1750         msi_free(table->labels);
1751     }
1752
1753     if (table->columns != NULL)
1754     {
1755         for (i = 0; i < table->numcolumns; i++)
1756             msi_free(table->columns[i]);
1757
1758         msi_free(table->columns);
1759     }
1760
1761     if (table->types != NULL)
1762     {
1763         for (i = 0; i < table->numtypes; i++)
1764             msi_free(table->types[i]);
1765
1766         msi_free(table->types);
1767     }
1768
1769     msi_free(table->name);
1770     merge_free_rows(table);
1771
1772     msi_free(table);
1773 }
1774
1775 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1776 {
1777     UINT r;
1778     MERGETABLE *table;
1779     MSIQUERY *mergeview = NULL;
1780
1781     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1782         'F','R','O','M',' ','`','%','s','`',0};
1783
1784     table = msi_alloc_zero(sizeof(MERGETABLE));
1785     if (!table)
1786     {
1787        *ptable = NULL;
1788        return ERROR_OUTOFMEMORY;
1789     }
1790
1791     r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1792     if (r != ERROR_SUCCESS)
1793         goto err;
1794
1795     r = MSI_OpenQuery(db, &mergeview, query, name);
1796     if (r != ERROR_SUCCESS)
1797         goto err;
1798
1799     r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1800     if (r != ERROR_SUCCESS)
1801         goto err;
1802
1803     r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1804     if (r != ERROR_SUCCESS)
1805         goto err;
1806
1807     list_init(&table->rows);
1808
1809     table->name = strdupW(name);
1810     table->numconflicts = 0;
1811
1812     msiobj_release(&mergeview->hdr);
1813     *ptable = table;
1814     return ERROR_SUCCESS;
1815
1816 err:
1817     msiobj_release(&mergeview->hdr);
1818     free_merge_table(table);
1819     *ptable = NULL;
1820     return r;
1821 }
1822
1823 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1824 {
1825     MERGEDATA *data = param;
1826     MERGETABLE *table;
1827     MSIQUERY *dbview = NULL;
1828     MSIQUERY *mergeview = NULL;
1829     LPCWSTR name;
1830     UINT r;
1831
1832     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1833         'F','R','O','M',' ','`','%','s','`',0};
1834
1835     name = MSI_RecordGetString(rec, 1);
1836
1837     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1838     if (r != ERROR_SUCCESS)
1839         goto done;
1840
1841     if (TABLE_Exists(data->db, name))
1842     {
1843         r = MSI_OpenQuery(data->db, &dbview, query, name);
1844         if (r != ERROR_SUCCESS)
1845             goto done;
1846
1847         r = merge_verify_colnames(dbview, mergeview);
1848         if (r != ERROR_SUCCESS)
1849             goto done;
1850
1851         r = merge_verify_primary_keys(data->db, data->merge, name);
1852         if (r != ERROR_SUCCESS)
1853             goto done;
1854     }
1855
1856     r = msi_get_merge_table(data->merge, name, &table);
1857     if (r != ERROR_SUCCESS)
1858         goto done;
1859
1860     data->curtable = table;
1861     data->curview = mergeview;
1862     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1863     if (r != ERROR_SUCCESS)
1864     {
1865         free_merge_table(table);
1866         goto done;
1867     }
1868
1869     list_add_tail(data->tabledata, &table->entry);
1870
1871 done:
1872     msiobj_release(&dbview->hdr);
1873     msiobj_release(&mergeview->hdr);
1874     return r;
1875 }
1876
1877 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1878                               struct list *tabledata)
1879 {
1880     static const WCHAR query[] = {
1881         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1882         '`','_','T','a','b','l','e','s','`',0};
1883     MSIQUERY *view;
1884     MERGEDATA data;
1885     UINT r;
1886
1887     r = MSI_DatabaseOpenViewW(merge, query, &view);
1888     if (r != ERROR_SUCCESS)
1889         return r;
1890
1891     data.db = db;
1892     data.merge = merge;
1893     data.tabledata = tabledata;
1894     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1895     msiobj_release(&view->hdr);
1896     return r;
1897 }
1898
1899 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1900 {
1901     UINT r;
1902     MERGEROW *row;
1903     MSIVIEW *tv;
1904
1905     if (!TABLE_Exists(db, table->name))
1906     {
1907         r = msi_add_table_to_db(db, table->columns, table->types,
1908                table->labels, table->numlabels, table->numcolumns);
1909         if (r != ERROR_SUCCESS)
1910            return ERROR_FUNCTION_FAILED;
1911     }
1912
1913     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1914     {
1915         r = TABLE_CreateView(db, table->name, &tv);
1916         if (r != ERROR_SUCCESS)
1917             return r;
1918
1919         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1920         tv->ops->delete(tv);
1921
1922         if (r != ERROR_SUCCESS)
1923             return r;
1924     }
1925
1926     return ERROR_SUCCESS;
1927 }
1928
1929 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1930                                 LPWSTR table, DWORD numconflicts)
1931 {
1932     UINT r;
1933     MSIQUERY *view;
1934
1935     static const WCHAR create[] = {
1936         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1937         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1938         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1939         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1940         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1941         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1942         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1943     static const WCHAR insert[] = {
1944         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1945         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1946         '`','N','u','m','R','o','w','M','e','r','g','e',
1947         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1948         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1949
1950     if (!TABLE_Exists(db, error))
1951     {
1952         r = MSI_OpenQuery(db, &view, create, error);
1953         if (r != ERROR_SUCCESS)
1954             return r;
1955
1956         r = MSI_ViewExecute(view, NULL);
1957         msiobj_release(&view->hdr);
1958         if (r != ERROR_SUCCESS)
1959             return r;
1960     }
1961
1962     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1963     if (r != ERROR_SUCCESS)
1964         return r;
1965
1966     r = MSI_ViewExecute(view, NULL);
1967     msiobj_release(&view->hdr);
1968     return r;
1969 }
1970
1971 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1972                               LPCWSTR szTableName)
1973 {
1974     struct list tabledata = LIST_INIT(tabledata);
1975     struct list *item, *cursor;
1976     MSIDATABASE *db, *merge;
1977     MERGETABLE *table;
1978     BOOL conflicts;
1979     UINT r;
1980
1981     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1982           debugstr_w(szTableName));
1983
1984     if (szTableName && !*szTableName)
1985         return ERROR_INVALID_TABLE;
1986
1987     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1988     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1989     if (!db || !merge)
1990     {
1991         r = ERROR_INVALID_HANDLE;
1992         goto done;
1993     }
1994
1995     r = gather_merge_data(db, merge, &tabledata);
1996     if (r != ERROR_SUCCESS)
1997         goto done;
1998
1999     conflicts = FALSE;
2000     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
2001     {
2002         if (table->numconflicts)
2003         {
2004             conflicts = TRUE;
2005
2006             r = update_merge_errors(db, szTableName, table->name,
2007                                     table->numconflicts);
2008             if (r != ERROR_SUCCESS)
2009                 break;
2010         }
2011         else
2012         {
2013             r = merge_table(db, table);
2014             if (r != ERROR_SUCCESS)
2015                 break;
2016         }
2017     }
2018
2019     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
2020     {
2021         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
2022         list_remove(&table->entry);
2023         free_merge_table(table);
2024     }
2025
2026     if (conflicts)
2027         r = ERROR_FUNCTION_FAILED;
2028
2029 done:
2030     msiobj_release(&db->hdr);
2031     msiobj_release(&merge->hdr);
2032     return r;
2033 }
2034
2035 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
2036 {
2037     MSIDBSTATE ret = MSIDBSTATE_READ;
2038     MSIDATABASE *db;
2039
2040     TRACE("%d\n", handle);
2041
2042     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
2043     if( !db )
2044     {
2045         IWineMsiRemoteDatabase *remote_database;
2046
2047         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
2048         if ( !remote_database )
2049             return MSIDBSTATE_ERROR;
2050
2051         IWineMsiRemoteDatabase_Release( remote_database );
2052         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
2053
2054         return MSIDBSTATE_READ;
2055     }
2056
2057     if (db->mode != MSIDBOPEN_READONLY )
2058         ret = MSIDBSTATE_WRITE;
2059     msiobj_release( &db->hdr );
2060
2061     return ret;
2062 }
2063
2064 typedef struct _msi_remote_database_impl {
2065     IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface;
2066     MSIHANDLE database;
2067     LONG refs;
2068 } msi_remote_database_impl;
2069
2070 static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface )
2071 {
2072     return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface);
2073 }
2074
2075 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2076                                           REFIID riid,LPVOID *ppobj)
2077 {
2078     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2079         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2080     {
2081         IUnknown_AddRef( iface );
2082         *ppobj = iface;
2083         return S_OK;
2084     }
2085
2086     return E_NOINTERFACE;
2087 }
2088
2089 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2090 {
2091     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2092
2093     return InterlockedIncrement( &This->refs );
2094 }
2095
2096 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2097 {
2098     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2099     ULONG r;
2100
2101     r = InterlockedDecrement( &This->refs );
2102     if (r == 0)
2103     {
2104         MsiCloseHandle( This->database );
2105         msi_free( This );
2106     }
2107     return r;
2108 }
2109
2110 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2111                                              LPCWSTR table, MSICONDITION *persistent )
2112 {
2113     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2114     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2115     return S_OK;
2116 }
2117
2118 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2119                                           LPCWSTR table, MSIHANDLE *keys )
2120 {
2121     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2122     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2123     return HRESULT_FROM_WIN32(r);
2124 }
2125
2126 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2127                                                 UINT updatecount, MSIHANDLE *suminfo )
2128 {
2129     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2130     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2131     return HRESULT_FROM_WIN32(r);
2132 }
2133
2134 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2135                                     LPCWSTR query, MSIHANDLE *view )
2136 {
2137     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2138     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2139     return HRESULT_FROM_WIN32(r);
2140 }
2141
2142 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2143 {
2144     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2145     This->database = handle;
2146     return S_OK;
2147 }
2148
2149 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2150 {
2151     mrd_QueryInterface,
2152     mrd_AddRef,
2153     mrd_Release,
2154     mrd_IsTablePersistent,
2155     mrd_GetPrimaryKeys,
2156     mrd_GetSummaryInformation,
2157     mrd_OpenView,
2158     mrd_SetMsiHandle,
2159 };
2160
2161 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2162 {
2163     msi_remote_database_impl *This;
2164
2165     This = msi_alloc( sizeof *This );
2166     if (!This)
2167         return E_OUTOFMEMORY;
2168
2169     This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl;
2170     This->database = 0;
2171     This->refs = 1;
2172
2173     *ppObj = This;
2174
2175     return S_OK;
2176 }