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
);
73 msi_free( db
->streams
);
76 void append_storage_to_db( MSIDATABASE
*db
, IStorage
*stg
)
80 t
= msi_alloc( 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 msi_free( db
->deletefile
);
101 msi_free( db
->tempfolder
);
104 static HRESULT
db_initialize( IStorage
*stg
, const GUID
*clsid
)
106 static const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
109 hr
= IStorage_SetClass( stg
, clsid
);
112 WARN("failed to set class id 0x%08x\n", hr
);
116 /* create the _Tables stream */
117 hr
= write_stream_data( stg
, szTables
, NULL
, 0, TRUE
);
120 WARN("failed to create _Tables stream 0x%08x\n", hr
);
124 hr
= msi_init_string_table( stg
);
127 WARN("failed to initialize string table 0x%08x\n", hr
);
131 hr
= IStorage_Commit( stg
, 0 );
134 WARN("failed to commit changes 0x%08x\n", hr
);
141 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
143 IStorage
*stg
= NULL
;
145 MSIDATABASE
*db
= NULL
;
146 UINT ret
= ERROR_FUNCTION_FAILED
;
150 BOOL created
= FALSE
, patch
= FALSE
;
151 WCHAR path
[MAX_PATH
];
153 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
156 return ERROR_INVALID_PARAMETER
;
158 save_path
= szDBPath
;
159 if ( IS_INTMSIDBOPEN(szPersist
) )
161 mode
= LOWORD(szPersist
);
165 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
166 return ERROR_OPEN_FAILED
;
168 szDBPath
= szPersist
;
169 mode
= MSI_OPEN_TRANSACT
;
173 if ((mode
& MSI_OPEN_PATCHFILE
) == MSI_OPEN_PATCHFILE
)
175 TRACE("Database is a patch\n");
176 mode
&= ~MSI_OPEN_PATCHFILE
;
180 if( mode
== MSI_OPEN_READONLY
)
182 r
= StgOpenStorage( szDBPath
, NULL
,
183 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
185 else if( mode
== MSI_OPEN_CREATE
)
187 r
= StgCreateDocfile( szDBPath
,
188 STGM_CREATE
|STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
191 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
194 else if( mode
== MSI_OPEN_CREATEDIRECT
)
196 r
= StgCreateDocfile( szDBPath
,
197 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
200 r
= db_initialize( stg
, patch
? &CLSID_MsiPatch
: &CLSID_MsiDatabase
);
203 else if( mode
== MSI_OPEN_TRANSACT
)
205 r
= StgOpenStorage( szDBPath
, NULL
,
206 STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
208 else if( mode
== MSI_OPEN_DIRECT
)
210 r
= StgOpenStorage( szDBPath
, NULL
,
211 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
215 ERR("unknown flag %x\n",mode
);
216 return ERROR_INVALID_PARAMETER
;
219 if( FAILED( r
) || !stg
)
221 WARN("open failed r = %08x for %s\n", r
, debugstr_w(szDBPath
));
222 return ERROR_FUNCTION_FAILED
;
225 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
228 FIXME("Failed to stat storage\n");
232 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
233 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) &&
234 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiTransform
) )
236 ERR("storage GUID is not a MSI database GUID %s\n",
237 debugstr_guid(&stat
.clsid
) );
241 if ( patch
&& !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
243 ERR("storage GUID is not the MSI patch GUID %s\n",
244 debugstr_guid(&stat
.clsid
) );
245 ret
= ERROR_OPEN_FAILED
;
249 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
253 FIXME("Failed to allocate a handle\n");
257 if (!wcschr( save_path
, '\\' ))
259 GetCurrentDirectoryW( MAX_PATH
, path
);
260 lstrcatW( path
, szBackSlash
);
261 lstrcatW( path
, save_path
);
264 lstrcpyW( path
, save_path
);
266 db
->path
= strdupW( path
);
267 db
->media_transform_offset
= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
;
268 db
->media_transform_disk_id
= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
;
270 if( TRACE_ON( msi
) )
271 enum_stream_names( stg
);
276 db
->deletefile
= strdupW( szDBPath
);
277 list_init( &db
->tables
);
278 list_init( &db
->transforms
);
280 db
->strings
= msi_load_string_table( stg
, &db
->bytes_per_strref
);
286 msiobj_addref( &db
->hdr
);
287 IStorage_AddRef( stg
);
292 msiobj_release( &db
->hdr
);
294 IStorage_Release( stg
);
299 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
304 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
306 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
307 if( ret
== ERROR_SUCCESS
)
309 *phDB
= alloc_msihandle( &db
->hdr
);
311 ret
= ERROR_NOT_ENOUGH_MEMORY
;
312 msiobj_release( &db
->hdr
);
318 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
320 HRESULT r
= ERROR_FUNCTION_FAILED
;
321 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
323 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
327 szwDBPath
= strdupAtoW( szDBPath
);
332 if( !IS_INTMSIDBOPEN(szPersist
) )
334 szwPersist
= strdupAtoW( szPersist
);
339 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
341 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
344 if( !IS_INTMSIDBOPEN(szPersist
) )
345 msi_free( szwPersist
);
346 msi_free( szwDBPath
);
351 static LPWSTR
msi_read_text_archive(LPCWSTR path
, DWORD
*len
)
356 DWORD read
, size
= 0;
358 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
359 if (file
== INVALID_HANDLE_VALUE
)
362 size
= GetFileSize( file
, NULL
);
363 if (!(data
= msi_alloc( size
))) goto done
;
365 if (!ReadFile( file
, data
, size
, &read
, NULL
) || read
!= size
) goto done
;
367 while (!data
[size
- 1]) size
--;
368 *len
= MultiByteToWideChar( CP_ACP
, 0, data
, size
, NULL
, 0 );
369 if ((wdata
= msi_alloc( (*len
+ 1) * sizeof(WCHAR
) )))
371 MultiByteToWideChar( CP_ACP
, 0, data
, size
, wdata
, *len
);
381 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
, DWORD
*len
)
383 LPWSTR ptr
= *line
, save
;
384 DWORD i
, count
= 1, chars_left
= *len
;
388 /* stay on this line */
389 while (chars_left
&& *ptr
!= '\n')
391 /* entries are separated by tabs */
399 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
403 /* store pointers into the data */
405 for (i
= 0, ptr
= *line
; i
< count
; i
++)
407 while (chars_left
&& *ptr
== '\r')
414 while (chars_left
&& *ptr
!= '\t' && *ptr
!= '\n' && *ptr
!= '\r')
416 if (!*ptr
) *ptr
= '\n'; /* convert embedded nulls to \n */
417 if (ptr
> *line
&& *ptr
== '\x19' && *(ptr
- 1) == '\x11')
426 /* NULL-separate the data */
427 if (*ptr
== '\n' || *ptr
== '\r')
429 while (chars_left
&& (*ptr
== '\n' || *ptr
== '\r'))
440 (*entries
)[i
] = save
;
443 /* move to the next line if there's more, else EOF */
447 *num_entries
= count
;
450 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
455 static const WCHAR create_fmt
[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
457 size
= ARRAY_SIZE(create_fmt
) + lstrlenW(table
) - 2;
458 prelude
= msi_alloc(size
* sizeof(WCHAR
));
462 swprintf(prelude
, size
, create_fmt
, table
);
466 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
470 DWORD sql_size
= 1, i
, len
;
471 WCHAR expanded
[128], *ptr
;
472 WCHAR size
[10], comma
[2], extra
[30];
474 static const WCHAR column_fmt
[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
475 static const WCHAR size_fmt
[] = {'(','%','s',')',0};
476 static const WCHAR type_char
[] = {'C','H','A','R',0};
477 static const WCHAR type_int
[] = {'I','N','T',0};
478 static const WCHAR type_long
[] = {'L','O','N','G',0};
479 static const WCHAR type_object
[] = {'O','B','J','E','C','T',0};
480 static const WCHAR type_notnull
[] = {' ','N','O','T',' ','N','U','L','L',0};
481 static const WCHAR localizable
[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
483 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
487 for (i
= 0; i
< num_columns
; i
++)
490 comma
[1] = size
[0] = extra
[0] = '\0';
492 if (i
== num_columns
- 1)
498 len
= wcstol(ptr
, NULL
, 10);
504 lstrcpyW(extra
, type_notnull
);
507 lstrcatW(extra
, localizable
);
509 swprintf(size
, ARRAY_SIZE(size
), size_fmt
, ptr
);
512 lstrcpyW(extra
, type_notnull
);
516 swprintf(size
, ARRAY_SIZE(size
), size_fmt
, ptr
);
519 lstrcpyW(extra
, type_notnull
);
528 WARN("invalid int width %u\n", len
);
534 lstrcpyW(extra
, type_notnull
);
540 ERR("Unknown type: %c\n", types
[i
][0]);
545 swprintf(expanded
, ARRAY_SIZE(expanded
), column_fmt
, columns_data
[i
], type
, size
, extra
, comma
);
546 sql_size
+= lstrlenW(expanded
);
548 p
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
556 lstrcatW(columns
, expanded
);
562 static LPWSTR
msi_build_createsql_postlude(LPWSTR
*primary_keys
, DWORD num_keys
)
564 LPWSTR postlude
, keys
, ptr
;
567 static const WCHAR key_fmt
[] = {'`','%','s','`',',',' ',0};
568 static const WCHAR postlude_fmt
[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
570 for (i
= 0, size
= 1; i
< num_keys
; i
++)
571 size
+= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) - 2;
573 keys
= msi_alloc(size
* sizeof(WCHAR
));
577 for (i
= 0, ptr
= keys
; i
< num_keys
; i
++)
579 ptr
+= swprintf(ptr
, size
- (ptr
- keys
), key_fmt
, primary_keys
[i
]);
582 /* remove final ', ' */
585 size
= lstrlenW(postlude_fmt
) + size
- 1;
586 postlude
= msi_alloc(size
* sizeof(WCHAR
));
590 swprintf(postlude
, size
, postlude_fmt
, keys
);
597 static UINT
msi_add_table_to_db(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
, LPWSTR
*labels
, DWORD num_labels
, DWORD num_columns
)
599 UINT r
= ERROR_OUTOFMEMORY
;
602 LPWSTR create_sql
= NULL
;
603 LPWSTR prelude
, columns_sql
, postlude
;
605 prelude
= msi_build_createsql_prelude(labels
[0]);
606 columns_sql
= msi_build_createsql_columns(columns
, types
, num_columns
);
607 postlude
= msi_build_createsql_postlude(labels
+ 1, num_labels
- 1); /* skip over table name */
609 if (!prelude
|| !columns_sql
|| !postlude
)
612 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
613 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
617 lstrcpyW(create_sql
, prelude
);
618 lstrcatW(create_sql
, columns_sql
);
619 lstrcatW(create_sql
, postlude
);
621 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
622 if (r
!= ERROR_SUCCESS
)
625 r
= MSI_ViewExecute(view
, NULL
);
627 msiobj_release(&view
->hdr
);
631 msi_free(columns_sql
);
633 msi_free(create_sql
);
637 static LPWSTR
msi_import_stream_filename(LPCWSTR path
, LPCWSTR name
)
640 LPWSTR fullname
, ptr
;
642 len
= lstrlenW(path
) + lstrlenW(name
) + 1;
643 fullname
= msi_alloc(len
*sizeof(WCHAR
));
647 lstrcpyW( fullname
, path
);
649 /* chop off extension from path */
650 ptr
= wcsrchr(fullname
, '.');
657 lstrcpyW( ptr
, name
);
661 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
662 LPWSTR
*data
, LPWSTR path
, MSIRECORD
**rec
)
666 *rec
= MSI_CreateRecord(num_columns
);
668 return ERROR_OUTOFMEMORY
;
670 for (i
= 0; i
< num_columns
; i
++)
674 case 'L': case 'l': case 'S': case 's':
675 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
679 MSI_RecordSetInteger(*rec
, i
+ 1, wcstol(data
[i
], NULL
, 10));
685 LPWSTR file
= msi_import_stream_filename(path
, data
[i
]);
687 return ERROR_FUNCTION_FAILED
;
689 r
= MSI_RecordSetStreamFromFileW(*rec
, i
+ 1, file
);
691 if (r
!= ERROR_SUCCESS
)
692 return ERROR_FUNCTION_FAILED
;
696 ERR("Unhandled column type: %c\n", types
[i
][0]);
697 msiobj_release(&(*rec
)->hdr
);
698 return ERROR_FUNCTION_FAILED
;
702 return ERROR_SUCCESS
;
705 static UINT
msi_add_records_to_table(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
,
706 LPWSTR
*labels
, LPWSTR
**records
,
707 int num_columns
, int num_records
,
715 static const WCHAR select
[] = {
716 'S','E','L','E','C','T',' ','*',' ',
717 'F','R','O','M',' ','`','%','s','`',0
720 r
= MSI_OpenQuery(db
, &view
, select
, labels
[0]);
721 if (r
!= ERROR_SUCCESS
)
724 while (MSI_ViewFetch(view
, &rec
) != ERROR_NO_MORE_ITEMS
)
726 r
= MSI_ViewModify(view
, MSIMODIFY_DELETE
, rec
);
727 msiobj_release(&rec
->hdr
);
728 if (r
!= ERROR_SUCCESS
)
732 for (i
= 0; i
< num_records
; i
++)
734 r
= construct_record(num_columns
, types
, records
[i
], path
, &rec
);
735 if (r
!= ERROR_SUCCESS
)
738 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
739 if (r
!= ERROR_SUCCESS
)
741 msiobj_release(&rec
->hdr
);
745 msiobj_release(&rec
->hdr
);
749 msiobj_release(&view
->hdr
);
753 static UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
757 DWORD num_labels
, num_types
;
758 DWORD num_columns
, num_records
= 0;
759 LPWSTR
*columns
, *types
, *labels
;
760 LPWSTR path
, ptr
, data
;
761 LPWSTR
**records
= NULL
;
762 LPWSTR
**temp_records
;
764 static const WCHAR suminfo
[] =
765 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
766 static const WCHAR forcecodepage
[] =
767 {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
769 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
771 if( folder
== NULL
|| file
== NULL
)
772 return ERROR_INVALID_PARAMETER
;
774 len
= lstrlenW(folder
) + lstrlenW(szBackSlash
) + lstrlenW(file
) + 1;
775 path
= msi_alloc( len
* sizeof(WCHAR
) );
777 return ERROR_OUTOFMEMORY
;
779 lstrcpyW( path
, folder
);
780 lstrcatW( path
, szBackSlash
);
781 lstrcatW( path
, file
);
783 data
= msi_read_text_archive( path
, &len
);
787 return ERROR_FUNCTION_FAILED
;
791 msi_parse_line( &ptr
, &columns
, &num_columns
, &len
);
792 msi_parse_line( &ptr
, &types
, &num_types
, &len
);
793 msi_parse_line( &ptr
, &labels
, &num_labels
, &len
);
795 if (num_columns
== 1 && !columns
[0][0] && num_labels
== 1 && !labels
[0][0] &&
796 num_types
== 2 && !wcscmp( types
[1], forcecodepage
))
798 r
= msi_set_string_table_codepage( db
->strings
, wcstol( types
[0], NULL
, 10 ) );
802 if (num_columns
!= num_types
)
804 r
= ERROR_FUNCTION_FAILED
;
808 records
= msi_alloc(sizeof(LPWSTR
*));
811 r
= ERROR_OUTOFMEMORY
;
815 /* read in the table records */
818 msi_parse_line( &ptr
, &records
[num_records
], NULL
, &len
);
821 temp_records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
824 r
= ERROR_OUTOFMEMORY
;
827 records
= temp_records
;
830 if (!wcscmp(labels
[0], suminfo
))
832 r
= msi_add_suminfo( db
, records
, num_records
, num_columns
);
833 if (r
!= ERROR_SUCCESS
)
835 r
= ERROR_FUNCTION_FAILED
;
841 if (!TABLE_Exists(db
, labels
[0]))
843 r
= msi_add_table_to_db( db
, columns
, types
, labels
, num_labels
, num_columns
);
844 if (r
!= ERROR_SUCCESS
)
846 r
= ERROR_FUNCTION_FAILED
;
851 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
, path
);
861 for (i
= 0; i
< num_records
; i
++)
862 msi_free(records
[i
]);
869 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
874 TRACE("%x %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
876 if (!(db
= msihandle2msiinfo(handle
, MSIHANDLETYPE_DATABASE
)))
877 return ERROR_INVALID_HANDLE
;
879 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
880 msiobj_release( &db
->hdr
);
884 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
885 LPCSTR szFolder
, LPCSTR szFilename
)
887 LPWSTR path
= NULL
, file
= NULL
;
888 UINT r
= ERROR_OUTOFMEMORY
;
890 TRACE("%x %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
894 path
= strdupAtoW( szFolder
);
901 file
= strdupAtoW( szFilename
);
906 r
= MsiDatabaseImportW( handle
, path
, file
);
915 static UINT
msi_export_field( HANDLE handle
, MSIRECORD
*row
, UINT field
)
922 buffer
= msi_alloc( sz
);
924 return ERROR_OUTOFMEMORY
;
926 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
927 if (r
== ERROR_MORE_DATA
)
931 sz
++; /* leave room for NULL terminator */
932 tmp
= msi_realloc( buffer
, sz
);
936 return ERROR_OUTOFMEMORY
;
940 r
= MSI_RecordGetStringA( row
, field
, buffer
, &sz
);
941 if (r
!= ERROR_SUCCESS
)
947 else if (r
!= ERROR_SUCCESS
)
953 ret
= WriteFile( handle
, buffer
, sz
, &sz
, NULL
);
955 return ret
? ERROR_SUCCESS
: ERROR_FUNCTION_FAILED
;
958 static UINT
msi_export_stream( const WCHAR
*folder
, const WCHAR
*table
, MSIRECORD
*row
, UINT field
, UINT start
)
960 static const WCHAR fmt
[] = {'%','s','\\','%','s',0};
961 WCHAR stream
[MAX_STREAM_NAME_LEN
+ 1], *path
;
962 DWORD sz
, read_size
, write_size
;
967 sz
= ARRAY_SIZE( stream
);
968 r
= MSI_RecordGetStringW( row
, start
, stream
, &sz
);
969 if (r
!= ERROR_SUCCESS
)
972 len
= sz
+ lstrlenW( folder
) + lstrlenW( table
) + ARRAY_SIZE( fmt
) + 1;
973 if (!(path
= msi_alloc( len
* sizeof(WCHAR
) )))
974 return ERROR_OUTOFMEMORY
;
976 len
= swprintf( path
, len
, fmt
, folder
, table
);
977 if (!CreateDirectoryW( path
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
)
980 return ERROR_FUNCTION_FAILED
;
984 lstrcpyW( path
+ len
, stream
);
985 file
= CreateFileW( path
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
986 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
988 if (file
== INVALID_HANDLE_VALUE
)
989 return ERROR_FUNCTION_FAILED
;
991 read_size
= sizeof(buffer
);
992 while (read_size
== sizeof(buffer
))
994 r
= MSI_RecordReadStream( row
, field
, buffer
, &read_size
);
995 if (r
!= ERROR_SUCCESS
)
1000 if (!WriteFile( file
, buffer
, read_size
, &write_size
, NULL
) || read_size
!= write_size
)
1002 CloseHandle( file
);
1003 return ERROR_WRITE_FAULT
;
1006 CloseHandle( file
);
1010 struct row_export_info
1013 const WCHAR
*folder
;
1017 static UINT
msi_export_record( struct row_export_info
*row_export_info
, MSIRECORD
*row
, UINT start
)
1019 HANDLE handle
= row_export_info
->handle
;
1020 UINT i
, count
, r
= ERROR_SUCCESS
;
1024 count
= MSI_RecordGetFieldCount( row
);
1025 for (i
= start
; i
<= count
; i
++)
1027 r
= msi_export_field( handle
, row
, i
);
1028 if (r
== ERROR_INVALID_PARAMETER
)
1030 r
= msi_export_stream( row_export_info
->folder
, row_export_info
->table
, row
, i
, start
);
1031 if (r
!= ERROR_SUCCESS
)
1034 /* exporting a binary stream, repeat the "Name" field */
1035 r
= msi_export_field( handle
, row
, start
);
1036 if (r
!= ERROR_SUCCESS
)
1039 else if (r
!= ERROR_SUCCESS
)
1042 sep
= (i
< count
) ? "\t" : "\r\n";
1043 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
1044 return ERROR_FUNCTION_FAILED
;
1049 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
1051 return msi_export_record( arg
, row
, 1 );
1054 static UINT
msi_export_forcecodepage( HANDLE handle
, UINT codepage
)
1056 static const char fmt
[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1057 char data
[sizeof(fmt
) + 10];
1058 DWORD sz
= sprintf( data
, fmt
, codepage
);
1060 if (!WriteFile(handle
, data
, sz
, &sz
, NULL
))
1061 return ERROR_FUNCTION_FAILED
;
1063 return ERROR_SUCCESS
;
1066 static UINT
msi_export_summaryinformation( MSIDATABASE
*db
, HANDLE handle
)
1068 static const char header
[] = "PropertyId\tValue\r\n"
1070 "_SummaryInformation\tPropertyId\r\n";
1071 DWORD sz
= ARRAY_SIZE(header
) - 1;
1073 if (!WriteFile(handle
, header
, sz
, &sz
, NULL
))
1074 return ERROR_WRITE_FAULT
;
1076 return msi_export_suminfo( db
, handle
);
1079 static UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
, LPCWSTR folder
, LPCWSTR file
)
1081 static const WCHAR query
[] = {
1082 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1083 static const WCHAR forcecodepage
[] = {
1084 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1085 static const WCHAR summaryinformation
[] = {
1086 '_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
1087 MSIRECORD
*rec
= NULL
;
1088 MSIQUERY
*view
= NULL
;
1093 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
1094 debugstr_w(folder
), debugstr_w(file
) );
1096 if( folder
== NULL
|| file
== NULL
)
1097 return ERROR_INVALID_PARAMETER
;
1099 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
1100 filename
= msi_alloc(len
* sizeof (WCHAR
));
1102 return ERROR_OUTOFMEMORY
;
1104 lstrcpyW( filename
, folder
);
1105 lstrcatW( filename
, szBackSlash
);
1106 lstrcatW( filename
, file
);
1108 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
1109 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1110 msi_free( filename
);
1111 if (handle
== INVALID_HANDLE_VALUE
)
1112 return ERROR_FUNCTION_FAILED
;
1114 if (!wcscmp( table
, forcecodepage
))
1116 UINT codepage
= msi_get_string_table_codepage( db
->strings
);
1117 r
= msi_export_forcecodepage( handle
, codepage
);
1121 if (!wcscmp( table
, summaryinformation
))
1123 r
= msi_export_summaryinformation( db
, handle
);
1127 r
= MSI_OpenQuery( db
, &view
, query
, table
);
1128 if (r
== ERROR_SUCCESS
)
1130 struct row_export_info row_export_info
= { handle
, folder
, table
};
1132 /* write out row 1, the column names */
1133 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
1134 if (r
== ERROR_SUCCESS
)
1136 msi_export_record( &row_export_info
, rec
, 1 );
1137 msiobj_release( &rec
->hdr
);
1140 /* write out row 2, the column types */
1141 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
1142 if (r
== ERROR_SUCCESS
)
1144 msi_export_record( &row_export_info
, rec
, 1 );
1145 msiobj_release( &rec
->hdr
);
1148 /* write out row 3, the table name + keys */
1149 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
1150 if (r
== ERROR_SUCCESS
)
1152 MSI_RecordSetStringW( rec
, 0, table
);
1153 msi_export_record( &row_export_info
, rec
, 0 );
1154 msiobj_release( &rec
->hdr
);
1157 /* write out row 4 onwards, the data */
1158 r
= MSI_IterateRecords( view
, 0, msi_export_row
, &row_export_info
);
1159 msiobj_release( &view
->hdr
);
1163 CloseHandle( handle
);
1167 /***********************************************************************
1168 * MsiExportDatabaseW [MSI.@]
1170 * Writes a file containing the table data as tab separated ASCII.
1172 * The format is as follows:
1174 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1175 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1176 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1178 * Followed by the data, starting at row 1 with one row per line
1180 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1182 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
1183 LPCWSTR szFolder
, LPCWSTR szFilename
)
1188 TRACE("%x %s %s %s\n", handle
, debugstr_w(szTable
),
1189 debugstr_w(szFolder
), debugstr_w(szFilename
));
1191 if (!(db
= msihandle2msiinfo(handle
, MSIHANDLETYPE_DATABASE
)))
1192 return ERROR_INVALID_HANDLE
;
1194 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
1195 msiobj_release( &db
->hdr
);
1199 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
1200 LPCSTR szFolder
, LPCSTR szFilename
)
1202 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
1203 UINT r
= ERROR_OUTOFMEMORY
;
1205 TRACE("%x %s %s %s\n", handle
, debugstr_a(szTable
),
1206 debugstr_a(szFolder
), debugstr_a(szFilename
));
1210 table
= strdupAtoW( szTable
);
1217 path
= strdupAtoW( szFolder
);
1224 file
= strdupAtoW( szFilename
);
1229 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
1239 UINT WINAPI
MsiDatabaseMergeA(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1245 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1246 debugstr_a(szTableName
));
1248 table
= strdupAtoW(szTableName
);
1249 r
= MsiDatabaseMergeW(hDatabase
, hDatabaseMerge
, table
);
1255 typedef struct _tagMERGETABLE
1269 typedef struct _tagMERGEROW
1275 typedef struct _tagMERGEDATA
1279 MERGETABLE
*curtable
;
1281 struct list
*tabledata
;
1284 static BOOL
merge_type_match(LPCWSTR type1
, LPCWSTR type2
)
1286 if (((type1
[0] == 'l') || (type1
[0] == 's')) &&
1287 ((type2
[0] == 'l') || (type2
[0] == 's')))
1290 if (((type1
[0] == 'L') || (type1
[0] == 'S')) &&
1291 ((type2
[0] == 'L') || (type2
[0] == 'S')))
1294 return !wcscmp( type1
, type2
);
1297 static UINT
merge_verify_colnames(MSIQUERY
*dbview
, MSIQUERY
*mergeview
)
1299 MSIRECORD
*dbrec
, *mergerec
;
1302 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_NAMES
, &dbrec
);
1303 if (r
!= ERROR_SUCCESS
)
1306 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_NAMES
, &mergerec
);
1307 if (r
!= ERROR_SUCCESS
)
1309 msiobj_release(&dbrec
->hdr
);
1313 count
= MSI_RecordGetFieldCount(dbrec
);
1314 for (i
= 1; i
<= count
; i
++)
1316 if (!MSI_RecordGetString(mergerec
, i
))
1319 if (wcscmp( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1321 r
= ERROR_DATATYPE_MISMATCH
;
1326 msiobj_release(&dbrec
->hdr
);
1327 msiobj_release(&mergerec
->hdr
);
1328 dbrec
= mergerec
= NULL
;
1330 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_TYPES
, &dbrec
);
1331 if (r
!= ERROR_SUCCESS
)
1334 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_TYPES
, &mergerec
);
1335 if (r
!= ERROR_SUCCESS
)
1337 msiobj_release(&dbrec
->hdr
);
1341 count
= MSI_RecordGetFieldCount(dbrec
);
1342 for (i
= 1; i
<= count
; i
++)
1344 if (!MSI_RecordGetString(mergerec
, i
))
1347 if (!merge_type_match(MSI_RecordGetString(dbrec
, i
),
1348 MSI_RecordGetString(mergerec
, i
)))
1350 r
= ERROR_DATATYPE_MISMATCH
;
1356 msiobj_release(&dbrec
->hdr
);
1357 msiobj_release(&mergerec
->hdr
);
1362 static UINT
merge_verify_primary_keys(MSIDATABASE
*db
, MSIDATABASE
*mergedb
,
1365 MSIRECORD
*dbrec
, *mergerec
= NULL
;
1368 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &dbrec
);
1369 if (r
!= ERROR_SUCCESS
)
1372 r
= MSI_DatabaseGetPrimaryKeys(mergedb
, table
, &mergerec
);
1373 if (r
!= ERROR_SUCCESS
)
1376 count
= MSI_RecordGetFieldCount(dbrec
);
1377 if (count
!= MSI_RecordGetFieldCount(mergerec
))
1379 r
= ERROR_DATATYPE_MISMATCH
;
1383 for (i
= 1; i
<= count
; i
++)
1385 if (wcscmp( MSI_RecordGetString( dbrec
, i
), MSI_RecordGetString( mergerec
, i
) ))
1387 r
= ERROR_DATATYPE_MISMATCH
;
1393 msiobj_release(&dbrec
->hdr
);
1394 msiobj_release(&mergerec
->hdr
);
1399 static LPWSTR
get_key_value(MSIQUERY
*view
, LPCWSTR key
, MSIRECORD
*rec
)
1401 MSIRECORD
*colnames
;
1403 UINT r
, i
= 0, sz
= 0;
1406 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &colnames
);
1407 if (r
!= ERROR_SUCCESS
)
1412 str
= msi_dup_record_field(colnames
, ++i
);
1413 cmp
= wcscmp( key
, str
);
1417 msiobj_release(&colnames
->hdr
);
1419 r
= MSI_RecordGetStringW(rec
, i
, NULL
, &sz
);
1420 if (r
!= ERROR_SUCCESS
)
1424 if (MSI_RecordGetString(rec
, i
)) /* check record field is a string */
1426 /* quote string record fields */
1427 static const WCHAR szQuote
[] = {'\'', 0};
1429 val
= msi_alloc(sz
*sizeof(WCHAR
));
1433 lstrcpyW(val
, szQuote
);
1434 r
= MSI_RecordGetStringW(rec
, i
, val
+1, &sz
);
1435 lstrcpyW(val
+1+sz
, szQuote
);
1439 /* do not quote integer record fields */
1440 val
= msi_alloc(sz
*sizeof(WCHAR
));
1444 r
= MSI_RecordGetStringW(rec
, i
, val
, &sz
);
1447 if (r
!= ERROR_SUCCESS
)
1449 ERR("failed to get string!\n");
1457 static LPWSTR
create_diff_row_query(MSIDATABASE
*merge
, MSIQUERY
*view
,
1458 LPWSTR table
, MSIRECORD
*rec
)
1460 LPWSTR query
= NULL
, clause
= NULL
, val
;
1461 LPCWSTR setptr
, key
;
1462 DWORD size
, oldsize
;
1466 static const WCHAR keyset
[] = {
1467 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1468 static const WCHAR lastkeyset
[] = {
1469 '`','%','s','`',' ','=',' ','%','s',' ',0};
1470 static const WCHAR fmt
[] = {'S','E','L','E','C','T',' ','*',' ',
1471 'F','R','O','M',' ','`','%','s','`',' ',
1472 'W','H','E','R','E',' ','%','s',0};
1474 r
= MSI_DatabaseGetPrimaryKeys(merge
, table
, &keys
);
1475 if (r
!= ERROR_SUCCESS
)
1478 clause
= msi_alloc_zero(sizeof(WCHAR
));
1483 count
= MSI_RecordGetFieldCount(keys
);
1484 for (i
= 1; i
<= count
; i
++)
1486 key
= MSI_RecordGetString(keys
, i
);
1487 val
= get_key_value(view
, key
, rec
);
1490 setptr
= lastkeyset
;
1495 size
+= lstrlenW(setptr
) + lstrlenW(key
) + lstrlenW(val
) - 4;
1496 clause
= msi_realloc(clause
, size
* sizeof (WCHAR
));
1503 swprintf(clause
+ oldsize
- 1, size
- (oldsize
- 1), setptr
, key
, val
);
1507 size
= lstrlenW(fmt
) + lstrlenW(table
) + lstrlenW(clause
) + 1;
1508 query
= msi_alloc(size
* sizeof(WCHAR
));
1512 swprintf(query
, size
, fmt
, table
, clause
);
1516 msiobj_release(&keys
->hdr
);
1520 static UINT
merge_diff_row(MSIRECORD
*rec
, LPVOID param
)
1522 MERGEDATA
*data
= param
;
1523 MERGETABLE
*table
= data
->curtable
;
1525 MSIQUERY
*dbview
= NULL
;
1526 MSIRECORD
*row
= NULL
;
1527 LPWSTR query
= NULL
;
1528 UINT r
= ERROR_SUCCESS
;
1530 if (TABLE_Exists(data
->db
, table
->name
))
1532 query
= create_diff_row_query(data
->merge
, data
->curview
, table
->name
, rec
);
1534 return ERROR_OUTOFMEMORY
;
1536 r
= MSI_DatabaseOpenViewW(data
->db
, query
, &dbview
);
1537 if (r
!= ERROR_SUCCESS
)
1540 r
= MSI_ViewExecute(dbview
, NULL
);
1541 if (r
!= ERROR_SUCCESS
)
1544 r
= MSI_ViewFetch(dbview
, &row
);
1545 if (r
== ERROR_SUCCESS
&& !MSI_RecordsAreEqual(rec
, row
))
1547 table
->numconflicts
++;
1550 else if (r
!= ERROR_NO_MORE_ITEMS
)
1556 mergerow
= msi_alloc(sizeof(MERGEROW
));
1559 r
= ERROR_OUTOFMEMORY
;
1563 mergerow
->data
= MSI_CloneRecord(rec
);
1564 if (!mergerow
->data
)
1566 r
= ERROR_OUTOFMEMORY
;
1571 list_add_tail(&table
->rows
, &mergerow
->entry
);
1575 msiobj_release(&row
->hdr
);
1576 msiobj_release(&dbview
->hdr
);
1580 static UINT
msi_get_table_labels(MSIDATABASE
*db
, LPCWSTR table
, LPWSTR
**labels
, DWORD
*numlabels
)
1583 MSIRECORD
*prec
= NULL
;
1585 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &prec
);
1586 if (r
!= ERROR_SUCCESS
)
1589 count
= MSI_RecordGetFieldCount(prec
);
1590 *numlabels
= count
+ 1;
1591 *labels
= msi_alloc((*numlabels
)*sizeof(LPWSTR
));
1594 r
= ERROR_OUTOFMEMORY
;
1598 (*labels
)[0] = strdupW(table
);
1599 for (i
=1; i
<=count
; i
++ )
1601 (*labels
)[i
] = strdupW(MSI_RecordGetString(prec
, i
));
1605 msiobj_release( &prec
->hdr
);
1609 static UINT
msi_get_query_columns(MSIQUERY
*query
, LPWSTR
**columns
, DWORD
*numcolumns
)
1612 MSIRECORD
*prec
= NULL
;
1614 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_NAMES
, &prec
);
1615 if (r
!= ERROR_SUCCESS
)
1618 count
= MSI_RecordGetFieldCount(prec
);
1619 *columns
= msi_alloc(count
*sizeof(LPWSTR
));
1622 r
= ERROR_OUTOFMEMORY
;
1626 for (i
=1; i
<=count
; i
++ )
1628 (*columns
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1631 *numcolumns
= count
;
1634 msiobj_release( &prec
->hdr
);
1638 static UINT
msi_get_query_types(MSIQUERY
*query
, LPWSTR
**types
, DWORD
*numtypes
)
1641 MSIRECORD
*prec
= NULL
;
1643 r
= MSI_ViewGetColumnInfo(query
, MSICOLINFO_TYPES
, &prec
);
1644 if (r
!= ERROR_SUCCESS
)
1647 count
= MSI_RecordGetFieldCount(prec
);
1648 *types
= msi_alloc(count
*sizeof(LPWSTR
));
1651 r
= ERROR_OUTOFMEMORY
;
1656 for (i
=1; i
<=count
; i
++ )
1658 (*types
)[i
-1] = strdupW(MSI_RecordGetString(prec
, i
));
1662 msiobj_release( &prec
->hdr
);
1666 static void merge_free_rows(MERGETABLE
*table
)
1668 struct list
*item
, *cursor
;
1670 LIST_FOR_EACH_SAFE(item
, cursor
, &table
->rows
)
1672 MERGEROW
*row
= LIST_ENTRY(item
, MERGEROW
, entry
);
1674 list_remove(&row
->entry
);
1675 msiobj_release(&row
->data
->hdr
);
1680 static void free_merge_table(MERGETABLE
*table
)
1684 if (table
->labels
!= NULL
)
1686 for (i
= 0; i
< table
->numlabels
; i
++)
1687 msi_free(table
->labels
[i
]);
1689 msi_free(table
->labels
);
1692 if (table
->columns
!= NULL
)
1694 for (i
= 0; i
< table
->numcolumns
; i
++)
1695 msi_free(table
->columns
[i
]);
1697 msi_free(table
->columns
);
1700 if (table
->types
!= NULL
)
1702 for (i
= 0; i
< table
->numtypes
; i
++)
1703 msi_free(table
->types
[i
]);
1705 msi_free(table
->types
);
1708 msi_free(table
->name
);
1709 merge_free_rows(table
);
1714 static UINT
msi_get_merge_table (MSIDATABASE
*db
, LPCWSTR name
, MERGETABLE
**ptable
)
1718 MSIQUERY
*mergeview
= NULL
;
1720 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1721 'F','R','O','M',' ','`','%','s','`',0};
1723 table
= msi_alloc_zero(sizeof(MERGETABLE
));
1727 return ERROR_OUTOFMEMORY
;
1730 r
= msi_get_table_labels(db
, name
, &table
->labels
, &table
->numlabels
);
1731 if (r
!= ERROR_SUCCESS
)
1734 r
= MSI_OpenQuery(db
, &mergeview
, query
, name
);
1735 if (r
!= ERROR_SUCCESS
)
1738 r
= msi_get_query_columns(mergeview
, &table
->columns
, &table
->numcolumns
);
1739 if (r
!= ERROR_SUCCESS
)
1742 r
= msi_get_query_types(mergeview
, &table
->types
, &table
->numtypes
);
1743 if (r
!= ERROR_SUCCESS
)
1746 list_init(&table
->rows
);
1748 table
->name
= strdupW(name
);
1749 table
->numconflicts
= 0;
1751 msiobj_release(&mergeview
->hdr
);
1753 return ERROR_SUCCESS
;
1756 msiobj_release(&mergeview
->hdr
);
1757 free_merge_table(table
);
1762 static UINT
merge_diff_tables(MSIRECORD
*rec
, LPVOID param
)
1764 MERGEDATA
*data
= param
;
1766 MSIQUERY
*dbview
= NULL
;
1767 MSIQUERY
*mergeview
= NULL
;
1771 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1772 'F','R','O','M',' ','`','%','s','`',0};
1774 name
= MSI_RecordGetString(rec
, 1);
1776 r
= MSI_OpenQuery(data
->merge
, &mergeview
, query
, name
);
1777 if (r
!= ERROR_SUCCESS
)
1780 if (TABLE_Exists(data
->db
, name
))
1782 r
= MSI_OpenQuery(data
->db
, &dbview
, query
, name
);
1783 if (r
!= ERROR_SUCCESS
)
1786 r
= merge_verify_colnames(dbview
, mergeview
);
1787 if (r
!= ERROR_SUCCESS
)
1790 r
= merge_verify_primary_keys(data
->db
, data
->merge
, name
);
1791 if (r
!= ERROR_SUCCESS
)
1795 r
= msi_get_merge_table(data
->merge
, name
, &table
);
1796 if (r
!= ERROR_SUCCESS
)
1799 data
->curtable
= table
;
1800 data
->curview
= mergeview
;
1801 r
= MSI_IterateRecords(mergeview
, NULL
, merge_diff_row
, data
);
1802 if (r
!= ERROR_SUCCESS
)
1804 free_merge_table(table
);
1808 list_add_tail(data
->tabledata
, &table
->entry
);
1811 msiobj_release(&dbview
->hdr
);
1812 msiobj_release(&mergeview
->hdr
);
1816 static UINT
gather_merge_data(MSIDATABASE
*db
, MSIDATABASE
*merge
,
1817 struct list
*tabledata
)
1819 static const WCHAR query
[] = {
1820 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1821 '`','_','T','a','b','l','e','s','`',0};
1826 r
= MSI_DatabaseOpenViewW(merge
, query
, &view
);
1827 if (r
!= ERROR_SUCCESS
)
1832 data
.tabledata
= tabledata
;
1833 r
= MSI_IterateRecords(view
, NULL
, merge_diff_tables
, &data
);
1834 msiobj_release(&view
->hdr
);
1838 static UINT
merge_table(MSIDATABASE
*db
, MERGETABLE
*table
)
1844 if (!TABLE_Exists(db
, table
->name
))
1846 r
= msi_add_table_to_db(db
, table
->columns
, table
->types
,
1847 table
->labels
, table
->numlabels
, table
->numcolumns
);
1848 if (r
!= ERROR_SUCCESS
)
1849 return ERROR_FUNCTION_FAILED
;
1852 LIST_FOR_EACH_ENTRY(row
, &table
->rows
, MERGEROW
, entry
)
1854 r
= TABLE_CreateView(db
, table
->name
, &tv
);
1855 if (r
!= ERROR_SUCCESS
)
1858 r
= tv
->ops
->insert_row(tv
, row
->data
, -1, FALSE
);
1859 tv
->ops
->delete(tv
);
1861 if (r
!= ERROR_SUCCESS
)
1865 return ERROR_SUCCESS
;
1868 static UINT
update_merge_errors(MSIDATABASE
*db
, LPCWSTR error
,
1869 LPWSTR table
, DWORD numconflicts
)
1874 static const WCHAR create
[] = {
1875 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1876 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1877 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1878 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1879 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1880 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1881 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1882 static const WCHAR insert
[] = {
1883 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1884 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1885 '`','N','u','m','R','o','w','M','e','r','g','e',
1886 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1887 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1889 if (!TABLE_Exists(db
, error
))
1891 r
= MSI_OpenQuery(db
, &view
, create
, error
);
1892 if (r
!= ERROR_SUCCESS
)
1895 r
= MSI_ViewExecute(view
, NULL
);
1896 msiobj_release(&view
->hdr
);
1897 if (r
!= ERROR_SUCCESS
)
1901 r
= MSI_OpenQuery(db
, &view
, insert
, error
, table
, numconflicts
);
1902 if (r
!= ERROR_SUCCESS
)
1905 r
= MSI_ViewExecute(view
, NULL
);
1906 msiobj_release(&view
->hdr
);
1910 UINT WINAPI
MsiDatabaseMergeW(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1911 LPCWSTR szTableName
)
1913 struct list tabledata
= LIST_INIT(tabledata
);
1914 struct list
*item
, *cursor
;
1915 MSIDATABASE
*db
, *merge
;
1920 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1921 debugstr_w(szTableName
));
1923 if (szTableName
&& !*szTableName
)
1924 return ERROR_INVALID_TABLE
;
1926 db
= msihandle2msiinfo(hDatabase
, MSIHANDLETYPE_DATABASE
);
1927 merge
= msihandle2msiinfo(hDatabaseMerge
, MSIHANDLETYPE_DATABASE
);
1930 r
= ERROR_INVALID_HANDLE
;
1934 r
= gather_merge_data(db
, merge
, &tabledata
);
1935 if (r
!= ERROR_SUCCESS
)
1939 LIST_FOR_EACH_ENTRY(table
, &tabledata
, MERGETABLE
, entry
)
1941 if (table
->numconflicts
)
1945 r
= update_merge_errors(db
, szTableName
, table
->name
,
1946 table
->numconflicts
);
1947 if (r
!= ERROR_SUCCESS
)
1952 r
= merge_table(db
, table
);
1953 if (r
!= ERROR_SUCCESS
)
1958 LIST_FOR_EACH_SAFE(item
, cursor
, &tabledata
)
1960 MERGETABLE
*table
= LIST_ENTRY(item
, MERGETABLE
, entry
);
1961 list_remove(&table
->entry
);
1962 free_merge_table(table
);
1966 r
= ERROR_FUNCTION_FAILED
;
1969 msiobj_release(&db
->hdr
);
1970 msiobj_release(&merge
->hdr
);
1974 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
1976 MSIDBSTATE ret
= MSIDBSTATE_READ
;
1979 TRACE("%d\n", handle
);
1981 if (!(db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
)))
1982 return MSIDBSTATE_ERROR
;
1984 if (db
->mode
!= MSI_OPEN_READONLY
)
1985 ret
= MSIDBSTATE_WRITE
;
1986 msiobj_release( &db
->hdr
);
1991 MSICONDITION __cdecl
s_remote_DatabaseIsTablePersistent(MSIHANDLE db
, LPCWSTR table
)
1993 return MsiDatabaseIsTablePersistentW(db
, table
);
1996 UINT __cdecl
s_remote_DatabaseGetPrimaryKeys(MSIHANDLE db
, LPCWSTR table
, struct wire_record
**rec
)
1999 UINT r
= MsiDatabaseGetPrimaryKeysW(db
, table
, &handle
);
2002 *rec
= marshal_record(handle
);
2003 MsiCloseHandle(handle
);
2007 UINT __cdecl
s_remote_DatabaseGetSummaryInformation(MSIHANDLE db
, UINT updatecount
, MSIHANDLE
*suminfo
)
2009 return MsiGetSummaryInformationW(db
, NULL
, updatecount
, suminfo
);
2012 UINT __cdecl
s_remote_DatabaseOpenView(MSIHANDLE db
, LPCWSTR query
, MSIHANDLE
*view
)
2014 return MsiDatabaseOpenViewW(db
, query
, view
);