msi/tests: Add missing '\n' to ok() calls.
[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, DWORD *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_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
269 {
270     MSIVIEW *view;
271     MSIRECORD *rec;
272     UINT row_count = 0, col_count = 0, i, ival, ret, type;
273
274     TRACE("%p %p\n", query, prec );
275
276     view = query->view;
277     if( !view )
278         return ERROR_FUNCTION_FAILED;
279
280     ret = view->ops->get_dimensions( view, &row_count, &col_count );
281     if( ret )
282         return ret;
283     if( !col_count )
284         return ERROR_INVALID_PARAMETER;
285
286     if( query->row >= row_count )
287         return ERROR_NO_MORE_ITEMS;
288
289     rec = MSI_CreateRecord( col_count );
290     if( !rec )
291         return ERROR_FUNCTION_FAILED;
292
293     for( i=1; i<=col_count; i++ )
294     {
295         ret = view->ops->get_column_info( view, i, NULL, &type );
296         if( ret )
297         {
298             ERR("Error getting column type for %d\n", i );
299             continue;
300         }
301         if (!MSITYPE_IS_BINARY(type))
302         {
303             ret = view->ops->fetch_int( view, query->row, i, &ival );
304             if( ret )
305             {
306                 ERR("Error fetching data for %d\n", i );
307                 continue;
308             }
309             if( ! (type & MSITYPE_VALID ) )
310                 ERR("Invalid type!\n");
311
312             /* check if it's nul (0) - if so, don't set anything */
313             if( !ival )
314                 continue;
315
316             if( type & MSITYPE_STRING )
317             {
318                 LPCWSTR sval;
319
320                 sval = msi_string_lookup_id( query->db->strings, ival );
321                 MSI_RecordSetStringW( rec, i, sval );
322             }
323             else
324             {
325                 if( (type & MSI_DATASIZEMASK) == 2 )
326                     MSI_RecordSetInteger( rec, i, ival - (1<<15) );
327                 else
328                     MSI_RecordSetInteger( rec, i, ival - (1<<31) );
329             }
330         }
331         else
332         {
333             IStream *stm = NULL;
334
335             ret = view->ops->fetch_stream( view, query->row, i, &stm );
336             if( ( ret == ERROR_SUCCESS ) && stm )
337             {
338                 MSI_RecordSetIStream( rec, i, stm );
339                 IStream_Release( stm );
340             }
341             else
342                 ERR("failed to get stream\n");
343         }
344     }
345     query->row ++;
346
347     *prec = rec;
348
349     return ERROR_SUCCESS;
350 }
351
352 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
353 {
354     MSIQUERY *query;
355     MSIRECORD *rec = NULL;
356     UINT ret;
357
358     TRACE("%ld %p\n", hView, record);
359
360     if( !record )
361         return ERROR_INVALID_PARAMETER;
362     *record = 0;
363
364     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
365     if( !query )
366         return ERROR_INVALID_HANDLE;
367     ret = MSI_ViewFetch( query, &rec );
368     if( ret == ERROR_SUCCESS )
369     {
370         *record = alloc_msihandle( &rec->hdr );
371         if (! *record)
372            ret = ERROR_NOT_ENOUGH_MEMORY;
373         msiobj_release( &rec->hdr );
374     }
375     msiobj_release( &query->hdr );
376     return ret;
377 }
378
379 UINT MSI_ViewClose(MSIQUERY *query)
380 {
381     MSIVIEW *view;
382
383     TRACE("%p\n", query );
384
385     view = query->view;
386     if( !view )
387         return ERROR_FUNCTION_FAILED;
388     if( !view->ops->close )
389         return ERROR_FUNCTION_FAILED;
390
391     return view->ops->close( view );
392 }
393
394 UINT WINAPI MsiViewClose(MSIHANDLE hView)
395 {
396     MSIQUERY *query;
397     UINT ret;
398
399     TRACE("%ld\n", hView );
400
401     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
402     if( !query )
403         return ERROR_INVALID_HANDLE;
404
405     ret = MSI_ViewClose( query );
406     msiobj_release( &query->hdr );
407     return ret;
408 }
409
410 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
411 {
412     MSIVIEW *view;
413
414     TRACE("%p %p\n", query, rec);
415
416     view = query->view;
417     if( !view )
418         return ERROR_FUNCTION_FAILED;
419     if( !view->ops->execute )
420         return ERROR_FUNCTION_FAILED;
421     query->row = 0;
422
423     return view->ops->execute( view, rec );
424 }
425
426 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
427 {
428     MSIQUERY *query;
429     MSIRECORD *rec = NULL;
430     UINT ret;
431     
432     TRACE("%ld %ld\n", hView, hRec);
433
434     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
435     if( !query )
436         return ERROR_INVALID_HANDLE;
437
438     if( hRec )
439     {
440         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
441         if( !rec )
442         {
443             ret = ERROR_INVALID_HANDLE;
444             goto out;
445         }
446     }
447
448     msiobj_lock( &rec->hdr );
449     ret = MSI_ViewExecute( query, rec );
450     msiobj_unlock( &rec->hdr );
451
452 out:
453     msiobj_release( &query->hdr );
454     if( rec )
455         msiobj_release( &rec->hdr );
456
457     return ret;
458 }
459
460 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
461 {
462     static const WCHAR fmt[] = { '%','d',0 };
463     WCHAR szType[0x10];
464
465     if (MSITYPE_IS_BINARY(type))
466         szType[0] = 'v';
467     else if (type & MSITYPE_LOCALIZABLE)
468         szType[0] = 'l';
469     else if (type & MSITYPE_STRING)
470         szType[0] = 's';
471     else
472         szType[0] = 'i';
473     if (type & MSITYPE_NULLABLE)
474         szType[0] &= ~0x20;
475
476     sprintfW( &szType[1], fmt, (type&0xff) );
477
478     TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
479
480     return MSI_RecordSetStringW( rec, field, szType );
481 }
482
483 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
484 {
485     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
486     MSIRECORD *rec;
487     MSIVIEW *view = query->view;
488     LPWSTR name;
489
490     if( !view )
491         return ERROR_FUNCTION_FAILED;
492
493     if( !view->ops->get_dimensions )
494         return ERROR_FUNCTION_FAILED;
495
496     r = view->ops->get_dimensions( view, NULL, &count );
497     if( r != ERROR_SUCCESS )
498         return r;
499     if( !count )
500         return ERROR_INVALID_PARAMETER;
501
502     rec = MSI_CreateRecord( count );
503     if( !rec )
504         return ERROR_FUNCTION_FAILED;
505
506     for( i=0; i<count; i++ )
507     {
508         name = NULL;
509         r = view->ops->get_column_info( view, i+1, &name, &type );
510         if( r != ERROR_SUCCESS )
511             continue;
512         if (info == MSICOLINFO_NAMES)
513             MSI_RecordSetStringW( rec, i+1, name );
514         else
515             msi_set_record_type_string( rec, i+1, type);
516         msi_free( name );
517     }
518
519     *prec = rec;
520     return ERROR_SUCCESS;
521 }
522
523 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
524 {
525     MSIQUERY *query = NULL;
526     MSIRECORD *rec = NULL;
527     UINT r;
528
529     TRACE("%ld %d %p\n", hView, info, hRec);
530
531     if( !hRec )
532         return ERROR_INVALID_PARAMETER;
533
534     if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
535         return ERROR_INVALID_PARAMETER;
536
537     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
538     if( !query )
539         return ERROR_INVALID_HANDLE;
540
541     r = MSI_ViewGetColumnInfo( query, info, &rec );
542     if ( r == ERROR_SUCCESS )
543     {
544         *hRec = alloc_msihandle( &rec->hdr );
545         if ( !*hRec )
546             r = ERROR_NOT_ENOUGH_MEMORY;
547         msiobj_release( &rec->hdr );
548     }
549
550     msiobj_release( &query->hdr );
551
552     return r;
553 }
554
555 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
556 {
557     MSIVIEW *view = NULL;
558
559     if ( !query || !rec )
560         return ERROR_INVALID_HANDLE;
561
562     view = query->view;
563     if ( !view  || !view->ops->modify)
564         return ERROR_FUNCTION_FAILED;
565
566     return view->ops->modify( view, mode, rec );
567 }
568
569 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
570                 MSIHANDLE hRecord)
571 {
572     MSIQUERY *query = NULL;
573     MSIRECORD *rec = NULL;
574     UINT r = ERROR_FUNCTION_FAILED;
575
576     TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
577
578     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
579     if( !query )
580         return ERROR_INVALID_HANDLE;
581
582     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
583     r = MSI_ViewModify( query, eModifyMode, rec );
584
585     msiobj_release( &query->hdr );
586     if( rec )
587         msiobj_release( &rec->hdr );
588
589     return r;
590 }
591
592 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
593                               DWORD *pcchBuf )
594 {
595     MSIQUERY *query = NULL;
596     static const WCHAR szError[] = { 0 };
597     MSIDBERROR r = MSIDBERROR_NOERROR;
598     int len;
599
600     FIXME("%ld %p %p - returns empty error string\n",
601           handle, szColumnNameBuffer, pcchBuf );
602
603     if( !pcchBuf )
604         return MSIDBERROR_INVALIDARG;
605
606     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
607     if( !query )
608         return MSIDBERROR_INVALIDARG;
609
610     len = lstrlenW( szError );
611     if( szColumnNameBuffer )
612     {
613         if( *pcchBuf > len )
614             lstrcpyW( szColumnNameBuffer, szError );
615         else
616             r = MSIDBERROR_MOREDATA;
617     }
618     *pcchBuf = len;
619
620     msiobj_release( &query->hdr );
621     return r;
622 }
623
624 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
625                               DWORD *pcchBuf )
626 {
627     static const CHAR szError[] = { 0 };
628     MSIQUERY *query = NULL;
629     MSIDBERROR r = MSIDBERROR_NOERROR;
630     int len;
631
632     FIXME("%ld %p %p - returns empty error string\n",
633           handle, szColumnNameBuffer, pcchBuf );
634
635     if( !pcchBuf )
636         return MSIDBERROR_INVALIDARG;
637
638     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
639     if( !query )
640         return MSIDBERROR_INVALIDARG;
641
642     len = lstrlenA( szError );
643     if( szColumnNameBuffer )
644     {
645         if( *pcchBuf > len )
646             lstrcpyA( szColumnNameBuffer, szError );
647         else
648             r = MSIDBERROR_MOREDATA;
649     }
650     *pcchBuf = len;
651
652     msiobj_release( &query->hdr );
653     return r;
654 }
655
656 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
657 {
658     FIXME("\n");
659     return 0;
660 }
661
662 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
663
664 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
665                  LPCWSTR szTransformFile, int iErrorCond )
666 {
667     HRESULT r;
668     UINT ret = ERROR_FUNCTION_FAILED;
669     IStorage *stg = NULL;
670     STATSTG stat;
671
672     TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
673
674     r = StgOpenStorage( szTransformFile, NULL,
675            STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
676     if ( FAILED(r) )
677         return ret;
678
679     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
680     if ( FAILED( r ) )
681         goto end;
682
683     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
684         goto end;
685
686     if( TRACE_ON( msi ) )
687         enum_stream_names( stg );
688
689     ret = msi_table_apply_transform( db, stg );
690
691 end:
692     IStorage_Release( stg );
693
694     return ret;
695 }
696
697 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
698                  LPCWSTR szTransformFile, int iErrorCond)
699 {
700     MSIDATABASE *db;
701     UINT r;
702
703     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
704     if( !db )
705         return ERROR_INVALID_HANDLE;
706
707     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
708     msiobj_release( &db->hdr );
709     return r;
710 }
711
712 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
713                  LPCSTR szTransformFile, int iErrorCond)
714 {
715     LPWSTR wstr;
716     UINT ret;
717
718     TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
719
720     wstr = strdupAtoW( szTransformFile );
721     if( szTransformFile && !wstr )
722         return ERROR_NOT_ENOUGH_MEMORY;
723
724     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
725
726     msi_free( wstr );
727
728     return ret;
729 }
730
731 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
732                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
733 {
734     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
735            debugstr_a(szTransformFile), iReserved1, iReserved2);
736     return ERROR_CALL_NOT_IMPLEMENTED;
737 }
738
739 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
740                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
741 {
742     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
743            debugstr_w(szTransformFile), iReserved1, iReserved2);
744     return ERROR_CALL_NOT_IMPLEMENTED;
745 }
746
747 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
748 {
749     MSIDATABASE *db;
750     UINT r;
751
752     TRACE("%ld\n", hdb);
753
754     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
755     if( !db )
756         return ERROR_INVALID_HANDLE;
757
758     /* FIXME: lock the database */
759
760     r = MSI_CommitTables( db );
761
762     /* FIXME: unlock the database */
763
764     msiobj_release( &db->hdr );
765
766     if (r == ERROR_SUCCESS)
767     {
768         msi_free( db->deletefile );
769         db->deletefile = NULL;
770     }
771
772     return r;
773 }
774
775 struct msi_primary_key_record_info
776 {
777     DWORD n;
778     MSIRECORD *rec;
779 };
780
781 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
782 {
783     struct msi_primary_key_record_info *info = param;
784     LPCWSTR name;
785     DWORD type;
786
787     type = MSI_RecordGetInteger( rec, 4 );
788     if( type & MSITYPE_KEY )
789     {
790         info->n++;
791         if( info->rec )
792         {
793             name = MSI_RecordGetString( rec, 3 );
794             MSI_RecordSetStringW( info->rec, info->n, name );
795         }
796     }
797
798     return ERROR_SUCCESS;
799 }
800
801 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
802                 LPCWSTR table, MSIRECORD **prec )
803 {
804     static const WCHAR sql[] = {
805         's','e','l','e','c','t',' ','*',' ',
806         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
807         'w','h','e','r','e',' ',
808         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
809     struct msi_primary_key_record_info info;
810     MSIQUERY *query = NULL;
811     MSIVIEW *view;
812     UINT r;
813     
814     r = MSI_OpenQuery( db, &query, sql, table );
815     if( r != ERROR_SUCCESS )
816         return r;
817
818     view = query->view;
819
820     /* count the number of primary key records */
821     info.n = 0;
822     info.rec = 0;
823     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
824     if( r == ERROR_SUCCESS )
825     {
826         TRACE("Found %d primary keys\n", info.n );
827
828         /* allocate a record and fill in the names of the tables */
829         info.rec = MSI_CreateRecord( info.n );
830         info.n = 0;
831         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
832         if( r == ERROR_SUCCESS )
833             *prec = info.rec;
834         else
835             msiobj_release( &info.rec->hdr );
836     }
837     msiobj_release( &query->hdr );
838
839     return r;
840 }
841
842 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
843                     LPCWSTR table, MSIHANDLE* phRec )
844 {
845     MSIRECORD *rec = NULL;
846     MSIDATABASE *db;
847     UINT r;
848
849     TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
850
851     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
852     if( !db )
853         return ERROR_INVALID_HANDLE;
854
855     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
856     if( r == ERROR_SUCCESS )
857     {
858         *phRec = alloc_msihandle( &rec->hdr );
859         if (! *phRec)
860            r = ERROR_NOT_ENOUGH_MEMORY;
861         msiobj_release( &rec->hdr );
862     }
863     msiobj_release( &db->hdr );
864
865     return r;
866 }
867
868 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
869                     LPCSTR table, MSIHANDLE* phRec)
870 {
871     LPWSTR szwTable = NULL;
872     UINT r;
873
874     TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
875
876     if( table )
877     {
878         szwTable = strdupAtoW( table );
879         if( !szwTable )
880             return ERROR_OUTOFMEMORY;
881     }
882     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
883     msi_free( szwTable );
884
885     return r;
886 }
887
888 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
889               MSIHANDLE hDatabase, LPCSTR szTableName)
890 {
891     LPWSTR szwTableName = NULL;
892     MSICONDITION r;
893
894     TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
895
896     if( szTableName )
897     {
898         szwTableName = strdupAtoW( szTableName );
899         if( !szwTableName )
900             return MSICONDITION_ERROR;
901     }
902     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
903     msi_free( szwTableName );
904
905     return r;
906 }
907
908 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
909               MSIHANDLE hDatabase, LPCWSTR szTableName)
910 {
911     MSIDATABASE *db;
912     MSICONDITION r;
913
914     TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
915
916     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
917     if( !db )
918         return MSICONDITION_ERROR;
919
920     r = MSI_DatabaseIsTablePersistent( db, szTableName );
921
922     msiobj_release( &db->hdr );
923
924     return r;
925 }