msi/tests: Fix copy-paste errors in tests.
[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_RecordSetIntPtr(*prec, 0, (INT_PTR)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_RecordGetIntPtr( rec, 0 ) != (INT_PTR)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     {
737         WARN("failed to open transform 0x%08x\n", r);
738         return ret;
739     }
740
741     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
742     if ( FAILED( r ) )
743         goto end;
744
745     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
746         goto end;
747
748     if( TRACE_ON( msi ) )
749         enum_stream_names( stg );
750
751     ret = msi_table_apply_transform( db, stg );
752
753 end:
754     IStorage_Release( stg );
755
756     return ret;
757 }
758
759 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
760                  LPCWSTR szTransformFile, int iErrorCond)
761 {
762     MSIDATABASE *db;
763     UINT r;
764
765     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
766     if( !db )
767     {
768         IWineMsiRemoteDatabase *remote_database;
769
770         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
771         if ( !remote_database )
772             return ERROR_INVALID_HANDLE;
773
774         IWineMsiRemoteDatabase_Release( remote_database );
775         WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
776
777         return ERROR_SUCCESS;
778     }
779
780     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
781     msiobj_release( &db->hdr );
782     return r;
783 }
784
785 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
786                  LPCSTR szTransformFile, int iErrorCond)
787 {
788     LPWSTR wstr;
789     UINT ret;
790
791     TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
792
793     wstr = strdupAtoW( szTransformFile );
794     if( szTransformFile && !wstr )
795         return ERROR_NOT_ENOUGH_MEMORY;
796
797     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
798
799     msi_free( wstr );
800
801     return ret;
802 }
803
804 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
805                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
806 {
807     FIXME("%d %d %s %d %d\n", hdb, hdbref,
808            debugstr_a(szTransformFile), iReserved1, iReserved2);
809     return ERROR_CALL_NOT_IMPLEMENTED;
810 }
811
812 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
813                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
814 {
815     FIXME("%d %d %s %d %d\n", hdb, hdbref,
816            debugstr_w(szTransformFile), iReserved1, iReserved2);
817     return ERROR_CALL_NOT_IMPLEMENTED;
818 }
819
820 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
821 {
822     MSIDATABASE *db;
823     UINT r;
824
825     TRACE("%d\n", hdb);
826
827     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
828     if( !db )
829     {
830         IWineMsiRemoteDatabase *remote_database;
831
832         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
833         if ( !remote_database )
834             return ERROR_INVALID_HANDLE;
835
836         IWineMsiRemoteDatabase_Release( remote_database );
837         WARN("not allowed during a custom action!\n");
838
839         return ERROR_SUCCESS;
840     }
841
842     /* FIXME: lock the database */
843
844     r = MSI_CommitTables( db );
845     if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
846
847     /* FIXME: unlock the database */
848
849     msiobj_release( &db->hdr );
850
851     if (r == ERROR_SUCCESS)
852     {
853         msi_free( db->deletefile );
854         db->deletefile = NULL;
855     }
856
857     return r;
858 }
859
860 struct msi_primary_key_record_info
861 {
862     DWORD n;
863     MSIRECORD *rec;
864 };
865
866 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
867 {
868     struct msi_primary_key_record_info *info = param;
869     LPCWSTR name, table;
870     DWORD type;
871
872     type = MSI_RecordGetInteger( rec, 4 );
873     if( type & MSITYPE_KEY )
874     {
875         info->n++;
876         if( info->rec )
877         {
878             if ( info->n == 1 )
879             {
880                 table = MSI_RecordGetString( rec, 1 );
881                 MSI_RecordSetStringW( info->rec, 0, table);
882             }
883
884             name = MSI_RecordGetString( rec, 3 );
885             MSI_RecordSetStringW( info->rec, info->n, name );
886         }
887     }
888
889     return ERROR_SUCCESS;
890 }
891
892 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
893                 LPCWSTR table, MSIRECORD **prec )
894 {
895     static const WCHAR sql[] = {
896         's','e','l','e','c','t',' ','*',' ',
897         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
898         'w','h','e','r','e',' ',
899         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
900     struct msi_primary_key_record_info info;
901     MSIQUERY *query = NULL;
902     UINT r;
903
904     if (!TABLE_Exists( db, table ))
905         return ERROR_INVALID_TABLE;
906
907     r = MSI_OpenQuery( db, &query, sql, table );
908     if( r != ERROR_SUCCESS )
909         return r;
910
911     /* count the number of primary key records */
912     info.n = 0;
913     info.rec = 0;
914     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
915     if( r == ERROR_SUCCESS )
916     {
917         TRACE("Found %d primary keys\n", info.n );
918
919         /* allocate a record and fill in the names of the tables */
920         info.rec = MSI_CreateRecord( info.n );
921         info.n = 0;
922         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
923         if( r == ERROR_SUCCESS )
924             *prec = info.rec;
925         else
926             msiobj_release( &info.rec->hdr );
927     }
928     msiobj_release( &query->hdr );
929
930     return r;
931 }
932
933 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
934                     LPCWSTR table, MSIHANDLE* phRec )
935 {
936     MSIRECORD *rec = NULL;
937     MSIDATABASE *db;
938     UINT r;
939
940     TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec);
941
942     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
943     if( !db )
944     {
945         HRESULT hr;
946         IWineMsiRemoteDatabase *remote_database;
947
948         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
949         if ( !remote_database )
950             return ERROR_INVALID_HANDLE;
951
952         hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, table, phRec );
953         IWineMsiRemoteDatabase_Release( remote_database );
954
955         if (FAILED(hr))
956         {
957             if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
958                 return HRESULT_CODE(hr);
959
960             return ERROR_FUNCTION_FAILED;
961         }
962
963         return ERROR_SUCCESS;
964     }
965
966     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
967     if( r == ERROR_SUCCESS )
968     {
969         *phRec = alloc_msihandle( &rec->hdr );
970         if (! *phRec)
971            r = ERROR_NOT_ENOUGH_MEMORY;
972         msiobj_release( &rec->hdr );
973     }
974     msiobj_release( &db->hdr );
975
976     return r;
977 }
978
979 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
980                     LPCSTR table, MSIHANDLE* phRec)
981 {
982     LPWSTR szwTable = NULL;
983     UINT r;
984
985     TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec);
986
987     if( table )
988     {
989         szwTable = strdupAtoW( table );
990         if( !szwTable )
991             return ERROR_OUTOFMEMORY;
992     }
993     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
994     msi_free( szwTable );
995
996     return r;
997 }
998
999 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
1000               MSIHANDLE hDatabase, LPCSTR szTableName)
1001 {
1002     LPWSTR szwTableName = NULL;
1003     MSICONDITION r;
1004
1005     TRACE("%x %s\n", hDatabase, debugstr_a(szTableName));
1006
1007     if( szTableName )
1008     {
1009         szwTableName = strdupAtoW( szTableName );
1010         if( !szwTableName )
1011             return MSICONDITION_ERROR;
1012     }
1013     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1014     msi_free( szwTableName );
1015
1016     return r;
1017 }
1018
1019 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
1020               MSIHANDLE hDatabase, LPCWSTR szTableName)
1021 {
1022     MSIDATABASE *db;
1023     MSICONDITION r;
1024
1025     TRACE("%x %s\n", hDatabase, debugstr_w(szTableName));
1026
1027     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1028     if( !db )
1029     {
1030         HRESULT hr;
1031         MSICONDITION condition;
1032         IWineMsiRemoteDatabase *remote_database;
1033
1034         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
1035         if ( !remote_database )
1036             return MSICONDITION_ERROR;
1037
1038         hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database,
1039                                                        szTableName, &condition );
1040         IWineMsiRemoteDatabase_Release( remote_database );
1041
1042         if (FAILED(hr))
1043             return MSICONDITION_ERROR;
1044
1045         return condition;
1046     }
1047
1048     r = MSI_DatabaseIsTablePersistent( db, szTableName );
1049
1050     msiobj_release( &db->hdr );
1051
1052     return r;
1053 }