msi: Fix handling of single quoted column names in SELECT queries.
[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_UNKNOWN)
505         szType[0] = 'f';
506     else if (type & MSITYPE_STRING)
507     {
508         if (temporary)
509             szType[0] = 'g';
510         else
511           szType[0] = 's';
512     }
513     else
514     {
515         if (temporary)
516             szType[0] = 'j';
517         else
518             szType[0] = 'i';
519     }
520
521     if (type & MSITYPE_NULLABLE)
522         szType[0] &= ~0x20;
523
524     sprintfW( &szType[1], fmt, (type&0xff) );
525
526     TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
527
528     return MSI_RecordSetStringW( rec, field, szType );
529 }
530
531 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
532 {
533     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
534     MSIRECORD *rec;
535     MSIVIEW *view = query->view;
536     LPCWSTR name;
537     BOOL temporary;
538
539     if( !view )
540         return ERROR_FUNCTION_FAILED;
541
542     if( !view->ops->get_dimensions )
543         return ERROR_FUNCTION_FAILED;
544
545     r = view->ops->get_dimensions( view, NULL, &count );
546     if( r != ERROR_SUCCESS )
547         return r;
548     if( !count )
549         return ERROR_INVALID_PARAMETER;
550
551     rec = MSI_CreateRecord( count );
552     if( !rec )
553         return ERROR_FUNCTION_FAILED;
554
555     for( i=0; i<count; i++ )
556     {
557         name = NULL;
558         r = view->ops->get_column_info( view, i+1, &name, &type, &temporary, NULL );
559         if( r != ERROR_SUCCESS )
560             continue;
561         if (info == MSICOLINFO_NAMES)
562             MSI_RecordSetStringW( rec, i+1, name );
563         else
564             msi_set_record_type_string( rec, i+1, type, temporary );
565     }
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_RecordGetIntPtr( rec, 0 ) != (INT_PTR)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 buffer, LPDWORD buflen )
648 {
649     MSIQUERY *query;
650     const WCHAR *column;
651     MSIDBERROR r;
652     DWORD len;
653
654     TRACE("%u %p %p\n", handle, buffer, buflen);
655
656     if (!buflen)
657         return MSIDBERROR_INVALIDARG;
658
659     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
660     if( !query )
661         return MSIDBERROR_INVALIDARG;
662
663     if ((r = query->view->error)) column = query->view->error_column;
664     else column = szEmpty;
665
666     len = strlenW( column );
667     if (buffer)
668     {
669         if (*buflen > len)
670             strcpyW( buffer, column );
671         else
672             r = MSIDBERROR_MOREDATA;
673     }
674     *buflen = len;
675     msiobj_release( &query->hdr );
676     return r;
677 }
678
679 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR buffer, LPDWORD buflen )
680 {
681     MSIQUERY *query;
682     const WCHAR *column;
683     MSIDBERROR r;
684     DWORD len;
685
686     TRACE("%u %p %p\n", handle, buffer, buflen);
687
688     if (!buflen)
689         return MSIDBERROR_INVALIDARG;
690
691     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
692     if (!query)
693         return MSIDBERROR_INVALIDARG;
694
695     if ((r = query->view->error)) column = query->view->error_column;
696     else column = szEmpty;
697
698     len = WideCharToMultiByte( CP_ACP, 0, column, -1, NULL, 0, NULL, NULL );
699     if (buffer)
700     {
701         if (*buflen >= len)
702             WideCharToMultiByte( CP_ACP, 0, column, -1, buffer, *buflen, NULL, NULL );
703         else
704             r = MSIDBERROR_MOREDATA;
705     }
706     *buflen = len - 1;
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     {
731         WARN("failed to open transform 0x%08x\n", r);
732         return ret;
733     }
734
735     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
736     if ( FAILED( r ) )
737         goto end;
738
739     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
740         goto end;
741
742     if( TRACE_ON( msi ) )
743         enum_stream_names( stg );
744
745     ret = msi_table_apply_transform( db, stg );
746
747 end:
748     IStorage_Release( stg );
749
750     return ret;
751 }
752
753 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
754                  LPCWSTR szTransformFile, int iErrorCond)
755 {
756     MSIDATABASE *db;
757     UINT r;
758
759     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
760     if( !db )
761     {
762         IWineMsiRemoteDatabase *remote_database;
763
764         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
765         if ( !remote_database )
766             return ERROR_INVALID_HANDLE;
767
768         IWineMsiRemoteDatabase_Release( remote_database );
769         WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
770
771         return ERROR_SUCCESS;
772     }
773
774     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
775     msiobj_release( &db->hdr );
776     return r;
777 }
778
779 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
780                  LPCSTR szTransformFile, int iErrorCond)
781 {
782     LPWSTR wstr;
783     UINT ret;
784
785     TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
786
787     wstr = strdupAtoW( szTransformFile );
788     if( szTransformFile && !wstr )
789         return ERROR_NOT_ENOUGH_MEMORY;
790
791     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
792
793     msi_free( wstr );
794
795     return ret;
796 }
797
798 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
799                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
800 {
801     FIXME("%d %d %s %d %d\n", hdb, hdbref,
802            debugstr_a(szTransformFile), iReserved1, iReserved2);
803     return ERROR_CALL_NOT_IMPLEMENTED;
804 }
805
806 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
807                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
808 {
809     FIXME("%d %d %s %d %d\n", hdb, hdbref,
810            debugstr_w(szTransformFile), iReserved1, iReserved2);
811     return ERROR_CALL_NOT_IMPLEMENTED;
812 }
813
814 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
815 {
816     MSIDATABASE *db;
817     UINT r;
818
819     TRACE("%d\n", hdb);
820
821     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
822     if( !db )
823     {
824         IWineMsiRemoteDatabase *remote_database;
825
826         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
827         if ( !remote_database )
828             return ERROR_INVALID_HANDLE;
829
830         IWineMsiRemoteDatabase_Release( remote_database );
831         WARN("not allowed during a custom action!\n");
832
833         return ERROR_SUCCESS;
834     }
835
836     if (db->mode == MSIDBOPEN_READONLY)
837     {
838         msiobj_release( &db->hdr );
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 }