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