wine/dlls/msi/msiquery.c
Hans Leidekker 0dba391d67 msi: Don't write streams to storage until the database is committed.
Native allows streams to be created with names that exceed the maximum
length allowed by OLE storages. These streams can be used normally, it's
just not possible to commit such a database.
2015-02-13 21:48:21 +09:00

1057 lines
25 KiB
C

/*
* 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 - (1<<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;
}