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