-/*\r
- * Implementation of the Microsoft Installer (msi.dll)\r
- *\r
- * Copyright 2002-2005 Mike McCormack for CodeWeavers\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-#include <stdarg.h>\r
-\r
-#define COBJMACROS\r
-\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "winerror.h"\r
-#include "wine/debug.h"\r
-#include "wine/unicode.h"\r
-#include "msi.h"\r
-#include "msiquery.h"\r
-#include "objbase.h"\r
-#include "objidl.h"\r
-#include "msipriv.h"\r
-#include "winnls.h"\r
-\r
-#include "query.h"\r
-\r
-WINE_DEFAULT_DEBUG_CHANNEL(msi);\r
-\r
-void MSI_CloseView( MSIOBJECTHDR *arg )\r
-{\r
- MSIQUERY *query = (MSIQUERY*) arg;\r
- struct list *ptr, *t;\r
-\r
- if( query->view && query->view->ops->delete )\r
- query->view->ops->delete( query->view );\r
- msiobj_release( &query->db->hdr );\r
-\r
- LIST_FOR_EACH_SAFE( ptr, t, &query->mem )\r
- {\r
- HeapFree( GetProcessHeap(), 0, ptr );\r
- }\r
-}\r
-\r
-UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )\r
-{\r
- LPWSTR col_name;\r
- UINT i, count, r;\r
-\r
- r = table->ops->get_dimensions( table, NULL, &count );\r
- if( r != ERROR_SUCCESS )\r
- return r;\r
-\r
- for( i=1; i<=count; i++ )\r
- {\r
- INT x;\r
-\r
- col_name = NULL;\r
- r = table->ops->get_column_info( table, i, &col_name, NULL );\r
- if( r != ERROR_SUCCESS )\r
- return r;\r
- x = lstrcmpW( name, col_name );\r
- HeapFree( GetProcessHeap(), 0, col_name );\r
- if( !x )\r
- {\r
- *n = i;\r
- return ERROR_SUCCESS;\r
- }\r
- }\r
-\r
- return ERROR_INVALID_PARAMETER;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,\r
- LPCSTR szQuery, MSIHANDLE *phView)\r
-{\r
- UINT r;\r
- LPWSTR szwQuery;\r
-\r
- TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);\r
-\r
- if( szQuery )\r
- {\r
- szwQuery = strdupAtoW( szQuery );\r
- if( !szwQuery )\r
- return ERROR_FUNCTION_FAILED;\r
- }\r
- else\r
- szwQuery = NULL;\r
-\r
- r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);\r
-\r
- HeapFree( GetProcessHeap(), 0, szwQuery );\r
- return r;\r
-}\r
-\r
-UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,\r
- LPCWSTR szQuery, MSIQUERY **pView)\r
-{\r
- MSIQUERY *query;\r
- UINT r;\r
-\r
- TRACE("%s %p\n", debugstr_w(szQuery), pView);\r
-\r
- if( !szQuery)\r
- return ERROR_INVALID_PARAMETER;\r
-\r
- /* pre allocate a handle to hold a pointer to the view */\r
- query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),\r
- MSI_CloseView );\r
- if( !query )\r
- return ERROR_FUNCTION_FAILED;\r
-\r
- msiobj_addref( &db->hdr );\r
- query->row = 0;\r
- query->db = db;\r
- query->view = NULL;\r
- list_init( &query->mem );\r
-\r
- r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );\r
- if( r == ERROR_SUCCESS )\r
- {\r
- msiobj_addref( &query->hdr );\r
- *pView = query;\r
- }\r
-\r
- msiobj_release( &query->hdr );\r
- return r;\r
-}\r
-\r
-UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )\r
-{\r
- LPWSTR szQuery;\r
- LPCWSTR p;\r
- UINT sz, rc;\r
- va_list va;\r
-\r
- /* figure out how much space we need to allocate */\r
- va_start(va, fmt);\r
- sz = lstrlenW(fmt) + 1;\r
- p = fmt;\r
- while (*p)\r
- {\r
- p = strchrW(p, '%');\r
- if (!p)\r
- break;\r
- p++;\r
- switch (*p)\r
- {\r
- case 's': /* a string */\r
- sz += lstrlenW(va_arg(va,LPCWSTR));\r
- break;\r
- case 'd':\r
- case 'i': /* an integer -2147483648 seems to be longest */\r
- sz += 3*sizeof(int);\r
- (void)va_arg(va,int);\r
- break;\r
- case '%': /* a single % - leave it alone */\r
- break;\r
- default:\r
- FIXME("Unhandled character type %c\n",*p);\r
- }\r
- p++;\r
- }\r
- va_end(va);\r
-\r
- /* construct the string */\r
- szQuery = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));\r
- va_start(va, fmt);\r
- vsnprintfW(szQuery, sz, fmt, va);\r
- va_end(va);\r
-\r
- /* perform the query */\r
- rc = MSI_DatabaseOpenViewW(db, szQuery, view);\r
- HeapFree(GetProcessHeap(), 0, szQuery);\r
- return rc;\r
-}\r
-\r
-UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,\r
- record_func func, LPVOID param )\r
-{\r
- MSIRECORD *rec = NULL;\r
- UINT r, n = 0, max = 0;\r
-\r
- r = MSI_ViewExecute( view, NULL );\r
- if( r != ERROR_SUCCESS )\r
- return r;\r
-\r
- if( count )\r
- max = *count;\r
-\r
- /* iterate a query */\r
- for( n = 0; (max == 0) || (n < max); n++ )\r
- {\r
- r = MSI_ViewFetch( view, &rec );\r
- if( r != ERROR_SUCCESS )\r
- break;\r
- r = func( rec, param );\r
- msiobj_release( &rec->hdr );\r
- if( r != ERROR_SUCCESS )\r
- break;\r
- }\r
-\r
- MSI_ViewClose( view );\r
-\r
- if( count )\r
- *count = n;\r
-\r
- if( r == ERROR_NO_MORE_ITEMS )\r
- r = ERROR_SUCCESS;\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,\r
- LPCWSTR szQuery, MSIHANDLE *phView)\r
-{\r
- MSIDATABASE *db;\r
- MSIQUERY *query = NULL;\r
- UINT ret;\r
-\r
- TRACE("%s %p\n", debugstr_w(szQuery), phView);\r
-\r
- db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );\r
- if( !db )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- ret = MSI_DatabaseOpenViewW( db, szQuery, &query );\r
- if( ret == ERROR_SUCCESS )\r
- {\r
- *phView = alloc_msihandle( &query->hdr );\r
- msiobj_release( &query->hdr );\r
- }\r
- msiobj_release( &db->hdr );\r
-\r
- return ret;\r
-}\r
-\r
-UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)\r
-{\r
- MSIVIEW *view;\r
- MSIRECORD *rec;\r
- UINT row_count = 0, col_count = 0, i, ival, ret, type;\r
-\r
- TRACE("%p %p\n", query, prec );\r
-\r
- view = query->view;\r
- if( !view )\r
- return ERROR_FUNCTION_FAILED;\r
-\r
- ret = view->ops->get_dimensions( view, &row_count, &col_count );\r
- if( ret )\r
- return ret;\r
- if( !col_count )\r
- return ERROR_INVALID_PARAMETER;\r
-\r
- if( query->row >= row_count )\r
- return ERROR_NO_MORE_ITEMS;\r
-\r
- rec = MSI_CreateRecord( col_count );\r
- if( !rec )\r
- return ERROR_FUNCTION_FAILED;\r
-\r
- for( i=1; i<=col_count; i++ )\r
- {\r
- ret = view->ops->get_column_info( view, i, NULL, &type );\r
- if( ret )\r
- {\r
- ERR("Error getting column type for %d\n", i );\r
- continue;\r
- }\r
- if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |\r
- MSITYPE_NULLABLE)))\r
- {\r
- ret = view->ops->fetch_int( view, query->row, i, &ival );\r
- if( ret )\r
- {\r
- ERR("Error fetching data for %d\n", i );\r
- continue;\r
- }\r
- if( ! (type & MSITYPE_VALID ) )\r
- ERR("Invalid type!\n");\r
-\r
- /* check if it's nul (0) - if so, don't set anything */\r
- if( !ival )\r
- continue;\r
-\r
- if( type & MSITYPE_STRING )\r
- {\r
- LPWSTR sval;\r
-\r
- sval = MSI_makestring( query->db, ival );\r
- MSI_RecordSetStringW( rec, i, sval );\r
- HeapFree( GetProcessHeap(), 0, sval );\r
- }\r
- else\r
- {\r
- if( (type & MSI_DATASIZEMASK) == 2 )\r
- MSI_RecordSetInteger( rec, i, ival - (1<<15) );\r
- else\r
- MSI_RecordSetInteger( rec, i, ival - (1<<31) );\r
- }\r
- }\r
- else\r
- {\r
- IStream *stm = NULL;\r
-\r
- ret = view->ops->fetch_stream( view, query->row, i, &stm );\r
- if( ( ret == ERROR_SUCCESS ) && stm )\r
- {\r
- MSI_RecordSetIStream( rec, i, stm );\r
- IStream_Release( stm );\r
- }\r
- else\r
- ERR("failed to get stream\n");\r
- }\r
- }\r
- query->row ++;\r
-\r
- *prec = rec;\r
-\r
- return ERROR_SUCCESS;\r
-}\r
-\r
-UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)\r
-{\r
- MSIQUERY *query;\r
- MSIRECORD *rec = NULL;\r
- UINT ret;\r
-\r
- TRACE("%ld %p\n", hView, record);\r
-\r
- query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );\r
- if( !query )\r
- return ERROR_INVALID_HANDLE;\r
- ret = MSI_ViewFetch( query, &rec );\r
- if( ret == ERROR_SUCCESS )\r
- {\r
- *record = alloc_msihandle( &rec->hdr );\r
- msiobj_release( &rec->hdr );\r
- }\r
- msiobj_release( &query->hdr );\r
- return ret;\r
-}\r
-\r
-UINT MSI_ViewClose(MSIQUERY *query)\r
-{\r
- MSIVIEW *view;\r
-\r
- TRACE("%p\n", query );\r
-\r
- view = query->view;\r
- if( !view )\r
- return ERROR_FUNCTION_FAILED;\r
- if( !view->ops->close )\r
- return ERROR_FUNCTION_FAILED;\r
-\r
- return view->ops->close( view );\r
-}\r
-\r
-UINT WINAPI MsiViewClose(MSIHANDLE hView)\r
-{\r
- MSIQUERY *query;\r
- UINT ret;\r
-\r
- TRACE("%ld\n", hView );\r
-\r
- query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );\r
- if( !query )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- ret = MSI_ViewClose( query );\r
- msiobj_release( &query->hdr );\r
- return ret;\r
-}\r
-\r
-UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )\r
-{\r
- MSIVIEW *view;\r
-\r
- TRACE("%p %p\n", query, rec);\r
-\r
- view = query->view;\r
- if( !view )\r
- return ERROR_FUNCTION_FAILED;\r
- if( !view->ops->execute )\r
- return ERROR_FUNCTION_FAILED;\r
- query->row = 0;\r
-\r
- return view->ops->execute( view, rec );\r
-}\r
-\r
-UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)\r
-{\r
- MSIQUERY *query;\r
- MSIRECORD *rec = NULL;\r
- UINT ret;\r
- \r
- TRACE("%ld %ld\n", hView, hRec);\r
-\r
- query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );\r
- if( !query )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- if( hRec )\r
- {\r
- rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );\r
- if( !rec )\r
- {\r
- ret = ERROR_INVALID_HANDLE;\r
- goto out;\r
- }\r
- }\r
-\r
- msiobj_lock( &rec->hdr );\r
- ret = MSI_ViewExecute( query, rec );\r
- msiobj_unlock( &rec->hdr );\r
-\r
-out:\r
- msiobj_release( &query->hdr );\r
- if( rec )\r
- msiobj_release( &rec->hdr );\r
-\r
- return ret;\r
-}\r
-\r
-UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)\r
-{\r
- MSIVIEW *view = NULL;\r
- MSIQUERY *query = NULL;\r
- MSIRECORD *rec = NULL;\r
- UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;\r
- LPWSTR name;\r
-\r
- TRACE("%ld %d %p\n", hView, info, hRec);\r
-\r
- query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );\r
- if( !query )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- view = query->view;\r
- if( !view )\r
- goto out;\r
-\r
- if( !view->ops->get_dimensions )\r
- goto out;\r
-\r
- r = view->ops->get_dimensions( view, NULL, &count );\r
- if( r )\r
- goto out;\r
- if( !count )\r
- {\r
- r = ERROR_INVALID_PARAMETER;\r
- goto out;\r
- }\r
-\r
- rec = MSI_CreateRecord( count );\r
- if( !rec )\r
- {\r
- r = ERROR_FUNCTION_FAILED;\r
- goto out;\r
- }\r
-\r
- for( i=0; i<count; i++ )\r
- {\r
- name = NULL;\r
- r = view->ops->get_column_info( view, i+1, &name, &type );\r
- if( r != ERROR_SUCCESS )\r
- continue;\r
- MSI_RecordSetStringW( rec, i+1, name );\r
- HeapFree( GetProcessHeap(), 0, name );\r
- }\r
-\r
- *hRec = alloc_msihandle( &rec->hdr );\r
-\r
-out:\r
- msiobj_release( &query->hdr );\r
- if( rec )\r
- msiobj_release( &rec->hdr );\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,\r
- MSIHANDLE hRecord)\r
-{\r
- MSIVIEW *view = NULL;\r
- MSIQUERY *query = NULL;\r
- MSIRECORD *rec = NULL;\r
- UINT r = ERROR_FUNCTION_FAILED;\r
-\r
- TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);\r
-\r
- query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );\r
- if( !query )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- view = query->view;\r
- if( !view )\r
- goto out;\r
-\r
- if( !view->ops->modify )\r
- goto out;\r
-\r
- rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );\r
- if( !rec )\r
- {\r
- r = ERROR_INVALID_HANDLE;\r
- goto out;\r
- }\r
-\r
- r = view->ops->modify( view, eModifyMode, rec );\r
-\r
-out:\r
- msiobj_release( &query->hdr );\r
- if( rec )\r
- msiobj_release( &rec->hdr );\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, \r
- LPCSTR szTransformFile, int iErrorCond)\r
-{\r
- FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, \r
- LPCWSTR szTransformFile, int iErrorCond)\r
-{\r
- FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,\r
- LPCSTR szTransformFile, int iReserved1, int iReserved2 )\r
-{\r
- FIXME("%ld %ld %s %d %d\n", hdb, hdbref, \r
- debugstr_a(szTransformFile), iReserved1, iReserved2);\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,\r
- LPCWSTR szTransformFile, int iReserved1, int iReserved2 )\r
-{\r
- FIXME("%ld %ld %s %d %d\n", hdb, hdbref, \r
- debugstr_w(szTransformFile), iReserved1, iReserved2);\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )\r
-{\r
- MSIDATABASE *db;\r
- UINT r;\r
-\r
- TRACE("%ld\n", hdb);\r
-\r
- db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );\r
- if( !db )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- /* FIXME: lock the database */\r
-\r
- r = MSI_CommitTables( db );\r
-\r
- /* FIXME: unlock the database */\r
-\r
- msiobj_release( &db->hdr );\r
-\r
- return r;\r
-}\r
-\r
-struct msi_primary_key_record_info\r
-{\r
- DWORD n;\r
- MSIRECORD *rec;\r
-};\r
-\r
-static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )\r
-{\r
- struct msi_primary_key_record_info *info = param;\r
- LPCWSTR name;\r
- DWORD type;\r
-\r
- type = MSI_RecordGetInteger( rec, 4 );\r
- if( type & MSITYPE_KEY )\r
- {\r
- info->n++;\r
- if( info->rec )\r
- {\r
- name = MSI_RecordGetString( rec, 3 );\r
- MSI_RecordSetStringW( info->rec, info->n, name );\r
- }\r
- }\r
-\r
- return ERROR_SUCCESS;\r
-}\r
-\r
-UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,\r
- LPCWSTR table, MSIRECORD **prec )\r
-{\r
- static const WCHAR sql[] = {\r
- 's','e','l','e','c','t',' ','*',' ',\r
- 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',\r
- 'w','h','e','r','e',' ',\r
- '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };\r
- struct msi_primary_key_record_info info;\r
- MSIQUERY *query = NULL;\r
- MSIVIEW *view;\r
- UINT r;\r
- \r
- r = MSI_OpenQuery( db, &query, sql, table );\r
- if( r != ERROR_SUCCESS )\r
- return r;\r
-\r
- view = query->view;\r
-\r
- /* count the number of primary key records */\r
- info.n = 0;\r
- info.rec = 0;\r
- r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );\r
- if( r == ERROR_SUCCESS )\r
- {\r
- TRACE("Found %ld primary keys\n", info.n );\r
-\r
- /* allocate a record and fill in the names of the tables */\r
- info.rec = MSI_CreateRecord( info.n );\r
- info.n = 0;\r
- r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );\r
- if( r == ERROR_SUCCESS )\r
- *prec = info.rec;\r
- else\r
- msiobj_release( &info.rec->hdr );\r
- }\r
- msiobj_release( &query->hdr );\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,\r
- LPCWSTR table, MSIHANDLE* phRec )\r
-{\r
- MSIRECORD *rec = NULL;\r
- MSIDATABASE *db;\r
- UINT r;\r
-\r
- TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);\r
-\r
- db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );\r
- if( !db )\r
- return ERROR_INVALID_HANDLE;\r
-\r
- r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );\r
- if( r == ERROR_SUCCESS )\r
- {\r
- *phRec = alloc_msihandle( &rec->hdr );\r
- msiobj_release( &rec->hdr );\r
- }\r
- msiobj_release( &db->hdr );\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, \r
- LPCSTR table, MSIHANDLE* phRec)\r
-{\r
- LPWSTR szwTable = NULL;\r
- UINT r;\r
-\r
- TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);\r
-\r
- if( table )\r
- {\r
- szwTable = strdupAtoW( table );\r
- if( !szwTable )\r
- return ERROR_OUTOFMEMORY;\r
- }\r
- r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );\r
- HeapFree( GetProcessHeap(), 0, szwTable );\r
-\r
- return r;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseIsTablePersistentA(\r
- MSIHANDLE hDatabase, LPSTR szTableName)\r
-{\r
- FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
-\r
-UINT WINAPI MsiDatabaseIsTablePersistentW(\r
- MSIHANDLE hDatabase, LPWSTR szTableName)\r
-{\r
- FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));\r
- return ERROR_CALL_NOT_IMPLEMENTED;\r
-}\r
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+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 )
+ {
+ HeapFree( GetProcessHeap(), 0, ptr );
+ }
+}
+
+UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
+{
+ LPWSTR col_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;
+
+ col_name = NULL;
+ r = table->ops->get_column_info( table, i, &col_name, NULL );
+ if( r != ERROR_SUCCESS )
+ return r;
+ x = lstrcmpW( name, col_name );
+ HeapFree( GetProcessHeap(), 0, col_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("%ld %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);
+
+ HeapFree( GetProcessHeap(), 0, 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->row = 0;
+ query->db = db;
+ query->view = NULL;
+ 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, ... )
+{
+ LPWSTR szQuery;
+ LPCWSTR p;
+ UINT sz, rc;
+ va_list va;
+
+ /* figure out how much space we need to allocate */
+ va_start(va, fmt);
+ sz = lstrlenW(fmt) + 1;
+ p = fmt;
+ while (*p)
+ {
+ p = strchrW(p, '%');
+ if (!p)
+ break;
+ p++;
+ switch (*p)
+ {
+ case 's': /* a string */
+ sz += lstrlenW(va_arg(va,LPCWSTR));
+ break;
+ case 'd':
+ case 'i': /* an integer -2147483648 seems to be longest */
+ sz += 3*sizeof(int);
+ (void)va_arg(va,int);
+ break;
+ case '%': /* a single % - leave it alone */
+ break;
+ default:
+ FIXME("Unhandled character type %c\n",*p);
+ }
+ p++;
+ }
+ va_end(va);
+
+ /* construct the string */
+ szQuery = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
+ va_start(va, fmt);
+ vsnprintfW(szQuery, sz, fmt, va);
+ va_end(va);
+
+ /* perform the query */
+ rc = MSI_DatabaseOpenViewW(db, szQuery, view);
+ HeapFree(GetProcessHeap(), 0, szQuery);
+ return rc;
+}
+
+UINT MSI_IterateRecords( MSIQUERY *view, DWORD *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;
+ 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;
+}
+
+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 )
+ return ERROR_INVALID_HANDLE;
+
+ ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
+ if( ret == ERROR_SUCCESS )
+ {
+ *phView = alloc_msihandle( &query->hdr );
+ msiobj_release( &query->hdr );
+ }
+ msiobj_release( &db->hdr );
+
+ return ret;
+}
+
+UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
+{
+ MSIVIEW *view;
+ MSIRECORD *rec;
+ UINT row_count = 0, col_count = 0, i, ival, ret, type;
+
+ TRACE("%p %p\n", query, prec );
+
+ view = query->view;
+ if( !view )
+ return ERROR_FUNCTION_FAILED;
+
+ ret = view->ops->get_dimensions( view, &row_count, &col_count );
+ if( ret )
+ return ret;
+ if( !col_count )
+ return ERROR_INVALID_PARAMETER;
+
+ if( query->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 );
+ if( ret )
+ {
+ ERR("Error getting column type for %d\n", i );
+ continue;
+ }
+ if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
+ MSITYPE_NULLABLE)))
+ {
+ ret = view->ops->fetch_int( view, query->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 )
+ {
+ LPWSTR sval;
+
+ sval = MSI_makestring( query->db, ival );
+ MSI_RecordSetStringW( rec, i, sval );
+ HeapFree( GetProcessHeap(), 0, sval );
+ }
+ else
+ {
+ if( (type & MSI_DATASIZEMASK) == 2 )
+ MSI_RecordSetInteger( rec, i, ival - (1<<15) );
+ else
+ MSI_RecordSetInteger( rec, i, ival - (1<<31) );
+ }
+ }
+ else
+ {
+ IStream *stm = NULL;
+
+ ret = view->ops->fetch_stream( view, query->row, i, &stm );
+ if( ( ret == ERROR_SUCCESS ) && stm )
+ {
+ MSI_RecordSetIStream( rec, i, stm );
+ IStream_Release( stm );
+ }
+ else
+ ERR("failed to get stream\n");
+ }
+ }
+ query->row ++;
+
+ *prec = rec;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
+{
+ MSIQUERY *query;
+ MSIRECORD *rec = NULL;
+ UINT ret;
+
+ TRACE("%ld %p\n", hView, record);
+
+ 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 );
+ 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("%ld\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("%ld %ld\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;
+}
+
+UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
+{
+ MSIVIEW *view = NULL;
+ MSIQUERY *query = NULL;
+ MSIRECORD *rec = NULL;
+ UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
+ LPWSTR name;
+
+ TRACE("%ld %d %p\n", hView, info, hRec);
+
+ query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+ if( !query )
+ return ERROR_INVALID_HANDLE;
+
+ view = query->view;
+ if( !view )
+ goto out;
+
+ if( !view->ops->get_dimensions )
+ goto out;
+
+ r = view->ops->get_dimensions( view, NULL, &count );
+ if( r )
+ goto out;
+ if( !count )
+ {
+ r = ERROR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ rec = MSI_CreateRecord( count );
+ if( !rec )
+ {
+ r = ERROR_FUNCTION_FAILED;
+ goto out;
+ }
+
+ for( i=0; i<count; i++ )
+ {
+ name = NULL;
+ r = view->ops->get_column_info( view, i+1, &name, &type );
+ if( r != ERROR_SUCCESS )
+ continue;
+ MSI_RecordSetStringW( rec, i+1, name );
+ HeapFree( GetProcessHeap(), 0, name );
+ }
+
+ *hRec = alloc_msihandle( &rec->hdr );
+
+out:
+ msiobj_release( &query->hdr );
+ if( rec )
+ msiobj_release( &rec->hdr );
+
+ return r;
+}
+
+UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
+ MSIHANDLE hRecord)
+{
+ MSIVIEW *view = NULL;
+ MSIQUERY *query = NULL;
+ MSIRECORD *rec = NULL;
+ UINT r = ERROR_FUNCTION_FAILED;
+
+ TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
+
+ query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+ if( !query )
+ return ERROR_INVALID_HANDLE;
+
+ view = query->view;
+ if( !view )
+ goto out;
+
+ if( !view->ops->modify )
+ goto out;
+
+ rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ {
+ r = ERROR_INVALID_HANDLE;
+ goto out;
+ }
+
+ r = view->ops->modify( view, eModifyMode, rec );
+
+out:
+ msiobj_release( &query->hdr );
+ if( rec )
+ msiobj_release( &rec->hdr );
+
+ return r;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
+ LPCSTR szTransformFile, int iErrorCond)
+{
+ FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
+ LPCWSTR szTransformFile, int iErrorCond)
+{
+ FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
+ LPCSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+ FIXME("%ld %ld %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("%ld %ld %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("%ld\n", hdb);
+
+ db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+ if( !db )
+ return ERROR_INVALID_HANDLE;
+
+ /* FIXME: lock the database */
+
+ r = MSI_CommitTables( db );
+
+ /* FIXME: unlock the database */
+
+ msiobj_release( &db->hdr );
+
+ 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;
+ DWORD type;
+
+ type = MSI_RecordGetInteger( rec, 4 );
+ if( type & MSITYPE_KEY )
+ {
+ info->n++;
+ if( info->rec )
+ {
+ 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;
+ MSIVIEW *view;
+ UINT r;
+
+ r = MSI_OpenQuery( db, &query, sql, table );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ view = query->view;
+
+ /* 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 %ld 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("%ld %s %p\n", hdb, debugstr_w(table), phRec);
+
+ db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+ if( !db )
+ return ERROR_INVALID_HANDLE;
+
+ r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
+ if( r == ERROR_SUCCESS )
+ {
+ *phRec = alloc_msihandle( &rec->hdr );
+ 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("%ld %s %p\n", hdb, debugstr_a(table), phRec);
+
+ if( table )
+ {
+ szwTable = strdupAtoW( table );
+ if( !szwTable )
+ return ERROR_OUTOFMEMORY;
+ }
+ r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
+ HeapFree( GetProcessHeap(), 0, szwTable );
+
+ return r;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentA(
+ MSIHANDLE hDatabase, LPSTR szTableName)
+{
+ FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentW(
+ MSIHANDLE hDatabase, LPWSTR szTableName)
+{
+ FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}