msi: Improve parsing of the package platform.
[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     LPCWSTR col_name, haystack_table_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         r = table->ops->get_column_info( table, i, &col_name, NULL,
73                                          NULL, &haystack_table_name );
74         if( r != ERROR_SUCCESS )
75             return r;
76         x = strcmpW( name, col_name );
77         if( table_name )
78             x |= strcmpW( table_name, haystack_table_name );
79         if( !x )
80         {
81             *n = i;
82             return ERROR_SUCCESS;
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, 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, 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_RecordSetIntPtr(*prec, 0, (INT_PTR)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     LPCWSTR 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, NULL );
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     }
564     *prec = rec;
565     return ERROR_SUCCESS;
566 }
567
568 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
569 {
570     MSIQUERY *query = NULL;
571     MSIRECORD *rec = NULL;
572     UINT r;
573
574     TRACE("%d %d %p\n", hView, info, hRec);
575
576     if( !hRec )
577         return ERROR_INVALID_PARAMETER;
578
579     if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
580         return ERROR_INVALID_PARAMETER;
581
582     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
583     if( !query )
584         return ERROR_INVALID_HANDLE;
585
586     r = MSI_ViewGetColumnInfo( query, info, &rec );
587     if ( r == ERROR_SUCCESS )
588     {
589         *hRec = alloc_msihandle( &rec->hdr );
590         if ( !*hRec )
591             r = ERROR_NOT_ENOUGH_MEMORY;
592         msiobj_release( &rec->hdr );
593     }
594
595     msiobj_release( &query->hdr );
596
597     return r;
598 }
599
600 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
601 {
602     MSIVIEW *view = NULL;
603     UINT r;
604
605     if ( !query || !rec )
606         return ERROR_INVALID_HANDLE;
607
608     view = query->view;
609     if ( !view  || !view->ops->modify)
610         return ERROR_FUNCTION_FAILED;
611
612     if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetIntPtr( rec, 0 ) != (INT_PTR)query )
613         return ERROR_FUNCTION_FAILED;
614
615     r = view->ops->modify( view, mode, rec, query->row );
616     if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS)
617         query->row--;
618
619     return r;
620 }
621
622 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
623                 MSIHANDLE hRecord)
624 {
625     MSIQUERY *query = NULL;
626     MSIRECORD *rec = NULL;
627     UINT r = ERROR_FUNCTION_FAILED;
628
629     TRACE("%d %x %d\n", hView, eModifyMode, hRecord);
630
631     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
632     if( !query )
633         return ERROR_INVALID_HANDLE;
634
635     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
636     r = MSI_ViewModify( query, eModifyMode, rec );
637
638     msiobj_release( &query->hdr );
639     if( rec )
640         msiobj_release( &rec->hdr );
641
642     return r;
643 }
644
645 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR buffer, LPDWORD buflen )
646 {
647     MSIQUERY *query;
648     const WCHAR *column;
649     MSIDBERROR r;
650     DWORD len;
651
652     TRACE("%u %p %p\n", handle, buffer, buflen);
653
654     if (!buflen)
655         return MSIDBERROR_INVALIDARG;
656
657     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
658     if( !query )
659         return MSIDBERROR_INVALIDARG;
660
661     if ((r = query->view->error)) column = query->view->error_column;
662     else column = szEmpty;
663
664     len = strlenW( column );
665     if (buffer)
666     {
667         if (*buflen > len)
668             strcpyW( buffer, column );
669         else
670             r = MSIDBERROR_MOREDATA;
671     }
672     *buflen = len;
673     msiobj_release( &query->hdr );
674     return r;
675 }
676
677 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR buffer, LPDWORD buflen )
678 {
679     MSIQUERY *query;
680     const WCHAR *column;
681     MSIDBERROR r;
682     DWORD len;
683
684     TRACE("%u %p %p\n", handle, buffer, buflen);
685
686     if (!buflen)
687         return MSIDBERROR_INVALIDARG;
688
689     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
690     if (!query)
691         return MSIDBERROR_INVALIDARG;
692
693     if ((r = query->view->error)) column = query->view->error_column;
694     else column = szEmpty;
695
696     len = WideCharToMultiByte( CP_ACP, 0, column, -1, NULL, 0, NULL, NULL );
697     if (buffer)
698     {
699         if (*buflen >= len)
700             WideCharToMultiByte( CP_ACP, 0, column, -1, buffer, *buflen, NULL, NULL );
701         else
702             r = MSIDBERROR_MOREDATA;
703     }
704     *buflen = len - 1;
705     msiobj_release( &query->hdr );
706     return r;
707 }
708
709 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
710 {
711     FIXME("\n");
712     return 0;
713 }
714
715 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
716                  LPCWSTR szTransformFile, int iErrorCond )
717 {
718     HRESULT r;
719     UINT ret = ERROR_FUNCTION_FAILED;
720     IStorage *stg = NULL;
721     STATSTG stat;
722
723     TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
724
725     r = StgOpenStorage( szTransformFile, NULL,
726            STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
727     if ( FAILED(r) )
728     {
729         WARN("failed to open transform 0x%08x\n", r);
730         return ret;
731     }
732
733     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
734     if ( FAILED( r ) )
735         goto end;
736
737     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
738         goto end;
739
740     if( TRACE_ON( msi ) )
741         enum_stream_names( stg );
742
743     ret = msi_table_apply_transform( db, stg );
744
745 end:
746     IStorage_Release( stg );
747
748     return ret;
749 }
750
751 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
752                  LPCWSTR szTransformFile, int iErrorCond)
753 {
754     MSIDATABASE *db;
755     UINT r;
756
757     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
758     if( !db )
759     {
760         IWineMsiRemoteDatabase *remote_database;
761
762         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
763         if ( !remote_database )
764             return ERROR_INVALID_HANDLE;
765
766         IWineMsiRemoteDatabase_Release( remote_database );
767         WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
768
769         return ERROR_SUCCESS;
770     }
771
772     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
773     msiobj_release( &db->hdr );
774     return r;
775 }
776
777 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
778                  LPCSTR szTransformFile, int iErrorCond)
779 {
780     LPWSTR wstr;
781     UINT ret;
782
783     TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
784
785     wstr = strdupAtoW( szTransformFile );
786     if( szTransformFile && !wstr )
787         return ERROR_NOT_ENOUGH_MEMORY;
788
789     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
790
791     msi_free( wstr );
792
793     return ret;
794 }
795
796 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
797                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
798 {
799     FIXME("%d %d %s %d %d\n", hdb, hdbref,
800            debugstr_a(szTransformFile), iReserved1, iReserved2);
801     return ERROR_CALL_NOT_IMPLEMENTED;
802 }
803
804 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
805                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
806 {
807     FIXME("%d %d %s %d %d\n", hdb, hdbref,
808            debugstr_w(szTransformFile), iReserved1, iReserved2);
809     return ERROR_CALL_NOT_IMPLEMENTED;
810 }
811
812 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
813 {
814     MSIDATABASE *db;
815     UINT r;
816
817     TRACE("%d\n", hdb);
818
819     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
820     if( !db )
821     {
822         IWineMsiRemoteDatabase *remote_database;
823
824         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
825         if ( !remote_database )
826             return ERROR_INVALID_HANDLE;
827
828         IWineMsiRemoteDatabase_Release( remote_database );
829         WARN("not allowed during a custom action!\n");
830
831         return ERROR_SUCCESS;
832     }
833
834     if (db->mode == MSIDBOPEN_READONLY)
835     {
836         msiobj_release( &db->hdr );
837         return ERROR_SUCCESS;
838     }
839
840     /* FIXME: lock the database */
841
842     r = MSI_CommitTables( db );
843     if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
844
845     /* FIXME: unlock the database */
846
847     msiobj_release( &db->hdr );
848
849     if (r == ERROR_SUCCESS)
850     {
851         msi_free( db->deletefile );
852         db->deletefile = NULL;
853     }
854
855     return r;
856 }
857
858 struct msi_primary_key_record_info
859 {
860     DWORD n;
861     MSIRECORD *rec;
862 };
863
864 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
865 {
866     struct msi_primary_key_record_info *info = param;
867     LPCWSTR name, table;
868     DWORD type;
869
870     type = MSI_RecordGetInteger( rec, 4 );
871     if( type & MSITYPE_KEY )
872     {
873         info->n++;
874         if( info->rec )
875         {
876             if ( info->n == 1 )
877             {
878                 table = MSI_RecordGetString( rec, 1 );
879                 MSI_RecordSetStringW( info->rec, 0, table);
880             }
881
882             name = MSI_RecordGetString( rec, 3 );
883             MSI_RecordSetStringW( info->rec, info->n, name );
884         }
885     }
886
887     return ERROR_SUCCESS;
888 }
889
890 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
891                 LPCWSTR table, MSIRECORD **prec )
892 {
893     static const WCHAR sql[] = {
894         's','e','l','e','c','t',' ','*',' ',
895         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
896         'w','h','e','r','e',' ',
897         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
898     struct msi_primary_key_record_info info;
899     MSIQUERY *query = NULL;
900     UINT r;
901
902     if (!TABLE_Exists( db, table ))
903         return ERROR_INVALID_TABLE;
904
905     r = MSI_OpenQuery( db, &query, sql, table );
906     if( r != ERROR_SUCCESS )
907         return r;
908
909     /* count the number of primary key records */
910     info.n = 0;
911     info.rec = 0;
912     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
913     if( r == ERROR_SUCCESS )
914     {
915         TRACE("Found %d primary keys\n", info.n );
916
917         /* allocate a record and fill in the names of the tables */
918         info.rec = MSI_CreateRecord( info.n );
919         info.n = 0;
920         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
921         if( r == ERROR_SUCCESS )
922             *prec = info.rec;
923         else
924             msiobj_release( &info.rec->hdr );
925     }
926     msiobj_release( &query->hdr );
927
928     return r;
929 }
930
931 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
932                     LPCWSTR table, MSIHANDLE* phRec )
933 {
934     MSIRECORD *rec = NULL;
935     MSIDATABASE *db;
936     UINT r;
937
938     TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec);
939
940     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
941     if( !db )
942     {
943         HRESULT hr;
944         IWineMsiRemoteDatabase *remote_database;
945
946         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
947         if ( !remote_database )
948             return ERROR_INVALID_HANDLE;
949
950         hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, table, phRec );
951         IWineMsiRemoteDatabase_Release( remote_database );
952
953         if (FAILED(hr))
954         {
955             if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
956                 return HRESULT_CODE(hr);
957
958             return ERROR_FUNCTION_FAILED;
959         }
960
961         return ERROR_SUCCESS;
962     }
963
964     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
965     if( r == ERROR_SUCCESS )
966     {
967         *phRec = alloc_msihandle( &rec->hdr );
968         if (! *phRec)
969            r = ERROR_NOT_ENOUGH_MEMORY;
970         msiobj_release( &rec->hdr );
971     }
972     msiobj_release( &db->hdr );
973
974     return r;
975 }
976
977 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
978                     LPCSTR table, MSIHANDLE* phRec)
979 {
980     LPWSTR szwTable = NULL;
981     UINT r;
982
983     TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec);
984
985     if( table )
986     {
987         szwTable = strdupAtoW( table );
988         if( !szwTable )
989             return ERROR_OUTOFMEMORY;
990     }
991     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
992     msi_free( szwTable );
993
994     return r;
995 }
996
997 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
998               MSIHANDLE hDatabase, LPCSTR szTableName)
999 {
1000     LPWSTR szwTableName = NULL;
1001     MSICONDITION r;
1002
1003     TRACE("%x %s\n", hDatabase, debugstr_a(szTableName));
1004
1005     if( szTableName )
1006     {
1007         szwTableName = strdupAtoW( szTableName );
1008         if( !szwTableName )
1009             return MSICONDITION_ERROR;
1010     }
1011     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1012     msi_free( szwTableName );
1013
1014     return r;
1015 }
1016
1017 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
1018               MSIHANDLE hDatabase, LPCWSTR szTableName)
1019 {
1020     MSIDATABASE *db;
1021     MSICONDITION r;
1022
1023     TRACE("%x %s\n", hDatabase, debugstr_w(szTableName));
1024
1025     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1026     if( !db )
1027     {
1028         HRESULT hr;
1029         MSICONDITION condition;
1030         IWineMsiRemoteDatabase *remote_database;
1031
1032         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
1033         if ( !remote_database )
1034             return MSICONDITION_ERROR;
1035
1036         hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database,
1037                                                        szTableName, &condition );
1038         IWineMsiRemoteDatabase_Release( remote_database );
1039
1040         if (FAILED(hr))
1041             return MSICONDITION_ERROR;
1042
1043         return condition;
1044     }
1045
1046     r = MSI_DatabaseIsTablePersistent( db, szTableName );
1047
1048     msiobj_release( &db->hdr );
1049
1050     return r;
1051 }