ddraw: Move the wined3d_texture_update_desc() call into ddraw_surface_create_wined3d_...
[wine.git] / dlls / msi / database.c
blob20c74c4d7420b610c4945c4532f80d986a8e22b7
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
22 #include <stdio.h>
24 #define COBJMACROS
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"
39 #include "initguid.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44 * .MSI file format
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.
53 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
55 static void free_transforms( MSIDATABASE *db )
57 while( !list_empty( &db->transforms ) )
59 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ), MSITRANSFORM, entry );
60 list_remove( &t->entry );
61 IStorage_Release( t->stg );
62 free( t );
66 static void free_streams( MSIDATABASE *db )
68 UINT i;
69 for (i = 0; i < db->num_streams; i++)
71 if (db->streams[i].stream) IStream_Release( db->streams[i].stream );
73 free( db->streams );
76 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
78 MSITRANSFORM *t;
80 t = malloc( sizeof *t );
81 t->stg = stg;
82 IStorage_AddRef( stg );
83 list_add_head( &db->transforms, &t->entry );
86 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
88 MSIDATABASE *db = (MSIDATABASE *) arg;
90 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)
98 DeleteFileW( db->deletefile );
99 free( db->deletefile );
101 free( db->tempfolder );
104 static HRESULT db_initialize( IStorage *stg, const GUID *clsid )
106 HRESULT hr;
108 hr = IStorage_SetClass( stg, clsid );
109 if (FAILED( hr ))
111 WARN("failed to set class id %#lx\n", hr);
112 return hr;
115 /* create the _Tables stream */
116 hr = write_stream_data( stg, L"_Tables", NULL, 0, TRUE );
117 if (FAILED( hr ))
119 WARN("failed to create _Tables stream %#lx\n", hr);
120 return hr;
123 hr = msi_init_string_table( stg );
124 if (FAILED( hr ))
126 WARN("failed to initialize string table %#lx\n", hr);
127 return hr;
130 hr = IStorage_Commit( stg, 0 );
131 if (FAILED( hr ))
133 WARN("failed to commit changes %#lx\n", hr);
134 return hr;
137 return S_OK;
140 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
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];
152 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
154 if( !pdb )
155 return ERROR_INVALID_PARAMETER;
157 save_path = szDBPath;
158 if ( IS_INTMSIDBOPEN(szPersist) )
160 mode = LOWORD(szPersist);
162 else
164 if (!CopyFileW( szDBPath, szPersist, FALSE ))
165 return ERROR_OPEN_FAILED;
167 szDBPath = szPersist;
168 mode = MSI_OPEN_TRANSACT;
169 created = TRUE;
172 if ((mode & MSI_OPEN_PATCHFILE) == MSI_OPEN_PATCHFILE)
174 TRACE("Database is a patch\n");
175 mode &= ~MSI_OPEN_PATCHFILE;
176 patch = TRUE;
179 if( mode == MSI_OPEN_READONLY )
181 r = StgOpenStorage( szDBPath, NULL,
182 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
184 else if( mode == MSI_OPEN_CREATE )
186 r = StgCreateDocfile( szDBPath,
187 STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
189 if( SUCCEEDED(r) )
190 r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
191 created = TRUE;
193 else if( mode == MSI_OPEN_CREATEDIRECT )
195 r = StgCreateDocfile( szDBPath,
196 STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
198 if( SUCCEEDED(r) )
199 r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
200 created = TRUE;
202 else if( mode == MSI_OPEN_TRANSACT )
204 r = StgOpenStorage( szDBPath, NULL,
205 STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
207 else if( mode == MSI_OPEN_DIRECT )
209 r = StgOpenStorage( szDBPath, NULL,
210 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
212 else
214 ERR("unknown flag %x\n",mode);
215 return ERROR_INVALID_PARAMETER;
218 if( FAILED( r ) || !stg )
220 WARN("open failed r = %#lx for %s\n", r, debugstr_w(szDBPath));
221 return ERROR_FUNCTION_FAILED;
224 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
225 if( FAILED( r ) )
227 FIXME("Failed to stat storage\n");
228 goto end;
231 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
232 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
233 !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
235 ERR("storage GUID is not a MSI database GUID %s\n",
236 debugstr_guid(&stat.clsid) );
237 goto end;
240 if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
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;
248 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
249 MSI_CloseDatabase );
250 if( !db )
252 FIXME("Failed to allocate a handle\n");
253 goto end;
256 if (!wcschr( save_path, '\\' ))
258 GetCurrentDirectoryW( MAX_PATH, path );
259 lstrcatW( path, L"\\" );
260 lstrcatW( path, save_path );
262 else
263 lstrcpyW( path, save_path );
265 db->path = wcsdup( path );
266 db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
267 db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
269 if( TRACE_ON( msi ) )
270 enum_stream_names( stg );
272 db->storage = stg;
273 db->mode = mode;
274 if (created)
275 db->deletefile = wcsdup( szDBPath );
276 list_init( &db->tables );
277 list_init( &db->transforms );
279 db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
280 if( !db->strings )
281 goto end;
283 ret = ERROR_SUCCESS;
285 msiobj_addref( &db->hdr );
286 IStorage_AddRef( stg );
287 *pdb = db;
289 end:
290 if( db )
291 msiobj_release( &db->hdr );
292 if( stg )
293 IStorage_Release( stg );
295 return ret;
298 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
300 MSIDATABASE *db;
301 UINT ret;
303 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
305 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
306 if( ret == ERROR_SUCCESS )
308 *phDB = alloc_msihandle( &db->hdr );
309 if (! *phDB)
310 ret = ERROR_NOT_ENOUGH_MEMORY;
311 msiobj_release( &db->hdr );
314 return ret;
317 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
319 HRESULT r = ERROR_FUNCTION_FAILED;
320 LPWSTR szwDBPath = NULL, szwPersist = NULL;
322 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
324 if( szDBPath )
326 szwDBPath = strdupAtoW( szDBPath );
327 if( !szwDBPath )
328 goto end;
331 if( !IS_INTMSIDBOPEN(szPersist) )
333 szwPersist = strdupAtoW( szPersist );
334 if( !szwPersist )
335 goto end;
337 else
338 szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
340 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
342 end:
343 if( !IS_INTMSIDBOPEN(szPersist) )
344 free( szwPersist );
345 free( szwDBPath );
347 return r;
350 static WCHAR *read_text_archive(const WCHAR *path, DWORD *len)
352 HANDLE file;
353 LPSTR data = NULL;
354 LPWSTR wdata = NULL;
355 DWORD read, size = 0;
357 file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
358 if (file == INVALID_HANDLE_VALUE)
359 return NULL;
361 size = GetFileSize( file, NULL );
362 if (!(data = malloc( size ))) goto done;
364 if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done;
366 while (!data[size - 1]) size--;
367 *len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 );
368 if ((wdata = malloc( (*len + 1) * sizeof(WCHAR) )))
370 MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len );
371 wdata[*len] = 0;
374 done:
375 CloseHandle( file );
376 free( data );
377 return wdata;
380 static void parse_line(WCHAR **line, WCHAR ***entries, DWORD *num_entries, DWORD *len)
382 LPWSTR ptr = *line, save;
383 DWORD i, count = 1, chars_left = *len;
385 *entries = NULL;
387 /* stay on this line */
388 while (chars_left && *ptr != '\n')
390 /* entries are separated by tabs */
391 if (*ptr == '\t')
392 count++;
394 ptr++;
395 chars_left--;
398 *entries = malloc(count * sizeof(WCHAR *));
399 if (!*entries)
400 return;
402 /* store pointers into the data */
403 chars_left = *len;
404 for (i = 0, ptr = *line; i < count; i++)
406 while (chars_left && *ptr == '\r')
408 ptr++;
409 chars_left--;
411 save = ptr;
413 while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
415 if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */
416 if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11')
418 *ptr = '\n';
419 *(ptr - 1) = '\r';
421 ptr++;
422 chars_left--;
425 /* NULL-separate the data */
426 if (*ptr == '\n' || *ptr == '\r')
428 while (chars_left && (*ptr == '\n' || *ptr == '\r'))
430 *(ptr++) = 0;
431 chars_left--;
434 else if (*ptr)
436 *(ptr++) = 0;
437 chars_left--;
439 (*entries)[i] = save;
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;
449 static WCHAR *build_createsql_prelude(const WCHAR *table)
451 LPWSTR prelude;
452 DWORD size;
454 size = ARRAY_SIZE(L"CREATE TABLE `%s` ( ") + lstrlenW(table) - 2;
455 prelude = malloc(size * sizeof(WCHAR));
456 if (!prelude)
457 return NULL;
459 swprintf(prelude, size, L"CREATE TABLE `%s` ( ", table);
460 return prelude;
463 static WCHAR *build_createsql_columns(WCHAR **columns_data, WCHAR **types, DWORD num_columns)
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];
471 columns = calloc(sql_size, sizeof(WCHAR));
472 if (!columns)
473 return NULL;
475 for (i = 0; i < num_columns; i++)
477 type = NULL;
478 comma[1] = size[0] = extra[0] = '\0';
480 if (i == num_columns - 1)
481 comma[0] = '\0';
482 else
483 comma[0] = ',';
485 ptr = &types[i][1];
486 len = wcstol(ptr, NULL, 10);
487 extra[0] = '\0';
489 switch (types[i][0])
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
516 WARN("invalid int width %lu\n", len);
517 free(columns);
518 return NULL;
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 free(columns);
530 return NULL;
533 swprintf(expanded, ARRAY_SIZE(expanded), L"`%s` %s%s%s%s ", columns_data[i], type, size, extra, comma);
534 sql_size += lstrlenW(expanded);
536 p = realloc(columns, sql_size * sizeof(WCHAR));
537 if (!p)
539 free(columns);
540 return NULL;
542 columns = p;
544 lstrcatW(columns, expanded);
547 return columns;
550 static WCHAR *build_createsql_postlude(WCHAR **primary_keys, DWORD num_keys)
552 LPWSTR postlude, keys, ptr;
553 DWORD size, i;
555 for (i = 0, size = 1; i < num_keys; i++)
556 size += lstrlenW(L"`%s`, ") + lstrlenW(primary_keys[i]) - 2;
558 keys = malloc(size * sizeof(WCHAR));
559 if (!keys)
560 return NULL;
562 for (i = 0, ptr = keys; i < num_keys; i++)
564 ptr += swprintf(ptr, size - (ptr - keys), L"`%s`, ", primary_keys[i]);
567 /* remove final ', ' */
568 *(ptr - 2) = '\0';
570 size = lstrlenW(L"PRIMARY KEY %s)") + size - 1;
571 postlude = malloc(size * sizeof(WCHAR));
572 if (!postlude)
573 goto done;
575 swprintf(postlude, size, L"PRIMARY KEY %s)", keys);
577 done:
578 free(keys);
579 return postlude;
582 static UINT add_table_to_db(MSIDATABASE *db, WCHAR **columns, WCHAR **types, WCHAR **labels, DWORD num_labels,
583 DWORD num_columns)
585 UINT r = ERROR_OUTOFMEMORY;
586 DWORD size;
587 MSIQUERY *view;
588 LPWSTR create_sql = NULL;
589 LPWSTR prelude, columns_sql, postlude;
591 prelude = build_createsql_prelude(labels[0]);
592 columns_sql = build_createsql_columns(columns, types, num_columns);
593 postlude = build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
595 if (!prelude || !columns_sql || !postlude)
596 goto done;
598 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
599 create_sql = malloc(size * sizeof(WCHAR));
600 if (!create_sql)
601 goto done;
603 lstrcpyW(create_sql, prelude);
604 lstrcatW(create_sql, columns_sql);
605 lstrcatW(create_sql, postlude);
607 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
608 if (r != ERROR_SUCCESS)
609 goto done;
611 r = MSI_ViewExecute(view, NULL);
612 MSI_ViewClose(view);
613 msiobj_release(&view->hdr);
615 done:
616 free(prelude);
617 free(columns_sql);
618 free(postlude);
619 free(create_sql);
620 return r;
623 static WCHAR *import_stream_filename(const WCHAR *path, const WCHAR *name)
625 DWORD len;
626 LPWSTR fullname, ptr;
628 len = lstrlenW(path) + lstrlenW(name) + 1;
629 fullname = malloc(len * sizeof(WCHAR));
630 if (!fullname)
631 return NULL;
633 lstrcpyW( fullname, path );
635 /* chop off extension from path */
636 ptr = wcsrchr(fullname, '.');
637 if (!ptr)
639 free(fullname);
640 return NULL;
642 *ptr++ = '\\';
643 lstrcpyW( ptr, name );
644 return fullname;
647 static UINT construct_record(DWORD num_columns, LPWSTR *types,
648 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
650 UINT i;
652 *rec = MSI_CreateRecord(num_columns);
653 if (!*rec)
654 return ERROR_OUTOFMEMORY;
656 for (i = 0; i < num_columns; i++)
658 switch (types[i][0])
660 case 'L': case 'l': case 'S': case 's':
661 MSI_RecordSetStringW(*rec, i + 1, data[i]);
662 break;
663 case 'I': case 'i':
664 if (*data[i])
665 MSI_RecordSetInteger(*rec, i + 1, wcstol(data[i], NULL, 10));
666 break;
667 case 'V': case 'v':
668 if (*data[i])
670 UINT r;
671 WCHAR *file = import_stream_filename(path, data[i]);
672 if (!file)
673 return ERROR_FUNCTION_FAILED;
675 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
676 free (file);
677 if (r != ERROR_SUCCESS)
678 return ERROR_FUNCTION_FAILED;
680 break;
681 default:
682 ERR("Unhandled column type: %c\n", types[i][0]);
683 msiobj_release(&(*rec)->hdr);
684 return ERROR_FUNCTION_FAILED;
688 return ERROR_SUCCESS;
691 static UINT add_records_to_table(MSIDATABASE *db, WCHAR **columns, WCHAR **types, WCHAR **labels, WCHAR ***records,
692 int num_columns, int num_records, WCHAR *path)
694 UINT r;
695 int i;
696 MSIQUERY *view;
697 MSIRECORD *rec;
699 r = MSI_OpenQuery(db, &view, L"SELECT * FROM `%s`", labels[0]);
700 if (r != ERROR_SUCCESS)
701 return r;
703 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
705 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
706 msiobj_release(&rec->hdr);
707 if (r != ERROR_SUCCESS)
708 goto done;
711 for (i = 0; i < num_records; i++)
713 r = construct_record(num_columns, types, records[i], path, &rec);
714 if (r != ERROR_SUCCESS)
715 goto done;
717 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
718 if (r != ERROR_SUCCESS)
720 msiobj_release(&rec->hdr);
721 goto done;
724 msiobj_release(&rec->hdr);
727 done:
728 msiobj_release(&view->hdr);
729 return r;
732 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
734 UINT r;
735 DWORD len, i, num_labels, num_types, num_columns, num_records = 0;
736 WCHAR **columns, **types, **labels, *path, *ptr, *data, ***records = NULL, ***temp_records;
738 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
740 if (!folder || !file)
741 return ERROR_INVALID_PARAMETER;
743 len = lstrlenW(folder) + lstrlenW(L"\\") + lstrlenW(file) + 1;
744 path = malloc( len * sizeof(WCHAR) );
745 if (!path)
746 return ERROR_OUTOFMEMORY;
748 lstrcpyW( path, folder );
749 lstrcatW( path, L"\\" );
750 lstrcatW( path, file );
752 data = read_text_archive( path, &len );
753 if (!data)
755 free(path);
756 return ERROR_FUNCTION_FAILED;
759 ptr = data;
760 parse_line( &ptr, &columns, &num_columns, &len );
761 parse_line( &ptr, &types, &num_types, &len );
762 parse_line( &ptr, &labels, &num_labels, &len );
764 if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
765 num_types == 2 && !wcscmp( types[1], L"_ForceCodepage" ))
767 r = msi_set_string_table_codepage( db->strings, wcstol( types[0], NULL, 10 ) );
768 goto done;
771 if (num_columns != num_types)
773 r = ERROR_FUNCTION_FAILED;
774 goto done;
777 records = malloc(sizeof(WCHAR **));
778 if (!records)
780 r = ERROR_OUTOFMEMORY;
781 goto done;
784 /* read in the table records */
785 while (len)
787 parse_line( &ptr, &records[num_records], NULL, &len );
789 num_records++;
790 temp_records = realloc(records, (num_records + 1) * sizeof(WCHAR **));
791 if (!temp_records)
793 r = ERROR_OUTOFMEMORY;
794 goto done;
796 records = temp_records;
799 if (!wcscmp(labels[0], L"_SummaryInformation"))
801 r = msi_add_suminfo( db, records, num_records, num_columns );
802 if (r != ERROR_SUCCESS)
804 r = ERROR_FUNCTION_FAILED;
805 goto done;
808 else
810 if (!TABLE_Exists(db, labels[0]))
812 r = add_table_to_db( db, columns, types, labels, num_labels, num_columns );
813 if (r != ERROR_SUCCESS)
815 r = ERROR_FUNCTION_FAILED;
816 goto done;
820 r = add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
823 done:
824 free(path);
825 free(data);
826 free(columns);
827 free(types);
828 free(labels);
830 for (i = 0; i < num_records; i++)
831 free(records[i]);
833 free(records);
834 return r;
837 UINT WINAPI MsiDatabaseImportW( MSIHANDLE handle, const WCHAR *szFolder, const WCHAR *szFilename )
839 MSIDATABASE *db;
840 UINT r;
842 TRACE( "%lu %s %s\n", handle, debugstr_w(szFolder), debugstr_w(szFilename) );
844 if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
845 return ERROR_INVALID_HANDLE;
847 r = MSI_DatabaseImport( db, szFolder, szFilename );
848 msiobj_release( &db->hdr );
849 return r;
852 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, const char *szFolder, const char *szFilename )
854 WCHAR *path = NULL, *file = NULL;
855 UINT r = ERROR_OUTOFMEMORY;
857 TRACE( "%lu %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename) );
859 if( szFolder )
861 path = strdupAtoW( szFolder );
862 if( !path )
863 goto end;
866 if( szFilename )
868 file = strdupAtoW( szFilename );
869 if( !file )
870 goto end;
873 r = MsiDatabaseImportW( handle, path, file );
875 end:
876 free( path );
877 free( file );
879 return r;
882 static UINT export_field( HANDLE handle, MSIRECORD *row, UINT field )
884 char *buffer;
885 BOOL ret;
886 DWORD sz = 0x100;
887 UINT r;
889 buffer = malloc( sz );
890 if (!buffer)
891 return ERROR_OUTOFMEMORY;
893 r = MSI_RecordGetStringA( row, field, buffer, &sz );
894 if (r == ERROR_MORE_DATA)
896 char *tmp;
898 sz++; /* leave room for NULL terminator */
899 tmp = realloc( buffer, sz );
900 if (!tmp)
902 free( buffer );
903 return ERROR_OUTOFMEMORY;
905 buffer = tmp;
907 r = MSI_RecordGetStringA( row, field, buffer, &sz );
908 if (r != ERROR_SUCCESS)
910 free( buffer );
911 return r;
914 else if (r != ERROR_SUCCESS)
916 free( buffer );
917 return r;
920 ret = WriteFile( handle, buffer, sz, &sz, NULL );
921 free( buffer );
922 return ret ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
925 static UINT export_stream( const WCHAR *folder, const WCHAR *table, MSIRECORD *row, UINT field, UINT start )
927 WCHAR stream[MAX_STREAM_NAME_LEN + 1], *path;
928 DWORD sz, read_size, write_size;
929 char buffer[1024];
930 HANDLE file;
931 UINT len, r;
933 sz = ARRAY_SIZE( stream );
934 r = MSI_RecordGetStringW( row, start, stream, &sz );
935 if (r != ERROR_SUCCESS)
936 return r;
938 len = sz + lstrlenW( folder ) + lstrlenW( table ) + ARRAY_SIZE( L"%s\\%s" ) + 1;
939 if (!(path = malloc( len * sizeof(WCHAR) )))
940 return ERROR_OUTOFMEMORY;
942 len = swprintf( path, len, L"%s\\%s", folder, table );
943 if (!CreateDirectoryW( path, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
945 free( path );
946 return ERROR_FUNCTION_FAILED;
949 path[len++] = '\\';
950 lstrcpyW( path + len, stream );
951 file = CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
952 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
953 free( path );
954 if (file == INVALID_HANDLE_VALUE)
955 return ERROR_FUNCTION_FAILED;
957 read_size = sizeof(buffer);
958 while (read_size == sizeof(buffer))
960 r = MSI_RecordReadStream( row, field, buffer, &read_size );
961 if (r != ERROR_SUCCESS)
963 CloseHandle( file );
964 return r;
966 if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
968 CloseHandle( file );
969 return ERROR_WRITE_FAULT;
972 CloseHandle( file );
973 return r;
976 struct row_export_info
978 HANDLE handle;
979 const WCHAR *folder;
980 const WCHAR *table;
983 static UINT export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
985 HANDLE handle = row_export_info->handle;
986 UINT i, count, r = ERROR_SUCCESS;
987 const char *sep;
988 DWORD sz;
990 count = MSI_RecordGetFieldCount( row );
991 for (i = start; i <= count; i++)
993 r = export_field( handle, row, i );
994 if (r == ERROR_INVALID_PARAMETER)
996 r = export_stream( row_export_info->folder, row_export_info->table, row, i, start );
997 if (r != ERROR_SUCCESS)
998 return r;
1000 /* exporting a binary stream, repeat the "Name" field */
1001 r = export_field( handle, row, start );
1002 if (r != ERROR_SUCCESS)
1003 return r;
1005 else if (r != ERROR_SUCCESS)
1006 return r;
1008 sep = (i < count) ? "\t" : "\r\n";
1009 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1010 return ERROR_FUNCTION_FAILED;
1012 return r;
1015 static UINT export_row( MSIRECORD *row, void *arg )
1017 return export_record( arg, row, 1 );
1020 static UINT export_forcecodepage( HANDLE handle, UINT codepage )
1022 static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1023 char data[sizeof(fmt) + 10];
1024 DWORD sz = sprintf( data, fmt, codepage );
1026 if (!WriteFile(handle, data, sz, &sz, NULL))
1027 return ERROR_FUNCTION_FAILED;
1029 return ERROR_SUCCESS;
1032 static UINT export_summaryinformation( MSIDATABASE *db, HANDLE handle )
1034 static const char header[] = "PropertyId\tValue\r\n"
1035 "i2\tl255\r\n"
1036 "_SummaryInformation\tPropertyId\r\n";
1037 DWORD sz = ARRAY_SIZE(header) - 1;
1039 if (!WriteFile(handle, header, sz, &sz, NULL))
1040 return ERROR_WRITE_FAULT;
1042 return msi_export_suminfo( db, handle );
1045 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, LPCWSTR file )
1047 MSIRECORD *rec = NULL;
1048 MSIQUERY *view = NULL;
1049 WCHAR *filename;
1050 HANDLE handle;
1051 UINT len, r;
1053 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1054 debugstr_w(folder), debugstr_w(file) );
1056 if (!folder || !file)
1057 return ERROR_INVALID_PARAMETER;
1059 len = lstrlenW(folder) + lstrlenW(file) + 2;
1060 filename = malloc(len * sizeof(WCHAR));
1061 if (!filename)
1062 return ERROR_OUTOFMEMORY;
1064 lstrcpyW( filename, folder );
1065 lstrcatW( filename, L"\\" );
1066 lstrcatW( filename, file );
1068 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1069 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1070 free( filename );
1071 if (handle == INVALID_HANDLE_VALUE)
1072 return ERROR_FUNCTION_FAILED;
1074 if (!wcscmp( table, L"_ForceCodepage" ))
1076 UINT codepage = msi_get_string_table_codepage( db->strings );
1077 r = export_forcecodepage( handle, codepage );
1078 goto done;
1081 if (!wcscmp( table, L"_SummaryInformation" ))
1083 r = export_summaryinformation( db, handle );
1084 goto done;
1087 r = MSI_OpenQuery( db, &view, L"SELECT * FROM %s", table );
1088 if (r == ERROR_SUCCESS)
1090 struct row_export_info row_export_info = { handle, folder, table };
1092 /* write out row 1, the column names */
1093 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1094 if (r == ERROR_SUCCESS)
1096 export_record( &row_export_info, rec, 1 );
1097 msiobj_release( &rec->hdr );
1100 /* write out row 2, the column types */
1101 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1102 if (r == ERROR_SUCCESS)
1104 export_record( &row_export_info, rec, 1 );
1105 msiobj_release( &rec->hdr );
1108 /* write out row 3, the table name + keys */
1109 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1110 if (r == ERROR_SUCCESS)
1112 MSI_RecordSetStringW( rec, 0, table );
1113 export_record( &row_export_info, rec, 0 );
1114 msiobj_release( &rec->hdr );
1117 /* write out row 4 onwards, the data */
1118 r = MSI_IterateRecords( view, 0, export_row, &row_export_info );
1119 msiobj_release( &view->hdr );
1122 done:
1123 CloseHandle( handle );
1124 return r;
1127 /***********************************************************************
1128 * MsiExportDatabaseW [MSI.@]
1130 * Writes a file containing the table data as tab separated ASCII.
1132 * The format is as follows:
1134 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1135 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1136 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1138 * Followed by the data, starting at row 1 with one row per line
1140 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1142 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, const WCHAR *szTable, const WCHAR *szFolder, const WCHAR *szFilename )
1144 MSIDATABASE *db;
1145 UINT r;
1147 TRACE( "%lu %s %s %s\n", handle, debugstr_w(szTable), debugstr_w(szFolder), debugstr_w(szFilename) );
1149 if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
1150 return ERROR_INVALID_HANDLE;
1152 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1153 msiobj_release( &db->hdr );
1154 return r;
1157 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, const char *szTable, const char *szFolder, const char *szFilename )
1159 WCHAR *path = NULL, *file = NULL, *table = NULL;
1160 UINT r = ERROR_OUTOFMEMORY;
1162 TRACE( "%lu %s %s %s\n", handle, debugstr_a(szTable), debugstr_a(szFolder), debugstr_a(szFilename) );
1164 if( szTable )
1166 table = strdupAtoW( szTable );
1167 if( !table )
1168 goto end;
1171 if( szFolder )
1173 path = strdupAtoW( szFolder );
1174 if( !path )
1175 goto end;
1178 if( szFilename )
1180 file = strdupAtoW( szFilename );
1181 if( !file )
1182 goto end;
1185 r = MsiDatabaseExportW( handle, table, path, file );
1187 end:
1188 free( table );
1189 free( path );
1190 free( file );
1192 return r;
1195 UINT WINAPI MsiDatabaseMergeA( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const char *szTableName )
1197 UINT r;
1198 WCHAR *table;
1200 TRACE("%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_a(szTableName) );
1202 table = strdupAtoW(szTableName);
1203 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1205 free(table);
1206 return r;
1209 struct merge_table
1211 struct list entry;
1212 struct list rows;
1213 LPWSTR name;
1214 DWORD numconflicts;
1215 LPWSTR *columns;
1216 DWORD numcolumns;
1217 LPWSTR *types;
1218 DWORD numtypes;
1219 LPWSTR *labels;
1220 DWORD numlabels;
1223 struct merge_row
1225 struct list entry;
1226 MSIRECORD *data;
1229 struct merge_data
1231 MSIDATABASE *db;
1232 MSIDATABASE *merge;
1233 struct merge_table *curtable;
1234 MSIQUERY *curview;
1235 struct list *tabledata;
1238 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1240 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1241 ((type2[0] == 'l') || (type2[0] == 's')))
1242 return TRUE;
1244 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1245 ((type2[0] == 'L') || (type2[0] == 'S')))
1246 return TRUE;
1248 return !wcscmp( type1, type2 );
1251 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1253 MSIRECORD *dbrec, *mergerec;
1254 UINT r, i, count;
1256 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1257 if (r != ERROR_SUCCESS)
1258 return r;
1260 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1261 if (r != ERROR_SUCCESS)
1263 msiobj_release(&dbrec->hdr);
1264 return r;
1267 count = MSI_RecordGetFieldCount(dbrec);
1268 for (i = 1; i <= count; i++)
1270 if (!MSI_RecordGetString(mergerec, i))
1271 break;
1273 if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1275 r = ERROR_DATATYPE_MISMATCH;
1276 goto done;
1280 msiobj_release(&dbrec->hdr);
1281 msiobj_release(&mergerec->hdr);
1282 dbrec = mergerec = NULL;
1284 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1285 if (r != ERROR_SUCCESS)
1286 return r;
1288 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1289 if (r != ERROR_SUCCESS)
1291 msiobj_release(&dbrec->hdr);
1292 return r;
1295 count = MSI_RecordGetFieldCount(dbrec);
1296 for (i = 1; i <= count; i++)
1298 if (!MSI_RecordGetString(mergerec, i))
1299 break;
1301 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1302 MSI_RecordGetString(mergerec, i)))
1304 r = ERROR_DATATYPE_MISMATCH;
1305 break;
1309 done:
1310 msiobj_release(&dbrec->hdr);
1311 msiobj_release(&mergerec->hdr);
1313 return r;
1316 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1317 LPCWSTR table)
1319 MSIRECORD *dbrec, *mergerec = NULL;
1320 UINT r, i, count;
1322 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1323 if (r != ERROR_SUCCESS)
1324 return r;
1326 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1327 if (r != ERROR_SUCCESS)
1328 goto done;
1330 count = MSI_RecordGetFieldCount(dbrec);
1331 if (count != MSI_RecordGetFieldCount(mergerec))
1333 r = ERROR_DATATYPE_MISMATCH;
1334 goto done;
1337 for (i = 1; i <= count; i++)
1339 if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1341 r = ERROR_DATATYPE_MISMATCH;
1342 goto done;
1346 done:
1347 msiobj_release(&dbrec->hdr);
1348 msiobj_release(&mergerec->hdr);
1350 return r;
1353 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1355 MSIRECORD *colnames;
1356 LPWSTR str, val;
1357 UINT r, i = 0;
1358 DWORD sz = 0;
1359 int cmp;
1361 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1362 if (r != ERROR_SUCCESS)
1363 return NULL;
1367 str = msi_dup_record_field(colnames, ++i);
1368 cmp = wcscmp( key, str );
1369 free(str);
1370 } while (cmp);
1372 msiobj_release(&colnames->hdr);
1374 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1375 if (r != ERROR_SUCCESS)
1376 return NULL;
1377 sz++;
1379 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1381 /* quote string record fields */
1382 sz += 2;
1383 val = malloc(sz * sizeof(WCHAR));
1384 if (!val)
1385 return NULL;
1387 lstrcpyW(val, L"'");
1388 r = MSI_RecordGetStringW(rec, i, val + 1, &sz);
1389 lstrcpyW(val + 1 + sz, L"'");
1391 else
1393 /* do not quote integer record fields */
1394 val = malloc(sz * sizeof(WCHAR));
1395 if (!val)
1396 return NULL;
1398 r = MSI_RecordGetStringW(rec, i, val, &sz);
1401 if (r != ERROR_SUCCESS)
1403 ERR("failed to get string!\n");
1404 free(val);
1405 return NULL;
1408 return val;
1411 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1412 LPWSTR table, MSIRECORD *rec)
1414 WCHAR *query = NULL, *clause = NULL, *new_clause, *val;
1415 LPCWSTR setptr, key;
1416 DWORD size, oldsize;
1417 MSIRECORD *keys;
1418 UINT r, i, count;
1420 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1421 if (r != ERROR_SUCCESS)
1422 return NULL;
1424 size = 1;
1425 count = MSI_RecordGetFieldCount(keys);
1426 for (i = 1; i <= count; i++)
1428 key = MSI_RecordGetString(keys, i);
1429 val = get_key_value(view, key, rec);
1431 if (i == count)
1432 setptr = L"`%s` = %s ";
1433 else
1434 setptr = L"`%s` = %s AND ";
1436 oldsize = size;
1437 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1438 new_clause = realloc(clause, size * sizeof(WCHAR));
1439 if (!new_clause)
1441 free(val);
1442 goto done;
1444 clause = new_clause;
1446 swprintf(clause + oldsize - 1, size - (oldsize - 1), setptr, key, val);
1447 free(val);
1450 size = lstrlenW(L"SELECT * FROM `%s` WHERE %s") + lstrlenW(table) + lstrlenW(clause) + 1;
1451 query = malloc(size * sizeof(WCHAR));
1452 if (!query)
1453 goto done;
1455 swprintf(query, size, L"SELECT * FROM `%s` WHERE %s", table, clause);
1457 done:
1458 free(clause);
1459 msiobj_release(&keys->hdr);
1460 return query;
1463 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1465 struct merge_data *data = param;
1466 struct merge_table *table = data->curtable;
1467 struct merge_row *mergerow;
1468 MSIQUERY *dbview = NULL;
1469 MSIRECORD *row = NULL;
1470 LPWSTR query = NULL;
1471 UINT r = ERROR_SUCCESS;
1473 if (TABLE_Exists(data->db, table->name))
1475 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1476 if (!query)
1477 return ERROR_OUTOFMEMORY;
1479 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1480 if (r != ERROR_SUCCESS)
1481 goto done;
1483 r = MSI_ViewExecute(dbview, NULL);
1484 if (r != ERROR_SUCCESS)
1485 goto done;
1487 r = MSI_ViewFetch(dbview, &row);
1488 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1490 table->numconflicts++;
1491 goto done;
1493 else if (r != ERROR_NO_MORE_ITEMS)
1494 goto done;
1496 r = ERROR_SUCCESS;
1499 mergerow = malloc(sizeof(*mergerow));
1500 if (!mergerow)
1502 r = ERROR_OUTOFMEMORY;
1503 goto done;
1506 mergerow->data = MSI_CloneRecord(rec);
1507 if (!mergerow->data)
1509 r = ERROR_OUTOFMEMORY;
1510 free(mergerow);
1511 goto done;
1514 list_add_tail(&table->rows, &mergerow->entry);
1516 done:
1517 free(query);
1518 msiobj_release(&row->hdr);
1519 msiobj_release(&dbview->hdr);
1520 return r;
1523 static UINT get_table_labels(MSIDATABASE *db, const WCHAR *table, WCHAR ***labels, DWORD *numlabels)
1525 UINT r, i, count;
1526 MSIRECORD *prec = NULL;
1528 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1529 if (r != ERROR_SUCCESS)
1530 return r;
1532 count = MSI_RecordGetFieldCount(prec);
1533 *numlabels = count + 1;
1534 *labels = malloc((*numlabels) * sizeof(WCHAR *));
1535 if (!*labels)
1537 r = ERROR_OUTOFMEMORY;
1538 goto end;
1541 (*labels)[0] = wcsdup(table);
1542 for (i=1; i<=count; i++ )
1544 (*labels)[i] = wcsdup(MSI_RecordGetString(prec, i));
1547 end:
1548 msiobj_release( &prec->hdr );
1549 return r;
1552 static UINT get_query_columns(MSIQUERY *query, WCHAR ***columns, DWORD *numcolumns)
1554 UINT r, i, count;
1555 MSIRECORD *prec = NULL;
1557 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1558 if (r != ERROR_SUCCESS)
1559 return r;
1561 count = MSI_RecordGetFieldCount(prec);
1562 *columns = malloc(count * sizeof(WCHAR *));
1563 if (!*columns)
1565 r = ERROR_OUTOFMEMORY;
1566 goto end;
1569 for (i=1; i<=count; i++ )
1571 (*columns)[i-1] = wcsdup(MSI_RecordGetString(prec, i));
1574 *numcolumns = count;
1576 end:
1577 msiobj_release( &prec->hdr );
1578 return r;
1581 static UINT get_query_types(MSIQUERY *query, WCHAR ***types, DWORD *numtypes)
1583 UINT r, i, count;
1584 MSIRECORD *prec = NULL;
1586 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1587 if (r != ERROR_SUCCESS)
1588 return r;
1590 count = MSI_RecordGetFieldCount(prec);
1591 *types = malloc(count * sizeof(WCHAR *));
1592 if (!*types)
1594 r = ERROR_OUTOFMEMORY;
1595 goto end;
1598 *numtypes = count;
1599 for (i=1; i<=count; i++ )
1601 (*types)[i-1] = wcsdup(MSI_RecordGetString(prec, i));
1604 end:
1605 msiobj_release( &prec->hdr );
1606 return r;
1609 static void merge_free_rows(struct merge_table *table)
1611 struct list *item, *cursor;
1613 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1615 struct merge_row *row = LIST_ENTRY(item, struct merge_row, entry);
1617 list_remove(&row->entry);
1618 msiobj_release(&row->data->hdr);
1619 free(row);
1623 static void free_merge_table(struct merge_table *table)
1625 UINT i;
1627 if (table->labels != NULL)
1629 for (i = 0; i < table->numlabels; i++)
1630 free(table->labels[i]);
1632 free(table->labels);
1635 if (table->columns != NULL)
1637 for (i = 0; i < table->numcolumns; i++)
1638 free(table->columns[i]);
1640 free(table->columns);
1643 if (table->types != NULL)
1645 for (i = 0; i < table->numtypes; i++)
1646 free(table->types[i]);
1648 free(table->types);
1651 free(table->name);
1652 merge_free_rows(table);
1654 free(table);
1657 static UINT get_merge_table(MSIDATABASE *db, const WCHAR *name, struct merge_table **ptable)
1659 UINT r;
1660 struct merge_table *table;
1661 MSIQUERY *mergeview = NULL;
1663 table = calloc(1, sizeof(*table));
1664 if (!table)
1666 *ptable = NULL;
1667 return ERROR_OUTOFMEMORY;
1670 r = get_table_labels(db, name, &table->labels, &table->numlabels);
1671 if (r != ERROR_SUCCESS)
1672 goto err;
1674 r = MSI_OpenQuery(db, &mergeview, L"SELECT * FROM `%s`", name);
1675 if (r != ERROR_SUCCESS)
1676 goto err;
1678 r = get_query_columns(mergeview, &table->columns, &table->numcolumns);
1679 if (r != ERROR_SUCCESS)
1680 goto err;
1682 r = get_query_types(mergeview, &table->types, &table->numtypes);
1683 if (r != ERROR_SUCCESS)
1684 goto err;
1686 list_init(&table->rows);
1688 table->name = wcsdup(name);
1689 table->numconflicts = 0;
1691 msiobj_release(&mergeview->hdr);
1692 *ptable = table;
1693 return ERROR_SUCCESS;
1695 err:
1696 msiobj_release(&mergeview->hdr);
1697 free_merge_table(table);
1698 *ptable = NULL;
1699 return r;
1702 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1704 struct merge_data *data = param;
1705 struct merge_table *table;
1706 MSIQUERY *dbview = NULL;
1707 MSIQUERY *mergeview = NULL;
1708 LPCWSTR name;
1709 UINT r;
1711 name = MSI_RecordGetString(rec, 1);
1713 r = MSI_OpenQuery(data->merge, &mergeview, L"SELECT * FROM `%s`", name);
1714 if (r != ERROR_SUCCESS)
1715 goto done;
1717 if (TABLE_Exists(data->db, name))
1719 r = MSI_OpenQuery(data->db, &dbview, L"SELECT * FROM `%s`", name);
1720 if (r != ERROR_SUCCESS)
1721 goto done;
1723 r = merge_verify_colnames(dbview, mergeview);
1724 if (r != ERROR_SUCCESS)
1725 goto done;
1727 r = merge_verify_primary_keys(data->db, data->merge, name);
1728 if (r != ERROR_SUCCESS)
1729 goto done;
1732 r = get_merge_table(data->merge, name, &table);
1733 if (r != ERROR_SUCCESS)
1734 goto done;
1736 data->curtable = table;
1737 data->curview = mergeview;
1738 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1739 if (r != ERROR_SUCCESS)
1741 free_merge_table(table);
1742 goto done;
1745 list_add_tail(data->tabledata, &table->entry);
1747 done:
1748 msiobj_release(&dbview->hdr);
1749 msiobj_release(&mergeview->hdr);
1750 return r;
1753 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1754 struct list *tabledata)
1756 MSIQUERY *view;
1757 struct merge_data data;
1758 UINT r;
1760 r = MSI_DatabaseOpenViewW(merge, L"SELECT * FROM `_Tables`", &view);
1761 if (r != ERROR_SUCCESS)
1762 return r;
1764 data.db = db;
1765 data.merge = merge;
1766 data.tabledata = tabledata;
1767 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1768 msiobj_release(&view->hdr);
1769 return r;
1772 static UINT merge_table(MSIDATABASE *db, struct merge_table *table)
1774 UINT r;
1775 struct merge_row *row;
1776 MSIVIEW *tv;
1778 if (!TABLE_Exists(db, table->name))
1780 r = add_table_to_db(db, table->columns, table->types, table->labels, table->numlabels, table->numcolumns);
1781 if (r != ERROR_SUCCESS)
1782 return ERROR_FUNCTION_FAILED;
1785 LIST_FOR_EACH_ENTRY(row, &table->rows, struct merge_row, entry)
1787 r = TABLE_CreateView(db, table->name, &tv);
1788 if (r != ERROR_SUCCESS)
1789 return r;
1791 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1792 tv->ops->delete(tv);
1794 if (r != ERROR_SUCCESS)
1795 return r;
1798 return ERROR_SUCCESS;
1801 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1802 LPWSTR table, DWORD numconflicts)
1804 UINT r;
1805 MSIQUERY *view;
1807 if (!TABLE_Exists(db, error))
1809 r = MSI_OpenQuery(db, &view, L"CREATE TABLE `%s` (`Table` CHAR(255) NOT NULL, `NumRowMergeConflicts` SHORT "
1810 "NOT NULL PRIMARY KEY `Table`)" , error);
1811 if (r != ERROR_SUCCESS)
1812 return r;
1814 r = MSI_ViewExecute(view, NULL);
1815 msiobj_release(&view->hdr);
1816 if (r != ERROR_SUCCESS)
1817 return r;
1820 r = MSI_OpenQuery(db, &view, L"INSERT INTO `%s` (`Table`, `NumRowMergeConflicts`) VALUES ('%s', %d)", error,
1821 table, numconflicts);
1822 if (r != ERROR_SUCCESS)
1823 return r;
1825 r = MSI_ViewExecute(view, NULL);
1826 msiobj_release(&view->hdr);
1827 return r;
1830 UINT WINAPI MsiDatabaseMergeW( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const WCHAR *szTableName )
1832 struct list tabledata = LIST_INIT(tabledata);
1833 struct list *item, *cursor;
1834 MSIDATABASE *db, *merge;
1835 struct merge_table *table;
1836 BOOL conflicts;
1837 UINT r;
1839 TRACE( "%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_w(szTableName) );
1841 if (szTableName && !*szTableName)
1842 return ERROR_INVALID_TABLE;
1844 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1845 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1846 if (!db || !merge)
1848 r = ERROR_INVALID_HANDLE;
1849 goto done;
1852 r = gather_merge_data(db, merge, &tabledata);
1853 if (r != ERROR_SUCCESS)
1854 goto done;
1856 conflicts = FALSE;
1857 LIST_FOR_EACH_ENTRY(table, &tabledata, struct merge_table, entry)
1859 if (table->numconflicts)
1861 conflicts = TRUE;
1863 r = update_merge_errors(db, szTableName, table->name,
1864 table->numconflicts);
1865 if (r != ERROR_SUCCESS)
1866 break;
1868 else
1870 r = merge_table(db, table);
1871 if (r != ERROR_SUCCESS)
1872 break;
1876 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1878 struct merge_table *table = LIST_ENTRY(item, struct merge_table, entry);
1879 list_remove(&table->entry);
1880 free_merge_table(table);
1883 if (conflicts)
1884 r = ERROR_FUNCTION_FAILED;
1886 done:
1887 msiobj_release(&db->hdr);
1888 msiobj_release(&merge->hdr);
1889 return r;
1892 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1894 MSIDBSTATE ret = MSIDBSTATE_READ;
1895 MSIDATABASE *db;
1897 TRACE( "%lu\n", handle );
1899 if (!(db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE )))
1900 return MSIDBSTATE_ERROR;
1902 if (db->mode != MSI_OPEN_READONLY )
1903 ret = MSIDBSTATE_WRITE;
1904 msiobj_release( &db->hdr );
1906 return ret;
1909 MSICONDITION __cdecl s_remote_DatabaseIsTablePersistent(MSIHANDLE db, LPCWSTR table)
1911 return MsiDatabaseIsTablePersistentW(db, table);
1914 UINT __cdecl s_remote_DatabaseGetPrimaryKeys(MSIHANDLE db, LPCWSTR table, struct wire_record **rec)
1916 MSIHANDLE handle;
1917 UINT r = MsiDatabaseGetPrimaryKeysW(db, table, &handle);
1918 *rec = NULL;
1919 if (!r)
1920 *rec = marshal_record(handle);
1921 MsiCloseHandle(handle);
1922 return r;
1925 UINT __cdecl s_remote_DatabaseGetSummaryInformation(MSIHANDLE db, UINT updatecount, MSIHANDLE *suminfo)
1927 return MsiGetSummaryInformationW(db, NULL, updatecount, suminfo);
1930 UINT __cdecl s_remote_DatabaseOpenView(MSIHANDLE db, LPCWSTR query, MSIHANDLE *view)
1932 return MsiDatabaseOpenViewW(db, query, view);