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