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