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