Replace instances of HeapAlloc/MultiByteToWideChar with the internally
[wine] / dlls / msi / msiquery.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2005 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "msipriv.h"
35 #include "winnls.h"
36
37 #include "query.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40
41 void MSI_CloseView( MSIOBJECTHDR *arg )
42 {
43     MSIQUERY *query = (MSIQUERY*) arg;
44
45     if( query->view && query->view->ops->delete )
46         query->view->ops->delete( query->view );
47     msiobj_release( &query->db->hdr );
48 }
49
50 UINT VIEW_find_column( MSIVIEW *table, LPWSTR name, UINT *n )
51 {
52     LPWSTR col_name;
53     UINT i, count, r;
54
55     r = table->ops->get_dimensions( table, NULL, &count );
56     if( r != ERROR_SUCCESS )
57         return r;
58
59     for( i=1; i<=count; i++ )
60     {
61         INT x;
62
63         col_name = NULL;
64         r = table->ops->get_column_info( table, i, &col_name, NULL );
65         if( r != ERROR_SUCCESS )
66             return r;
67         x = lstrcmpW( name, col_name );
68         HeapFree( GetProcessHeap(), 0, col_name );
69         if( !x )
70         {
71             *n = i;
72             return ERROR_SUCCESS;
73         }
74     }
75
76     return ERROR_INVALID_PARAMETER;
77 }
78
79 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
80               LPCSTR szQuery, MSIHANDLE *phView)
81 {
82     UINT r;
83     LPWSTR szwQuery;
84
85     TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
86
87     if( szQuery )
88     {
89         szwQuery = strdupAtoW( szQuery );
90         if( !szwQuery )
91             return ERROR_FUNCTION_FAILED;
92     }
93     else
94         szwQuery = NULL;
95
96     r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
97
98     HeapFree( GetProcessHeap(), 0, szwQuery );
99     return r;
100 }
101
102 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
103               LPCWSTR szQuery, MSIQUERY **pView)
104 {
105     MSIQUERY *query;
106     UINT r;
107
108     TRACE("%s %p\n", debugstr_w(szQuery), pView);
109
110     if( !szQuery)
111         return ERROR_INVALID_PARAMETER;
112
113     /* pre allocate a handle to hold a pointer to the view */
114     query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
115                               MSI_CloseView );
116     if( !query )
117         return ERROR_FUNCTION_FAILED;
118
119     msiobj_addref( &db->hdr );
120     query->row = 0;
121     query->db = db;
122     query->view = NULL;
123
124     r = MSI_ParseSQL( db, szQuery, &query->view );
125     if( r == ERROR_SUCCESS )
126     {
127         msiobj_addref( &query->hdr );
128         *pView = query;
129     }
130
131     msiobj_release( &query->hdr );
132     return r;
133 }
134
135 UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
136 {
137     LPWSTR szQuery;
138     LPCWSTR p;
139     UINT sz, rc;
140     va_list va;
141
142     /* figure out how much space we need to allocate */
143     va_start(va, fmt);
144     sz = strlenW(fmt) + 1;
145     p = fmt;
146     while (*p)
147     {
148         p = strchrW(p, '%');
149         if (!p)
150             break;
151         p++;
152         switch (*p)
153         {
154         case 's':  /* a string */
155             sz += strlenW(va_arg(va,LPCWSTR));
156             break;
157         case 'd':
158         case 'i':  /* an integer -2147483648 seems to be longest */
159             sz += 3*sizeof(int);
160             (void)va_arg(va,int);
161             break;
162         case '%':  /* a single % - leave it alone */
163             break;
164         default:
165             FIXME("Unhandled character type %c\n",*p);
166         }
167         p++;
168     }
169     va_end(va);
170
171     /* construct the string */
172     szQuery = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
173     va_start(va, fmt);
174     vsnprintfW(szQuery, sz, fmt, va);
175     va_end(va);
176
177     /* perform the query */
178     rc = MSI_DatabaseOpenViewW(db, szQuery, view);
179     HeapFree(GetProcessHeap(), 0, szQuery);
180     return rc;
181 }
182
183 UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
184                          record_func func, LPVOID param )
185 {
186     MSIRECORD *rec = NULL;
187     UINT r, n = 0, max = 0;
188
189     r = MSI_ViewExecute( view, NULL );
190     if( r != ERROR_SUCCESS )
191         return r;
192
193     if( count )
194         max = *count;
195
196     /* iterate a query */
197     for( n = 0; (max == 0) || (n < max); n++ )
198     {
199         r = MSI_ViewFetch( view, &rec );
200         if( r != ERROR_SUCCESS )
201             break;
202         r = func( rec, param );
203         msiobj_release( &rec->hdr );
204         if( r != ERROR_SUCCESS )
205             break;
206     }
207
208     MSI_ViewClose( view );
209
210     if( count )
211         *count = n;
212
213     if( r == ERROR_NO_MORE_ITEMS )
214         r = ERROR_SUCCESS;
215
216     return r;
217 }
218
219 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
220               LPCWSTR szQuery, MSIHANDLE *phView)
221 {
222     MSIDATABASE *db;
223     MSIQUERY *query = NULL;
224     UINT ret;
225
226     TRACE("%s %p\n", debugstr_w(szQuery), phView);
227
228     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
229     if( !db )
230         return ERROR_INVALID_HANDLE;
231
232     ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
233     if( ret == ERROR_SUCCESS )
234     {
235         *phView = alloc_msihandle( &query->hdr );
236         msiobj_release( &query->hdr );
237     }
238     msiobj_release( &db->hdr );
239
240     return ret;
241 }
242
243 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
244 {
245     MSIVIEW *view;
246     MSIRECORD *rec;
247     UINT row_count = 0, col_count = 0, i, ival, ret, type;
248
249     TRACE("%p %p\n", query, prec );
250
251     view = query->view;
252     if( !view )
253         return ERROR_FUNCTION_FAILED;
254
255     ret = view->ops->get_dimensions( view, &row_count, &col_count );
256     if( ret )
257         return ret;
258     if( !col_count )
259         return ERROR_INVALID_PARAMETER;
260
261     if( query->row >= row_count )
262         return ERROR_NO_MORE_ITEMS;
263
264     rec = MSI_CreateRecord( col_count );
265     if( !rec )
266         return ERROR_FUNCTION_FAILED;
267
268     for( i=1; i<=col_count; i++ )
269     {
270         ret = view->ops->get_column_info( view, i, NULL, &type );
271         if( ret )
272         {
273             ERR("Error getting column type for %d\n", i );
274             continue;
275         }
276         if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
277                                                    MSITYPE_NULLABLE)))
278         {
279             ret = view->ops->fetch_int( view, query->row, i, &ival );
280             if( ret )
281             {
282                 ERR("Error fetching data for %d\n", i );
283                 continue;
284             }
285             if( ! (type & MSITYPE_VALID ) )
286                 ERR("Invalid type!\n");
287
288             /* check if it's nul (0) - if so, don't set anything */
289             if( !ival )
290                 continue;
291
292             if( type & MSITYPE_STRING )
293             {
294                 LPWSTR sval;
295
296                 sval = MSI_makestring( query->db, ival );
297                 MSI_RecordSetStringW( rec, i, sval );
298                 HeapFree( GetProcessHeap(), 0, sval );
299             }
300             else
301             {
302                 if( (type & MSI_DATASIZEMASK) == 2 )
303                     MSI_RecordSetInteger( rec, i, ival - (1<<15) );
304                 else
305                     MSI_RecordSetInteger( rec, i, ival - (1<<31) );
306             }
307         }
308         else
309         {
310             IStream *stm = NULL;
311
312             ret = view->ops->fetch_stream( view, query->row, i, &stm );
313             if( ( ret == ERROR_SUCCESS ) && stm )
314             {
315                 MSI_RecordSetIStream( rec, i, stm );
316                 IStream_Release( stm );
317             }
318             else
319                 ERR("failed to get stream\n");
320         }
321     }
322     query->row ++;
323
324     *prec = rec;
325
326     return ERROR_SUCCESS;
327 }
328
329 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
330 {
331     MSIQUERY *query;
332     MSIRECORD *rec = NULL;
333     UINT ret;
334
335     TRACE("%ld %p\n", hView, record);
336
337     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
338     if( !query )
339         return ERROR_INVALID_HANDLE;
340     ret = MSI_ViewFetch( query, &rec );
341     if( ret == ERROR_SUCCESS )
342     {
343         *record = alloc_msihandle( &rec->hdr );
344         msiobj_release( &rec->hdr );
345     }
346     msiobj_release( &query->hdr );
347     return ret;
348 }
349
350 UINT MSI_ViewClose(MSIQUERY *query)
351 {
352     MSIVIEW *view;
353
354     TRACE("%p\n", query );
355
356     view = query->view;
357     if( !view )
358         return ERROR_FUNCTION_FAILED;
359     if( !view->ops->close )
360         return ERROR_FUNCTION_FAILED;
361
362     return view->ops->close( view );
363 }
364
365 UINT WINAPI MsiViewClose(MSIHANDLE hView)
366 {
367     MSIQUERY *query;
368     UINT ret;
369
370     TRACE("%ld\n", hView );
371
372     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
373     if( !query )
374         return ERROR_INVALID_HANDLE;
375
376     ret = MSI_ViewClose( query );
377     msiobj_release( &query->hdr );
378     return ret;
379 }
380
381 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
382 {
383     MSIVIEW *view;
384
385     TRACE("%p %p\n", query, rec);
386
387     view = query->view;
388     if( !view )
389         return ERROR_FUNCTION_FAILED;
390     if( !view->ops->execute )
391         return ERROR_FUNCTION_FAILED;
392     query->row = 0;
393
394     return view->ops->execute( view, rec );
395 }
396
397 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
398 {
399     MSIQUERY *query;
400     MSIRECORD *rec = NULL;
401     UINT ret;
402     
403     TRACE("%ld %ld\n", hView, hRec);
404
405     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
406     if( !query )
407         return ERROR_INVALID_HANDLE;
408
409     if( hRec )
410     {
411         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
412         if( !rec )
413         {
414             ret = ERROR_INVALID_HANDLE;
415             goto out;
416         }
417     }
418
419     msiobj_lock( &rec->hdr );
420     ret = MSI_ViewExecute( query, rec );
421     msiobj_unlock( &rec->hdr );
422
423 out:
424     msiobj_release( &query->hdr );
425     if( rec )
426         msiobj_release( &rec->hdr );
427
428     return ret;
429 }
430
431 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
432 {
433     MSIVIEW *view = NULL;
434     MSIQUERY *query = NULL;
435     MSIRECORD *rec = NULL;
436     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
437     LPWSTR name;
438
439     TRACE("%ld %d %p\n", hView, info, hRec);
440
441     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
442     if( !query )
443         return ERROR_INVALID_HANDLE;
444
445     view = query->view;
446     if( !view )
447         goto out;
448
449     if( !view->ops->get_dimensions )
450         goto out;
451
452     r = view->ops->get_dimensions( view, NULL, &count );
453     if( r )
454         goto out;
455     if( !count )
456     {
457         r = ERROR_INVALID_PARAMETER;
458         goto out;
459     }
460
461     rec = MSI_CreateRecord( count );
462     if( !rec )
463     {
464         r = ERROR_FUNCTION_FAILED;
465         goto out;
466     }
467
468     for( i=0; i<count; i++ )
469     {
470         name = NULL;
471         r = view->ops->get_column_info( view, i+1, &name, &type );
472         if( r != ERROR_SUCCESS )
473             continue;
474         MSI_RecordSetStringW( rec, i+1, name );
475         HeapFree( GetProcessHeap(), 0, name );
476     }
477
478     *hRec = alloc_msihandle( &rec->hdr );
479
480 out:
481     msiobj_release( &query->hdr );
482     if( rec )
483         msiobj_release( &rec->hdr );
484
485     return r;
486 }
487
488 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
489                 MSIHANDLE hRecord)
490 {
491     MSIVIEW *view = NULL;
492     MSIQUERY *query = NULL;
493     MSIRECORD *rec = NULL;
494     UINT r = ERROR_FUNCTION_FAILED;
495
496     TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
497
498     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
499     if( !query )
500         return ERROR_INVALID_HANDLE;
501
502     view = query->view;
503     if( !view )
504         goto out;
505
506     if( !view->ops->modify )
507         goto out;
508
509     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
510     if( !rec )
511     {
512         r = ERROR_INVALID_HANDLE;
513         goto out;
514     }
515
516     r = view->ops->modify( view, eModifyMode, rec );
517
518 out:
519     msiobj_release( &query->hdr );
520     if( rec )
521         msiobj_release( &rec->hdr );
522
523     return r;
524 }
525
526 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
527                  LPCSTR szTransformFile, int iErrorCond)
528 {
529     FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
530     return ERROR_CALL_NOT_IMPLEMENTED;
531 }
532
533 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
534                  LPCWSTR szTransformFile, int iErrorCond)
535 {
536     FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
537     return ERROR_CALL_NOT_IMPLEMENTED;
538 }
539
540 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
541                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
542 {
543     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
544            debugstr_a(szTransformFile), iReserved1, iReserved2);
545     return ERROR_CALL_NOT_IMPLEMENTED;
546 }
547
548 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
549                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
550 {
551     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
552            debugstr_w(szTransformFile), iReserved1, iReserved2);
553     return ERROR_CALL_NOT_IMPLEMENTED;
554 }
555
556 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
557 {
558     MSIDATABASE *db;
559     UINT r;
560
561     TRACE("%ld\n", hdb);
562
563     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
564     if( !db )
565         return ERROR_INVALID_HANDLE;
566
567     /* FIXME: lock the database */
568
569     r = MSI_CommitTables( db );
570
571     /* FIXME: unlock the database */
572
573     msiobj_release( &db->hdr );
574
575     return r;
576 }
577
578 struct msi_primary_key_record_info
579 {
580     DWORD n;
581     MSIRECORD *rec;
582 };
583
584 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
585 {
586     struct msi_primary_key_record_info *info = param;
587     LPCWSTR name;
588     DWORD type;
589
590     type = MSI_RecordGetInteger( rec, 4 );
591     if( type & MSITYPE_KEY )
592     {
593         info->n++;
594         if( info->rec )
595         {
596             name = MSI_RecordGetString( rec, 3 );
597             MSI_RecordSetStringW( info->rec, info->n, name );
598         }
599     }
600
601     return ERROR_SUCCESS;
602 }
603
604 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
605                 LPCWSTR table, MSIRECORD **prec )
606 {
607     static const WCHAR sql[] = {
608         's','e','l','e','c','t',' ','*',' ',
609         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
610         'w','h','e','r','e',' ',
611         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
612     struct msi_primary_key_record_info info;
613     MSIQUERY *query = NULL;
614     MSIVIEW *view;
615     UINT r;
616     
617     r = MSI_OpenQuery( db, &query, sql, table );
618     if( r != ERROR_SUCCESS )
619         return r;
620
621     view = query->view;
622
623     /* count the number of primary key records */
624     info.n = 0;
625     info.rec = 0;
626     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
627     if( r == ERROR_SUCCESS )
628     {
629         TRACE("Found %ld primary keys\n", info.n );
630
631         /* allocate a record and fill in the names of the tables */
632         info.rec = MSI_CreateRecord( info.n );
633         info.n = 0;
634         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
635         if( r == ERROR_SUCCESS )
636             *prec = info.rec;
637         else
638             msiobj_release( &info.rec->hdr );
639     }
640     msiobj_release( &query->hdr );
641
642     return r;
643 }
644
645 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
646                     LPCWSTR table, MSIHANDLE* phRec )
647 {
648     MSIRECORD *rec = NULL;
649     MSIDATABASE *db;
650     UINT r;
651
652     TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
653
654     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
655     if( !db )
656         return ERROR_INVALID_HANDLE;
657
658     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
659     if( r == ERROR_SUCCESS )
660     {
661         *phRec = alloc_msihandle( &rec->hdr );
662         msiobj_release( &rec->hdr );
663     }
664     msiobj_release( &db->hdr );
665
666     return r;
667 }
668
669 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
670                     LPCSTR table, MSIHANDLE* phRec)
671 {
672     LPWSTR szwTable = NULL;
673     UINT r;
674
675     TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
676
677     if( table )
678     {
679         szwTable = strdupAtoW( table );
680         if( !szwTable )
681             return ERROR_OUTOFMEMORY;
682     }
683     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
684     HeapFree( GetProcessHeap(), 0, szwTable );
685
686     return r;
687 }
688
689 UINT WINAPI MsiDatabaseIsTablePersistentA(
690               MSIHANDLE hDatabase, LPSTR szTableName)
691 {
692     FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
693     return ERROR_CALL_NOT_IMPLEMENTED;
694 }
695
696 UINT WINAPI MsiDatabaseIsTablePersistentW(
697               MSIHANDLE hDatabase, LPWSTR szTableName)
698 {
699     FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
700     return ERROR_CALL_NOT_IMPLEMENTED;
701 }