msctf: Implement ITfClientId.
[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
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32 #include "msi.h"
33 #include "msiquery.h"
34 #include "msipriv.h"
35 #include "objidl.h"
36 #include "objbase.h"
37 #include "msiserver.h"
38 #include "query.h"
39
40 #include "initguid.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44 DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
45              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
46 DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
47              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
48
49 /*
50  *  .MSI  file format
51  *
52  *  An .msi file is a structured storage file.
53  *  It contains a number of streams.
54  *  A stream for each table in the database.
55  *  Two streams for the string table in the database.
56  *  Any binary data in a table is a reference to a stream.
57  */
58
59 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
60 {
61     MSIDATABASE *db = (MSIDATABASE *) arg;
62
63     msi_free(db->path);
64     free_cached_tables( db );
65     msi_free_transforms( db );
66     msi_destroy_stringtable( db->strings );
67     IStorage_Release( db->storage );
68     if (db->deletefile)
69     {
70         DeleteFileW( db->deletefile );
71         msi_free( db->deletefile );
72     }
73 }
74
75 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
76 {
77     IStorage *stg = NULL;
78     HRESULT r;
79     MSIDATABASE *db = NULL;
80     UINT ret = ERROR_FUNCTION_FAILED;
81     LPCWSTR szMode, save_path;
82     STATSTG stat;
83     BOOL created = FALSE;
84     WCHAR path[MAX_PATH];
85
86     static const WCHAR backslash[] = {'\\',0};
87     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
88
89     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
90
91     if( !pdb )
92         return ERROR_INVALID_PARAMETER;
93
94     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
95         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
96     {
97         TRACE("Database is a patch\n");
98         szPersist -= MSIDBOPEN_PATCHFILE;
99     }
100
101     save_path = szDBPath;
102     szMode = szPersist;
103     if( HIWORD( szPersist ) )
104     {
105         if (!CopyFileW( szDBPath, szPersist, FALSE ))
106             return ERROR_OPEN_FAILED;
107
108         szDBPath = szPersist;
109         szPersist = MSIDBOPEN_TRANSACT;
110         created = TRUE;
111     }
112
113     if( szPersist == MSIDBOPEN_READONLY )
114     {
115         r = StgOpenStorage( szDBPath, NULL,
116               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
117     }
118     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
119     {
120         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
121          * used here: */
122         r = StgCreateDocfile( szDBPath,
123               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
124         if( r == ERROR_SUCCESS )
125         {
126             IStorage_SetClass( stg, &CLSID_MsiDatabase );
127             /* create the _Tables stream */
128             r = write_stream_data(stg, szTables, NULL, 0, TRUE);
129             if (SUCCEEDED(r))
130                 r = msi_init_string_table( stg );
131         }
132         created = TRUE;
133     }
134     else if( szPersist == MSIDBOPEN_TRANSACT )
135     {
136         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
137          * used here: */
138         r = StgOpenStorage( szDBPath, NULL,
139               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
140     }
141     else if( szPersist == MSIDBOPEN_DIRECT )
142     {
143         r = StgOpenStorage( szDBPath, NULL,
144               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
145     }
146     else
147     {
148         ERR("unknown flag %p\n",szPersist);
149         return ERROR_INVALID_PARAMETER;
150     }
151
152     if( FAILED( r ) || !stg )
153     {
154         FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
155         return ERROR_FUNCTION_FAILED;
156     }
157
158     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
159     if( FAILED( r ) )
160     {
161         FIXME("Failed to stat storage\n");
162         goto end;
163     }
164
165     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
166          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
167     {
168         ERR("storage GUID is not a MSI database GUID %s\n",
169              debugstr_guid(&stat.clsid) );
170         goto end;
171     }
172
173     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
174                               MSI_CloseDatabase );
175     if( !db )
176     {
177         FIXME("Failed to allocate a handle\n");
178         goto end;
179     }
180
181     if (!strchrW( save_path, '\\' ))
182     {
183         GetCurrentDirectoryW( MAX_PATH, path );
184         lstrcatW( path, backslash );
185         lstrcatW( path, save_path );
186     }
187     else
188         lstrcpyW( path, save_path );
189
190     db->path = strdupW( path );
191
192     if( TRACE_ON( msi ) )
193         enum_stream_names( stg );
194
195     db->storage = stg;
196     db->mode = szMode;
197     if (created)
198         db->deletefile = strdupW( szDBPath );
199     else
200         db->deletefile = NULL;
201     list_init( &db->tables );
202     list_init( &db->transforms );
203
204     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
205     if( !db->strings )
206         goto end;
207
208     ret = ERROR_SUCCESS;
209
210     msiobj_addref( &db->hdr );
211     IStorage_AddRef( stg );
212     *pdb = db;
213
214 end:
215     if( db )
216         msiobj_release( &db->hdr );
217     if( stg )
218         IStorage_Release( stg );
219
220     return ret;
221 }
222
223 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
224 {
225     MSIDATABASE *db;
226     UINT ret;
227
228     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
229
230     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
231     if( ret == ERROR_SUCCESS )
232     {
233         *phDB = alloc_msihandle( &db->hdr );
234         if (! *phDB)
235             ret = ERROR_NOT_ENOUGH_MEMORY;
236         msiobj_release( &db->hdr );
237     }
238
239     return ret;
240 }
241
242 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
243 {
244     HRESULT r = ERROR_FUNCTION_FAILED;
245     LPWSTR szwDBPath = NULL, szwPersist = NULL;
246
247     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
248
249     if( szDBPath )
250     {
251         szwDBPath = strdupAtoW( szDBPath );
252         if( !szwDBPath )
253             goto end;
254     }
255
256     if( HIWORD(szPersist) )
257     {
258         szwPersist = strdupAtoW( szPersist );
259         if( !szwPersist )
260             goto end;
261     }
262     else
263         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
264
265     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
266
267 end:
268     if( HIWORD(szPersist) )
269         msi_free( szwPersist );
270     msi_free( szwDBPath );
271
272     return r;
273 }
274
275 static LPWSTR msi_read_text_archive(LPCWSTR path)
276 {
277     HANDLE file;
278     LPSTR data = NULL;
279     LPWSTR wdata = NULL;
280     DWORD read, size = 0;
281
282     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
283     if (file == INVALID_HANDLE_VALUE)
284         return NULL;
285
286     size = GetFileSize( file, NULL );
287     data = msi_alloc( size + 1 );
288     if (!data)
289         goto done;
290
291     if (!ReadFile( file, data, size, &read, NULL ))
292         goto done;
293
294     data[size] = '\0';
295     wdata = strdupAtoW( data );
296
297 done:
298     CloseHandle( file );
299     msi_free( data );
300     return wdata;
301 }
302
303 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
304 {
305     LPWSTR ptr = *line, save;
306     DWORD i, count = 1;
307
308     *entries = NULL;
309
310     /* stay on this line */
311     while (*ptr && *ptr != '\n')
312     {
313         /* entries are separated by tabs */
314         if (*ptr == '\t')
315             count++;
316
317         ptr++;
318     }
319
320     *entries = msi_alloc(count * sizeof(LPWSTR));
321     if (!*entries)
322         return;
323
324     /* store pointers into the data */
325     for (i = 0, ptr = *line; i < count; i++)
326     {
327         while (*ptr && *ptr == '\r') ptr++;
328         save = ptr;
329
330         while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
331
332         /* NULL-separate the data */
333         if (*ptr == '\n' || *ptr == '\r')
334         {
335             while (*ptr == '\n' || *ptr == '\r')
336                 *(ptr++) = '\0';
337         }
338         else if (*ptr)
339             *ptr++ = '\0';
340
341         (*entries)[i] = save;
342     }
343
344     /* move to the next line if there's more, else EOF */
345     *line = ptr;
346
347     if (num_entries)
348         *num_entries = count;
349 }
350
351 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
352 {
353     LPWSTR prelude;
354     DWORD size;
355
356     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
357
358     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
359     prelude = msi_alloc(size * sizeof(WCHAR));
360     if (!prelude)
361         return NULL;
362
363     sprintfW(prelude, create_fmt, table);
364     return prelude;
365 }
366
367 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
368 {
369     LPWSTR columns, p;
370     LPCWSTR type;
371     DWORD sql_size = 1, i, len;
372     WCHAR expanded[128], *ptr;
373     WCHAR size[10], comma[2], extra[30];
374
375     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
376     static const WCHAR size_fmt[] = {'(','%','s',')',0};
377     static const WCHAR type_char[] = {'C','H','A','R',0};
378     static const WCHAR type_int[] = {'I','N','T',0};
379     static const WCHAR type_long[] = {'L','O','N','G',0};
380     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
381     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
382
383     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
384     if (!columns)
385         return NULL;
386
387     for (i = 0; i < num_columns; i++)
388     {
389         type = NULL;
390         comma[1] = size[0] = extra[0] = '\0';
391
392         if (i == num_columns - 1)
393             comma[0] = '\0';
394         else
395             comma[0] = ',';
396
397         ptr = &types[i][1];
398         len = atolW(ptr);
399         extra[0] = '\0';
400
401         switch (types[i][0])
402         {
403             case 'l':
404                 lstrcpyW(extra, type_notnull);
405             case 'L':
406                 lstrcatW(extra, localizable);
407                 type = type_char;
408                 sprintfW(size, size_fmt, ptr);
409                 break;
410             case 's':
411                 lstrcpyW(extra, type_notnull);
412             case 'S':
413                 type = type_char;
414                 sprintfW(size, size_fmt, ptr);
415                 break;
416             case 'i':
417                 lstrcpyW(extra, type_notnull);
418             case 'I':
419                 if (len == 2)
420                     type = type_int;
421                 else
422                     type = type_long;
423                 break;
424             default:
425                 ERR("Unknown type: %c\n", types[i][0]);
426                 msi_free(columns);
427                 return NULL;
428         }
429
430         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
431         sql_size += lstrlenW(expanded);
432
433         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
434         if (!p)
435         {
436             msi_free(columns);
437             return NULL;
438         }
439         columns = p;
440
441         lstrcatW(columns, expanded);
442     }
443
444     return columns;
445 }
446
447 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
448 {
449     LPWSTR postlude, keys, ptr;
450     DWORD size, key_size, i;
451
452     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
453     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
454
455     for (i = 0, size = 1; i < num_keys; i++)
456         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
457
458     keys = msi_alloc(size * sizeof(WCHAR));
459     if (!keys)
460         return NULL;
461
462     for (i = 0, ptr = keys; i < num_keys; i++)
463     {
464         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
465         sprintfW(ptr, key_fmt, primary_keys[i]);
466         ptr += key_size;
467     }
468
469     /* remove final ', ' */
470     *(ptr - 2) = '\0';
471
472     size = lstrlenW(postlude_fmt) + size - 1;
473     postlude = msi_alloc(size * sizeof(WCHAR));
474     if (!postlude)
475         goto done;
476
477     sprintfW(postlude, postlude_fmt, keys);
478
479 done:
480     msi_free(keys);
481     return postlude;
482 }
483
484 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
485 {
486     UINT r;
487     DWORD size;
488     MSIQUERY *view;
489     LPWSTR create_sql;
490     LPWSTR prelude, columns_sql, postlude;
491
492     prelude = msi_build_createsql_prelude(labels[0]);
493     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
494     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
495
496     if (!prelude || !columns_sql || !postlude)
497         return ERROR_OUTOFMEMORY;
498
499     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
500     create_sql = msi_alloc(size * sizeof(WCHAR));
501     if (!create_sql)
502         return ERROR_OUTOFMEMORY;
503
504     lstrcpyW(create_sql, prelude);
505     lstrcatW(create_sql, columns_sql);
506     lstrcatW(create_sql, postlude);
507
508     msi_free(prelude);
509     msi_free(columns_sql);
510     msi_free(postlude);
511
512     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
513     msi_free(create_sql);
514
515     if (r != ERROR_SUCCESS)
516         return r;
517
518     r = MSI_ViewExecute(view, NULL);
519     MSI_ViewClose(view);
520     msiobj_release(&view->hdr);
521
522     return r;
523 }
524
525 static UINT construct_record(DWORD num_columns, LPWSTR *types,
526                              LPWSTR *data, MSIRECORD **rec)
527 {
528     UINT i;
529
530     *rec = MSI_CreateRecord(num_columns);
531     if (!*rec)
532         return ERROR_OUTOFMEMORY;
533
534     for (i = 0; i < num_columns; i++)
535     {
536         switch (types[i][0])
537         {
538             case 'L': case 'l': case 'S': case 's':
539                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
540                 break;
541             case 'I': case 'i':
542                 if (*data[i])
543                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
544                 break;
545             default:
546                 ERR("Unhandled column type: %c\n", types[i][0]);
547                 msiobj_release(&(*rec)->hdr);
548                 return ERROR_FUNCTION_FAILED;
549         }
550     }
551
552     return ERROR_SUCCESS;
553 }
554
555 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
556                                      LPWSTR *labels, LPWSTR **records,
557                                      int num_columns, int num_records)
558 {
559     UINT r;
560     int i;
561     MSIQUERY *view;
562     MSIRECORD *rec;
563
564     static const WCHAR select[] = {
565         'S','E','L','E','C','T',' ','*',' ',
566         'F','R','O','M',' ','`','%','s','`',0
567     };
568
569     r = MSI_OpenQuery(db, &view, select, labels[0]);
570     if (r != ERROR_SUCCESS)
571         return r;
572
573     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
574     {
575         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
576         if (r != ERROR_SUCCESS)
577             goto done;
578     }
579
580     for (i = 0; i < num_records; i++)
581     {
582         r = construct_record(num_columns, types, records[i], &rec);
583         if (r != ERROR_SUCCESS)
584             goto done;
585
586         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
587         if (r != ERROR_SUCCESS)
588         {
589             msiobj_release(&rec->hdr);
590             goto done;
591         }
592
593         msiobj_release(&rec->hdr);
594     }
595
596 done:
597     msiobj_release(&view->hdr);
598     return r;
599 }
600
601 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
602 {
603     UINT r;
604     DWORD len, i;
605     DWORD num_labels, num_types;
606     DWORD num_columns, num_records = 0;
607     LPWSTR *columns, *types, *labels;
608     LPWSTR path, ptr, data;
609     LPWSTR **records = NULL;
610     LPWSTR **temp_records;
611
612     static const WCHAR backslash[] = {'\\',0};
613     static const WCHAR suminfo[] =
614         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
615
616     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
617
618     if( folder == NULL || file == NULL )
619         return ERROR_INVALID_PARAMETER;
620
621     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
622     path = msi_alloc( len * sizeof(WCHAR) );
623     if (!path)
624         return ERROR_OUTOFMEMORY;
625
626     lstrcpyW( path, folder );
627     lstrcatW( path, backslash );
628     lstrcatW( path, file );
629
630     data = msi_read_text_archive( path );
631
632     ptr = data;
633     msi_parse_line( &ptr, &columns, &num_columns );
634     msi_parse_line( &ptr, &types, &num_types );
635     msi_parse_line( &ptr, &labels, &num_labels );
636
637     if (num_columns != num_types)
638     {
639         r = ERROR_FUNCTION_FAILED;
640         goto done;
641     }
642
643     records = msi_alloc(sizeof(LPWSTR *));
644     if (!records)
645     {
646         r = ERROR_OUTOFMEMORY;
647         goto done;
648     }
649
650     /* read in the table records */
651     while (*ptr)
652     {
653         msi_parse_line( &ptr, &records[num_records], NULL );
654
655         num_records++;
656         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
657         if (!temp_records)
658         {
659             r = ERROR_OUTOFMEMORY;
660             goto done;
661         }
662         records = temp_records;
663     }
664
665     if (!strcmpW(labels[0], suminfo))
666     {
667         r = msi_add_suminfo( db, records, num_records, num_columns );
668         if (r != ERROR_SUCCESS)
669         {
670             r = ERROR_FUNCTION_FAILED;
671             goto done;
672         }
673     }
674     else
675     {
676         if (!TABLE_Exists(db, labels[0]))
677         {
678             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
679             if (r != ERROR_SUCCESS)
680             {
681                 r = ERROR_FUNCTION_FAILED;
682                 goto done;
683             }
684         }
685
686         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
687     }
688
689 done:
690     msi_free(path);
691     msi_free(data);
692     msi_free(columns);
693     msi_free(types);
694     msi_free(labels);
695
696     for (i = 0; i < num_records; i++)
697         msi_free(records[i]);
698
699     msi_free(records);
700
701     return r;
702 }
703
704 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
705 {
706     MSIDATABASE *db;
707     UINT r;
708
709     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
710
711     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
712     if( !db )
713     {
714         IWineMsiRemoteDatabase *remote_database;
715
716         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
717         if ( !remote_database )
718             return ERROR_INVALID_HANDLE;
719
720         IWineMsiRemoteDatabase_Release( remote_database );
721         WARN("MsiDatabaseImport not allowed during a custom action!\n");
722
723         return ERROR_SUCCESS;
724     }
725
726     r = MSI_DatabaseImport( db, szFolder, szFilename );
727     msiobj_release( &db->hdr );
728     return r;
729 }
730
731 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
732                LPCSTR szFolder, LPCSTR szFilename )
733 {
734     LPWSTR path = NULL, file = NULL;
735     UINT r = ERROR_OUTOFMEMORY;
736
737     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
738
739     if( szFolder )
740     {
741         path = strdupAtoW( szFolder );
742         if( !path )
743             goto end;
744     }
745
746     if( szFilename )
747     {
748         file = strdupAtoW( szFilename );
749         if( !file )
750             goto end;
751     }
752
753     r = MsiDatabaseImportW( handle, path, file );
754
755 end:
756     msi_free( path );
757     msi_free( file );
758
759     return r;
760 }
761
762 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
763 {
764     UINT i, count, len, r = ERROR_SUCCESS;
765     const char *sep;
766     char *buffer;
767     DWORD sz;
768
769     len = 0x100;
770     buffer = msi_alloc( len );
771     if ( !buffer )
772         return ERROR_OUTOFMEMORY;
773
774     count = MSI_RecordGetFieldCount( row );
775     for ( i=start; i<=count; i++ )
776     {
777         sz = len;
778         r = MSI_RecordGetStringA( row, i, buffer, &sz );
779         if (r == ERROR_MORE_DATA)
780         {
781             char *p = msi_realloc( buffer, sz + 1 );
782             if (!p)
783                 break;
784             len = sz + 1;
785             buffer = p;
786         }
787         sz = len;
788         r = MSI_RecordGetStringA( row, i, buffer, &sz );
789         if (r != ERROR_SUCCESS)
790             break;
791
792         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
793         {
794             r = ERROR_FUNCTION_FAILED;
795             break;
796         }
797
798         sep = (i < count) ? "\t" : "\r\n";
799         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
800         {
801             r = ERROR_FUNCTION_FAILED;
802             break;
803         }
804     }
805     msi_free( buffer );
806     return r;
807 }
808
809 static UINT msi_export_row( MSIRECORD *row, void *arg )
810 {
811     return msi_export_record( arg, row, 1 );
812 }
813
814 static UINT msi_export_forcecodepage( HANDLE handle )
815 {
816     DWORD sz;
817
818     static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
819
820     FIXME("Read the codepage from the strings table!\n");
821
822     sz = lstrlenA(data) + 1;
823     if (!WriteFile(handle, data, sz, &sz, NULL))
824         return ERROR_FUNCTION_FAILED;
825
826     return ERROR_SUCCESS;
827 }
828
829 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
830                LPCWSTR folder, LPCWSTR file )
831 {
832     static const WCHAR query[] = {
833         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
834     static const WCHAR szbs[] = { '\\', 0 };
835     static const WCHAR forcecodepage[] = {
836         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
837     MSIRECORD *rec = NULL;
838     MSIQUERY *view = NULL;
839     LPWSTR filename;
840     HANDLE handle;
841     UINT len, r;
842
843     TRACE("%p %s %s %s\n", db, debugstr_w(table),
844           debugstr_w(folder), debugstr_w(file) );
845
846     if( folder == NULL || file == NULL )
847         return ERROR_INVALID_PARAMETER;
848
849     len = lstrlenW(folder) + lstrlenW(file) + 2;
850     filename = msi_alloc(len * sizeof (WCHAR));
851     if (!filename)
852         return ERROR_OUTOFMEMORY;
853
854     lstrcpyW( filename, folder );
855     lstrcatW( filename, szbs );
856     lstrcatW( filename, file );
857
858     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
859                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
860     msi_free( filename );
861     if (handle == INVALID_HANDLE_VALUE)
862         return ERROR_FUNCTION_FAILED;
863
864     if (!lstrcmpW( table, forcecodepage ))
865     {
866         r = msi_export_forcecodepage( handle );
867         goto done;
868     }
869
870     r = MSI_OpenQuery( db, &view, query, table );
871     if (r == ERROR_SUCCESS)
872     {
873         /* write out row 1, the column names */
874         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
875         if (r == ERROR_SUCCESS)
876         {
877             msi_export_record( handle, rec, 1 );
878             msiobj_release( &rec->hdr );
879         }
880
881         /* write out row 2, the column types */
882         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
883         if (r == ERROR_SUCCESS)
884         {
885             msi_export_record( handle, rec, 1 );
886             msiobj_release( &rec->hdr );
887         }
888
889         /* write out row 3, the table name + keys */
890         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
891         if (r == ERROR_SUCCESS)
892         {
893             MSI_RecordSetStringW( rec, 0, table );
894             msi_export_record( handle, rec, 0 );
895             msiobj_release( &rec->hdr );
896         }
897
898         /* write out row 4 onwards, the data */
899         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
900         msiobj_release( &view->hdr );
901     }
902
903 done:
904     CloseHandle( handle );
905     return r;
906 }
907
908 /***********************************************************************
909  * MsiExportDatabaseW        [MSI.@]
910  *
911  * Writes a file containing the table data as tab separated ASCII.
912  *
913  * The format is as follows:
914  *
915  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
916  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
917  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
918  *
919  * Followed by the data, starting at row 1 with one row per line
920  *
921  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
922  */
923 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
924                LPCWSTR szFolder, LPCWSTR szFilename )
925 {
926     MSIDATABASE *db;
927     UINT r;
928
929     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
930           debugstr_w(szFolder), debugstr_w(szFilename));
931
932     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
933     if( !db )
934     {
935         IWineMsiRemoteDatabase *remote_database;
936
937         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
938         if ( !remote_database )
939             return ERROR_INVALID_HANDLE;
940
941         IWineMsiRemoteDatabase_Release( remote_database );
942         WARN("MsiDatabaseExport not allowed during a custom action!\n");
943
944         return ERROR_SUCCESS;
945     }
946
947     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
948     msiobj_release( &db->hdr );
949     return r;
950 }
951
952 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
953                LPCSTR szFolder, LPCSTR szFilename )
954 {
955     LPWSTR path = NULL, file = NULL, table = NULL;
956     UINT r = ERROR_OUTOFMEMORY;
957
958     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
959           debugstr_a(szFolder), debugstr_a(szFilename));
960
961     if( szTable )
962     {
963         table = strdupAtoW( szTable );
964         if( !table )
965             goto end;
966     }
967
968     if( szFolder )
969     {
970         path = strdupAtoW( szFolder );
971         if( !path )
972             goto end;
973     }
974
975     if( szFilename )
976     {
977         file = strdupAtoW( szFilename );
978         if( !file )
979             goto end;
980     }
981
982     r = MsiDatabaseExportW( handle, table, path, file );
983
984 end:
985     msi_free( table );
986     msi_free( path );
987     msi_free( file );
988
989     return r;
990 }
991
992 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
993                               LPCSTR szTableName)
994 {
995     UINT r;
996     LPWSTR table;
997
998     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
999           debugstr_a(szTableName));
1000
1001     table = strdupAtoW(szTableName);
1002     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1003
1004     msi_free(table);
1005     return r;
1006 }
1007
1008 typedef struct _tagMERGETABLE
1009 {
1010     struct list entry;
1011     struct list rows;
1012     LPWSTR name;
1013     DWORD numconflicts;
1014 } MERGETABLE;
1015
1016 typedef struct _tagMERGEROW
1017 {
1018     struct list entry;
1019     MSIRECORD *data;
1020 } MERGEROW;
1021
1022 typedef struct _tagMERGEDATA
1023 {
1024     MSIDATABASE *db;
1025     MSIDATABASE *merge;
1026     MERGETABLE *curtable;
1027     MSIQUERY *curview;
1028     struct list *tabledata;
1029 } MERGEDATA;
1030
1031 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1032 {
1033     MSIRECORD *dbrec, *mergerec;
1034     UINT r, i, count;
1035
1036     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1037     if (r != ERROR_SUCCESS)
1038         return r;
1039
1040     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1041     if (r != ERROR_SUCCESS)
1042         return r;
1043
1044     count = MSI_RecordGetFieldCount(dbrec);
1045     for (i = 1; i <= count; i++)
1046     {
1047         if (!MSI_RecordGetString(mergerec, i))
1048             break;
1049
1050         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1051                      MSI_RecordGetString(mergerec, i)))
1052         {
1053             r = ERROR_DATATYPE_MISMATCH;
1054             goto done;
1055         }
1056     }
1057
1058     msiobj_release(&dbrec->hdr);
1059     msiobj_release(&mergerec->hdr);
1060     dbrec = mergerec = NULL;
1061
1062     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1063     if (r != ERROR_SUCCESS)
1064         return r;
1065
1066     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1067     if (r != ERROR_SUCCESS)
1068         return r;
1069
1070     count = MSI_RecordGetFieldCount(dbrec);
1071     for (i = 1; i <= count; i++)
1072     {
1073         if (!MSI_RecordGetString(mergerec, i))
1074             break;
1075
1076         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1077                      MSI_RecordGetString(mergerec, i)))
1078         {
1079             r = ERROR_DATATYPE_MISMATCH;
1080             break;
1081         }
1082     }
1083
1084 done:
1085     msiobj_release(&dbrec->hdr);
1086     msiobj_release(&mergerec->hdr);
1087
1088     return r;
1089 }
1090
1091 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1092                                       LPCWSTR table)
1093 {
1094     MSIRECORD *dbrec, *mergerec = NULL;
1095     UINT r, i, count;
1096
1097     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1098     if (r != ERROR_SUCCESS)
1099         return r;
1100
1101     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1102     if (r != ERROR_SUCCESS)
1103         goto done;
1104
1105     count = MSI_RecordGetFieldCount(dbrec);
1106     if (count != MSI_RecordGetFieldCount(mergerec))
1107     {
1108         r = ERROR_DATATYPE_MISMATCH;
1109         goto done;
1110     }
1111
1112     for (i = 1; i <= count; i++)
1113     {
1114         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1115                      MSI_RecordGetString(mergerec, i)))
1116         {
1117             r = ERROR_DATATYPE_MISMATCH;
1118             goto done;
1119         }
1120     }
1121
1122 done:
1123     msiobj_release(&dbrec->hdr);
1124     msiobj_release(&mergerec->hdr);
1125
1126     return r;
1127 }
1128
1129 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1130 {
1131     MSIRECORD *colnames;
1132     LPWSTR str;
1133     UINT r, i = 0;
1134     int cmp;
1135
1136     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1137     if (r != ERROR_SUCCESS)
1138         return NULL;
1139
1140     do
1141     {
1142         str = msi_dup_record_field(colnames, ++i);
1143         cmp = lstrcmpW(key, str);
1144         msi_free(str);
1145     } while (cmp);
1146
1147     msiobj_release(&colnames->hdr);
1148     return msi_dup_record_field(rec, i);
1149 }
1150
1151 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1152                                     LPWSTR table, MSIRECORD *rec)
1153 {
1154     LPWSTR query = NULL, clause = NULL;
1155     LPWSTR ptr = NULL, val;
1156     LPCWSTR setptr;
1157     DWORD size = 1, oldsize;
1158     LPCWSTR key;
1159     MSIRECORD *keys;
1160     UINT r, i, count;
1161
1162     static const WCHAR keyset[] = {
1163         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1164     static const WCHAR lastkeyset[] = {
1165         '`','%','s','`',' ','=',' ','%','s',' ',0};
1166     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1167         'F','R','O','M',' ','`','%','s','`',' ',
1168         'W','H','E','R','E',' ','%','s',0};
1169
1170     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1171     if (r != ERROR_SUCCESS)
1172         return NULL;
1173
1174     clause = msi_alloc_zero(size * sizeof(WCHAR));
1175     if (!clause)
1176         goto done;
1177
1178     ptr = clause;
1179     count = MSI_RecordGetFieldCount(keys);
1180     for (i = 1; i <= count; i++)
1181     {
1182         key = MSI_RecordGetString(keys, i);
1183         val = get_key_value(view, key, rec);
1184
1185         if (i == count)
1186             setptr = lastkeyset;
1187         else
1188             setptr = keyset;
1189
1190         oldsize = size;
1191         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1192         clause = msi_realloc(clause, size * sizeof (WCHAR));
1193         if (!clause)
1194         {
1195             msi_free(val);
1196             goto done;
1197         }
1198
1199         ptr = clause + oldsize - 1;
1200         sprintfW(ptr, setptr, key, val);
1201         msi_free(val);
1202     }
1203
1204     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1205     query = msi_alloc(size * sizeof(WCHAR));
1206     if (!query)
1207         goto done;
1208
1209     sprintfW(query, fmt, table, clause);
1210
1211 done:
1212     msi_free(clause);
1213     msiobj_release(&keys->hdr);
1214     return query;
1215 }
1216
1217 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1218 {
1219     MERGEDATA *data = param;
1220     MERGETABLE *table = data->curtable;
1221     MERGEROW *mergerow;
1222     MSIQUERY *dbview;
1223     MSIRECORD *row = NULL;
1224     LPWSTR query;
1225     UINT r;
1226
1227     query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1228     if (!query)
1229         return ERROR_OUTOFMEMORY;
1230
1231     r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1232     if (r != ERROR_SUCCESS)
1233         goto done;
1234
1235     r = MSI_ViewExecute(dbview, NULL);
1236     if (r != ERROR_SUCCESS)
1237         goto done;
1238
1239     r = MSI_ViewFetch(dbview, &row);
1240     if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1241     {
1242         table->numconflicts++;
1243         goto done;
1244     }
1245     else if (r != ERROR_NO_MORE_ITEMS)
1246         goto done;
1247
1248     mergerow = msi_alloc(sizeof(MERGEROW));
1249     if (!mergerow)
1250     {
1251         r = ERROR_OUTOFMEMORY;
1252         goto done;
1253     }
1254
1255     mergerow->data = MSI_CloneRecord(rec);
1256     if (!mergerow->data)
1257     {
1258         r = ERROR_OUTOFMEMORY;
1259         msi_free(mergerow);
1260         goto done;
1261     }
1262
1263     list_add_tail(&table->rows, &mergerow->entry);
1264
1265 done:
1266     msi_free(query);
1267     msiobj_release(&row->hdr);
1268     msiobj_release(&dbview->hdr);
1269     return r;
1270 }
1271
1272 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1273 {
1274     MERGEDATA *data = param;
1275     MERGETABLE *table;
1276     MSIQUERY *dbview;
1277     MSIQUERY *mergeview = NULL;
1278     LPCWSTR name;
1279     UINT r;
1280
1281     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1282         'F','R','O','M',' ','`','%','s','`',0};
1283
1284     name = MSI_RecordGetString(rec, 1);
1285
1286     r = MSI_OpenQuery(data->db, &dbview, query, name);
1287     if (r != ERROR_SUCCESS)
1288         return r;
1289
1290     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1291     if (r != ERROR_SUCCESS)
1292         goto done;
1293
1294     r = merge_verify_colnames(dbview, mergeview);
1295     if (r != ERROR_SUCCESS)
1296         goto done;
1297
1298     r = merge_verify_primary_keys(data->db, data->merge, name);
1299     if (r != ERROR_SUCCESS)
1300         goto done;
1301
1302     table = msi_alloc(sizeof(MERGETABLE));
1303     if (!table)
1304     {
1305         r = ERROR_OUTOFMEMORY;
1306         goto done;
1307     }
1308
1309     list_init(&table->rows);
1310     table->name = strdupW(name);
1311     table->numconflicts = 0;
1312     data->curtable = table;
1313     data->curview = mergeview;
1314
1315     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1316     if (r != ERROR_SUCCESS)
1317     {
1318         msi_free(table->name);
1319         msi_free(table);
1320         goto done;
1321     }
1322
1323     list_add_tail(data->tabledata, &table->entry);
1324
1325 done:
1326     msiobj_release(&dbview->hdr);
1327     msiobj_release(&mergeview->hdr);
1328     return r;
1329 }
1330
1331 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1332                               struct list *tabledata)
1333 {
1334     UINT r;
1335     MSIQUERY *view;
1336     MERGEDATA data;
1337
1338     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1339         'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1340
1341     r = MSI_DatabaseOpenViewW(merge, query, &view);
1342     if (r != ERROR_SUCCESS)
1343         return r;
1344
1345     data.db = db;
1346     data.merge = merge;
1347     data.tabledata = tabledata;
1348     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1349
1350     msiobj_release(&view->hdr);
1351     return r;
1352 }
1353
1354 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1355 {
1356     UINT r;
1357     MERGEROW *row;
1358     MSIVIEW *tv;
1359
1360     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1361     {
1362         r = TABLE_CreateView(db, table->name, &tv);
1363         if (r != ERROR_SUCCESS)
1364             return r;
1365
1366         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1367         tv->ops->delete(tv);
1368
1369         if (r != ERROR_SUCCESS)
1370             return r;
1371     }
1372
1373     return ERROR_SUCCESS;
1374 }
1375
1376 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1377                                 LPWSTR table, DWORD numconflicts)
1378 {
1379     UINT r;
1380     MSIQUERY *view;
1381
1382     static const WCHAR create[] = {
1383         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1384         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1385         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1386         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1387         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1388         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1389         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1390     static const WCHAR insert[] = {
1391         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1392         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1393         '`','N','u','m','R','o','w','M','e','r','g','e',
1394         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1395         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1396
1397     if (!TABLE_Exists(db, error))
1398     {
1399         r = MSI_OpenQuery(db, &view, create, error);
1400         if (r != ERROR_SUCCESS)
1401             return r;
1402
1403         r = MSI_ViewExecute(view, NULL);
1404         msiobj_release(&view->hdr);
1405         if (r != ERROR_SUCCESS)
1406             return r;
1407     }
1408
1409     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1410     if (r != ERROR_SUCCESS)
1411         return r;
1412
1413     r = MSI_ViewExecute(view, NULL);
1414     msiobj_release(&view->hdr);
1415     return r;
1416 }
1417
1418 static void merge_free_rows(MERGETABLE *table)
1419 {
1420     struct list *item, *cursor;
1421
1422     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1423     {
1424         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1425
1426         list_remove(&row->entry);
1427         merge_free_rows(table);
1428         msiobj_release(&row->data->hdr);
1429         msi_free(row);
1430     }
1431 }
1432
1433 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1434                               LPCWSTR szTableName)
1435 {
1436     struct list tabledata = LIST_INIT(tabledata);
1437     struct list *item, *cursor;
1438     MSIDATABASE *db, *merge;
1439     MERGETABLE *table;
1440     BOOL conflicts;
1441     UINT r;
1442
1443     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1444           debugstr_w(szTableName));
1445
1446     if (szTableName && !*szTableName)
1447         return ERROR_INVALID_TABLE;
1448
1449     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1450     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1451     if (!db || !merge)
1452     {
1453         r = ERROR_INVALID_HANDLE;
1454         goto done;
1455     }
1456
1457     r = gather_merge_data(db, merge, &tabledata);
1458     if (r != ERROR_SUCCESS)
1459         goto done;
1460
1461     conflicts = FALSE;
1462     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1463     {
1464         if (table->numconflicts)
1465         {
1466             conflicts = TRUE;
1467
1468             r = update_merge_errors(db, szTableName, table->name,
1469                                     table->numconflicts);
1470             if (r != ERROR_SUCCESS)
1471                 break;
1472         }
1473         else
1474         {
1475             r = merge_table(db, table);
1476             if (r != ERROR_SUCCESS)
1477                 break;
1478         }
1479     }
1480
1481     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1482     {
1483         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1484
1485         list_remove(&table->entry);
1486         merge_free_rows(table);
1487         msi_free(table->name);
1488         msi_free(table);
1489     }
1490
1491     if (conflicts)
1492         r = ERROR_FUNCTION_FAILED;
1493
1494 done:
1495     msiobj_release(&db->hdr);
1496     msiobj_release(&merge->hdr);
1497     return r;
1498 }
1499
1500 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1501 {
1502     MSIDBSTATE ret = MSIDBSTATE_READ;
1503     MSIDATABASE *db;
1504
1505     TRACE("%d\n", handle);
1506
1507     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1508     if( !db )
1509     {
1510         IWineMsiRemoteDatabase *remote_database;
1511
1512         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1513         if ( !remote_database )
1514             return MSIDBSTATE_ERROR;
1515
1516         IWineMsiRemoteDatabase_Release( remote_database );
1517         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1518
1519         return MSIDBSTATE_READ;
1520     }
1521
1522     if (db->mode != MSIDBOPEN_READONLY )
1523         ret = MSIDBSTATE_WRITE;
1524     msiobj_release( &db->hdr );
1525
1526     return ret;
1527 }
1528
1529 typedef struct _msi_remote_database_impl {
1530     const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1531     MSIHANDLE database;
1532     LONG refs;
1533 } msi_remote_database_impl;
1534
1535 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
1536 {
1537     return (msi_remote_database_impl *)iface;
1538 }
1539
1540 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
1541                                           REFIID riid,LPVOID *ppobj)
1542 {
1543     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1544         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
1545     {
1546         IUnknown_AddRef( iface );
1547         *ppobj = iface;
1548         return S_OK;
1549     }
1550
1551     return E_NOINTERFACE;
1552 }
1553
1554 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
1555 {
1556     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1557
1558     return InterlockedIncrement( &This->refs );
1559 }
1560
1561 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
1562 {
1563     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1564     ULONG r;
1565
1566     r = InterlockedDecrement( &This->refs );
1567     if (r == 0)
1568     {
1569         MsiCloseHandle( This->database );
1570         msi_free( This );
1571     }
1572     return r;
1573 }
1574
1575 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
1576                                              BSTR table, MSICONDITION *persistent )
1577 {
1578     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1579     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
1580     return S_OK;
1581 }
1582
1583 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
1584                                           BSTR table, MSIHANDLE *keys )
1585 {
1586     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1587     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
1588     return HRESULT_FROM_WIN32(r);
1589 }
1590
1591 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
1592                                                 UINT updatecount, MSIHANDLE *suminfo )
1593 {
1594     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1595     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
1596     return HRESULT_FROM_WIN32(r);
1597 }
1598
1599 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
1600                                     BSTR query, MSIHANDLE *view )
1601 {
1602     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1603     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
1604     return HRESULT_FROM_WIN32(r);
1605 }
1606
1607 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
1608 {
1609     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1610     This->database = handle;
1611     return S_OK;
1612 }
1613
1614 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
1615 {
1616     mrd_QueryInterface,
1617     mrd_AddRef,
1618     mrd_Release,
1619     mrd_IsTablePersistent,
1620     mrd_GetPrimaryKeys,
1621     mrd_GetSummaryInformation,
1622     mrd_OpenView,
1623     mrd_SetMsiHandle,
1624 };
1625
1626 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
1627 {
1628     msi_remote_database_impl *This;
1629
1630     This = msi_alloc( sizeof *This );
1631     if (!This)
1632         return E_OUTOFMEMORY;
1633
1634     This->lpVtbl = &msi_remote_database_vtbl;
1635     This->database = 0;
1636     This->refs = 1;
1637
1638     *ppObj = This;
1639
1640     return S_OK;
1641 }