Release 1.1.37.
[wine/gsoc-2012-control.git] / dlls / msi / database.c
blobb0966a25cf377e90a4fa5a04623e211595097415
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 typedef struct tagMSITRANSFORM {
55 struct list entry;
56 IStorage *stg;
57 } MSITRANSFORM;
59 typedef struct tagMSISTREAM {
60 struct list entry;
61 IStream *stm;
62 } MSISTREAM;
64 static UINT find_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
66 MSISTREAM *stream;
68 LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
70 HRESULT r;
71 STATSTG stat;
73 r = IStream_Stat( stream->stm, &stat, 0 );
74 if( FAILED( r ) )
76 WARN("failed to stat stream r = %08x!\n", r);
77 continue;
80 if( !lstrcmpW( name, stat.pwcsName ) )
82 TRACE("found %s\n", debugstr_w(name));
83 *stm = stream->stm;
84 CoTaskMemFree( stat.pwcsName );
85 return ERROR_SUCCESS;
88 CoTaskMemFree( stat.pwcsName );
91 return ERROR_FUNCTION_FAILED;
94 static UINT clone_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
96 IStream *stream;
98 if (find_open_stream( db, name, &stream ) == ERROR_SUCCESS)
100 HRESULT r;
101 LARGE_INTEGER pos;
103 r = IStream_Clone( stream, stm );
104 if( FAILED( r ) )
106 WARN("failed to clone stream r = %08x!\n", r);
107 return ERROR_FUNCTION_FAILED;
110 pos.QuadPart = 0;
111 r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
112 if( FAILED( r ) )
114 IStream_Release( *stm );
115 return ERROR_FUNCTION_FAILED;
118 return ERROR_SUCCESS;
121 return ERROR_FUNCTION_FAILED;
124 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
126 LPWSTR encname;
127 HRESULT r;
129 encname = encode_streamname(FALSE, stname);
131 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
133 if (clone_open_stream( db, encname, stm ) == ERROR_SUCCESS)
135 msi_free( encname );
136 return ERROR_SUCCESS;
139 r = IStorage_OpenStream( db->storage, encname, NULL,
140 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
141 if( FAILED( r ) )
143 MSITRANSFORM *transform;
145 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
147 TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
148 r = IStorage_OpenStream( transform->stg, encname, NULL,
149 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
150 if (SUCCEEDED(r))
151 break;
155 msi_free( encname );
157 if( SUCCEEDED(r) )
159 MSISTREAM *stream;
161 stream = msi_alloc( sizeof(MSISTREAM) );
162 if( !stream )
163 return ERROR_NOT_ENOUGH_MEMORY;
165 stream->stm = *stm;
166 IStream_AddRef( *stm );
167 list_add_tail( &db->streams, &stream->entry );
170 return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
173 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
174 USHORT **pdata, UINT *psz )
176 HRESULT r;
177 UINT ret = ERROR_FUNCTION_FAILED;
178 VOID *data;
179 ULONG sz, count;
180 IStream *stm = NULL;
181 STATSTG stat;
183 r = db_get_raw_stream( db, stname, &stm );
184 if( r != ERROR_SUCCESS)
185 return ret;
186 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
187 if( FAILED( r ) )
189 WARN("open stream failed r = %08x!\n", r);
190 goto end;
193 if( stat.cbSize.QuadPart >> 32 )
195 WARN("Too big!\n");
196 goto end;
199 sz = stat.cbSize.QuadPart;
200 data = msi_alloc( sz );
201 if( !data )
203 WARN("couldn't allocate memory r=%08x!\n", r);
204 ret = ERROR_NOT_ENOUGH_MEMORY;
205 goto end;
208 r = IStream_Read(stm, data, sz, &count );
209 if( FAILED( r ) || ( count != sz ) )
211 msi_free( data );
212 WARN("read stream failed r = %08x!\n", r);
213 goto end;
216 *pdata = data;
217 *psz = sz;
218 ret = ERROR_SUCCESS;
220 end:
221 IStream_Release( stm );
223 return ret;
226 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
228 MSITRANSFORM *t;
230 t = msi_alloc( sizeof *t );
231 t->stg = stg;
232 IStorage_AddRef( stg );
233 list_add_tail( &db->transforms, &t->entry );
236 static void free_transforms( MSIDATABASE *db )
238 while( !list_empty( &db->transforms ) )
240 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
241 MSITRANSFORM, entry );
242 list_remove( &t->entry );
243 IStorage_Release( t->stg );
244 msi_free( t );
248 static void free_streams( MSIDATABASE *db )
250 while( !list_empty( &db->streams ) )
252 MSISTREAM *s = LIST_ENTRY( list_head( &db->streams ),
253 MSISTREAM, entry );
254 list_remove( &s->entry );
255 IStream_Release( s->stm );
256 msi_free( s );
260 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
262 MSIDATABASE *db = (MSIDATABASE *) arg;
264 msi_free(db->path);
265 free_cached_tables( db );
266 free_streams( db );
267 free_transforms( db );
268 msi_destroy_stringtable( db->strings );
269 IStorage_Release( db->storage );
270 if (db->deletefile)
272 DeleteFileW( db->deletefile );
273 msi_free( db->deletefile );
275 if (db->localfile)
277 DeleteFileW( db->localfile );
278 msi_free( db->localfile );
282 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
284 IStorage *stg = NULL;
285 HRESULT r;
286 MSIDATABASE *db = NULL;
287 UINT ret = ERROR_FUNCTION_FAILED;
288 LPCWSTR szMode, save_path;
289 STATSTG stat;
290 BOOL created = FALSE;
291 WCHAR path[MAX_PATH];
293 static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
295 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
297 if( !pdb )
298 return ERROR_INVALID_PARAMETER;
300 if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
301 szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
303 TRACE("Database is a patch\n");
304 szPersist -= MSIDBOPEN_PATCHFILE;
307 save_path = szDBPath;
308 szMode = szPersist;
309 if( HIWORD( szPersist ) )
311 if (!CopyFileW( szDBPath, szPersist, FALSE ))
312 return ERROR_OPEN_FAILED;
314 szDBPath = szPersist;
315 szPersist = MSIDBOPEN_TRANSACT;
316 created = TRUE;
319 if( szPersist == MSIDBOPEN_READONLY )
321 r = StgOpenStorage( szDBPath, NULL,
322 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
324 else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
326 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
327 * used here: */
328 r = StgCreateDocfile( szDBPath,
329 STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
330 if( r == ERROR_SUCCESS )
332 IStorage_SetClass( stg, &CLSID_MsiDatabase );
333 /* create the _Tables stream */
334 r = write_stream_data(stg, szTables, NULL, 0, TRUE);
335 if (SUCCEEDED(r))
336 r = msi_init_string_table( stg );
338 created = TRUE;
340 else if( szPersist == MSIDBOPEN_TRANSACT )
342 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
343 * used here: */
344 r = StgOpenStorage( szDBPath, NULL,
345 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
347 else if( szPersist == MSIDBOPEN_DIRECT )
349 r = StgOpenStorage( szDBPath, NULL,
350 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
352 else
354 ERR("unknown flag %p\n",szPersist);
355 return ERROR_INVALID_PARAMETER;
358 if( FAILED( r ) || !stg )
360 FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
361 return ERROR_FUNCTION_FAILED;
364 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
365 if( FAILED( r ) )
367 FIXME("Failed to stat storage\n");
368 goto end;
371 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
372 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
373 !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
375 ERR("storage GUID is not a MSI database GUID %s\n",
376 debugstr_guid(&stat.clsid) );
377 goto end;
380 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
381 MSI_CloseDatabase );
382 if( !db )
384 FIXME("Failed to allocate a handle\n");
385 goto end;
388 if (!strchrW( save_path, '\\' ))
390 GetCurrentDirectoryW( MAX_PATH, path );
391 lstrcatW( path, szBackSlash );
392 lstrcatW( path, save_path );
394 else
395 lstrcpyW( path, save_path );
397 db->path = strdupW( path );
399 if( TRACE_ON( msi ) )
400 enum_stream_names( stg );
402 db->storage = stg;
403 db->mode = szMode;
404 if (created)
405 db->deletefile = strdupW( szDBPath );
406 list_init( &db->tables );
407 list_init( &db->transforms );
408 list_init( &db->streams );
410 db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
411 if( !db->strings )
412 goto end;
414 ret = ERROR_SUCCESS;
416 msiobj_addref( &db->hdr );
417 IStorage_AddRef( stg );
418 *pdb = db;
420 end:
421 if( db )
422 msiobj_release( &db->hdr );
423 if( stg )
424 IStorage_Release( stg );
426 return ret;
429 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
431 MSIDATABASE *db;
432 UINT ret;
434 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
436 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
437 if( ret == ERROR_SUCCESS )
439 *phDB = alloc_msihandle( &db->hdr );
440 if (! *phDB)
441 ret = ERROR_NOT_ENOUGH_MEMORY;
442 msiobj_release( &db->hdr );
445 return ret;
448 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
450 HRESULT r = ERROR_FUNCTION_FAILED;
451 LPWSTR szwDBPath = NULL, szwPersist = NULL;
453 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
455 if( szDBPath )
457 szwDBPath = strdupAtoW( szDBPath );
458 if( !szwDBPath )
459 goto end;
462 if( HIWORD(szPersist) )
464 szwPersist = strdupAtoW( szPersist );
465 if( !szwPersist )
466 goto end;
468 else
469 szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
471 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
473 end:
474 if( HIWORD(szPersist) )
475 msi_free( szwPersist );
476 msi_free( szwDBPath );
478 return r;
481 static LPWSTR msi_read_text_archive(LPCWSTR path)
483 HANDLE file;
484 LPSTR data = NULL;
485 LPWSTR wdata = NULL;
486 DWORD read, size = 0;
488 file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
489 if (file == INVALID_HANDLE_VALUE)
490 return NULL;
492 size = GetFileSize( file, NULL );
493 data = msi_alloc( size + 1 );
494 if (!data)
495 goto done;
497 if (!ReadFile( file, data, size, &read, NULL ))
498 goto done;
500 data[size] = '\0';
501 wdata = strdupAtoW( data );
503 done:
504 CloseHandle( file );
505 msi_free( data );
506 return wdata;
509 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
511 LPWSTR ptr = *line, save;
512 DWORD i, count = 1;
514 *entries = NULL;
516 /* stay on this line */
517 while (*ptr && *ptr != '\n')
519 /* entries are separated by tabs */
520 if (*ptr == '\t')
521 count++;
523 ptr++;
526 *entries = msi_alloc(count * sizeof(LPWSTR));
527 if (!*entries)
528 return;
530 /* store pointers into the data */
531 for (i = 0, ptr = *line; i < count; i++)
533 while (*ptr && *ptr == '\r') ptr++;
534 save = ptr;
536 while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
538 /* NULL-separate the data */
539 if (*ptr == '\n' || *ptr == '\r')
541 while (*ptr == '\n' || *ptr == '\r')
542 *(ptr++) = '\0';
544 else if (*ptr)
545 *ptr++ = '\0';
547 (*entries)[i] = save;
550 /* move to the next line if there's more, else EOF */
551 *line = ptr;
553 if (num_entries)
554 *num_entries = count;
557 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
559 LPWSTR prelude;
560 DWORD size;
562 static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
564 size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
565 prelude = msi_alloc(size * sizeof(WCHAR));
566 if (!prelude)
567 return NULL;
569 sprintfW(prelude, create_fmt, table);
570 return prelude;
573 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
575 LPWSTR columns, p;
576 LPCWSTR type;
577 DWORD sql_size = 1, i, len;
578 WCHAR expanded[128], *ptr;
579 WCHAR size[10], comma[2], extra[30];
581 static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
582 static const WCHAR size_fmt[] = {'(','%','s',')',0};
583 static const WCHAR type_char[] = {'C','H','A','R',0};
584 static const WCHAR type_int[] = {'I','N','T',0};
585 static const WCHAR type_long[] = {'L','O','N','G',0};
586 static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
587 static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
588 static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
590 columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
591 if (!columns)
592 return NULL;
594 for (i = 0; i < num_columns; i++)
596 type = NULL;
597 comma[1] = size[0] = extra[0] = '\0';
599 if (i == num_columns - 1)
600 comma[0] = '\0';
601 else
602 comma[0] = ',';
604 ptr = &types[i][1];
605 len = atolW(ptr);
606 extra[0] = '\0';
608 switch (types[i][0])
610 case 'l':
611 lstrcpyW(extra, type_notnull);
612 case 'L':
613 lstrcatW(extra, localizable);
614 type = type_char;
615 sprintfW(size, size_fmt, ptr);
616 break;
617 case 's':
618 lstrcpyW(extra, type_notnull);
619 case 'S':
620 type = type_char;
621 sprintfW(size, size_fmt, ptr);
622 break;
623 case 'i':
624 lstrcpyW(extra, type_notnull);
625 case 'I':
626 if (len <= 2)
627 type = type_int;
628 else if (len == 4)
629 type = type_long;
630 else
632 WARN("invalid int width %u\n", len);
633 msi_free(columns);
634 return NULL;
636 break;
637 case 'v':
638 lstrcpyW(extra, type_notnull);
639 case 'V':
640 type = type_object;
641 break;
642 default:
643 ERR("Unknown type: %c\n", types[i][0]);
644 msi_free(columns);
645 return NULL;
648 sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
649 sql_size += lstrlenW(expanded);
651 p = msi_realloc(columns, sql_size * sizeof(WCHAR));
652 if (!p)
654 msi_free(columns);
655 return NULL;
657 columns = p;
659 lstrcatW(columns, expanded);
662 return columns;
665 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
667 LPWSTR postlude, keys, ptr;
668 DWORD size, key_size, i;
670 static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
671 static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
673 for (i = 0, size = 1; i < num_keys; i++)
674 size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
676 keys = msi_alloc(size * sizeof(WCHAR));
677 if (!keys)
678 return NULL;
680 for (i = 0, ptr = keys; i < num_keys; i++)
682 key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
683 sprintfW(ptr, key_fmt, primary_keys[i]);
684 ptr += key_size;
687 /* remove final ', ' */
688 *(ptr - 2) = '\0';
690 size = lstrlenW(postlude_fmt) + size - 1;
691 postlude = msi_alloc(size * sizeof(WCHAR));
692 if (!postlude)
693 goto done;
695 sprintfW(postlude, postlude_fmt, keys);
697 done:
698 msi_free(keys);
699 return postlude;
702 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
704 UINT r = ERROR_OUTOFMEMORY;
705 DWORD size;
706 MSIQUERY *view;
707 LPWSTR create_sql = NULL;
708 LPWSTR prelude, columns_sql, postlude;
710 prelude = msi_build_createsql_prelude(labels[0]);
711 columns_sql = msi_build_createsql_columns(columns, types, num_columns);
712 postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
714 if (!prelude || !columns_sql || !postlude)
715 goto done;
717 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
718 create_sql = msi_alloc(size * sizeof(WCHAR));
719 if (!create_sql)
720 goto done;
722 lstrcpyW(create_sql, prelude);
723 lstrcatW(create_sql, columns_sql);
724 lstrcatW(create_sql, postlude);
726 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
727 if (r != ERROR_SUCCESS)
728 goto done;
730 r = MSI_ViewExecute(view, NULL);
731 MSI_ViewClose(view);
732 msiobj_release(&view->hdr);
734 done:
735 msi_free(prelude);
736 msi_free(columns_sql);
737 msi_free(postlude);
738 msi_free(create_sql);
739 return r;
742 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
744 DWORD len;
745 LPWSTR fullname, ptr;
747 len = lstrlenW(path) + lstrlenW(name) + 1;
748 fullname = msi_alloc(len*sizeof(WCHAR));
749 if (!fullname)
750 return NULL;
752 lstrcpyW( fullname, path );
754 /* chop off extension from path */
755 ptr = strrchrW(fullname, '.');
756 if (!ptr)
758 msi_free (fullname);
759 return NULL;
761 *ptr++ = '\\';
762 lstrcpyW( ptr, name );
763 return fullname;
766 static UINT construct_record(DWORD num_columns, LPWSTR *types,
767 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
769 UINT i;
771 *rec = MSI_CreateRecord(num_columns);
772 if (!*rec)
773 return ERROR_OUTOFMEMORY;
775 for (i = 0; i < num_columns; i++)
777 switch (types[i][0])
779 case 'L': case 'l': case 'S': case 's':
780 MSI_RecordSetStringW(*rec, i + 1, data[i]);
781 break;
782 case 'I': case 'i':
783 if (*data[i])
784 MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
785 break;
786 case 'V': case 'v':
787 if (*data[i])
789 UINT r;
790 LPWSTR file = msi_import_stream_filename(path, data[i]);
791 if (!file)
792 return ERROR_FUNCTION_FAILED;
794 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
795 msi_free (file);
796 if (r != ERROR_SUCCESS)
797 return ERROR_FUNCTION_FAILED;
799 break;
800 default:
801 ERR("Unhandled column type: %c\n", types[i][0]);
802 msiobj_release(&(*rec)->hdr);
803 return ERROR_FUNCTION_FAILED;
807 return ERROR_SUCCESS;
810 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
811 LPWSTR *labels, LPWSTR **records,
812 int num_columns, int num_records,
813 LPWSTR path)
815 UINT r;
816 int i;
817 MSIQUERY *view;
818 MSIRECORD *rec;
820 static const WCHAR select[] = {
821 'S','E','L','E','C','T',' ','*',' ',
822 'F','R','O','M',' ','`','%','s','`',0
825 r = MSI_OpenQuery(db, &view, select, labels[0]);
826 if (r != ERROR_SUCCESS)
827 return r;
829 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
831 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
832 msiobj_release(&rec->hdr);
833 if (r != ERROR_SUCCESS)
834 goto done;
837 for (i = 0; i < num_records; i++)
839 r = construct_record(num_columns, types, records[i], path, &rec);
840 if (r != ERROR_SUCCESS)
841 goto done;
843 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
844 if (r != ERROR_SUCCESS)
846 msiobj_release(&rec->hdr);
847 goto done;
850 msiobj_release(&rec->hdr);
853 done:
854 msiobj_release(&view->hdr);
855 return r;
858 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
860 UINT r;
861 DWORD len, i;
862 DWORD num_labels, num_types;
863 DWORD num_columns, num_records = 0;
864 LPWSTR *columns, *types, *labels;
865 LPWSTR path, ptr, data;
866 LPWSTR **records = NULL;
867 LPWSTR **temp_records;
869 static const WCHAR suminfo[] =
870 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
872 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
874 if( folder == NULL || file == NULL )
875 return ERROR_INVALID_PARAMETER;
877 len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
878 path = msi_alloc( len * sizeof(WCHAR) );
879 if (!path)
880 return ERROR_OUTOFMEMORY;
882 lstrcpyW( path, folder );
883 lstrcatW( path, szBackSlash );
884 lstrcatW( path, file );
886 data = msi_read_text_archive( path );
888 ptr = data;
889 msi_parse_line( &ptr, &columns, &num_columns );
890 msi_parse_line( &ptr, &types, &num_types );
891 msi_parse_line( &ptr, &labels, &num_labels );
893 if (num_columns != num_types)
895 r = ERROR_FUNCTION_FAILED;
896 goto done;
899 records = msi_alloc(sizeof(LPWSTR *));
900 if (!records)
902 r = ERROR_OUTOFMEMORY;
903 goto done;
906 /* read in the table records */
907 while (*ptr)
909 msi_parse_line( &ptr, &records[num_records], NULL );
911 num_records++;
912 temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
913 if (!temp_records)
915 r = ERROR_OUTOFMEMORY;
916 goto done;
918 records = temp_records;
921 if (!strcmpW(labels[0], suminfo))
923 r = msi_add_suminfo( db, records, num_records, num_columns );
924 if (r != ERROR_SUCCESS)
926 r = ERROR_FUNCTION_FAILED;
927 goto done;
930 else
932 if (!TABLE_Exists(db, labels[0]))
934 r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
935 if (r != ERROR_SUCCESS)
937 r = ERROR_FUNCTION_FAILED;
938 goto done;
942 r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
945 done:
946 msi_free(path);
947 msi_free(data);
948 msi_free(columns);
949 msi_free(types);
950 msi_free(labels);
952 for (i = 0; i < num_records; i++)
953 msi_free(records[i]);
955 msi_free(records);
957 return r;
960 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
962 MSIDATABASE *db;
963 UINT r;
965 TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
967 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
968 if( !db )
970 IWineMsiRemoteDatabase *remote_database;
972 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
973 if ( !remote_database )
974 return ERROR_INVALID_HANDLE;
976 IWineMsiRemoteDatabase_Release( remote_database );
977 WARN("MsiDatabaseImport not allowed during a custom action!\n");
979 return ERROR_SUCCESS;
982 r = MSI_DatabaseImport( db, szFolder, szFilename );
983 msiobj_release( &db->hdr );
984 return r;
987 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
988 LPCSTR szFolder, LPCSTR szFilename )
990 LPWSTR path = NULL, file = NULL;
991 UINT r = ERROR_OUTOFMEMORY;
993 TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
995 if( szFolder )
997 path = strdupAtoW( szFolder );
998 if( !path )
999 goto end;
1002 if( szFilename )
1004 file = strdupAtoW( szFilename );
1005 if( !file )
1006 goto end;
1009 r = MsiDatabaseImportW( handle, path, file );
1011 end:
1012 msi_free( path );
1013 msi_free( file );
1015 return r;
1018 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1020 UINT i, count, len, r = ERROR_SUCCESS;
1021 const char *sep;
1022 char *buffer;
1023 DWORD sz;
1025 len = 0x100;
1026 buffer = msi_alloc( len );
1027 if ( !buffer )
1028 return ERROR_OUTOFMEMORY;
1030 count = MSI_RecordGetFieldCount( row );
1031 for ( i=start; i<=count; i++ )
1033 sz = len;
1034 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1035 if (r == ERROR_MORE_DATA)
1037 char *p = msi_realloc( buffer, sz + 1 );
1038 if (!p)
1039 break;
1040 len = sz + 1;
1041 buffer = p;
1043 sz = len;
1044 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1045 if (r != ERROR_SUCCESS)
1046 break;
1048 if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1050 r = ERROR_FUNCTION_FAILED;
1051 break;
1054 sep = (i < count) ? "\t" : "\r\n";
1055 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1057 r = ERROR_FUNCTION_FAILED;
1058 break;
1061 msi_free( buffer );
1062 return r;
1065 static UINT msi_export_row( MSIRECORD *row, void *arg )
1067 return msi_export_record( arg, row, 1 );
1070 static UINT msi_export_forcecodepage( HANDLE handle )
1072 DWORD sz;
1074 static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
1076 FIXME("Read the codepage from the strings table!\n");
1078 sz = lstrlenA(data) + 1;
1079 if (!WriteFile(handle, data, sz, &sz, NULL))
1080 return ERROR_FUNCTION_FAILED;
1082 return ERROR_SUCCESS;
1085 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1086 LPCWSTR folder, LPCWSTR file )
1088 static const WCHAR query[] = {
1089 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1090 static const WCHAR forcecodepage[] = {
1091 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1092 MSIRECORD *rec = NULL;
1093 MSIQUERY *view = NULL;
1094 LPWSTR filename;
1095 HANDLE handle;
1096 UINT len, r;
1098 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1099 debugstr_w(folder), debugstr_w(file) );
1101 if( folder == NULL || file == NULL )
1102 return ERROR_INVALID_PARAMETER;
1104 len = lstrlenW(folder) + lstrlenW(file) + 2;
1105 filename = msi_alloc(len * sizeof (WCHAR));
1106 if (!filename)
1107 return ERROR_OUTOFMEMORY;
1109 lstrcpyW( filename, folder );
1110 lstrcatW( filename, szBackSlash );
1111 lstrcatW( filename, file );
1113 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1114 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1115 msi_free( filename );
1116 if (handle == INVALID_HANDLE_VALUE)
1117 return ERROR_FUNCTION_FAILED;
1119 if (!lstrcmpW( table, forcecodepage ))
1121 r = msi_export_forcecodepage( handle );
1122 goto done;
1125 r = MSI_OpenQuery( db, &view, query, table );
1126 if (r == ERROR_SUCCESS)
1128 /* write out row 1, the column names */
1129 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1130 if (r == ERROR_SUCCESS)
1132 msi_export_record( handle, rec, 1 );
1133 msiobj_release( &rec->hdr );
1136 /* write out row 2, the column types */
1137 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1138 if (r == ERROR_SUCCESS)
1140 msi_export_record( handle, rec, 1 );
1141 msiobj_release( &rec->hdr );
1144 /* write out row 3, the table name + keys */
1145 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1146 if (r == ERROR_SUCCESS)
1148 MSI_RecordSetStringW( rec, 0, table );
1149 msi_export_record( handle, rec, 0 );
1150 msiobj_release( &rec->hdr );
1153 /* write out row 4 onwards, the data */
1154 r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1155 msiobj_release( &view->hdr );
1158 done:
1159 CloseHandle( handle );
1160 return r;
1163 /***********************************************************************
1164 * MsiExportDatabaseW [MSI.@]
1166 * Writes a file containing the table data as tab separated ASCII.
1168 * The format is as follows:
1170 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1171 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1172 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1174 * Followed by the data, starting at row 1 with one row per line
1176 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1178 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1179 LPCWSTR szFolder, LPCWSTR szFilename )
1181 MSIDATABASE *db;
1182 UINT r;
1184 TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1185 debugstr_w(szFolder), debugstr_w(szFilename));
1187 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1188 if( !db )
1190 IWineMsiRemoteDatabase *remote_database;
1192 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1193 if ( !remote_database )
1194 return ERROR_INVALID_HANDLE;
1196 IWineMsiRemoteDatabase_Release( remote_database );
1197 WARN("MsiDatabaseExport not allowed during a custom action!\n");
1199 return ERROR_SUCCESS;
1202 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1203 msiobj_release( &db->hdr );
1204 return r;
1207 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1208 LPCSTR szFolder, LPCSTR szFilename )
1210 LPWSTR path = NULL, file = NULL, table = NULL;
1211 UINT r = ERROR_OUTOFMEMORY;
1213 TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1214 debugstr_a(szFolder), debugstr_a(szFilename));
1216 if( szTable )
1218 table = strdupAtoW( szTable );
1219 if( !table )
1220 goto end;
1223 if( szFolder )
1225 path = strdupAtoW( szFolder );
1226 if( !path )
1227 goto end;
1230 if( szFilename )
1232 file = strdupAtoW( szFilename );
1233 if( !file )
1234 goto end;
1237 r = MsiDatabaseExportW( handle, table, path, file );
1239 end:
1240 msi_free( table );
1241 msi_free( path );
1242 msi_free( file );
1244 return r;
1247 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1248 LPCSTR szTableName)
1250 UINT r;
1251 LPWSTR table;
1253 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1254 debugstr_a(szTableName));
1256 table = strdupAtoW(szTableName);
1257 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1259 msi_free(table);
1260 return r;
1263 typedef struct _tagMERGETABLE
1265 struct list entry;
1266 struct list rows;
1267 LPWSTR name;
1268 DWORD numconflicts;
1269 LPWSTR *columns;
1270 DWORD numcolumns;
1271 LPWSTR *types;
1272 DWORD numtypes;
1273 LPWSTR *labels;
1274 DWORD numlabels;
1275 } MERGETABLE;
1277 typedef struct _tagMERGEROW
1279 struct list entry;
1280 MSIRECORD *data;
1281 } MERGEROW;
1283 typedef struct _tagMERGEDATA
1285 MSIDATABASE *db;
1286 MSIDATABASE *merge;
1287 MERGETABLE *curtable;
1288 MSIQUERY *curview;
1289 struct list *tabledata;
1290 } MERGEDATA;
1292 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1294 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1295 ((type2[0] == 'l') || (type2[0] == 's')))
1296 return TRUE;
1298 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1299 ((type2[0] == 'L') || (type2[0] == 'S')))
1300 return TRUE;
1302 return !lstrcmpW(type1, type2);
1305 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1307 MSIRECORD *dbrec, *mergerec;
1308 UINT r, i, count;
1310 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1311 if (r != ERROR_SUCCESS)
1312 return r;
1314 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1315 if (r != ERROR_SUCCESS)
1316 return r;
1318 count = MSI_RecordGetFieldCount(dbrec);
1319 for (i = 1; i <= count; i++)
1321 if (!MSI_RecordGetString(mergerec, i))
1322 break;
1324 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1325 MSI_RecordGetString(mergerec, i)))
1327 r = ERROR_DATATYPE_MISMATCH;
1328 goto done;
1332 msiobj_release(&dbrec->hdr);
1333 msiobj_release(&mergerec->hdr);
1334 dbrec = mergerec = NULL;
1336 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1337 if (r != ERROR_SUCCESS)
1338 return r;
1340 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1341 if (r != ERROR_SUCCESS)
1342 return r;
1344 count = MSI_RecordGetFieldCount(dbrec);
1345 for (i = 1; i <= count; i++)
1347 if (!MSI_RecordGetString(mergerec, i))
1348 break;
1350 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1351 MSI_RecordGetString(mergerec, i)))
1353 r = ERROR_DATATYPE_MISMATCH;
1354 break;
1358 done:
1359 msiobj_release(&dbrec->hdr);
1360 msiobj_release(&mergerec->hdr);
1362 return r;
1365 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1366 LPCWSTR table)
1368 MSIRECORD *dbrec, *mergerec = NULL;
1369 UINT r, i, count;
1371 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1372 if (r != ERROR_SUCCESS)
1373 return r;
1375 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1376 if (r != ERROR_SUCCESS)
1377 goto done;
1379 count = MSI_RecordGetFieldCount(dbrec);
1380 if (count != MSI_RecordGetFieldCount(mergerec))
1382 r = ERROR_DATATYPE_MISMATCH;
1383 goto done;
1386 for (i = 1; i <= count; i++)
1388 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1389 MSI_RecordGetString(mergerec, i)))
1391 r = ERROR_DATATYPE_MISMATCH;
1392 goto done;
1396 done:
1397 msiobj_release(&dbrec->hdr);
1398 msiobj_release(&mergerec->hdr);
1400 return r;
1403 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1405 MSIRECORD *colnames;
1406 LPWSTR str, val;
1407 UINT r, i = 0, sz = 0;
1408 int cmp;
1410 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1411 if (r != ERROR_SUCCESS)
1412 return NULL;
1416 str = msi_dup_record_field(colnames, ++i);
1417 cmp = lstrcmpW(key, str);
1418 msi_free(str);
1419 } while (cmp);
1421 msiobj_release(&colnames->hdr);
1423 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1424 if (r != ERROR_SUCCESS)
1425 return NULL;
1426 sz++;
1428 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1430 /* quote string record fields */
1431 const WCHAR szQuote[] = {'\'', 0};
1432 sz += 2;
1433 val = msi_alloc(sz*sizeof(WCHAR));
1434 if (!val)
1435 return NULL;
1437 lstrcpyW(val, szQuote);
1438 r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1439 lstrcpyW(val+1+sz, szQuote);
1441 else
1443 /* do not quote integer record fields */
1444 val = msi_alloc(sz*sizeof(WCHAR));
1445 if (!val)
1446 return NULL;
1448 r = MSI_RecordGetStringW(rec, i, val, &sz);
1451 if (r != ERROR_SUCCESS)
1453 ERR("failed to get string!\n");
1454 msi_free(val);
1455 return NULL;
1458 return val;
1461 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1462 LPWSTR table, MSIRECORD *rec)
1464 LPWSTR query = NULL, clause = NULL;
1465 LPWSTR ptr = NULL, val;
1466 LPCWSTR setptr;
1467 DWORD size = 1, oldsize;
1468 LPCWSTR key;
1469 MSIRECORD *keys;
1470 UINT r, i, count;
1472 static const WCHAR keyset[] = {
1473 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1474 static const WCHAR lastkeyset[] = {
1475 '`','%','s','`',' ','=',' ','%','s',' ',0};
1476 static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1477 'F','R','O','M',' ','`','%','s','`',' ',
1478 'W','H','E','R','E',' ','%','s',0};
1480 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1481 if (r != ERROR_SUCCESS)
1482 return NULL;
1484 clause = msi_alloc_zero(size * sizeof(WCHAR));
1485 if (!clause)
1486 goto done;
1488 ptr = clause;
1489 count = MSI_RecordGetFieldCount(keys);
1490 for (i = 1; i <= count; i++)
1492 key = MSI_RecordGetString(keys, i);
1493 val = get_key_value(view, key, rec);
1495 if (i == count)
1496 setptr = lastkeyset;
1497 else
1498 setptr = keyset;
1500 oldsize = size;
1501 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1502 clause = msi_realloc(clause, size * sizeof (WCHAR));
1503 if (!clause)
1505 msi_free(val);
1506 goto done;
1509 ptr = clause + oldsize - 1;
1510 sprintfW(ptr, setptr, key, val);
1511 msi_free(val);
1514 size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1515 query = msi_alloc(size * sizeof(WCHAR));
1516 if (!query)
1517 goto done;
1519 sprintfW(query, fmt, table, clause);
1521 done:
1522 msi_free(clause);
1523 msiobj_release(&keys->hdr);
1524 return query;
1527 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1529 MERGEDATA *data = param;
1530 MERGETABLE *table = data->curtable;
1531 MERGEROW *mergerow;
1532 MSIQUERY *dbview = NULL;
1533 MSIRECORD *row = NULL;
1534 LPWSTR query = NULL;
1535 UINT r = ERROR_SUCCESS;
1537 if (TABLE_Exists(data->db, table->name))
1539 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1540 if (!query)
1541 return ERROR_OUTOFMEMORY;
1543 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1544 if (r != ERROR_SUCCESS)
1545 goto done;
1547 r = MSI_ViewExecute(dbview, NULL);
1548 if (r != ERROR_SUCCESS)
1549 goto done;
1551 r = MSI_ViewFetch(dbview, &row);
1552 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1554 table->numconflicts++;
1555 goto done;
1557 else if (r != ERROR_NO_MORE_ITEMS)
1558 goto done;
1560 r = ERROR_SUCCESS;
1563 mergerow = msi_alloc(sizeof(MERGEROW));
1564 if (!mergerow)
1566 r = ERROR_OUTOFMEMORY;
1567 goto done;
1570 mergerow->data = MSI_CloneRecord(rec);
1571 if (!mergerow->data)
1573 r = ERROR_OUTOFMEMORY;
1574 msi_free(mergerow);
1575 goto done;
1578 list_add_tail(&table->rows, &mergerow->entry);
1580 done:
1581 msi_free(query);
1582 msiobj_release(&row->hdr);
1583 msiobj_release(&dbview->hdr);
1584 return r;
1587 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1589 UINT r, i, count;
1590 MSIRECORD *prec = NULL;
1592 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1593 if (r != ERROR_SUCCESS)
1594 return r;
1596 count = MSI_RecordGetFieldCount(prec);
1597 *numlabels = count + 1;
1598 *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1599 if (!*labels)
1601 r = ERROR_OUTOFMEMORY;
1602 goto end;
1605 (*labels)[0] = strdupW(table);
1606 for (i=1; i<=count; i++ )
1608 (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1611 end:
1612 msiobj_release( &prec->hdr );
1613 return r;
1616 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1618 UINT r, i, count;
1619 MSIRECORD *prec = NULL;
1621 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1622 if (r != ERROR_SUCCESS)
1623 return r;
1625 count = MSI_RecordGetFieldCount(prec);
1626 *columns = msi_alloc(count*sizeof(LPWSTR));
1627 if (!*columns)
1629 r = ERROR_OUTOFMEMORY;
1630 goto end;
1633 for (i=1; i<=count; i++ )
1635 (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1638 *numcolumns = count;
1640 end:
1641 msiobj_release( &prec->hdr );
1642 return r;
1645 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1647 UINT r, i, count;
1648 MSIRECORD *prec = NULL;
1650 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1651 if (r != ERROR_SUCCESS)
1652 return r;
1654 count = MSI_RecordGetFieldCount(prec);
1655 *types = msi_alloc(count*sizeof(LPWSTR));
1656 if (!*types)
1658 r = ERROR_OUTOFMEMORY;
1659 goto end;
1662 *numtypes = count;
1663 for (i=1; i<=count; i++ )
1665 (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1668 end:
1669 msiobj_release( &prec->hdr );
1670 return r;
1673 static void merge_free_rows(MERGETABLE *table)
1675 struct list *item, *cursor;
1677 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1679 MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1681 list_remove(&row->entry);
1682 msiobj_release(&row->data->hdr);
1683 msi_free(row);
1687 static void free_merge_table(MERGETABLE *table)
1689 UINT i;
1691 if (table->labels != NULL)
1693 for (i = 0; i < table->numlabels; i++)
1694 msi_free(table->labels[i]);
1696 msi_free(table->labels);
1699 if (table->columns != NULL)
1701 for (i = 0; i < table->numcolumns; i++)
1702 msi_free(table->columns[i]);
1704 msi_free(table->columns);
1707 if (table->types != NULL)
1709 for (i = 0; i < table->numtypes; i++)
1710 msi_free(table->types[i]);
1712 msi_free(table->types);
1715 msi_free(table->name);
1716 merge_free_rows(table);
1718 msi_free(table);
1721 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1723 UINT r;
1724 MERGETABLE *table;
1725 MSIQUERY *mergeview = NULL;
1727 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1728 'F','R','O','M',' ','`','%','s','`',0};
1730 table = msi_alloc_zero(sizeof(MERGETABLE));
1731 if (!table)
1733 *ptable = NULL;
1734 return ERROR_OUTOFMEMORY;
1737 r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1738 if (r != ERROR_SUCCESS)
1739 goto err;
1741 r = MSI_OpenQuery(db, &mergeview, query, name);
1742 if (r != ERROR_SUCCESS)
1743 goto err;
1745 r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1746 if (r != ERROR_SUCCESS)
1747 goto err;
1749 r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1750 if (r != ERROR_SUCCESS)
1751 goto err;
1753 list_init(&table->rows);
1755 table->name = strdupW(name);
1756 table->numconflicts = 0;
1758 msiobj_release(&mergeview->hdr);
1759 *ptable = table;
1760 return ERROR_SUCCESS;
1762 err:
1763 msiobj_release(&mergeview->hdr);
1764 free_merge_table(table);
1765 *ptable = NULL;
1766 return r;
1769 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1771 MERGEDATA *data = param;
1772 MERGETABLE *table;
1773 MSIQUERY *dbview = NULL;
1774 MSIQUERY *mergeview = NULL;
1775 LPCWSTR name;
1776 UINT r;
1778 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1779 'F','R','O','M',' ','`','%','s','`',0};
1781 name = MSI_RecordGetString(rec, 1);
1783 r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1784 if (r != ERROR_SUCCESS)
1785 goto done;
1787 if (TABLE_Exists(data->db, name))
1789 r = MSI_OpenQuery(data->db, &dbview, query, name);
1790 if (r != ERROR_SUCCESS)
1791 goto done;
1793 r = merge_verify_colnames(dbview, mergeview);
1794 if (r != ERROR_SUCCESS)
1795 goto done;
1797 r = merge_verify_primary_keys(data->db, data->merge, name);
1798 if (r != ERROR_SUCCESS)
1799 goto done;
1802 r = msi_get_merge_table(data->merge, name, &table);
1803 if (r != ERROR_SUCCESS)
1804 goto done;
1806 data->curtable = table;
1807 data->curview = mergeview;
1808 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1809 if (r != ERROR_SUCCESS)
1811 free_merge_table(table);
1812 goto done;
1815 list_add_tail(data->tabledata, &table->entry);
1817 done:
1818 msiobj_release(&dbview->hdr);
1819 msiobj_release(&mergeview->hdr);
1820 return r;
1823 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1824 struct list *tabledata)
1826 UINT r;
1827 MSIQUERY *view;
1828 MERGEDATA data;
1830 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1831 'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1833 r = MSI_DatabaseOpenViewW(merge, query, &view);
1834 if (r != ERROR_SUCCESS)
1835 return r;
1837 data.db = db;
1838 data.merge = merge;
1839 data.tabledata = tabledata;
1840 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1842 msiobj_release(&view->hdr);
1843 return r;
1846 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1848 UINT r;
1849 MERGEROW *row;
1850 MSIVIEW *tv;
1852 if (!TABLE_Exists(db, table->name))
1854 r = msi_add_table_to_db(db, table->columns, table->types,
1855 table->labels, table->numlabels, table->numcolumns);
1856 if (r != ERROR_SUCCESS)
1857 return ERROR_FUNCTION_FAILED;
1860 LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1862 r = TABLE_CreateView(db, table->name, &tv);
1863 if (r != ERROR_SUCCESS)
1864 return r;
1866 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1867 tv->ops->delete(tv);
1869 if (r != ERROR_SUCCESS)
1870 return r;
1873 return ERROR_SUCCESS;
1876 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1877 LPWSTR table, DWORD numconflicts)
1879 UINT r;
1880 MSIQUERY *view;
1882 static const WCHAR create[] = {
1883 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1884 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1885 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1886 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1887 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1888 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1889 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1890 static const WCHAR insert[] = {
1891 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1892 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1893 '`','N','u','m','R','o','w','M','e','r','g','e',
1894 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1895 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1897 if (!TABLE_Exists(db, error))
1899 r = MSI_OpenQuery(db, &view, create, error);
1900 if (r != ERROR_SUCCESS)
1901 return r;
1903 r = MSI_ViewExecute(view, NULL);
1904 msiobj_release(&view->hdr);
1905 if (r != ERROR_SUCCESS)
1906 return r;
1909 r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1910 if (r != ERROR_SUCCESS)
1911 return r;
1913 r = MSI_ViewExecute(view, NULL);
1914 msiobj_release(&view->hdr);
1915 return r;
1918 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1919 LPCWSTR szTableName)
1921 struct list tabledata = LIST_INIT(tabledata);
1922 struct list *item, *cursor;
1923 MSIDATABASE *db, *merge;
1924 MERGETABLE *table;
1925 BOOL conflicts;
1926 UINT r;
1928 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1929 debugstr_w(szTableName));
1931 if (szTableName && !*szTableName)
1932 return ERROR_INVALID_TABLE;
1934 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1935 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1936 if (!db || !merge)
1938 r = ERROR_INVALID_HANDLE;
1939 goto done;
1942 r = gather_merge_data(db, merge, &tabledata);
1943 if (r != ERROR_SUCCESS)
1944 goto done;
1946 conflicts = FALSE;
1947 LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1949 if (table->numconflicts)
1951 conflicts = TRUE;
1953 r = update_merge_errors(db, szTableName, table->name,
1954 table->numconflicts);
1955 if (r != ERROR_SUCCESS)
1956 break;
1958 else
1960 r = merge_table(db, table);
1961 if (r != ERROR_SUCCESS)
1962 break;
1966 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1968 MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1969 list_remove(&table->entry);
1970 free_merge_table(table);
1973 if (conflicts)
1974 r = ERROR_FUNCTION_FAILED;
1976 done:
1977 msiobj_release(&db->hdr);
1978 msiobj_release(&merge->hdr);
1979 return r;
1982 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1984 MSIDBSTATE ret = MSIDBSTATE_READ;
1985 MSIDATABASE *db;
1987 TRACE("%d\n", handle);
1989 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1990 if( !db )
1992 IWineMsiRemoteDatabase *remote_database;
1994 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1995 if ( !remote_database )
1996 return MSIDBSTATE_ERROR;
1998 IWineMsiRemoteDatabase_Release( remote_database );
1999 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
2001 return MSIDBSTATE_READ;
2004 if (db->mode != MSIDBOPEN_READONLY )
2005 ret = MSIDBSTATE_WRITE;
2006 msiobj_release( &db->hdr );
2008 return ret;
2011 typedef struct _msi_remote_database_impl {
2012 const IWineMsiRemoteDatabaseVtbl *lpVtbl;
2013 MSIHANDLE database;
2014 LONG refs;
2015 } msi_remote_database_impl;
2017 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
2019 return (msi_remote_database_impl *)iface;
2022 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2023 REFIID riid,LPVOID *ppobj)
2025 if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2026 IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2028 IUnknown_AddRef( iface );
2029 *ppobj = iface;
2030 return S_OK;
2033 return E_NOINTERFACE;
2036 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2038 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2040 return InterlockedIncrement( &This->refs );
2043 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2045 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2046 ULONG r;
2048 r = InterlockedDecrement( &This->refs );
2049 if (r == 0)
2051 MsiCloseHandle( This->database );
2052 msi_free( This );
2054 return r;
2057 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2058 BSTR table, MSICONDITION *persistent )
2060 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2061 *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2062 return S_OK;
2065 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2066 BSTR table, MSIHANDLE *keys )
2068 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2069 UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2070 return HRESULT_FROM_WIN32(r);
2073 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2074 UINT updatecount, MSIHANDLE *suminfo )
2076 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2077 UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2078 return HRESULT_FROM_WIN32(r);
2081 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2082 BSTR query, MSIHANDLE *view )
2084 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2085 UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2086 return HRESULT_FROM_WIN32(r);
2089 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2091 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2092 This->database = handle;
2093 return S_OK;
2096 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2098 mrd_QueryInterface,
2099 mrd_AddRef,
2100 mrd_Release,
2101 mrd_IsTablePersistent,
2102 mrd_GetPrimaryKeys,
2103 mrd_GetSummaryInformation,
2104 mrd_OpenView,
2105 mrd_SetMsiHandle,
2108 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2110 msi_remote_database_impl *This;
2112 This = msi_alloc( sizeof *This );
2113 if (!This)
2114 return E_OUTOFMEMORY;
2116 This->lpVtbl = &msi_remote_database_vtbl;
2117 This->database = 0;
2118 This->refs = 1;
2120 *ppObj = This;
2122 return S_OK;