dmime: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / msi / database.c
bloba39321a18bb552aa1acea32ed19aa0b2619feb1a
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 UINT 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--;
399 * make sure this line has the same number of entries as there are columns
400 * which are indicated by the first line.
402 if (*num_entries && *num_entries != count)
403 return ERROR_FUNCTION_FAILED;
405 *entries = malloc(count * sizeof(WCHAR *));
406 if (!*entries)
407 return ERROR_OUTOFMEMORY;
409 /* store pointers into the data */
410 chars_left = *len;
411 for (i = 0, ptr = *line; i < count; i++)
413 while (chars_left && *ptr == '\r')
415 ptr++;
416 chars_left--;
418 save = ptr;
420 while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
422 if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */
423 if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11')
425 *ptr = '\n';
426 *(ptr - 1) = '\r';
428 ptr++;
429 chars_left--;
432 /* NULL-separate the data */
433 if (*ptr == '\n' || *ptr == '\r')
435 while (chars_left && (*ptr == '\n' || *ptr == '\r'))
437 *(ptr++) = 0;
438 chars_left--;
441 else if (*ptr)
443 *(ptr++) = 0;
444 chars_left--;
446 (*entries)[i] = save;
449 /* move to the next line if there's more, else EOF */
450 *line = ptr;
451 *len = chars_left;
452 if (!*num_entries)
453 *num_entries = count;
455 return ERROR_SUCCESS;
458 static WCHAR *build_createsql_prelude(const WCHAR *table)
460 LPWSTR prelude;
461 DWORD size;
463 size = ARRAY_SIZE(L"CREATE TABLE `%s` ( ") + lstrlenW(table) - 2;
464 prelude = malloc(size * sizeof(WCHAR));
465 if (!prelude)
466 return NULL;
468 swprintf(prelude, size, L"CREATE TABLE `%s` ( ", table);
469 return prelude;
472 static WCHAR *build_createsql_columns(WCHAR **columns_data, WCHAR **types, DWORD num_columns)
474 LPWSTR columns, p;
475 LPCWSTR type;
476 DWORD sql_size = 1, i, len;
477 WCHAR expanded[128], *ptr;
478 WCHAR size[10], comma[2], extra[30];
480 columns = calloc(sql_size, sizeof(WCHAR));
481 if (!columns)
482 return NULL;
484 for (i = 0; i < num_columns; i++)
486 type = NULL;
487 comma[1] = size[0] = extra[0] = '\0';
489 if (i == num_columns - 1)
490 comma[0] = '\0';
491 else
492 comma[0] = ',';
494 ptr = &types[i][1];
495 len = wcstol(ptr, NULL, 10);
496 extra[0] = '\0';
498 switch (types[i][0])
500 case 'l':
501 lstrcpyW(extra, L" NOT NULL");
502 /* fall through */
503 case 'L':
504 lstrcatW(extra, L" LOCALIZABLE");
505 type = L"CHAR";
506 swprintf(size, ARRAY_SIZE(size), L"(%s)", ptr);
507 break;
508 case 's':
509 lstrcpyW(extra, L" NOT NULL");
510 /* fall through */
511 case 'S':
512 type = L"CHAR";
513 swprintf(size, ARRAY_SIZE(size), L"(%s)", ptr);
514 break;
515 case 'i':
516 lstrcpyW(extra, L" NOT NULL");
517 /* fall through */
518 case 'I':
519 if (len <= 2)
520 type = L"INT";
521 else if (len == 4)
522 type = L"LONG";
523 else
525 WARN("invalid int width %lu\n", len);
526 free(columns);
527 return NULL;
529 break;
530 case 'v':
531 lstrcpyW(extra, L" NOT NULL");
532 /* fall through */
533 case 'V':
534 type = L"OBJECT";
535 break;
536 default:
537 ERR("Unknown type: %c\n", types[i][0]);
538 free(columns);
539 return NULL;
542 swprintf(expanded, ARRAY_SIZE(expanded), L"`%s` %s%s%s%s ", columns_data[i], type, size, extra, comma);
543 sql_size += lstrlenW(expanded);
545 p = realloc(columns, sql_size * sizeof(WCHAR));
546 if (!p)
548 free(columns);
549 return NULL;
551 columns = p;
553 lstrcatW(columns, expanded);
556 return columns;
559 static WCHAR *build_createsql_postlude(WCHAR **primary_keys, DWORD num_keys)
561 LPWSTR postlude, keys, ptr;
562 DWORD size, i;
564 for (i = 0, size = 1; i < num_keys; i++)
565 size += lstrlenW(L"`%s`, ") + lstrlenW(primary_keys[i]) - 2;
567 keys = malloc(size * sizeof(WCHAR));
568 if (!keys)
569 return NULL;
571 for (i = 0, ptr = keys; i < num_keys; i++)
573 ptr += swprintf(ptr, size - (ptr - keys), L"`%s`, ", primary_keys[i]);
576 /* remove final ', ' */
577 *(ptr - 2) = '\0';
579 size = lstrlenW(L"PRIMARY KEY %s)") + size - 1;
580 postlude = malloc(size * sizeof(WCHAR));
581 if (!postlude)
582 goto done;
584 swprintf(postlude, size, L"PRIMARY KEY %s)", keys);
586 done:
587 free(keys);
588 return postlude;
591 static UINT add_table_to_db(MSIDATABASE *db, WCHAR **columns, WCHAR **types, WCHAR **labels, DWORD num_labels,
592 DWORD num_columns)
594 UINT r = ERROR_OUTOFMEMORY;
595 DWORD size;
596 MSIQUERY *view;
597 LPWSTR create_sql = NULL;
598 LPWSTR prelude, columns_sql, postlude;
600 prelude = build_createsql_prelude(labels[0]);
601 columns_sql = build_createsql_columns(columns, types, num_columns);
602 postlude = build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
604 if (!prelude || !columns_sql || !postlude)
605 goto done;
607 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
608 create_sql = malloc(size * sizeof(WCHAR));
609 if (!create_sql)
610 goto done;
612 lstrcpyW(create_sql, prelude);
613 lstrcatW(create_sql, columns_sql);
614 lstrcatW(create_sql, postlude);
616 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
617 if (r != ERROR_SUCCESS)
618 goto done;
620 r = MSI_ViewExecute(view, NULL);
621 MSI_ViewClose(view);
622 msiobj_release(&view->hdr);
624 done:
625 free(prelude);
626 free(columns_sql);
627 free(postlude);
628 free(create_sql);
629 return r;
632 static WCHAR *import_stream_filename(const WCHAR *path, const WCHAR *name)
634 DWORD len;
635 LPWSTR fullname, ptr;
637 len = lstrlenW(path) + lstrlenW(name) + 1;
638 fullname = malloc(len * sizeof(WCHAR));
639 if (!fullname)
640 return NULL;
642 lstrcpyW( fullname, path );
644 /* chop off extension from path */
645 ptr = wcsrchr(fullname, '.');
646 if (!ptr)
648 free(fullname);
649 return NULL;
651 *ptr++ = '\\';
652 lstrcpyW( ptr, name );
653 return fullname;
656 static UINT construct_record(DWORD num_columns, LPWSTR *types,
657 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
659 UINT i;
661 *rec = MSI_CreateRecord(num_columns);
662 if (!*rec)
663 return ERROR_OUTOFMEMORY;
665 for (i = 0; i < num_columns; i++)
667 switch (types[i][0])
669 case 'L': case 'l': case 'S': case 's':
670 MSI_RecordSetStringW(*rec, i + 1, data[i]);
671 break;
672 case 'I': case 'i':
673 if (*data[i])
674 MSI_RecordSetInteger(*rec, i + 1, wcstol(data[i], NULL, 10));
675 break;
676 case 'V': case 'v':
677 if (*data[i])
679 UINT r;
680 WCHAR *file = import_stream_filename(path, data[i]);
681 if (!file)
682 return ERROR_FUNCTION_FAILED;
684 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
685 free (file);
686 if (r != ERROR_SUCCESS)
687 return ERROR_FUNCTION_FAILED;
689 break;
690 default:
691 ERR("Unhandled column type: %c\n", types[i][0]);
692 msiobj_release(&(*rec)->hdr);
693 return ERROR_FUNCTION_FAILED;
697 return ERROR_SUCCESS;
700 static UINT add_records_to_table(MSIDATABASE *db, WCHAR **columns, WCHAR **types, WCHAR **labels, WCHAR ***records,
701 int num_columns, int num_records, WCHAR *path)
703 UINT r;
704 int i;
705 MSIQUERY *view;
706 MSIRECORD *rec;
708 r = MSI_OpenQuery(db, &view, L"SELECT * FROM `%s`", labels[0]);
709 if (r != ERROR_SUCCESS)
710 return r;
712 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
714 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
715 msiobj_release(&rec->hdr);
716 if (r != ERROR_SUCCESS)
717 goto done;
720 for (i = 0; i < num_records; i++)
722 r = construct_record(num_columns, types, records[i], path, &rec);
723 if (r != ERROR_SUCCESS)
724 goto done;
726 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
727 if (r != ERROR_SUCCESS)
729 msiobj_release(&rec->hdr);
730 goto done;
733 msiobj_release(&rec->hdr);
736 done:
737 msiobj_release(&view->hdr);
738 return r;
741 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
743 UINT r;
744 DWORD len, i, num_labels = 0, num_types = 0, num_columns = 0, num_records = 0;
745 WCHAR **columns, **types, **labels, *path, *ptr, *data, ***records = NULL, ***temp_records;
747 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
749 if (!folder || !file)
750 return ERROR_INVALID_PARAMETER;
752 len = lstrlenW(folder) + lstrlenW(L"\\") + lstrlenW(file) + 1;
753 path = malloc( len * sizeof(WCHAR) );
754 if (!path)
755 return ERROR_OUTOFMEMORY;
757 lstrcpyW( path, folder );
758 lstrcatW( path, L"\\" );
759 lstrcatW( path, file );
761 data = read_text_archive( path, &len );
762 if (!data)
764 free(path);
765 return ERROR_FUNCTION_FAILED;
768 ptr = data;
769 parse_line( &ptr, &columns, &num_columns, &len );
770 parse_line( &ptr, &types, &num_types, &len );
771 parse_line( &ptr, &labels, &num_labels, &len );
773 if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
774 num_types == 2 && !wcscmp( types[1], L"_ForceCodepage" ))
776 r = msi_set_string_table_codepage( db->strings, wcstol( types[0], NULL, 10 ) );
777 goto done;
780 if (num_columns != num_types)
782 r = ERROR_FUNCTION_FAILED;
783 goto done;
786 records = malloc(sizeof(WCHAR **));
787 if (!records)
789 r = ERROR_OUTOFMEMORY;
790 goto done;
793 /* read in the table records */
794 while (len)
796 r = parse_line( &ptr, &records[num_records], &num_columns, &len );
797 if (r != ERROR_SUCCESS)
798 goto done;
800 num_records++;
801 temp_records = realloc(records, (num_records + 1) * sizeof(WCHAR **));
802 if (!temp_records)
804 r = ERROR_OUTOFMEMORY;
805 goto done;
807 records = temp_records;
810 if (!wcscmp(labels[0], L"_SummaryInformation"))
812 r = msi_add_suminfo( db, records, num_records, num_columns );
813 if (r != ERROR_SUCCESS)
815 r = ERROR_FUNCTION_FAILED;
816 goto done;
819 else
821 if (!TABLE_Exists(db, labels[0]))
823 r = add_table_to_db( db, columns, types, labels, num_labels, num_columns );
824 if (r != ERROR_SUCCESS)
826 r = ERROR_FUNCTION_FAILED;
827 goto done;
831 r = add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
834 done:
835 free(path);
836 free(data);
837 free(columns);
838 free(types);
839 free(labels);
841 for (i = 0; i < num_records; i++)
842 free(records[i]);
844 free(records);
845 return r;
848 UINT WINAPI MsiDatabaseImportW( MSIHANDLE handle, const WCHAR *szFolder, const WCHAR *szFilename )
850 MSIDATABASE *db;
851 UINT r;
853 TRACE( "%lu %s %s\n", handle, debugstr_w(szFolder), debugstr_w(szFilename) );
855 if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
856 return ERROR_INVALID_HANDLE;
858 r = MSI_DatabaseImport( db, szFolder, szFilename );
859 msiobj_release( &db->hdr );
860 return r;
863 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, const char *szFolder, const char *szFilename )
865 WCHAR *path = NULL, *file = NULL;
866 UINT r = ERROR_OUTOFMEMORY;
868 TRACE( "%lu %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename) );
870 if( szFolder )
872 path = strdupAtoW( szFolder );
873 if( !path )
874 goto end;
877 if( szFilename )
879 file = strdupAtoW( szFilename );
880 if( !file )
881 goto end;
884 r = MsiDatabaseImportW( handle, path, file );
886 end:
887 free( path );
888 free( file );
890 return r;
893 static UINT export_field( HANDLE handle, MSIRECORD *row, UINT field )
895 char *buffer, *ptr;
896 BOOL ret;
897 DWORD sz = 0x100;
898 UINT r;
900 buffer = malloc( sz );
901 if (!buffer)
902 return ERROR_OUTOFMEMORY;
904 r = MSI_RecordGetStringA( row, field, buffer, &sz );
905 if (r == ERROR_MORE_DATA)
907 char *tmp;
909 sz++; /* leave room for NULL terminator */
910 tmp = realloc( buffer, sz );
911 if (!tmp)
913 free( buffer );
914 return ERROR_OUTOFMEMORY;
916 buffer = tmp;
918 r = MSI_RecordGetStringA( row, field, buffer, &sz );
919 if (r != ERROR_SUCCESS)
921 free( buffer );
922 return r;
925 else if (r != ERROR_SUCCESS)
927 free( buffer );
928 return r;
931 ptr = buffer;
932 while( *ptr )
934 if (*ptr == '\r' && *( ptr + 1 ) == '\n')
936 *ptr++ = '\x11';
937 *ptr++ = '\x19';
938 continue;
941 if (*ptr == '\n')
942 *ptr = '\x19';
944 ptr++;
947 ret = WriteFile( handle, buffer, sz, &sz, NULL );
948 free( buffer );
949 return ret ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
952 static UINT export_stream( const WCHAR *folder, const WCHAR *table, MSIRECORD *row, UINT field, UINT start )
954 WCHAR stream[MAX_STREAM_NAME_LEN + 1], *path;
955 DWORD sz, read_size, write_size;
956 char buffer[1024];
957 HANDLE file;
958 UINT len, r;
960 sz = ARRAY_SIZE( stream );
961 r = MSI_RecordGetStringW( row, start, stream, &sz );
962 if (r != ERROR_SUCCESS)
963 return r;
965 len = sz + lstrlenW( folder ) + lstrlenW( table ) + ARRAY_SIZE( L"%s\\%s" ) + 1;
966 if (!(path = malloc( len * sizeof(WCHAR) )))
967 return ERROR_OUTOFMEMORY;
969 len = swprintf( path, len, L"%s\\%s", folder, table );
970 if (!CreateDirectoryW( path, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
972 free( path );
973 return ERROR_FUNCTION_FAILED;
976 path[len++] = '\\';
977 lstrcpyW( path + len, stream );
978 file = CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
979 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
980 free( path );
981 if (file == INVALID_HANDLE_VALUE)
982 return ERROR_FUNCTION_FAILED;
984 read_size = sizeof(buffer);
985 while (read_size == sizeof(buffer))
987 r = MSI_RecordReadStream( row, field, buffer, &read_size );
988 if (r != ERROR_SUCCESS)
990 CloseHandle( file );
991 return r;
993 if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
995 CloseHandle( file );
996 return ERROR_WRITE_FAULT;
999 CloseHandle( file );
1000 return r;
1003 struct row_export_info
1005 HANDLE handle;
1006 const WCHAR *folder;
1007 const WCHAR *table;
1010 static UINT export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
1012 HANDLE handle = row_export_info->handle;
1013 UINT i, count, r = ERROR_SUCCESS;
1014 const char *sep;
1015 DWORD sz;
1017 count = MSI_RecordGetFieldCount( row );
1018 for (i = start; i <= count; i++)
1020 r = export_field( handle, row, i );
1021 if (r == ERROR_INVALID_PARAMETER)
1023 r = export_stream( row_export_info->folder, row_export_info->table, row, i, start );
1024 if (r != ERROR_SUCCESS)
1025 return r;
1027 /* exporting a binary stream, repeat the "Name" field */
1028 r = export_field( handle, row, start );
1029 if (r != ERROR_SUCCESS)
1030 return r;
1032 else if (r != ERROR_SUCCESS)
1033 return r;
1035 sep = (i < count) ? "\t" : "\r\n";
1036 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1037 return ERROR_FUNCTION_FAILED;
1039 return r;
1042 static UINT export_row( MSIRECORD *row, void *arg )
1044 return export_record( arg, row, 1 );
1047 static UINT export_forcecodepage( HANDLE handle, UINT codepage )
1049 static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1050 char data[sizeof(fmt) + 10];
1051 DWORD sz = sprintf( data, fmt, codepage );
1053 if (!WriteFile(handle, data, sz, &sz, NULL))
1054 return ERROR_FUNCTION_FAILED;
1056 return ERROR_SUCCESS;
1059 static UINT export_summaryinformation( MSIDATABASE *db, HANDLE handle )
1061 static const char header[] = "PropertyId\tValue\r\n"
1062 "i2\tl255\r\n"
1063 "_SummaryInformation\tPropertyId\r\n";
1064 DWORD sz = ARRAY_SIZE(header) - 1;
1066 if (!WriteFile(handle, header, sz, &sz, NULL))
1067 return ERROR_WRITE_FAULT;
1069 return msi_export_suminfo( db, handle );
1072 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, LPCWSTR file )
1074 MSIRECORD *rec = NULL;
1075 MSIQUERY *view = NULL;
1076 WCHAR *filename;
1077 HANDLE handle;
1078 UINT len, r;
1080 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1081 debugstr_w(folder), debugstr_w(file) );
1083 if (!folder || !file)
1084 return ERROR_INVALID_PARAMETER;
1086 len = lstrlenW(folder) + lstrlenW(file) + 2;
1087 filename = malloc(len * sizeof(WCHAR));
1088 if (!filename)
1089 return ERROR_OUTOFMEMORY;
1091 lstrcpyW( filename, folder );
1092 lstrcatW( filename, L"\\" );
1093 lstrcatW( filename, file );
1095 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1096 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1097 free( filename );
1098 if (handle == INVALID_HANDLE_VALUE)
1099 return ERROR_FUNCTION_FAILED;
1101 if (!wcscmp( table, L"_ForceCodepage" ))
1103 UINT codepage = msi_get_string_table_codepage( db->strings );
1104 r = export_forcecodepage( handle, codepage );
1105 goto done;
1108 if (!wcscmp( table, L"_SummaryInformation" ))
1110 r = export_summaryinformation( db, handle );
1111 goto done;
1114 r = MSI_OpenQuery( db, &view, L"SELECT * FROM %s", table );
1115 if (r == ERROR_SUCCESS)
1117 struct row_export_info row_export_info = { handle, folder, table };
1119 /* write out row 1, the column names */
1120 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1121 if (r == ERROR_SUCCESS)
1123 export_record( &row_export_info, rec, 1 );
1124 msiobj_release( &rec->hdr );
1127 /* write out row 2, the column types */
1128 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1129 if (r == ERROR_SUCCESS)
1131 export_record( &row_export_info, rec, 1 );
1132 msiobj_release( &rec->hdr );
1135 /* write out row 3, the table name + keys */
1136 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1137 if (r == ERROR_SUCCESS)
1139 MSI_RecordSetStringW( rec, 0, table );
1140 export_record( &row_export_info, rec, 0 );
1141 msiobj_release( &rec->hdr );
1144 /* write out row 4 onwards, the data */
1145 r = MSI_IterateRecords( view, 0, export_row, &row_export_info );
1146 msiobj_release( &view->hdr );
1149 done:
1150 CloseHandle( handle );
1151 return r;
1154 /***********************************************************************
1155 * MsiExportDatabaseW [MSI.@]
1157 * Writes a file containing the table data as tab separated ASCII.
1159 * The format is as follows:
1161 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1162 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1163 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1165 * Followed by the data, starting at row 1 with one row per line
1167 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1169 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, const WCHAR *szTable, const WCHAR *szFolder, const WCHAR *szFilename )
1171 MSIDATABASE *db;
1172 UINT r;
1174 TRACE( "%lu %s %s %s\n", handle, debugstr_w(szTable), debugstr_w(szFolder), debugstr_w(szFilename) );
1176 if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
1177 return ERROR_INVALID_HANDLE;
1179 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1180 msiobj_release( &db->hdr );
1181 return r;
1184 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, const char *szTable, const char *szFolder, const char *szFilename )
1186 WCHAR *path = NULL, *file = NULL, *table = NULL;
1187 UINT r = ERROR_OUTOFMEMORY;
1189 TRACE( "%lu %s %s %s\n", handle, debugstr_a(szTable), debugstr_a(szFolder), debugstr_a(szFilename) );
1191 if( szTable )
1193 table = strdupAtoW( szTable );
1194 if( !table )
1195 goto end;
1198 if( szFolder )
1200 path = strdupAtoW( szFolder );
1201 if( !path )
1202 goto end;
1205 if( szFilename )
1207 file = strdupAtoW( szFilename );
1208 if( !file )
1209 goto end;
1212 r = MsiDatabaseExportW( handle, table, path, file );
1214 end:
1215 free( table );
1216 free( path );
1217 free( file );
1219 return r;
1222 UINT WINAPI MsiDatabaseMergeA( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const char *szTableName )
1224 UINT r;
1225 WCHAR *table;
1227 TRACE("%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_a(szTableName) );
1229 table = strdupAtoW(szTableName);
1230 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1232 free(table);
1233 return r;
1236 struct merge_table
1238 struct list entry;
1239 struct list rows;
1240 LPWSTR name;
1241 DWORD numconflicts;
1242 LPWSTR *columns;
1243 DWORD numcolumns;
1244 LPWSTR *types;
1245 DWORD numtypes;
1246 LPWSTR *labels;
1247 DWORD numlabels;
1250 struct merge_row
1252 struct list entry;
1253 MSIRECORD *data;
1256 struct merge_data
1258 MSIDATABASE *db;
1259 MSIDATABASE *merge;
1260 struct merge_table *curtable;
1261 MSIQUERY *curview;
1262 struct list *tabledata;
1265 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1267 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1268 ((type2[0] == 'l') || (type2[0] == 's')))
1269 return TRUE;
1271 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1272 ((type2[0] == 'L') || (type2[0] == 'S')))
1273 return TRUE;
1275 return !wcscmp( type1, type2 );
1278 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1280 MSIRECORD *dbrec, *mergerec;
1281 UINT r, i, count;
1283 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1284 if (r != ERROR_SUCCESS)
1285 return r;
1287 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1288 if (r != ERROR_SUCCESS)
1290 msiobj_release(&dbrec->hdr);
1291 return r;
1294 count = MSI_RecordGetFieldCount(dbrec);
1295 for (i = 1; i <= count; i++)
1297 if (!MSI_RecordGetString(mergerec, i))
1298 break;
1300 if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1302 r = ERROR_DATATYPE_MISMATCH;
1303 goto done;
1307 msiobj_release(&dbrec->hdr);
1308 msiobj_release(&mergerec->hdr);
1309 dbrec = mergerec = NULL;
1311 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1312 if (r != ERROR_SUCCESS)
1313 return r;
1315 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1316 if (r != ERROR_SUCCESS)
1318 msiobj_release(&dbrec->hdr);
1319 return r;
1322 count = MSI_RecordGetFieldCount(dbrec);
1323 for (i = 1; i <= count; i++)
1325 if (!MSI_RecordGetString(mergerec, i))
1326 break;
1328 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1329 MSI_RecordGetString(mergerec, i)))
1331 r = ERROR_DATATYPE_MISMATCH;
1332 break;
1336 done:
1337 msiobj_release(&dbrec->hdr);
1338 msiobj_release(&mergerec->hdr);
1340 return r;
1343 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1344 LPCWSTR table)
1346 MSIRECORD *dbrec, *mergerec = NULL;
1347 UINT r, i, count;
1349 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1350 if (r != ERROR_SUCCESS)
1351 return r;
1353 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1354 if (r != ERROR_SUCCESS)
1355 goto done;
1357 count = MSI_RecordGetFieldCount(dbrec);
1358 if (count != MSI_RecordGetFieldCount(mergerec))
1360 r = ERROR_DATATYPE_MISMATCH;
1361 goto done;
1364 for (i = 1; i <= count; i++)
1366 if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1368 r = ERROR_DATATYPE_MISMATCH;
1369 goto done;
1373 done:
1374 msiobj_release(&dbrec->hdr);
1375 msiobj_release(&mergerec->hdr);
1377 return r;
1380 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1382 MSIRECORD *colnames;
1383 LPWSTR str, val;
1384 UINT r, i = 0;
1385 DWORD sz = 0;
1386 int cmp;
1388 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1389 if (r != ERROR_SUCCESS)
1390 return NULL;
1394 str = msi_dup_record_field(colnames, ++i);
1395 cmp = wcscmp( key, str );
1396 free(str);
1397 } while (cmp);
1399 msiobj_release(&colnames->hdr);
1401 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1402 if (r != ERROR_SUCCESS)
1403 return NULL;
1404 sz++;
1406 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1408 /* quote string record fields */
1409 sz += 2;
1410 val = malloc(sz * sizeof(WCHAR));
1411 if (!val)
1412 return NULL;
1414 lstrcpyW(val, L"'");
1415 r = MSI_RecordGetStringW(rec, i, val + 1, &sz);
1416 lstrcpyW(val + 1 + sz, L"'");
1418 else
1420 /* do not quote integer record fields */
1421 val = malloc(sz * sizeof(WCHAR));
1422 if (!val)
1423 return NULL;
1425 r = MSI_RecordGetStringW(rec, i, val, &sz);
1428 if (r != ERROR_SUCCESS)
1430 ERR("failed to get string!\n");
1431 free(val);
1432 return NULL;
1435 return val;
1438 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1439 LPWSTR table, MSIRECORD *rec)
1441 WCHAR *query = NULL, *clause = NULL, *new_clause, *val;
1442 LPCWSTR setptr, key;
1443 DWORD size, oldsize;
1444 MSIRECORD *keys;
1445 UINT r, i, count;
1447 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1448 if (r != ERROR_SUCCESS)
1449 return NULL;
1451 size = 1;
1452 count = MSI_RecordGetFieldCount(keys);
1453 for (i = 1; i <= count; i++)
1455 key = MSI_RecordGetString(keys, i);
1456 val = get_key_value(view, key, rec);
1458 if (i == count)
1459 setptr = L"`%s` = %s ";
1460 else
1461 setptr = L"`%s` = %s AND ";
1463 oldsize = size;
1464 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1465 new_clause = realloc(clause, size * sizeof(WCHAR));
1466 if (!new_clause)
1468 free(val);
1469 goto done;
1471 clause = new_clause;
1473 swprintf(clause + oldsize - 1, size - (oldsize - 1), setptr, key, val);
1474 free(val);
1477 size = lstrlenW(L"SELECT * FROM `%s` WHERE %s") + lstrlenW(table) + lstrlenW(clause) + 1;
1478 query = malloc(size * sizeof(WCHAR));
1479 if (!query)
1480 goto done;
1482 swprintf(query, size, L"SELECT * FROM `%s` WHERE %s", table, clause);
1484 done:
1485 free(clause);
1486 msiobj_release(&keys->hdr);
1487 return query;
1490 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1492 struct merge_data *data = param;
1493 struct merge_table *table = data->curtable;
1494 struct merge_row *mergerow;
1495 MSIQUERY *dbview = NULL;
1496 MSIRECORD *row = NULL;
1497 LPWSTR query = NULL;
1498 UINT r = ERROR_SUCCESS;
1500 if (TABLE_Exists(data->db, table->name))
1502 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1503 if (!query)
1504 return ERROR_OUTOFMEMORY;
1506 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1507 if (r != ERROR_SUCCESS)
1508 goto done;
1510 r = MSI_ViewExecute(dbview, NULL);
1511 if (r != ERROR_SUCCESS)
1512 goto done;
1514 r = MSI_ViewFetch(dbview, &row);
1515 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1517 table->numconflicts++;
1518 goto done;
1520 else if (r != ERROR_NO_MORE_ITEMS)
1521 goto done;
1523 r = ERROR_SUCCESS;
1526 mergerow = malloc(sizeof(*mergerow));
1527 if (!mergerow)
1529 r = ERROR_OUTOFMEMORY;
1530 goto done;
1533 mergerow->data = MSI_CloneRecord(rec);
1534 if (!mergerow->data)
1536 r = ERROR_OUTOFMEMORY;
1537 free(mergerow);
1538 goto done;
1541 list_add_tail(&table->rows, &mergerow->entry);
1543 done:
1544 free(query);
1545 msiobj_release(&row->hdr);
1546 msiobj_release(&dbview->hdr);
1547 return r;
1550 static UINT get_table_labels(MSIDATABASE *db, const WCHAR *table, WCHAR ***labels, DWORD *numlabels)
1552 UINT r, i, count;
1553 MSIRECORD *prec = NULL;
1555 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1556 if (r != ERROR_SUCCESS)
1557 return r;
1559 count = MSI_RecordGetFieldCount(prec);
1560 *numlabels = count + 1;
1561 *labels = malloc((*numlabels) * sizeof(WCHAR *));
1562 if (!*labels)
1564 r = ERROR_OUTOFMEMORY;
1565 goto end;
1568 (*labels)[0] = wcsdup(table);
1569 for (i=1; i<=count; i++ )
1571 (*labels)[i] = wcsdup(MSI_RecordGetString(prec, i));
1574 end:
1575 msiobj_release( &prec->hdr );
1576 return r;
1579 static UINT get_query_columns(MSIQUERY *query, WCHAR ***columns, DWORD *numcolumns)
1581 UINT r, i, count;
1582 MSIRECORD *prec = NULL;
1584 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1585 if (r != ERROR_SUCCESS)
1586 return r;
1588 count = MSI_RecordGetFieldCount(prec);
1589 *columns = malloc(count * sizeof(WCHAR *));
1590 if (!*columns)
1592 r = ERROR_OUTOFMEMORY;
1593 goto end;
1596 for (i=1; i<=count; i++ )
1598 (*columns)[i-1] = wcsdup(MSI_RecordGetString(prec, i));
1601 *numcolumns = count;
1603 end:
1604 msiobj_release( &prec->hdr );
1605 return r;
1608 static UINT get_query_types(MSIQUERY *query, WCHAR ***types, DWORD *numtypes)
1610 UINT r, i, count;
1611 MSIRECORD *prec = NULL;
1613 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1614 if (r != ERROR_SUCCESS)
1615 return r;
1617 count = MSI_RecordGetFieldCount(prec);
1618 *types = malloc(count * sizeof(WCHAR *));
1619 if (!*types)
1621 r = ERROR_OUTOFMEMORY;
1622 goto end;
1625 *numtypes = count;
1626 for (i=1; i<=count; i++ )
1628 (*types)[i-1] = wcsdup(MSI_RecordGetString(prec, i));
1631 end:
1632 msiobj_release( &prec->hdr );
1633 return r;
1636 static void merge_free_rows(struct merge_table *table)
1638 struct list *item, *cursor;
1640 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1642 struct merge_row *row = LIST_ENTRY(item, struct merge_row, entry);
1644 list_remove(&row->entry);
1645 msiobj_release(&row->data->hdr);
1646 free(row);
1650 static void free_merge_table(struct merge_table *table)
1652 UINT i;
1654 if (table->labels != NULL)
1656 for (i = 0; i < table->numlabels; i++)
1657 free(table->labels[i]);
1659 free(table->labels);
1662 if (table->columns != NULL)
1664 for (i = 0; i < table->numcolumns; i++)
1665 free(table->columns[i]);
1667 free(table->columns);
1670 if (table->types != NULL)
1672 for (i = 0; i < table->numtypes; i++)
1673 free(table->types[i]);
1675 free(table->types);
1678 free(table->name);
1679 merge_free_rows(table);
1681 free(table);
1684 static UINT get_merge_table(MSIDATABASE *db, const WCHAR *name, struct merge_table **ptable)
1686 UINT r;
1687 struct merge_table *table;
1688 MSIQUERY *mergeview = NULL;
1690 table = calloc(1, sizeof(*table));
1691 if (!table)
1693 *ptable = NULL;
1694 return ERROR_OUTOFMEMORY;
1697 r = get_table_labels(db, name, &table->labels, &table->numlabels);
1698 if (r != ERROR_SUCCESS)
1699 goto err;
1701 r = MSI_OpenQuery(db, &mergeview, L"SELECT * FROM `%s`", name);
1702 if (r != ERROR_SUCCESS)
1703 goto err;
1705 r = get_query_columns(mergeview, &table->columns, &table->numcolumns);
1706 if (r != ERROR_SUCCESS)
1707 goto err;
1709 r = get_query_types(mergeview, &table->types, &table->numtypes);
1710 if (r != ERROR_SUCCESS)
1711 goto err;
1713 list_init(&table->rows);
1715 table->name = wcsdup(name);
1716 table->numconflicts = 0;
1718 msiobj_release(&mergeview->hdr);
1719 *ptable = table;
1720 return ERROR_SUCCESS;
1722 err:
1723 msiobj_release(&mergeview->hdr);
1724 free_merge_table(table);
1725 *ptable = NULL;
1726 return r;
1729 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1731 struct merge_data *data = param;
1732 struct merge_table *table;
1733 MSIQUERY *dbview = NULL;
1734 MSIQUERY *mergeview = NULL;
1735 LPCWSTR name;
1736 UINT r;
1738 name = MSI_RecordGetString(rec, 1);
1740 r = MSI_OpenQuery(data->merge, &mergeview, L"SELECT * FROM `%s`", name);
1741 if (r != ERROR_SUCCESS)
1742 goto done;
1744 if (TABLE_Exists(data->db, name))
1746 r = MSI_OpenQuery(data->db, &dbview, L"SELECT * FROM `%s`", name);
1747 if (r != ERROR_SUCCESS)
1748 goto done;
1750 r = merge_verify_colnames(dbview, mergeview);
1751 if (r != ERROR_SUCCESS)
1752 goto done;
1754 r = merge_verify_primary_keys(data->db, data->merge, name);
1755 if (r != ERROR_SUCCESS)
1756 goto done;
1759 r = get_merge_table(data->merge, name, &table);
1760 if (r != ERROR_SUCCESS)
1761 goto done;
1763 data->curtable = table;
1764 data->curview = mergeview;
1765 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1766 if (r != ERROR_SUCCESS)
1768 free_merge_table(table);
1769 goto done;
1772 list_add_tail(data->tabledata, &table->entry);
1774 done:
1775 msiobj_release(&dbview->hdr);
1776 msiobj_release(&mergeview->hdr);
1777 return r;
1780 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1781 struct list *tabledata)
1783 MSIQUERY *view;
1784 struct merge_data data;
1785 UINT r;
1787 r = MSI_DatabaseOpenViewW(merge, L"SELECT * FROM `_Tables`", &view);
1788 if (r != ERROR_SUCCESS)
1789 return r;
1791 data.db = db;
1792 data.merge = merge;
1793 data.tabledata = tabledata;
1794 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1795 msiobj_release(&view->hdr);
1796 return r;
1799 static UINT merge_table(MSIDATABASE *db, struct merge_table *table)
1801 UINT r;
1802 struct merge_row *row;
1803 MSIVIEW *tv;
1805 if (!TABLE_Exists(db, table->name))
1807 r = add_table_to_db(db, table->columns, table->types, table->labels, table->numlabels, table->numcolumns);
1808 if (r != ERROR_SUCCESS)
1809 return ERROR_FUNCTION_FAILED;
1812 LIST_FOR_EACH_ENTRY(row, &table->rows, struct merge_row, entry)
1814 r = TABLE_CreateView(db, table->name, &tv);
1815 if (r != ERROR_SUCCESS)
1816 return r;
1818 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1819 tv->ops->delete(tv);
1821 if (r != ERROR_SUCCESS)
1822 return r;
1825 return ERROR_SUCCESS;
1828 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1829 LPWSTR table, DWORD numconflicts)
1831 UINT r;
1832 MSIQUERY *view;
1834 if (!TABLE_Exists(db, error))
1836 r = MSI_OpenQuery(db, &view, L"CREATE TABLE `%s` (`Table` CHAR(255) NOT NULL, `NumRowMergeConflicts` SHORT "
1837 "NOT NULL PRIMARY KEY `Table`)" , error);
1838 if (r != ERROR_SUCCESS)
1839 return r;
1841 r = MSI_ViewExecute(view, NULL);
1842 msiobj_release(&view->hdr);
1843 if (r != ERROR_SUCCESS)
1844 return r;
1847 r = MSI_OpenQuery(db, &view, L"INSERT INTO `%s` (`Table`, `NumRowMergeConflicts`) VALUES ('%s', %d)", error,
1848 table, numconflicts);
1849 if (r != ERROR_SUCCESS)
1850 return r;
1852 r = MSI_ViewExecute(view, NULL);
1853 msiobj_release(&view->hdr);
1854 return r;
1857 UINT WINAPI MsiDatabaseMergeW( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const WCHAR *szTableName )
1859 struct list tabledata = LIST_INIT(tabledata);
1860 struct list *item, *cursor;
1861 MSIDATABASE *db, *merge;
1862 struct merge_table *table;
1863 BOOL conflicts;
1864 UINT r;
1866 TRACE( "%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_w(szTableName) );
1868 if (szTableName && !*szTableName)
1869 return ERROR_INVALID_TABLE;
1871 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1872 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1873 if (!db || !merge)
1875 r = ERROR_INVALID_HANDLE;
1876 goto done;
1879 r = gather_merge_data(db, merge, &tabledata);
1880 if (r != ERROR_SUCCESS)
1881 goto done;
1883 conflicts = FALSE;
1884 LIST_FOR_EACH_ENTRY(table, &tabledata, struct merge_table, entry)
1886 if (table->numconflicts)
1888 conflicts = TRUE;
1890 r = update_merge_errors(db, szTableName, table->name,
1891 table->numconflicts);
1892 if (r != ERROR_SUCCESS)
1893 break;
1895 else
1897 r = merge_table(db, table);
1898 if (r != ERROR_SUCCESS)
1899 break;
1903 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1905 struct merge_table *table = LIST_ENTRY(item, struct merge_table, entry);
1906 list_remove(&table->entry);
1907 free_merge_table(table);
1910 if (conflicts)
1911 r = ERROR_FUNCTION_FAILED;
1913 done:
1914 msiobj_release(&db->hdr);
1915 msiobj_release(&merge->hdr);
1916 return r;
1919 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1921 MSIDBSTATE ret = MSIDBSTATE_READ;
1922 MSIDATABASE *db;
1924 TRACE( "%lu\n", handle );
1926 if (!(db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE )))
1927 return MSIDBSTATE_ERROR;
1929 if (db->mode != MSI_OPEN_READONLY )
1930 ret = MSIDBSTATE_WRITE;
1931 msiobj_release( &db->hdr );
1933 return ret;
1936 MSICONDITION __cdecl s_remote_DatabaseIsTablePersistent(MSIHANDLE db, LPCWSTR table)
1938 return MsiDatabaseIsTablePersistentW(db, table);
1941 UINT __cdecl s_remote_DatabaseGetPrimaryKeys(MSIHANDLE db, LPCWSTR table, struct wire_record **rec)
1943 MSIHANDLE handle;
1944 UINT r = MsiDatabaseGetPrimaryKeysW(db, table, &handle);
1945 *rec = NULL;
1946 if (!r)
1947 *rec = marshal_record(handle);
1948 MsiCloseHandle(handle);
1949 return r;
1952 UINT __cdecl s_remote_DatabaseGetSummaryInformation(MSIHANDLE db, UINT updatecount, MSIHANDLE *suminfo)
1954 return MsiGetSummaryInformationW(db, NULL, updatecount, suminfo);
1957 UINT __cdecl s_remote_DatabaseOpenView(MSIHANDLE db, LPCWSTR query, MSIHANDLE *view)
1959 return MsiDatabaseOpenViewW(db, query, view);