| /* |
| * Implementation of the Microsoft Installer (msi.dll) |
| * |
| * Copyright 2002-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 |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| #include "msi.h" |
| #include "msiquery.h" |
| #include "objbase.h" |
| #include "objidl.h" |
| #include "msipriv.h" |
| #include "winnls.h" |
| |
| #include "query.h" |
| #include "msiserver.h" |
| |
| #include "initguid.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); |
| |
| static void MSI_CloseView( MSIOBJECTHDR *arg ) |
| { |
| MSIQUERY *query = (MSIQUERY*) arg; |
| struct list *ptr, *t; |
| |
| if( query->view && query->view->ops->delete ) |
| query->view->ops->delete( query->view ); |
| msiobj_release( &query->db->hdr ); |
| |
| LIST_FOR_EACH_SAFE( ptr, t, &query->mem ) |
| { |
| msi_free( ptr ); |
| } |
| } |
| |
| UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, LPCWSTR table_name, UINT *n ) |
| { |
| LPCWSTR col_name, haystack_table_name; |
| UINT i, count, r; |
| |
| r = table->ops->get_dimensions( table, NULL, &count ); |
| if( r != ERROR_SUCCESS ) |
| return r; |
| |
| for( i=1; i<=count; i++ ) |
| { |
| INT x; |
| |
| r = table->ops->get_column_info( table, i, &col_name, NULL, |
| NULL, &haystack_table_name ); |
| if( r != ERROR_SUCCESS ) |
| return r; |
| x = strcmpW( name, col_name ); |
| if( table_name ) |
| x |= strcmpW( table_name, haystack_table_name ); |
| if( !x ) |
| { |
| *n = i; |
| return ERROR_SUCCESS; |
| } |
| } |
| return ERROR_INVALID_PARAMETER; |
| } |
| |
| UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb, |
| LPCSTR szQuery, MSIHANDLE *phView) |
| { |
| UINT r; |
| LPWSTR szwQuery; |
| |
| TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView); |
| |
| if( szQuery ) |
| { |
| szwQuery = strdupAtoW( szQuery ); |
| if( !szwQuery ) |
| return ERROR_FUNCTION_FAILED; |
| } |
| else |
| szwQuery = NULL; |
| |
| r = MsiDatabaseOpenViewW( hdb, szwQuery, phView); |
| |
| msi_free( szwQuery ); |
| return r; |
| } |
| |
| UINT MSI_DatabaseOpenViewW(MSIDATABASE *db, |
| LPCWSTR szQuery, MSIQUERY **pView) |
| { |
| MSIQUERY *query; |
| UINT r; |
| |
| TRACE("%s %p\n", debugstr_w(szQuery), pView); |
| |
| if( !szQuery) |
| return ERROR_INVALID_PARAMETER; |
| |
| /* pre allocate a handle to hold a pointer to the view */ |
| query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), |
| MSI_CloseView ); |
| if( !query ) |
| return ERROR_FUNCTION_FAILED; |
| |
| msiobj_addref( &db->hdr ); |
| query->db = db; |
| list_init( &query->mem ); |
| |
| r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem ); |
| if( r == ERROR_SUCCESS ) |
| { |
| msiobj_addref( &query->hdr ); |
| *pView = query; |
| } |
| |
| msiobj_release( &query->hdr ); |
| return r; |
| } |
| |
| UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... ) |
| { |
| UINT r; |
| int size = 100, res; |
| LPWSTR query; |
| |
| /* construct the string */ |
| for (;;) |
| { |
| va_list va; |
| query = msi_alloc( size*sizeof(WCHAR) ); |
| va_start(va, fmt); |
| res = vsnprintfW(query, size, fmt, va); |
| va_end(va); |
| if (res == -1) size *= 2; |
| else if (res >= size) size = res + 1; |
| else break; |
| msi_free( query ); |
| } |
| /* perform the query */ |
| r = MSI_DatabaseOpenViewW(db, query, view); |
| msi_free(query); |
| return r; |
| } |
| |
| UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count, |
| record_func func, LPVOID param ) |
| { |
| MSIRECORD *rec = NULL; |
| UINT r, n = 0, max = 0; |
| |
| r = MSI_ViewExecute( view, NULL ); |
| if( r != ERROR_SUCCESS ) |
| return r; |
| |
| if( count ) |
| max = *count; |
| |
| /* iterate a query */ |
| for( n = 0; (max == 0) || (n < max); n++ ) |
| { |
| r = MSI_ViewFetch( view, &rec ); |
| if( r != ERROR_SUCCESS ) |
| break; |
| if (func) |
| r = func( rec, param ); |
| msiobj_release( &rec->hdr ); |
| if( r != ERROR_SUCCESS ) |
| break; |
| } |
| |
| MSI_ViewClose( view ); |
| |
| if( count ) |
| *count = n; |
| |
| if( r == ERROR_NO_MORE_ITEMS ) |
| r = ERROR_SUCCESS; |
| |
| return r; |
| } |
| |
| /* return a single record from a query */ |
| MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... ) |
| { |
| MSIRECORD *rec = NULL; |
| MSIQUERY *view = NULL; |
| UINT r; |
| int size = 100, res; |
| LPWSTR query; |
| |
| /* construct the string */ |
| for (;;) |
| { |
| va_list va; |
| query = msi_alloc( size*sizeof(WCHAR) ); |
| va_start(va, fmt); |
| res = vsnprintfW(query, size, fmt, va); |
| va_end(va); |
| if (res == -1) size *= 2; |
| else if (res >= size) size = res + 1; |
| else break; |
| msi_free( query ); |
| } |
| /* perform the query */ |
| r = MSI_DatabaseOpenViewW(db, query, &view); |
| msi_free(query); |
| |
| if( r == ERROR_SUCCESS ) |
| { |
| MSI_ViewExecute( view, NULL ); |
| MSI_ViewFetch( view, &rec ); |
| MSI_ViewClose( view ); |
| msiobj_release( &view->hdr ); |
| } |
| return rec; |
| } |
| |
| UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb, |
| LPCWSTR szQuery, MSIHANDLE *phView) |
| { |
| MSIDATABASE *db; |
| MSIQUERY *query = NULL; |
| UINT ret; |
| |
| TRACE("%s %p\n", debugstr_w(szQuery), phView); |
| |
| db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); |
| if( !db ) |
| { |
| HRESULT hr; |
| IWineMsiRemoteDatabase *remote_database; |
| |
| remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); |
| if ( !remote_database ) |
| return ERROR_INVALID_HANDLE; |
| |
| hr = IWineMsiRemoteDatabase_OpenView( remote_database, szQuery, phView ); |
| IWineMsiRemoteDatabase_Release( remote_database ); |
| |
| if (FAILED(hr)) |
| { |
| if (HRESULT_FACILITY(hr) == FACILITY_WIN32) |
| return HRESULT_CODE(hr); |
| |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| ret = MSI_DatabaseOpenViewW( db, szQuery, &query ); |
| if( ret == ERROR_SUCCESS ) |
| { |
| *phView = alloc_msihandle( &query->hdr ); |
| if (! *phView) |
| ret = ERROR_NOT_ENOUGH_MEMORY; |
| msiobj_release( &query->hdr ); |
| } |
| msiobj_release( &db->hdr ); |
| |
| return ret; |
| } |
| |
| UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec) |
| { |
| UINT row_count = 0, col_count = 0, i, ival, ret, type; |
| |
| TRACE("%p %p %d %p\n", db, view, row, rec); |
| |
| ret = view->ops->get_dimensions(view, &row_count, &col_count); |
| if (ret) |
| return ret; |
| |
| if (!col_count) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (row >= row_count) |
| return ERROR_NO_MORE_ITEMS; |
| |
| *rec = MSI_CreateRecord(col_count); |
| if (!*rec) |
| return ERROR_FUNCTION_FAILED; |
| |
| for (i = 1; i <= col_count; i++) |
| { |
| ret = view->ops->get_column_info(view, i, NULL, &type, NULL, NULL); |
| if (ret) |
| { |
| ERR("Error getting column type for %d\n", i); |
| continue; |
| } |
| |
| if (MSITYPE_IS_BINARY(type)) |
| { |
| IStream *stm = NULL; |
| |
| ret = view->ops->fetch_stream(view, row, i, &stm); |
| if ((ret == ERROR_SUCCESS) && stm) |
| { |
| MSI_RecordSetIStream(*rec, i, stm); |
| IStream_Release(stm); |
| } |
| else |
| WARN("failed to get stream\n"); |
| |
| continue; |
| } |
| |
| ret = view->ops->fetch_int(view, row, i, &ival); |
| if (ret) |
| { |
| ERR("Error fetching data for %d\n", i); |
| continue; |
| } |
| |
| if (! (type & MSITYPE_VALID)) |
| ERR("Invalid type!\n"); |
| |
| /* check if it's nul (0) - if so, don't set anything */ |
| if (!ival) |
| continue; |
| |
| if (type & MSITYPE_STRING) |
| { |
| int len; |
| const WCHAR *sval = msi_string_lookup( db->strings, ival, &len ); |
| msi_record_set_string( *rec, i, sval, len ); |
| } |
| else |
| { |
| if ((type & MSI_DATASIZEMASK) == 2) |
| MSI_RecordSetInteger(*rec, i, ival - (1<<15)); |
| else |
| MSI_RecordSetInteger(*rec, i, ival - (1u<<31)); |
| } |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec) |
| { |
| MSIVIEW *view; |
| UINT r; |
| |
| TRACE("%p %p\n", query, prec ); |
| |
| view = query->view; |
| if( !view ) |
| return ERROR_FUNCTION_FAILED; |
| |
| r = msi_view_get_row(query->db, view, query->row, prec); |
| if (r == ERROR_SUCCESS) |
| { |
| query->row ++; |
| MSI_RecordSetIntPtr(*prec, 0, (INT_PTR)query); |
| } |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record) |
| { |
| MSIQUERY *query; |
| MSIRECORD *rec = NULL; |
| UINT ret; |
| |
| TRACE("%d %p\n", hView, record); |
| |
| if( !record ) |
| return ERROR_INVALID_PARAMETER; |
| *record = 0; |
| |
| query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return ERROR_INVALID_HANDLE; |
| ret = MSI_ViewFetch( query, &rec ); |
| if( ret == ERROR_SUCCESS ) |
| { |
| *record = alloc_msihandle( &rec->hdr ); |
| if (! *record) |
| ret = ERROR_NOT_ENOUGH_MEMORY; |
| msiobj_release( &rec->hdr ); |
| } |
| msiobj_release( &query->hdr ); |
| return ret; |
| } |
| |
| UINT MSI_ViewClose(MSIQUERY *query) |
| { |
| MSIVIEW *view; |
| |
| TRACE("%p\n", query ); |
| |
| view = query->view; |
| if( !view ) |
| return ERROR_FUNCTION_FAILED; |
| if( !view->ops->close ) |
| return ERROR_FUNCTION_FAILED; |
| |
| return view->ops->close( view ); |
| } |
| |
| UINT WINAPI MsiViewClose(MSIHANDLE hView) |
| { |
| MSIQUERY *query; |
| UINT ret; |
| |
| TRACE("%d\n", hView ); |
| |
| query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return ERROR_INVALID_HANDLE; |
| |
| ret = MSI_ViewClose( query ); |
| msiobj_release( &query->hdr ); |
| return ret; |
| } |
| |
| UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec ) |
| { |
| MSIVIEW *view; |
| |
| TRACE("%p %p\n", query, rec); |
| |
| view = query->view; |
| if( !view ) |
| return ERROR_FUNCTION_FAILED; |
| if( !view->ops->execute ) |
| return ERROR_FUNCTION_FAILED; |
| query->row = 0; |
| |
| return view->ops->execute( view, rec ); |
| } |
| |
| UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec) |
| { |
| MSIQUERY *query; |
| MSIRECORD *rec = NULL; |
| UINT ret; |
| |
| TRACE("%d %d\n", hView, hRec); |
| |
| query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return ERROR_INVALID_HANDLE; |
| |
| if( hRec ) |
| { |
| rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD ); |
| if( !rec ) |
| { |
| ret = ERROR_INVALID_HANDLE; |
| goto out; |
| } |
| } |
| |
| msiobj_lock( &rec->hdr ); |
| ret = MSI_ViewExecute( query, rec ); |
| msiobj_unlock( &rec->hdr ); |
| |
| out: |
| msiobj_release( &query->hdr ); |
| if( rec ) |
| msiobj_release( &rec->hdr ); |
| |
| return ret; |
| } |
| |
| static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, |
| UINT type, BOOL temporary ) |
| { |
| static const WCHAR fmt[] = { '%','d',0 }; |
| WCHAR szType[0x10]; |
| |
| if (MSITYPE_IS_BINARY(type)) |
| szType[0] = 'v'; |
| else if (type & MSITYPE_LOCALIZABLE) |
| szType[0] = 'l'; |
| else if (type & MSITYPE_UNKNOWN) |
| szType[0] = 'f'; |
| else if (type & MSITYPE_STRING) |
| { |
| if (temporary) |
| szType[0] = 'g'; |
| else |
| szType[0] = 's'; |
| } |
| else |
| { |
| if (temporary) |
| szType[0] = 'j'; |
| else |
| szType[0] = 'i'; |
| } |
| |
| if (type & MSITYPE_NULLABLE) |
| szType[0] &= ~0x20; |
| |
| sprintfW( &szType[1], fmt, (type&0xff) ); |
| |
| TRACE("type %04x -> %s\n", type, debugstr_w(szType) ); |
| |
| return MSI_RecordSetStringW( rec, field, szType ); |
| } |
| |
| UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec ) |
| { |
| UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type; |
| MSIRECORD *rec; |
| MSIVIEW *view = query->view; |
| LPCWSTR name; |
| BOOL temporary; |
| |
| if( !view ) |
| return ERROR_FUNCTION_FAILED; |
| |
| if( !view->ops->get_dimensions ) |
| return ERROR_FUNCTION_FAILED; |
| |
| r = view->ops->get_dimensions( view, NULL, &count ); |
| if( r != ERROR_SUCCESS ) |
| return r; |
| if( !count ) |
| return ERROR_INVALID_PARAMETER; |
| |
| rec = MSI_CreateRecord( count ); |
| if( !rec ) |
| return ERROR_FUNCTION_FAILED; |
| |
| for( i=0; i<count; i++ ) |
| { |
| name = NULL; |
| r = view->ops->get_column_info( view, i+1, &name, &type, &temporary, NULL ); |
| if( r != ERROR_SUCCESS ) |
| continue; |
| if (info == MSICOLINFO_NAMES) |
| MSI_RecordSetStringW( rec, i+1, name ); |
| else |
| msi_set_record_type_string( rec, i+1, type, temporary ); |
| } |
| *prec = rec; |
| return ERROR_SUCCESS; |
| } |
| |
| UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec) |
| { |
| MSIQUERY *query = NULL; |
| MSIRECORD *rec = NULL; |
| UINT r; |
| |
| TRACE("%d %d %p\n", hView, info, hRec); |
| |
| if( !hRec ) |
| return ERROR_INVALID_PARAMETER; |
| |
| if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES ) |
| return ERROR_INVALID_PARAMETER; |
| |
| query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return ERROR_INVALID_HANDLE; |
| |
| r = MSI_ViewGetColumnInfo( query, info, &rec ); |
| if ( r == ERROR_SUCCESS ) |
| { |
| *hRec = alloc_msihandle( &rec->hdr ); |
| if ( !*hRec ) |
| r = ERROR_NOT_ENOUGH_MEMORY; |
| msiobj_release( &rec->hdr ); |
| } |
| |
| msiobj_release( &query->hdr ); |
| |
| return r; |
| } |
| |
| UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec ) |
| { |
| MSIVIEW *view = NULL; |
| UINT r; |
| |
| if ( !query || !rec ) |
| return ERROR_INVALID_HANDLE; |
| |
| view = query->view; |
| if ( !view || !view->ops->modify) |
| return ERROR_FUNCTION_FAILED; |
| |
| if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetIntPtr( rec, 0 ) != (INT_PTR)query ) |
| return ERROR_FUNCTION_FAILED; |
| |
| r = view->ops->modify( view, mode, rec, query->row ); |
| if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS) |
| query->row--; |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode, |
| MSIHANDLE hRecord) |
| { |
| MSIQUERY *query = NULL; |
| MSIRECORD *rec = NULL; |
| UINT r = ERROR_FUNCTION_FAILED; |
| |
| TRACE("%d %x %d\n", hView, eModifyMode, hRecord); |
| |
| query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return ERROR_INVALID_HANDLE; |
| |
| rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); |
| r = MSI_ViewModify( query, eModifyMode, rec ); |
| |
| msiobj_release( &query->hdr ); |
| if( rec ) |
| msiobj_release( &rec->hdr ); |
| |
| return r; |
| } |
| |
| MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR buffer, LPDWORD buflen ) |
| { |
| MSIQUERY *query; |
| const WCHAR *column; |
| MSIDBERROR r; |
| DWORD len; |
| |
| TRACE("%u %p %p\n", handle, buffer, buflen); |
| |
| if (!buflen) |
| return MSIDBERROR_INVALIDARG; |
| |
| query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); |
| if( !query ) |
| return MSIDBERROR_INVALIDARG; |
| |
| if ((r = query->view->error)) column = query->view->error_column; |
| else column = szEmpty; |
| |
| len = strlenW( column ); |
| if (buffer) |
| { |
| if (*buflen > len) |
| strcpyW( buffer, column ); |
| else |
| r = MSIDBERROR_MOREDATA; |
| } |
| *buflen = len; |
| msiobj_release( &query->hdr ); |
| return r; |
| } |
| |
| MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR buffer, LPDWORD buflen ) |
| { |
| MSIQUERY *query; |
| const WCHAR *column; |
| MSIDBERROR r; |
| DWORD len; |
| |
| TRACE("%u %p %p\n", handle, buffer, buflen); |
| |
| if (!buflen) |
| return MSIDBERROR_INVALIDARG; |
| |
| query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); |
| if (!query) |
| return MSIDBERROR_INVALIDARG; |
| |
| if ((r = query->view->error)) column = query->view->error_column; |
| else column = szEmpty; |
| |
| len = WideCharToMultiByte( CP_ACP, 0, column, -1, NULL, 0, NULL, NULL ); |
| if (buffer) |
| { |
| if (*buflen >= len) |
| WideCharToMultiByte( CP_ACP, 0, column, -1, buffer, *buflen, NULL, NULL ); |
| else |
| r = MSIDBERROR_MOREDATA; |
| } |
| *buflen = len - 1; |
| msiobj_release( &query->hdr ); |
| return r; |
| } |
| |
| MSIHANDLE WINAPI MsiGetLastErrorRecord( void ) |
| { |
| FIXME("\n"); |
| return 0; |
| } |
| |
| UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, |
| LPCWSTR szTransformFile, int iErrorCond ) |
| { |
| HRESULT r; |
| UINT ret = ERROR_FUNCTION_FAILED; |
| IStorage *stg = NULL; |
| STATSTG stat; |
| |
| TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond); |
| |
| r = StgOpenStorage( szTransformFile, NULL, |
| STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); |
| if ( FAILED(r) ) |
| { |
| WARN("failed to open transform 0x%08x\n", r); |
| return ret; |
| } |
| |
| r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); |
| if ( FAILED( r ) ) |
| goto end; |
| |
| if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) ) |
| goto end; |
| |
| if( TRACE_ON( msi ) ) |
| enum_stream_names( stg ); |
| |
| ret = msi_table_apply_transform( db, stg ); |
| |
| end: |
| IStorage_Release( stg ); |
| |
| return ret; |
| } |
| |
| UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, |
| LPCWSTR szTransformFile, int iErrorCond) |
| { |
| MSIDATABASE *db; |
| UINT r; |
| |
| db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); |
| if( !db ) |
| { |
| IWineMsiRemoteDatabase *remote_database; |
| |
| remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); |
| if ( !remote_database ) |
| return ERROR_INVALID_HANDLE; |
| |
| IWineMsiRemoteDatabase_Release( remote_database ); |
| WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n"); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond ); |
| msiobj_release( &db->hdr ); |
| return r; |
| } |
| |
| UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, |
| LPCSTR szTransformFile, int iErrorCond) |
| { |
| LPWSTR wstr; |
| UINT ret; |
| |
| TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond); |
| |
| wstr = strdupAtoW( szTransformFile ); |
| if( szTransformFile && !wstr ) |
| return ERROR_NOT_ENOUGH_MEMORY; |
| |
| ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond); |
| |
| msi_free( wstr ); |
| |
| return ret; |
| } |
| |
| UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, |
| LPCSTR szTransformFile, int iReserved1, int iReserved2 ) |
| { |
| FIXME("%d %d %s %d %d\n", hdb, hdbref, |
| debugstr_a(szTransformFile), iReserved1, iReserved2); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, |
| LPCWSTR szTransformFile, int iReserved1, int iReserved2 ) |
| { |
| FIXME("%d %d %s %d %d\n", hdb, hdbref, |
| debugstr_w(szTransformFile), iReserved1, iReserved2); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb ) |
| { |
| MSIDATABASE *db; |
| UINT r; |
| |
| TRACE("%d\n", hdb); |
| |
| db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); |
| if( !db ) |
| { |
| IWineMsiRemoteDatabase *remote_database; |
| |
| remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); |
| if ( !remote_database ) |
| return ERROR_INVALID_HANDLE; |
| |
| IWineMsiRemoteDatabase_Release( remote_database ); |
| WARN("not allowed during a custom action!\n"); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| if (db->mode == MSIDBOPEN_READONLY) |
| { |
| msiobj_release( &db->hdr ); |
| return ERROR_SUCCESS; |
| } |
| |
| /* FIXME: lock the database */ |
| |
| r = msi_commit_streams( db ); |
| if (r != ERROR_SUCCESS) ERR("Failed to commit streams!\n"); |
| else |
| { |
| r = MSI_CommitTables( db ); |
| if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n"); |
| } |
| |
| /* FIXME: unlock the database */ |
| |
| msiobj_release( &db->hdr ); |
| |
| if (r == ERROR_SUCCESS) |
| { |
| msi_free( db->deletefile ); |
| db->deletefile = NULL; |
| } |
| |
| return r; |
| } |
| |
| struct msi_primary_key_record_info |
| { |
| DWORD n; |
| MSIRECORD *rec; |
| }; |
| |
| static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param ) |
| { |
| struct msi_primary_key_record_info *info = param; |
| LPCWSTR name, table; |
| DWORD type; |
| |
| type = MSI_RecordGetInteger( rec, 4 ); |
| if( type & MSITYPE_KEY ) |
| { |
| info->n++; |
| if( info->rec ) |
| { |
| if ( info->n == 1 ) |
| { |
| table = MSI_RecordGetString( rec, 1 ); |
| MSI_RecordSetStringW( info->rec, 0, table); |
| } |
| |
| name = MSI_RecordGetString( rec, 3 ); |
| MSI_RecordSetStringW( info->rec, info->n, name ); |
| } |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db, |
| LPCWSTR table, MSIRECORD **prec ) |
| { |
| static const WCHAR sql[] = { |
| 's','e','l','e','c','t',' ','*',' ', |
| 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ', |
| 'w','h','e','r','e',' ', |
| '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 }; |
| struct msi_primary_key_record_info info; |
| MSIQUERY *query = NULL; |
| UINT r; |
| |
| if (!TABLE_Exists( db, table )) |
| return ERROR_INVALID_TABLE; |
| |
| r = MSI_OpenQuery( db, &query, sql, table ); |
| if( r != ERROR_SUCCESS ) |
| return r; |
| |
| /* count the number of primary key records */ |
| info.n = 0; |
| info.rec = 0; |
| r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); |
| if( r == ERROR_SUCCESS ) |
| { |
| TRACE("Found %d primary keys\n", info.n ); |
| |
| /* allocate a record and fill in the names of the tables */ |
| info.rec = MSI_CreateRecord( info.n ); |
| info.n = 0; |
| r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); |
| if( r == ERROR_SUCCESS ) |
| *prec = info.rec; |
| else |
| msiobj_release( &info.rec->hdr ); |
| } |
| msiobj_release( &query->hdr ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb, |
| LPCWSTR table, MSIHANDLE* phRec ) |
| { |
| MSIRECORD *rec = NULL; |
| MSIDATABASE *db; |
| UINT r; |
| |
| TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec); |
| |
| db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); |
| if( !db ) |
| { |
| HRESULT hr; |
| IWineMsiRemoteDatabase *remote_database; |
| |
| remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); |
| if ( !remote_database ) |
| return ERROR_INVALID_HANDLE; |
| |
| hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, table, phRec ); |
| IWineMsiRemoteDatabase_Release( remote_database ); |
| |
| if (FAILED(hr)) |
| { |
| if (HRESULT_FACILITY(hr) == FACILITY_WIN32) |
| return HRESULT_CODE(hr); |
| |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); |
| if( r == ERROR_SUCCESS ) |
| { |
| *phRec = alloc_msihandle( &rec->hdr ); |
| if (! *phRec) |
| r = ERROR_NOT_ENOUGH_MEMORY; |
| msiobj_release( &rec->hdr ); |
| } |
| msiobj_release( &db->hdr ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, |
| LPCSTR table, MSIHANDLE* phRec) |
| { |
| LPWSTR szwTable = NULL; |
| UINT r; |
| |
| TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec); |
| |
| if( table ) |
| { |
| szwTable = strdupAtoW( table ); |
| if( !szwTable ) |
| return ERROR_OUTOFMEMORY; |
| } |
| r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec ); |
| msi_free( szwTable ); |
| |
| return r; |
| } |
| |
| MSICONDITION WINAPI MsiDatabaseIsTablePersistentA( |
| MSIHANDLE hDatabase, LPCSTR szTableName) |
| { |
| LPWSTR szwTableName = NULL; |
| MSICONDITION r; |
| |
| TRACE("%x %s\n", hDatabase, debugstr_a(szTableName)); |
| |
| if( szTableName ) |
| { |
| szwTableName = strdupAtoW( szTableName ); |
| if( !szwTableName ) |
| return MSICONDITION_ERROR; |
| } |
| r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName ); |
| msi_free( szwTableName ); |
| |
| return r; |
| } |
| |
| MSICONDITION WINAPI MsiDatabaseIsTablePersistentW( |
| MSIHANDLE hDatabase, LPCWSTR szTableName) |
| { |
| MSIDATABASE *db; |
| MSICONDITION r; |
| |
| TRACE("%x %s\n", hDatabase, debugstr_w(szTableName)); |
| |
| db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); |
| if( !db ) |
| { |
| HRESULT hr; |
| MSICONDITION condition; |
| IWineMsiRemoteDatabase *remote_database; |
| |
| remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase ); |
| if ( !remote_database ) |
| return MSICONDITION_ERROR; |
| |
| hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database, |
| szTableName, &condition ); |
| IWineMsiRemoteDatabase_Release( remote_database ); |
| |
| if (FAILED(hr)) |
| return MSICONDITION_ERROR; |
| |
| return condition; |
| } |
| |
| r = MSI_DatabaseIsTablePersistent( db, szTableName ); |
| |
| msiobj_release( &db->hdr ); |
| |
| return r; |
| } |