riched32/tests: Remove the todo_wine logic where appropriate.
[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 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
614     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
615
616     if( folder == NULL || file == NULL )
617         return ERROR_INVALID_PARAMETER;
618
619     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
620     path = msi_alloc( len * sizeof(WCHAR) );
621     if (!path)
622         return ERROR_OUTOFMEMORY;
623
624     lstrcpyW( path, folder );
625     lstrcatW( path, backslash );
626     lstrcatW( path, file );
627
628     data = msi_read_text_archive( path );
629
630     ptr = data;
631     msi_parse_line( &ptr, &columns, &num_columns );
632     msi_parse_line( &ptr, &types, &num_types );
633     msi_parse_line( &ptr, &labels, &num_labels );
634
635     if (num_columns != num_types)
636     {
637         r = ERROR_FUNCTION_FAILED;
638         goto done;
639     }
640
641     records = msi_alloc(sizeof(LPWSTR *));
642     if (!records)
643     {
644         r = ERROR_OUTOFMEMORY;
645         goto done;
646     }
647
648     /* read in the table records */
649     while (*ptr)
650     {
651         msi_parse_line( &ptr, &records[num_records], NULL );
652
653         num_records++;
654         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
655         if (!temp_records)
656         {
657             r = ERROR_OUTOFMEMORY;
658             goto done;
659         }
660         records = temp_records;
661     }
662
663     if (!TABLE_Exists(db, labels[0]))
664     {
665         r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
666         if (r != ERROR_SUCCESS)
667         {
668             r = ERROR_FUNCTION_FAILED;
669             goto done;
670         }
671     }
672
673     r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
674
675 done:
676     msi_free(path);
677     msi_free(data);
678     msi_free(columns);
679     msi_free(types);
680     msi_free(labels);
681
682     for (i = 0; i < num_records; i++)
683         msi_free(records[i]);
684
685     msi_free(records);
686
687     return r;
688 }
689
690 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
691 {
692     MSIDATABASE *db;
693     UINT r;
694
695     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
696
697     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
698     if( !db )
699     {
700         IWineMsiRemoteDatabase *remote_database;
701
702         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
703         if ( !remote_database )
704             return ERROR_INVALID_HANDLE;
705
706         IWineMsiRemoteDatabase_Release( remote_database );
707         WARN("MsiDatabaseImport not allowed during a custom action!\n");
708
709         return ERROR_SUCCESS;
710     }
711
712     r = MSI_DatabaseImport( db, szFolder, szFilename );
713     msiobj_release( &db->hdr );
714     return r;
715 }
716
717 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
718                LPCSTR szFolder, LPCSTR szFilename )
719 {
720     LPWSTR path = NULL, file = NULL;
721     UINT r = ERROR_OUTOFMEMORY;
722
723     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
724
725     if( szFolder )
726     {
727         path = strdupAtoW( szFolder );
728         if( !path )
729             goto end;
730     }
731
732     if( szFilename )
733     {
734         file = strdupAtoW( szFilename );
735         if( !file )
736             goto end;
737     }
738
739     r = MsiDatabaseImportW( handle, path, file );
740
741 end:
742     msi_free( path );
743     msi_free( file );
744
745     return r;
746 }
747
748 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
749 {
750     UINT i, count, len, r = ERROR_SUCCESS;
751     const char *sep;
752     char *buffer;
753     DWORD sz;
754
755     len = 0x100;
756     buffer = msi_alloc( len );
757     if ( !buffer )
758         return ERROR_OUTOFMEMORY;
759
760     count = MSI_RecordGetFieldCount( row );
761     for ( i=start; i<=count; i++ )
762     {
763         sz = len;
764         r = MSI_RecordGetStringA( row, i, buffer, &sz );
765         if (r == ERROR_MORE_DATA)
766         {
767             char *p = msi_realloc( buffer, sz + 1 );
768             if (!p)
769                 break;
770             len = sz + 1;
771             buffer = p;
772         }
773         sz = len;
774         r = MSI_RecordGetStringA( row, i, buffer, &sz );
775         if (r != ERROR_SUCCESS)
776             break;
777
778         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
779         {
780             r = ERROR_FUNCTION_FAILED;
781             break;
782         }
783
784         sep = (i < count) ? "\t" : "\r\n";
785         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
786         {
787             r = ERROR_FUNCTION_FAILED;
788             break;
789         }
790     }
791     msi_free( buffer );
792     return r;
793 }
794
795 static UINT msi_export_row( MSIRECORD *row, void *arg )
796 {
797     return msi_export_record( arg, row, 1 );
798 }
799
800 static UINT msi_export_forcecodepage( HANDLE handle )
801 {
802     DWORD sz;
803
804     static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
805
806     FIXME("Read the codepage from the strings table!\n");
807
808     sz = lstrlenA(data) + 1;
809     if (!WriteFile(handle, data, sz, &sz, NULL))
810         return ERROR_FUNCTION_FAILED;
811
812     return ERROR_SUCCESS;
813 }
814
815 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
816                LPCWSTR folder, LPCWSTR file )
817 {
818     static const WCHAR query[] = {
819         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
820     static const WCHAR szbs[] = { '\\', 0 };
821     static const WCHAR forcecodepage[] = {
822         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
823     MSIRECORD *rec = NULL;
824     MSIQUERY *view = NULL;
825     LPWSTR filename;
826     HANDLE handle;
827     UINT len, r;
828
829     TRACE("%p %s %s %s\n", db, debugstr_w(table),
830           debugstr_w(folder), debugstr_w(file) );
831
832     if( folder == NULL || file == NULL )
833         return ERROR_INVALID_PARAMETER;
834
835     len = lstrlenW(folder) + lstrlenW(file) + 2;
836     filename = msi_alloc(len * sizeof (WCHAR));
837     if (!filename)
838         return ERROR_OUTOFMEMORY;
839
840     lstrcpyW( filename, folder );
841     lstrcatW( filename, szbs );
842     lstrcatW( filename, file );
843
844     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
845                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
846     msi_free( filename );
847     if (handle == INVALID_HANDLE_VALUE)
848         return ERROR_FUNCTION_FAILED;
849
850     if (!lstrcmpW( table, forcecodepage ))
851     {
852         r = msi_export_forcecodepage( handle );
853         goto done;
854     }
855
856     r = MSI_OpenQuery( db, &view, query, table );
857     if (r == ERROR_SUCCESS)
858     {
859         /* write out row 1, the column names */
860         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
861         if (r == ERROR_SUCCESS)
862         {
863             msi_export_record( handle, rec, 1 );
864             msiobj_release( &rec->hdr );
865         }
866
867         /* write out row 2, the column types */
868         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
869         if (r == ERROR_SUCCESS)
870         {
871             msi_export_record( handle, rec, 1 );
872             msiobj_release( &rec->hdr );
873         }
874
875         /* write out row 3, the table name + keys */
876         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
877         if (r == ERROR_SUCCESS)
878         {
879             MSI_RecordSetStringW( rec, 0, table );
880             msi_export_record( handle, rec, 0 );
881             msiobj_release( &rec->hdr );
882         }
883
884         /* write out row 4 onwards, the data */
885         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
886         msiobj_release( &view->hdr );
887     }
888
889 done:
890     CloseHandle( handle );
891     return r;
892 }
893
894 /***********************************************************************
895  * MsiExportDatabaseW        [MSI.@]
896  *
897  * Writes a file containing the table data as tab separated ASCII.
898  *
899  * The format is as follows:
900  *
901  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
902  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
903  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
904  *
905  * Followed by the data, starting at row 1 with one row per line
906  *
907  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
908  */
909 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
910                LPCWSTR szFolder, LPCWSTR szFilename )
911 {
912     MSIDATABASE *db;
913     UINT r;
914
915     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
916           debugstr_w(szFolder), debugstr_w(szFilename));
917
918     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
919     if( !db )
920     {
921         IWineMsiRemoteDatabase *remote_database;
922
923         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
924         if ( !remote_database )
925             return ERROR_INVALID_HANDLE;
926
927         IWineMsiRemoteDatabase_Release( remote_database );
928         WARN("MsiDatabaseExport not allowed during a custom action!\n");
929
930         return ERROR_SUCCESS;
931     }
932
933     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
934     msiobj_release( &db->hdr );
935     return r;
936 }
937
938 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
939                LPCSTR szFolder, LPCSTR szFilename )
940 {
941     LPWSTR path = NULL, file = NULL, table = NULL;
942     UINT r = ERROR_OUTOFMEMORY;
943
944     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
945           debugstr_a(szFolder), debugstr_a(szFilename));
946
947     if( szTable )
948     {
949         table = strdupAtoW( szTable );
950         if( !table )
951             goto end;
952     }
953
954     if( szFolder )
955     {
956         path = strdupAtoW( szFolder );
957         if( !path )
958             goto end;
959     }
960
961     if( szFilename )
962     {
963         file = strdupAtoW( szFilename );
964         if( !file )
965             goto end;
966     }
967
968     r = MsiDatabaseExportW( handle, table, path, file );
969
970 end:
971     msi_free( table );
972     msi_free( path );
973     msi_free( file );
974
975     return r;
976 }
977
978 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
979                               LPCSTR szTableName)
980 {
981     UINT r;
982     LPWSTR table;
983
984     TRACE("(%ld, %ld, %s)\n", hDatabase, hDatabaseMerge,
985           debugstr_a(szTableName));
986
987     table = strdupAtoW(szTableName);
988     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
989
990     msi_free(table);
991     return r;
992 }
993
994 typedef struct _tagMERGETABLE
995 {
996     struct list entry;
997     struct list rows;
998     LPWSTR name;
999     DWORD numconflicts;
1000 } MERGETABLE;
1001
1002 typedef struct _tagMERGEROW
1003 {
1004     struct list entry;
1005     MSIRECORD *data;
1006 } MERGEROW;
1007
1008 typedef struct _tagMERGEDATA
1009 {
1010     MSIDATABASE *db;
1011     MSIDATABASE *merge;
1012     MERGETABLE *curtable;
1013     MSIQUERY *curview;
1014     struct list *tabledata;
1015 } MERGEDATA;
1016
1017 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1018 {
1019     MSIRECORD *dbrec, *mergerec;
1020     UINT r, i, count;
1021
1022     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1023     if (r != ERROR_SUCCESS)
1024         return r;
1025
1026     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1027     if (r != ERROR_SUCCESS)
1028         return r;
1029
1030     count = MSI_RecordGetFieldCount(dbrec);
1031     for (i = 1; i <= count; i++)
1032     {
1033         if (!MSI_RecordGetString(mergerec, i))
1034             break;
1035
1036         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1037                      MSI_RecordGetString(mergerec, i)))
1038         {
1039             r = ERROR_DATATYPE_MISMATCH;
1040             goto done;
1041         }
1042     }
1043
1044     msiobj_release(&dbrec->hdr);
1045     msiobj_release(&mergerec->hdr);
1046     dbrec = mergerec = NULL;
1047
1048     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1049     if (r != ERROR_SUCCESS)
1050         return r;
1051
1052     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1053     if (r != ERROR_SUCCESS)
1054         return r;
1055
1056     count = MSI_RecordGetFieldCount(dbrec);
1057     for (i = 1; i <= count; i++)
1058     {
1059         if (!MSI_RecordGetString(mergerec, i))
1060             break;
1061
1062         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1063                      MSI_RecordGetString(mergerec, i)))
1064         {
1065             r = ERROR_DATATYPE_MISMATCH;
1066             break;
1067         }
1068     }
1069
1070 done:
1071     msiobj_release(&dbrec->hdr);
1072     msiobj_release(&mergerec->hdr);
1073
1074     return r;
1075 }
1076
1077 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1078                                       LPCWSTR table)
1079 {
1080     MSIRECORD *dbrec, *mergerec = NULL;
1081     UINT r, i, count;
1082
1083     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1084     if (r != ERROR_SUCCESS)
1085         return r;
1086
1087     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1088     if (r != ERROR_SUCCESS)
1089         goto done;
1090
1091     count = MSI_RecordGetFieldCount(dbrec);
1092     if (count != MSI_RecordGetFieldCount(mergerec))
1093     {
1094         r = ERROR_DATATYPE_MISMATCH;
1095         goto done;
1096     }
1097
1098     for (i = 1; i <= count; i++)
1099     {
1100         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1101                      MSI_RecordGetString(mergerec, i)))
1102         {
1103             r = ERROR_DATATYPE_MISMATCH;
1104             goto done;
1105         }
1106     }
1107
1108 done:
1109     msiobj_release(&dbrec->hdr);
1110     msiobj_release(&mergerec->hdr);
1111
1112     return r;
1113 }
1114
1115 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1116 {
1117     MSIRECORD *colnames;
1118     LPWSTR str;
1119     UINT r, i = 0;
1120     int cmp;
1121
1122     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1123     if (r != ERROR_SUCCESS)
1124         return NULL;
1125
1126     do
1127     {
1128         str = msi_dup_record_field(colnames, ++i);
1129         cmp = lstrcmpW(key, str);
1130         msi_free(str);
1131     } while (cmp);
1132
1133     msiobj_release(&colnames->hdr);
1134     return msi_dup_record_field(rec, i);
1135 }
1136
1137 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1138                                     LPWSTR table, MSIRECORD *rec)
1139 {
1140     LPWSTR query = NULL, clause = NULL;
1141     LPWSTR ptr = NULL, val;
1142     LPCWSTR setptr;
1143     DWORD size = 1, oldsize;
1144     LPCWSTR key;
1145     MSIRECORD *keys;
1146     UINT r, i, count;
1147
1148     static const WCHAR keyset[] = {
1149         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1150     static const WCHAR lastkeyset[] = {
1151         '`','%','s','`',' ','=',' ','%','s',' ',0};
1152     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1153         'F','R','O','M',' ','`','%','s','`',' ',
1154         'W','H','E','R','E',' ','%','s',0};
1155
1156     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1157     if (r != ERROR_SUCCESS)
1158         return NULL;
1159
1160     clause = msi_alloc_zero(size * sizeof(WCHAR));
1161     if (!clause)
1162         goto done;
1163
1164     ptr = clause;
1165     count = MSI_RecordGetFieldCount(keys);
1166     for (i = 1; i <= count; i++)
1167     {
1168         key = MSI_RecordGetString(keys, i);
1169         val = get_key_value(view, key, rec);
1170
1171         if (i == count)
1172             setptr = lastkeyset;
1173         else
1174             setptr = keyset;
1175
1176         oldsize = size;
1177         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1178         clause = msi_realloc(clause, size * sizeof (WCHAR));
1179         if (!clause)
1180         {
1181             msi_free(val);
1182             goto done;
1183         }
1184
1185         ptr = clause + oldsize - 1;
1186         sprintfW(ptr, setptr, key, val);
1187         msi_free(val);
1188     }
1189
1190     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1191     query = msi_alloc(size * sizeof(WCHAR));
1192     if (!query)
1193         goto done;
1194
1195     sprintfW(query, fmt, table, clause);
1196
1197 done:
1198     msi_free(clause);
1199     msiobj_release(&keys->hdr);
1200     return query;
1201 }
1202
1203 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1204 {
1205     MERGEDATA *data = (MERGEDATA *)param;
1206     MERGETABLE *table = data->curtable;
1207     MERGEROW *mergerow;
1208     MSIQUERY *dbview;
1209     MSIRECORD *row;
1210     LPWSTR query;
1211     UINT r;
1212
1213     query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1214     if (!query)
1215         return ERROR_OUTOFMEMORY;
1216
1217     r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1218     if (r != ERROR_SUCCESS)
1219         goto done;
1220
1221     r = MSI_ViewExecute(dbview, NULL);
1222     if (r != ERROR_SUCCESS)
1223         goto done;
1224
1225     r = MSI_ViewFetch(dbview, &row);
1226     if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1227     {
1228         table->numconflicts++;
1229         goto done;
1230     }
1231     else if (r != ERROR_NO_MORE_ITEMS)
1232         goto done;
1233
1234     mergerow = msi_alloc(sizeof(MERGEROW));
1235     if (!mergerow)
1236     {
1237         r = ERROR_OUTOFMEMORY;
1238         goto done;
1239     }
1240
1241     mergerow->data = MSI_CloneRecord(rec);
1242     if (!mergerow->data)
1243     {
1244         r = ERROR_OUTOFMEMORY;
1245         msi_free(mergerow);
1246         goto done;
1247     }
1248
1249     list_add_tail(&table->rows, &mergerow->entry);
1250
1251 done:
1252     msi_free(query);
1253     msiobj_release(&row->hdr);
1254     msiobj_release(&dbview->hdr);
1255     return r;
1256 }
1257
1258 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1259 {
1260     MERGEDATA *data = (MERGEDATA *)param;
1261     MERGETABLE *table;
1262     MSIQUERY *dbview;
1263     MSIQUERY *mergeview = NULL;
1264     LPCWSTR name;
1265     UINT r;
1266
1267     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1268         'F','R','O','M',' ','`','%','s','`',0};
1269
1270     name = MSI_RecordGetString(rec, 1);
1271
1272     r = MSI_OpenQuery(data->db, &dbview, query, name);
1273     if (r != ERROR_SUCCESS)
1274         return r;
1275
1276     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1277     if (r != ERROR_SUCCESS)
1278         goto done;
1279
1280     r = merge_verify_colnames(dbview, mergeview);
1281     if (r != ERROR_SUCCESS)
1282         goto done;
1283
1284     r = merge_verify_primary_keys(data->db, data->merge, name);
1285     if (r != ERROR_SUCCESS)
1286         goto done;
1287
1288     table = msi_alloc(sizeof(MERGETABLE));
1289     if (!table)
1290     {
1291         r = ERROR_OUTOFMEMORY;
1292         goto done;
1293     }
1294
1295     list_init(&table->rows);
1296     table->name = strdupW(name);
1297     table->numconflicts = 0;
1298     data->curtable = table;
1299     data->curview = mergeview;
1300
1301     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1302     if (r != ERROR_SUCCESS)
1303     {
1304         msi_free(table->name);
1305         msi_free(table);
1306         goto done;
1307     }
1308
1309     list_add_tail(data->tabledata, &table->entry);
1310
1311 done:
1312     msiobj_release(&dbview->hdr);
1313     msiobj_release(&mergeview->hdr);
1314     return r;
1315 }
1316
1317 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1318                               struct list *tabledata)
1319 {
1320     UINT r;
1321     MSIQUERY *view;
1322     MERGEDATA data;
1323
1324     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1325         'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1326
1327     r = MSI_DatabaseOpenViewW(merge, query, &view);
1328     if (r != ERROR_SUCCESS)
1329         return r;
1330
1331     data.db = db;
1332     data.merge = merge;
1333     data.tabledata = tabledata;
1334     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1335
1336     msiobj_release(&view->hdr);
1337     return r;
1338 }
1339
1340 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1341 {
1342     UINT r;
1343     MERGEROW *row;
1344     MSIVIEW *tv;
1345
1346     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1347     {
1348         r = TABLE_CreateView(db, table->name, &tv);
1349         if (r != ERROR_SUCCESS)
1350             return r;
1351
1352         r = tv->ops->insert_row(tv, row->data, FALSE);
1353         tv->ops->delete(tv);
1354
1355         if (r != ERROR_SUCCESS)
1356             return r;
1357     }
1358
1359     return ERROR_SUCCESS;
1360 }
1361
1362 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1363                                 LPWSTR table, DWORD numconflicts)
1364 {
1365     UINT r;
1366     MSIQUERY *view;
1367
1368     static const WCHAR create[] = {
1369         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1370         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1371         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1372         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1373         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1374         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1375         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1376     static const WCHAR insert[] = {
1377         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1378         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1379         '`','N','u','m','R','o','w','M','e','r','g','e',
1380         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1381         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1382
1383     if (!TABLE_Exists(db, error))
1384     {
1385         r = MSI_OpenQuery(db, &view, create, error);
1386         if (r != ERROR_SUCCESS)
1387             return r;
1388
1389         r = MSI_ViewExecute(view, NULL);
1390         msiobj_release(&view->hdr);
1391         if (r != ERROR_SUCCESS)
1392             return r;
1393     }
1394
1395     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1396     if (r != ERROR_SUCCESS)
1397         return r;
1398
1399     r = MSI_ViewExecute(view, NULL);
1400     msiobj_release(&view->hdr);
1401     return r;
1402 }
1403
1404 static void merge_free_rows(MERGETABLE *table)
1405 {
1406     struct list *item, *cursor;
1407
1408     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1409     {
1410         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1411
1412         list_remove(&row->entry);
1413         merge_free_rows(table);
1414         msiobj_release(&row->data->hdr);
1415         msi_free(row);
1416     }
1417 }
1418
1419 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1420                               LPCWSTR szTableName)
1421 {
1422     struct list tabledata = LIST_INIT(tabledata);
1423     struct list *item, *cursor;
1424     MSIDATABASE *db, *merge;
1425     MERGETABLE *table;
1426     BOOL conflicts;
1427     UINT r;
1428
1429     TRACE("(%ld, %ld, %s)\n", hDatabase, hDatabaseMerge,
1430           debugstr_w(szTableName));
1431
1432     if (szTableName && !*szTableName)
1433         return ERROR_INVALID_TABLE;
1434
1435     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1436     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1437     if (!db || !merge)
1438     {
1439         r = ERROR_INVALID_HANDLE;
1440         goto done;
1441     }
1442
1443     r = gather_merge_data(db, merge, &tabledata);
1444     if (r != ERROR_SUCCESS)
1445         goto done;
1446
1447     conflicts = FALSE;
1448     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1449     {
1450         if (table->numconflicts)
1451         {
1452             conflicts = TRUE;
1453
1454             r = update_merge_errors(db, szTableName, table->name,
1455                                     table->numconflicts);
1456             if (r != ERROR_SUCCESS)
1457                 break;
1458         }
1459         else
1460         {
1461             r = merge_table(db, table);
1462             if (r != ERROR_SUCCESS)
1463                 break;
1464         }
1465     }
1466
1467     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1468     {
1469         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1470
1471         list_remove(&table->entry);
1472         merge_free_rows(table);
1473         msi_free(table->name);
1474         msi_free(table);
1475     }
1476
1477     if (conflicts)
1478         r = ERROR_FUNCTION_FAILED;
1479
1480 done:
1481     msiobj_release(&db->hdr);
1482     msiobj_release(&merge->hdr);
1483     return r;
1484 }
1485
1486 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1487 {
1488     MSIDBSTATE ret = MSIDBSTATE_READ;
1489     MSIDATABASE *db;
1490
1491     TRACE("%ld\n", handle);
1492
1493     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1494     if( !db )
1495     {
1496         IWineMsiRemoteDatabase *remote_database;
1497
1498         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1499         if ( !remote_database )
1500             return MSIDBSTATE_ERROR;
1501
1502         IWineMsiRemoteDatabase_Release( remote_database );
1503         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1504
1505         return MSIDBSTATE_READ;
1506     }
1507
1508     if (db->mode != MSIDBOPEN_READONLY )
1509         ret = MSIDBSTATE_WRITE;
1510     msiobj_release( &db->hdr );
1511
1512     return ret;
1513 }
1514
1515 typedef struct _msi_remote_database_impl {
1516     const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1517     MSIHANDLE database;
1518     LONG refs;
1519 } msi_remote_database_impl;
1520
1521 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
1522 {
1523     return (msi_remote_database_impl *)iface;
1524 }
1525
1526 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
1527                                           REFIID riid,LPVOID *ppobj)
1528 {
1529     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1530         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
1531     {
1532         IUnknown_AddRef( iface );
1533         *ppobj = iface;
1534         return S_OK;
1535     }
1536
1537     return E_NOINTERFACE;
1538 }
1539
1540 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
1541 {
1542     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1543
1544     return InterlockedIncrement( &This->refs );
1545 }
1546
1547 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
1548 {
1549     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1550     ULONG r;
1551
1552     r = InterlockedDecrement( &This->refs );
1553     if (r == 0)
1554     {
1555         MsiCloseHandle( This->database );
1556         msi_free( This );
1557     }
1558     return r;
1559 }
1560
1561 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
1562                                              BSTR table, MSICONDITION *persistent )
1563 {
1564     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1565     *persistent = MsiDatabaseIsTablePersistentW(This->database, (LPWSTR)table);
1566     return S_OK;
1567 }
1568
1569 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
1570                                           BSTR table, MSIHANDLE *keys )
1571 {
1572     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1573     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, (LPWSTR)table, keys);
1574     return HRESULT_FROM_WIN32(r);
1575 }
1576
1577 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
1578                                                 UINT updatecount, MSIHANDLE *suminfo )
1579 {
1580     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1581     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
1582     return HRESULT_FROM_WIN32(r);
1583 }
1584
1585 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
1586                                     BSTR query, MSIHANDLE *view )
1587 {
1588     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1589     UINT r = MsiDatabaseOpenViewW(This->database, (LPWSTR)query, view);
1590     return HRESULT_FROM_WIN32(r);
1591 }
1592
1593 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
1594 {
1595     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1596     This->database = handle;
1597     return S_OK;
1598 }
1599
1600 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
1601 {
1602     mrd_QueryInterface,
1603     mrd_AddRef,
1604     mrd_Release,
1605     mrd_IsTablePersistent,
1606     mrd_GetPrimaryKeys,
1607     mrd_GetSummaryInformation,
1608     mrd_OpenView,
1609     mrd_SetMsiHandle,
1610 };
1611
1612 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
1613 {
1614     msi_remote_database_impl *This;
1615
1616     This = msi_alloc( sizeof *This );
1617     if (!This)
1618         return E_OUTOFMEMORY;
1619
1620     This->lpVtbl = &msi_remote_database_vtbl;
1621     This->database = 0;
1622     This->refs = 1;
1623
1624     *ppObj = This;
1625
1626     return S_OK;
1627 }