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
30 #include "wine/debug.h"
36 #include "msiserver.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
46 * An .msi file is a structured storage file.
47 * It contains a number of streams.
48 * A stream for each table in the database.
49 * Two streams for the string table in the database.
50 * Any binary data in a table is a reference to a stream.
53 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
55 static void free_transforms( MSIDATABASE
*db
)
57 while( !list_empty( &db
->transforms
) )
59 MSITRANSFORM
*t
= LIST_ENTRY( list_head( &db
->transforms
), MSITRANSFORM
, entry
);
60 list_remove( &t
->entry
);
61 IStorage_Release( t
->stg
);
66 static void free_streams( MSIDATABASE
*db
)
69 for (i
= 0; i
< db
->num_streams
; i
++)
71 if (db
->streams
[i
].stream
) IStream_Release( db
->streams
[i
].stream
);
76 void append_storage_to_db( MSIDATABASE
*db
, IStorage
*stg
)
80 t
= malloc( sizeof *t
);
82 IStorage_AddRef( stg
);
83 list_add_head( &db
->transforms
, &t
->entry
);
86 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
88 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
92 free_cached_tables( db
);
93 free_transforms( db
);
94 if (db
->strings
) msi_destroy_stringtable( db
->strings
);
95 IStorage_Release( db
->storage
);
98 DeleteFileW( db
->deletefile
);
99 free( db
->deletefile
);
101 free( db
->tempfolder
);
104 static HRESULT
db_initialize( IStorage
*stg
, const GUID
*clsid
)
108 hr
= IStorage_SetClass( stg
, clsid
);
111 WARN("failed to set class id %#lx\n", hr
);
115 /* create the _Tables stream */
116 hr
= write_stream_data( stg
, L
"_Tables", NULL
, 0, TRUE
);
119 WARN("failed to create _Tables stream %#lx\n", hr
);
123 hr
= msi_init_string_table( stg
);
126 WARN("failed to initialize string table %#lx\n", hr
);
130 hr
= IStorage_Commit( stg
, 0 );
133 WARN("failed to commit changes %#lx\n", hr
);
140 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
142 IStorage
*stg
= NULL
;
144 MSIDATABASE
*db
= NULL
;
145 UINT ret
= ERROR_FUNCTION_FAILED
;
149 BOOL created
= FALSE
, patch
= FALSE
;
150 WCHAR path
[MAX_PATH
];
152 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
155 return ERROR_INVALID_PARAMETER
;
157 save_path
= szDBPath
;
158 if ( IS_INTMSIDBOPEN(szPersist
) )
160 mode
= LOWORD(szPersist
);
164 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
165 return ERROR_OPEN_FAILED
;
167 szDBPath
= szPersist
;
168 mode
= MSI_OPEN_TRANSACT
;
172 if ((mode
& MSI_OPEN_PATCHFILE
) == MSI_OPEN_PATCHFILE
)
174 TRACE("Database is a patch\n");
175 mode
&= ~MSI_OPEN_PATCHFILE
;
179 if( mode
== MSI_OPEN_READONLY
)
181 r
= StgOpenStorage( szDBPath
, NULL
,
182 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
184 else if( mode
== MSI_OPEN_CREATE
)
186 r
= StgCreateDocfile( szDBPath
,
187 STGM_CREATE
|STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
190 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
193 else if( mode
== MSI_OPEN_CREATEDIRECT
)
195 r
= StgCreateDocfile( szDBPath
,
196 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
199 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
202 else if( mode
== MSI_OPEN_TRANSACT
)
204 r
= StgOpenStorage( szDBPath
, NULL
,
205 STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
207 else if( mode
== MSI_OPEN_DIRECT
)
209 r
= StgOpenStorage( szDBPath
, NULL
,
210 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
214 ERR("unknown flag %x\n",mode
);
215 return ERROR_INVALID_PARAMETER
;
218 if( FAILED( r
) || !stg
)
220 WARN("open failed r = %#lx for %s\n", r
, debugstr_w(szDBPath
));
221 return ERROR_FUNCTION_FAILED
;
224 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
227 FIXME("Failed to stat storage\n");
231 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
232 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) &&
233 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiTransform
) )
235 ERR("storage GUID is not a MSI database GUID %s\n",
236 debugstr_guid(&stat
.clsid
) );
240 if ( patch
&& !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
242 ERR("storage GUID is not the MSI patch GUID %s\n",
243 debugstr_guid(&stat
.clsid
) );
244 ret
= ERROR_OPEN_FAILED
;
248 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
252 FIXME("Failed to allocate a handle\n");
256 if (!wcschr( save_path
, '\\' ))
258 GetCurrentDirectoryW( MAX_PATH
, path
);
259 lstrcatW( path
, L
"\\" );
260 lstrcatW( path
, save_path
);
263 lstrcpyW( path
, save_path
);
265 db
->path
= wcsdup( path
);
266 db
->media_transform_offset
= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
;
267 db
->media_transform_disk_id
= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
;
269 if( TRACE_ON( msi
) )
270 enum_stream_names( stg
);
275 db
->deletefile
= wcsdup( szDBPath
);
276 list_init( &db
->tables
);
277 list_init( &db
->transforms
);
279 db
->strings
= msi_load_string_table( stg
, &db
->bytes_per_strref
);
285 msiobj_addref( &db
->hdr
);
286 IStorage_AddRef( stg
);
291 msiobj_release( &db
->hdr
);
293 IStorage_Release( stg
);
298 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
303 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
305 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
306 if( ret
== ERROR_SUCCESS
)
308 *phDB
= alloc_msihandle( &db
->hdr
);
310 ret
= ERROR_NOT_ENOUGH_MEMORY
;
311 msiobj_release( &db
->hdr
);
317 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
319 HRESULT r
= ERROR_FUNCTION_FAILED
;
320 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
322 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
326 szwDBPath
= strdupAtoW( szDBPath
);
331 if( !IS_INTMSIDBOPEN(szPersist
) )
333 szwPersist
= strdupAtoW( szPersist
);
338 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
340 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
343 if( !IS_INTMSIDBOPEN(szPersist
) )
350 static WCHAR
*read_text_archive(const WCHAR
*path
, DWORD
*len
)
355 DWORD read
, size
= 0;
357 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
358 if (file
== INVALID_HANDLE_VALUE
)
361 size
= GetFileSize( file
, NULL
);
362 if (!(data
= malloc( size
))) goto done
;
364 if (!ReadFile( file
, data
, size
, &read
, NULL
) || read
!= size
) goto done
;
366 while (!data
[size
- 1]) size
--;
367 *len
= MultiByteToWideChar( CP_ACP
, 0, data
, size
, NULL
, 0 );
368 if ((wdata
= malloc( (*len
+ 1) * sizeof(WCHAR
) )))
370 MultiByteToWideChar( CP_ACP
, 0, data
, size
, wdata
, *len
);
380 static UINT
parse_line(WCHAR
**line
, WCHAR
***entries
, DWORD
*num_entries
, DWORD
*len
)
382 LPWSTR ptr
= *line
, save
;
383 DWORD i
, count
= 1, chars_left
= *len
;
387 /* stay on this line */
388 while (chars_left
&& *ptr
!= '\n')
390 /* entries are separated by tabs */
399 * make sure this line has the same number of entries as there are columns
400 * which are indicated by the first line.
402 if (*num_entries
&& *num_entries
!= count
)
403 return ERROR_FUNCTION_FAILED
;
405 *entries
= malloc(count
* sizeof(WCHAR
*));
407 return ERROR_OUTOFMEMORY
;
409 /* store pointers into the data */
411 for (i
= 0, ptr
= *line
; i
< count
; i
++)
413 while (chars_left
&& *ptr
== '\r')
420 while (chars_left
&& *ptr
!= '\t' && *ptr
!= '\n' && *ptr
!= '\r')
422 if (!*ptr
) *ptr
= '\n'; /* convert embedded nulls to \n */
423 if (ptr
> *line
&& *ptr
== '\x19' && *(ptr
- 1) == '\x11')
432 /* NULL-separate the data */
433 if (*ptr
== '\n' || *ptr
== '\r')
435 while (chars_left
&& (*ptr
== '\n' || *ptr
== '\r'))
446 (*entries
)[i
] = save
;
449 /* move to the next line if there's more, else EOF */
453 *num_entries
= count
;
455 return ERROR_SUCCESS
;
458 static WCHAR
*build_createsql_prelude(const WCHAR
*table
)
463 size
= ARRAY_SIZE(L
"CREATE TABLE `%s` ( ") + lstrlenW(table
) - 2;
464 prelude
= malloc(size
* sizeof(WCHAR
));
468 swprintf(prelude
, size
, L
"CREATE TABLE `%s` ( ", table
);
472 static WCHAR
*build_createsql_columns(WCHAR
**columns_data
, WCHAR
**types
, DWORD num_columns
)
476 DWORD sql_size
= 1, i
, len
;
477 WCHAR expanded
[128], *ptr
;
478 WCHAR size
[10], comma
[2], extra
[30];
480 columns
= calloc(sql_size
, sizeof(WCHAR
));
484 for (i
= 0; i
< num_columns
; i
++)
487 comma
[1] = size
[0] = extra
[0] = '\0';
489 if (i
== num_columns
- 1)
495 len
= wcstol(ptr
, NULL
, 10);
501 lstrcpyW(extra
, L
" NOT NULL");
504 lstrcatW(extra
, L
" LOCALIZABLE");
506 swprintf(size
, ARRAY_SIZE(size
), L
"(%s)", ptr
);
509 lstrcpyW(extra
, L
" NOT NULL");
513 swprintf(size
, ARRAY_SIZE(size
), L
"(%s)", ptr
);
516 lstrcpyW(extra
, L
" NOT NULL");
525 WARN("invalid int width %lu\n", len
);
531 lstrcpyW(extra
, L
" NOT NULL");
537 ERR("Unknown type: %c\n", types
[i
][0]);
542 swprintf(expanded
, ARRAY_SIZE(expanded
), L
"`%s` %s%s%s%s ", columns_data
[i
], type
, size
, extra
, comma
);
543 sql_size
+= lstrlenW(expanded
);
545 p
= realloc(columns
, sql_size
* sizeof(WCHAR
));
553 lstrcatW(columns
, expanded
);
559 static WCHAR
*build_createsql_postlude(WCHAR
**primary_keys
, DWORD num_keys
)
561 LPWSTR postlude
, keys
, ptr
;
564 for (i
= 0, size
= 1; i
< num_keys
; i
++)
565 size
+= lstrlenW(L
"`%s`, ") + lstrlenW(primary_keys
[i
]) - 2;
567 keys
= malloc(size
* sizeof(WCHAR
));
571 for (i
= 0, ptr
= keys
; i
< num_keys
; i
++)
573 ptr
+= swprintf(ptr
, size
- (ptr
- keys
), L
"`%s`, ", primary_keys
[i
]);
576 /* remove final ', ' */
579 size
= lstrlenW(L
"PRIMARY KEY %s)") + size
- 1;
580 postlude
= malloc(size
* sizeof(WCHAR
));
584 swprintf(postlude
, size
, L
"PRIMARY KEY %s)", keys
);
591 static UINT
add_table_to_db(MSIDATABASE
*db
, WCHAR
**columns
, WCHAR
**types
, WCHAR
**labels
, DWORD num_labels
,
594 UINT r
= ERROR_OUTOFMEMORY
;
597 LPWSTR create_sql
= NULL
;
598 LPWSTR prelude
, columns_sql
, postlude
;
600 prelude
= build_createsql_prelude(labels
[0]);
601 columns_sql
= build_createsql_columns(columns
, types
, num_columns
);
602 postlude
= build_createsql_postlude(labels
+ 1, num_labels
- 1); /* skip over table name */
604 if (!prelude
|| !columns_sql
|| !postlude
)
607 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
608 create_sql
= malloc(size
* sizeof(WCHAR
));
612 lstrcpyW(create_sql
, prelude
);
613 lstrcatW(create_sql
, columns_sql
);
614 lstrcatW(create_sql
, postlude
);
616 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
617 if (r
!= ERROR_SUCCESS
)
620 r
= MSI_ViewExecute(view
, NULL
);
622 msiobj_release(&view
->hdr
);
632 static WCHAR
*import_stream_filename(const WCHAR
*path
, const WCHAR
*name
)
635 LPWSTR fullname
, ptr
;
637 len
= lstrlenW(path
) + lstrlenW(name
) + 1;
638 fullname
= malloc(len
* sizeof(WCHAR
));
642 lstrcpyW( fullname
, path
);
644 /* chop off extension from path */
645 ptr
= wcsrchr(fullname
, '.');
652 lstrcpyW( ptr
, name
);
656 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
657 LPWSTR
*data
, LPWSTR path
, MSIRECORD
**rec
)
661 *rec
= MSI_CreateRecord(num_columns
);
663 return ERROR_OUTOFMEMORY
;
665 for (i
= 0; i
< num_columns
; i
++)
669 case 'L': case 'l': case 'S': case 's':
670 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
674 MSI_RecordSetInteger(*rec
, i
+ 1, wcstol(data
[i
], NULL
, 10));
680 WCHAR
*file
= import_stream_filename(path
, data
[i
]);
682 return ERROR_FUNCTION_FAILED
;
684 r
= MSI_RecordSetStreamFromFileW(*rec
, i
+ 1, file
);
686 if (r
!= ERROR_SUCCESS
)
687 return ERROR_FUNCTION_FAILED
;
691 ERR("Unhandled column type: %c\n", types
[i
][0]);
692 msiobj_release(&(*rec
)->hdr
);
693 return ERROR_FUNCTION_FAILED
;
697 return ERROR_SUCCESS
;
700 static UINT
add_records_to_table(MSIDATABASE
*db
, WCHAR
**columns
, WCHAR
**types
, WCHAR
**labels
, WCHAR
***records
,
701 int num_columns
, int num_records
, WCHAR
*path
)
708 r
= MSI_OpenQuery(db
, &view
, L
"SELECT * FROM `%s`", labels
[0]);
709 if (r
!= ERROR_SUCCESS
)
712 while (MSI_ViewFetch(view
, &rec
) != ERROR_NO_MORE_ITEMS
)
714 r
= MSI_ViewModify(view
, MSIMODIFY_DELETE
, rec
);
715 msiobj_release(&rec
->hdr
);
716 if (r
!= ERROR_SUCCESS
)
720 for (i
= 0; i
< num_records
; i
++)
722 r
= construct_record(num_columns
, types
, records
[i
], path
, &rec
);
723 if (r
!= ERROR_SUCCESS
)
726 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
727 if (r
!= ERROR_SUCCESS
)
729 msiobj_release(&rec
->hdr
);
733 msiobj_release(&rec
->hdr
);
737 msiobj_release(&view
->hdr
);
741 static UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
744 DWORD len
, i
, num_labels
= 0, num_types
= 0, num_columns
= 0, num_records
= 0;
745 WCHAR
**columns
, **types
, **labels
, *path
, *ptr
, *data
, ***records
= NULL
, ***temp_records
;
747 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
749 if (!folder
|| !file
)
750 return ERROR_INVALID_PARAMETER
;
752 len
= lstrlenW(folder
) + lstrlenW(L
"\\") + lstrlenW(file
) + 1;
753 path
= malloc( len
* sizeof(WCHAR
) );
755 return ERROR_OUTOFMEMORY
;
757 lstrcpyW( path
, folder
);
758 lstrcatW( path
, L
"\\" );
759 lstrcatW( path
, file
);
761 data
= read_text_archive( path
, &len
);
765 return ERROR_FUNCTION_FAILED
;
769 parse_line( &ptr
, &columns
, &num_columns
, &len
);
770 parse_line( &ptr
, &types
, &num_types
, &len
);
771 parse_line( &ptr
, &labels
, &num_labels
, &len
);
773 if (num_columns
== 1 && !columns
[0][0] && num_labels
== 1 && !labels
[0][0] &&
774 num_types
== 2 && !wcscmp( types
[1], L
"_ForceCodepage" ))
776 r
= msi_set_string_table_codepage( db
->strings
, wcstol( types
[0], NULL
, 10 ) );
780 if (num_columns
!= num_types
)
782 r
= ERROR_FUNCTION_FAILED
;
786 records
= malloc(sizeof(WCHAR
**));
789 r
= ERROR_OUTOFMEMORY
;
793 /* read in the table records */
796 r
= parse_line( &ptr
, &records
[num_records
], &num_columns
, &len
);
797 if (r
!= ERROR_SUCCESS
)
801 temp_records
= realloc(records
, (num_records
+ 1) * sizeof(WCHAR
**));
804 r
= ERROR_OUTOFMEMORY
;
807 records
= temp_records
;
810 if (!wcscmp(labels
[0], L
"_SummaryInformation"))
812 r
= msi_add_suminfo( db
, records
, num_records
, num_columns
);
813 if (r
!= ERROR_SUCCESS
)
815 r
= ERROR_FUNCTION_FAILED
;
821 if (!TABLE_Exists(db
, labels
[0]))
823 r
= add_table_to_db( db
, columns
, types
, labels
, num_labels
, num_columns
);
824 if (r
!= ERROR_SUCCESS
)
826 r
= ERROR_FUNCTION_FAILED
;
831 r
= add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
, path
);
841 for (i
= 0; i
< num_records
; i
++)
848 UINT WINAPI
MsiDatabaseImportW( MSIHANDLE handle
, const WCHAR
*szFolder
, const WCHAR
*szFilename
)
853 TRACE( "%lu %s %s\n", handle
, debugstr_w(szFolder
), debugstr_w(szFilename
) );
855 if (!(db
= msihandle2msiinfo(handle
, MSIHANDLETYPE_DATABASE
)))
856 return ERROR_INVALID_HANDLE
;
858 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
859 msiobj_release( &db
->hdr
);
863 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
, const char *szFolder
, const char *szFilename
)
865 WCHAR
*path
= NULL
, *file
= NULL
;
866 UINT r
= ERROR_OUTOFMEMORY
;
868 TRACE( "%lu %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
) );
872 path
= strdupAtoW( szFolder
);
879 file
= strdupAtoW( szFilename
);
884 r
= MsiDatabaseImportW( handle
, path
, file
);
893 static UINT
export_field( HANDLE handle
, MSIRECORD
*row
, UINT field
)
900 buffer
= malloc( sz
);
902 return ERROR_OUTOFMEMORY
;
904 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
905 if (r
== ERROR_MORE_DATA
)
909 sz
++; /* leave room for NULL terminator */
910 tmp
= realloc( buffer
, sz
);
914 return ERROR_OUTOFMEMORY
;
918 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
919 if (r
!= ERROR_SUCCESS
)
925 else if (r
!= ERROR_SUCCESS
)
934 if (*ptr
== '\r' && *( ptr
+ 1 ) == '\n')
947 ret
= WriteFile( handle
, buffer
, sz
, &sz
, NULL
);
949 return ret
? ERROR_SUCCESS
: ERROR_FUNCTION_FAILED
;
952 static UINT
export_stream( const WCHAR
*folder
, const WCHAR
*table
, MSIRECORD
*row
, UINT field
, UINT start
)
954 WCHAR stream
[MAX_STREAM_NAME_LEN
+ 1], *path
;
955 DWORD sz
, read_size
, write_size
;
960 sz
= ARRAY_SIZE( stream
);
961 r
= MSI_RecordGetStringW( row
, start
, stream
, &sz
);
962 if (r
!= ERROR_SUCCESS
)
965 len
= sz
+ lstrlenW( folder
) + lstrlenW( table
) + ARRAY_SIZE( L
"%s\\%s" ) + 1;
966 if (!(path
= malloc( len
* sizeof(WCHAR
) )))
967 return ERROR_OUTOFMEMORY
;
969 len
= swprintf( path
, len
, L
"%s\\%s", folder
, table
);
970 if (!CreateDirectoryW( path
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
)
973 return ERROR_FUNCTION_FAILED
;
977 lstrcpyW( path
+ len
, stream
);
978 file
= CreateFileW( path
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
979 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
981 if (file
== INVALID_HANDLE_VALUE
)
982 return ERROR_FUNCTION_FAILED
;
984 read_size
= sizeof(buffer
);
985 while (read_size
== sizeof(buffer
))
987 r
= MSI_RecordReadStream( row
, field
, buffer
, &read_size
);
988 if (r
!= ERROR_SUCCESS
)
993 if (!WriteFile( file
, buffer
, read_size
, &write_size
, NULL
) || read_size
!= write_size
)
996 return ERROR_WRITE_FAULT
;
1003 struct row_export_info
1006 const WCHAR
*folder
;
1010 static UINT
export_record( struct row_export_info
*row_export_info
, MSIRECORD
*row
, UINT start
)
1012 HANDLE handle
= row_export_info
->handle
;
1013 UINT i
, count
, r
= ERROR_SUCCESS
;
1017 count
= MSI_RecordGetFieldCount( row
);
1018 for (i
= start
; i
<= count
; i
++)
1020 r
= export_field( handle
, row
, i
);
1021 if (r
== ERROR_INVALID_PARAMETER
)
1023 r
= export_stream( row_export_info
->folder
, row_export_info
->table
, row
, i
, start
);
1024 if (r
!= ERROR_SUCCESS
)
1027 /* exporting a binary stream, repeat the "Name" field */
1028 r
= export_field( handle
, row
, start
);
1029 if (r
!= ERROR_SUCCESS
)
1032 else if (r
!= ERROR_SUCCESS
)
1035 sep
= (i
< count
) ? "\t" : "\r\n";
1036 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
1037 return ERROR_FUNCTION_FAILED
;
1042 static UINT
export_row( MSIRECORD
*row
, void *arg
)
1044 return export_record( arg
, row
, 1 );
1047 static UINT
export_forcecodepage( HANDLE handle
, UINT codepage
)
1049 static const char fmt
[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1050 char data
[sizeof(fmt
) + 10];
1051 DWORD sz
= sprintf( data
, fmt
, codepage
);
1053 if (!WriteFile(handle
, data
, sz
, &sz
, NULL
))
1054 return ERROR_FUNCTION_FAILED
;
1056 return ERROR_SUCCESS
;
1059 static UINT
export_summaryinformation( MSIDATABASE
*db
, HANDLE handle
)
1061 static const char header
[] = "PropertyId\tValue\r\n"
1063 "_SummaryInformation\tPropertyId\r\n";
1064 DWORD sz
= ARRAY_SIZE(header
) - 1;
1066 if (!WriteFile(handle
, header
, sz
, &sz
, NULL
))
1067 return ERROR_WRITE_FAULT
;
1069 return msi_export_suminfo( db
, handle
);
1072 static UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
, LPCWSTR folder
, LPCWSTR file
)
1074 MSIRECORD
*rec
= NULL
;
1075 MSIQUERY
*view
= NULL
;
1080 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
1081 debugstr_w(folder
), debugstr_w(file
) );
1083 if (!folder
|| !file
)
1084 return ERROR_INVALID_PARAMETER
;
1086 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
1087 filename
= malloc(len
* sizeof(WCHAR
));
1089 return ERROR_OUTOFMEMORY
;
1091 lstrcpyW( filename
, folder
);
1092 lstrcatW( filename
, L
"\\" );
1093 lstrcatW( filename
, file
);
1095 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
1096 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1098 if (handle
== INVALID_HANDLE_VALUE
)
1099 return ERROR_FUNCTION_FAILED
;
1101 if (!wcscmp( table
, L
"_ForceCodepage" ))
1103 UINT codepage
= msi_get_string_table_codepage( db
->strings
);
1104 r
= export_forcecodepage( handle
, codepage
);
1108 if (!wcscmp( table
, L
"_SummaryInformation" ))
1110 r
= export_summaryinformation( db
, handle
);
1114 r
= MSI_OpenQuery( db
, &view
, L
"SELECT * FROM %s", table
);
1115 if (r
== ERROR_SUCCESS
)
1117 struct row_export_info row_export_info
= { handle
, folder
, table
};
1119 /* write out row 1, the column names */
1120 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
1121 if (r
== ERROR_SUCCESS
)
1123 export_record( &row_export_info
, rec
, 1 );
1124 msiobj_release( &rec
->hdr
);
1127 /* write out row 2, the column types */
1128 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
1129 if (r
== ERROR_SUCCESS
)
1131 export_record( &row_export_info
, rec
, 1 );
1132 msiobj_release( &rec
->hdr
);
1135 /* write out row 3, the table name + keys */
1136 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
1137 if (r
== ERROR_SUCCESS
)
1139 MSI_RecordSetStringW( rec
, 0, table
);
1140 export_record( &row_export_info
, rec
, 0 );
1141 msiobj_release( &rec
->hdr
);
1144 /* write out row 4 onwards, the data */
1145 r
= MSI_IterateRecords( view
, 0, export_row
, &row_export_info
);
1146 msiobj_release( &view
->hdr
);
1150 CloseHandle( handle
);
1154 /***********************************************************************
1155 * MsiExportDatabaseW [MSI.@]
1157 * Writes a file containing the table data as tab separated ASCII.
1159 * The format is as follows:
1161 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1162 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1163 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1165 * Followed by the data, starting at row 1 with one row per line
1167 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1169 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, const WCHAR
*szTable
, const WCHAR
*szFolder
, const WCHAR
*szFilename
)
1174 TRACE( "%lu %s %s %s\n", handle
, debugstr_w(szTable
), debugstr_w(szFolder
), debugstr_w(szFilename
) );
1176 if (!(db
= msihandle2msiinfo(handle
, MSIHANDLETYPE_DATABASE
)))
1177 return ERROR_INVALID_HANDLE
;
1179 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
1180 msiobj_release( &db
->hdr
);
1184 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, const char *szTable
, const char *szFolder
, const char *szFilename
)
1186 WCHAR
*path
= NULL
, *file
= NULL
, *table
= NULL
;
1187 UINT r
= ERROR_OUTOFMEMORY
;
1189 TRACE( "%lu %s %s %s\n", handle
, debugstr_a(szTable
), debugstr_a(szFolder
), debugstr_a(szFilename
) );
1193 table
= strdupAtoW( szTable
);
1200 path
= strdupAtoW( szFolder
);
1207 file
= strdupAtoW( szFilename
);
1212 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
1222 UINT WINAPI
MsiDatabaseMergeA( MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
, const char *szTableName
)
1227 TRACE("%lu, %lu, %s\n", hDatabase
, hDatabaseMerge
, debugstr_a(szTableName
) );
1229 table
= strdupAtoW(szTableName
);
1230 r
= MsiDatabaseMergeW(hDatabase
, hDatabaseMerge
, table
);
1260 struct merge_table
*curtable
;
1262 struct list
*tabledata
;
1265 static BOOL
merge_type_match(LPCWSTR type1
, LPCWSTR type2
)
1267 if (((type1
[0] == 'l') || (type1
[0] == 's')) &&
1268 ((type2
[0] == 'l') || (type2
[0] == 's')))
1271 if (((type1
[0] == 'L') || (type1
[0] == 'S')) &&
1272 ((type2
[0] == 'L') || (type2
[0] == 'S')))
1275 return !wcscmp( type1
, type2
);
1278 static UINT
merge_verify_colnames(MSIQUERY
*dbview
, MSIQUERY
*mergeview
)
1280 MSIRECORD
*dbrec
, *mergerec
;
1283 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_NAMES
, &dbrec
);
1284 if (r
!= ERROR_SUCCESS
)
1287 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_NAMES
, &mergerec
);
1288 if (r
!= ERROR_SUCCESS
)
1290 msiobj_release(&dbrec
->hdr
);
1294 count
= MSI_RecordGetFieldCount(dbrec
);
1295 for (i
= 1; i
<= count
; i
++)
1297 if (!MSI_RecordGetString(mergerec
, i
))
1300 if (wcscmp( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1302 r
= ERROR_DATATYPE_MISMATCH
;
1307 msiobj_release(&dbrec
->hdr
);
1308 msiobj_release(&mergerec
->hdr
);
1309 dbrec
= mergerec
= NULL
;
1311 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_TYPES
, &dbrec
);
1312 if (r
!= ERROR_SUCCESS
)
1315 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_TYPES
, &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 (!merge_type_match(MSI_RecordGetString(dbrec
, i
),
1329 MSI_RecordGetString(mergerec
, i
)))
1331 r
= ERROR_DATATYPE_MISMATCH
;
1337 msiobj_release(&dbrec
->hdr
);
1338 msiobj_release(&mergerec
->hdr
);
1343 static UINT
merge_verify_primary_keys(MSIDATABASE
*db
, MSIDATABASE
*mergedb
,
1346 MSIRECORD
*dbrec
, *mergerec
= NULL
;
1349 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &dbrec
);
1350 if (r
!= ERROR_SUCCESS
)
1353 r
= MSI_DatabaseGetPrimaryKeys(mergedb
, table
, &mergerec
);
1354 if (r
!= ERROR_SUCCESS
)
1357 count
= MSI_RecordGetFieldCount(dbrec
);
1358 if (count
!= MSI_RecordGetFieldCount(mergerec
))
1360 r
= ERROR_DATATYPE_MISMATCH
;
1364 for (i
= 1; i
<= count
; i
++)
1366 if (wcscmp( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1368 r
= ERROR_DATATYPE_MISMATCH
;
1374 msiobj_release(&dbrec
->hdr
);
1375 msiobj_release(&mergerec
->hdr
);
1380 static LPWSTR
get_key_value(MSIQUERY
*view
, LPCWSTR key
, MSIRECORD
*rec
)
1382 MSIRECORD
*colnames
;
1388 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &colnames
);
1389 if (r
!= ERROR_SUCCESS
)
1394 str
= msi_dup_record_field(colnames
, ++i
);
1395 cmp
= wcscmp( key
, str
);
1399 msiobj_release(&colnames
->hdr
);
1401 r
= MSI_RecordGetStringW(rec
, i
, NULL
, &sz
);
1402 if (r
!= ERROR_SUCCESS
)
1406 if (MSI_RecordGetString(rec
, i
)) /* check record field is a string */
1408 /* quote string record fields */
1410 val
= malloc(sz
* sizeof(WCHAR
));
1414 lstrcpyW(val
, L
"'");
1415 r
= MSI_RecordGetStringW(rec
, i
, val
+ 1, &sz
);
1416 lstrcpyW(val
+ 1 + sz
, L
"'");
1420 /* do not quote integer record fields */
1421 val
= malloc(sz
* sizeof(WCHAR
));
1425 r
= MSI_RecordGetStringW(rec
, i
, val
, &sz
);
1428 if (r
!= ERROR_SUCCESS
)
1430 ERR("failed to get string!\n");
1438 static LPWSTR
create_diff_row_query(MSIDATABASE
*merge
, MSIQUERY
*view
,
1439 LPWSTR table
, MSIRECORD
*rec
)
1441 WCHAR
*query
= NULL
, *clause
= NULL
, *new_clause
, *val
;
1442 LPCWSTR setptr
, key
;
1443 DWORD size
, oldsize
;
1447 r
= MSI_DatabaseGetPrimaryKeys(merge
, table
, &keys
);
1448 if (r
!= ERROR_SUCCESS
)
1452 count
= MSI_RecordGetFieldCount(keys
);
1453 for (i
= 1; i
<= count
; i
++)
1455 key
= MSI_RecordGetString(keys
, i
);
1456 val
= get_key_value(view
, key
, rec
);
1459 setptr
= L
"`%s` = %s ";
1461 setptr
= L
"`%s` = %s AND ";
1464 size
+= lstrlenW(setptr
) + lstrlenW(key
) + lstrlenW(val
) - 4;
1465 new_clause
= realloc(clause
, size
* sizeof(WCHAR
));
1471 clause
= new_clause
;
1473 swprintf(clause
+ oldsize
- 1, size
- (oldsize
- 1), setptr
, key
, val
);
1477 size
= lstrlenW(L
"SELECT * FROM `%s` WHERE %s") + lstrlenW(table
) + lstrlenW(clause
) + 1;
1478 query
= malloc(size
* sizeof(WCHAR
));
1482 swprintf(query
, size
, L
"SELECT * FROM `%s` WHERE %s", table
, clause
);
1486 msiobj_release(&keys
->hdr
);
1490 static UINT
merge_diff_row(MSIRECORD
*rec
, LPVOID param
)
1492 struct merge_data
*data
= param
;
1493 struct merge_table
*table
= data
->curtable
;
1494 struct merge_row
*mergerow
;
1495 MSIQUERY
*dbview
= NULL
;
1496 MSIRECORD
*row
= NULL
;
1497 LPWSTR query
= NULL
;
1498 UINT r
= ERROR_SUCCESS
;
1500 if (TABLE_Exists(data
->db
, table
->name
))
1502 query
= create_diff_row_query(data
->merge
, data
->curview
, table
->name
, rec
);
1504 return ERROR_OUTOFMEMORY
;
1506 r
= MSI_DatabaseOpenViewW(data
->db
, query
, &dbview
);
1507 if (r
!= ERROR_SUCCESS
)
1510 r
= MSI_ViewExecute(dbview
, NULL
);
1511 if (r
!= ERROR_SUCCESS
)
1514 r
= MSI_ViewFetch(dbview
, &row
);
1515 if (r
== ERROR_SUCCESS
&& !MSI_RecordsAreEqual(rec
, row
))
1517 table
->numconflicts
++;
1520 else if (r
!= ERROR_NO_MORE_ITEMS
)
1526 mergerow
= malloc(sizeof(*mergerow
));
1529 r
= ERROR_OUTOFMEMORY
;
1533 mergerow
->data
= MSI_CloneRecord(rec
);
1534 if (!mergerow
->data
)
1536 r
= ERROR_OUTOFMEMORY
;
1541 list_add_tail(&table
->rows
, &mergerow
->entry
);
1545 msiobj_release(&row
->hdr
);
1546 msiobj_release(&dbview
->hdr
);
1550 static UINT
get_table_labels(MSIDATABASE
*db
, const WCHAR
*table
, WCHAR
***labels
, DWORD
*numlabels
)
1553 MSIRECORD
*prec
= NULL
;
1555 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &prec
);
1556 if (r
!= ERROR_SUCCESS
)
1559 count
= MSI_RecordGetFieldCount(prec
);
1560 *numlabels
= count
+ 1;
1561 *labels
= malloc((*numlabels
) * sizeof(WCHAR
*));
1564 r
= ERROR_OUTOFMEMORY
;
1568 (*labels
)[0] = wcsdup(table
);
1569 for (i
=1; i
<=count
; i
++ )
1571 (*labels
)[i
] = wcsdup(MSI_RecordGetString(prec
, i
));
1575 msiobj_release( &prec
->hdr
);
1579 static UINT
get_query_columns(MSIQUERY
*query
, WCHAR
***columns
, DWORD
*numcolumns
)
1582 MSIRECORD
*prec
= NULL
;
1584 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_NAMES
, &prec
);
1585 if (r
!= ERROR_SUCCESS
)
1588 count
= MSI_RecordGetFieldCount(prec
);
1589 *columns
= malloc(count
* sizeof(WCHAR
*));
1592 r
= ERROR_OUTOFMEMORY
;
1596 for (i
=1; i
<=count
; i
++ )
1598 (*columns
)[i
-1] = wcsdup(MSI_RecordGetString(prec
, i
));
1601 *numcolumns
= count
;
1604 msiobj_release( &prec
->hdr
);
1608 static UINT
get_query_types(MSIQUERY
*query
, WCHAR
***types
, DWORD
*numtypes
)
1611 MSIRECORD
*prec
= NULL
;
1613 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_TYPES
, &prec
);
1614 if (r
!= ERROR_SUCCESS
)
1617 count
= MSI_RecordGetFieldCount(prec
);
1618 *types
= malloc(count
* sizeof(WCHAR
*));
1621 r
= ERROR_OUTOFMEMORY
;
1626 for (i
=1; i
<=count
; i
++ )
1628 (*types
)[i
-1] = wcsdup(MSI_RecordGetString(prec
, i
));
1632 msiobj_release( &prec
->hdr
);
1636 static void merge_free_rows(struct merge_table
*table
)
1638 struct list
*item
, *cursor
;
1640 LIST_FOR_EACH_SAFE(item
, cursor
, &table
->rows
)
1642 struct merge_row
*row
= LIST_ENTRY(item
, struct merge_row
, entry
);
1644 list_remove(&row
->entry
);
1645 msiobj_release(&row
->data
->hdr
);
1650 static void free_merge_table(struct merge_table
*table
)
1654 if (table
->labels
!= NULL
)
1656 for (i
= 0; i
< table
->numlabels
; i
++)
1657 free(table
->labels
[i
]);
1659 free(table
->labels
);
1662 if (table
->columns
!= NULL
)
1664 for (i
= 0; i
< table
->numcolumns
; i
++)
1665 free(table
->columns
[i
]);
1667 free(table
->columns
);
1670 if (table
->types
!= NULL
)
1672 for (i
= 0; i
< table
->numtypes
; i
++)
1673 free(table
->types
[i
]);
1679 merge_free_rows(table
);
1684 static UINT
get_merge_table(MSIDATABASE
*db
, const WCHAR
*name
, struct merge_table
**ptable
)
1687 struct merge_table
*table
;
1688 MSIQUERY
*mergeview
= NULL
;
1690 table
= calloc(1, sizeof(*table
));
1694 return ERROR_OUTOFMEMORY
;
1697 r
= get_table_labels(db
, name
, &table
->labels
, &table
->numlabels
);
1698 if (r
!= ERROR_SUCCESS
)
1701 r
= MSI_OpenQuery(db
, &mergeview
, L
"SELECT * FROM `%s`", name
);
1702 if (r
!= ERROR_SUCCESS
)
1705 r
= get_query_columns(mergeview
, &table
->columns
, &table
->numcolumns
);
1706 if (r
!= ERROR_SUCCESS
)
1709 r
= get_query_types(mergeview
, &table
->types
, &table
->numtypes
);
1710 if (r
!= ERROR_SUCCESS
)
1713 list_init(&table
->rows
);
1715 table
->name
= wcsdup(name
);
1716 table
->numconflicts
= 0;
1718 msiobj_release(&mergeview
->hdr
);
1720 return ERROR_SUCCESS
;
1723 msiobj_release(&mergeview
->hdr
);
1724 free_merge_table(table
);
1729 static UINT
merge_diff_tables(MSIRECORD
*rec
, LPVOID param
)
1731 struct merge_data
*data
= param
;
1732 struct merge_table
*table
;
1733 MSIQUERY
*dbview
= NULL
;
1734 MSIQUERY
*mergeview
= NULL
;
1738 name
= MSI_RecordGetString(rec
, 1);
1740 r
= MSI_OpenQuery(data
->merge
, &mergeview
, L
"SELECT * FROM `%s`", name
);
1741 if (r
!= ERROR_SUCCESS
)
1744 if (TABLE_Exists(data
->db
, name
))
1746 r
= MSI_OpenQuery(data
->db
, &dbview
, L
"SELECT * FROM `%s`", name
);
1747 if (r
!= ERROR_SUCCESS
)
1750 r
= merge_verify_colnames(dbview
, mergeview
);
1751 if (r
!= ERROR_SUCCESS
)
1754 r
= merge_verify_primary_keys(data
->db
, data
->merge
, name
);
1755 if (r
!= ERROR_SUCCESS
)
1759 r
= get_merge_table(data
->merge
, name
, &table
);
1760 if (r
!= ERROR_SUCCESS
)
1763 data
->curtable
= table
;
1764 data
->curview
= mergeview
;
1765 r
= MSI_IterateRecords(mergeview
, NULL
, merge_diff_row
, data
);
1766 if (r
!= ERROR_SUCCESS
)
1768 free_merge_table(table
);
1772 list_add_tail(data
->tabledata
, &table
->entry
);
1775 msiobj_release(&dbview
->hdr
);
1776 msiobj_release(&mergeview
->hdr
);
1780 static UINT
gather_merge_data(MSIDATABASE
*db
, MSIDATABASE
*merge
,
1781 struct list
*tabledata
)
1784 struct merge_data data
;
1787 r
= MSI_DatabaseOpenViewW(merge
, L
"SELECT * FROM `_Tables`", &view
);
1788 if (r
!= ERROR_SUCCESS
)
1793 data
.tabledata
= tabledata
;
1794 r
= MSI_IterateRecords(view
, NULL
, merge_diff_tables
, &data
);
1795 msiobj_release(&view
->hdr
);
1799 static UINT
merge_table(MSIDATABASE
*db
, struct merge_table
*table
)
1802 struct merge_row
*row
;
1805 if (!TABLE_Exists(db
, table
->name
))
1807 r
= add_table_to_db(db
, table
->columns
, table
->types
, table
->labels
, table
->numlabels
, table
->numcolumns
);
1808 if (r
!= ERROR_SUCCESS
)
1809 return ERROR_FUNCTION_FAILED
;
1812 LIST_FOR_EACH_ENTRY(row
, &table
->rows
, struct merge_row
, entry
)
1814 r
= TABLE_CreateView(db
, table
->name
, &tv
);
1815 if (r
!= ERROR_SUCCESS
)
1818 r
= tv
->ops
->insert_row(tv
, row
->data
, -1, FALSE
);
1819 tv
->ops
->delete(tv
);
1821 if (r
!= ERROR_SUCCESS
)
1825 return ERROR_SUCCESS
;
1828 static UINT
update_merge_errors(MSIDATABASE
*db
, LPCWSTR error
,
1829 LPWSTR table
, DWORD numconflicts
)
1834 if (!TABLE_Exists(db
, error
))
1836 r
= MSI_OpenQuery(db
, &view
, L
"CREATE TABLE `%s` (`Table` CHAR(255) NOT NULL, `NumRowMergeConflicts` SHORT "
1837 "NOT NULL PRIMARY KEY `Table`)" , error
);
1838 if (r
!= ERROR_SUCCESS
)
1841 r
= MSI_ViewExecute(view
, NULL
);
1842 msiobj_release(&view
->hdr
);
1843 if (r
!= ERROR_SUCCESS
)
1847 r
= MSI_OpenQuery(db
, &view
, L
"INSERT INTO `%s` (`Table`, `NumRowMergeConflicts`) VALUES ('%s', %d)", error
,
1848 table
, numconflicts
);
1849 if (r
!= ERROR_SUCCESS
)
1852 r
= MSI_ViewExecute(view
, NULL
);
1853 msiobj_release(&view
->hdr
);
1857 UINT WINAPI
MsiDatabaseMergeW( MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
, const WCHAR
*szTableName
)
1859 struct list tabledata
= LIST_INIT(tabledata
);
1860 struct list
*item
, *cursor
;
1861 MSIDATABASE
*db
, *merge
;
1862 struct merge_table
*table
;
1866 TRACE( "%lu, %lu, %s\n", hDatabase
, hDatabaseMerge
, debugstr_w(szTableName
) );
1868 if (szTableName
&& !*szTableName
)
1869 return ERROR_INVALID_TABLE
;
1871 db
= msihandle2msiinfo(hDatabase
, MSIHANDLETYPE_DATABASE
);
1872 merge
= msihandle2msiinfo(hDatabaseMerge
, MSIHANDLETYPE_DATABASE
);
1875 r
= ERROR_INVALID_HANDLE
;
1879 r
= gather_merge_data(db
, merge
, &tabledata
);
1880 if (r
!= ERROR_SUCCESS
)
1884 LIST_FOR_EACH_ENTRY(table
, &tabledata
, struct merge_table
, entry
)
1886 if (table
->numconflicts
)
1890 r
= update_merge_errors(db
, szTableName
, table
->name
,
1891 table
->numconflicts
);
1892 if (r
!= ERROR_SUCCESS
)
1897 r
= merge_table(db
, table
);
1898 if (r
!= ERROR_SUCCESS
)
1903 LIST_FOR_EACH_SAFE(item
, cursor
, &tabledata
)
1905 struct merge_table
*table
= LIST_ENTRY(item
, struct merge_table
, entry
);
1906 list_remove(&table
->entry
);
1907 free_merge_table(table
);
1911 r
= ERROR_FUNCTION_FAILED
;
1914 msiobj_release(&db
->hdr
);
1915 msiobj_release(&merge
->hdr
);
1919 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
1921 MSIDBSTATE ret
= MSIDBSTATE_READ
;
1924 TRACE( "%lu\n", handle
);
1926 if (!(db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
)))
1927 return MSIDBSTATE_ERROR
;
1929 if (db
->mode
!= MSI_OPEN_READONLY
)
1930 ret
= MSIDBSTATE_WRITE
;
1931 msiobj_release( &db
->hdr
);
1936 MSICONDITION __cdecl
s_remote_DatabaseIsTablePersistent(MSIHANDLE db
, LPCWSTR table
)
1938 return MsiDatabaseIsTablePersistentW(db
, table
);
1941 UINT __cdecl
s_remote_DatabaseGetPrimaryKeys(MSIHANDLE db
, LPCWSTR table
, struct wire_record
**rec
)
1944 UINT r
= MsiDatabaseGetPrimaryKeysW(db
, table
, &handle
);
1947 *rec
= marshal_record(handle
);
1948 MsiCloseHandle(handle
);
1952 UINT __cdecl
s_remote_DatabaseGetSummaryInformation(MSIHANDLE db
, UINT updatecount
, MSIHANDLE
*suminfo
)
1954 return MsiGetSummaryInformationW(db
, NULL
, updatecount
, suminfo
);
1957 UINT __cdecl
s_remote_DatabaseOpenView(MSIHANDLE db
, LPCWSTR query
, MSIHANDLE
*view
)
1959 return MsiDatabaseOpenViewW(db
, query
, view
);