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