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
24 #define NONAMELESSUNION
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
37 #include "msiserver.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
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
{
59 typedef struct tagMSISTREAM
{
64 static UINT
find_open_stream( MSIDATABASE
*db
, LPCWSTR name
, IStream
**stm
)
68 LIST_FOR_EACH_ENTRY( stream
, &db
->streams
, MSISTREAM
, entry
)
73 r
= IStream_Stat( stream
->stm
, &stat
, 0 );
76 WARN("failed to stat stream r = %08x!\n", r
);
80 if( !lstrcmpW( name
, stat
.pwcsName
) )
82 TRACE("found %s\n", debugstr_w(name
));
84 CoTaskMemFree( stat
.pwcsName
);
88 CoTaskMemFree( stat
.pwcsName
);
91 return ERROR_FUNCTION_FAILED
;
94 static UINT
clone_open_stream( MSIDATABASE
*db
, LPCWSTR name
, IStream
**stm
)
98 if (find_open_stream( db
, name
, &stream
) == ERROR_SUCCESS
)
103 r
= IStream_Clone( stream
, stm
);
106 WARN("failed to clone stream r = %08x!\n", r
);
107 return ERROR_FUNCTION_FAILED
;
111 r
= IStream_Seek( *stm
, pos
, STREAM_SEEK_SET
, NULL
);
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
)
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
)
136 return ERROR_SUCCESS
;
139 r
= IStorage_OpenStream( db
->storage
, encname
, NULL
,
140 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, stm
);
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
);
161 stream
= msi_alloc( sizeof(MSISTREAM
) );
163 return ERROR_NOT_ENOUGH_MEMORY
;
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
)
177 UINT ret
= ERROR_FUNCTION_FAILED
;
183 r
= db_get_raw_stream( db
, stname
, &stm
);
184 if( r
!= ERROR_SUCCESS
)
186 r
= IStream_Stat(stm
, &stat
, STATFLAG_NONAME
);
189 WARN("open stream failed r = %08x!\n", r
);
193 if( stat
.cbSize
.QuadPart
>> 32 )
199 sz
= stat
.cbSize
.QuadPart
;
200 data
= msi_alloc( sz
);
203 WARN("couldn't allocate memory r=%08x!\n", r
);
204 ret
= ERROR_NOT_ENOUGH_MEMORY
;
208 r
= IStream_Read(stm
, data
, sz
, &count
);
209 if( FAILED( r
) || ( count
!= sz
) )
212 WARN("read stream failed r = %08x!\n", r
);
221 IStream_Release( stm
);
226 void append_storage_to_db( MSIDATABASE
*db
, IStorage
*stg
)
230 t
= msi_alloc( sizeof *t
);
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
);
248 static void free_streams( MSIDATABASE
*db
)
250 while( !list_empty( &db
->streams
) )
252 MSISTREAM
*s
= LIST_ENTRY( list_head( &db
->streams
),
254 list_remove( &s
->entry
);
255 IStream_Release( s
->stm
);
260 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
262 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
265 free_cached_tables( db
);
267 free_transforms( db
);
268 msi_destroy_stringtable( db
->strings
);
269 IStorage_Release( db
->storage
);
272 DeleteFileW( db
->deletefile
);
273 msi_free( db
->deletefile
);
277 DeleteFileW( db
->localfile
);
278 msi_free( db
->localfile
);
282 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
284 IStorage
*stg
= NULL
;
286 MSIDATABASE
*db
= NULL
;
287 UINT ret
= ERROR_FUNCTION_FAILED
;
288 LPCWSTR szMode
, save_path
;
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
) );
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
;
309 if( HIWORD( szPersist
) )
311 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
312 return ERROR_OPEN_FAILED
;
314 szDBPath
= szPersist
;
315 szPersist
= MSIDBOPEN_TRANSACT
;
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
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
);
336 r
= msi_init_string_table( stg
);
340 else if( szPersist
== MSIDBOPEN_TRANSACT
)
342 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
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
);
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
);
367 FIXME("Failed to stat storage\n");
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
) );
380 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
384 FIXME("Failed to allocate a handle\n");
388 if (!strchrW( save_path
, '\\' ))
390 GetCurrentDirectoryW( MAX_PATH
, path
);
391 lstrcatW( path
, szBackSlash
);
392 lstrcatW( path
, save_path
);
395 lstrcpyW( path
, save_path
);
397 db
->path
= strdupW( path
);
399 if( TRACE_ON( msi
) )
400 enum_stream_names( stg
);
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
);
416 msiobj_addref( &db
->hdr
);
417 IStorage_AddRef( stg
);
422 msiobj_release( &db
->hdr
);
424 IStorage_Release( stg
);
429 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
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
);
441 ret
= ERROR_NOT_ENOUGH_MEMORY
;
442 msiobj_release( &db
->hdr
);
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
);
457 szwDBPath
= strdupAtoW( szDBPath
);
462 if( HIWORD(szPersist
) )
464 szwPersist
= strdupAtoW( szPersist
);
469 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
471 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
474 if( HIWORD(szPersist
) )
475 msi_free( szwPersist
);
476 msi_free( szwDBPath
);
481 static LPWSTR
msi_read_text_archive(LPCWSTR path
)
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
)
492 size
= GetFileSize( file
, NULL
);
493 data
= msi_alloc( size
+ 1 );
497 if (!ReadFile( file
, data
, size
, &read
, NULL
))
501 wdata
= strdupAtoW( data
);
509 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
)
511 LPWSTR ptr
= *line
, save
;
516 /* stay on this line */
517 while (*ptr
&& *ptr
!= '\n')
519 /* entries are separated by tabs */
526 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
530 /* store pointers into the data */
531 for (i
= 0, ptr
= *line
; i
< count
; i
++)
533 while (*ptr
&& *ptr
== '\r') 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')
547 (*entries
)[i
] = save
;
550 /* move to the next line if there's more, else EOF */
554 *num_entries
= count
;
557 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
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
));
569 sprintfW(prelude
, create_fmt
, table
);
573 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
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
));
594 for (i
= 0; i
< num_columns
; i
++)
597 comma
[1] = size
[0] = extra
[0] = '\0';
599 if (i
== num_columns
- 1)
611 lstrcpyW(extra
, type_notnull
);
613 lstrcatW(extra
, localizable
);
615 sprintfW(size
, size_fmt
, ptr
);
618 lstrcpyW(extra
, type_notnull
);
621 sprintfW(size
, size_fmt
, ptr
);
624 lstrcpyW(extra
, type_notnull
);
632 WARN("invalid int width %u\n", len
);
638 lstrcpyW(extra
, type_notnull
);
643 ERR("Unknown type: %c\n", types
[i
][0]);
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
));
659 lstrcatW(columns
, expanded
);
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
));
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
]);
687 /* remove final ', ' */
690 size
= lstrlenW(postlude_fmt
) + size
- 1;
691 postlude
= msi_alloc(size
* sizeof(WCHAR
));
695 sprintfW(postlude
, postlude_fmt
, keys
);
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
;
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
)
717 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
718 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
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
)
730 r
= MSI_ViewExecute(view
, NULL
);
732 msiobj_release(&view
->hdr
);
736 msi_free(columns_sql
);
738 msi_free(create_sql
);
742 static LPWSTR
msi_import_stream_filename(LPCWSTR path
, LPCWSTR name
)
745 LPWSTR fullname
, ptr
;
747 len
= lstrlenW(path
) + lstrlenW(name
) + 1;
748 fullname
= msi_alloc(len
*sizeof(WCHAR
));
752 lstrcpyW( fullname
, path
);
754 /* chop off extension from path */
755 ptr
= strrchrW(fullname
, '.');
762 lstrcpyW( ptr
, name
);
766 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
767 LPWSTR
*data
, LPWSTR path
, MSIRECORD
**rec
)
771 *rec
= MSI_CreateRecord(num_columns
);
773 return ERROR_OUTOFMEMORY
;
775 for (i
= 0; i
< num_columns
; i
++)
779 case 'L': case 'l': case 'S': case 's':
780 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
784 MSI_RecordSetInteger(*rec
, i
+ 1, atoiW(data
[i
]));
790 LPWSTR file
= msi_import_stream_filename(path
, data
[i
]);
792 return ERROR_FUNCTION_FAILED
;
794 r
= MSI_RecordSetStreamFromFileW(*rec
, i
+ 1, file
);
796 if (r
!= ERROR_SUCCESS
)
797 return ERROR_FUNCTION_FAILED
;
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
,
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
)
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
)
837 for (i
= 0; i
< num_records
; i
++)
839 r
= construct_record(num_columns
, types
, records
[i
], path
, &rec
);
840 if (r
!= ERROR_SUCCESS
)
843 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
844 if (r
!= ERROR_SUCCESS
)
846 msiobj_release(&rec
->hdr
);
850 msiobj_release(&rec
->hdr
);
854 msiobj_release(&view
->hdr
);
858 static UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
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
) );
880 return ERROR_OUTOFMEMORY
;
882 lstrcpyW( path
, folder
);
883 lstrcatW( path
, szBackSlash
);
884 lstrcatW( path
, file
);
886 data
= msi_read_text_archive( path
);
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
;
899 records
= msi_alloc(sizeof(LPWSTR
*));
902 r
= ERROR_OUTOFMEMORY
;
906 /* read in the table records */
909 msi_parse_line( &ptr
, &records
[num_records
], NULL
);
912 temp_records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
915 r
= ERROR_OUTOFMEMORY
;
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
;
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
;
942 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
, path
);
952 for (i
= 0; i
< num_records
; i
++)
953 msi_free(records
[i
]);
960 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
965 TRACE("%x %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
967 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
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
);
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
));
997 path
= strdupAtoW( szFolder
);
1004 file
= strdupAtoW( szFilename
);
1009 r
= MsiDatabaseImportW( handle
, path
, file
);
1018 static UINT
msi_export_record( HANDLE handle
, MSIRECORD
*row
, UINT start
)
1020 UINT i
, count
, len
, r
= ERROR_SUCCESS
;
1026 buffer
= msi_alloc( len
);
1028 return ERROR_OUTOFMEMORY
;
1030 count
= MSI_RecordGetFieldCount( row
);
1031 for ( i
=start
; i
<=count
; i
++ )
1034 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
1035 if (r
== ERROR_MORE_DATA
)
1037 char *p
= msi_realloc( buffer
, sz
+ 1 );
1044 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
1045 if (r
!= ERROR_SUCCESS
)
1048 if (!WriteFile( handle
, buffer
, sz
, &sz
, NULL
))
1050 r
= ERROR_FUNCTION_FAILED
;
1054 sep
= (i
< count
) ? "\t" : "\r\n";
1055 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
1057 r
= ERROR_FUNCTION_FAILED
;
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
)
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
;
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
));
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
);
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
);
1159 CloseHandle( handle
);
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
)
1184 TRACE("%x %s %s %s\n", handle
, debugstr_w(szTable
),
1185 debugstr_w(szFolder
), debugstr_w(szFilename
));
1187 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
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
);
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
));
1218 table
= strdupAtoW( szTable
);
1225 path
= strdupAtoW( szFolder
);
1232 file
= strdupAtoW( szFilename
);
1237 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
1247 UINT WINAPI
MsiDatabaseMergeA(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1253 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1254 debugstr_a(szTableName
));
1256 table
= strdupAtoW(szTableName
);
1257 r
= MsiDatabaseMergeW(hDatabase
, hDatabaseMerge
, table
);
1263 typedef struct _tagMERGETABLE
1277 typedef struct _tagMERGEROW
1283 typedef struct _tagMERGEDATA
1287 MERGETABLE
*curtable
;
1289 struct list
*tabledata
;
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')))
1298 if (((type1
[0] == 'L') || (type1
[0] == 'S')) &&
1299 ((type2
[0] == 'L') || (type2
[0] == 'S')))
1302 return !lstrcmpW(type1
, type2
);
1305 static UINT
merge_verify_colnames(MSIQUERY
*dbview
, MSIQUERY
*mergeview
)
1307 MSIRECORD
*dbrec
, *mergerec
;
1310 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_NAMES
, &dbrec
);
1311 if (r
!= ERROR_SUCCESS
)
1314 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_NAMES
, &mergerec
);
1315 if (r
!= ERROR_SUCCESS
)
1318 count
= MSI_RecordGetFieldCount(dbrec
);
1319 for (i
= 1; i
<= count
; i
++)
1321 if (!MSI_RecordGetString(mergerec
, i
))
1324 if (lstrcmpW(MSI_RecordGetString(dbrec
, i
),
1325 MSI_RecordGetString(mergerec
, i
)))
1327 r
= ERROR_DATATYPE_MISMATCH
;
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
)
1340 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_TYPES
, &mergerec
);
1341 if (r
!= ERROR_SUCCESS
)
1344 count
= MSI_RecordGetFieldCount(dbrec
);
1345 for (i
= 1; i
<= count
; i
++)
1347 if (!MSI_RecordGetString(mergerec
, i
))
1350 if (!merge_type_match(MSI_RecordGetString(dbrec
, i
),
1351 MSI_RecordGetString(mergerec
, i
)))
1353 r
= ERROR_DATATYPE_MISMATCH
;
1359 msiobj_release(&dbrec
->hdr
);
1360 msiobj_release(&mergerec
->hdr
);
1365 static UINT
merge_verify_primary_keys(MSIDATABASE
*db
, MSIDATABASE
*mergedb
,
1368 MSIRECORD
*dbrec
, *mergerec
= NULL
;
1371 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &dbrec
);
1372 if (r
!= ERROR_SUCCESS
)
1375 r
= MSI_DatabaseGetPrimaryKeys(mergedb
, table
, &mergerec
);
1376 if (r
!= ERROR_SUCCESS
)
1379 count
= MSI_RecordGetFieldCount(dbrec
);
1380 if (count
!= MSI_RecordGetFieldCount(mergerec
))
1382 r
= ERROR_DATATYPE_MISMATCH
;
1386 for (i
= 1; i
<= count
; i
++)
1388 if (lstrcmpW(MSI_RecordGetString(dbrec
, i
),
1389 MSI_RecordGetString(mergerec
, i
)))
1391 r
= ERROR_DATATYPE_MISMATCH
;
1397 msiobj_release(&dbrec
->hdr
);
1398 msiobj_release(&mergerec
->hdr
);
1403 static LPWSTR
get_key_value(MSIQUERY
*view
, LPCWSTR key
, MSIRECORD
*rec
)
1405 MSIRECORD
*colnames
;
1407 UINT r
, i
= 0, sz
= 0;
1410 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &colnames
);
1411 if (r
!= ERROR_SUCCESS
)
1416 str
= msi_dup_record_field(colnames
, ++i
);
1417 cmp
= lstrcmpW(key
, str
);
1421 msiobj_release(&colnames
->hdr
);
1423 r
= MSI_RecordGetStringW(rec
, i
, NULL
, &sz
);
1424 if (r
!= ERROR_SUCCESS
)
1428 if (MSI_RecordGetString(rec
, i
)) /* check record field is a string */
1430 /* quote string record fields */
1431 const WCHAR szQuote
[] = {'\'', 0};
1433 val
= msi_alloc(sz
*sizeof(WCHAR
));
1437 lstrcpyW(val
, szQuote
);
1438 r
= MSI_RecordGetStringW(rec
, i
, val
+1, &sz
);
1439 lstrcpyW(val
+1+sz
, szQuote
);
1443 /* do not quote integer record fields */
1444 val
= msi_alloc(sz
*sizeof(WCHAR
));
1448 r
= MSI_RecordGetStringW(rec
, i
, val
, &sz
);
1451 if (r
!= ERROR_SUCCESS
)
1453 ERR("failed to get string!\n");
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
;
1467 DWORD size
= 1, oldsize
;
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
)
1484 clause
= msi_alloc_zero(size
* sizeof(WCHAR
));
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
);
1496 setptr
= lastkeyset
;
1501 size
+= lstrlenW(setptr
) + lstrlenW(key
) + lstrlenW(val
) - 4;
1502 clause
= msi_realloc(clause
, size
* sizeof (WCHAR
));
1509 ptr
= clause
+ oldsize
- 1;
1510 sprintfW(ptr
, setptr
, key
, val
);
1514 size
= lstrlenW(fmt
) + lstrlenW(table
) + lstrlenW(clause
) + 1;
1515 query
= msi_alloc(size
* sizeof(WCHAR
));
1519 sprintfW(query
, fmt
, table
, clause
);
1523 msiobj_release(&keys
->hdr
);
1527 static UINT
merge_diff_row(MSIRECORD
*rec
, LPVOID param
)
1529 MERGEDATA
*data
= param
;
1530 MERGETABLE
*table
= data
->curtable
;
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
);
1541 return ERROR_OUTOFMEMORY
;
1543 r
= MSI_DatabaseOpenViewW(data
->db
, query
, &dbview
);
1544 if (r
!= ERROR_SUCCESS
)
1547 r
= MSI_ViewExecute(dbview
, NULL
);
1548 if (r
!= ERROR_SUCCESS
)
1551 r
= MSI_ViewFetch(dbview
, &row
);
1552 if (r
== ERROR_SUCCESS
&& !MSI_RecordsAreEqual(rec
, row
))
1554 table
->numconflicts
++;
1557 else if (r
!= ERROR_NO_MORE_ITEMS
)
1563 mergerow
= msi_alloc(sizeof(MERGEROW
));
1566 r
= ERROR_OUTOFMEMORY
;
1570 mergerow
->data
= MSI_CloneRecord(rec
);
1571 if (!mergerow
->data
)
1573 r
= ERROR_OUTOFMEMORY
;
1578 list_add_tail(&table
->rows
, &mergerow
->entry
);
1582 msiobj_release(&row
->hdr
);
1583 msiobj_release(&dbview
->hdr
);
1587 static UINT
msi_get_table_labels(MSIDATABASE
*db
, LPCWSTR table
, LPWSTR
**labels
, DWORD
*numlabels
)
1590 MSIRECORD
*prec
= NULL
;
1592 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &prec
);
1593 if (r
!= ERROR_SUCCESS
)
1596 count
= MSI_RecordGetFieldCount(prec
);
1597 *numlabels
= count
+ 1;
1598 *labels
= msi_alloc((*numlabels
)*sizeof(LPWSTR
));
1601 r
= ERROR_OUTOFMEMORY
;
1605 (*labels
)[0] = strdupW(table
);
1606 for (i
=1; i
<=count
; i
++ )
1608 (*labels
)[i
] = strdupW(MSI_RecordGetString(prec
, i
));
1612 msiobj_release( &prec
->hdr
);
1616 static UINT
msi_get_query_columns(MSIQUERY
*query
, LPWSTR
**columns
, DWORD
*numcolumns
)
1619 MSIRECORD
*prec
= NULL
;
1621 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_NAMES
, &prec
);
1622 if (r
!= ERROR_SUCCESS
)
1625 count
= MSI_RecordGetFieldCount(prec
);
1626 *columns
= msi_alloc(count
*sizeof(LPWSTR
));
1629 r
= ERROR_OUTOFMEMORY
;
1633 for (i
=1; i
<=count
; i
++ )
1635 (*columns
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1638 *numcolumns
= count
;
1641 msiobj_release( &prec
->hdr
);
1645 static UINT
msi_get_query_types(MSIQUERY
*query
, LPWSTR
**types
, DWORD
*numtypes
)
1648 MSIRECORD
*prec
= NULL
;
1650 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_TYPES
, &prec
);
1651 if (r
!= ERROR_SUCCESS
)
1654 count
= MSI_RecordGetFieldCount(prec
);
1655 *types
= msi_alloc(count
*sizeof(LPWSTR
));
1658 r
= ERROR_OUTOFMEMORY
;
1663 for (i
=1; i
<=count
; i
++ )
1665 (*types
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1669 msiobj_release( &prec
->hdr
);
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
);
1687 static void free_merge_table(MERGETABLE
*table
)
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
);
1721 static UINT
msi_get_merge_table (MSIDATABASE
*db
, LPCWSTR name
, MERGETABLE
**ptable
)
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
));
1734 return ERROR_OUTOFMEMORY
;
1737 r
= msi_get_table_labels(db
, name
, &table
->labels
, &table
->numlabels
);
1738 if (r
!= ERROR_SUCCESS
)
1741 r
= MSI_OpenQuery(db
, &mergeview
, query
, name
);
1742 if (r
!= ERROR_SUCCESS
)
1745 r
= msi_get_query_columns(mergeview
, &table
->columns
, &table
->numcolumns
);
1746 if (r
!= ERROR_SUCCESS
)
1749 r
= msi_get_query_types(mergeview
, &table
->types
, &table
->numtypes
);
1750 if (r
!= ERROR_SUCCESS
)
1753 list_init(&table
->rows
);
1755 table
->name
= strdupW(name
);
1756 table
->numconflicts
= 0;
1758 msiobj_release(&mergeview
->hdr
);
1760 return ERROR_SUCCESS
;
1763 msiobj_release(&mergeview
->hdr
);
1764 free_merge_table(table
);
1769 static UINT
merge_diff_tables(MSIRECORD
*rec
, LPVOID param
)
1771 MERGEDATA
*data
= param
;
1773 MSIQUERY
*dbview
= NULL
;
1774 MSIQUERY
*mergeview
= NULL
;
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
)
1787 if (TABLE_Exists(data
->db
, name
))
1789 r
= MSI_OpenQuery(data
->db
, &dbview
, query
, name
);
1790 if (r
!= ERROR_SUCCESS
)
1793 r
= merge_verify_colnames(dbview
, mergeview
);
1794 if (r
!= ERROR_SUCCESS
)
1797 r
= merge_verify_primary_keys(data
->db
, data
->merge
, name
);
1798 if (r
!= ERROR_SUCCESS
)
1802 r
= msi_get_merge_table(data
->merge
, name
, &table
);
1803 if (r
!= ERROR_SUCCESS
)
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
);
1815 list_add_tail(data
->tabledata
, &table
->entry
);
1818 msiobj_release(&dbview
->hdr
);
1819 msiobj_release(&mergeview
->hdr
);
1823 static UINT
gather_merge_data(MSIDATABASE
*db
, MSIDATABASE
*merge
,
1824 struct list
*tabledata
)
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
)
1839 data
.tabledata
= tabledata
;
1840 r
= MSI_IterateRecords(view
, NULL
, merge_diff_tables
, &data
);
1842 msiobj_release(&view
->hdr
);
1846 static UINT
merge_table(MSIDATABASE
*db
, MERGETABLE
*table
)
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
)
1866 r
= tv
->ops
->insert_row(tv
, row
->data
, -1, FALSE
);
1867 tv
->ops
->delete(tv
);
1869 if (r
!= ERROR_SUCCESS
)
1873 return ERROR_SUCCESS
;
1876 static UINT
update_merge_errors(MSIDATABASE
*db
, LPCWSTR error
,
1877 LPWSTR table
, DWORD numconflicts
)
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
)
1903 r
= MSI_ViewExecute(view
, NULL
);
1904 msiobj_release(&view
->hdr
);
1905 if (r
!= ERROR_SUCCESS
)
1909 r
= MSI_OpenQuery(db
, &view
, insert
, error
, table
, numconflicts
);
1910 if (r
!= ERROR_SUCCESS
)
1913 r
= MSI_ViewExecute(view
, NULL
);
1914 msiobj_release(&view
->hdr
);
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
;
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
);
1938 r
= ERROR_INVALID_HANDLE
;
1942 r
= gather_merge_data(db
, merge
, &tabledata
);
1943 if (r
!= ERROR_SUCCESS
)
1947 LIST_FOR_EACH_ENTRY(table
, &tabledata
, MERGETABLE
, entry
)
1949 if (table
->numconflicts
)
1953 r
= update_merge_errors(db
, szTableName
, table
->name
,
1954 table
->numconflicts
);
1955 if (r
!= ERROR_SUCCESS
)
1960 r
= merge_table(db
, table
);
1961 if (r
!= ERROR_SUCCESS
)
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
);
1974 r
= ERROR_FUNCTION_FAILED
;
1977 msiobj_release(&db
->hdr
);
1978 msiobj_release(&merge
->hdr
);
1982 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
1984 MSIDBSTATE ret
= MSIDBSTATE_READ
;
1987 TRACE("%d\n", handle
);
1989 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
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
);
2011 typedef struct _msi_remote_database_impl
{
2012 const IWineMsiRemoteDatabaseVtbl
*lpVtbl
;
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
);
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
);
2048 r
= InterlockedDecrement( &This
->refs
);
2051 MsiCloseHandle( This
->database
);
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
);
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
;
2096 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl
=
2101 mrd_IsTablePersistent
,
2103 mrd_GetSummaryInformation
,
2108 HRESULT
create_msi_remote_database( IUnknown
*pOuter
, LPVOID
*ppObj
)
2110 msi_remote_database_impl
*This
;
2112 This
= msi_alloc( sizeof *This
);
2114 return E_OUTOFMEMORY
;
2116 This
->lpVtbl
= &msi_remote_database_vtbl
;