msi: Use the file's component instead of passing an extra parameter to set_file_source.
[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 WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
556                 MSIHANDLE hRecord)
557 {
558     MSIVIEW *view = NULL;
559     MSIQUERY *query = NULL;
560     MSIRECORD *rec = NULL;
561     UINT r = ERROR_FUNCTION_FAILED;
562
563     TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
564
565     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
566     if( !query )
567         return ERROR_INVALID_HANDLE;
568
569     view = query->view;
570     if( !view )
571         goto out;
572
573     if( !view->ops->modify )
574         goto out;
575
576     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
577     if( !rec )
578     {
579         r = ERROR_INVALID_HANDLE;
580         goto out;
581     }
582
583     r = view->ops->modify( view, eModifyMode, rec );
584
585 out:
586     msiobj_release( &query->hdr );
587     if( rec )
588         msiobj_release( &rec->hdr );
589
590     return r;
591 }
592
593 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
594                               DWORD *pcchBuf )
595 {
596     MSIQUERY *query = NULL;
597     static const WCHAR szError[] = { 0 };
598     MSIDBERROR r = MSIDBERROR_NOERROR;
599     int len;
600
601     FIXME("%ld %p %p - returns empty error string\n",
602           handle, szColumnNameBuffer, pcchBuf );
603
604     if( !pcchBuf )
605         return MSIDBERROR_INVALIDARG;
606
607     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
608     if( !query )
609         return MSIDBERROR_INVALIDARG;
610
611     len = lstrlenW( szError );
612     if( szColumnNameBuffer )
613     {
614         if( *pcchBuf > len )
615             lstrcpyW( szColumnNameBuffer, szError );
616         else
617             r = MSIDBERROR_MOREDATA;
618     }
619     *pcchBuf = len;
620
621     msiobj_release( &query->hdr );
622     return r;
623 }
624
625 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
626                               DWORD *pcchBuf )
627 {
628     static const CHAR szError[] = { 0 };
629     MSIQUERY *query = NULL;
630     MSIDBERROR r = MSIDBERROR_NOERROR;
631     int len;
632
633     FIXME("%ld %p %p - returns empty error string\n",
634           handle, szColumnNameBuffer, pcchBuf );
635
636     if( !pcchBuf )
637         return MSIDBERROR_INVALIDARG;
638
639     query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
640     if( !query )
641         return MSIDBERROR_INVALIDARG;
642
643     len = lstrlenA( szError );
644     if( szColumnNameBuffer )
645     {
646         if( *pcchBuf > len )
647             lstrcpyA( szColumnNameBuffer, szError );
648         else
649             r = MSIDBERROR_MOREDATA;
650     }
651     *pcchBuf = len;
652
653     msiobj_release( &query->hdr );
654     return r;
655 }
656
657 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
658 {
659     FIXME("\n");
660     return 0;
661 }
662
663 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
664
665 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
666                  LPCWSTR szTransformFile, int iErrorCond )
667 {
668     HRESULT r;
669     UINT ret = ERROR_FUNCTION_FAILED;
670     IStorage *stg = NULL;
671     STATSTG stat;
672
673     TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
674
675     r = StgOpenStorage( szTransformFile, NULL,
676            STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
677     if ( FAILED(r) )
678         return ret;
679
680     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
681     if ( FAILED( r ) )
682         goto end;
683
684     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
685         goto end;
686
687     if( TRACE_ON( msi ) )
688         enum_stream_names( stg );
689
690     ret = msi_table_apply_transform( db, stg );
691
692 end:
693     IStorage_Release( stg );
694
695     return ret;
696 }
697
698 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
699                  LPCWSTR szTransformFile, int iErrorCond)
700 {
701     MSIDATABASE *db;
702     UINT r;
703
704     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
705     if( !db )
706         return ERROR_INVALID_HANDLE;
707
708     r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
709     msiobj_release( &db->hdr );
710     return r;
711 }
712
713 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
714                  LPCSTR szTransformFile, int iErrorCond)
715 {
716     LPWSTR wstr;
717     UINT ret;
718
719     TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
720
721     wstr = strdupAtoW( szTransformFile );
722     if( szTransformFile && !wstr )
723         return ERROR_NOT_ENOUGH_MEMORY;
724
725     ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
726
727     msi_free( wstr );
728
729     return ret;
730 }
731
732 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
733                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
734 {
735     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
736            debugstr_a(szTransformFile), iReserved1, iReserved2);
737     return ERROR_CALL_NOT_IMPLEMENTED;
738 }
739
740 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
741                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
742 {
743     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
744            debugstr_w(szTransformFile), iReserved1, iReserved2);
745     return ERROR_CALL_NOT_IMPLEMENTED;
746 }
747
748 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
749 {
750     MSIDATABASE *db;
751     UINT r;
752
753     TRACE("%ld\n", hdb);
754
755     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
756     if( !db )
757         return ERROR_INVALID_HANDLE;
758
759     /* FIXME: lock the database */
760
761     r = MSI_CommitTables( db );
762
763     /* FIXME: unlock the database */
764
765     msiobj_release( &db->hdr );
766
767     if (r == ERROR_SUCCESS)
768     {
769         msi_free( db->deletefile );
770         db->deletefile = NULL;
771     }
772
773     return r;
774 }
775
776 struct msi_primary_key_record_info
777 {
778     DWORD n;
779     MSIRECORD *rec;
780 };
781
782 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
783 {
784     struct msi_primary_key_record_info *info = param;
785     LPCWSTR name;
786     DWORD type;
787
788     type = MSI_RecordGetInteger( rec, 4 );
789     if( type & MSITYPE_KEY )
790     {
791         info->n++;
792         if( info->rec )
793         {
794             name = MSI_RecordGetString( rec, 3 );
795             MSI_RecordSetStringW( info->rec, info->n, name );
796         }
797     }
798
799     return ERROR_SUCCESS;
800 }
801
802 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
803                 LPCWSTR table, MSIRECORD **prec )
804 {
805     static const WCHAR sql[] = {
806         's','e','l','e','c','t',' ','*',' ',
807         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
808         'w','h','e','r','e',' ',
809         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
810     struct msi_primary_key_record_info info;
811     MSIQUERY *query = NULL;
812     MSIVIEW *view;
813     UINT r;
814     
815     r = MSI_OpenQuery( db, &query, sql, table );
816     if( r != ERROR_SUCCESS )
817         return r;
818
819     view = query->view;
820
821     /* count the number of primary key records */
822     info.n = 0;
823     info.rec = 0;
824     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
825     if( r == ERROR_SUCCESS )
826     {
827         TRACE("Found %d primary keys\n", info.n );
828
829         /* allocate a record and fill in the names of the tables */
830         info.rec = MSI_CreateRecord( info.n );
831         info.n = 0;
832         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
833         if( r == ERROR_SUCCESS )
834             *prec = info.rec;
835         else
836             msiobj_release( &info.rec->hdr );
837     }
838     msiobj_release( &query->hdr );
839
840     return r;
841 }
842
843 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
844                     LPCWSTR table, MSIHANDLE* phRec )
845 {
846     MSIRECORD *rec = NULL;
847     MSIDATABASE *db;
848     UINT r;
849
850     TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
851
852     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
853     if( !db )
854         return ERROR_INVALID_HANDLE;
855
856     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
857     if( r == ERROR_SUCCESS )
858     {
859         *phRec = alloc_msihandle( &rec->hdr );
860         if (! *phRec)
861            r = ERROR_NOT_ENOUGH_MEMORY;
862         msiobj_release( &rec->hdr );
863     }
864     msiobj_release( &db->hdr );
865
866     return r;
867 }
868
869 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
870                     LPCSTR table, MSIHANDLE* phRec)
871 {
872     LPWSTR szwTable = NULL;
873     UINT r;
874
875     TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
876
877     if( table )
878     {
879         szwTable = strdupAtoW( table );
880         if( !szwTable )
881             return ERROR_OUTOFMEMORY;
882     }
883     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
884     msi_free( szwTable );
885
886     return r;
887 }
888
889 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
890               MSIHANDLE hDatabase, LPCSTR szTableName)
891 {
892     LPWSTR szwTableName = NULL;
893     MSICONDITION r;
894
895     TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
896
897     if( szTableName )
898     {
899         szwTableName = strdupAtoW( szTableName );
900         if( !szwTableName )
901             return MSICONDITION_ERROR;
902     }
903     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
904     msi_free( szwTableName );
905
906     return r;
907 }
908
909 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
910               MSIHANDLE hDatabase, LPCWSTR szTableName)
911 {
912     MSIDATABASE *db;
913     MSICONDITION r;
914
915     TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
916
917     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
918     if( !db )
919         return MSICONDITION_ERROR;
920
921     r = MSI_DatabaseIsTablePersistent( db, szTableName );
922
923     msiobj_release( &db->hdr );
924
925     return r;
926 }