msi: Add more tests for MsiGetSourcePath.
[wine] / dlls / msi / msiquery.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2005 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "msipriv.h"
35 #include "winnls.h"
36
37 #include "query.h"
38 #include "msiserver.h"
39
40 #include "initguid.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44 static void MSI_CloseView( MSIOBJECTHDR *arg )
45 {
46     MSIQUERY *query = (MSIQUERY*) arg;
47     struct list *ptr, *t;
48
49     if( query->view && query->view->ops->delete )
50         query->view->ops->delete( query->view );
51     msiobj_release( &query->db->hdr );
52
53     LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
54     {
55         msi_free( ptr );
56     }
57 }
58
59 UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
60 {
61     LPWSTR col_name;
62     UINT i, count, r;
63
64     r = table->ops->get_dimensions( table, NULL, &count );
65     if( r != ERROR_SUCCESS )
66         return r;
67
68     for( i=1; i<=count; i++ )
69     {
70         INT x;
71
72         col_name = NULL;
73         r = table->ops->get_column_info( table, i, &col_name, NULL, NULL );
74         if( r != ERROR_SUCCESS )
75             return r;
76         x = lstrcmpW( name, col_name );
77         msi_free( col_name );
78         if( !x )
79         {
80             *n = i;
81             return ERROR_SUCCESS;
82         }
83     }
84
85     return ERROR_INVALID_PARAMETER;
86 }
87
88 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
89               LPCSTR szQuery, MSIHANDLE *phView)
90 {
91     UINT r;
92     LPWSTR szwQuery;
93
94     TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView);
95
96     if( szQuery )
97     {
98         szwQuery = strdupAtoW( szQuery );
99         if( !szwQuery )
100             return ERROR_FUNCTION_FAILED;
101     }
102     else
103         szwQuery = NULL;
104
105     r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
106
107     msi_free( szwQuery );
108     return r;
109 }
110
111 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
112               LPCWSTR szQuery, MSIQUERY **pView)
113 {
114     MSIQUERY *query;
115     UINT r;
116
117     TRACE("%s %p\n", debugstr_w(szQuery), pView);
118
119     if( !szQuery)
120         return ERROR_INVALID_PARAMETER;
121
122     /* pre allocate a handle to hold a pointer to the view */
123     query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
124                               MSI_CloseView );
125     if( !query )
126         return ERROR_FUNCTION_FAILED;
127
128     msiobj_addref( &db->hdr );
129     query->row = 0;
130     query->db = db;
131     query->view = NULL;
132     list_init( &query->mem );
133
134     r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
135     if( r == ERROR_SUCCESS )
136     {
137         msiobj_addref( &query->hdr );
138         *pView = query;
139     }
140
141     msiobj_release( &query->hdr );
142     return r;
143 }
144
145 UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
146 {
147     UINT r;
148     int size = 100, res;
149     LPWSTR query;
150
151     /* construct the string */
152     for (;;)
153     {
154         va_list va;
155         query = msi_alloc( size*sizeof(WCHAR) );
156         va_start(va, fmt);
157         res = vsnprintfW(query, size, fmt, va);
158         va_end(va);
159         if (res == -1) size *= 2;
160         else if (res >= size) size = res + 1;
161         else break;
162         msi_free( query );
163     }
164     /* perform the query */
165     r = MSI_DatabaseOpenViewW(db, query, view);
166     msi_free(query);
167     return r;
168 }
169
170 UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count,
171                          record_func func, LPVOID param )
172 {
173     MSIRECORD *rec = NULL;
174     UINT r, n = 0, max = 0;
175
176     r = MSI_ViewExecute( view, NULL );
177     if( r != ERROR_SUCCESS )
178         return r;
179
180     if( count )
181         max = *count;
182
183     /* iterate a query */
184     for( n = 0; (max == 0) || (n < max); n++ )
185     {
186         r = MSI_ViewFetch( view, &rec );
187         if( r != ERROR_SUCCESS )
188             break;
189         if (func)
190             r = func( rec, param );
191         msiobj_release( &rec->hdr );
192         if( r != ERROR_SUCCESS )
193             break;
194     }
195
196     MSI_ViewClose( view );
197
198     if( count )
199         *count = n;
200
201     if( r == ERROR_NO_MORE_ITEMS )
202         r = ERROR_SUCCESS;
203
204     return r;
205 }
206
207 /* return a single record from a query */
208 MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
209 {
210     MSIRECORD *rec = NULL;
211     MSIQUERY *view = NULL;
212     UINT r;
213     int size = 100, res;
214     LPWSTR query;
215
216     /* construct the string */
217     for (;;)
218     {
219         va_list va;
220         query = msi_alloc( size*sizeof(WCHAR) );
221         va_start(va, fmt);
222         res = vsnprintfW(query, size, fmt, va);
223         va_end(va);
224         if (res == -1) size *= 2;
225         else if (res >= size) size = res + 1;
226         else break;
227         msi_free( query );
228     }
229     /* perform the query */
230     r = MSI_DatabaseOpenViewW(db, query, &view);
231     msi_free(query);
232
233     if( r == ERROR_SUCCESS )
234     {
235         MSI_ViewExecute( view, NULL );
236         MSI_ViewFetch( view, &rec );
237         MSI_ViewClose( view );
238         msiobj_release( &view->hdr );
239     }
240     return rec;
241 }
242
243 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
244               LPCWSTR szQuery, MSIHANDLE *phView)
245 {
246     MSIDATABASE *db;
247     MSIQUERY *query = NULL;
248     UINT ret;
249
250     TRACE("%s %p\n", debugstr_w(szQuery), phView);
251
252     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
253     if( !db )
254     {
255         HRESULT hr;
256         IWineMsiRemoteDatabase *remote_database;
257
258         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
259         if ( !remote_database )
260             return ERROR_INVALID_HANDLE;
261
262         hr = IWineMsiRemoteDatabase_OpenView( remote_database, (BSTR)szQuery, phView );
263         IWineMsiRemoteDatabase_Release( remote_database );
264
265         if (FAILED(hr))
266         {
267             if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
268                 return HRESULT_CODE(hr);
269
270             return ERROR_FUNCTION_FAILED;
271         }
272
273         return ERROR_SUCCESS;
274     }
275
276     ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
277     if( ret == ERROR_SUCCESS )
278     {
279         *phView = alloc_msihandle( &query->hdr );
280         if (! *phView)
281            ret = ERROR_NOT_ENOUGH_MEMORY;
282         msiobj_release( &query->hdr );
283     }
284     msiobj_release( &db->hdr );
285
286     return ret;
287 }
288
289 UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec)
290 {
291     UINT row_count = 0, col_count = 0, i, ival, ret, type;
292
293     TRACE("%p %p %d %p\n", db, view, row, rec);
294
295     ret = view->ops->get_dimensions(view, &row_count, &col_count);
296     if (ret)
297         return ret;
298
299     if (!col_count)
300         return ERROR_INVALID_PARAMETER;
301
302     if (row >= row_count)
303         return ERROR_NO_MORE_ITEMS;
304
305     *rec = MSI_CreateRecord(col_count);
306     if (!*rec)
307         return ERROR_FUNCTION_FAILED;
308
309     for (i = 1; i <= col_count; i++)
310     {
311         ret = view->ops->get_column_info(view, i, NULL, &type, NULL);
312         if (ret)
313         {
314             ERR("Error getting column type for %d\n", i);
315             continue;
316         }
317
318         if (MSITYPE_IS_BINARY(type))
319         {
320             IStream *stm = NULL;
321
322             ret = view->ops->fetch_stream(view, row, i, &stm);
323             if ((ret == ERROR_SUCCESS) && stm)
324             {
325                 MSI_RecordSetIStream(*rec, i, stm);
326                 IStream_Release(stm);
327             }
328             else
329                 WARN("failed to get stream\n");
330
331             continue;
332         }
333
334         ret = view->ops->fetch_int(view, row, i, &ival);
335         if (ret)
336         {
337             ERR("Error fetching data for %d\n", i);
338             continue;
339         }
340
341         if (! (type & MSITYPE_VALID))
342             ERR("Invalid type!\n");
343
344         /* check if it's nul (0) - if so, don't set anything */
345         if (!ival)
346             continue;
347
348         if (type & MSITYPE_STRING)
349         {
350             LPCWSTR sval;
351
352             sval = msi_string_lookup_id(db->strings, ival);
353             MSI_RecordSetStringW(*rec, i, sval);
354         }
355         else
356         {
357             if ((type & MSI_DATASIZEMASK) == 2)
358                 MSI_RecordSetInteger(*rec, i, ival - (1<<15));
359             else
360                 MSI_RecordSetInteger(*rec, i, ival - (1<<31));
361         }
362     }
363
364     return ERROR_SUCCESS;
365 }
366
367 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
368 {
369     MSIVIEW *view;
370     UINT r;
371
372     TRACE("%p %p\n", query, prec );
373
374     view = query->view;
375     if( !view )
376         return ERROR_FUNCTION_FAILED;
377
378     r = msi_view_get_row(query->db, view, query->row, prec);
379     if (r == ERROR_SUCCESS)
380     {
381         query->row ++;
382         MSI_RecordSetInteger(*prec, 0, (int)query);
383     }
384
385     return r;
386 }
387
388 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
389 {
390     MSIQUERY *query;
391     MSIRECORD *rec = NULL;
392     UINT ret;
393
394     TRACE("%d %p\n", hView, record);
395
396     if( !record )
397         return ERROR_INVALID_PARAMETER;
398     *record = 0;
399
400     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
401     if( !query )
402         return ERROR_INVALID_HANDLE;
403     ret = MSI_ViewFetch( query, &rec );
404     if( ret == ERROR_SUCCESS )
405     {
406         *record = alloc_msihandle( &rec->hdr );
407         if (! *record)
408            ret = ERROR_NOT_ENOUGH_MEMORY;
409         msiobj_release( &rec->hdr );
410     }
411     msiobj_release( &query->hdr );
412     return ret;
413 }
414
415 UINT MSI_ViewClose(MSIQUERY *query)
416 {
417     MSIVIEW *view;
418
419     TRACE("%p\n", query );
420
421     view = query->view;
422     if( !view )
423         return ERROR_FUNCTION_FAILED;
424     if( !view->ops->close )
425         return ERROR_FUNCTION_FAILED;
426
427     return view->ops->close( view );
428 }
429
430 UINT WINAPI MsiViewClose(MSIHANDLE hView)
431 {
432     MSIQUERY *query;
433     UINT ret;
434
435     TRACE("%d\n", hView );
436
437     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
438     if( !query )
439         return ERROR_INVALID_HANDLE;
440
441     ret = MSI_ViewClose( query );
442     msiobj_release( &query->hdr );
443     return ret;
444 }
445
446 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
447 {
448     MSIVIEW *view;
449
450     TRACE("%p %p\n", query, rec);
451
452     view = query->view;
453     if( !view )
454         return ERROR_FUNCTION_FAILED;
455     if( !view->ops->execute )
456         return ERROR_FUNCTION_FAILED;
457     query->row = 0;
458
459     return view->ops->execute( view, rec );
460 }
461
462 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
463 {
464     MSIQUERY *query;
465     MSIRECORD *rec = NULL;
466     UINT ret;
467     
468     TRACE("%d %d\n", hView, hRec);
469
470     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
471     if( !query )
472         return ERROR_INVALID_HANDLE;
473
474     if( hRec )
475     {
476         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
477         if( !rec )
478         {
479             ret = ERROR_INVALID_HANDLE;
480             goto out;
481         }
482     }
483
484     msiobj_lock( &rec->hdr );
485     ret = MSI_ViewExecute( query, rec );
486     msiobj_unlock( &rec->hdr );
487
488 out:
489     msiobj_release( &query->hdr );
490     if( rec )
491         msiobj_release( &rec->hdr );
492
493     return ret;
494 }
495
496 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field,
497                                         UINT type, BOOL temporary )
498 {
499     static const WCHAR fmt[] = { '%','d',0 };
500     WCHAR szType[0x10];
501
502     if (MSITYPE_IS_BINARY(type))
503         szType[0] = 'v';
504     else if (type & MSITYPE_LOCALIZABLE)
505         szType[0] = 'l';
506     else if (type & MSITYPE_STRING)
507     {
508         if (temporary)
509             szType[0] = 'g';
510         else
511           szType[0] = 's';
512     }
513     else
514     {
515         if (temporary)
516             szType[0] = 'j';
517         else
518             szType[0] = 'i';
519     }
520
521     if (type & MSITYPE_NULLABLE)
522         szType[0] &= ~0x20;
523
524     sprintfW( &szType[1], fmt, (type&0xff) );
525
526     TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
527
528     return MSI_RecordSetStringW( rec, field, szType );
529 }
530
531 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
532 {
533     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
534     MSIRECORD *rec;
535     MSIVIEW *view = query->view;
536     LPWSTR name;
537     BOOL temporary;
538
539     if( !view )
540         return ERROR_FUNCTION_FAILED;
541
542     if( !view->ops->get_dimensions )
543         return ERROR_FUNCTION_FAILED;
544
545     r = view->ops->get_dimensions( view, NULL, &count );
546     if( r != ERROR_SUCCESS )
547         return r;
548     if( !count )
549         return ERROR_INVALID_PARAMETER;
550
551     rec = MSI_CreateRecord( count );
552     if( !rec )
553         return ERROR_FUNCTION_FAILED;
554
555     for( i=0; i<count; i++ )
556     {
557         name = NULL;
558         r = view->ops->get_column_info( view, i+1, &name, &type, &temporary );
559         if( r != ERROR_SUCCESS )
560             continue;
561         if (info == MSICOLINFO_NAMES)
562             MSI_RecordSetStringW( rec, i+1, name );
563         else
564             msi_set_record_type_string( rec, i+1, type, temporary );
565         msi_free( name );
566     }
567
568     *prec = rec;
569     return ERROR_SUCCESS;
570 }
571
572 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
573 {
574     MSIQUERY *query = NULL;
575     MSIRECORD *rec = NULL;
576     UINT r;
577
578     TRACE("%d %d %p\n", hView, info, hRec);
579
580     if( !hRec )
581         return ERROR_INVALID_PARAMETER;
582
583     if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
584         return ERROR_INVALID_PARAMETER;
585
586     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
587     if( !query )
588         return ERROR_INVALID_HANDLE;
589
590     r = MSI_ViewGetColumnInfo( query, info, &rec );
591     if ( r == ERROR_SUCCESS )
592     {
593         *hRec = alloc_msihandle( &rec->hdr );
594         if ( !*hRec )
595             r = ERROR_NOT_ENOUGH_MEMORY;
596         msiobj_release( &rec->hdr );
597     }
598
599     msiobj_release( &query->hdr );
600
601     return r;
602 }
603
604 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
605 {
606     MSIVIEW *view = NULL;
607     UINT r;
608
609     if ( !query || !rec )
610         return ERROR_INVALID_HANDLE;
611
612     view = query->view;
613     if ( !view  || !view->ops->modify)
614         return ERROR_FUNCTION_FAILED;
615
616     if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetInteger( rec, 0 ) != (int)query )
617         return ERROR_FUNCTION_FAILED;
618
619     r = view->ops->modify( view, mode, rec, query->row );
620     if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS)
621         query->row--;
622
623     return r;
624 }
625
626 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
627                 MSIHANDLE hRecord)
628 {
629     MSIQUERY *query = NULL;
630     MSIRECORD *rec = NULL;
631     UINT r = ERROR_FUNCTION_FAILED;
632
633     TRACE("%d %x %d\n", hView, eModifyMode, hRecord);
634
635     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
636     if( !query )
637         return ERROR_INVALID_HANDLE;
638
639     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
640     r = MSI_ViewModify( query, eModifyMode, rec );
641
642     msiobj_release( &query->hdr );
643     if( rec )
644         msiobj_release( &rec->hdr );
645
646     return r;
647 }
648
649 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
650                               LPDWORD pcchBuf )
651 {
652     MSIQUERY *query = NULL;
653     static const WCHAR szError[] = { 0 };
654     MSIDBERROR r = MSIDBERROR_NOERROR;
655     DWORD len;
656
657     FIXME("%d %p %p - returns empty error string\n",
658           handle, szColumnNameBuffer, pcchBuf );
659
660     if( !pcchBuf )
661         return MSIDBERROR_INVALIDARG;
662
663     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
664     if( !query )
665         return MSIDBERROR_INVALIDARG;
666
667     len = strlenW( szError );
668     if( szColumnNameBuffer )
669     {
670         if( *pcchBuf > len )
671             lstrcpyW( szColumnNameBuffer, szError );
672         else
673             r = MSIDBERROR_MOREDATA;
674     }
675     *pcchBuf = len;
676
677     msiobj_release( &query->hdr );
678     return r;
679 }
680
681 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
682                               LPDWORD pcchBuf )
683 {
684     static const CHAR szError[] = { 0 };
685     MSIQUERY *query = NULL;
686     MSIDBERROR r = MSIDBERROR_NOERROR;
687     DWORD len;
688
689     FIXME("%d %p %p - returns empty error string\n",
690           handle, szColumnNameBuffer, pcchBuf );
691
692     if( !pcchBuf )
693         return MSIDBERROR_INVALIDARG;
694
695     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
696     if( !query )
697         return MSIDBERROR_INVALIDARG;
698
699     len = strlen( szError );
700     if( szColumnNameBuffer )
701     {
702         if( *pcchBuf > len )
703             lstrcpyA( szColumnNameBuffer, szError );
704         else
705             r = MSIDBERROR_MOREDATA;
706     }
707     *pcchBuf = len;
708
709     msiobj_release( &query->hdr );
710     return r;
711 }
712
713 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
714 {
715     FIXME("\n");
716     return 0;
717 }
718
719 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
720
721 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
722                  LPCWSTR szTransformFile, int iErrorCond )
723 {
724     HRESULT r;
725     UINT ret = ERROR_FUNCTION_FAILED;
726     IStorage *stg = NULL;
727     STATSTG stat;
728
729     TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
730
731     r = StgOpenStorage( szTransformFile, NULL,
732            STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
733     if ( FAILED(r) )
734         return ret;
735
736     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
737     if ( FAILED( r ) )
738         goto end;
739
740     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
741         goto end;
742
743     if( TRACE_ON( msi ) )
744         enum_stream_names( stg );
745
746     ret = msi_table_apply_transform( db, stg );
747
748 end:
749     IStorage_Release( stg );
750
751     return ret;
752 }
753
754 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
755                  LPCWSTR szTransformFile, int iErrorCond)
756 {
757     MSIDATABASE *db;
758     UINT r;
759
760     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
761     if( !db )
762     {
763         IWineMsiRemoteDatabase *remote_database;
764
765         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
766         if ( !remote_database )
767             return ERROR_INVALID_HANDLE;
768
769         IWineMsiRemoteDatabase_Release( remote_database );
770         WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
771
772         return ERROR_SUCCESS;
773     }
774
775     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
776     msiobj_release( &db->hdr );
777     return r;
778 }
779
780 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
781                  LPCSTR szTransformFile, int iErrorCond)
782 {
783     LPWSTR wstr;
784     UINT ret;
785
786     TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
787
788     wstr = strdupAtoW( szTransformFile );
789     if( szTransformFile && !wstr )
790         return ERROR_NOT_ENOUGH_MEMORY;
791
792     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
793
794     msi_free( wstr );
795
796     return ret;
797 }
798
799 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
800                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
801 {
802     FIXME("%d %d %s %d %d\n", hdb, hdbref,
803            debugstr_a(szTransformFile), iReserved1, iReserved2);
804     return ERROR_CALL_NOT_IMPLEMENTED;
805 }
806
807 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
808                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
809 {
810     FIXME("%d %d %s %d %d\n", hdb, hdbref,
811            debugstr_w(szTransformFile), iReserved1, iReserved2);
812     return ERROR_CALL_NOT_IMPLEMENTED;
813 }
814
815 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
816 {
817     MSIDATABASE *db;
818     UINT r;
819
820     TRACE("%d\n", hdb);
821
822     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
823     if( !db )
824     {
825         IWineMsiRemoteDatabase *remote_database;
826
827         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
828         if ( !remote_database )
829             return ERROR_INVALID_HANDLE;
830
831         IWineMsiRemoteDatabase_Release( remote_database );
832         WARN("MsiDatabaseCommit not allowed during a custom action!\n");
833
834         return ERROR_SUCCESS;
835     }
836
837     /* FIXME: lock the database */
838
839     r = MSI_CommitTables( db );
840     if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
841
842     /* FIXME: unlock the database */
843
844     msiobj_release( &db->hdr );
845
846     if (r == ERROR_SUCCESS)
847     {
848         msi_free( db->deletefile );
849         db->deletefile = NULL;
850     }
851
852     return r;
853 }
854
855 struct msi_primary_key_record_info
856 {
857     DWORD n;
858     MSIRECORD *rec;
859 };
860
861 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
862 {
863     struct msi_primary_key_record_info *info = param;
864     LPCWSTR name, table;
865     DWORD type;
866
867     type = MSI_RecordGetInteger( rec, 4 );
868     if( type & MSITYPE_KEY )
869     {
870         info->n++;
871         if( info->rec )
872         {
873             if ( info->n == 1 )
874             {
875                 table = MSI_RecordGetString( rec, 1 );
876                 MSI_RecordSetStringW( info->rec, 0, table);
877             }
878
879             name = MSI_RecordGetString( rec, 3 );
880             MSI_RecordSetStringW( info->rec, info->n, name );
881         }
882     }
883
884     return ERROR_SUCCESS;
885 }
886
887 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
888                 LPCWSTR table, MSIRECORD **prec )
889 {
890     static const WCHAR sql[] = {
891         's','e','l','e','c','t',' ','*',' ',
892         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
893         'w','h','e','r','e',' ',
894         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
895     struct msi_primary_key_record_info info;
896     MSIQUERY *query = NULL;
897     UINT r;
898
899     r = MSI_OpenQuery( db, &query, sql, table );
900     if( r != ERROR_SUCCESS )
901         return r;
902
903     /* count the number of primary key records */
904     info.n = 0;
905     info.rec = 0;
906     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
907     if( r == ERROR_SUCCESS )
908     {
909         TRACE("Found %d primary keys\n", info.n );
910
911         /* allocate a record and fill in the names of the tables */
912         info.rec = MSI_CreateRecord( info.n );
913         info.n = 0;
914         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
915         if( r == ERROR_SUCCESS )
916             *prec = info.rec;
917         else
918             msiobj_release( &info.rec->hdr );
919     }
920     msiobj_release( &query->hdr );
921
922     return r;
923 }
924
925 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
926                     LPCWSTR table, MSIHANDLE* phRec )
927 {
928     MSIRECORD *rec = NULL;
929     MSIDATABASE *db;
930     UINT r;
931
932     TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec);
933
934     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
935     if( !db )
936     {
937         HRESULT hr;
938         IWineMsiRemoteDatabase *remote_database;
939
940         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
941         if ( !remote_database )
942             return ERROR_INVALID_HANDLE;
943
944         hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, (BSTR)table, phRec );
945         IWineMsiRemoteDatabase_Release( remote_database );
946
947         if (FAILED(hr))
948         {
949             if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
950                 return HRESULT_CODE(hr);
951
952             return ERROR_FUNCTION_FAILED;
953         }
954
955         return ERROR_SUCCESS;
956     }
957
958     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
959     if( r == ERROR_SUCCESS )
960     {
961         *phRec = alloc_msihandle( &rec->hdr );
962         if (! *phRec)
963            r = ERROR_NOT_ENOUGH_MEMORY;
964         msiobj_release( &rec->hdr );
965     }
966     msiobj_release( &db->hdr );
967
968     return r;
969 }
970
971 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
972                     LPCSTR table, MSIHANDLE* phRec)
973 {
974     LPWSTR szwTable = NULL;
975     UINT r;
976
977     TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec);
978
979     if( table )
980     {
981         szwTable = strdupAtoW( table );
982         if( !szwTable )
983             return ERROR_OUTOFMEMORY;
984     }
985     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
986     msi_free( szwTable );
987
988     return r;
989 }
990
991 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
992               MSIHANDLE hDatabase, LPCSTR szTableName)
993 {
994     LPWSTR szwTableName = NULL;
995     MSICONDITION r;
996
997     TRACE("%x %s\n", hDatabase, debugstr_a(szTableName));
998
999     if( szTableName )
1000     {
1001         szwTableName = strdupAtoW( szTableName );
1002         if( !szwTableName )
1003             return MSICONDITION_ERROR;
1004     }
1005     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1006     msi_free( szwTableName );
1007
1008     return r;
1009 }
1010
1011 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
1012               MSIHANDLE hDatabase, LPCWSTR szTableName)
1013 {
1014     MSIDATABASE *db;
1015     MSICONDITION r;
1016
1017     TRACE("%x %s\n", hDatabase, debugstr_w(szTableName));
1018
1019     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1020     if( !db )
1021     {
1022         HRESULT hr;
1023         MSICONDITION condition;
1024         IWineMsiRemoteDatabase *remote_database;
1025
1026         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
1027         if ( !remote_database )
1028             return MSICONDITION_ERROR;
1029
1030         hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database,
1031                                                        (BSTR)szTableName, &condition );
1032         IWineMsiRemoteDatabase_Release( remote_database );
1033
1034         if (FAILED(hr))
1035             return MSICONDITION_ERROR;
1036
1037         return condition;
1038     }
1039
1040     r = MSI_DatabaseIsTablePersistent( db, szTableName );
1041
1042     msiobj_release( &db->hdr );
1043
1044     return r;
1045 }