set svn:eol-style to native
[reactos.git] / reactos / lib / msi / msiquery.c
index cdd4457..f833847 100644 (file)
-/*\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;
+}