janitorial: Remove remaining NULL checks before free() (found by Smatch).
[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                 LPCWSTR sval;
394
395                 sval = msi_string_lookup_id( query->db->strings, ival );
396                 MSI_RecordSetStringW( rec, i, sval );
397             }
398             else
399             {
400                 if( (type & MSI_DATASIZEMASK) == 2 )
401                     MSI_RecordSetInteger( rec, i, ival - (1<<15) );
402                 else
403                     MSI_RecordSetInteger( rec, i, ival - (1<<31) );
404             }
405         }
406         else
407         {
408             IStream *stm = NULL;
409
410             ret = view->ops->fetch_stream( view, query->row, i, &stm );
411             if( ( ret == ERROR_SUCCESS ) && stm )
412             {
413                 MSI_RecordSetIStream( rec, i, stm );
414                 IStream_Release( stm );
415             }
416             else
417                 ERR("failed to get stream\n");
418         }
419     }
420     query->row ++;
421
422     *prec = rec;
423
424     return ERROR_SUCCESS;
425 }
426
427 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
428 {
429     MSIQUERY *query;
430     MSIRECORD *rec = NULL;
431     UINT ret;
432
433     TRACE("%ld %p\n", hView, record);
434
435     if( !record )
436         return ERROR_INVALID_PARAMETER;
437     *record = 0;
438
439     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
440     if( !query )
441         return ERROR_INVALID_HANDLE;
442     ret = MSI_ViewFetch( query, &rec );
443     if( ret == ERROR_SUCCESS )
444     {
445         *record = alloc_msihandle( &rec->hdr );
446         if (! *record)
447            ret = ERROR_NOT_ENOUGH_MEMORY;
448         msiobj_release( &rec->hdr );
449     }
450     msiobj_release( &query->hdr );
451     return ret;
452 }
453
454 UINT MSI_ViewClose(MSIQUERY *query)
455 {
456     MSIVIEW *view;
457
458     TRACE("%p\n", query );
459
460     view = query->view;
461     if( !view )
462         return ERROR_FUNCTION_FAILED;
463     if( !view->ops->close )
464         return ERROR_FUNCTION_FAILED;
465
466     return view->ops->close( view );
467 }
468
469 UINT WINAPI MsiViewClose(MSIHANDLE hView)
470 {
471     MSIQUERY *query;
472     UINT ret;
473
474     TRACE("%ld\n", hView );
475
476     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
477     if( !query )
478         return ERROR_INVALID_HANDLE;
479
480     ret = MSI_ViewClose( query );
481     msiobj_release( &query->hdr );
482     return ret;
483 }
484
485 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
486 {
487     MSIVIEW *view;
488
489     TRACE("%p %p\n", query, rec);
490
491     view = query->view;
492     if( !view )
493         return ERROR_FUNCTION_FAILED;
494     if( !view->ops->execute )
495         return ERROR_FUNCTION_FAILED;
496     query->row = 0;
497
498     return view->ops->execute( view, rec );
499 }
500
501 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
502 {
503     MSIQUERY *query;
504     MSIRECORD *rec = NULL;
505     UINT ret;
506     
507     TRACE("%ld %ld\n", hView, hRec);
508
509     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
510     if( !query )
511         return ERROR_INVALID_HANDLE;
512
513     if( hRec )
514     {
515         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
516         if( !rec )
517         {
518             ret = ERROR_INVALID_HANDLE;
519             goto out;
520         }
521     }
522
523     msiobj_lock( &rec->hdr );
524     ret = MSI_ViewExecute( query, rec );
525     msiobj_unlock( &rec->hdr );
526
527 out:
528     msiobj_release( &query->hdr );
529     if( rec )
530         msiobj_release( &rec->hdr );
531
532     return ret;
533 }
534
535 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
536 {
537     static const WCHAR fmt[] = { '%','d',0 };
538     WCHAR szType[0x10];
539
540     if (MSITYPE_IS_BINARY(type))
541         szType[0] = 'v';
542     else if (type & MSITYPE_LOCALIZABLE)
543         szType[0] = 'l';
544     else if (type & MSITYPE_STRING)
545         szType[0] = 's';
546     else
547         szType[0] = 'i';
548     if (type & MSITYPE_NULLABLE)
549         szType[0] &= ~0x20;
550
551     sprintfW( &szType[1], fmt, (type&0xff) );
552
553     TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
554
555     return MSI_RecordSetStringW( rec, field, szType );
556 }
557
558 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
559 {
560     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
561     MSIRECORD *rec;
562     MSIVIEW *view = query->view;
563     LPWSTR name;
564
565     if( !view )
566         return ERROR_FUNCTION_FAILED;
567
568     if( !view->ops->get_dimensions )
569         return ERROR_FUNCTION_FAILED;
570
571     r = view->ops->get_dimensions( view, NULL, &count );
572     if( r != ERROR_SUCCESS )
573         return r;
574     if( !count )
575         return ERROR_INVALID_PARAMETER;
576
577     rec = MSI_CreateRecord( count );
578     if( !rec )
579         return ERROR_FUNCTION_FAILED;
580
581     for( i=0; i<count; i++ )
582     {
583         name = NULL;
584         r = view->ops->get_column_info( view, i+1, &name, &type );
585         if( r != ERROR_SUCCESS )
586             continue;
587         if (info == MSICOLINFO_NAMES)
588             MSI_RecordSetStringW( rec, i+1, name );
589         else
590             msi_set_record_type_string( rec, i+1, type);
591         msi_free( name );
592     }
593
594     *prec = rec;
595     return ERROR_SUCCESS;
596 }
597
598 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
599 {
600     MSIQUERY *query = NULL;
601     MSIRECORD *rec = NULL;
602     UINT r;
603
604     TRACE("%ld %d %p\n", hView, info, hRec);
605
606     if( !hRec )
607         return ERROR_INVALID_PARAMETER;
608
609     if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
610         return ERROR_INVALID_PARAMETER;
611
612     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
613     if( !query )
614         return ERROR_INVALID_HANDLE;
615
616     r = MSI_ViewGetColumnInfo( query, info, &rec );
617     if ( r == ERROR_SUCCESS )
618     {
619         *hRec = alloc_msihandle( &rec->hdr );
620         if ( !*hRec )
621             r = ERROR_NOT_ENOUGH_MEMORY;
622         msiobj_release( &rec->hdr );
623     }
624
625     msiobj_release( &query->hdr );
626
627     return r;
628 }
629
630 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
631                 MSIHANDLE hRecord)
632 {
633     MSIVIEW *view = NULL;
634     MSIQUERY *query = NULL;
635     MSIRECORD *rec = NULL;
636     UINT r = ERROR_FUNCTION_FAILED;
637
638     TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
639
640     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
641     if( !query )
642         return ERROR_INVALID_HANDLE;
643
644     view = query->view;
645     if( !view )
646         goto out;
647
648     if( !view->ops->modify )
649         goto out;
650
651     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
652     if( !rec )
653     {
654         r = ERROR_INVALID_HANDLE;
655         goto out;
656     }
657
658     r = view->ops->modify( view, eModifyMode, rec );
659
660 out:
661     msiobj_release( &query->hdr );
662     if( rec )
663         msiobj_release( &rec->hdr );
664
665     return r;
666 }
667
668 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
669                               DWORD *pcchBuf )
670 {
671     MSIQUERY *query = NULL;
672     static const WCHAR szError[] = { 0 };
673     MSIDBERROR r = MSIDBERROR_NOERROR;
674     int len;
675
676     FIXME("%ld %p %p - returns empty error string\n",
677           handle, szColumnNameBuffer, pcchBuf );
678
679     if( !pcchBuf )
680         return MSIDBERROR_INVALIDARG;
681
682     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
683     if( !query )
684         return MSIDBERROR_INVALIDARG;
685
686     len = lstrlenW( szError );
687     if( szColumnNameBuffer )
688     {
689         if( *pcchBuf > len )
690             lstrcpyW( szColumnNameBuffer, szError );
691         else
692             r = MSIDBERROR_MOREDATA;
693     }
694     *pcchBuf = len;
695
696     msiobj_release( &query->hdr );
697     return r;
698 }
699
700 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
701                               DWORD *pcchBuf )
702 {
703     static const CHAR szError[] = { 0 };
704     MSIQUERY *query = NULL;
705     MSIDBERROR r = MSIDBERROR_NOERROR;
706     int len;
707
708     FIXME("%ld %p %p - returns empty error string\n",
709           handle, szColumnNameBuffer, pcchBuf );
710
711     if( !pcchBuf )
712         return MSIDBERROR_INVALIDARG;
713
714     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
715     if( !query )
716         return MSIDBERROR_INVALIDARG;
717
718     len = lstrlenA( szError );
719     if( szColumnNameBuffer )
720     {
721         if( *pcchBuf > len )
722             lstrcpyA( szColumnNameBuffer, szError );
723         else
724             r = MSIDBERROR_MOREDATA;
725     }
726     *pcchBuf = len;
727
728     msiobj_release( &query->hdr );
729     return r;
730 }
731
732 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
733 {
734     FIXME("\n");
735     return 0;
736 }
737
738 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
739
740 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, 
741                  LPCWSTR szTransformFile, int iErrorCond )
742 {
743     UINT r;
744     IStorage *stg = NULL;
745  
746     TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
747
748     r = StgOpenStorage( szTransformFile, NULL,
749            STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
750     if( r )
751         return r;
752
753     if( TRACE_ON( msi ) )
754         enum_stream_names( stg );
755
756     r = msi_table_apply_transform( db, stg );
757
758     IStorage_Release( stg );
759
760     return r;
761 }
762
763 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
764                  LPCWSTR szTransformFile, int iErrorCond)
765 {
766     MSIDATABASE *db;
767     UINT r;
768
769     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
770     if( !db )
771         return ERROR_INVALID_HANDLE;
772
773     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
774     msiobj_release( &db->hdr );
775     return r;
776 }
777
778 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
779                  LPCSTR szTransformFile, int iErrorCond)
780 {
781     LPWSTR wstr;
782     UINT ret;
783
784     TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
785
786     wstr = strdupAtoW( szTransformFile );
787     if( szTransformFile && !wstr )
788         return ERROR_NOT_ENOUGH_MEMORY;
789
790     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
791
792     msi_free( wstr );
793
794     return ret;
795 }
796
797 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
798                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
799 {
800     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
801            debugstr_a(szTransformFile), iReserved1, iReserved2);
802     return ERROR_CALL_NOT_IMPLEMENTED;
803 }
804
805 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
806                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
807 {
808     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
809            debugstr_w(szTransformFile), iReserved1, iReserved2);
810     return ERROR_CALL_NOT_IMPLEMENTED;
811 }
812
813 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
814 {
815     MSIDATABASE *db;
816     UINT r;
817
818     TRACE("%ld\n", hdb);
819
820     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
821     if( !db )
822         return ERROR_INVALID_HANDLE;
823
824     /* FIXME: lock the database */
825
826     r = MSI_CommitTables( db );
827
828     /* FIXME: unlock the database */
829
830     msiobj_release( &db->hdr );
831
832     if (r == ERROR_SUCCESS)
833     {
834         msi_free( db->deletefile );
835         db->deletefile = NULL;
836     }
837
838     return r;
839 }
840
841 struct msi_primary_key_record_info
842 {
843     DWORD n;
844     MSIRECORD *rec;
845 };
846
847 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
848 {
849     struct msi_primary_key_record_info *info = param;
850     LPCWSTR name;
851     DWORD type;
852
853     type = MSI_RecordGetInteger( rec, 4 );
854     if( type & MSITYPE_KEY )
855     {
856         info->n++;
857         if( info->rec )
858         {
859             name = MSI_RecordGetString( rec, 3 );
860             MSI_RecordSetStringW( info->rec, info->n, name );
861         }
862     }
863
864     return ERROR_SUCCESS;
865 }
866
867 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
868                 LPCWSTR table, MSIRECORD **prec )
869 {
870     static const WCHAR sql[] = {
871         's','e','l','e','c','t',' ','*',' ',
872         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
873         'w','h','e','r','e',' ',
874         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
875     struct msi_primary_key_record_info info;
876     MSIQUERY *query = NULL;
877     MSIVIEW *view;
878     UINT r;
879     
880     r = MSI_OpenQuery( db, &query, sql, table );
881     if( r != ERROR_SUCCESS )
882         return r;
883
884     view = query->view;
885
886     /* count the number of primary key records */
887     info.n = 0;
888     info.rec = 0;
889     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
890     if( r == ERROR_SUCCESS )
891     {
892         TRACE("Found %d primary keys\n", info.n );
893
894         /* allocate a record and fill in the names of the tables */
895         info.rec = MSI_CreateRecord( info.n );
896         info.n = 0;
897         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
898         if( r == ERROR_SUCCESS )
899             *prec = info.rec;
900         else
901             msiobj_release( &info.rec->hdr );
902     }
903     msiobj_release( &query->hdr );
904
905     return r;
906 }
907
908 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
909                     LPCWSTR table, MSIHANDLE* phRec )
910 {
911     MSIRECORD *rec = NULL;
912     MSIDATABASE *db;
913     UINT r;
914
915     TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
916
917     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
918     if( !db )
919         return ERROR_INVALID_HANDLE;
920
921     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
922     if( r == ERROR_SUCCESS )
923     {
924         *phRec = alloc_msihandle( &rec->hdr );
925         if (! *phRec)
926            r = ERROR_NOT_ENOUGH_MEMORY;
927         msiobj_release( &rec->hdr );
928     }
929     msiobj_release( &db->hdr );
930
931     return r;
932 }
933
934 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
935                     LPCSTR table, MSIHANDLE* phRec)
936 {
937     LPWSTR szwTable = NULL;
938     UINT r;
939
940     TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
941
942     if( table )
943     {
944         szwTable = strdupAtoW( table );
945         if( !szwTable )
946             return ERROR_OUTOFMEMORY;
947     }
948     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
949     msi_free( szwTable );
950
951     return r;
952 }
953
954 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
955               MSIHANDLE hDatabase, LPCSTR szTableName)
956 {
957     LPWSTR szwTableName = NULL;
958     MSICONDITION r;
959
960     TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
961
962     if( szTableName )
963     {
964         szwTableName = strdupAtoW( szTableName );
965         if( !szwTableName )
966             return MSICONDITION_ERROR;
967     }
968     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
969     msi_free( szwTableName );
970
971     return r;
972 }
973
974 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
975               MSIHANDLE hDatabase, LPCWSTR szTableName)
976 {
977     MSIDATABASE *db;
978     MSICONDITION r;
979
980     TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
981
982     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
983     if( !db )
984         return MSICONDITION_ERROR;
985
986     r = MSI_DatabaseIsTablePersistent( db, szTableName );
987
988     msiobj_release( &db->hdr );
989
990     return r;
991 }