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