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