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