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