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