xref: /reactos/dll/win32/msi/msiquery.c (revision f59c58d8)
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/exception.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "winnls.h"
35 
36 #include "msipriv.h"
37 #include "query.h"
38 #include "winemsi_s.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 = wcscmp( name, col_name );
77         if( table_name )
78             x |= wcscmp( 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, const char *szQuery, MSIHANDLE *phView )
89 {
90     UINT r;
91     WCHAR *szwQuery;
92 
93     TRACE( "%lu, %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, const WCHAR *szQuery, MSIQUERY **pView )
111 {
112     MSIQUERY *query;
113     UINT r;
114 
115     TRACE( "%s, %p\n", debugstr_w(szQuery), pView );
116 
117     /* pre allocate a handle to hold a pointer to the view */
118     query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
119                               MSI_CloseView );
120     if( !query )
121         return ERROR_FUNCTION_FAILED;
122 
123     msiobj_addref( &db->hdr );
124     query->db = db;
125     list_init( &query->mem );
126 
127     r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
128     if( r == ERROR_SUCCESS )
129     {
130         msiobj_addref( &query->hdr );
131         *pView = query;
132     }
133 
134     msiobj_release( &query->hdr );
135     return r;
136 }
137 
138 UINT WINAPIV MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
139 {
140     UINT r;
141     int size = 100, res;
142     LPWSTR query;
143 
144     /* construct the string */
145     for (;;)
146     {
147         va_list va;
148         query = msi_alloc( size*sizeof(WCHAR) );
149         va_start(va, fmt);
150         res = vswprintf(query, size, fmt, va);
151         va_end(va);
152         if (res == -1) size *= 2;
153         else if (res >= size) size = res + 1;
154         else break;
155         msi_free( query );
156     }
157     /* perform the query */
158     r = MSI_DatabaseOpenViewW(db, query, view);
159     msi_free(query);
160     return r;
161 }
162 
163 UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count,
164                          record_func func, LPVOID param )
165 {
166     MSIRECORD *rec = NULL;
167     UINT r, n = 0, max = 0;
168 
169     r = MSI_ViewExecute( view, NULL );
170     if( r != ERROR_SUCCESS )
171         return r;
172 
173     if( count )
174         max = *count;
175 
176     /* iterate a query */
177     for( n = 0; (max == 0) || (n < max); n++ )
178     {
179         r = MSI_ViewFetch( view, &rec );
180         if( r != ERROR_SUCCESS )
181             break;
182         if (func)
183             r = func( rec, param );
184         msiobj_release( &rec->hdr );
185         if( r != ERROR_SUCCESS )
186             break;
187     }
188 
189     MSI_ViewClose( view );
190 
191     if( count )
192         *count = n;
193 
194     if( r == ERROR_NO_MORE_ITEMS )
195         r = ERROR_SUCCESS;
196 
197     return r;
198 }
199 
200 /* return a single record from a query */
201 MSIRECORD * WINAPIV MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
202 {
203     MSIRECORD *rec = NULL;
204     MSIQUERY *view = NULL;
205     UINT r;
206     int size = 100, res;
207     LPWSTR query;
208 
209     /* construct the string */
210     for (;;)
211     {
212         va_list va;
213         query = msi_alloc( size*sizeof(WCHAR) );
214         va_start(va, fmt);
215         res = vswprintf(query, size, fmt, va);
216         va_end(va);
217         if (res == -1) size *= 2;
218         else if (res >= size) size = res + 1;
219         else break;
220         msi_free( query );
221     }
222     /* perform the query */
223     r = MSI_DatabaseOpenViewW(db, query, &view);
224     msi_free(query);
225 
226     if( r == ERROR_SUCCESS )
227     {
228         MSI_ViewExecute( view, NULL );
229         MSI_ViewFetch( view, &rec );
230         MSI_ViewClose( view );
231         msiobj_release( &view->hdr );
232     }
233     return rec;
234 }
235 
236 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
237               LPCWSTR szQuery, MSIHANDLE *phView)
238 {
239     MSIDATABASE *db;
240     MSIQUERY *query = NULL;
241     UINT ret;
242 
243     TRACE("%s %p\n", debugstr_w(szQuery), phView);
244 
245     if (!phView)
246         return ERROR_INVALID_PARAMETER;
247 
248     if (!szQuery)
249         return ERROR_BAD_QUERY_SYNTAX;
250 
251     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
252     if( !db )
253     {
254         MSIHANDLE remote, remote_view;
255 
256         if (!(remote = msi_get_remote(hdb)))
257             return ERROR_INVALID_HANDLE;
258 
259         __TRY
260         {
261             ret = remote_DatabaseOpenView(remote, szQuery, &remote_view);
262         }
263         __EXCEPT(rpc_filter)
264         {
265             ret = GetExceptionCode();
266         }
267         __ENDTRY
268 
269         if (!ret)
270             *phView = alloc_msi_remote_handle(remote_view);
271         return ret;
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_refresh_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     for (i = 1; i <= col_count; i++)
301     {
302         ret = view->ops->get_column_info(view, i, NULL, &type, NULL, NULL);
303         if (ret)
304         {
305             ERR("Error getting column type for %d\n", i);
306             continue;
307         }
308 
309         if (MSITYPE_IS_BINARY(type))
310         {
311             IStream *stm = NULL;
312 
313             ret = view->ops->fetch_stream(view, row, i, &stm);
314             if ((ret == ERROR_SUCCESS) && stm)
315             {
316                 MSI_RecordSetIStream(rec, i, stm);
317                 IStream_Release(stm);
318             }
319             else
320                 WARN("failed to get stream\n");
321 
322             continue;
323         }
324 
325         ret = view->ops->fetch_int(view, row, i, &ival);
326         if (ret)
327         {
328             ERR("Error fetching data for %d\n", i);
329             continue;
330         }
331 
332         if (! (type & MSITYPE_VALID))
333             ERR("Invalid type!\n");
334 
335         if (type & MSITYPE_STRING)
336         {
337             int len;
338             const WCHAR *sval = msi_string_lookup(db->strings, ival, &len);
339             msi_record_set_string(rec, i, sval, len);
340         }
341         else
342         {
343             if ((type & MSI_DATASIZEMASK) == 2)
344                 MSI_RecordSetInteger(rec, i, ival ? ival - (1<<15) : MSI_NULL_INTEGER);
345             else
346                 MSI_RecordSetInteger(rec, i, ival - (1u<<31));
347         }
348     }
349 
350     return ERROR_SUCCESS;
351 }
352 
353 UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec)
354 {
355     UINT row_count = 0, col_count = 0, r;
356     MSIRECORD *object;
357 
358     TRACE("view %p, row %u, rec %p.\n", view, row, rec);
359 
360     if ((r = view->ops->get_dimensions(view, &row_count, &col_count)))
361         return r;
362 
363     if (row >= row_count)
364         return ERROR_NO_MORE_ITEMS;
365 
366     if (!(object = MSI_CreateRecord( col_count )))
367         return ERROR_OUTOFMEMORY;
368 
369     if ((r = msi_view_refresh_row(db, view, row, object)))
370         msiobj_release( &object->hdr );
371     else
372         *rec = object;
373 
374     return r;
375 }
376 
377 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
378 {
379     MSIVIEW *view;
380     UINT r;
381 
382     TRACE("%p %p\n", query, prec );
383 
384     view = query->view;
385     if( !view )
386         return ERROR_FUNCTION_FAILED;
387 
388     r = msi_view_get_row(query->db, view, query->row, prec);
389     if (r == ERROR_SUCCESS)
390     {
391         query->row ++;
392         (*prec)->cookie = (UINT64)(ULONG_PTR)query;
393         MSI_RecordSetInteger(*prec, 0, 1);
394     }
395     else if (r == ERROR_NO_MORE_ITEMS)
396     {
397         /* end of view; reset cursor to first row */
398         query->row = 0;
399     }
400 
401     return r;
402 }
403 
404 UINT WINAPI MsiViewFetch( MSIHANDLE hView, MSIHANDLE *record )
405 {
406     MSIQUERY *query;
407     MSIRECORD *rec = NULL;
408     UINT ret;
409 
410     TRACE( "%lu, %p\n", hView, record );
411 
412     if( !record )
413         return ERROR_INVALID_PARAMETER;
414     *record = 0;
415 
416     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
417     if (!query)
418     {
419         struct wire_record *wire_rec = NULL;
420         MSIHANDLE remote;
421 
422         if (!(remote = msi_get_remote(hView)))
423             return ERROR_INVALID_HANDLE;
424 
425         __TRY
426         {
427             ret = remote_ViewFetch(remote, &wire_rec);
428         }
429         __EXCEPT(rpc_filter)
430         {
431             ret = GetExceptionCode();
432         }
433         __ENDTRY
434 
435         if (!ret)
436         {
437             ret = unmarshal_record(wire_rec, record);
438             free_remote_record(wire_rec);
439         }
440         return ret;
441     }
442     ret = MSI_ViewFetch( query, &rec );
443     if( ret == ERROR_SUCCESS )
444     {
445         *record = alloc_msihandle( &rec->hdr );
446         if (! *record)
447            ret = ERROR_NOT_ENOUGH_MEMORY;
448         msiobj_release( &rec->hdr );
449     }
450     msiobj_release( &query->hdr );
451     return ret;
452 }
453 
454 UINT MSI_ViewClose(MSIQUERY *query)
455 {
456     MSIVIEW *view;
457 
458     TRACE("%p\n", query );
459 
460     view = query->view;
461     if( !view )
462         return ERROR_FUNCTION_FAILED;
463     if( !view->ops->close )
464         return ERROR_FUNCTION_FAILED;
465 
466     return view->ops->close( view );
467 }
468 
469 UINT WINAPI MsiViewClose( MSIHANDLE hView )
470 {
471     MSIQUERY *query;
472     UINT ret;
473 
474     TRACE( "%lu\n", hView );
475 
476     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
477     if (!query)
478     {
479         MSIHANDLE remote;
480 
481         if (!(remote = msi_get_remote(hView)))
482             return ERROR_INVALID_HANDLE;
483 
484         __TRY
485         {
486             ret = remote_ViewClose(remote);
487         }
488         __EXCEPT(rpc_filter)
489         {
490             ret = GetExceptionCode();
491         }
492         __ENDTRY
493 
494         return ret;
495     }
496 
497     ret = MSI_ViewClose( query );
498     msiobj_release( &query->hdr );
499     return ret;
500 }
501 
502 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
503 {
504     MSIVIEW *view;
505 
506     TRACE("%p %p\n", query, rec);
507 
508     view = query->view;
509     if( !view )
510         return ERROR_FUNCTION_FAILED;
511     if( !view->ops->execute )
512         return ERROR_FUNCTION_FAILED;
513     query->row = 0;
514 
515     return view->ops->execute( view, rec );
516 }
517 
518 UINT WINAPI MsiViewExecute( MSIHANDLE hView, MSIHANDLE hRec )
519 {
520     MSIQUERY *query;
521     MSIRECORD *rec = NULL;
522     UINT ret;
523 
524     TRACE( "%lu, %lu\n", hView, hRec );
525 
526     if( hRec )
527     {
528         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
529         if( !rec )
530             return ERROR_INVALID_HANDLE;
531     }
532 
533     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
534     if( !query )
535     {
536         MSIHANDLE remote;
537 
538         if (!(remote = msi_get_remote(hView)))
539             return ERROR_INVALID_HANDLE;
540 
541         __TRY
542         {
543             ret = remote_ViewExecute(remote, rec ? (struct wire_record *)&rec->count : NULL);
544         }
545         __EXCEPT(rpc_filter)
546         {
547             ret = GetExceptionCode();
548         }
549         __ENDTRY
550 
551         if (rec)
552             msiobj_release(&rec->hdr);
553         return ret;
554     }
555 
556     msiobj_lock( &rec->hdr );
557     ret = MSI_ViewExecute( query, rec );
558     msiobj_unlock( &rec->hdr );
559 
560     msiobj_release( &query->hdr );
561     if( rec )
562         msiobj_release( &rec->hdr );
563 
564     return ret;
565 }
566 
567 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field,
568                                         UINT type, BOOL temporary )
569 {
570     WCHAR szType[0x10];
571 
572     if (MSITYPE_IS_BINARY(type))
573         szType[0] = 'v';
574     else if (type & MSITYPE_LOCALIZABLE)
575         szType[0] = 'l';
576     else if (type & MSITYPE_UNKNOWN)
577         szType[0] = 'f';
578     else if (type & MSITYPE_STRING)
579     {
580         if (temporary)
581             szType[0] = 'g';
582         else
583           szType[0] = 's';
584     }
585     else
586     {
587         if (temporary)
588             szType[0] = 'j';
589         else
590             szType[0] = 'i';
591     }
592 
593     if (type & MSITYPE_NULLABLE)
594         szType[0] &= ~0x20;
595 
596     swprintf( &szType[1], ARRAY_SIZE(szType) - 1, L"%d", (type&0xff) );
597 
598     TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
599 
600     return MSI_RecordSetStringW( rec, field, szType );
601 }
602 
603 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
604 {
605     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
606     MSIRECORD *rec;
607     MSIVIEW *view = query->view;
608     LPCWSTR name;
609     BOOL temporary;
610 
611     if( !view )
612         return ERROR_FUNCTION_FAILED;
613 
614     if( !view->ops->get_dimensions )
615         return ERROR_FUNCTION_FAILED;
616 
617     r = view->ops->get_dimensions( view, NULL, &count );
618     if( r != ERROR_SUCCESS )
619         return r;
620     if( !count )
621         return ERROR_INVALID_PARAMETER;
622 
623     rec = MSI_CreateRecord( count );
624     if( !rec )
625         return ERROR_FUNCTION_FAILED;
626 
627     for( i=0; i<count; i++ )
628     {
629         name = NULL;
630         r = view->ops->get_column_info( view, i+1, &name, &type, &temporary, NULL );
631         if( r != ERROR_SUCCESS )
632             continue;
633         if (info == MSICOLINFO_NAMES)
634             MSI_RecordSetStringW( rec, i+1, name );
635         else
636             msi_set_record_type_string( rec, i+1, type, temporary );
637     }
638     *prec = rec;
639     return ERROR_SUCCESS;
640 }
641 
642 UINT WINAPI MsiViewGetColumnInfo( MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec )
643 {
644     MSIQUERY *query = NULL;
645     MSIRECORD *rec = NULL;
646     UINT r;
647 
648     TRACE( "%lu, %d, %p\n", hView, info, hRec );
649 
650     if( !hRec )
651         return ERROR_INVALID_PARAMETER;
652 
653     if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
654         return ERROR_INVALID_PARAMETER;
655 
656     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
657     if (!query)
658     {
659         struct wire_record *wire_rec = NULL;
660         MSIHANDLE remote;
661 
662         if (!(remote = msi_get_remote(hView)))
663             return ERROR_INVALID_HANDLE;
664 
665         __TRY
666         {
667             r = remote_ViewGetColumnInfo(remote, info, &wire_rec);
668         }
669         __EXCEPT(rpc_filter)
670         {
671             r = GetExceptionCode();
672         }
673         __ENDTRY
674 
675         if (!r)
676         {
677             r = unmarshal_record(wire_rec, hRec);
678             free_remote_record(wire_rec);
679         }
680 
681         return r;
682     }
683 
684     r = MSI_ViewGetColumnInfo( query, info, &rec );
685     if ( r == ERROR_SUCCESS )
686     {
687         *hRec = alloc_msihandle( &rec->hdr );
688         if ( !*hRec )
689             r = ERROR_NOT_ENOUGH_MEMORY;
690         msiobj_release( &rec->hdr );
691     }
692 
693     msiobj_release( &query->hdr );
694 
695     return r;
696 }
697 
698 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
699 {
700     MSIVIEW *view = NULL;
701     UINT r;
702 
703     if ( !query || !rec )
704         return ERROR_INVALID_HANDLE;
705 
706     view = query->view;
707     if ( !view  || !view->ops->modify)
708         return ERROR_FUNCTION_FAILED;
709 
710     if ( mode == MSIMODIFY_UPDATE && rec->cookie != (UINT64)(ULONG_PTR)query )
711         return ERROR_FUNCTION_FAILED;
712 
713     r = view->ops->modify( view, mode, rec, query->row - 1 );
714     if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS)
715         query->row--;
716 
717     return r;
718 }
719 
720 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode, MSIHANDLE hRecord )
721 {
722     MSIQUERY *query = NULL;
723     MSIRECORD *rec = NULL;
724     UINT r = ERROR_FUNCTION_FAILED;
725 
726     TRACE( "%lu, %#x, %lu\n", hView, eModifyMode, hRecord );
727 
728     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
729 
730     if (!rec)
731         return ERROR_INVALID_HANDLE;
732 
733     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
734     if (!query)
735     {
736         struct wire_record *wire_refreshed = NULL;
737         MSIHANDLE remote;
738 
739         if (!(remote = msi_get_remote(hView)))
740             return ERROR_INVALID_HANDLE;
741 
742         __TRY
743         {
744             r = remote_ViewModify(remote, eModifyMode,
745                 (struct wire_record *)&rec->count, &wire_refreshed);
746         }
747         __EXCEPT(rpc_filter)
748         {
749             r = GetExceptionCode();
750         }
751         __ENDTRY
752 
753         if (!r && (eModifyMode == MSIMODIFY_REFRESH || eModifyMode == MSIMODIFY_SEEK))
754         {
755             r = copy_remote_record(wire_refreshed, hRecord);
756             free_remote_record(wire_refreshed);
757         }
758 
759         msiobj_release(&rec->hdr);
760         return r;
761     }
762 
763     r = MSI_ViewModify( query, eModifyMode, rec );
764 
765     msiobj_release( &query->hdr );
766     msiobj_release(&rec->hdr);
767     return r;
768 }
769 
770 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, WCHAR *buffer, DWORD *buflen )
771 {
772     MSIQUERY *query;
773     const WCHAR *column;
774     MSIDBERROR r;
775 
776     TRACE( "%lu, %p, %p\n", handle, buffer, buflen );
777 
778     if (!buflen)
779         return MSIDBERROR_INVALIDARG;
780 
781     if (!(query = msihandle2msiinfo(handle, MSIHANDLETYPE_VIEW)))
782     {
783         WCHAR *remote_column = NULL;
784         MSIHANDLE remote;
785 
786         if (!(remote = msi_get_remote(handle)))
787             return MSIDBERROR_INVALIDARG;
788 
789         if (!*buflen)
790             return MSIDBERROR_FUNCTIONERROR;
791 
792         __TRY
793         {
794             r = remote_ViewGetError(remote, &remote_column);
795         }
796         __EXCEPT(rpc_filter)
797         {
798             r = GetExceptionCode();
799         }
800         __ENDTRY;
801 
802         if (msi_strncpyW(remote_column ? remote_column : L"", -1, buffer, buflen) == ERROR_MORE_DATA)
803             r = MSIDBERROR_MOREDATA;
804 
805         if (remote_column)
806             midl_user_free(remote_column);
807 
808         return r;
809     }
810 
811     if ((r = query->view->error)) column = query->view->error_column;
812     else column = L"";
813 
814     if (msi_strncpyW(column, -1, buffer, buflen) == ERROR_MORE_DATA)
815         r = MSIDBERROR_MOREDATA;
816 
817     msiobj_release( &query->hdr );
818     return r;
819 }
820 
821 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, char *buffer, DWORD *buflen )
822 {
823     MSIQUERY *query;
824     const WCHAR *column;
825     MSIDBERROR r;
826 
827     TRACE( "%lu, %p, %p\n", handle, buffer, buflen );
828 
829     if (!buflen)
830         return MSIDBERROR_INVALIDARG;
831 
832     if (!(query = msihandle2msiinfo(handle, MSIHANDLETYPE_VIEW)))
833     {
834         WCHAR *remote_column = NULL;
835         MSIHANDLE remote;
836 
837         if (!(remote = msi_get_remote(handle)))
838             return MSIDBERROR_INVALIDARG;
839 
840         if (!*buflen)
841             return MSIDBERROR_FUNCTIONERROR;
842 
843         __TRY
844         {
845             r = remote_ViewGetError(remote, &remote_column);
846         }
847         __EXCEPT(rpc_filter)
848         {
849             r = GetExceptionCode();
850         }
851         __ENDTRY;
852 
853         if (msi_strncpyWtoA(remote_column ? remote_column : L"", -1, buffer, buflen, FALSE) == ERROR_MORE_DATA)
854             r = MSIDBERROR_MOREDATA;
855 
856         if (remote_column)
857             midl_user_free(remote_column);
858 
859         return r;
860     }
861 
862     if ((r = query->view->error)) column = query->view->error_column;
863     else column = L"";
864 
865     if (msi_strncpyWtoA(column, -1, buffer, buflen, FALSE) == ERROR_MORE_DATA)
866         r = MSIDBERROR_MOREDATA;
867 
868     msiobj_release( &query->hdr );
869     return r;
870 }
871 
872 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
873 {
874     FIXME("\n");
875     return 0;
876 }
877 
878 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, const WCHAR *transform, int error_cond )
879 {
880     HRESULT hr;
881     UINT ret = ERROR_FUNCTION_FAILED;
882     IStorage *stg;
883     STATSTG stat;
884 
885     TRACE( "%p %s %08x\n", db, debugstr_w(transform), error_cond );
886 
887     if (*transform == ':')
888     {
889         hr = IStorage_OpenStorage( db->storage, transform + 1, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
890         if (FAILED( hr ))
891         {
892             WARN( "failed to open substorage transform %#lx\n", hr );
893             return ERROR_FUNCTION_FAILED;
894         }
895     }
896     else
897     {
898         hr = StgOpenStorage( transform, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg );
899         if (FAILED( hr ))
900         {
901             WARN( "failed to open file transform %#lx\n", hr );
902             return ERROR_FUNCTION_FAILED;
903         }
904     }
905 
906     hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
907     if (FAILED( hr )) goto end;
908     if (!IsEqualGUID( &stat.clsid, &CLSID_MsiTransform )) goto end;
909     if (TRACE_ON( msi )) enum_stream_names( stg );
910 
911     ret = msi_table_apply_transform( db, stg, error_cond );
912 
913 end:
914     IStorage_Release( stg );
915     return ret;
916 }
917 
918 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, const WCHAR *transform, int error_cond )
919 {
920     MSIDATABASE *db;
921     UINT r;
922 
923     if (error_cond & ~MSITRANSFORM_ERROR_VIEWTRANSFORM) FIXME( "ignoring error conditions\n" );
924 
925     if (!(db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE)))
926         return ERROR_INVALID_HANDLE;
927 
928     r = MSI_DatabaseApplyTransformW( db, transform, error_cond );
929     msiobj_release( &db->hdr );
930     return r;
931 }
932 
933 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, const char *transform, int error_cond )
934 {
935     WCHAR *wstr;
936     UINT ret;
937 
938     TRACE( "%lu, %s, %#x\n", hdb, debugstr_a(transform), error_cond );
939 
940     wstr = strdupAtoW( transform );
941     if (transform && !wstr)
942         return ERROR_NOT_ENOUGH_MEMORY;
943 
944     ret = MsiDatabaseApplyTransformW( hdb, wstr, error_cond );
945     msi_free( wstr );
946     return ret;
947 }
948 
949 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, const char *szTransformFile,
950                                            int iReserved1, int iReserved2 )
951 {
952     FIXME( "%lu, %lu, %s, %d, %d\n", hdb, hdbref, debugstr_a(szTransformFile), iReserved1, iReserved2 );
953     return ERROR_CALL_NOT_IMPLEMENTED;
954 }
955 
956 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, const WCHAR *szTransformFile,
957                                            int iReserved1, int iReserved2 )
958 {
959     FIXME( "%lu, %lu, %s, %d, %d\n", hdb, hdbref, debugstr_w(szTransformFile), iReserved1, iReserved2 );
960     return ERROR_CALL_NOT_IMPLEMENTED;
961 }
962 
963 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
964 {
965     MSIDATABASE *db;
966     UINT r;
967 
968     TRACE( "%lu\n", hdb );
969 
970     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
971     if( !db )
972     {
973         MSIHANDLE remote;
974 
975         if (!(remote = msi_get_remote(hdb)))
976             return ERROR_INVALID_HANDLE;
977 
978         WARN("not allowed during a custom action!\n");
979 
980         return ERROR_SUCCESS;
981     }
982 
983     if (db->mode == MSI_OPEN_READONLY)
984     {
985         msiobj_release( &db->hdr );
986         return ERROR_SUCCESS;
987     }
988 
989     /* FIXME: lock the database */
990 
991     r = msi_commit_streams( db );
992     if (r != ERROR_SUCCESS) ERR("Failed to commit streams!\n");
993     else
994     {
995         r = MSI_CommitTables( db );
996         if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
997     }
998 
999     /* FIXME: unlock the database */
1000 
1001     msiobj_release( &db->hdr );
1002 
1003     if (r == ERROR_SUCCESS)
1004     {
1005         msi_free( db->deletefile );
1006         db->deletefile = NULL;
1007     }
1008 
1009     return r;
1010 }
1011 
1012 struct msi_primary_key_record_info
1013 {
1014     DWORD n;
1015     MSIRECORD *rec;
1016 };
1017 
1018 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
1019 {
1020     struct msi_primary_key_record_info *info = param;
1021     LPCWSTR name, table;
1022     DWORD type;
1023 
1024     type = MSI_RecordGetInteger( rec, 4 );
1025     if( type & MSITYPE_KEY )
1026     {
1027         info->n++;
1028         if( info->rec )
1029         {
1030             if ( info->n == 1 )
1031             {
1032                 table = MSI_RecordGetString( rec, 1 );
1033                 MSI_RecordSetStringW( info->rec, 0, table);
1034             }
1035 
1036             name = MSI_RecordGetString( rec, 3 );
1037             MSI_RecordSetStringW( info->rec, info->n, name );
1038         }
1039     }
1040 
1041     return ERROR_SUCCESS;
1042 }
1043 
1044 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
1045                 LPCWSTR table, MSIRECORD **prec )
1046 {
1047     struct msi_primary_key_record_info info;
1048     MSIQUERY *query = NULL;
1049     UINT r;
1050 
1051     if (!TABLE_Exists( db, table ))
1052         return ERROR_INVALID_TABLE;
1053 
1054     r = MSI_OpenQuery( db, &query, L"SELECT * FROM `_Columns` WHERE `Table` = '%s'", table );
1055     if( r != ERROR_SUCCESS )
1056         return r;
1057 
1058     /* count the number of primary key records */
1059     info.n = 0;
1060     info.rec = 0;
1061     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
1062     if( r == ERROR_SUCCESS )
1063     {
1064         TRACE( "found %lu primary keys\n", info.n );
1065 
1066         /* allocate a record and fill in the names of the tables */
1067         info.rec = MSI_CreateRecord( info.n );
1068         info.n = 0;
1069         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
1070         if( r == ERROR_SUCCESS )
1071             *prec = info.rec;
1072         else
1073             msiobj_release( &info.rec->hdr );
1074     }
1075     msiobj_release( &query->hdr );
1076 
1077     return r;
1078 }
1079 
1080 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb, const WCHAR *table, MSIHANDLE *phRec )
1081 {
1082     MSIRECORD *rec = NULL;
1083     MSIDATABASE *db;
1084     UINT r;
1085 
1086     TRACE( "%lu, %s, %p\n", hdb, debugstr_w(table), phRec );
1087 
1088     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
1089     if( !db )
1090     {
1091         struct wire_record *wire_rec = NULL;
1092         MSIHANDLE remote;
1093 
1094         if (!(remote = msi_get_remote(hdb)))
1095             return ERROR_INVALID_HANDLE;
1096 
1097         __TRY
1098         {
1099             r = remote_DatabaseGetPrimaryKeys(remote, table, &wire_rec);
1100         }
1101         __EXCEPT(rpc_filter)
1102         {
1103             r = GetExceptionCode();
1104         }
1105         __ENDTRY
1106 
1107         if (!r)
1108         {
1109             r = unmarshal_record(wire_rec, phRec);
1110             free_remote_record(wire_rec);
1111         }
1112 
1113         return r;
1114     }
1115 
1116     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1117     if( r == ERROR_SUCCESS )
1118     {
1119         *phRec = alloc_msihandle( &rec->hdr );
1120         if (! *phRec)
1121            r = ERROR_NOT_ENOUGH_MEMORY;
1122         msiobj_release( &rec->hdr );
1123     }
1124     msiobj_release( &db->hdr );
1125 
1126     return r;
1127 }
1128 
1129 UINT WINAPI MsiDatabaseGetPrimaryKeysA( MSIHANDLE hdb, const char *table, MSIHANDLE *phRec )
1130 {
1131     WCHAR *szwTable = NULL;
1132     UINT r;
1133 
1134     TRACE( "%lu, %s, %p\n", hdb, debugstr_a(table), phRec );
1135 
1136     if( table )
1137     {
1138         szwTable = strdupAtoW( table );
1139         if( !szwTable )
1140             return ERROR_OUTOFMEMORY;
1141     }
1142     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
1143     msi_free( szwTable );
1144 
1145     return r;
1146 }
1147 
1148 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA( MSIHANDLE hDatabase, const char *szTableName )
1149 {
1150     WCHAR *szwTableName = NULL;
1151     MSICONDITION r;
1152 
1153     TRACE( "%lu, %s\n", hDatabase, debugstr_a(szTableName) );
1154 
1155     if( szTableName )
1156     {
1157         szwTableName = strdupAtoW( szTableName );
1158         if( !szwTableName )
1159             return MSICONDITION_ERROR;
1160     }
1161     r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1162     msi_free( szwTableName );
1163 
1164     return r;
1165 }
1166 
1167 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW( MSIHANDLE hDatabase, const WCHAR *szTableName )
1168 {
1169     MSIDATABASE *db;
1170     MSICONDITION r;
1171 
1172     TRACE( "%lu, %s\n", hDatabase, debugstr_w(szTableName) );
1173 
1174     db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1175     if( !db )
1176     {
1177         MSIHANDLE remote;
1178 
1179         if (!(remote = msi_get_remote(hDatabase)))
1180             return MSICONDITION_ERROR;
1181 
1182         __TRY
1183         {
1184             r = remote_DatabaseIsTablePersistent(remote, szTableName);
1185         }
1186         __EXCEPT(rpc_filter)
1187         {
1188             r = MSICONDITION_ERROR;
1189         }
1190         __ENDTRY
1191 
1192         return r;
1193     }
1194 
1195     r = MSI_DatabaseIsTablePersistent( db, szTableName );
1196 
1197     msiobj_release( &db->hdr );
1198 
1199     return r;
1200 }
1201 
1202 UINT __cdecl s_remote_ViewClose(MSIHANDLE view)
1203 {
1204     return MsiViewClose(view);
1205 }
1206 
1207 UINT __cdecl s_remote_ViewExecute(MSIHANDLE view, struct wire_record *remote_rec)
1208 {
1209     MSIHANDLE rec = 0;
1210     UINT r;
1211 
1212     if ((r = unmarshal_record(remote_rec, &rec)))
1213         return r;
1214 
1215     r = MsiViewExecute(view, rec);
1216 
1217     MsiCloseHandle(rec);
1218     return r;
1219 }
1220 
1221 UINT __cdecl s_remote_ViewFetch(MSIHANDLE view, struct wire_record **rec)
1222 {
1223     MSIHANDLE handle;
1224     UINT r = MsiViewFetch(view, &handle);
1225     *rec = NULL;
1226     if (!r)
1227         *rec = marshal_record(handle);
1228     MsiCloseHandle(handle);
1229     return r;
1230 }
1231 
1232 UINT __cdecl s_remote_ViewGetColumnInfo(MSIHANDLE view, MSICOLINFO info, struct wire_record **rec)
1233 {
1234     MSIHANDLE handle;
1235     UINT r = MsiViewGetColumnInfo(view, info, &handle);
1236     *rec = NULL;
1237     if (!r)
1238         *rec = marshal_record(handle);
1239     MsiCloseHandle(handle);
1240     return r;
1241 }
1242 
1243 MSIDBERROR __cdecl s_remote_ViewGetError(MSIHANDLE view, LPWSTR *column)
1244 {
1245     WCHAR empty[1];
1246     DWORD size = 1;
1247     UINT r;
1248 
1249     r = MsiViewGetErrorW(view, empty, &size);
1250     if (r == MSIDBERROR_MOREDATA)
1251     {
1252         if (!(*column = midl_user_allocate(++size * sizeof(WCHAR))))
1253             return MSIDBERROR_FUNCTIONERROR;
1254         r = MsiViewGetErrorW(view, *column, &size);
1255     }
1256     return r;
1257 }
1258 
1259 UINT __cdecl s_remote_ViewModify(MSIHANDLE view, MSIMODIFY mode,
1260     struct wire_record *remote_rec, struct wire_record **remote_refreshed)
1261 {
1262     MSIHANDLE handle = 0;
1263     UINT r;
1264 
1265     if ((r = unmarshal_record(remote_rec, &handle)))
1266         return r;
1267 
1268     r = MsiViewModify(view, mode, handle);
1269     *remote_refreshed = NULL;
1270     if (!r && (mode == MSIMODIFY_REFRESH || mode == MSIMODIFY_SEEK))
1271         *remote_refreshed = marshal_record(handle);
1272 
1273     MsiCloseHandle(handle);
1274     return r;
1275 }
1276