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