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