2006-02-17 00:04:10 +00:00
|
|
|
/*
|
|
|
|
* Implementation of the Microsoft Installer (msi.dll)
|
|
|
|
*
|
|
|
|
* Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-08-01 23:12:11 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2006-02-17 00:04:10 +00:00
|
|
|
*/
|
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#define COBJMACROS
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winreg.h"
|
|
|
|
#include "winnls.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "msi.h"
|
|
|
|
#include "msiquery.h"
|
2013-12-25 20:42:59 +00:00
|
|
|
#include "msipriv.h"
|
2018-03-04 23:30:58 +00:00
|
|
|
#include "objidl.h"
|
|
|
|
#include "objbase.h"
|
|
|
|
#include "msiserver.h"
|
|
|
|
#include "query.h"
|
2013-01-24 23:00:42 +00:00
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
#include "initguid.h"
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* .MSI file format
|
|
|
|
*
|
|
|
|
* An .msi file is a structured storage file.
|
|
|
|
* It contains a number of streams.
|
|
|
|
* A stream for each table in the database.
|
|
|
|
* Two streams for the string table in the database.
|
|
|
|
* Any binary data in a table is a reference to a stream.
|
|
|
|
*/
|
|
|
|
|
2010-02-06 21:28:28 +00:00
|
|
|
#define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
|
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
static void free_transforms( MSIDATABASE *db )
|
|
|
|
{
|
|
|
|
while( !list_empty( &db->transforms ) )
|
|
|
|
{
|
2015-03-09 20:28:19 +00:00
|
|
|
MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ), MSITRANSFORM, entry );
|
2009-12-22 09:28:03 +00:00
|
|
|
list_remove( &t->entry );
|
|
|
|
IStorage_Release( t->stg );
|
|
|
|
msi_free( t );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_streams( MSIDATABASE *db )
|
|
|
|
{
|
2015-03-09 20:28:19 +00:00
|
|
|
UINT i;
|
|
|
|
for (i = 0; i < db->num_streams; i++)
|
2009-12-22 09:28:03 +00:00
|
|
|
{
|
2015-03-09 20:28:19 +00:00
|
|
|
if (db->streams[i].stream) IStream_Release( db->streams[i].stream );
|
2009-12-22 09:28:03 +00:00
|
|
|
}
|
2015-03-09 20:28:19 +00:00
|
|
|
msi_free( db->streams );
|
2009-12-22 09:28:03 +00:00
|
|
|
}
|
|
|
|
|
2010-03-01 12:01:30 +00:00
|
|
|
void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
|
|
|
|
{
|
|
|
|
MSITRANSFORM *t;
|
|
|
|
|
|
|
|
t = msi_alloc( sizeof *t );
|
|
|
|
t->stg = stg;
|
|
|
|
IStorage_AddRef( stg );
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
list_add_head( &db->transforms, &t->entry );
|
2010-03-01 12:01:30 +00:00
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
|
|
|
|
{
|
|
|
|
MSIDATABASE *db = (MSIDATABASE *) arg;
|
|
|
|
|
2006-10-22 20:23:59 +00:00
|
|
|
msi_free(db->path);
|
2009-12-22 09:28:03 +00:00
|
|
|
free_streams( db );
|
2015-03-09 20:28:19 +00:00
|
|
|
free_cached_tables( db );
|
2009-12-22 09:28:03 +00:00
|
|
|
free_transforms( db );
|
2010-10-22 13:18:11 +00:00
|
|
|
if (db->strings) msi_destroy_stringtable( db->strings );
|
2006-11-28 11:21:39 +00:00
|
|
|
IStorage_Release( db->storage );
|
2006-09-18 16:45:21 +00:00
|
|
|
if (db->deletefile)
|
|
|
|
{
|
|
|
|
DeleteFileW( db->deletefile );
|
|
|
|
msi_free( db->deletefile );
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
msi_free( db->tempfolder );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2010-10-22 13:18:11 +00:00
|
|
|
static HRESULT db_initialize( IStorage *stg, const GUID *clsid )
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
hr = IStorage_SetClass( stg, clsid );
|
|
|
|
if (FAILED( hr ))
|
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("failed to set class id %#lx\n", hr);
|
2010-10-22 13:18:11 +00:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create the _Tables stream */
|
2022-03-13 20:31:42 +00:00
|
|
|
hr = write_stream_data( stg, L"_Tables", NULL, 0, TRUE );
|
2010-10-22 13:18:11 +00:00
|
|
|
if (FAILED( hr ))
|
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("failed to create _Tables stream %#lx\n", hr);
|
2010-10-22 13:18:11 +00:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = msi_init_string_table( stg );
|
|
|
|
if (FAILED( hr ))
|
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("failed to initialize string table %#lx\n", hr);
|
2010-10-22 13:18:11 +00:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IStorage_Commit( stg, 0 );
|
|
|
|
if (FAILED( hr ))
|
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("failed to commit changes %#lx\n", hr);
|
2010-10-22 13:18:11 +00:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
|
|
|
|
{
|
|
|
|
IStorage *stg = NULL;
|
|
|
|
HRESULT r;
|
|
|
|
MSIDATABASE *db = NULL;
|
|
|
|
UINT ret = ERROR_FUNCTION_FAILED;
|
2022-03-13 18:05:01 +00:00
|
|
|
LPCWSTR save_path;
|
|
|
|
UINT mode;
|
2006-02-17 00:04:10 +00:00
|
|
|
STATSTG stat;
|
2010-05-29 08:55:43 +00:00
|
|
|
BOOL created = FALSE, patch = FALSE;
|
2006-10-22 20:23:59 +00:00
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
|
|
|
|
|
|
|
|
if( !pdb )
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
2022-03-13 18:05:01 +00:00
|
|
|
save_path = szDBPath;
|
|
|
|
if ( IS_INTMSIDBOPEN(szPersist) )
|
2008-09-21 15:24:30 +00:00
|
|
|
{
|
2022-03-13 18:05:01 +00:00
|
|
|
mode = LOWORD(szPersist);
|
2008-09-21 15:24:30 +00:00
|
|
|
}
|
2022-03-13 18:05:01 +00:00
|
|
|
else
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2006-09-18 16:45:21 +00:00
|
|
|
if (!CopyFileW( szDBPath, szPersist, FALSE ))
|
|
|
|
return ERROR_OPEN_FAILED;
|
|
|
|
|
|
|
|
szDBPath = szPersist;
|
2022-03-13 18:05:01 +00:00
|
|
|
mode = MSI_OPEN_TRANSACT;
|
2006-09-18 16:45:21 +00:00
|
|
|
created = TRUE;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2006-09-18 16:45:21 +00:00
|
|
|
|
2022-03-13 18:05:01 +00:00
|
|
|
if ((mode & MSI_OPEN_PATCHFILE) == MSI_OPEN_PATCHFILE)
|
|
|
|
{
|
|
|
|
TRACE("Database is a patch\n");
|
|
|
|
mode &= ~MSI_OPEN_PATCHFILE;
|
|
|
|
patch = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( mode == MSI_OPEN_READONLY )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
r = StgOpenStorage( szDBPath, NULL,
|
|
|
|
STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
|
|
|
|
}
|
2022-03-13 18:05:01 +00:00
|
|
|
else if( mode == MSI_OPEN_CREATE )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2006-09-18 16:45:21 +00:00
|
|
|
r = StgCreateDocfile( szDBPath,
|
2010-10-22 13:18:11 +00:00
|
|
|
STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
|
|
|
|
|
|
|
|
if( SUCCEEDED(r) )
|
|
|
|
r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
|
|
|
|
created = TRUE;
|
|
|
|
}
|
2022-03-13 18:05:01 +00:00
|
|
|
else if( mode == MSI_OPEN_CREATEDIRECT )
|
2010-10-22 13:18:11 +00:00
|
|
|
{
|
|
|
|
r = StgCreateDocfile( szDBPath,
|
|
|
|
STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
|
|
|
|
|
|
|
|
if( SUCCEEDED(r) )
|
|
|
|
r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
|
2006-09-18 16:45:21 +00:00
|
|
|
created = TRUE;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2022-03-13 18:05:01 +00:00
|
|
|
else if( mode == MSI_OPEN_TRANSACT )
|
2006-08-30 19:24:26 +00:00
|
|
|
{
|
|
|
|
r = StgOpenStorage( szDBPath, NULL,
|
2012-01-21 17:19:12 +00:00
|
|
|
STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
|
2006-08-30 19:24:26 +00:00
|
|
|
}
|
2022-03-13 18:05:01 +00:00
|
|
|
else if( mode == MSI_OPEN_DIRECT )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
r = StgOpenStorage( szDBPath, NULL,
|
|
|
|
STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-03-13 18:05:01 +00:00
|
|
|
ERR("unknown flag %x\n",mode);
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if( FAILED( r ) || !stg )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("open failed r = %#lx for %s\n", r, debugstr_w(szDBPath));
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
|
|
|
|
if( FAILED( r ) )
|
|
|
|
{
|
|
|
|
FIXME("Failed to stat storage\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
|
2009-08-08 11:55:51 +00:00
|
|
|
!IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
|
|
|
|
!IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
ERR("storage GUID is not a MSI database GUID %s\n",
|
|
|
|
debugstr_guid(&stat.clsid) );
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2010-05-29 08:55:43 +00:00
|
|
|
if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
|
|
|
|
{
|
|
|
|
ERR("storage GUID is not the MSI patch GUID %s\n",
|
|
|
|
debugstr_guid(&stat.clsid) );
|
|
|
|
ret = ERROR_OPEN_FAILED;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
|
|
|
|
MSI_CloseDatabase );
|
|
|
|
if( !db )
|
|
|
|
{
|
|
|
|
FIXME("Failed to allocate a handle\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-03-13 00:35:24 +00:00
|
|
|
if (!wcschr( save_path, '\\' ))
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
GetCurrentDirectoryW( MAX_PATH, path );
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcatW( path, L"\\" );
|
2006-10-22 20:23:59 +00:00
|
|
|
lstrcatW( path, save_path );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lstrcpyW( path, save_path );
|
|
|
|
|
|
|
|
db->path = strdupW( path );
|
2012-01-21 17:19:12 +00:00
|
|
|
db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
|
|
|
|
db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
if( TRACE_ON( msi ) )
|
|
|
|
enum_stream_names( stg );
|
|
|
|
|
|
|
|
db->storage = stg;
|
2022-03-13 18:05:01 +00:00
|
|
|
db->mode = mode;
|
2006-09-18 16:45:21 +00:00
|
|
|
if (created)
|
|
|
|
db->deletefile = strdupW( szDBPath );
|
2006-02-17 00:04:10 +00:00
|
|
|
list_init( &db->tables );
|
|
|
|
list_init( &db->transforms );
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
|
2006-02-17 00:04:10 +00:00
|
|
|
if( !db->strings )
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
msiobj_addref( &db->hdr );
|
|
|
|
IStorage_AddRef( stg );
|
|
|
|
*pdb = db;
|
|
|
|
|
|
|
|
end:
|
|
|
|
if( db )
|
|
|
|
msiobj_release( &db->hdr );
|
|
|
|
if( stg )
|
|
|
|
IStorage_Release( stg );
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
|
|
|
|
{
|
|
|
|
MSIDATABASE *db;
|
|
|
|
UINT ret;
|
|
|
|
|
|
|
|
TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
|
|
|
|
|
|
|
|
ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
|
|
|
|
if( ret == ERROR_SUCCESS )
|
|
|
|
{
|
|
|
|
*phDB = alloc_msihandle( &db->hdr );
|
2006-08-30 19:24:26 +00:00
|
|
|
if (! *phDB)
|
|
|
|
ret = ERROR_NOT_ENOUGH_MEMORY;
|
2006-02-17 00:04:10 +00:00
|
|
|
msiobj_release( &db->hdr );
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
|
|
|
|
{
|
|
|
|
HRESULT r = ERROR_FUNCTION_FAILED;
|
|
|
|
LPWSTR szwDBPath = NULL, szwPersist = NULL;
|
|
|
|
|
|
|
|
TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
|
|
|
|
|
|
|
|
if( szDBPath )
|
|
|
|
{
|
|
|
|
szwDBPath = strdupAtoW( szDBPath );
|
|
|
|
if( !szwDBPath )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2010-02-06 21:28:28 +00:00
|
|
|
if( !IS_INTMSIDBOPEN(szPersist) )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
szwPersist = strdupAtoW( szPersist );
|
|
|
|
if( !szwPersist )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
else
|
2006-08-01 23:12:11 +00:00
|
|
|
szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
|
|
|
|
|
|
|
|
end:
|
2010-02-06 21:28:28 +00:00
|
|
|
if( !IS_INTMSIDBOPEN(szPersist) )
|
2006-02-17 00:04:10 +00:00
|
|
|
msi_free( szwPersist );
|
|
|
|
msi_free( szwDBPath );
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
static LPWSTR msi_read_text_archive(LPCWSTR path, DWORD *len)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
HANDLE file;
|
|
|
|
LPSTR data = NULL;
|
|
|
|
LPWSTR wdata = NULL;
|
|
|
|
DWORD read, size = 0;
|
|
|
|
|
|
|
|
file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
|
|
|
|
if (file == INVALID_HANDLE_VALUE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
size = GetFileSize( file, NULL );
|
2012-01-21 17:19:12 +00:00
|
|
|
if (!(data = msi_alloc( size ))) goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
while (!data[size - 1]) size--;
|
|
|
|
*len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 );
|
|
|
|
if ((wdata = msi_alloc( (*len + 1) * sizeof(WCHAR) )))
|
|
|
|
{
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len );
|
|
|
|
wdata[*len] = 0;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
CloseHandle( file );
|
|
|
|
msi_free( data );
|
|
|
|
return wdata;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries, DWORD *len)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
LPWSTR ptr = *line, save;
|
2012-01-21 17:19:12 +00:00
|
|
|
DWORD i, count = 1, chars_left = *len;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
*entries = NULL;
|
|
|
|
|
|
|
|
/* stay on this line */
|
2012-01-21 17:19:12 +00:00
|
|
|
while (chars_left && *ptr != '\n')
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
/* entries are separated by tabs */
|
|
|
|
if (*ptr == '\t')
|
|
|
|
count++;
|
|
|
|
|
|
|
|
ptr++;
|
2012-01-21 17:19:12 +00:00
|
|
|
chars_left--;
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*entries = msi_alloc(count * sizeof(LPWSTR));
|
|
|
|
if (!*entries)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* store pointers into the data */
|
2012-01-21 17:19:12 +00:00
|
|
|
chars_left = *len;
|
2006-10-22 20:23:59 +00:00
|
|
|
for (i = 0, ptr = *line; i < count; i++)
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
while (chars_left && *ptr == '\r')
|
|
|
|
{
|
|
|
|
ptr++;
|
|
|
|
chars_left--;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
save = ptr;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
|
|
|
|
{
|
|
|
|
if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */
|
|
|
|
if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11')
|
|
|
|
{
|
|
|
|
*ptr = '\n';
|
|
|
|
*(ptr - 1) = '\r';
|
|
|
|
}
|
|
|
|
ptr++;
|
|
|
|
chars_left--;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
/* NULL-separate the data */
|
2008-04-04 13:43:40 +00:00
|
|
|
if (*ptr == '\n' || *ptr == '\r')
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
while (chars_left && (*ptr == '\n' || *ptr == '\r'))
|
|
|
|
{
|
|
|
|
*(ptr++) = 0;
|
|
|
|
chars_left--;
|
|
|
|
}
|
2008-04-04 13:43:40 +00:00
|
|
|
}
|
|
|
|
else if (*ptr)
|
2012-01-21 17:19:12 +00:00
|
|
|
{
|
|
|
|
*(ptr++) = 0;
|
|
|
|
chars_left--;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
(*entries)[i] = save;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* move to the next line if there's more, else EOF */
|
|
|
|
*line = ptr;
|
2012-01-21 17:19:12 +00:00
|
|
|
*len = chars_left;
|
2006-10-22 20:23:59 +00:00
|
|
|
if (num_entries)
|
|
|
|
*num_entries = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR msi_build_createsql_prelude(LPWSTR table)
|
|
|
|
{
|
|
|
|
LPWSTR prelude;
|
|
|
|
DWORD size;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
size = ARRAY_SIZE(L"CREATE TABLE `%s` ( ") + lstrlenW(table) - 2;
|
2006-10-22 20:23:59 +00:00
|
|
|
prelude = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!prelude)
|
|
|
|
return NULL;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
swprintf(prelude, size, L"CREATE TABLE `%s` ( ", table);
|
2006-10-22 20:23:59 +00:00
|
|
|
return prelude;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
|
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
LPWSTR columns, p;
|
2006-10-22 20:23:59 +00:00
|
|
|
LPCWSTR type;
|
|
|
|
DWORD sql_size = 1, i, len;
|
|
|
|
WCHAR expanded[128], *ptr;
|
2006-11-28 11:21:39 +00:00
|
|
|
WCHAR size[10], comma[2], extra[30];
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
|
|
|
|
if (!columns)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < num_columns; i++)
|
|
|
|
{
|
|
|
|
type = NULL;
|
|
|
|
comma[1] = size[0] = extra[0] = '\0';
|
|
|
|
|
|
|
|
if (i == num_columns - 1)
|
|
|
|
comma[0] = '\0';
|
|
|
|
else
|
|
|
|
comma[0] = ',';
|
|
|
|
|
|
|
|
ptr = &types[i][1];
|
2022-03-13 00:35:24 +00:00
|
|
|
len = wcstol(ptr, NULL, 10);
|
2006-11-28 11:21:39 +00:00
|
|
|
extra[0] = '\0';
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
switch (types[i][0])
|
|
|
|
{
|
2006-11-28 11:21:39 +00:00
|
|
|
case 'l':
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcpyW(extra, L" NOT NULL");
|
2012-01-21 17:19:12 +00:00
|
|
|
/* fall through */
|
2006-11-28 11:21:39 +00:00
|
|
|
case 'L':
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcatW(extra, L" LOCALIZABLE");
|
|
|
|
type = L"CHAR";
|
|
|
|
swprintf(size, ARRAY_SIZE(size), L"(%s)", ptr);
|
2006-11-28 11:21:39 +00:00
|
|
|
break;
|
|
|
|
case 's':
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcpyW(extra, L" NOT NULL");
|
2012-01-21 17:19:12 +00:00
|
|
|
/* fall through */
|
2006-11-28 11:21:39 +00:00
|
|
|
case 'S':
|
2022-03-13 20:31:42 +00:00
|
|
|
type = L"CHAR";
|
|
|
|
swprintf(size, ARRAY_SIZE(size), L"(%s)", ptr);
|
2006-10-22 20:23:59 +00:00
|
|
|
break;
|
|
|
|
case 'i':
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcpyW(extra, L" NOT NULL");
|
2012-01-21 17:19:12 +00:00
|
|
|
/* fall through */
|
2006-10-22 20:23:59 +00:00
|
|
|
case 'I':
|
2009-08-08 11:55:51 +00:00
|
|
|
if (len <= 2)
|
2022-03-13 20:31:42 +00:00
|
|
|
type = L"INT";
|
2009-08-08 11:55:51 +00:00
|
|
|
else if (len == 4)
|
2022-03-13 20:31:42 +00:00
|
|
|
type = L"LONG";
|
2009-08-08 11:55:51 +00:00
|
|
|
else
|
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WARN("invalid int width %lu\n", len);
|
2009-08-08 11:55:51 +00:00
|
|
|
msi_free(columns);
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
break;
|
2009-06-06 15:59:47 +00:00
|
|
|
case 'v':
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcpyW(extra, L" NOT NULL");
|
2012-01-21 17:19:12 +00:00
|
|
|
/* fall through */
|
2009-06-06 15:59:47 +00:00
|
|
|
case 'V':
|
2022-03-13 20:31:42 +00:00
|
|
|
type = L"OBJECT";
|
2009-06-06 15:59:47 +00:00
|
|
|
break;
|
2008-02-10 13:16:32 +00:00
|
|
|
default:
|
|
|
|
ERR("Unknown type: %c\n", types[i][0]);
|
|
|
|
msi_free(columns);
|
|
|
|
return NULL;
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
swprintf(expanded, ARRAY_SIZE(expanded), L"`%s` %s%s%s%s ", columns_data[i], type, size, extra, comma);
|
2006-10-22 20:23:59 +00:00
|
|
|
sql_size += lstrlenW(expanded);
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
p = msi_realloc(columns, sql_size * sizeof(WCHAR));
|
|
|
|
if (!p)
|
|
|
|
{
|
|
|
|
msi_free(columns);
|
2006-10-22 20:23:59 +00:00
|
|
|
return NULL;
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
|
|
|
columns = p;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
lstrcatW(columns, expanded);
|
|
|
|
}
|
|
|
|
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
|
|
|
|
{
|
|
|
|
LPWSTR postlude, keys, ptr;
|
2016-11-22 12:25:27 +00:00
|
|
|
DWORD size, i;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
for (i = 0, size = 1; i < num_keys; i++)
|
2022-03-13 20:31:42 +00:00
|
|
|
size += lstrlenW(L"`%s`, ") + lstrlenW(primary_keys[i]) - 2;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
keys = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!keys)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0, ptr = keys; i < num_keys; i++)
|
|
|
|
{
|
2022-03-13 20:31:42 +00:00
|
|
|
ptr += swprintf(ptr, size - (ptr - keys), L"`%s`, ", primary_keys[i]);
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* remove final ', ' */
|
|
|
|
*(ptr - 2) = '\0';
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
size = lstrlenW(L"PRIMARY KEY %s)") + size - 1;
|
2006-10-22 20:23:59 +00:00
|
|
|
postlude = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!postlude)
|
|
|
|
goto done;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
swprintf(postlude, size, L"PRIMARY KEY %s)", keys);
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free(keys);
|
|
|
|
return postlude;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
|
|
|
|
{
|
2009-12-22 09:28:03 +00:00
|
|
|
UINT r = ERROR_OUTOFMEMORY;
|
2006-10-22 20:23:59 +00:00
|
|
|
DWORD size;
|
|
|
|
MSIQUERY *view;
|
2009-12-22 09:28:03 +00:00
|
|
|
LPWSTR create_sql = NULL;
|
2006-10-22 20:23:59 +00:00
|
|
|
LPWSTR prelude, columns_sql, postlude;
|
|
|
|
|
|
|
|
prelude = msi_build_createsql_prelude(labels[0]);
|
|
|
|
columns_sql = msi_build_createsql_columns(columns, types, num_columns);
|
|
|
|
postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
|
|
|
|
|
|
|
|
if (!prelude || !columns_sql || !postlude)
|
2009-12-22 09:28:03 +00:00
|
|
|
goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
|
|
|
|
create_sql = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!create_sql)
|
2009-12-22 09:28:03 +00:00
|
|
|
goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
lstrcpyW(create_sql, prelude);
|
|
|
|
lstrcatW(create_sql, columns_sql);
|
|
|
|
lstrcatW(create_sql, postlude);
|
|
|
|
|
|
|
|
r = MSI_DatabaseOpenViewW( db, create_sql, &view );
|
|
|
|
if (r != ERROR_SUCCESS)
|
2009-12-22 09:28:03 +00:00
|
|
|
goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
r = MSI_ViewExecute(view, NULL);
|
|
|
|
MSI_ViewClose(view);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
done:
|
|
|
|
msi_free(prelude);
|
|
|
|
msi_free(columns_sql);
|
|
|
|
msi_free(postlude);
|
|
|
|
msi_free(create_sql);
|
2006-10-22 20:23:59 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
|
|
|
|
{
|
|
|
|
DWORD len;
|
|
|
|
LPWSTR fullname, ptr;
|
|
|
|
|
|
|
|
len = lstrlenW(path) + lstrlenW(name) + 1;
|
|
|
|
fullname = msi_alloc(len*sizeof(WCHAR));
|
|
|
|
if (!fullname)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
lstrcpyW( fullname, path );
|
|
|
|
|
|
|
|
/* chop off extension from path */
|
2022-03-13 00:35:24 +00:00
|
|
|
ptr = wcsrchr(fullname, '.');
|
2009-06-06 15:59:47 +00:00
|
|
|
if (!ptr)
|
|
|
|
{
|
|
|
|
msi_free (fullname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*ptr++ = '\\';
|
|
|
|
lstrcpyW( ptr, name );
|
|
|
|
return fullname;
|
|
|
|
}
|
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
static UINT construct_record(DWORD num_columns, LPWSTR *types,
|
2009-06-06 15:59:47 +00:00
|
|
|
LPWSTR *data, LPWSTR path, MSIRECORD **rec)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
2008-04-04 13:43:40 +00:00
|
|
|
UINT i;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
*rec = MSI_CreateRecord(num_columns);
|
|
|
|
if (!*rec)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_columns; i++)
|
|
|
|
{
|
|
|
|
switch (types[i][0])
|
|
|
|
{
|
|
|
|
case 'L': case 'l': case 'S': case 's':
|
2008-04-04 13:43:40 +00:00
|
|
|
MSI_RecordSetStringW(*rec, i + 1, data[i]);
|
2006-10-22 20:23:59 +00:00
|
|
|
break;
|
|
|
|
case 'I': case 'i':
|
2008-04-04 13:43:40 +00:00
|
|
|
if (*data[i])
|
2022-03-13 00:35:24 +00:00
|
|
|
MSI_RecordSetInteger(*rec, i + 1, wcstol(data[i], NULL, 10));
|
2006-10-22 20:23:59 +00:00
|
|
|
break;
|
2009-06-06 15:59:47 +00:00
|
|
|
case 'V': case 'v':
|
|
|
|
if (*data[i])
|
|
|
|
{
|
|
|
|
UINT r;
|
|
|
|
LPWSTR file = msi_import_stream_filename(path, data[i]);
|
|
|
|
if (!file)
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
|
|
|
|
msi_free (file);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
break;
|
2006-10-22 20:23:59 +00:00
|
|
|
default:
|
2008-04-04 13:43:40 +00:00
|
|
|
ERR("Unhandled column type: %c\n", types[i][0]);
|
|
|
|
msiobj_release(&(*rec)->hdr);
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
return ERROR_SUCCESS;
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
|
|
|
|
LPWSTR *labels, LPWSTR **records,
|
2009-06-06 15:59:47 +00:00
|
|
|
int num_columns, int num_records,
|
|
|
|
LPWSTR path)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
2008-04-04 13:43:40 +00:00
|
|
|
UINT r;
|
2008-12-27 15:10:14 +00:00
|
|
|
int i;
|
2006-10-22 20:23:59 +00:00
|
|
|
MSIQUERY *view;
|
2008-04-04 13:43:40 +00:00
|
|
|
MSIRECORD *rec;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(db, &view, L"SELECT * FROM `%s`", labels[0]);
|
2008-04-04 13:43:40 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
|
|
|
|
{
|
|
|
|
r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
|
2009-12-22 09:28:03 +00:00
|
|
|
msiobj_release(&rec->hdr);
|
2008-04-04 13:43:40 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
for (i = 0; i < num_records; i++)
|
|
|
|
{
|
2009-06-06 15:59:47 +00:00
|
|
|
r = construct_record(num_columns, types, records[i], path, &rec);
|
2008-04-04 13:43:40 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
|
2006-10-22 20:23:59 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
2008-04-04 13:43:40 +00:00
|
|
|
{
|
|
|
|
msiobj_release(&rec->hdr);
|
2006-10-22 20:23:59 +00:00
|
|
|
goto done;
|
2008-04-04 13:43:40 +00:00
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
msiobj_release(&rec->hdr);
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2008-04-04 13:43:40 +00:00
|
|
|
msiobj_release(&view->hdr);
|
2006-10-22 20:23:59 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2009-01-18 12:52:01 +00:00
|
|
|
static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
UINT r;
|
2022-03-13 20:31:42 +00:00
|
|
|
DWORD len, i, num_labels, num_types, num_columns, num_records = 0;
|
|
|
|
WCHAR **columns, **types, **labels, *path, *ptr, *data, ***records = NULL, ***temp_records;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
if (!folder || !file)
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_INVALID_PARAMETER;
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
len = lstrlenW(folder) + lstrlenW(L"\\") + lstrlenW(file) + 1;
|
2006-10-22 20:23:59 +00:00
|
|
|
path = msi_alloc( len * sizeof(WCHAR) );
|
|
|
|
if (!path)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
|
|
|
|
lstrcpyW( path, folder );
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcatW( path, L"\\" );
|
2006-10-22 20:23:59 +00:00
|
|
|
lstrcatW( path, file );
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
data = msi_read_text_archive( path, &len );
|
2017-10-08 08:14:40 +00:00
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
msi_free(path);
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
ptr = data;
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_parse_line( &ptr, &columns, &num_columns, &len );
|
|
|
|
msi_parse_line( &ptr, &types, &num_types, &len );
|
|
|
|
msi_parse_line( &ptr, &labels, &num_labels, &len );
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2011-03-20 08:47:41 +00:00
|
|
|
if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
|
2022-03-13 20:31:42 +00:00
|
|
|
num_types == 2 && !wcscmp( types[1], L"_ForceCodepage" ))
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2022-03-13 00:35:24 +00:00
|
|
|
r = msi_set_string_table_codepage( db->strings, wcstol( types[0], NULL, 10 ) );
|
2011-03-20 08:47:41 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
if (num_columns != num_types)
|
|
|
|
{
|
|
|
|
r = ERROR_FUNCTION_FAILED;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
records = msi_alloc(sizeof(WCHAR **));
|
2006-10-22 20:23:59 +00:00
|
|
|
if (!records)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto done;
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
/* read in the table records */
|
2012-01-21 17:19:12 +00:00
|
|
|
while (len)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_parse_line( &ptr, &records[num_records], NULL, &len );
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
num_records++;
|
2022-03-13 20:31:42 +00:00
|
|
|
temp_records = msi_realloc(records, (num_records + 1) * sizeof(WCHAR **));
|
2008-01-16 10:11:22 +00:00
|
|
|
if (!temp_records)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
records = temp_records;
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
if (!wcscmp(labels[0], L"_SummaryInformation"))
|
2008-04-04 13:43:40 +00:00
|
|
|
{
|
2009-05-20 12:59:23 +00:00
|
|
|
r = msi_add_suminfo( db, records, num_records, num_columns );
|
2008-04-04 13:43:40 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
r = ERROR_FUNCTION_FAILED;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2009-05-20 12:59:23 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!TABLE_Exists(db, labels[0]))
|
|
|
|
{
|
|
|
|
r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
r = ERROR_FUNCTION_FAILED;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
|
2009-05-20 12:59:23 +00:00
|
|
|
}
|
2006-10-22 20:23:59 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free(path);
|
|
|
|
msi_free(data);
|
|
|
|
msi_free(columns);
|
|
|
|
msi_free(types);
|
|
|
|
msi_free(labels);
|
|
|
|
|
|
|
|
for (i = 0; i < num_records; i++)
|
|
|
|
msi_free(records[i]);
|
|
|
|
|
|
|
|
msi_free(records);
|
|
|
|
return r;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseImportW( MSIHANDLE handle, const WCHAR *szFolder, const WCHAR *szFilename )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
MSIDATABASE *db;
|
|
|
|
UINT r;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu %s %s\n", handle, debugstr_w(szFolder), debugstr_w(szFilename) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2022-03-13 00:21:41 +00:00
|
|
|
if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
|
|
|
|
return ERROR_INVALID_HANDLE;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
r = MSI_DatabaseImport( db, szFolder, szFilename );
|
|
|
|
msiobj_release( &db->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, const char *szFolder, const char *szFilename )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WCHAR *path = NULL, *file = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT r = ERROR_OUTOFMEMORY;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
if( szFolder )
|
|
|
|
{
|
|
|
|
path = strdupAtoW( szFolder );
|
|
|
|
if( !path )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( szFilename )
|
|
|
|
{
|
|
|
|
file = strdupAtoW( szFilename );
|
|
|
|
if( !file )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = MsiDatabaseImportW( handle, path, file );
|
|
|
|
|
|
|
|
end:
|
|
|
|
msi_free( path );
|
|
|
|
msi_free( file );
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-03-12 23:53:39 +00:00
|
|
|
static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field )
|
|
|
|
{
|
|
|
|
char *buffer;
|
|
|
|
BOOL ret;
|
|
|
|
DWORD sz = 0x100;
|
|
|
|
UINT r;
|
|
|
|
|
|
|
|
buffer = msi_alloc( sz );
|
|
|
|
if (!buffer)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
|
|
|
|
r = MSI_RecordGetStringA( row, field, buffer, &sz );
|
|
|
|
if (r == ERROR_MORE_DATA)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
sz++; /* leave room for NULL terminator */
|
|
|
|
tmp = msi_realloc( buffer, sz );
|
|
|
|
if (!tmp)
|
|
|
|
{
|
|
|
|
msi_free( buffer );
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
buffer = tmp;
|
|
|
|
|
|
|
|
r = MSI_RecordGetStringA( row, field, buffer, &sz );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
msi_free( buffer );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
msi_free( buffer );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = WriteFile( handle, buffer, sz, &sz, NULL );
|
|
|
|
msi_free( buffer );
|
|
|
|
return ret ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
2022-03-13 00:00:49 +00:00
|
|
|
static UINT msi_export_stream( const WCHAR *folder, const WCHAR *table, MSIRECORD *row, UINT field, UINT start )
|
|
|
|
{
|
|
|
|
WCHAR stream[MAX_STREAM_NAME_LEN + 1], *path;
|
|
|
|
DWORD sz, read_size, write_size;
|
|
|
|
char buffer[1024];
|
|
|
|
HANDLE file;
|
|
|
|
UINT len, r;
|
|
|
|
|
|
|
|
sz = ARRAY_SIZE( stream );
|
|
|
|
r = MSI_RecordGetStringW( row, start, stream, &sz );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
len = sz + lstrlenW( folder ) + lstrlenW( table ) + ARRAY_SIZE( L"%s\\%s" ) + 1;
|
2022-03-13 00:35:24 +00:00
|
|
|
if (!(path = msi_alloc( len * sizeof(WCHAR) )))
|
2022-03-13 00:00:49 +00:00
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
len = swprintf( path, len, L"%s\\%s", folder, table );
|
2022-03-13 00:00:49 +00:00
|
|
|
if (!CreateDirectoryW( path, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
|
|
|
|
{
|
|
|
|
msi_free( path );
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
path[len++] = '\\';
|
2022-03-13 00:35:24 +00:00
|
|
|
lstrcpyW( path + len, stream );
|
2022-03-13 00:00:49 +00:00
|
|
|
file = CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
msi_free( path );
|
|
|
|
if (file == INVALID_HANDLE_VALUE)
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
read_size = sizeof(buffer);
|
|
|
|
while (read_size == sizeof(buffer))
|
|
|
|
{
|
|
|
|
r = MSI_RecordReadStream( row, field, buffer, &read_size );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
CloseHandle( file );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
|
|
|
|
{
|
|
|
|
CloseHandle( file );
|
|
|
|
return ERROR_WRITE_FAULT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CloseHandle( file );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct row_export_info
|
2018-03-04 23:30:58 +00:00
|
|
|
{
|
2022-03-13 00:00:49 +00:00
|
|
|
HANDLE handle;
|
|
|
|
const WCHAR *folder;
|
|
|
|
const WCHAR *table;
|
|
|
|
};
|
|
|
|
|
|
|
|
static UINT msi_export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
|
|
|
|
{
|
|
|
|
HANDLE handle = row_export_info->handle;
|
2018-03-04 23:30:58 +00:00
|
|
|
UINT i, count, r = ERROR_SUCCESS;
|
|
|
|
const char *sep;
|
|
|
|
DWORD sz;
|
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount( row );
|
|
|
|
for (i = start; i <= count; i++)
|
|
|
|
{
|
|
|
|
r = msi_export_field( handle, row, i );
|
2022-03-13 00:00:49 +00:00
|
|
|
if (r == ERROR_INVALID_PARAMETER)
|
|
|
|
{
|
|
|
|
r = msi_export_stream( row_export_info->folder, row_export_info->table, row, i, start );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/* exporting a binary stream, repeat the "Name" field */
|
|
|
|
r = msi_export_field( handle, row, start );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
else if (r != ERROR_SUCCESS)
|
2018-03-04 23:30:58 +00:00
|
|
|
return r;
|
|
|
|
|
|
|
|
sep = (i < count) ? "\t" : "\r\n";
|
|
|
|
if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
2006-09-09 17:48:42 +00:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_export_row( MSIRECORD *row, void *arg )
|
|
|
|
{
|
|
|
|
return msi_export_record( arg, row, 1 );
|
|
|
|
}
|
|
|
|
|
2011-03-20 08:47:41 +00:00
|
|
|
static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
|
2008-04-04 13:43:40 +00:00
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
|
|
|
|
char data[sizeof(fmt) + 10];
|
2022-03-12 23:46:25 +00:00
|
|
|
DWORD sz = sprintf( data, fmt, codepage );
|
2008-04-04 13:43:40 +00:00
|
|
|
|
|
|
|
if (!WriteFile(handle, data, sz, &sz, NULL))
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
static UINT msi_export_summaryinformation( MSIDATABASE *db, HANDLE handle )
|
|
|
|
{
|
|
|
|
static const char header[] = "PropertyId\tValue\r\n"
|
|
|
|
"i2\tl255\r\n"
|
|
|
|
"_SummaryInformation\tPropertyId\r\n";
|
2022-03-12 23:46:25 +00:00
|
|
|
DWORD sz = ARRAY_SIZE(header) - 1;
|
2018-03-04 23:30:58 +00:00
|
|
|
|
|
|
|
if (!WriteFile(handle, header, sz, &sz, NULL))
|
|
|
|
return ERROR_WRITE_FAULT;
|
|
|
|
|
|
|
|
return msi_export_suminfo( db, handle );
|
|
|
|
}
|
|
|
|
|
2022-03-12 23:46:25 +00:00
|
|
|
static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, LPCWSTR file )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2006-09-09 17:48:42 +00:00
|
|
|
MSIRECORD *rec = NULL;
|
|
|
|
MSIQUERY *view = NULL;
|
2022-03-13 20:31:42 +00:00
|
|
|
WCHAR *filename;
|
2006-09-09 17:48:42 +00:00
|
|
|
HANDLE handle;
|
|
|
|
UINT len, r;
|
|
|
|
|
|
|
|
TRACE("%p %s %s %s\n", db, debugstr_w(table),
|
2006-02-17 00:04:10 +00:00
|
|
|
debugstr_w(folder), debugstr_w(file) );
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
if (!folder || !file)
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_INVALID_PARAMETER;
|
2006-09-09 17:48:42 +00:00
|
|
|
|
|
|
|
len = lstrlenW(folder) + lstrlenW(file) + 2;
|
|
|
|
filename = msi_alloc(len * sizeof (WCHAR));
|
|
|
|
if (!filename)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
|
|
|
|
lstrcpyW( filename, folder );
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcatW( filename, L"\\" );
|
2006-09-09 17:48:42 +00:00
|
|
|
lstrcatW( filename, file );
|
|
|
|
|
|
|
|
handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
|
|
|
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
msi_free( filename );
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
if (!wcscmp( table, L"_ForceCodepage" ))
|
2008-04-04 13:43:40 +00:00
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
UINT codepage = msi_get_string_table_codepage( db->strings );
|
|
|
|
r = msi_export_forcecodepage( handle, codepage );
|
2008-04-04 13:43:40 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
if (!wcscmp( table, L"_SummaryInformation" ))
|
2018-03-04 23:30:58 +00:00
|
|
|
{
|
|
|
|
r = msi_export_summaryinformation( db, handle );
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery( db, &view, L"SELECT * FROM %s", table );
|
2006-09-09 17:48:42 +00:00
|
|
|
if (r == ERROR_SUCCESS)
|
|
|
|
{
|
2018-03-04 23:30:58 +00:00
|
|
|
struct row_export_info row_export_info = { handle, folder, table };
|
|
|
|
|
2006-09-09 17:48:42 +00:00
|
|
|
/* write out row 1, the column names */
|
|
|
|
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
|
|
|
|
if (r == ERROR_SUCCESS)
|
|
|
|
{
|
2018-03-04 23:30:58 +00:00
|
|
|
msi_export_record( &row_export_info, rec, 1 );
|
2006-09-09 17:48:42 +00:00
|
|
|
msiobj_release( &rec->hdr );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write out row 2, the column types */
|
|
|
|
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
|
|
|
|
if (r == ERROR_SUCCESS)
|
|
|
|
{
|
2018-03-04 23:30:58 +00:00
|
|
|
msi_export_record( &row_export_info, rec, 1 );
|
2006-09-09 17:48:42 +00:00
|
|
|
msiobj_release( &rec->hdr );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write out row 3, the table name + keys */
|
|
|
|
r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
|
|
|
|
if (r == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
MSI_RecordSetStringW( rec, 0, table );
|
2018-03-04 23:30:58 +00:00
|
|
|
msi_export_record( &row_export_info, rec, 0 );
|
2006-09-09 17:48:42 +00:00
|
|
|
msiobj_release( &rec->hdr );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write out row 4 onwards, the data */
|
2018-03-04 23:30:58 +00:00
|
|
|
r = MSI_IterateRecords( view, 0, msi_export_row, &row_export_info );
|
2006-09-09 17:48:42 +00:00
|
|
|
msiobj_release( &view->hdr );
|
|
|
|
}
|
|
|
|
|
2008-04-04 13:43:40 +00:00
|
|
|
done:
|
2006-09-09 17:48:42 +00:00
|
|
|
CloseHandle( handle );
|
|
|
|
return r;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2006-09-09 17:48:42 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* MsiExportDatabaseW [MSI.@]
|
|
|
|
*
|
|
|
|
* Writes a file containing the table data as tab separated ASCII.
|
|
|
|
*
|
|
|
|
* The format is as follows:
|
|
|
|
*
|
|
|
|
* row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
|
|
|
|
* row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
|
|
|
|
* row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
|
|
|
|
*
|
|
|
|
* Followed by the data, starting at row 1 with one row per line
|
|
|
|
*
|
|
|
|
* row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
|
|
|
|
*/
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, const WCHAR *szTable, const WCHAR *szFolder, const WCHAR *szFilename )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
MSIDATABASE *db;
|
|
|
|
UINT r;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu %s %s %s\n", handle, debugstr_w(szTable), debugstr_w(szFolder), debugstr_w(szFilename) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2022-03-13 00:21:40 +00:00
|
|
|
if (!(db = msihandle2msiinfo(handle, MSIHANDLETYPE_DATABASE)))
|
|
|
|
return ERROR_INVALID_HANDLE;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
|
|
|
|
msiobj_release( &db->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, const char *szTable, const char *szFolder, const char *szFilename )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2022-03-14 20:20:29 +00:00
|
|
|
WCHAR *path = NULL, *file = NULL, *table = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT r = ERROR_OUTOFMEMORY;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu %s %s %s\n", handle, debugstr_a(szTable), debugstr_a(szFolder), debugstr_a(szFilename) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
if( szTable )
|
|
|
|
{
|
|
|
|
table = strdupAtoW( szTable );
|
|
|
|
if( !table )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( szFolder )
|
|
|
|
{
|
|
|
|
path = strdupAtoW( szFolder );
|
|
|
|
if( !path )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( szFilename )
|
|
|
|
{
|
|
|
|
file = strdupAtoW( szFilename );
|
|
|
|
if( !file )
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = MsiDatabaseExportW( handle, table, path, file );
|
|
|
|
|
|
|
|
end:
|
|
|
|
msi_free( table );
|
|
|
|
msi_free( path );
|
|
|
|
msi_free( file );
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseMergeA( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const char *szTableName )
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
|
|
|
UINT r;
|
2022-03-14 20:20:29 +00:00
|
|
|
WCHAR *table;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE("%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_a(szTableName) );
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
table = strdupAtoW(szTableName);
|
|
|
|
r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
|
|
|
|
|
|
|
|
msi_free(table);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _tagMERGETABLE
|
|
|
|
{
|
|
|
|
struct list entry;
|
|
|
|
struct list rows;
|
|
|
|
LPWSTR name;
|
|
|
|
DWORD numconflicts;
|
2009-06-06 15:59:47 +00:00
|
|
|
LPWSTR *columns;
|
|
|
|
DWORD numcolumns;
|
|
|
|
LPWSTR *types;
|
|
|
|
DWORD numtypes;
|
|
|
|
LPWSTR *labels;
|
|
|
|
DWORD numlabels;
|
2008-12-27 15:10:14 +00:00
|
|
|
} MERGETABLE;
|
|
|
|
|
|
|
|
typedef struct _tagMERGEROW
|
|
|
|
{
|
|
|
|
struct list entry;
|
|
|
|
MSIRECORD *data;
|
|
|
|
} MERGEROW;
|
|
|
|
|
|
|
|
typedef struct _tagMERGEDATA
|
|
|
|
{
|
|
|
|
MSIDATABASE *db;
|
|
|
|
MSIDATABASE *merge;
|
|
|
|
MERGETABLE *curtable;
|
|
|
|
MSIQUERY *curview;
|
|
|
|
struct list *tabledata;
|
|
|
|
} MERGEDATA;
|
|
|
|
|
2009-10-25 11:06:09 +00:00
|
|
|
static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
|
|
|
|
{
|
|
|
|
if (((type1[0] == 'l') || (type1[0] == 's')) &&
|
|
|
|
((type2[0] == 'l') || (type2[0] == 's')))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (((type1[0] == 'L') || (type1[0] == 'S')) &&
|
|
|
|
((type2[0] == 'L') || (type2[0] == 'S')))
|
|
|
|
return TRUE;
|
|
|
|
|
2022-03-13 00:35:24 +00:00
|
|
|
return !wcscmp( type1, type2 );
|
2009-10-25 11:06:09 +00:00
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
|
|
|
|
{
|
|
|
|
MSIRECORD *dbrec, *mergerec;
|
|
|
|
UINT r, i, count;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
2013-09-22 16:30:18 +00:00
|
|
|
{
|
|
|
|
msiobj_release(&dbrec->hdr);
|
2008-12-27 15:10:14 +00:00
|
|
|
return r;
|
2013-09-22 16:30:18 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(dbrec);
|
|
|
|
for (i = 1; i <= count; i++)
|
|
|
|
{
|
|
|
|
if (!MSI_RecordGetString(mergerec, i))
|
|
|
|
break;
|
|
|
|
|
2022-03-13 00:35:24 +00:00
|
|
|
if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
|
|
|
r = ERROR_DATATYPE_MISMATCH;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msiobj_release(&dbrec->hdr);
|
|
|
|
msiobj_release(&mergerec->hdr);
|
|
|
|
dbrec = mergerec = NULL;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
2013-09-22 16:30:18 +00:00
|
|
|
{
|
|
|
|
msiobj_release(&dbrec->hdr);
|
2008-12-27 15:10:14 +00:00
|
|
|
return r;
|
2013-09-22 16:30:18 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(dbrec);
|
|
|
|
for (i = 1; i <= count; i++)
|
|
|
|
{
|
|
|
|
if (!MSI_RecordGetString(mergerec, i))
|
|
|
|
break;
|
|
|
|
|
2009-10-25 11:06:09 +00:00
|
|
|
if (!merge_type_match(MSI_RecordGetString(dbrec, i),
|
2008-12-27 15:10:14 +00:00
|
|
|
MSI_RecordGetString(mergerec, i)))
|
|
|
|
{
|
|
|
|
r = ERROR_DATATYPE_MISMATCH;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
msiobj_release(&dbrec->hdr);
|
|
|
|
msiobj_release(&mergerec->hdr);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
|
|
|
|
LPCWSTR table)
|
|
|
|
{
|
|
|
|
MSIRECORD *dbrec, *mergerec = NULL;
|
|
|
|
UINT r, i, count;
|
|
|
|
|
|
|
|
r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(dbrec);
|
|
|
|
if (count != MSI_RecordGetFieldCount(mergerec))
|
|
|
|
{
|
|
|
|
r = ERROR_DATATYPE_MISMATCH;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i <= count; i++)
|
|
|
|
{
|
2022-03-13 00:35:24 +00:00
|
|
|
if (wcscmp( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
|
|
|
r = ERROR_DATATYPE_MISMATCH;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
msiobj_release(&dbrec->hdr);
|
|
|
|
msiobj_release(&mergerec->hdr);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
|
|
|
|
{
|
|
|
|
MSIRECORD *colnames;
|
2009-06-06 15:59:47 +00:00
|
|
|
LPWSTR str, val;
|
2022-03-14 20:20:24 +00:00
|
|
|
UINT r, i = 0;
|
|
|
|
DWORD sz = 0;
|
2008-12-27 15:10:14 +00:00
|
|
|
int cmp;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
str = msi_dup_record_field(colnames, ++i);
|
2022-03-13 00:35:24 +00:00
|
|
|
cmp = wcscmp( key, str );
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(str);
|
|
|
|
} while (cmp);
|
|
|
|
|
|
|
|
msiobj_release(&colnames->hdr);
|
2009-06-06 15:59:47 +00:00
|
|
|
|
|
|
|
r = MSI_RecordGetStringW(rec, i, NULL, &sz);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return NULL;
|
|
|
|
sz++;
|
|
|
|
|
|
|
|
if (MSI_RecordGetString(rec, i)) /* check record field is a string */
|
|
|
|
{
|
|
|
|
/* quote string record fields */
|
|
|
|
sz += 2;
|
2022-03-13 20:31:42 +00:00
|
|
|
val = msi_alloc(sz * sizeof(WCHAR));
|
2009-06-06 15:59:47 +00:00
|
|
|
if (!val)
|
|
|
|
return NULL;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
lstrcpyW(val, L"'");
|
|
|
|
r = MSI_RecordGetStringW(rec, i, val + 1, &sz);
|
|
|
|
lstrcpyW(val + 1 + sz, L"'");
|
2009-06-06 15:59:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* do not quote integer record fields */
|
2022-03-13 20:31:42 +00:00
|
|
|
val = msi_alloc(sz * sizeof(WCHAR));
|
2009-06-06 15:59:47 +00:00
|
|
|
if (!val)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
r = MSI_RecordGetStringW(rec, i, val, &sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("failed to get string!\n");
|
|
|
|
msi_free(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
|
|
|
|
LPWSTR table, MSIRECORD *rec)
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
LPWSTR query = NULL, clause = NULL, val;
|
|
|
|
LPCWSTR setptr, key;
|
|
|
|
DWORD size, oldsize;
|
2008-12-27 15:10:14 +00:00
|
|
|
MSIRECORD *keys;
|
|
|
|
UINT r, i, count;
|
|
|
|
|
|
|
|
r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return NULL;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
clause = msi_alloc_zero(sizeof(WCHAR));
|
2008-12-27 15:10:14 +00:00
|
|
|
if (!clause)
|
|
|
|
goto done;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
size = 1;
|
2008-12-27 15:10:14 +00:00
|
|
|
count = MSI_RecordGetFieldCount(keys);
|
|
|
|
for (i = 1; i <= count; i++)
|
|
|
|
{
|
|
|
|
key = MSI_RecordGetString(keys, i);
|
|
|
|
val = get_key_value(view, key, rec);
|
|
|
|
|
|
|
|
if (i == count)
|
2022-03-13 20:31:42 +00:00
|
|
|
setptr = L"`%s` = %s ";
|
2008-12-27 15:10:14 +00:00
|
|
|
else
|
2022-03-13 20:31:42 +00:00
|
|
|
setptr = L"`%s` = %s AND ";
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
oldsize = size;
|
|
|
|
size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
|
|
|
|
clause = msi_realloc(clause, size * sizeof (WCHAR));
|
|
|
|
if (!clause)
|
|
|
|
{
|
|
|
|
msi_free(val);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2022-03-13 00:35:24 +00:00
|
|
|
swprintf(clause + oldsize - 1, size - (oldsize - 1), setptr, key, val);
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(val);
|
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
size = lstrlenW(L"SELECT * FROM `%s` WHERE %s") + lstrlenW(table) + lstrlenW(clause) + 1;
|
2008-12-27 15:10:14 +00:00
|
|
|
query = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!query)
|
|
|
|
goto done;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
swprintf(query, size, L"SELECT * FROM `%s` WHERE %s", table, clause);
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free(clause);
|
|
|
|
msiobj_release(&keys->hdr);
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
|
|
|
|
{
|
2009-01-18 12:52:01 +00:00
|
|
|
MERGEDATA *data = param;
|
2008-12-27 15:10:14 +00:00
|
|
|
MERGETABLE *table = data->curtable;
|
|
|
|
MERGEROW *mergerow;
|
2009-06-06 15:59:47 +00:00
|
|
|
MSIQUERY *dbview = NULL;
|
2009-01-31 14:13:20 +00:00
|
|
|
MSIRECORD *row = NULL;
|
2009-06-06 15:59:47 +00:00
|
|
|
LPWSTR query = NULL;
|
|
|
|
UINT r = ERROR_SUCCESS;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
if (TABLE_Exists(data->db, table->name))
|
|
|
|
{
|
|
|
|
query = create_diff_row_query(data->merge, data->curview, table->name, rec);
|
|
|
|
if (!query)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = MSI_ViewExecute(dbview, NULL);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = MSI_ViewFetch(dbview, &row);
|
|
|
|
if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
|
|
|
|
{
|
|
|
|
table->numconflicts++;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
else if (r != ERROR_NO_MORE_ITEMS)
|
|
|
|
goto done;
|
2009-10-25 11:06:09 +00:00
|
|
|
|
|
|
|
r = ERROR_SUCCESS;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mergerow = msi_alloc(sizeof(MERGEROW));
|
|
|
|
if (!mergerow)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
mergerow->data = MSI_CloneRecord(rec);
|
|
|
|
if (!mergerow->data)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
msi_free(mergerow);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail(&table->rows, &mergerow->entry);
|
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free(query);
|
|
|
|
msiobj_release(&row->hdr);
|
|
|
|
msiobj_release(&dbview->hdr);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
|
|
|
|
{
|
|
|
|
UINT r, i, count;
|
|
|
|
MSIRECORD *prec = NULL;
|
|
|
|
|
|
|
|
r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(prec);
|
|
|
|
*numlabels = count + 1;
|
|
|
|
*labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
|
|
|
|
if (!*labels)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*labels)[0] = strdupW(table);
|
|
|
|
for (i=1; i<=count; i++ )
|
|
|
|
{
|
|
|
|
(*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
msiobj_release( &prec->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
|
|
|
|
{
|
|
|
|
UINT r, i, count;
|
|
|
|
MSIRECORD *prec = NULL;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(prec);
|
|
|
|
*columns = msi_alloc(count*sizeof(LPWSTR));
|
|
|
|
if (!*columns)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=1; i<=count; i++ )
|
|
|
|
{
|
|
|
|
(*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
*numcolumns = count;
|
|
|
|
|
|
|
|
end:
|
|
|
|
msiobj_release( &prec->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
|
|
|
|
{
|
|
|
|
UINT r, i, count;
|
|
|
|
MSIRECORD *prec = NULL;
|
|
|
|
|
|
|
|
r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
count = MSI_RecordGetFieldCount(prec);
|
|
|
|
*types = msi_alloc(count*sizeof(LPWSTR));
|
|
|
|
if (!*types)
|
|
|
|
{
|
|
|
|
r = ERROR_OUTOFMEMORY;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
*numtypes = count;
|
2009-06-06 15:59:47 +00:00
|
|
|
for (i=1; i<=count; i++ )
|
|
|
|
{
|
|
|
|
(*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
msiobj_release( &prec->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void merge_free_rows(MERGETABLE *table)
|
|
|
|
{
|
|
|
|
struct list *item, *cursor;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
|
|
|
|
{
|
|
|
|
MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
|
|
|
|
|
|
|
|
list_remove(&row->entry);
|
|
|
|
msiobj_release(&row->data->hdr);
|
|
|
|
msi_free(row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_merge_table(MERGETABLE *table)
|
|
|
|
{
|
2009-12-22 09:28:03 +00:00
|
|
|
UINT i;
|
2009-06-06 15:59:47 +00:00
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
if (table->labels != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < table->numlabels; i++)
|
|
|
|
msi_free(table->labels[i]);
|
2009-06-06 15:59:47 +00:00
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
msi_free(table->labels);
|
|
|
|
}
|
2009-06-06 15:59:47 +00:00
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
if (table->columns != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < table->numcolumns; i++)
|
|
|
|
msi_free(table->columns[i]);
|
2009-06-06 15:59:47 +00:00
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
msi_free(table->columns);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (table->types != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < table->numtypes; i++)
|
|
|
|
msi_free(table->types[i]);
|
|
|
|
|
|
|
|
msi_free(table->types);
|
|
|
|
}
|
|
|
|
|
|
|
|
msi_free(table->name);
|
|
|
|
merge_free_rows(table);
|
|
|
|
|
|
|
|
msi_free(table);
|
2009-06-06 15:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
|
|
|
|
{
|
|
|
|
UINT r;
|
|
|
|
MERGETABLE *table;
|
|
|
|
MSIQUERY *mergeview = NULL;
|
|
|
|
|
|
|
|
table = msi_alloc_zero(sizeof(MERGETABLE));
|
|
|
|
if (!table)
|
|
|
|
{
|
|
|
|
*ptable = NULL;
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto err;
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(db, &mergeview, L"SELECT * FROM `%s`", name);
|
2009-06-06 15:59:47 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
list_init(&table->rows);
|
|
|
|
|
|
|
|
table->name = strdupW(name);
|
|
|
|
table->numconflicts = 0;
|
|
|
|
|
|
|
|
msiobj_release(&mergeview->hdr);
|
|
|
|
*ptable = table;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
|
|
|
err:
|
|
|
|
msiobj_release(&mergeview->hdr);
|
|
|
|
free_merge_table(table);
|
|
|
|
*ptable = NULL;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
|
|
|
|
{
|
2009-01-18 12:52:01 +00:00
|
|
|
MERGEDATA *data = param;
|
2008-12-27 15:10:14 +00:00
|
|
|
MERGETABLE *table;
|
2009-06-06 15:59:47 +00:00
|
|
|
MSIQUERY *dbview = NULL;
|
2008-12-27 15:10:14 +00:00
|
|
|
MSIQUERY *mergeview = NULL;
|
|
|
|
LPCWSTR name;
|
|
|
|
UINT r;
|
|
|
|
|
|
|
|
name = MSI_RecordGetString(rec, 1);
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(data->merge, &mergeview, L"SELECT * FROM `%s`", name);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
if (TABLE_Exists(data->db, name))
|
|
|
|
{
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(data->db, &dbview, L"SELECT * FROM `%s`", name);
|
2009-06-06 15:59:47 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = merge_verify_colnames(dbview, mergeview);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = merge_verify_primary_keys(data->db, data->merge, name);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
r = msi_get_merge_table(data->merge, name, &table);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
data->curtable = table;
|
|
|
|
data->curview = mergeview;
|
|
|
|
r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
{
|
2009-06-06 15:59:47 +00:00
|
|
|
free_merge_table(table);
|
2008-12-27 15:10:14 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail(data->tabledata, &table->entry);
|
|
|
|
|
|
|
|
done:
|
|
|
|
msiobj_release(&dbview->hdr);
|
|
|
|
msiobj_release(&mergeview->hdr);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
|
|
|
|
struct list *tabledata)
|
|
|
|
{
|
|
|
|
MSIQUERY *view;
|
|
|
|
MERGEDATA data;
|
2012-01-21 17:19:12 +00:00
|
|
|
UINT r;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_DatabaseOpenViewW(merge, L"SELECT * FROM `_Tables`", &view);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
data.db = db;
|
|
|
|
data.merge = merge;
|
|
|
|
data.tabledata = tabledata;
|
|
|
|
r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
|
|
|
|
{
|
|
|
|
UINT r;
|
|
|
|
MERGEROW *row;
|
|
|
|
MSIVIEW *tv;
|
|
|
|
|
2009-06-06 15:59:47 +00:00
|
|
|
if (!TABLE_Exists(db, table->name))
|
|
|
|
{
|
|
|
|
r = msi_add_table_to_db(db, table->columns, table->types,
|
|
|
|
table->labels, table->numlabels, table->numcolumns);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
|
|
|
|
{
|
|
|
|
r = TABLE_CreateView(db, table->name, &tv);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
2009-05-20 12:59:23 +00:00
|
|
|
r = tv->ops->insert_row(tv, row->data, -1, FALSE);
|
2008-12-27 15:10:14 +00:00
|
|
|
tv->ops->delete(tv);
|
|
|
|
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
|
|
|
|
LPWSTR table, DWORD numconflicts)
|
|
|
|
{
|
|
|
|
UINT r;
|
|
|
|
MSIQUERY *view;
|
|
|
|
|
|
|
|
if (!TABLE_Exists(db, error))
|
|
|
|
{
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(db, &view, L"CREATE TABLE `%s` (`Table` CHAR(255) NOT NULL, `NumRowMergeConflicts` SHORT "
|
|
|
|
"NOT NULL PRIMARY KEY `Table`)" , error);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = MSI_ViewExecute(view, NULL);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-03-13 20:31:42 +00:00
|
|
|
r = MSI_OpenQuery(db, &view, L"INSERT INTO `%s` (`Table`, `NumRowMergeConflicts`) VALUES ('%s', %d)", error,
|
|
|
|
table, numconflicts);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = MSI_ViewExecute(view, NULL);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
UINT WINAPI MsiDatabaseMergeW( MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, const WCHAR *szTableName )
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
|
|
|
struct list tabledata = LIST_INIT(tabledata);
|
|
|
|
struct list *item, *cursor;
|
|
|
|
MSIDATABASE *db, *merge;
|
|
|
|
MERGETABLE *table;
|
|
|
|
BOOL conflicts;
|
|
|
|
UINT r;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu, %lu, %s\n", hDatabase, hDatabaseMerge, debugstr_w(szTableName) );
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
if (szTableName && !*szTableName)
|
|
|
|
return ERROR_INVALID_TABLE;
|
|
|
|
|
|
|
|
db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
|
|
|
|
merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
|
|
|
|
if (!db || !merge)
|
|
|
|
{
|
|
|
|
r = ERROR_INVALID_HANDLE;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = gather_merge_data(db, merge, &tabledata);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
conflicts = FALSE;
|
|
|
|
LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
|
|
|
|
{
|
|
|
|
if (table->numconflicts)
|
|
|
|
{
|
|
|
|
conflicts = TRUE;
|
|
|
|
|
|
|
|
r = update_merge_errors(db, szTableName, table->name,
|
|
|
|
table->numconflicts);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r = merge_table(db, table);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
|
|
|
|
{
|
|
|
|
MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
|
|
|
|
list_remove(&table->entry);
|
2009-06-06 15:59:47 +00:00
|
|
|
free_merge_table(table);
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (conflicts)
|
|
|
|
r = ERROR_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
done:
|
|
|
|
msiobj_release(&db->hdr);
|
|
|
|
msiobj_release(&merge->hdr);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
|
|
|
|
{
|
|
|
|
MSIDBSTATE ret = MSIDBSTATE_READ;
|
|
|
|
MSIDATABASE *db;
|
|
|
|
|
2022-03-14 20:20:29 +00:00
|
|
|
TRACE( "%lu\n", handle );
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2022-03-13 00:19:31 +00:00
|
|
|
if (!(db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE )))
|
|
|
|
return MSIDBSTATE_ERROR;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2022-03-13 18:05:01 +00:00
|
|
|
if (db->mode != MSI_OPEN_READONLY )
|
2006-08-01 23:12:11 +00:00
|
|
|
ret = MSIDBSTATE_WRITE;
|
|
|
|
msiobj_release( &db->hdr );
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2022-03-12 14:12:08 +00:00
|
|
|
MSICONDITION __cdecl s_remote_DatabaseIsTablePersistent(MSIHANDLE db, LPCWSTR table)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
2022-03-12 14:11:52 +00:00
|
|
|
return MsiDatabaseIsTablePersistentW(db, table);
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:12:08 +00:00
|
|
|
UINT __cdecl s_remote_DatabaseGetPrimaryKeys(MSIHANDLE db, LPCWSTR table, struct wire_record **rec)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
2022-03-12 14:11:56 +00:00
|
|
|
MSIHANDLE handle;
|
|
|
|
UINT r = MsiDatabaseGetPrimaryKeysW(db, table, &handle);
|
|
|
|
*rec = NULL;
|
|
|
|
if (!r)
|
|
|
|
*rec = marshal_record(handle);
|
|
|
|
MsiCloseHandle(handle);
|
|
|
|
return r;
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:12:08 +00:00
|
|
|
UINT __cdecl s_remote_DatabaseGetSummaryInformation(MSIHANDLE db, UINT updatecount, MSIHANDLE *suminfo)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
2022-03-12 14:11:57 +00:00
|
|
|
return MsiGetSummaryInformationW(db, NULL, updatecount, suminfo);
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:12:08 +00:00
|
|
|
UINT __cdecl s_remote_DatabaseOpenView(MSIHANDLE db, LPCWSTR query, MSIHANDLE *view)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
2022-03-12 14:11:52 +00:00
|
|
|
return MsiDatabaseOpenViewW(db, query, view);
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|