2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define NONAMELESSUNION
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
37 #include "msiserver.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
44 DEFINE_GUID( CLSID_MsiDatabase
, 0x000c1084, 0x0000, 0x0000,
45 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
46 DEFINE_GUID( CLSID_MsiPatch
, 0x000c1086, 0x0000, 0x0000,
47 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
52 * An .msi file is a structured storage file.
53 * It contains a number of streams.
54 * A stream for each table in the database.
55 * Two streams for the string table in the database.
56 * Any binary data in a table is a reference to a stream.
59 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
61 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
64 free_cached_tables( db
);
65 msi_free_transforms( db
);
66 msi_destroy_stringtable( db
->strings
);
67 IStorage_Release( db
->storage
);
70 DeleteFileW( db
->deletefile
);
71 msi_free( db
->deletefile
);
75 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
79 MSIDATABASE
*db
= NULL
;
80 UINT ret
= ERROR_FUNCTION_FAILED
;
81 LPCWSTR szMode
, save_path
;
86 static const WCHAR backslash
[] = {'\\',0};
87 static const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
89 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
92 return ERROR_INVALID_PARAMETER
;
94 if (szPersist
- MSIDBOPEN_PATCHFILE
>= MSIDBOPEN_READONLY
&&
95 szPersist
- MSIDBOPEN_PATCHFILE
<= MSIDBOPEN_CREATEDIRECT
)
97 TRACE("Database is a patch\n");
98 szPersist
-= MSIDBOPEN_PATCHFILE
;
101 save_path
= szDBPath
;
103 if( HIWORD( szPersist
) )
105 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
106 return ERROR_OPEN_FAILED
;
108 szDBPath
= szPersist
;
109 szPersist
= MSIDBOPEN_TRANSACT
;
113 if( szPersist
== MSIDBOPEN_READONLY
)
115 r
= StgOpenStorage( szDBPath
, NULL
,
116 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
118 else if( szPersist
== MSIDBOPEN_CREATE
|| szPersist
== MSIDBOPEN_CREATEDIRECT
)
120 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
122 r
= StgCreateDocfile( szDBPath
,
123 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
124 if( r
== ERROR_SUCCESS
)
126 IStorage_SetClass( stg
, &CLSID_MsiDatabase
);
127 /* create the _Tables stream */
128 r
= write_stream_data(stg
, szTables
, NULL
, 0, TRUE
);
130 r
= msi_init_string_table( stg
);
134 else if( szPersist
== MSIDBOPEN_TRANSACT
)
136 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
138 r
= StgOpenStorage( szDBPath
, NULL
,
139 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
141 else if( szPersist
== MSIDBOPEN_DIRECT
)
143 r
= StgOpenStorage( szDBPath
, NULL
,
144 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
148 ERR("unknown flag %p\n",szPersist
);
149 return ERROR_INVALID_PARAMETER
;
152 if( FAILED( r
) || !stg
)
154 FIXME("open failed r = %08x for %s\n", r
, debugstr_w(szDBPath
));
155 return ERROR_FUNCTION_FAILED
;
158 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
161 FIXME("Failed to stat storage\n");
165 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
166 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
168 ERR("storage GUID is not a MSI database GUID %s\n",
169 debugstr_guid(&stat
.clsid
) );
173 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
177 FIXME("Failed to allocate a handle\n");
181 if (!strchrW( save_path
, '\\' ))
183 GetCurrentDirectoryW( MAX_PATH
, path
);
184 lstrcatW( path
, backslash
);
185 lstrcatW( path
, save_path
);
188 lstrcpyW( path
, save_path
);
190 db
->path
= strdupW( path
);
192 if( TRACE_ON( msi
) )
193 enum_stream_names( stg
);
198 db
->deletefile
= strdupW( szDBPath
);
200 db
->deletefile
= NULL
;
201 list_init( &db
->tables
);
202 list_init( &db
->transforms
);
204 db
->strings
= msi_load_string_table( stg
, &db
->bytes_per_strref
);
210 msiobj_addref( &db
->hdr
);
211 IStorage_AddRef( stg
);
216 msiobj_release( &db
->hdr
);
218 IStorage_Release( stg
);
223 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
228 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
230 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
231 if( ret
== ERROR_SUCCESS
)
233 *phDB
= alloc_msihandle( &db
->hdr
);
235 ret
= ERROR_NOT_ENOUGH_MEMORY
;
236 msiobj_release( &db
->hdr
);
242 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
244 HRESULT r
= ERROR_FUNCTION_FAILED
;
245 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
247 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
251 szwDBPath
= strdupAtoW( szDBPath
);
256 if( HIWORD(szPersist
) )
258 szwPersist
= strdupAtoW( szPersist
);
263 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
265 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
268 if( HIWORD(szPersist
) )
269 msi_free( szwPersist
);
270 msi_free( szwDBPath
);
275 static LPWSTR
msi_read_text_archive(LPCWSTR path
)
280 DWORD read
, size
= 0;
282 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
283 if (file
== INVALID_HANDLE_VALUE
)
286 size
= GetFileSize( file
, NULL
);
287 data
= msi_alloc( size
+ 1 );
291 if (!ReadFile( file
, data
, size
, &read
, NULL
))
295 wdata
= strdupAtoW( data
);
303 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
)
305 LPWSTR ptr
= *line
, save
;
310 /* stay on this line */
311 while (*ptr
&& *ptr
!= '\n')
313 /* entries are separated by tabs */
320 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
324 /* store pointers into the data */
325 for (i
= 0, ptr
= *line
; i
< count
; i
++)
327 while (*ptr
&& *ptr
== '\r') ptr
++;
330 while (*ptr
&& *ptr
!= '\t' && *ptr
!= '\n' && *ptr
!= '\r') ptr
++;
332 /* NULL-separate the data */
333 if (*ptr
== '\n' || *ptr
== '\r')
335 while (*ptr
== '\n' || *ptr
== '\r')
341 (*entries
)[i
] = save
;
344 /* move to the next line if there's more, else EOF */
348 *num_entries
= count
;
351 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
356 static const WCHAR create_fmt
[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
358 size
= sizeof(create_fmt
)/sizeof(create_fmt
[0]) + lstrlenW(table
) - 2;
359 prelude
= msi_alloc(size
* sizeof(WCHAR
));
363 sprintfW(prelude
, create_fmt
, table
);
367 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
371 DWORD sql_size
= 1, i
, len
;
372 WCHAR expanded
[128], *ptr
;
373 WCHAR size
[10], comma
[2], extra
[30];
375 static const WCHAR column_fmt
[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
376 static const WCHAR size_fmt
[] = {'(','%','s',')',0};
377 static const WCHAR type_char
[] = {'C','H','A','R',0};
378 static const WCHAR type_int
[] = {'I','N','T',0};
379 static const WCHAR type_long
[] = {'L','O','N','G',0};
380 static const WCHAR type_notnull
[] = {' ','N','O','T',' ','N','U','L','L',0};
381 static const WCHAR localizable
[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
383 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
387 for (i
= 0; i
< num_columns
; i
++)
390 comma
[1] = size
[0] = extra
[0] = '\0';
392 if (i
== num_columns
- 1)
404 lstrcpyW(extra
, type_notnull
);
406 lstrcatW(extra
, localizable
);
408 sprintfW(size
, size_fmt
, ptr
);
411 lstrcpyW(extra
, type_notnull
);
414 sprintfW(size
, size_fmt
, ptr
);
417 lstrcpyW(extra
, type_notnull
);
425 ERR("Unknown type: %c\n", types
[i
][0]);
430 sprintfW(expanded
, column_fmt
, columns_data
[i
], type
, size
, extra
, comma
);
431 sql_size
+= lstrlenW(expanded
);
433 p
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
441 lstrcatW(columns
, expanded
);
447 static LPWSTR
msi_build_createsql_postlude(LPWSTR
*primary_keys
, DWORD num_keys
)
449 LPWSTR postlude
, keys
, ptr
;
450 DWORD size
, key_size
, i
;
452 static const WCHAR key_fmt
[] = {'`','%','s','`',',',' ',0};
453 static const WCHAR postlude_fmt
[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
455 for (i
= 0, size
= 1; i
< num_keys
; i
++)
456 size
+= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) - 2;
458 keys
= msi_alloc(size
* sizeof(WCHAR
));
462 for (i
= 0, ptr
= keys
; i
< num_keys
; i
++)
464 key_size
= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) -2;
465 sprintfW(ptr
, key_fmt
, primary_keys
[i
]);
469 /* remove final ', ' */
472 size
= lstrlenW(postlude_fmt
) + size
- 1;
473 postlude
= msi_alloc(size
* sizeof(WCHAR
));
477 sprintfW(postlude
, postlude_fmt
, keys
);
484 static UINT
msi_add_table_to_db(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
, LPWSTR
*labels
, DWORD num_labels
, DWORD num_columns
)
490 LPWSTR prelude
, columns_sql
, postlude
;
492 prelude
= msi_build_createsql_prelude(labels
[0]);
493 columns_sql
= msi_build_createsql_columns(columns
, types
, num_columns
);
494 postlude
= msi_build_createsql_postlude(labels
+ 1, num_labels
- 1); /* skip over table name */
496 if (!prelude
|| !columns_sql
|| !postlude
)
497 return ERROR_OUTOFMEMORY
;
499 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
500 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
502 return ERROR_OUTOFMEMORY
;
504 lstrcpyW(create_sql
, prelude
);
505 lstrcatW(create_sql
, columns_sql
);
506 lstrcatW(create_sql
, postlude
);
509 msi_free(columns_sql
);
512 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
513 msi_free(create_sql
);
515 if (r
!= ERROR_SUCCESS
)
518 r
= MSI_ViewExecute(view
, NULL
);
520 msiobj_release(&view
->hdr
);
525 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
526 LPWSTR
*data
, MSIRECORD
**rec
)
530 *rec
= MSI_CreateRecord(num_columns
);
532 return ERROR_OUTOFMEMORY
;
534 for (i
= 0; i
< num_columns
; i
++)
538 case 'L': case 'l': case 'S': case 's':
539 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
543 MSI_RecordSetInteger(*rec
, i
+ 1, atoiW(data
[i
]));
546 ERR("Unhandled column type: %c\n", types
[i
][0]);
547 msiobj_release(&(*rec
)->hdr
);
548 return ERROR_FUNCTION_FAILED
;
552 return ERROR_SUCCESS
;
555 static UINT
msi_add_records_to_table(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
,
556 LPWSTR
*labels
, LPWSTR
**records
,
557 int num_columns
, int num_records
)
564 static const WCHAR select
[] = {
565 'S','E','L','E','C','T',' ','*',' ',
566 'F','R','O','M',' ','`','%','s','`',0
569 r
= MSI_OpenQuery(db
, &view
, select
, labels
[0]);
570 if (r
!= ERROR_SUCCESS
)
573 while (MSI_ViewFetch(view
, &rec
) != ERROR_NO_MORE_ITEMS
)
575 r
= MSI_ViewModify(view
, MSIMODIFY_DELETE
, rec
);
576 if (r
!= ERROR_SUCCESS
)
580 for (i
= 0; i
< num_records
; i
++)
582 r
= construct_record(num_columns
, types
, records
[i
], &rec
);
583 if (r
!= ERROR_SUCCESS
)
586 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
587 if (r
!= ERROR_SUCCESS
)
589 msiobj_release(&rec
->hdr
);
593 msiobj_release(&rec
->hdr
);
597 msiobj_release(&view
->hdr
);
601 static UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
605 DWORD num_labels
, num_types
;
606 DWORD num_columns
, num_records
= 0;
607 LPWSTR
*columns
, *types
, *labels
;
608 LPWSTR path
, ptr
, data
;
609 LPWSTR
**records
= NULL
;
610 LPWSTR
**temp_records
;
612 static const WCHAR backslash
[] = {'\\',0};
613 static const WCHAR suminfo
[] =
614 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
616 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
618 if( folder
== NULL
|| file
== NULL
)
619 return ERROR_INVALID_PARAMETER
;
621 len
= lstrlenW(folder
) + lstrlenW(backslash
) + lstrlenW(file
) + 1;
622 path
= msi_alloc( len
* sizeof(WCHAR
) );
624 return ERROR_OUTOFMEMORY
;
626 lstrcpyW( path
, folder
);
627 lstrcatW( path
, backslash
);
628 lstrcatW( path
, file
);
630 data
= msi_read_text_archive( path
);
633 msi_parse_line( &ptr
, &columns
, &num_columns
);
634 msi_parse_line( &ptr
, &types
, &num_types
);
635 msi_parse_line( &ptr
, &labels
, &num_labels
);
637 if (num_columns
!= num_types
)
639 r
= ERROR_FUNCTION_FAILED
;
643 records
= msi_alloc(sizeof(LPWSTR
*));
646 r
= ERROR_OUTOFMEMORY
;
650 /* read in the table records */
653 msi_parse_line( &ptr
, &records
[num_records
], NULL
);
656 temp_records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
659 r
= ERROR_OUTOFMEMORY
;
662 records
= temp_records
;
665 if (!strcmpW(labels
[0], suminfo
))
667 r
= msi_add_suminfo( db
, records
, num_records
, num_columns
);
668 if (r
!= ERROR_SUCCESS
)
670 r
= ERROR_FUNCTION_FAILED
;
676 if (!TABLE_Exists(db
, labels
[0]))
678 r
= msi_add_table_to_db( db
, columns
, types
, labels
, num_labels
, num_columns
);
679 if (r
!= ERROR_SUCCESS
)
681 r
= ERROR_FUNCTION_FAILED
;
686 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
);
696 for (i
= 0; i
< num_records
; i
++)
697 msi_free(records
[i
]);
704 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
709 TRACE("%x %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
711 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
714 IWineMsiRemoteDatabase
*remote_database
;
716 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
717 if ( !remote_database
)
718 return ERROR_INVALID_HANDLE
;
720 IWineMsiRemoteDatabase_Release( remote_database
);
721 WARN("MsiDatabaseImport not allowed during a custom action!\n");
723 return ERROR_SUCCESS
;
726 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
727 msiobj_release( &db
->hdr
);
731 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
732 LPCSTR szFolder
, LPCSTR szFilename
)
734 LPWSTR path
= NULL
, file
= NULL
;
735 UINT r
= ERROR_OUTOFMEMORY
;
737 TRACE("%x %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
741 path
= strdupAtoW( szFolder
);
748 file
= strdupAtoW( szFilename
);
753 r
= MsiDatabaseImportW( handle
, path
, file
);
762 static UINT
msi_export_record( HANDLE handle
, MSIRECORD
*row
, UINT start
)
764 UINT i
, count
, len
, r
= ERROR_SUCCESS
;
770 buffer
= msi_alloc( len
);
772 return ERROR_OUTOFMEMORY
;
774 count
= MSI_RecordGetFieldCount( row
);
775 for ( i
=start
; i
<=count
; i
++ )
778 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
779 if (r
== ERROR_MORE_DATA
)
781 char *p
= msi_realloc( buffer
, sz
+ 1 );
788 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
789 if (r
!= ERROR_SUCCESS
)
792 if (!WriteFile( handle
, buffer
, sz
, &sz
, NULL
))
794 r
= ERROR_FUNCTION_FAILED
;
798 sep
= (i
< count
) ? "\t" : "\r\n";
799 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
801 r
= ERROR_FUNCTION_FAILED
;
809 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
811 return msi_export_record( arg
, row
, 1 );
814 static UINT
msi_export_forcecodepage( HANDLE handle
)
818 static const char data
[] = "\r\n\r\n0\t_ForceCodepage\r\n";
820 FIXME("Read the codepage from the strings table!\n");
822 sz
= lstrlenA(data
) + 1;
823 if (!WriteFile(handle
, data
, sz
, &sz
, NULL
))
824 return ERROR_FUNCTION_FAILED
;
826 return ERROR_SUCCESS
;
829 static UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
,
830 LPCWSTR folder
, LPCWSTR file
)
832 static const WCHAR query
[] = {
833 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
834 static const WCHAR szbs
[] = { '\\', 0 };
835 static const WCHAR forcecodepage
[] = {
836 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
837 MSIRECORD
*rec
= NULL
;
838 MSIQUERY
*view
= NULL
;
843 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
844 debugstr_w(folder
), debugstr_w(file
) );
846 if( folder
== NULL
|| file
== NULL
)
847 return ERROR_INVALID_PARAMETER
;
849 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
850 filename
= msi_alloc(len
* sizeof (WCHAR
));
852 return ERROR_OUTOFMEMORY
;
854 lstrcpyW( filename
, folder
);
855 lstrcatW( filename
, szbs
);
856 lstrcatW( filename
, file
);
858 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
859 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
860 msi_free( filename
);
861 if (handle
== INVALID_HANDLE_VALUE
)
862 return ERROR_FUNCTION_FAILED
;
864 if (!lstrcmpW( table
, forcecodepage
))
866 r
= msi_export_forcecodepage( handle
);
870 r
= MSI_OpenQuery( db
, &view
, query
, table
);
871 if (r
== ERROR_SUCCESS
)
873 /* write out row 1, the column names */
874 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
875 if (r
== ERROR_SUCCESS
)
877 msi_export_record( handle
, rec
, 1 );
878 msiobj_release( &rec
->hdr
);
881 /* write out row 2, the column types */
882 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
883 if (r
== ERROR_SUCCESS
)
885 msi_export_record( handle
, rec
, 1 );
886 msiobj_release( &rec
->hdr
);
889 /* write out row 3, the table name + keys */
890 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
891 if (r
== ERROR_SUCCESS
)
893 MSI_RecordSetStringW( rec
, 0, table
);
894 msi_export_record( handle
, rec
, 0 );
895 msiobj_release( &rec
->hdr
);
898 /* write out row 4 onwards, the data */
899 r
= MSI_IterateRecords( view
, 0, msi_export_row
, handle
);
900 msiobj_release( &view
->hdr
);
904 CloseHandle( handle
);
908 /***********************************************************************
909 * MsiExportDatabaseW [MSI.@]
911 * Writes a file containing the table data as tab separated ASCII.
913 * The format is as follows:
915 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
916 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
917 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
919 * Followed by the data, starting at row 1 with one row per line
921 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
923 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
924 LPCWSTR szFolder
, LPCWSTR szFilename
)
929 TRACE("%x %s %s %s\n", handle
, debugstr_w(szTable
),
930 debugstr_w(szFolder
), debugstr_w(szFilename
));
932 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
935 IWineMsiRemoteDatabase
*remote_database
;
937 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
938 if ( !remote_database
)
939 return ERROR_INVALID_HANDLE
;
941 IWineMsiRemoteDatabase_Release( remote_database
);
942 WARN("MsiDatabaseExport not allowed during a custom action!\n");
944 return ERROR_SUCCESS
;
947 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
948 msiobj_release( &db
->hdr
);
952 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
953 LPCSTR szFolder
, LPCSTR szFilename
)
955 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
956 UINT r
= ERROR_OUTOFMEMORY
;
958 TRACE("%x %s %s %s\n", handle
, debugstr_a(szTable
),
959 debugstr_a(szFolder
), debugstr_a(szFilename
));
963 table
= strdupAtoW( szTable
);
970 path
= strdupAtoW( szFolder
);
977 file
= strdupAtoW( szFilename
);
982 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
992 UINT WINAPI
MsiDatabaseMergeA(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
998 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
999 debugstr_a(szTableName
));
1001 table
= strdupAtoW(szTableName
);
1002 r
= MsiDatabaseMergeW(hDatabase
, hDatabaseMerge
, table
);
1008 typedef struct _tagMERGETABLE
1016 typedef struct _tagMERGEROW
1022 typedef struct _tagMERGEDATA
1026 MERGETABLE
*curtable
;
1028 struct list
*tabledata
;
1031 static UINT
merge_verify_colnames(MSIQUERY
*dbview
, MSIQUERY
*mergeview
)
1033 MSIRECORD
*dbrec
, *mergerec
;
1036 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_NAMES
, &dbrec
);
1037 if (r
!= ERROR_SUCCESS
)
1040 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_NAMES
, &mergerec
);
1041 if (r
!= ERROR_SUCCESS
)
1044 count
= MSI_RecordGetFieldCount(dbrec
);
1045 for (i
= 1; i
<= count
; i
++)
1047 if (!MSI_RecordGetString(mergerec
, i
))
1050 if (lstrcmpW(MSI_RecordGetString(dbrec
, i
),
1051 MSI_RecordGetString(mergerec
, i
)))
1053 r
= ERROR_DATATYPE_MISMATCH
;
1058 msiobj_release(&dbrec
->hdr
);
1059 msiobj_release(&mergerec
->hdr
);
1060 dbrec
= mergerec
= NULL
;
1062 r
= MSI_ViewGetColumnInfo(dbview
, MSICOLINFO_TYPES
, &dbrec
);
1063 if (r
!= ERROR_SUCCESS
)
1066 r
= MSI_ViewGetColumnInfo(mergeview
, MSICOLINFO_TYPES
, &mergerec
);
1067 if (r
!= ERROR_SUCCESS
)
1070 count
= MSI_RecordGetFieldCount(dbrec
);
1071 for (i
= 1; i
<= count
; i
++)
1073 if (!MSI_RecordGetString(mergerec
, i
))
1076 if (lstrcmpW(MSI_RecordGetString(dbrec
, i
),
1077 MSI_RecordGetString(mergerec
, i
)))
1079 r
= ERROR_DATATYPE_MISMATCH
;
1085 msiobj_release(&dbrec
->hdr
);
1086 msiobj_release(&mergerec
->hdr
);
1091 static UINT
merge_verify_primary_keys(MSIDATABASE
*db
, MSIDATABASE
*mergedb
,
1094 MSIRECORD
*dbrec
, *mergerec
= NULL
;
1097 r
= MSI_DatabaseGetPrimaryKeys(db
, table
, &dbrec
);
1098 if (r
!= ERROR_SUCCESS
)
1101 r
= MSI_DatabaseGetPrimaryKeys(mergedb
, table
, &mergerec
);
1102 if (r
!= ERROR_SUCCESS
)
1105 count
= MSI_RecordGetFieldCount(dbrec
);
1106 if (count
!= MSI_RecordGetFieldCount(mergerec
))
1108 r
= ERROR_DATATYPE_MISMATCH
;
1112 for (i
= 1; i
<= count
; i
++)
1114 if (lstrcmpW(MSI_RecordGetString(dbrec
, i
),
1115 MSI_RecordGetString(mergerec
, i
)))
1117 r
= ERROR_DATATYPE_MISMATCH
;
1123 msiobj_release(&dbrec
->hdr
);
1124 msiobj_release(&mergerec
->hdr
);
1129 static LPWSTR
get_key_value(MSIQUERY
*view
, LPCWSTR key
, MSIRECORD
*rec
)
1131 MSIRECORD
*colnames
;
1136 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &colnames
);
1137 if (r
!= ERROR_SUCCESS
)
1142 str
= msi_dup_record_field(colnames
, ++i
);
1143 cmp
= lstrcmpW(key
, str
);
1147 msiobj_release(&colnames
->hdr
);
1148 return msi_dup_record_field(rec
, i
);
1151 static LPWSTR
create_diff_row_query(MSIDATABASE
*merge
, MSIQUERY
*view
,
1152 LPWSTR table
, MSIRECORD
*rec
)
1154 LPWSTR query
= NULL
, clause
= NULL
;
1155 LPWSTR ptr
= NULL
, val
;
1157 DWORD size
= 1, oldsize
;
1162 static const WCHAR keyset
[] = {
1163 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1164 static const WCHAR lastkeyset
[] = {
1165 '`','%','s','`',' ','=',' ','%','s',' ',0};
1166 static const WCHAR fmt
[] = {'S','E','L','E','C','T',' ','*',' ',
1167 'F','R','O','M',' ','`','%','s','`',' ',
1168 'W','H','E','R','E',' ','%','s',0};
1170 r
= MSI_DatabaseGetPrimaryKeys(merge
, table
, &keys
);
1171 if (r
!= ERROR_SUCCESS
)
1174 clause
= msi_alloc_zero(size
* sizeof(WCHAR
));
1179 count
= MSI_RecordGetFieldCount(keys
);
1180 for (i
= 1; i
<= count
; i
++)
1182 key
= MSI_RecordGetString(keys
, i
);
1183 val
= get_key_value(view
, key
, rec
);
1186 setptr
= lastkeyset
;
1191 size
+= lstrlenW(setptr
) + lstrlenW(key
) + lstrlenW(val
) - 4;
1192 clause
= msi_realloc(clause
, size
* sizeof (WCHAR
));
1199 ptr
= clause
+ oldsize
- 1;
1200 sprintfW(ptr
, setptr
, key
, val
);
1204 size
= lstrlenW(fmt
) + lstrlenW(table
) + lstrlenW(clause
) + 1;
1205 query
= msi_alloc(size
* sizeof(WCHAR
));
1209 sprintfW(query
, fmt
, table
, clause
);
1213 msiobj_release(&keys
->hdr
);
1217 static UINT
merge_diff_row(MSIRECORD
*rec
, LPVOID param
)
1219 MERGEDATA
*data
= param
;
1220 MERGETABLE
*table
= data
->curtable
;
1223 MSIRECORD
*row
= NULL
;
1227 query
= create_diff_row_query(data
->merge
, data
->curview
, table
->name
, rec
);
1229 return ERROR_OUTOFMEMORY
;
1231 r
= MSI_DatabaseOpenViewW(data
->db
, query
, &dbview
);
1232 if (r
!= ERROR_SUCCESS
)
1235 r
= MSI_ViewExecute(dbview
, NULL
);
1236 if (r
!= ERROR_SUCCESS
)
1239 r
= MSI_ViewFetch(dbview
, &row
);
1240 if (r
== ERROR_SUCCESS
&& !MSI_RecordsAreEqual(rec
, row
))
1242 table
->numconflicts
++;
1245 else if (r
!= ERROR_NO_MORE_ITEMS
)
1248 mergerow
= msi_alloc(sizeof(MERGEROW
));
1251 r
= ERROR_OUTOFMEMORY
;
1255 mergerow
->data
= MSI_CloneRecord(rec
);
1256 if (!mergerow
->data
)
1258 r
= ERROR_OUTOFMEMORY
;
1263 list_add_tail(&table
->rows
, &mergerow
->entry
);
1267 msiobj_release(&row
->hdr
);
1268 msiobj_release(&dbview
->hdr
);
1272 static UINT
merge_diff_tables(MSIRECORD
*rec
, LPVOID param
)
1274 MERGEDATA
*data
= param
;
1277 MSIQUERY
*mergeview
= NULL
;
1281 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1282 'F','R','O','M',' ','`','%','s','`',0};
1284 name
= MSI_RecordGetString(rec
, 1);
1286 r
= MSI_OpenQuery(data
->db
, &dbview
, query
, name
);
1287 if (r
!= ERROR_SUCCESS
)
1290 r
= MSI_OpenQuery(data
->merge
, &mergeview
, query
, name
);
1291 if (r
!= ERROR_SUCCESS
)
1294 r
= merge_verify_colnames(dbview
, mergeview
);
1295 if (r
!= ERROR_SUCCESS
)
1298 r
= merge_verify_primary_keys(data
->db
, data
->merge
, name
);
1299 if (r
!= ERROR_SUCCESS
)
1302 table
= msi_alloc(sizeof(MERGETABLE
));
1305 r
= ERROR_OUTOFMEMORY
;
1309 list_init(&table
->rows
);
1310 table
->name
= strdupW(name
);
1311 table
->numconflicts
= 0;
1312 data
->curtable
= table
;
1313 data
->curview
= mergeview
;
1315 r
= MSI_IterateRecords(mergeview
, NULL
, merge_diff_row
, data
);
1316 if (r
!= ERROR_SUCCESS
)
1318 msi_free(table
->name
);
1323 list_add_tail(data
->tabledata
, &table
->entry
);
1326 msiobj_release(&dbview
->hdr
);
1327 msiobj_release(&mergeview
->hdr
);
1331 static UINT
gather_merge_data(MSIDATABASE
*db
, MSIDATABASE
*merge
,
1332 struct list
*tabledata
)
1338 static const WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ',
1339 'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1341 r
= MSI_DatabaseOpenViewW(merge
, query
, &view
);
1342 if (r
!= ERROR_SUCCESS
)
1347 data
.tabledata
= tabledata
;
1348 r
= MSI_IterateRecords(view
, NULL
, merge_diff_tables
, &data
);
1350 msiobj_release(&view
->hdr
);
1354 static UINT
merge_table(MSIDATABASE
*db
, MERGETABLE
*table
)
1360 LIST_FOR_EACH_ENTRY(row
, &table
->rows
, MERGEROW
, entry
)
1362 r
= TABLE_CreateView(db
, table
->name
, &tv
);
1363 if (r
!= ERROR_SUCCESS
)
1366 r
= tv
->ops
->insert_row(tv
, row
->data
, -1, FALSE
);
1367 tv
->ops
->delete(tv
);
1369 if (r
!= ERROR_SUCCESS
)
1373 return ERROR_SUCCESS
;
1376 static UINT
update_merge_errors(MSIDATABASE
*db
, LPCWSTR error
,
1377 LPWSTR table
, DWORD numconflicts
)
1382 static const WCHAR create
[] = {
1383 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1384 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1385 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1386 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1387 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1388 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1389 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1390 static const WCHAR insert
[] = {
1391 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1392 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1393 '`','N','u','m','R','o','w','M','e','r','g','e',
1394 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1395 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1397 if (!TABLE_Exists(db
, error
))
1399 r
= MSI_OpenQuery(db
, &view
, create
, error
);
1400 if (r
!= ERROR_SUCCESS
)
1403 r
= MSI_ViewExecute(view
, NULL
);
1404 msiobj_release(&view
->hdr
);
1405 if (r
!= ERROR_SUCCESS
)
1409 r
= MSI_OpenQuery(db
, &view
, insert
, error
, table
, numconflicts
);
1410 if (r
!= ERROR_SUCCESS
)
1413 r
= MSI_ViewExecute(view
, NULL
);
1414 msiobj_release(&view
->hdr
);
1418 static void merge_free_rows(MERGETABLE
*table
)
1420 struct list
*item
, *cursor
;
1422 LIST_FOR_EACH_SAFE(item
, cursor
, &table
->rows
)
1424 MERGEROW
*row
= LIST_ENTRY(item
, MERGEROW
, entry
);
1426 list_remove(&row
->entry
);
1427 merge_free_rows(table
);
1428 msiobj_release(&row
->data
->hdr
);
1433 UINT WINAPI
MsiDatabaseMergeW(MSIHANDLE hDatabase
, MSIHANDLE hDatabaseMerge
,
1434 LPCWSTR szTableName
)
1436 struct list tabledata
= LIST_INIT(tabledata
);
1437 struct list
*item
, *cursor
;
1438 MSIDATABASE
*db
, *merge
;
1443 TRACE("(%d, %d, %s)\n", hDatabase
, hDatabaseMerge
,
1444 debugstr_w(szTableName
));
1446 if (szTableName
&& !*szTableName
)
1447 return ERROR_INVALID_TABLE
;
1449 db
= msihandle2msiinfo(hDatabase
, MSIHANDLETYPE_DATABASE
);
1450 merge
= msihandle2msiinfo(hDatabaseMerge
, MSIHANDLETYPE_DATABASE
);
1453 r
= ERROR_INVALID_HANDLE
;
1457 r
= gather_merge_data(db
, merge
, &tabledata
);
1458 if (r
!= ERROR_SUCCESS
)
1462 LIST_FOR_EACH_ENTRY(table
, &tabledata
, MERGETABLE
, entry
)
1464 if (table
->numconflicts
)
1468 r
= update_merge_errors(db
, szTableName
, table
->name
,
1469 table
->numconflicts
);
1470 if (r
!= ERROR_SUCCESS
)
1475 r
= merge_table(db
, table
);
1476 if (r
!= ERROR_SUCCESS
)
1481 LIST_FOR_EACH_SAFE(item
, cursor
, &tabledata
)
1483 MERGETABLE
*table
= LIST_ENTRY(item
, MERGETABLE
, entry
);
1485 list_remove(&table
->entry
);
1486 merge_free_rows(table
);
1487 msi_free(table
->name
);
1492 r
= ERROR_FUNCTION_FAILED
;
1495 msiobj_release(&db
->hdr
);
1496 msiobj_release(&merge
->hdr
);
1500 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
1502 MSIDBSTATE ret
= MSIDBSTATE_READ
;
1505 TRACE("%d\n", handle
);
1507 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
1510 IWineMsiRemoteDatabase
*remote_database
;
1512 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
1513 if ( !remote_database
)
1514 return MSIDBSTATE_ERROR
;
1516 IWineMsiRemoteDatabase_Release( remote_database
);
1517 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1519 return MSIDBSTATE_READ
;
1522 if (db
->mode
!= MSIDBOPEN_READONLY
)
1523 ret
= MSIDBSTATE_WRITE
;
1524 msiobj_release( &db
->hdr
);
1529 typedef struct _msi_remote_database_impl
{
1530 const IWineMsiRemoteDatabaseVtbl
*lpVtbl
;
1533 } msi_remote_database_impl
;
1535 static inline msi_remote_database_impl
* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase
* iface
)
1537 return (msi_remote_database_impl
*)iface
;
1540 static HRESULT WINAPI
mrd_QueryInterface( IWineMsiRemoteDatabase
*iface
,
1541 REFIID riid
,LPVOID
*ppobj
)
1543 if( IsEqualCLSID( riid
, &IID_IUnknown
) ||
1544 IsEqualCLSID( riid
, &IID_IWineMsiRemoteDatabase
) )
1546 IUnknown_AddRef( iface
);
1551 return E_NOINTERFACE
;
1554 static ULONG WINAPI
mrd_AddRef( IWineMsiRemoteDatabase
*iface
)
1556 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1558 return InterlockedIncrement( &This
->refs
);
1561 static ULONG WINAPI
mrd_Release( IWineMsiRemoteDatabase
*iface
)
1563 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1566 r
= InterlockedDecrement( &This
->refs
);
1569 MsiCloseHandle( This
->database
);
1575 static HRESULT WINAPI
mrd_IsTablePersistent( IWineMsiRemoteDatabase
*iface
,
1576 BSTR table
, MSICONDITION
*persistent
)
1578 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1579 *persistent
= MsiDatabaseIsTablePersistentW(This
->database
, table
);
1583 static HRESULT WINAPI
mrd_GetPrimaryKeys( IWineMsiRemoteDatabase
*iface
,
1584 BSTR table
, MSIHANDLE
*keys
)
1586 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1587 UINT r
= MsiDatabaseGetPrimaryKeysW(This
->database
, table
, keys
);
1588 return HRESULT_FROM_WIN32(r
);
1591 static HRESULT WINAPI
mrd_GetSummaryInformation( IWineMsiRemoteDatabase
*iface
,
1592 UINT updatecount
, MSIHANDLE
*suminfo
)
1594 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1595 UINT r
= MsiGetSummaryInformationW(This
->database
, NULL
, updatecount
, suminfo
);
1596 return HRESULT_FROM_WIN32(r
);
1599 static HRESULT WINAPI
mrd_OpenView( IWineMsiRemoteDatabase
*iface
,
1600 BSTR query
, MSIHANDLE
*view
)
1602 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1603 UINT r
= MsiDatabaseOpenViewW(This
->database
, query
, view
);
1604 return HRESULT_FROM_WIN32(r
);
1607 static HRESULT WINAPI
mrd_SetMsiHandle( IWineMsiRemoteDatabase
*iface
, MSIHANDLE handle
)
1609 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1610 This
->database
= handle
;
1614 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl
=
1619 mrd_IsTablePersistent
,
1621 mrd_GetSummaryInformation
,
1626 HRESULT
create_msi_remote_database( IUnknown
*pOuter
, LPVOID
*ppObj
)
1628 msi_remote_database_impl
*This
;
1630 This
= msi_alloc( sizeof *This
);
1632 return E_OUTOFMEMORY
;
1634 This
->lpVtbl
= &msi_remote_database_vtbl
;