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