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