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