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
25 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
30 * An .msi file is a structured storage file.
31 * It contains a number of streams.
32 * A stream for each table in the database.
33 * Two streams for the string table in the database.
34 * Any binary data in a table is a reference to a stream.
37 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
39 struct row_export_info
46 static void free_transforms( MSIDATABASE
*db
)
48 while( !list_empty( &db
->transforms
) )
50 MSITRANSFORM
*t
= LIST_ENTRY( list_head( &db
->transforms
), MSITRANSFORM
, entry
);
51 list_remove( &t
->entry
);
52 IStorage_Release( t
->stg
);
57 static void free_streams( MSIDATABASE
*db
)
60 for (i
= 0; i
< db
->num_streams
; i
++)
62 if (db
->streams
[i
].stream
) IStream_Release( db
->streams
[i
].stream
);
64 msi_free( db
->streams
);
67 void append_storage_to_db( MSIDATABASE
*db
, IStorage
*stg
)
71 t
= msi_alloc( sizeof *t
);
73 IStorage_AddRef( stg
);
74 list_add_head( &db
->transforms
, &t
->entry
);
77 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
79 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
83 free_cached_tables( db
);
84 free_transforms( db
);
85 if (db
->strings
) msi_destroy_stringtable( db
->strings
);
86 IStorage_Release( db
->storage
);
89 DeleteFileW( db
->deletefile
);
90 msi_free( db
->deletefile
);
92 msi_free( db
->tempfolder
);
95 static HRESULT
db_initialize( IStorage
*stg
, const GUID
*clsid
)
97 static const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
100 hr
= IStorage_SetClass( stg
, clsid
);
103 WARN("failed to set class id 0x%08x\n", hr
);
107 /* create the _Tables stream */
108 hr
= write_stream_data( stg
, szTables
, NULL
, 0, TRUE
);
111 WARN("failed to create _Tables stream 0x%08x\n", hr
);
115 hr
= msi_init_string_table( stg
);
118 WARN("failed to initialize string table 0x%08x\n", hr
);
122 hr
= IStorage_Commit( stg
, 0 );
125 WARN("failed to commit changes 0x%08x\n", hr
);
132 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
134 IStorage
*stg
= NULL
;
136 MSIDATABASE
*db
= NULL
;
137 UINT ret
= ERROR_FUNCTION_FAILED
;
138 LPCWSTR szMode
, save_path
;
140 BOOL created
= FALSE
, patch
= FALSE
;
141 WCHAR path
[MAX_PATH
];
143 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
146 return ERROR_INVALID_PARAMETER
;
148 if (szPersist
- MSIDBOPEN_PATCHFILE
<= MSIDBOPEN_CREATEDIRECT
)
150 TRACE("Database is a patch\n");
151 szPersist
-= MSIDBOPEN_PATCHFILE
;
155 save_path
= szDBPath
;
157 if( !IS_INTMSIDBOPEN(szPersist
) )
159 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
160 return ERROR_OPEN_FAILED
;
162 szDBPath
= szPersist
;
163 szPersist
= MSIDBOPEN_TRANSACT
;
167 if( szPersist
== MSIDBOPEN_READONLY
)
169 r
= StgOpenStorage( szDBPath
, NULL
,
170 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
172 else if( szPersist
== MSIDBOPEN_CREATE
)
174 r
= StgCreateDocfile( szDBPath
,
175 STGM_CREATE
|STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
178 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
181 else if( szPersist
== MSIDBOPEN_CREATEDIRECT
)
183 r
= StgCreateDocfile( szDBPath
,
184 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
187 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
190 else if( szPersist
== MSIDBOPEN_TRANSACT
)
192 r
= StgOpenStorage( szDBPath
, NULL
,
193 STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
195 else if( szPersist
== MSIDBOPEN_DIRECT
)
197 r
= StgOpenStorage( szDBPath
, NULL
,
198 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
202 ERR("unknown flag %p\n",szPersist
);
203 return ERROR_INVALID_PARAMETER
;
206 if( FAILED( r
) || !stg
)
208 WARN("open failed r = %08x for %s\n", r
, debugstr_w(szDBPath
));
209 return ERROR_FUNCTION_FAILED
;
212 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
215 FIXME("Failed to stat storage\n");
219 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
220 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) &&
221 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiTransform
) )
223 ERR("storage GUID is not a MSI database GUID %s\n",
224 debugstr_guid(&stat
.clsid
) );
228 if ( patch
&& !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
230 ERR("storage GUID is not the MSI patch GUID %s\n",
231 debugstr_guid(&stat
.clsid
) );
232 ret
= ERROR_OPEN_FAILED
;
236 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
240 FIXME("Failed to allocate a handle\n");
244 if (!strchrW( save_path
, '\\' ))
246 GetCurrentDirectoryW( MAX_PATH
, path
);
247 lstrcatW( path
, szBackSlash
);
248 lstrcatW( path
, save_path
);
251 lstrcpyW( path
, save_path
);
253 db
->path
= strdupW( path
);
254 db
->media_transform_offset
= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
;
255 db
->media_transform_disk_id
= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
;
257 if( TRACE_ON( msi
) )
258 enum_stream_names( stg
);
263 db
->deletefile
= strdupW( szDBPath
);
264 list_init( &db
->tables
);
265 list_init( &db
->transforms
);
267 db
->strings
= msi_load_string_table( stg
, &db
->bytes_per_strref
);
273 msiobj_addref( &db
->hdr
);
274 IStorage_AddRef( stg
);
279 msiobj_release( &db
->hdr
);
281 IStorage_Release( stg
);
286 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
291 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
293 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
294 if( ret
== ERROR_SUCCESS
)
296 *phDB
= alloc_msihandle( &db
->hdr
);
298 ret
= ERROR_NOT_ENOUGH_MEMORY
;
299 msiobj_release( &db
->hdr
);
305 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
307 HRESULT r
= ERROR_FUNCTION_FAILED
;
308 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
310 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
314 szwDBPath
= strdupAtoW( szDBPath
);
319 if( !IS_INTMSIDBOPEN(szPersist
) )
321 szwPersist
= strdupAtoW( szPersist
);
326 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
328 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
331 if( !IS_INTMSIDBOPEN(szPersist
) )
332 msi_free( szwPersist
);
333 msi_free( szwDBPath
);
338 static LPWSTR
msi_read_text_archive(LPCWSTR path
, DWORD
*len
)
343 DWORD read
, size
= 0;
345 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
346 if (file
== INVALID_HANDLE_VALUE
)
349 size
= GetFileSize( file
, NULL
);
350 if (!(data
= msi_alloc( size
))) goto done
;
352 if (!ReadFile( file
, data
, size
, &read
, NULL
) || read
!= size
) goto done
;
354 while (!data
[size
- 1]) size
--;
355 *len
= MultiByteToWideChar( CP_ACP
, 0, data
, size
, NULL
, 0 );
356 if ((wdata
= msi_alloc( (*len
+ 1) * sizeof(WCHAR
) )))
358 MultiByteToWideChar( CP_ACP
, 0, data
, size
, wdata
, *len
);
368 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
, DWORD
*len
)
370 LPWSTR ptr
= *line
, save
;
371 DWORD i
, count
= 1, chars_left
= *len
;
375 /* stay on this line */
376 while (chars_left
&& *ptr
!= '\n')
378 /* entries are separated by tabs */
386 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
390 /* store pointers into the data */
392 for (i
= 0, ptr
= *line
; i
< count
; i
++)
394 while (chars_left
&& *ptr
== '\r')
401 while (chars_left
&& *ptr
!= '\t' && *ptr
!= '\n' && *ptr
!= '\r')
403 if (!*ptr
) *ptr
= '\n'; /* convert embedded nulls to \n */
404 if (ptr
> *line
&& *ptr
== '\x19' && *(ptr
- 1) == '\x11')
413 /* NULL-separate the data */
414 if (*ptr
== '\n' || *ptr
== '\r')
416 while (chars_left
&& (*ptr
== '\n' || *ptr
== '\r'))
427 (*entries
)[i
] = save
;
430 /* move to the next line if there's more, else EOF */
434 *num_entries
= count
;
437 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
442 static const WCHAR create_fmt
[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
444 size
= sizeof(create_fmt
)/sizeof(create_fmt
[0]) + lstrlenW(table
) - 2;
445 prelude
= msi_alloc(size
* sizeof(WCHAR
));
449 sprintfW(prelude
, create_fmt
, table
);
453 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
457 DWORD sql_size
= 1, i
, len
;
458 WCHAR expanded
[128], *ptr
;
459 WCHAR size
[10], comma
[2], extra
[30];
461 static const WCHAR column_fmt
[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
462 static const WCHAR size_fmt
[] = {'(','%','s',')',0};
463 static const WCHAR type_char
[] = {'C','H','A','R',0};
464 static const WCHAR type_int
[] = {'I','N','T',0};
465 static const WCHAR type_long
[] = {'L','O','N','G',0};
466 static const WCHAR type_object
[] = {'O','B','J','E','C','T',0};
467 static const WCHAR type_notnull
[] = {' ','N','O','T',' ','N','U','L','L',0};
468 static const WCHAR localizable
[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
470 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
474 for (i
= 0; i
< num_columns
; i
++)
477 comma
[1] = size
[0] = extra
[0] = '\0';
479 if (i
== num_columns
- 1)
491 lstrcpyW(extra
, type_notnull
);
494 lstrcatW(extra
, localizable
);
496 sprintfW(size
, size_fmt
, ptr
);
499 lstrcpyW(extra
, type_notnull
);
503 sprintfW(size
, size_fmt
, ptr
);
506 lstrcpyW(extra
, type_notnull
);
515 WARN("invalid int width %u\n", len
);
521 lstrcpyW(extra
, type_notnull
);
527 ERR("Unknown type: %c\n", types
[i
][0]);
532 sprintfW(expanded
, column_fmt
, columns_data
[i
], type
, size
, extra
, comma
);
533 sql_size
+= lstrlenW(expanded
);
535 p
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
543 lstrcatW(columns
, expanded
);
549 static LPWSTR
msi_build_createsql_postlude(LPWSTR
*primary_keys
, DWORD num_keys
)
551 LPWSTR postlude
, keys
, ptr
;
552 DWORD size
, key_size
, i
;
554 static const WCHAR key_fmt
[] = {'`','%','s','`',',',' ',0};
555 static const WCHAR postlude_fmt
[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
557 for (i
= 0, size
= 1; i
< num_keys
; i
++)
558 size
+= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) - 2;
560 keys
= msi_alloc(size
* sizeof(WCHAR
));
564 for (i
= 0, ptr
= keys
; i
< num_keys
; i
++)
566 key_size
= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) -2;
567 sprintfW(ptr
, key_fmt
, primary_keys
[i
]);
571 /* remove final ', ' */
574 size
= lstrlenW(postlude_fmt
) + size
- 1;
575 postlude
= msi_alloc(size
* sizeof(WCHAR
));
579 sprintfW(postlude
, postlude_fmt
, keys
);
586 static UINT
msi_add_table_to_db(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
, LPWSTR
*labels
, DWORD num_labels
, DWORD num_columns
)
588 UINT r
= ERROR_OUTOFMEMORY
;
591 LPWSTR create_sql
= NULL
;
592 LPWSTR prelude
, columns_sql
, postlude
;
594 prelude
= msi_build_createsql_prelude(labels
[0]);
595 columns_sql
= msi_build_createsql_columns(columns
, types
, num_columns
);
596 postlude
= msi_build_createsql_postlude(labels
+ 1, num_labels
- 1); /* skip over table name */
598 if (!prelude
|| !columns_sql
|| !postlude
)
601 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
602 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
606 lstrcpyW(create_sql
, prelude
);
607 lstrcatW(create_sql
, columns_sql
);
608 lstrcatW(create_sql
, postlude
);
610 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
611 if (r
!= ERROR_SUCCESS
)
614 r
= MSI_ViewExecute(view
, NULL
);
616 msiobj_release(&view
->hdr
);
620 msi_free(columns_sql
);
622 msi_free(create_sql
);
626 static LPWSTR
msi_import_stream_filename(LPCWSTR path
, LPCWSTR name
)
629 LPWSTR fullname
, ptr
;
631 len
= lstrlenW(path
) + lstrlenW(name
) + 1;
632 fullname
= msi_alloc(len
*sizeof(WCHAR
));
636 lstrcpyW( fullname
, path
);
638 /* chop off extension from path */
639 ptr
= strrchrW(fullname
, '.');
646 lstrcpyW( ptr
, name
);
650 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
651 LPWSTR
*data
, LPWSTR path
, MSIRECORD
**rec
)
655 *rec
= MSI_CreateRecord(num_columns
);
657 return ERROR_OUTOFMEMORY
;
659 for (i
= 0; i
< num_columns
; i
++)
663 case 'L': case 'l': case 'S': case 's':
664 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
668 MSI_RecordSetInteger(*rec
, i
+ 1, atoiW(data
[i
]));
674 LPWSTR file
= msi_import_stream_filename(path
, data
[i
]);
676 return ERROR_FUNCTION_FAILED
;
678 r
= MSI_RecordSetStreamFromFileW(*rec
, i
+ 1, file
);
680 if (r
!= ERROR_SUCCESS
)
681 return ERROR_FUNCTION_FAILED
;
685 ERR("Unhandled column type: %c\n", types
[i
][0]);
686 msiobj_release(&(*rec
)->hdr
);
687 return ERROR_FUNCTION_FAILED
;
691 return ERROR_SUCCESS
;
694 static UINT
msi_add_records_to_table(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
,
695 LPWSTR
*labels
, LPWSTR
**records
,
696 int num_columns
, int num_records
,
704 static const WCHAR select
[] = {
705 'S','E','L','E','C','T',' ','*',' ',
706 'F','R','O','M',' ','`','%','s','`',0
709 r
= MSI_OpenQuery(db
, &view
, select
, labels
[0]);
710 if (r
!= ERROR_SUCCESS
)
713 while (MSI_ViewFetch(view
, &rec
) != ERROR_NO_MORE_ITEMS
)
715 r
= MSI_ViewModify(view
, MSIMODIFY_DELETE
, rec
);
716 msiobj_release(&rec
->hdr
);
717 if (r
!= ERROR_SUCCESS
)
721 for (i
= 0; i
< num_records
; i
++)
723 r
= construct_record(num_columns
, types
, records
[i
], path
, &rec
);
724 if (r
!= ERROR_SUCCESS
)
727 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
728 if (r
!= ERROR_SUCCESS
)
730 msiobj_release(&rec
->hdr
);
734 msiobj_release(&rec
->hdr
);
738 msiobj_release(&view
->hdr
);
742 static UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
746 DWORD num_labels
, num_types
;
747 DWORD num_columns
, num_records
= 0;
748 LPWSTR
*columns
, *types
, *labels
;
749 LPWSTR path
, ptr
, data
;
750 LPWSTR
**records
= NULL
;
751 LPWSTR
**temp_records
;
753 static const WCHAR suminfo
[] =
754 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
755 static const WCHAR forcecodepage
[] =
756 {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
758 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
760 if( folder
== NULL
|| file
== NULL
)
761 return ERROR_INVALID_PARAMETER
;
763 len
= lstrlenW(folder
) + lstrlenW(szBackSlash
) + lstrlenW(file
) + 1;
764 path
= msi_alloc( len
* sizeof(WCHAR
) );
766 return ERROR_OUTOFMEMORY
;
768 lstrcpyW( path
, folder
);
769 lstrcatW( path
, szBackSlash
);
770 lstrcatW( path
, file
);
772 data
= msi_read_text_archive( path
, &len
);
774 return ERROR_BAD_PATHNAME
;
777 msi_parse_line( &ptr
, &columns
, &num_columns
, &len
);
778 msi_parse_line( &ptr
, &types
, &num_types
, &len
);
779 msi_parse_line( &ptr
, &labels
, &num_labels
, &len
);
781 if (num_columns
== 1 && !columns
[0][0] && num_labels
== 1 && !labels
[0][0] &&
782 num_types
== 2 && !strcmpW( types
[1], forcecodepage
))
784 r
= msi_set_string_table_codepage( db
->strings
, atoiW( types
[0] ) );
788 if (num_columns
!= num_types
)
790 r
= ERROR_FUNCTION_FAILED
;
794 records
= msi_alloc(sizeof(LPWSTR
*));
797 r
= ERROR_OUTOFMEMORY
;
801 /* read in the table records */
804 msi_parse_line( &ptr
, &records
[num_records
], NULL
, &len
);
807 temp_records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
810 r
= ERROR_OUTOFMEMORY
;
813 records
= temp_records
;
816 if (!strcmpW(labels
[0], suminfo
))
818 r
= msi_add_suminfo( db
, records
, num_records
, num_columns
);
819 if (r
!= ERROR_SUCCESS
)
821 r
= ERROR_FUNCTION_FAILED
;
827 if (!TABLE_Exists(db
, labels
[0]))
829 r
= msi_add_table_to_db( db
, columns
, types
, labels
, num_labels
, num_columns
);
830 if (r
!= ERROR_SUCCESS
)
832 r
= ERROR_FUNCTION_FAILED
;
837 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
, path
);
847 for (i
= 0; i
< num_records
; i
++)
848 msi_free(records
[i
]);
855 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
860 TRACE("%x %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
862 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
865 IWineMsiRemoteDatabase
*remote_database
;
867 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
868 if ( !remote_database
)
869 return ERROR_INVALID_HANDLE
;
871 IWineMsiRemoteDatabase_Release( remote_database
);
872 WARN("MsiDatabaseImport not allowed during a custom action!\n");
874 return ERROR_SUCCESS
;
877 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
878 msiobj_release( &db
->hdr
);
882 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
883 LPCSTR szFolder
, LPCSTR szFilename
)
885 LPWSTR path
= NULL
, file
= NULL
;
886 UINT r
= ERROR_OUTOFMEMORY
;
888 TRACE("%x %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
892 path
= strdupAtoW( szFolder
);
899 file
= strdupAtoW( szFilename
);
904 r
= MsiDatabaseImportW( handle
, path
, file
);
913 static UINT
msi_export_field( HANDLE handle
, MSIRECORD
*row
, UINT field
)
921 buffer
= msi_alloc( sz
);
923 return ERROR_OUTOFMEMORY
;
925 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
926 if (r
== ERROR_MORE_DATA
)
930 sz
++; /* leave room for NULL terminator */
931 p
= msi_realloc( buffer
, sz
);
935 return ERROR_OUTOFMEMORY
;
939 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
940 if (r
!= ERROR_SUCCESS
)
946 else if (r
!= ERROR_SUCCESS
)
949 bret
= WriteFile( handle
, buffer
, sz
, &sz
, NULL
);
952 return ERROR_FUNCTION_FAILED
;
957 static UINT
msi_export_stream( LPCWSTR folder
, LPCWSTR table
, MSIRECORD
*row
, UINT field
,
960 static const WCHAR fmt_file
[] = { '%','s','/','%','s','/','%','s',0 };
961 static const WCHAR fmt_folder
[] = { '%','s','/','%','s',0 };
962 WCHAR stream_name
[256], stream_filename
[MAX_PATH
];
963 DWORD sz
, read_size
, write_size
;
968 /* get the name of the file */
969 sz
= sizeof(stream_name
)/sizeof(WCHAR
);
970 r
= MSI_RecordGetStringW( row
, start
, stream_name
, &sz
);
971 if (r
!= ERROR_SUCCESS
)
974 /* if the destination folder does not exist then create it (folder name = table name) */
975 snprintfW( stream_filename
, sizeof(stream_filename
), fmt_folder
, folder
, table
);
976 if (GetFileAttributesW( stream_filename
) == INVALID_FILE_ATTRIBUTES
)
978 if (!CreateDirectoryW( stream_filename
, NULL
))
979 return ERROR_PATH_NOT_FOUND
;
982 /* actually create the file */
983 snprintfW( stream_filename
, sizeof(stream_filename
), fmt_file
, folder
, table
, stream_name
);
984 file
= CreateFileW( stream_filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
985 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
986 if (file
== INVALID_HANDLE_VALUE
)
987 return ERROR_FILE_NOT_FOUND
;
989 /* copy the stream to the file */
990 read_size
= sizeof(buffer
);
991 while (read_size
== sizeof(buffer
))
993 r
= MSI_RecordReadStream( row
, field
, buffer
, &read_size
);
994 if (r
!= ERROR_SUCCESS
)
999 if (!WriteFile( file
, buffer
, read_size
, &write_size
, NULL
) || read_size
!= write_size
)
1001 CloseHandle( file
);
1002 return ERROR_WRITE_FAULT
;
1005 CloseHandle( file
);
1009 static UINT
msi_export_record( struct row_export_info
*row_export_info
, MSIRECORD
*row
, UINT start
)
1011 HANDLE handle
= row_export_info
->handle
;
1012 UINT i
, count
, r
= ERROR_SUCCESS
;
1016 count
= MSI_RecordGetFieldCount( row
);
1017 for (i
= start
; i
<= count
; i
++)
1019 r
= msi_export_field( handle
, row
, i
);
1020 if (r
== ERROR_INVALID_PARAMETER
)
1022 r
= msi_export_stream( row_export_info
->folder
, row_export_info
->table
, row
, i
, start
);
1023 if (r
!= ERROR_SUCCESS
)
1026 /* exporting a binary stream, repeat the "Name" field */
1027 r
= msi_export_field( handle
, row
, start
);
1028 if (r
!= ERROR_SUCCESS
)
1031 else if (r
!= ERROR_SUCCESS
)
1034 sep
= (i
< count
) ? "\t" : "\r\n";
1035 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
1036 return ERROR_FUNCTION_FAILED
;
1041 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
1043 return msi_export_record( arg
, row
, 1 );
1046 static UINT
msi_export_forcecodepage( HANDLE handle
, UINT codepage
)
1048 static const char fmt
[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1049 char data
[sizeof(fmt
) + 10];
1052 sprintf( data
, fmt
, codepage
);
1054 sz
= lstrlenA(data
) + 1;
1055 if (!WriteFile(handle
, data
, sz
, &sz
, NULL
))
1056 return ERROR_FUNCTION_FAILED
;
1058 return ERROR_SUCCESS
;
1061 static UINT
msi_export_summaryinformation( MSIDATABASE
*db
, HANDLE handle
)
1063 static const char header
[] = "PropertyId\tValue\r\n"
1065 "_SummaryInformation\tPropertyId\r\n";
1068 sz
= lstrlenA(header
);
1069 if (!WriteFile(handle
, header
, sz
, &sz
, NULL
))
1070 return ERROR_WRITE_FAULT
;
1072 return msi_export_suminfo( db
, handle
);
1075 static UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
,
1076 LPCWSTR folder
, LPCWSTR file
)
1078 static const WCHAR summaryinformation
[] = {
1079 '_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
1080 static const WCHAR query
[] = {
1081 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1082 static const WCHAR forcecodepage
[] = {
1083 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1084 MSIRECORD
*rec
= NULL
;
1085 MSIQUERY
*view
= NULL
;
1090 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
1091 debugstr_w(folder
), debugstr_w(file
) );
1093 if( folder
== NULL
|| file
== NULL
)
1094 return ERROR_INVALID_PARAMETER
;
1096 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
1097 filename
= msi_alloc(len
* sizeof (WCHAR
));
1099 return ERROR_OUTOFMEMORY
;
1101 lstrcpyW( filename
, folder
);
1102 lstrcatW( filename
, szBackSlash
);
1103 lstrcatW( filename
, file
);
1105 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
1106 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1107 msi_free( filename
);
1108 if (handle
== INVALID_HANDLE_VALUE
)
1109 return ERROR_FUNCTION_FAILED
;
1111 if (!strcmpW( table
, forcecodepage
))
1113 UINT codepage
= msi_get_string_table_codepage( db
->strings
);
1114 r
= msi_export_forcecodepage( handle
, codepage
);
1118 if (!strcmpW( table
, summaryinformation
))
1120 r
= msi_export_summaryinformation( db
, handle
);
1124 r
= MSI_OpenQuery( db
, &view
, query
, table
);
1125 if (r
== ERROR_SUCCESS
)
1127 struct row_export_info row_export_info
= { handle
, folder
, table
};
1129 /* write out row 1, the column names */
1130 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
1131 if (r
== ERROR_SUCCESS
)
1133 msi_export_record( &row_export_info
, rec
, 1 );
1134 msiobj_release( &rec
->hdr
);
1137 /* write out row 2, the column types */
1138 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
1139 if (r
== ERROR_SUCCESS
)
1141 msi_export_record( &row_export_info
, rec
, 1 );
1142 msiobj_release( &rec
->hdr
);
1145 /* write out row 3, the table name + keys */
1146 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
1147 if (r
== ERROR_SUCCESS
)
1149 MSI_RecordSetStringW( rec
, 0, table
);
1150 msi_export_record( &row_export_info
, rec
, 0 );
1151 msiobj_release( &rec
->hdr
);
1154 /* write out row 4 onwards, the data */
1155 r
= MSI_IterateRecords( view
, 0, msi_export_row
, &row_export_info
);
1156 msiobj_release( &view
->hdr
);
1160 CloseHandle( handle
);
1164 /***********************************************************************
1165 * MsiExportDatabaseW [MSI.@]
1167 * Writes a file containing the table data as tab separated ASCII.
1169 * The format is as follows:
1171 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1172 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1173 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1175 * Followed by the data, starting at row 1 with one row per line
1177 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1179 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
1180 LPCWSTR szFolder
, LPCWSTR szFilename
)
1185 TRACE("%x %s %s %s\n", handle
, debugstr_w(szTable
),
1186 debugstr_w(szFolder
), debugstr_w(szFilename
));
1188 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
1191 IWineMsiRemoteDatabase
*remote_database
;
1193 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
1194 if ( !remote_database
)
1195 return ERROR_INVALID_HANDLE
;
1197 IWineMsiRemoteDatabase_Release( remote_database
);
1198 WARN("MsiDatabaseExport not allowed during a custom action!\n");
1200 return ERROR_SUCCESS
;
1203 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
1204 msiobj_release( &db
->hdr
);
1208 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
1209 LPCSTR szFolder
, LPCSTR szFilename
)
1211 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
1212 UINT r
= ERROR_OUTOFMEMORY
;
1214 TRACE("%x %s %s %s\n", handle
, debugstr_a(szTable
),
1215 debugstr_a(szFolder
), debugstr_a(szFilename
));
1219 table
= strdupAtoW( szTable
);
1226 path
= strdupAtoW( szFolder
);
1233 file
= strdupAtoW( szFilename
);
1238 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
1248 UINT WINAPI
MsiDatabaseMergeA(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1254 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1255 debugstr_a(szTableName
));
1257 table
= strdupAtoW(szTableName
);
1258 r
= MsiDatabaseMergeW(hDatabase
, hDatabaseMerge
, table
);
1264 typedef struct _tagMERGETABLE
1278 typedef struct _tagMERGEROW
1284 typedef struct _tagMERGEDATA
1288 MERGETABLE
*curtable
;
1290 struct list
*tabledata
;
1293 static BOOL
merge_type_match(LPCWSTR type1
, LPCWSTR type2
)
1295 if (((type1
[0] == 'l') || (type1
[0] == 's')) &&
1296 ((type2
[0] == 'l') || (type2
[0] == 's')))
1299 if (((type1
[0] == 'L') || (type1
[0] == 'S')) &&
1300 ((type2
[0] == 'L') || (type2
[0] == 'S')))
1303 return !strcmpW( type1
, type2
);
1306 static UINT
merge_verify_colnames(MSIQUERY
*dbview
, MSIQUERY
*mergeview
)
1308 MSIRECORD
*dbrec
, *mergerec
;
1311 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_NAMES
, &dbrec
);
1312 if (r
!= ERROR_SUCCESS
)
1315 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_NAMES
, &mergerec
);
1316 if (r
!= ERROR_SUCCESS
)
1318 msiobj_release(&dbrec
->hdr
);
1322 count
= MSI_RecordGetFieldCount(dbrec
);
1323 for (i
= 1; i
<= count
; i
++)
1325 if (!MSI_RecordGetString(mergerec
, i
))
1328 if (strcmpW( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1330 r
= ERROR_DATATYPE_MISMATCH
;
1335 msiobj_release(&dbrec
->hdr
);
1336 msiobj_release(&mergerec
->hdr
);
1337 dbrec
= mergerec
= NULL
;
1339 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_TYPES
, &dbrec
);
1340 if (r
!= ERROR_SUCCESS
)
1343 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_TYPES
, &mergerec
);
1344 if (r
!= ERROR_SUCCESS
)
1346 msiobj_release(&dbrec
->hdr
);
1350 count
= MSI_RecordGetFieldCount(dbrec
);
1351 for (i
= 1; i
<= count
; i
++)
1353 if (!MSI_RecordGetString(mergerec
, i
))
1356 if (!merge_type_match(MSI_RecordGetString(dbrec
, i
),
1357 MSI_RecordGetString(mergerec
, i
)))
1359 r
= ERROR_DATATYPE_MISMATCH
;
1365 msiobj_release(&dbrec
->hdr
);
1366 msiobj_release(&mergerec
->hdr
);
1371 static UINT
merge_verify_primary_keys(MSIDATABASE
*db
, MSIDATABASE
*mergedb
,
1374 MSIRECORD
*dbrec
, *mergerec
= NULL
;
1377 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &dbrec
);
1378 if (r
!= ERROR_SUCCESS
)
1381 r
= MSI_DatabaseGetPrimaryKeys(mergedb
, table
, &mergerec
);
1382 if (r
!= ERROR_SUCCESS
)
1385 count
= MSI_RecordGetFieldCount(dbrec
);
1386 if (count
!= MSI_RecordGetFieldCount(mergerec
))
1388 r
= ERROR_DATATYPE_MISMATCH
;
1392 for (i
= 1; i
<= count
; i
++)
1394 if (strcmpW( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1396 r
= ERROR_DATATYPE_MISMATCH
;
1402 msiobj_release(&dbrec
->hdr
);
1403 msiobj_release(&mergerec
->hdr
);
1408 static LPWSTR
get_key_value(MSIQUERY
*view
, LPCWSTR key
, MSIRECORD
*rec
)
1410 MSIRECORD
*colnames
;
1412 UINT r
, i
= 0, sz
= 0;
1415 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &colnames
);
1416 if (r
!= ERROR_SUCCESS
)
1421 str
= msi_dup_record_field(colnames
, ++i
);
1422 cmp
= strcmpW( key
, str
);
1426 msiobj_release(&colnames
->hdr
);
1428 r
= MSI_RecordGetStringW(rec
, i
, NULL
, &sz
);
1429 if (r
!= ERROR_SUCCESS
)
1433 if (MSI_RecordGetString(rec
, i
)) /* check record field is a string */
1435 /* quote string record fields */
1436 const WCHAR szQuote
[] = {'\'', 0};
1438 val
= msi_alloc(sz
*sizeof(WCHAR
));
1442 lstrcpyW(val
, szQuote
);
1443 r
= MSI_RecordGetStringW(rec
, i
, val
+1, &sz
);
1444 lstrcpyW(val
+1+sz
, szQuote
);
1448 /* do not quote integer record fields */
1449 val
= msi_alloc(sz
*sizeof(WCHAR
));
1453 r
= MSI_RecordGetStringW(rec
, i
, val
, &sz
);
1456 if (r
!= ERROR_SUCCESS
)
1458 ERR("failed to get string!\n");
1466 static LPWSTR
create_diff_row_query(MSIDATABASE
*merge
, MSIQUERY
*view
,
1467 LPWSTR table
, MSIRECORD
*rec
)
1469 LPWSTR query
= NULL
, clause
= NULL
, val
;
1470 LPCWSTR setptr
, key
;
1471 DWORD size
, oldsize
;
1475 static const WCHAR keyset
[] = {
1476 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1477 static const WCHAR lastkeyset
[] = {
1478 '`','%','s','`',' ','=',' ','%','s',' ',0};
1479 static const WCHAR fmt
[] = {'S','E','L','E','C','T',' ','*',' ',
1480 'F','R','O','M',' ','`','%','s','`',' ',
1481 'W','H','E','R','E',' ','%','s',0};
1483 r
= MSI_DatabaseGetPrimaryKeys(merge
, table
, &keys
);
1484 if (r
!= ERROR_SUCCESS
)
1487 clause
= msi_alloc_zero(sizeof(WCHAR
));
1492 count
= MSI_RecordGetFieldCount(keys
);
1493 for (i
= 1; i
<= count
; i
++)
1495 key
= MSI_RecordGetString(keys
, i
);
1496 val
= get_key_value(view
, key
, rec
);
1499 setptr
= lastkeyset
;
1504 size
+= lstrlenW(setptr
) + lstrlenW(key
) + lstrlenW(val
) - 4;
1505 clause
= msi_realloc(clause
, size
* sizeof (WCHAR
));
1512 sprintfW(clause
+ oldsize
- 1, setptr
, key
, val
);
1516 size
= lstrlenW(fmt
) + lstrlenW(table
) + lstrlenW(clause
) + 1;
1517 query
= msi_alloc(size
* sizeof(WCHAR
));
1521 sprintfW(query
, fmt
, table
, clause
);
1525 msiobj_release(&keys
->hdr
);
1529 static UINT
merge_diff_row(MSIRECORD
*rec
, LPVOID param
)
1531 MERGEDATA
*data
= param
;
1532 MERGETABLE
*table
= data
->curtable
;
1534 MSIQUERY
*dbview
= NULL
;
1535 MSIRECORD
*row
= NULL
;
1536 LPWSTR query
= NULL
;
1537 UINT r
= ERROR_SUCCESS
;
1539 if (TABLE_Exists(data
->db
, table
->name
))
1541 query
= create_diff_row_query(data
->merge
, data
->curview
, table
->name
, rec
);
1543 return ERROR_OUTOFMEMORY
;
1545 r
= MSI_DatabaseOpenViewW(data
->db
, query
, &dbview
);
1546 if (r
!= ERROR_SUCCESS
)
1549 r
= MSI_ViewExecute(dbview
, NULL
);
1550 if (r
!= ERROR_SUCCESS
)
1553 r
= MSI_ViewFetch(dbview
, &row
);
1554 if (r
== ERROR_SUCCESS
&& !MSI_RecordsAreEqual(rec
, row
))
1556 table
->numconflicts
++;
1559 else if (r
!= ERROR_NO_MORE_ITEMS
)
1565 mergerow
= msi_alloc(sizeof(MERGEROW
));
1568 r
= ERROR_OUTOFMEMORY
;
1572 mergerow
->data
= MSI_CloneRecord(rec
);
1573 if (!mergerow
->data
)
1575 r
= ERROR_OUTOFMEMORY
;
1580 list_add_tail(&table
->rows
, &mergerow
->entry
);
1584 msiobj_release(&row
->hdr
);
1585 msiobj_release(&dbview
->hdr
);
1589 static UINT
msi_get_table_labels(MSIDATABASE
*db
, LPCWSTR table
, LPWSTR
**labels
, DWORD
*numlabels
)
1592 MSIRECORD
*prec
= NULL
;
1594 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &prec
);
1595 if (r
!= ERROR_SUCCESS
)
1598 count
= MSI_RecordGetFieldCount(prec
);
1599 *numlabels
= count
+ 1;
1600 *labels
= msi_alloc((*numlabels
)*sizeof(LPWSTR
));
1603 r
= ERROR_OUTOFMEMORY
;
1607 (*labels
)[0] = strdupW(table
);
1608 for (i
=1; i
<=count
; i
++ )
1610 (*labels
)[i
] = strdupW(MSI_RecordGetString(prec
, i
));
1614 msiobj_release( &prec
->hdr
);
1618 static UINT
msi_get_query_columns(MSIQUERY
*query
, LPWSTR
**columns
, DWORD
*numcolumns
)
1621 MSIRECORD
*prec
= NULL
;
1623 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_NAMES
, &prec
);
1624 if (r
!= ERROR_SUCCESS
)
1627 count
= MSI_RecordGetFieldCount(prec
);
1628 *columns
= msi_alloc(count
*sizeof(LPWSTR
));
1631 r
= ERROR_OUTOFMEMORY
;
1635 for (i
=1; i
<=count
; i
++ )
1637 (*columns
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1640 *numcolumns
= count
;
1643 msiobj_release( &prec
->hdr
);
1647 static UINT
msi_get_query_types(MSIQUERY
*query
, LPWSTR
**types
, DWORD
*numtypes
)
1650 MSIRECORD
*prec
= NULL
;
1652 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_TYPES
, &prec
);
1653 if (r
!= ERROR_SUCCESS
)
1656 count
= MSI_RecordGetFieldCount(prec
);
1657 *types
= msi_alloc(count
*sizeof(LPWSTR
));
1660 r
= ERROR_OUTOFMEMORY
;
1665 for (i
=1; i
<=count
; i
++ )
1667 (*types
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1671 msiobj_release( &prec
->hdr
);
1675 static void merge_free_rows(MERGETABLE
*table
)
1677 struct list
*item
, *cursor
;
1679 LIST_FOR_EACH_SAFE(item
, cursor
, &table
->rows
)
1681 MERGEROW
*row
= LIST_ENTRY(item
, MERGEROW
, entry
);
1683 list_remove(&row
->entry
);
1684 msiobj_release(&row
->data
->hdr
);
1689 static void free_merge_table(MERGETABLE
*table
)
1693 if (table
->labels
!= NULL
)
1695 for (i
= 0; i
< table
->numlabels
; i
++)
1696 msi_free(table
->labels
[i
]);
1698 msi_free(table
->labels
);
1701 if (table
->columns
!= NULL
)
1703 for (i
= 0; i
< table
->numcolumns
; i
++)
1704 msi_free(table
->columns
[i
]);
1706 msi_free(table
->columns
);
1709 if (table
->types
!= NULL
)
1711 for (i
= 0; i
< table
->numtypes
; i
++)
1712 msi_free(table
->types
[i
]);
1714 msi_free(table
->types
);
1717 msi_free(table
->name
);
1718 merge_free_rows(table
);
1723 static UINT
msi_get_merge_table (MSIDATABASE
*db
, LPCWSTR name
, MERGETABLE
**ptable
)
1727 MSIQUERY
*mergeview
= NULL
;
1729 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1730 'F','R','O','M',' ','`','%','s','`',0};
1732 table
= msi_alloc_zero(sizeof(MERGETABLE
));
1736 return ERROR_OUTOFMEMORY
;
1739 r
= msi_get_table_labels(db
, name
, &table
->labels
, &table
->numlabels
);
1740 if (r
!= ERROR_SUCCESS
)
1743 r
= MSI_OpenQuery(db
, &mergeview
, query
, name
);
1744 if (r
!= ERROR_SUCCESS
)
1747 r
= msi_get_query_columns(mergeview
, &table
->columns
, &table
->numcolumns
);
1748 if (r
!= ERROR_SUCCESS
)
1751 r
= msi_get_query_types(mergeview
, &table
->types
, &table
->numtypes
);
1752 if (r
!= ERROR_SUCCESS
)
1755 list_init(&table
->rows
);
1757 table
->name
= strdupW(name
);
1758 table
->numconflicts
= 0;
1760 msiobj_release(&mergeview
->hdr
);
1762 return ERROR_SUCCESS
;
1765 msiobj_release(&mergeview
->hdr
);
1766 free_merge_table(table
);
1771 static UINT
merge_diff_tables(MSIRECORD
*rec
, LPVOID param
)
1773 MERGEDATA
*data
= param
;
1775 MSIQUERY
*dbview
= NULL
;
1776 MSIQUERY
*mergeview
= NULL
;
1780 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1781 'F','R','O','M',' ','`','%','s','`',0};
1783 name
= MSI_RecordGetString(rec
, 1);
1785 r
= MSI_OpenQuery(data
->merge
, &mergeview
, query
, name
);
1786 if (r
!= ERROR_SUCCESS
)
1789 if (TABLE_Exists(data
->db
, name
))
1791 r
= MSI_OpenQuery(data
->db
, &dbview
, query
, name
);
1792 if (r
!= ERROR_SUCCESS
)
1795 r
= merge_verify_colnames(dbview
, mergeview
);
1796 if (r
!= ERROR_SUCCESS
)
1799 r
= merge_verify_primary_keys(data
->db
, data
->merge
, name
);
1800 if (r
!= ERROR_SUCCESS
)
1804 r
= msi_get_merge_table(data
->merge
, name
, &table
);
1805 if (r
!= ERROR_SUCCESS
)
1808 data
->curtable
= table
;
1809 data
->curview
= mergeview
;
1810 r
= MSI_IterateRecords(mergeview
, NULL
, merge_diff_row
, data
);
1811 if (r
!= ERROR_SUCCESS
)
1813 free_merge_table(table
);
1817 list_add_tail(data
->tabledata
, &table
->entry
);
1820 msiobj_release(&dbview
->hdr
);
1821 msiobj_release(&mergeview
->hdr
);
1825 static UINT
gather_merge_data(MSIDATABASE
*db
, MSIDATABASE
*merge
,
1826 struct list
*tabledata
)
1828 static const WCHAR query
[] = {
1829 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1830 '`','_','T','a','b','l','e','s','`',0};
1835 r
= MSI_DatabaseOpenViewW(merge
, query
, &view
);
1836 if (r
!= ERROR_SUCCESS
)
1841 data
.tabledata
= tabledata
;
1842 r
= MSI_IterateRecords(view
, NULL
, merge_diff_tables
, &data
);
1843 msiobj_release(&view
->hdr
);
1847 static UINT
merge_table(MSIDATABASE
*db
, MERGETABLE
*table
)
1853 if (!TABLE_Exists(db
, table
->name
))
1855 r
= msi_add_table_to_db(db
, table
->columns
, table
->types
,
1856 table
->labels
, table
->numlabels
, table
->numcolumns
);
1857 if (r
!= ERROR_SUCCESS
)
1858 return ERROR_FUNCTION_FAILED
;
1861 LIST_FOR_EACH_ENTRY(row
, &table
->rows
, MERGEROW
, entry
)
1863 r
= TABLE_CreateView(db
, table
->name
, &tv
);
1864 if (r
!= ERROR_SUCCESS
)
1867 r
= tv
->ops
->insert_row(tv
, row
->data
, -1, FALSE
);
1868 tv
->ops
->delete(tv
);
1870 if (r
!= ERROR_SUCCESS
)
1874 return ERROR_SUCCESS
;
1877 static UINT
update_merge_errors(MSIDATABASE
*db
, LPCWSTR error
,
1878 LPWSTR table
, DWORD numconflicts
)
1883 static const WCHAR create
[] = {
1884 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1885 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1886 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1887 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1888 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1889 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1890 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1891 static const WCHAR insert
[] = {
1892 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1893 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1894 '`','N','u','m','R','o','w','M','e','r','g','e',
1895 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1896 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1898 if (!TABLE_Exists(db
, error
))
1900 r
= MSI_OpenQuery(db
, &view
, create
, error
);
1901 if (r
!= ERROR_SUCCESS
)
1904 r
= MSI_ViewExecute(view
, NULL
);
1905 msiobj_release(&view
->hdr
);
1906 if (r
!= ERROR_SUCCESS
)
1910 r
= MSI_OpenQuery(db
, &view
, insert
, error
, table
, numconflicts
);
1911 if (r
!= ERROR_SUCCESS
)
1914 r
= MSI_ViewExecute(view
, NULL
);
1915 msiobj_release(&view
->hdr
);
1919 UINT WINAPI
MsiDatabaseMergeW(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1920 LPCWSTR szTableName
)
1922 struct list tabledata
= LIST_INIT(tabledata
);
1923 struct list
*item
, *cursor
;
1924 MSIDATABASE
*db
, *merge
;
1929 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1930 debugstr_w(szTableName
));
1932 if (szTableName
&& !*szTableName
)
1933 return ERROR_INVALID_TABLE
;
1935 db
= msihandle2msiinfo(hDatabase
, MSIHANDLETYPE_DATABASE
);
1936 merge
= msihandle2msiinfo(hDatabaseMerge
, MSIHANDLETYPE_DATABASE
);
1939 r
= ERROR_INVALID_HANDLE
;
1943 r
= gather_merge_data(db
, merge
, &tabledata
);
1944 if (r
!= ERROR_SUCCESS
)
1948 LIST_FOR_EACH_ENTRY(table
, &tabledata
, MERGETABLE
, entry
)
1950 if (table
->numconflicts
)
1954 r
= update_merge_errors(db
, szTableName
, table
->name
,
1955 table
->numconflicts
);
1956 if (r
!= ERROR_SUCCESS
)
1961 r
= merge_table(db
, table
);
1962 if (r
!= ERROR_SUCCESS
)
1967 LIST_FOR_EACH_SAFE(item
, cursor
, &tabledata
)
1969 MERGETABLE
*table
= LIST_ENTRY(item
, MERGETABLE
, entry
);
1970 list_remove(&table
->entry
);
1971 free_merge_table(table
);
1975 r
= ERROR_FUNCTION_FAILED
;
1978 msiobj_release(&db
->hdr
);
1979 msiobj_release(&merge
->hdr
);
1983 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
1985 MSIDBSTATE ret
= MSIDBSTATE_READ
;
1988 TRACE("%d\n", handle
);
1990 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
1993 IWineMsiRemoteDatabase
*remote_database
;
1995 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
1996 if ( !remote_database
)
1997 return MSIDBSTATE_ERROR
;
1999 IWineMsiRemoteDatabase_Release( remote_database
);
2000 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
2002 return MSIDBSTATE_READ
;
2005 if (db
->mode
!= MSIDBOPEN_READONLY
)
2006 ret
= MSIDBSTATE_WRITE
;
2007 msiobj_release( &db
->hdr
);
2012 typedef struct _msi_remote_database_impl
{
2013 IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface
;
2016 } msi_remote_database_impl
;
2018 static inline msi_remote_database_impl
*impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase
*iface
)
2020 return CONTAINING_RECORD(iface
, msi_remote_database_impl
, IWineMsiRemoteDatabase_iface
);
2023 static HRESULT WINAPI
mrd_QueryInterface( IWineMsiRemoteDatabase
*iface
,
2024 REFIID riid
,LPVOID
*ppobj
)
2026 if( IsEqualCLSID( riid
, &IID_IUnknown
) ||
2027 IsEqualCLSID( riid
, &IID_IWineMsiRemoteDatabase
) )
2029 IWineMsiRemoteDatabase_AddRef( iface
);
2034 return E_NOINTERFACE
;
2037 static ULONG WINAPI
mrd_AddRef( IWineMsiRemoteDatabase
*iface
)
2039 msi_remote_database_impl
* This
= impl_from_IWineMsiRemoteDatabase( iface
);
2041 return InterlockedIncrement( &This
->refs
);
2044 static ULONG WINAPI
mrd_Release( IWineMsiRemoteDatabase
*iface
)
2046 msi_remote_database_impl
* This
= impl_from_IWineMsiRemoteDatabase( iface
);
2049 r
= InterlockedDecrement( &This
->refs
);
2052 MsiCloseHandle( This
->database
);
2058 static HRESULT WINAPI
mrd_IsTablePersistent( IWineMsiRemoteDatabase
*iface
,
2059 LPCWSTR table
, MSICONDITION
*persistent
)
2061 msi_remote_database_impl
*This
= impl_from_IWineMsiRemoteDatabase( iface
);
2062 *persistent
= MsiDatabaseIsTablePersistentW(This
->database
, table
);
2066 static HRESULT WINAPI
mrd_GetPrimaryKeys( IWineMsiRemoteDatabase
*iface
,
2067 LPCWSTR table
, MSIHANDLE
*keys
)
2069 msi_remote_database_impl
*This
= impl_from_IWineMsiRemoteDatabase( iface
);
2070 UINT r
= MsiDatabaseGetPrimaryKeysW(This
->database
, table
, keys
);
2071 return HRESULT_FROM_WIN32(r
);
2074 static HRESULT WINAPI
mrd_GetSummaryInformation( IWineMsiRemoteDatabase
*iface
,
2075 UINT updatecount
, MSIHANDLE
*suminfo
)
2077 msi_remote_database_impl
*This
= impl_from_IWineMsiRemoteDatabase( iface
);
2078 UINT r
= MsiGetSummaryInformationW(This
->database
, NULL
, updatecount
, suminfo
);
2079 return HRESULT_FROM_WIN32(r
);
2082 static HRESULT WINAPI
mrd_OpenView( IWineMsiRemoteDatabase
*iface
,
2083 LPCWSTR query
, MSIHANDLE
*view
)
2085 msi_remote_database_impl
*This
= impl_from_IWineMsiRemoteDatabase( iface
);
2086 UINT r
= MsiDatabaseOpenViewW(This
->database
, query
, view
);
2087 return HRESULT_FROM_WIN32(r
);
2090 static HRESULT WINAPI
mrd_SetMsiHandle( IWineMsiRemoteDatabase
*iface
, MSIHANDLE handle
)
2092 msi_remote_database_impl
* This
= impl_from_IWineMsiRemoteDatabase( iface
);
2093 This
->database
= handle
;
2097 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl
=
2102 mrd_IsTablePersistent
,
2104 mrd_GetSummaryInformation
,
2109 HRESULT
create_msi_remote_database( IUnknown
*pOuter
, LPVOID
*ppObj
)
2111 msi_remote_database_impl
*This
;
2113 This
= msi_alloc( sizeof *This
);
2115 return E_OUTOFMEMORY
;
2117 This
->IWineMsiRemoteDatabase_iface
.lpVtbl
= &msi_remote_database_vtbl
;
2121 *ppObj
= &This
->IWineMsiRemoteDatabase_iface
;