gdiplus: Add test for GdipGetImageThumbnail.
[wine/hacks.git] / dlls / msi / database.c
blob3e57792634020b52ebc98bbaa274624df638530d
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>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32 #include "msi.h"
33 #include "msiquery.h"
34 #include "msipriv.h"
35 #include "objidl.h"
36 #include "objbase.h"
37 #include "msiserver.h"
38 #include "query.h"
40 #include "initguid.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
45 * .MSI file format
47 * An .msi file is a structured storage file.
48 * It contains a number of streams.
49 * A stream for each table in the database.
50 * Two streams for the string table in the database.
51 * Any binary data in a table is a reference to a stream.
54 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
56 typedef struct tagMSITRANSFORM {
57 struct list entry;
58 IStorage *stg;
59 } MSITRANSFORM;
61 typedef struct tagMSISTREAM {
62 struct list entry;
63 IStream *stm;
64 } MSISTREAM;
66 static UINT find_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
68 MSISTREAM *stream;
70 LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
72 HRESULT r;
73 STATSTG stat;
75 r = IStream_Stat( stream->stm, &stat, 0 );
76 if( FAILED( r ) )
78 WARN("failed to stat stream r = %08x!\n", r);
79 continue;
82 if( !lstrcmpW( name, stat.pwcsName ) )
84 TRACE("found %s\n", debugstr_w(name));
85 *stm = stream->stm;
86 CoTaskMemFree( stat.pwcsName );
87 return ERROR_SUCCESS;
90 CoTaskMemFree( stat.pwcsName );
93 return ERROR_FUNCTION_FAILED;
96 static UINT clone_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
98 IStream *stream;
100 if (find_open_stream( db, name, &stream ) == ERROR_SUCCESS)
102 HRESULT r;
103 LARGE_INTEGER pos;
105 r = IStream_Clone( stream, stm );
106 if( FAILED( r ) )
108 WARN("failed to clone stream r = %08x!\n", r);
109 return ERROR_FUNCTION_FAILED;
112 pos.QuadPart = 0;
113 r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
114 if( FAILED( r ) )
116 IStream_Release( *stm );
117 return ERROR_FUNCTION_FAILED;
120 return ERROR_SUCCESS;
123 return ERROR_FUNCTION_FAILED;
126 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
128 HRESULT r;
130 TRACE("%s\n", debugstr_w(stname));
132 if (clone_open_stream( db, stname, stm ) == ERROR_SUCCESS)
133 return ERROR_SUCCESS;
135 r = IStorage_OpenStream( db->storage, stname, NULL,
136 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
137 if( FAILED( r ) )
139 MSITRANSFORM *transform;
141 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
143 TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
144 r = IStorage_OpenStream( transform->stg, stname, NULL,
145 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
146 if (SUCCEEDED(r))
147 break;
151 if( SUCCEEDED(r) )
153 MSISTREAM *stream;
155 stream = msi_alloc( sizeof(MSISTREAM) );
156 if( !stream )
157 return ERROR_NOT_ENOUGH_MEMORY;
159 stream->stm = *stm;
160 IStream_AddRef( *stm );
161 list_add_tail( &db->streams, &stream->entry );
164 return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
167 static void free_transforms( MSIDATABASE *db )
169 while( !list_empty( &db->transforms ) )
171 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
172 MSITRANSFORM, entry );
173 list_remove( &t->entry );
174 IStorage_Release( t->stg );
175 msi_free( t );
179 void db_destroy_stream( MSIDATABASE *db, LPCWSTR stname )
181 MSISTREAM *stream, *stream2;
183 LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry )
185 HRESULT r;
186 STATSTG stat;
188 r = IStream_Stat( stream->stm, &stat, 0 );
189 if (FAILED(r))
191 WARN("failed to stat stream r = %08x\n", r);
192 continue;
195 if (!strcmpW( stname, stat.pwcsName ))
197 TRACE("destroying %s\n", debugstr_w(stname));
199 list_remove( &stream->entry );
200 IStream_Release( stream->stm );
201 msi_free( stream );
202 IStorage_DestroyElement( db->storage, stname );
203 CoTaskMemFree( stat.pwcsName );
204 break;
206 CoTaskMemFree( stat.pwcsName );
210 static void free_streams( MSIDATABASE *db )
212 while( !list_empty( &db->streams ) )
214 MSISTREAM *s = LIST_ENTRY( list_head( &db->streams ),
215 MSISTREAM, entry );
216 list_remove( &s->entry );
217 IStream_Release( s->stm );
218 msi_free( s );
222 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
224 MSITRANSFORM *t;
226 t = msi_alloc( sizeof *t );
227 t->stg = stg;
228 IStorage_AddRef( stg );
229 list_add_tail( &db->transforms, &t->entry );
231 /* the transform may add or replace streams */
232 free_streams( db );
235 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
237 MSIDATABASE *db = (MSIDATABASE *) arg;
239 msi_free(db->path);
240 free_cached_tables( db );
241 free_streams( db );
242 free_transforms( db );
243 msi_destroy_stringtable( db->strings );
244 IStorage_Release( db->storage );
245 if (db->deletefile)
247 DeleteFileW( db->deletefile );
248 msi_free( db->deletefile );
250 if (db->localfile)
252 DeleteFileW( db->localfile );
253 msi_free( db->localfile );
257 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
259 IStorage *stg = NULL;
260 HRESULT r;
261 MSIDATABASE *db = NULL;
262 UINT ret = ERROR_FUNCTION_FAILED;
263 LPCWSTR szMode, save_path;
264 STATSTG stat;
265 BOOL created = FALSE, patch = FALSE;
266 WCHAR path[MAX_PATH];
268 static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
270 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
272 if( !pdb )
273 return ERROR_INVALID_PARAMETER;
275 if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
276 szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
278 TRACE("Database is a patch\n");
279 szPersist -= MSIDBOPEN_PATCHFILE;
280 patch = TRUE;
283 save_path = szDBPath;
284 szMode = szPersist;
285 if( !IS_INTMSIDBOPEN(szPersist) )
287 if (!CopyFileW( szDBPath, szPersist, FALSE ))
288 return ERROR_OPEN_FAILED;
290 szDBPath = szPersist;
291 szPersist = MSIDBOPEN_TRANSACT;
292 created = TRUE;
295 if( szPersist == MSIDBOPEN_READONLY )
297 r = StgOpenStorage( szDBPath, NULL,
298 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
300 else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
302 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
303 * used here: */
304 r = StgCreateDocfile( szDBPath,
305 STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
306 if( r == ERROR_SUCCESS )
308 IStorage_SetClass( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
309 /* create the _Tables stream */
310 r = write_stream_data(stg, szTables, NULL, 0, TRUE);
311 if (SUCCEEDED(r))
312 r = msi_init_string_table( stg );
314 created = TRUE;
316 else if( szPersist == MSIDBOPEN_TRANSACT )
318 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
319 * used here: */
320 r = StgOpenStorage( szDBPath, NULL,
321 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
323 else if( szPersist == MSIDBOPEN_DIRECT )
325 r = StgOpenStorage( szDBPath, NULL,
326 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
328 else
330 ERR("unknown flag %p\n",szPersist);
331 return ERROR_INVALID_PARAMETER;
334 if( FAILED( r ) || !stg )
336 FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
337 return ERROR_FUNCTION_FAILED;
340 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
341 if( FAILED( r ) )
343 FIXME("Failed to stat storage\n");
344 goto end;
347 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
348 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
349 !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
351 ERR("storage GUID is not a MSI database GUID %s\n",
352 debugstr_guid(&stat.clsid) );
353 goto end;
356 if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
358 ERR("storage GUID is not the MSI patch GUID %s\n",
359 debugstr_guid(&stat.clsid) );
360 ret = ERROR_OPEN_FAILED;
361 goto end;
364 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
365 MSI_CloseDatabase );
366 if( !db )
368 FIXME("Failed to allocate a handle\n");
369 goto end;
372 if (!strchrW( save_path, '\\' ))
374 GetCurrentDirectoryW( MAX_PATH, path );
375 lstrcatW( path, szBackSlash );
376 lstrcatW( path, save_path );
378 else
379 lstrcpyW( path, save_path );
381 db->path = strdupW( path );
383 if( TRACE_ON( msi ) )
384 enum_stream_names( stg );
386 db->storage = stg;
387 db->mode = szMode;
388 if (created)
389 db->deletefile = strdupW( szDBPath );
390 list_init( &db->tables );
391 list_init( &db->transforms );
392 list_init( &db->streams );
394 db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
395 if( !db->strings )
396 goto end;
398 ret = ERROR_SUCCESS;
400 msiobj_addref( &db->hdr );
401 IStorage_AddRef( stg );
402 *pdb = db;
404 end:
405 if( db )
406 msiobj_release( &db->hdr );
407 if( stg )
408 IStorage_Release( stg );
410 return ret;
413 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
415 MSIDATABASE *db;
416 UINT ret;
418 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
420 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
421 if( ret == ERROR_SUCCESS )
423 *phDB = alloc_msihandle( &db->hdr );
424 if (! *phDB)
425 ret = ERROR_NOT_ENOUGH_MEMORY;
426 msiobj_release( &db->hdr );
429 return ret;
432 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
434 HRESULT r = ERROR_FUNCTION_FAILED;
435 LPWSTR szwDBPath = NULL, szwPersist = NULL;
437 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
439 if( szDBPath )
441 szwDBPath = strdupAtoW( szDBPath );
442 if( !szwDBPath )
443 goto end;
446 if( !IS_INTMSIDBOPEN(szPersist) )
448 szwPersist = strdupAtoW( szPersist );
449 if( !szwPersist )
450 goto end;
452 else
453 szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
455 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
457 end:
458 if( !IS_INTMSIDBOPEN(szPersist) )
459 msi_free( szwPersist );
460 msi_free( szwDBPath );
462 return r;
465 static LPWSTR msi_read_text_archive(LPCWSTR path)
467 HANDLE file;
468 LPSTR data = NULL;
469 LPWSTR wdata = NULL;
470 DWORD read, size = 0;
472 file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
473 if (file == INVALID_HANDLE_VALUE)
474 return NULL;
476 size = GetFileSize( file, NULL );
477 data = msi_alloc( size + 1 );
478 if (!data)
479 goto done;
481 if (!ReadFile( file, data, size, &read, NULL ))
482 goto done;
484 data[size] = '\0';
485 wdata = strdupAtoW( data );
487 done:
488 CloseHandle( file );
489 msi_free( data );
490 return wdata;
493 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
495 LPWSTR ptr = *line, save;
496 DWORD i, count = 1;
498 *entries = NULL;
500 /* stay on this line */
501 while (*ptr && *ptr != '\n')
503 /* entries are separated by tabs */
504 if (*ptr == '\t')
505 count++;
507 ptr++;
510 *entries = msi_alloc(count * sizeof(LPWSTR));
511 if (!*entries)
512 return;
514 /* store pointers into the data */
515 for (i = 0, ptr = *line; i < count; i++)
517 while (*ptr && *ptr == '\r') ptr++;
518 save = ptr;
520 while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
522 /* NULL-separate the data */
523 if (*ptr == '\n' || *ptr == '\r')
525 while (*ptr == '\n' || *ptr == '\r')
526 *(ptr++) = '\0';
528 else if (*ptr)
529 *ptr++ = '\0';
531 (*entries)[i] = save;
534 /* move to the next line if there's more, else EOF */
535 *line = ptr;
537 if (num_entries)
538 *num_entries = count;
541 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
543 LPWSTR prelude;
544 DWORD size;
546 static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
548 size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
549 prelude = msi_alloc(size * sizeof(WCHAR));
550 if (!prelude)
551 return NULL;
553 sprintfW(prelude, create_fmt, table);
554 return prelude;
557 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
559 LPWSTR columns, p;
560 LPCWSTR type;
561 DWORD sql_size = 1, i, len;
562 WCHAR expanded[128], *ptr;
563 WCHAR size[10], comma[2], extra[30];
565 static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
566 static const WCHAR size_fmt[] = {'(','%','s',')',0};
567 static const WCHAR type_char[] = {'C','H','A','R',0};
568 static const WCHAR type_int[] = {'I','N','T',0};
569 static const WCHAR type_long[] = {'L','O','N','G',0};
570 static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
571 static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
572 static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
574 columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
575 if (!columns)
576 return NULL;
578 for (i = 0; i < num_columns; i++)
580 type = NULL;
581 comma[1] = size[0] = extra[0] = '\0';
583 if (i == num_columns - 1)
584 comma[0] = '\0';
585 else
586 comma[0] = ',';
588 ptr = &types[i][1];
589 len = atolW(ptr);
590 extra[0] = '\0';
592 switch (types[i][0])
594 case 'l':
595 lstrcpyW(extra, type_notnull);
596 case 'L':
597 lstrcatW(extra, localizable);
598 type = type_char;
599 sprintfW(size, size_fmt, ptr);
600 break;
601 case 's':
602 lstrcpyW(extra, type_notnull);
603 case 'S':
604 type = type_char;
605 sprintfW(size, size_fmt, ptr);
606 break;
607 case 'i':
608 lstrcpyW(extra, type_notnull);
609 case 'I':
610 if (len <= 2)
611 type = type_int;
612 else if (len == 4)
613 type = type_long;
614 else
616 WARN("invalid int width %u\n", len);
617 msi_free(columns);
618 return NULL;
620 break;
621 case 'v':
622 lstrcpyW(extra, type_notnull);
623 case 'V':
624 type = type_object;
625 break;
626 default:
627 ERR("Unknown type: %c\n", types[i][0]);
628 msi_free(columns);
629 return NULL;
632 sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
633 sql_size += lstrlenW(expanded);
635 p = msi_realloc(columns, sql_size * sizeof(WCHAR));
636 if (!p)
638 msi_free(columns);
639 return NULL;
641 columns = p;
643 lstrcatW(columns, expanded);
646 return columns;
649 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
651 LPWSTR postlude, keys, ptr;
652 DWORD size, key_size, i;
654 static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
655 static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
657 for (i = 0, size = 1; i < num_keys; i++)
658 size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
660 keys = msi_alloc(size * sizeof(WCHAR));
661 if (!keys)
662 return NULL;
664 for (i = 0, ptr = keys; i < num_keys; i++)
666 key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
667 sprintfW(ptr, key_fmt, primary_keys[i]);
668 ptr += key_size;
671 /* remove final ', ' */
672 *(ptr - 2) = '\0';
674 size = lstrlenW(postlude_fmt) + size - 1;
675 postlude = msi_alloc(size * sizeof(WCHAR));
676 if (!postlude)
677 goto done;
679 sprintfW(postlude, postlude_fmt, keys);
681 done:
682 msi_free(keys);
683 return postlude;
686 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
688 UINT r = ERROR_OUTOFMEMORY;
689 DWORD size;
690 MSIQUERY *view;
691 LPWSTR create_sql = NULL;
692 LPWSTR prelude, columns_sql, postlude;
694 prelude = msi_build_createsql_prelude(labels[0]);
695 columns_sql = msi_build_createsql_columns(columns, types, num_columns);
696 postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
698 if (!prelude || !columns_sql || !postlude)
699 goto done;
701 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
702 create_sql = msi_alloc(size * sizeof(WCHAR));
703 if (!create_sql)
704 goto done;
706 lstrcpyW(create_sql, prelude);
707 lstrcatW(create_sql, columns_sql);
708 lstrcatW(create_sql, postlude);
710 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
711 if (r != ERROR_SUCCESS)
712 goto done;
714 r = MSI_ViewExecute(view, NULL);
715 MSI_ViewClose(view);
716 msiobj_release(&view->hdr);
718 done:
719 msi_free(prelude);
720 msi_free(columns_sql);
721 msi_free(postlude);
722 msi_free(create_sql);
723 return r;
726 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
728 DWORD len;
729 LPWSTR fullname, ptr;
731 len = lstrlenW(path) + lstrlenW(name) + 1;
732 fullname = msi_alloc(len*sizeof(WCHAR));
733 if (!fullname)
734 return NULL;
736 lstrcpyW( fullname, path );
738 /* chop off extension from path */
739 ptr = strrchrW(fullname, '.');
740 if (!ptr)
742 msi_free (fullname);
743 return NULL;
745 *ptr++ = '\\';
746 lstrcpyW( ptr, name );
747 return fullname;
750 static UINT construct_record(DWORD num_columns, LPWSTR *types,
751 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
753 UINT i;
755 *rec = MSI_CreateRecord(num_columns);
756 if (!*rec)
757 return ERROR_OUTOFMEMORY;
759 for (i = 0; i < num_columns; i++)
761 switch (types[i][0])
763 case 'L': case 'l': case 'S': case 's':
764 MSI_RecordSetStringW(*rec, i + 1, data[i]);
765 break;
766 case 'I': case 'i':
767 if (*data[i])
768 MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
769 break;
770 case 'V': case 'v':
771 if (*data[i])
773 UINT r;
774 LPWSTR file = msi_import_stream_filename(path, data[i]);
775 if (!file)
776 return ERROR_FUNCTION_FAILED;
778 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
779 msi_free (file);
780 if (r != ERROR_SUCCESS)
781 return ERROR_FUNCTION_FAILED;
783 break;
784 default:
785 ERR("Unhandled column type: %c\n", types[i][0]);
786 msiobj_release(&(*rec)->hdr);
787 return ERROR_FUNCTION_FAILED;
791 return ERROR_SUCCESS;
794 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
795 LPWSTR *labels, LPWSTR **records,
796 int num_columns, int num_records,
797 LPWSTR path)
799 UINT r;
800 int i;
801 MSIQUERY *view;
802 MSIRECORD *rec;
804 static const WCHAR select[] = {
805 'S','E','L','E','C','T',' ','*',' ',
806 'F','R','O','M',' ','`','%','s','`',0
809 r = MSI_OpenQuery(db, &view, select, labels[0]);
810 if (r != ERROR_SUCCESS)
811 return r;
813 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
815 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
816 msiobj_release(&rec->hdr);
817 if (r != ERROR_SUCCESS)
818 goto done;
821 for (i = 0; i < num_records; i++)
823 r = construct_record(num_columns, types, records[i], path, &rec);
824 if (r != ERROR_SUCCESS)
825 goto done;
827 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
828 if (r != ERROR_SUCCESS)
830 msiobj_release(&rec->hdr);
831 goto done;
834 msiobj_release(&rec->hdr);
837 done:
838 msiobj_release(&view->hdr);
839 return r;
842 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
844 UINT r;
845 DWORD len, i;
846 DWORD num_labels, num_types;
847 DWORD num_columns, num_records = 0;
848 LPWSTR *columns, *types, *labels;
849 LPWSTR path, ptr, data;
850 LPWSTR **records = NULL;
851 LPWSTR **temp_records;
853 static const WCHAR suminfo[] =
854 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
856 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
858 if( folder == NULL || file == NULL )
859 return ERROR_INVALID_PARAMETER;
861 len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
862 path = msi_alloc( len * sizeof(WCHAR) );
863 if (!path)
864 return ERROR_OUTOFMEMORY;
866 lstrcpyW( path, folder );
867 lstrcatW( path, szBackSlash );
868 lstrcatW( path, file );
870 data = msi_read_text_archive( path );
872 ptr = data;
873 msi_parse_line( &ptr, &columns, &num_columns );
874 msi_parse_line( &ptr, &types, &num_types );
875 msi_parse_line( &ptr, &labels, &num_labels );
877 if (num_columns != num_types)
879 r = ERROR_FUNCTION_FAILED;
880 goto done;
883 records = msi_alloc(sizeof(LPWSTR *));
884 if (!records)
886 r = ERROR_OUTOFMEMORY;
887 goto done;
890 /* read in the table records */
891 while (*ptr)
893 msi_parse_line( &ptr, &records[num_records], NULL );
895 num_records++;
896 temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
897 if (!temp_records)
899 r = ERROR_OUTOFMEMORY;
900 goto done;
902 records = temp_records;
905 if (!strcmpW(labels[0], suminfo))
907 r = msi_add_suminfo( db, records, num_records, num_columns );
908 if (r != ERROR_SUCCESS)
910 r = ERROR_FUNCTION_FAILED;
911 goto done;
914 else
916 if (!TABLE_Exists(db, labels[0]))
918 r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
919 if (r != ERROR_SUCCESS)
921 r = ERROR_FUNCTION_FAILED;
922 goto done;
926 r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
929 done:
930 msi_free(path);
931 msi_free(data);
932 msi_free(columns);
933 msi_free(types);
934 msi_free(labels);
936 for (i = 0; i < num_records; i++)
937 msi_free(records[i]);
939 msi_free(records);
941 return r;
944 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
946 MSIDATABASE *db;
947 UINT r;
949 TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
951 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
952 if( !db )
954 IWineMsiRemoteDatabase *remote_database;
956 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
957 if ( !remote_database )
958 return ERROR_INVALID_HANDLE;
960 IWineMsiRemoteDatabase_Release( remote_database );
961 WARN("MsiDatabaseImport not allowed during a custom action!\n");
963 return ERROR_SUCCESS;
966 r = MSI_DatabaseImport( db, szFolder, szFilename );
967 msiobj_release( &db->hdr );
968 return r;
971 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
972 LPCSTR szFolder, LPCSTR szFilename )
974 LPWSTR path = NULL, file = NULL;
975 UINT r = ERROR_OUTOFMEMORY;
977 TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
979 if( szFolder )
981 path = strdupAtoW( szFolder );
982 if( !path )
983 goto end;
986 if( szFilename )
988 file = strdupAtoW( szFilename );
989 if( !file )
990 goto end;
993 r = MsiDatabaseImportW( handle, path, file );
995 end:
996 msi_free( path );
997 msi_free( file );
999 return r;
1002 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1004 UINT i, count, len, r = ERROR_SUCCESS;
1005 const char *sep;
1006 char *buffer;
1007 DWORD sz;
1009 len = 0x100;
1010 buffer = msi_alloc( len );
1011 if ( !buffer )
1012 return ERROR_OUTOFMEMORY;
1014 count = MSI_RecordGetFieldCount( row );
1015 for ( i=start; i<=count; i++ )
1017 sz = len;
1018 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1019 if (r == ERROR_MORE_DATA)
1021 char *p = msi_realloc( buffer, sz + 1 );
1022 if (!p)
1023 break;
1024 len = sz + 1;
1025 buffer = p;
1027 sz = len;
1028 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1029 if (r != ERROR_SUCCESS)
1030 break;
1032 if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1034 r = ERROR_FUNCTION_FAILED;
1035 break;
1038 sep = (i < count) ? "\t" : "\r\n";
1039 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1041 r = ERROR_FUNCTION_FAILED;
1042 break;
1045 msi_free( buffer );
1046 return r;
1049 static UINT msi_export_row( MSIRECORD *row, void *arg )
1051 return msi_export_record( arg, row, 1 );
1054 static UINT msi_export_forcecodepage( HANDLE handle )
1056 DWORD sz;
1058 static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
1060 FIXME("Read the codepage from the strings table!\n");
1062 sz = lstrlenA(data) + 1;
1063 if (!WriteFile(handle, data, sz, &sz, NULL))
1064 return ERROR_FUNCTION_FAILED;
1066 return ERROR_SUCCESS;
1069 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1070 LPCWSTR folder, LPCWSTR file )
1072 static const WCHAR query[] = {
1073 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1074 static const WCHAR forcecodepage[] = {
1075 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1076 MSIRECORD *rec = NULL;
1077 MSIQUERY *view = NULL;
1078 LPWSTR filename;
1079 HANDLE handle;
1080 UINT len, r;
1082 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1083 debugstr_w(folder), debugstr_w(file) );
1085 if( folder == NULL || file == NULL )
1086 return ERROR_INVALID_PARAMETER;
1088 len = lstrlenW(folder) + lstrlenW(file) + 2;
1089 filename = msi_alloc(len * sizeof (WCHAR));
1090 if (!filename)
1091 return ERROR_OUTOFMEMORY;
1093 lstrcpyW( filename, folder );
1094 lstrcatW( filename, szBackSlash );
1095 lstrcatW( filename, file );
1097 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1098 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1099 msi_free( filename );
1100 if (handle == INVALID_HANDLE_VALUE)
1101 return ERROR_FUNCTION_FAILED;
1103 if (!lstrcmpW( table, forcecodepage ))
1105 r = msi_export_forcecodepage( handle );
1106 goto done;
1109 r = MSI_OpenQuery( db, &view, query, table );
1110 if (r == ERROR_SUCCESS)
1112 /* write out row 1, the column names */
1113 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1114 if (r == ERROR_SUCCESS)
1116 msi_export_record( handle, rec, 1 );
1117 msiobj_release( &rec->hdr );
1120 /* write out row 2, the column types */
1121 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1122 if (r == ERROR_SUCCESS)
1124 msi_export_record( handle, rec, 1 );
1125 msiobj_release( &rec->hdr );
1128 /* write out row 3, the table name + keys */
1129 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1130 if (r == ERROR_SUCCESS)
1132 MSI_RecordSetStringW( rec, 0, table );
1133 msi_export_record( handle, rec, 0 );
1134 msiobj_release( &rec->hdr );
1137 /* write out row 4 onwards, the data */
1138 r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1139 msiobj_release( &view->hdr );
1142 done:
1143 CloseHandle( handle );
1144 return r;
1147 /***********************************************************************
1148 * MsiExportDatabaseW [MSI.@]
1150 * Writes a file containing the table data as tab separated ASCII.
1152 * The format is as follows:
1154 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1155 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1156 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1158 * Followed by the data, starting at row 1 with one row per line
1160 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1162 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1163 LPCWSTR szFolder, LPCWSTR szFilename )
1165 MSIDATABASE *db;
1166 UINT r;
1168 TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1169 debugstr_w(szFolder), debugstr_w(szFilename));
1171 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1172 if( !db )
1174 IWineMsiRemoteDatabase *remote_database;
1176 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1177 if ( !remote_database )
1178 return ERROR_INVALID_HANDLE;
1180 IWineMsiRemoteDatabase_Release( remote_database );
1181 WARN("MsiDatabaseExport not allowed during a custom action!\n");
1183 return ERROR_SUCCESS;
1186 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1187 msiobj_release( &db->hdr );
1188 return r;
1191 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1192 LPCSTR szFolder, LPCSTR szFilename )
1194 LPWSTR path = NULL, file = NULL, table = NULL;
1195 UINT r = ERROR_OUTOFMEMORY;
1197 TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1198 debugstr_a(szFolder), debugstr_a(szFilename));
1200 if( szTable )
1202 table = strdupAtoW( szTable );
1203 if( !table )
1204 goto end;
1207 if( szFolder )
1209 path = strdupAtoW( szFolder );
1210 if( !path )
1211 goto end;
1214 if( szFilename )
1216 file = strdupAtoW( szFilename );
1217 if( !file )
1218 goto end;
1221 r = MsiDatabaseExportW( handle, table, path, file );
1223 end:
1224 msi_free( table );
1225 msi_free( path );
1226 msi_free( file );
1228 return r;
1231 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1232 LPCSTR szTableName)
1234 UINT r;
1235 LPWSTR table;
1237 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1238 debugstr_a(szTableName));
1240 table = strdupAtoW(szTableName);
1241 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1243 msi_free(table);
1244 return r;
1247 typedef struct _tagMERGETABLE
1249 struct list entry;
1250 struct list rows;
1251 LPWSTR name;
1252 DWORD numconflicts;
1253 LPWSTR *columns;
1254 DWORD numcolumns;
1255 LPWSTR *types;
1256 DWORD numtypes;
1257 LPWSTR *labels;
1258 DWORD numlabels;
1259 } MERGETABLE;
1261 typedef struct _tagMERGEROW
1263 struct list entry;
1264 MSIRECORD *data;
1265 } MERGEROW;
1267 typedef struct _tagMERGEDATA
1269 MSIDATABASE *db;
1270 MSIDATABASE *merge;
1271 MERGETABLE *curtable;
1272 MSIQUERY *curview;
1273 struct list *tabledata;
1274 } MERGEDATA;
1276 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1278 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1279 ((type2[0] == 'l') || (type2[0] == 's')))
1280 return TRUE;
1282 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1283 ((type2[0] == 'L') || (type2[0] == 'S')))
1284 return TRUE;
1286 return !lstrcmpW(type1, type2);
1289 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1291 MSIRECORD *dbrec, *mergerec;
1292 UINT r, i, count;
1294 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1295 if (r != ERROR_SUCCESS)
1296 return r;
1298 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1299 if (r != ERROR_SUCCESS)
1300 return r;
1302 count = MSI_RecordGetFieldCount(dbrec);
1303 for (i = 1; i <= count; i++)
1305 if (!MSI_RecordGetString(mergerec, i))
1306 break;
1308 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1309 MSI_RecordGetString(mergerec, i)))
1311 r = ERROR_DATATYPE_MISMATCH;
1312 goto done;
1316 msiobj_release(&dbrec->hdr);
1317 msiobj_release(&mergerec->hdr);
1318 dbrec = mergerec = NULL;
1320 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1321 if (r != ERROR_SUCCESS)
1322 return r;
1324 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1325 if (r != ERROR_SUCCESS)
1326 return r;
1328 count = MSI_RecordGetFieldCount(dbrec);
1329 for (i = 1; i <= count; i++)
1331 if (!MSI_RecordGetString(mergerec, i))
1332 break;
1334 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1335 MSI_RecordGetString(mergerec, i)))
1337 r = ERROR_DATATYPE_MISMATCH;
1338 break;
1342 done:
1343 msiobj_release(&dbrec->hdr);
1344 msiobj_release(&mergerec->hdr);
1346 return r;
1349 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1350 LPCWSTR table)
1352 MSIRECORD *dbrec, *mergerec = NULL;
1353 UINT r, i, count;
1355 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1356 if (r != ERROR_SUCCESS)
1357 return r;
1359 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1360 if (r != ERROR_SUCCESS)
1361 goto done;
1363 count = MSI_RecordGetFieldCount(dbrec);
1364 if (count != MSI_RecordGetFieldCount(mergerec))
1366 r = ERROR_DATATYPE_MISMATCH;
1367 goto done;
1370 for (i = 1; i <= count; i++)
1372 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1373 MSI_RecordGetString(mergerec, i)))
1375 r = ERROR_DATATYPE_MISMATCH;
1376 goto done;
1380 done:
1381 msiobj_release(&dbrec->hdr);
1382 msiobj_release(&mergerec->hdr);
1384 return r;
1387 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1389 MSIRECORD *colnames;
1390 LPWSTR str, val;
1391 UINT r, i = 0, sz = 0;
1392 int cmp;
1394 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1395 if (r != ERROR_SUCCESS)
1396 return NULL;
1400 str = msi_dup_record_field(colnames, ++i);
1401 cmp = lstrcmpW(key, str);
1402 msi_free(str);
1403 } while (cmp);
1405 msiobj_release(&colnames->hdr);
1407 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1408 if (r != ERROR_SUCCESS)
1409 return NULL;
1410 sz++;
1412 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1414 /* quote string record fields */
1415 const WCHAR szQuote[] = {'\'', 0};
1416 sz += 2;
1417 val = msi_alloc(sz*sizeof(WCHAR));
1418 if (!val)
1419 return NULL;
1421 lstrcpyW(val, szQuote);
1422 r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1423 lstrcpyW(val+1+sz, szQuote);
1425 else
1427 /* do not quote integer record fields */
1428 val = msi_alloc(sz*sizeof(WCHAR));
1429 if (!val)
1430 return NULL;
1432 r = MSI_RecordGetStringW(rec, i, val, &sz);
1435 if (r != ERROR_SUCCESS)
1437 ERR("failed to get string!\n");
1438 msi_free(val);
1439 return NULL;
1442 return val;
1445 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1446 LPWSTR table, MSIRECORD *rec)
1448 LPWSTR query = NULL, clause = NULL;
1449 LPWSTR ptr = NULL, val;
1450 LPCWSTR setptr;
1451 DWORD size = 1, oldsize;
1452 LPCWSTR key;
1453 MSIRECORD *keys;
1454 UINT r, i, count;
1456 static const WCHAR keyset[] = {
1457 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1458 static const WCHAR lastkeyset[] = {
1459 '`','%','s','`',' ','=',' ','%','s',' ',0};
1460 static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1461 'F','R','O','M',' ','`','%','s','`',' ',
1462 'W','H','E','R','E',' ','%','s',0};
1464 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1465 if (r != ERROR_SUCCESS)
1466 return NULL;
1468 clause = msi_alloc_zero(size * sizeof(WCHAR));
1469 if (!clause)
1470 goto done;
1472 ptr = clause;
1473 count = MSI_RecordGetFieldCount(keys);
1474 for (i = 1; i <= count; i++)
1476 key = MSI_RecordGetString(keys, i);
1477 val = get_key_value(view, key, rec);
1479 if (i == count)
1480 setptr = lastkeyset;
1481 else
1482 setptr = keyset;
1484 oldsize = size;
1485 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1486 clause = msi_realloc(clause, size * sizeof (WCHAR));
1487 if (!clause)
1489 msi_free(val);
1490 goto done;
1493 ptr = clause + oldsize - 1;
1494 sprintfW(ptr, setptr, key, val);
1495 msi_free(val);
1498 size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1499 query = msi_alloc(size * sizeof(WCHAR));
1500 if (!query)
1501 goto done;
1503 sprintfW(query, fmt, table, clause);
1505 done:
1506 msi_free(clause);
1507 msiobj_release(&keys->hdr);
1508 return query;
1511 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1513 MERGEDATA *data = param;
1514 MERGETABLE *table = data->curtable;
1515 MERGEROW *mergerow;
1516 MSIQUERY *dbview = NULL;
1517 MSIRECORD *row = NULL;
1518 LPWSTR query = NULL;
1519 UINT r = ERROR_SUCCESS;
1521 if (TABLE_Exists(data->db, table->name))
1523 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1524 if (!query)
1525 return ERROR_OUTOFMEMORY;
1527 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1528 if (r != ERROR_SUCCESS)
1529 goto done;
1531 r = MSI_ViewExecute(dbview, NULL);
1532 if (r != ERROR_SUCCESS)
1533 goto done;
1535 r = MSI_ViewFetch(dbview, &row);
1536 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1538 table->numconflicts++;
1539 goto done;
1541 else if (r != ERROR_NO_MORE_ITEMS)
1542 goto done;
1544 r = ERROR_SUCCESS;
1547 mergerow = msi_alloc(sizeof(MERGEROW));
1548 if (!mergerow)
1550 r = ERROR_OUTOFMEMORY;
1551 goto done;
1554 mergerow->data = MSI_CloneRecord(rec);
1555 if (!mergerow->data)
1557 r = ERROR_OUTOFMEMORY;
1558 msi_free(mergerow);
1559 goto done;
1562 list_add_tail(&table->rows, &mergerow->entry);
1564 done:
1565 msi_free(query);
1566 msiobj_release(&row->hdr);
1567 msiobj_release(&dbview->hdr);
1568 return r;
1571 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1573 UINT r, i, count;
1574 MSIRECORD *prec = NULL;
1576 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1577 if (r != ERROR_SUCCESS)
1578 return r;
1580 count = MSI_RecordGetFieldCount(prec);
1581 *numlabels = count + 1;
1582 *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1583 if (!*labels)
1585 r = ERROR_OUTOFMEMORY;
1586 goto end;
1589 (*labels)[0] = strdupW(table);
1590 for (i=1; i<=count; i++ )
1592 (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1595 end:
1596 msiobj_release( &prec->hdr );
1597 return r;
1600 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1602 UINT r, i, count;
1603 MSIRECORD *prec = NULL;
1605 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1606 if (r != ERROR_SUCCESS)
1607 return r;
1609 count = MSI_RecordGetFieldCount(prec);
1610 *columns = msi_alloc(count*sizeof(LPWSTR));
1611 if (!*columns)
1613 r = ERROR_OUTOFMEMORY;
1614 goto end;
1617 for (i=1; i<=count; i++ )
1619 (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1622 *numcolumns = count;
1624 end:
1625 msiobj_release( &prec->hdr );
1626 return r;
1629 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1631 UINT r, i, count;
1632 MSIRECORD *prec = NULL;
1634 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1635 if (r != ERROR_SUCCESS)
1636 return r;
1638 count = MSI_RecordGetFieldCount(prec);
1639 *types = msi_alloc(count*sizeof(LPWSTR));
1640 if (!*types)
1642 r = ERROR_OUTOFMEMORY;
1643 goto end;
1646 *numtypes = count;
1647 for (i=1; i<=count; i++ )
1649 (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1652 end:
1653 msiobj_release( &prec->hdr );
1654 return r;
1657 static void merge_free_rows(MERGETABLE *table)
1659 struct list *item, *cursor;
1661 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1663 MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1665 list_remove(&row->entry);
1666 msiobj_release(&row->data->hdr);
1667 msi_free(row);
1671 static void free_merge_table(MERGETABLE *table)
1673 UINT i;
1675 if (table->labels != NULL)
1677 for (i = 0; i < table->numlabels; i++)
1678 msi_free(table->labels[i]);
1680 msi_free(table->labels);
1683 if (table->columns != NULL)
1685 for (i = 0; i < table->numcolumns; i++)
1686 msi_free(table->columns[i]);
1688 msi_free(table->columns);
1691 if (table->types != NULL)
1693 for (i = 0; i < table->numtypes; i++)
1694 msi_free(table->types[i]);
1696 msi_free(table->types);
1699 msi_free(table->name);
1700 merge_free_rows(table);
1702 msi_free(table);
1705 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1707 UINT r;
1708 MERGETABLE *table;
1709 MSIQUERY *mergeview = NULL;
1711 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1712 'F','R','O','M',' ','`','%','s','`',0};
1714 table = msi_alloc_zero(sizeof(MERGETABLE));
1715 if (!table)
1717 *ptable = NULL;
1718 return ERROR_OUTOFMEMORY;
1721 r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1722 if (r != ERROR_SUCCESS)
1723 goto err;
1725 r = MSI_OpenQuery(db, &mergeview, query, name);
1726 if (r != ERROR_SUCCESS)
1727 goto err;
1729 r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1730 if (r != ERROR_SUCCESS)
1731 goto err;
1733 r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1734 if (r != ERROR_SUCCESS)
1735 goto err;
1737 list_init(&table->rows);
1739 table->name = strdupW(name);
1740 table->numconflicts = 0;
1742 msiobj_release(&mergeview->hdr);
1743 *ptable = table;
1744 return ERROR_SUCCESS;
1746 err:
1747 msiobj_release(&mergeview->hdr);
1748 free_merge_table(table);
1749 *ptable = NULL;
1750 return r;
1753 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1755 MERGEDATA *data = param;
1756 MERGETABLE *table;
1757 MSIQUERY *dbview = NULL;
1758 MSIQUERY *mergeview = NULL;
1759 LPCWSTR name;
1760 UINT r;
1762 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1763 'F','R','O','M',' ','`','%','s','`',0};
1765 name = MSI_RecordGetString(rec, 1);
1767 r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1768 if (r != ERROR_SUCCESS)
1769 goto done;
1771 if (TABLE_Exists(data->db, name))
1773 r = MSI_OpenQuery(data->db, &dbview, query, name);
1774 if (r != ERROR_SUCCESS)
1775 goto done;
1777 r = merge_verify_colnames(dbview, mergeview);
1778 if (r != ERROR_SUCCESS)
1779 goto done;
1781 r = merge_verify_primary_keys(data->db, data->merge, name);
1782 if (r != ERROR_SUCCESS)
1783 goto done;
1786 r = msi_get_merge_table(data->merge, name, &table);
1787 if (r != ERROR_SUCCESS)
1788 goto done;
1790 data->curtable = table;
1791 data->curview = mergeview;
1792 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1793 if (r != ERROR_SUCCESS)
1795 free_merge_table(table);
1796 goto done;
1799 list_add_tail(data->tabledata, &table->entry);
1801 done:
1802 msiobj_release(&dbview->hdr);
1803 msiobj_release(&mergeview->hdr);
1804 return r;
1807 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1808 struct list *tabledata)
1810 UINT r;
1811 MSIQUERY *view;
1812 MERGEDATA data;
1814 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1815 'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1817 r = MSI_DatabaseOpenViewW(merge, query, &view);
1818 if (r != ERROR_SUCCESS)
1819 return r;
1821 data.db = db;
1822 data.merge = merge;
1823 data.tabledata = tabledata;
1824 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1826 msiobj_release(&view->hdr);
1827 return r;
1830 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1832 UINT r;
1833 MERGEROW *row;
1834 MSIVIEW *tv;
1836 if (!TABLE_Exists(db, table->name))
1838 r = msi_add_table_to_db(db, table->columns, table->types,
1839 table->labels, table->numlabels, table->numcolumns);
1840 if (r != ERROR_SUCCESS)
1841 return ERROR_FUNCTION_FAILED;
1844 LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1846 r = TABLE_CreateView(db, table->name, &tv);
1847 if (r != ERROR_SUCCESS)
1848 return r;
1850 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1851 tv->ops->delete(tv);
1853 if (r != ERROR_SUCCESS)
1854 return r;
1857 return ERROR_SUCCESS;
1860 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1861 LPWSTR table, DWORD numconflicts)
1863 UINT r;
1864 MSIQUERY *view;
1866 static const WCHAR create[] = {
1867 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1868 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1869 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1870 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1871 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1872 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1873 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1874 static const WCHAR insert[] = {
1875 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1876 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1877 '`','N','u','m','R','o','w','M','e','r','g','e',
1878 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1879 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1881 if (!TABLE_Exists(db, error))
1883 r = MSI_OpenQuery(db, &view, create, error);
1884 if (r != ERROR_SUCCESS)
1885 return r;
1887 r = MSI_ViewExecute(view, NULL);
1888 msiobj_release(&view->hdr);
1889 if (r != ERROR_SUCCESS)
1890 return r;
1893 r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1894 if (r != ERROR_SUCCESS)
1895 return r;
1897 r = MSI_ViewExecute(view, NULL);
1898 msiobj_release(&view->hdr);
1899 return r;
1902 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1903 LPCWSTR szTableName)
1905 struct list tabledata = LIST_INIT(tabledata);
1906 struct list *item, *cursor;
1907 MSIDATABASE *db, *merge;
1908 MERGETABLE *table;
1909 BOOL conflicts;
1910 UINT r;
1912 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1913 debugstr_w(szTableName));
1915 if (szTableName && !*szTableName)
1916 return ERROR_INVALID_TABLE;
1918 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1919 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1920 if (!db || !merge)
1922 r = ERROR_INVALID_HANDLE;
1923 goto done;
1926 r = gather_merge_data(db, merge, &tabledata);
1927 if (r != ERROR_SUCCESS)
1928 goto done;
1930 conflicts = FALSE;
1931 LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1933 if (table->numconflicts)
1935 conflicts = TRUE;
1937 r = update_merge_errors(db, szTableName, table->name,
1938 table->numconflicts);
1939 if (r != ERROR_SUCCESS)
1940 break;
1942 else
1944 r = merge_table(db, table);
1945 if (r != ERROR_SUCCESS)
1946 break;
1950 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1952 MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1953 list_remove(&table->entry);
1954 free_merge_table(table);
1957 if (conflicts)
1958 r = ERROR_FUNCTION_FAILED;
1960 done:
1961 msiobj_release(&db->hdr);
1962 msiobj_release(&merge->hdr);
1963 return r;
1966 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1968 MSIDBSTATE ret = MSIDBSTATE_READ;
1969 MSIDATABASE *db;
1971 TRACE("%d\n", handle);
1973 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1974 if( !db )
1976 IWineMsiRemoteDatabase *remote_database;
1978 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1979 if ( !remote_database )
1980 return MSIDBSTATE_ERROR;
1982 IWineMsiRemoteDatabase_Release( remote_database );
1983 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1985 return MSIDBSTATE_READ;
1988 if (db->mode != MSIDBOPEN_READONLY )
1989 ret = MSIDBSTATE_WRITE;
1990 msiobj_release( &db->hdr );
1992 return ret;
1995 typedef struct _msi_remote_database_impl {
1996 const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1997 MSIHANDLE database;
1998 LONG refs;
1999 } msi_remote_database_impl;
2001 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
2003 return (msi_remote_database_impl *)iface;
2006 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2007 REFIID riid,LPVOID *ppobj)
2009 if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2010 IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2012 IUnknown_AddRef( iface );
2013 *ppobj = iface;
2014 return S_OK;
2017 return E_NOINTERFACE;
2020 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2022 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2024 return InterlockedIncrement( &This->refs );
2027 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2029 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2030 ULONG r;
2032 r = InterlockedDecrement( &This->refs );
2033 if (r == 0)
2035 MsiCloseHandle( This->database );
2036 msi_free( This );
2038 return r;
2041 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2042 LPCWSTR table, MSICONDITION *persistent )
2044 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2045 *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2046 return S_OK;
2049 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2050 LPCWSTR table, MSIHANDLE *keys )
2052 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2053 UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2054 return HRESULT_FROM_WIN32(r);
2057 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2058 UINT updatecount, MSIHANDLE *suminfo )
2060 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2061 UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2062 return HRESULT_FROM_WIN32(r);
2065 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2066 LPCWSTR query, MSIHANDLE *view )
2068 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2069 UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2070 return HRESULT_FROM_WIN32(r);
2073 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2075 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2076 This->database = handle;
2077 return S_OK;
2080 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2082 mrd_QueryInterface,
2083 mrd_AddRef,
2084 mrd_Release,
2085 mrd_IsTablePersistent,
2086 mrd_GetPrimaryKeys,
2087 mrd_GetSummaryInformation,
2088 mrd_OpenView,
2089 mrd_SetMsiHandle,
2092 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2094 msi_remote_database_impl *This;
2096 This = msi_alloc( sizeof *This );
2097 if (!This)
2098 return E_OUTOFMEMORY;
2100 This->lpVtbl = &msi_remote_database_vtbl;
2101 This->database = 0;
2102 This->refs = 1;
2104 *ppObj = This;
2106 return S_OK;