Reverse the order for deleting the items in resetcontent to correctly
[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     if( query )
427         msiobj_release( &query->hdr );
428     if( rec )
429         msiobj_release( &rec->hdr );
430
431     return ret;
432 }
433
434 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
435 {
436     MSIVIEW *view = NULL;
437     MSIQUERY *query = NULL;
438     MSIRECORD *rec = NULL;
439     UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
440     LPWSTR name;
441
442     TRACE("%ld %d %p\n", hView, info, hRec);
443
444     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
445     if( !query )
446         return ERROR_INVALID_HANDLE;
447
448     view = query->view;
449     if( !view )
450         goto out;
451
452     if( !view->ops->get_dimensions )
453         goto out;
454
455     r = view->ops->get_dimensions( view, NULL, &count );
456     if( r )
457         goto out;
458     if( !count )
459     {
460         r = ERROR_INVALID_PARAMETER;
461         goto out;
462     }
463
464     rec = MSI_CreateRecord( count );
465     if( !rec )
466     {
467         r = ERROR_FUNCTION_FAILED;
468         goto out;
469     }
470
471     for( i=0; i<count; i++ )
472     {
473         name = NULL;
474         r = view->ops->get_column_info( view, i+1, &name, &type );
475         if( r != ERROR_SUCCESS )
476             continue;
477         MSI_RecordSetStringW( rec, i+1, name );
478         HeapFree( GetProcessHeap(), 0, name );
479     }
480
481     *hRec = alloc_msihandle( &rec->hdr );
482
483 out:
484     if( query )
485         msiobj_release( &query->hdr );
486     if( rec )
487         msiobj_release( &rec->hdr );
488
489     return r;
490 }
491
492 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
493                 MSIHANDLE hRecord)
494 {
495     MSIVIEW *view = NULL;
496     MSIQUERY *query = NULL;
497     MSIRECORD *rec = NULL;
498     UINT r = ERROR_FUNCTION_FAILED;
499
500     TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
501
502     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
503     if( !query )
504         return ERROR_INVALID_HANDLE;
505
506     view = query->view;
507     if( !view )
508         goto out;
509
510     if( !view->ops->modify )
511         goto out;
512
513     rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
514     if( !rec )
515     {
516         r = ERROR_INVALID_HANDLE;
517         goto out;
518     }
519
520     r = view->ops->modify( view, eModifyMode, rec );
521
522 out:
523     if( query )
524         msiobj_release( &query->hdr );
525     if( rec )
526         msiobj_release( &rec->hdr );
527
528     return r;
529 }
530
531 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
532                  LPCSTR szTransformFile, int iErrorCond)
533 {
534     FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
535     return ERROR_CALL_NOT_IMPLEMENTED;
536 }
537
538 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
539                  LPCWSTR szTransformFile, int iErrorCond)
540 {
541     FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
542     return ERROR_CALL_NOT_IMPLEMENTED;
543 }
544
545 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
546                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
547 {
548     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
549            debugstr_a(szTransformFile), iReserved1, iReserved2);
550     return ERROR_CALL_NOT_IMPLEMENTED;
551 }
552
553 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
554                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
555 {
556     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
557            debugstr_w(szTransformFile), iReserved1, iReserved2);
558     return ERROR_CALL_NOT_IMPLEMENTED;
559 }
560
561 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
562 {
563     MSIDATABASE *db;
564     UINT r;
565
566     TRACE("%ld\n", hdb);
567
568     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
569     if( !db )
570         return ERROR_INVALID_HANDLE;
571
572     /* FIXME: lock the database */
573
574     r = MSI_CommitTables( db );
575
576     /* FIXME: unlock the database */
577
578     msiobj_release( &db->hdr );
579
580     return r;
581 }
582
583 struct msi_primary_key_record_info
584 {
585     DWORD n;
586     MSIRECORD *rec;
587 };
588
589 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
590 {
591     struct msi_primary_key_record_info *info = param;
592     LPCWSTR name;
593     DWORD type;
594
595     type = MSI_RecordGetInteger( rec, 4 );
596     if( type & MSITYPE_KEY )
597     {
598         info->n++;
599         if( info->rec )
600         {
601             name = MSI_RecordGetString( rec, 3 );
602             MSI_RecordSetStringW( info->rec, info->n, name );
603         }
604     }
605
606     return ERROR_SUCCESS;
607 }
608
609 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
610                 LPCWSTR table, MSIRECORD **prec )
611 {
612     static const WCHAR sql[] = {
613         's','e','l','e','c','t',' ','*',' ',
614         'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
615         'w','h','e','r','e',' ',
616         '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
617     struct msi_primary_key_record_info info;
618     MSIQUERY *query = NULL;
619     MSIVIEW *view;
620     UINT r;
621     
622     r = MSI_OpenQuery( db, &query, sql, table );
623     if( r != ERROR_SUCCESS )
624         return r;
625
626     view = query->view;
627
628     /* count the number of primary key records */
629     info.n = 0;
630     info.rec = 0;
631     r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
632     if( r == ERROR_SUCCESS )
633     {
634         TRACE("Found %ld primary keys\n", info.n );
635
636         /* allocate a record and fill in the names of the tables */
637         info.rec = MSI_CreateRecord( info.n );
638         info.n = 0;
639         r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
640         if( r == ERROR_SUCCESS )
641             *prec = info.rec;
642         else
643             msiobj_release( &info.rec->hdr );
644     }
645     msiobj_release( &query->hdr );
646
647     return r;
648 }
649
650 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
651                     LPCWSTR table, MSIHANDLE* phRec )
652 {
653     MSIRECORD *rec = NULL;
654     MSIDATABASE *db;
655     UINT r;
656
657     TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
658
659     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
660     if( !db )
661         return ERROR_INVALID_HANDLE;
662
663     r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
664     if( r == ERROR_SUCCESS )
665     {
666         *phRec = alloc_msihandle( &rec->hdr );
667         msiobj_release( &rec->hdr );
668     }
669     msiobj_release( &db->hdr );
670
671     return r;
672 }
673
674 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
675                     LPCSTR table, MSIHANDLE* phRec)
676 {
677     LPWSTR szwTable = NULL;
678     DWORD len;
679     UINT r;
680
681     TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
682
683     if( table )
684     {
685         len = MultiByteToWideChar( CP_ACP, 0, table, -1, NULL, 0 );
686         szwTable = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
687         MultiByteToWideChar( CP_ACP, 0, table, -1, szwTable, len );
688     }
689     r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
690     HeapFree( GetProcessHeap(), 0, szwTable );
691
692     return r;
693 }
694
695 UINT WINAPI MsiDatabaseIsTablePersistentA(
696               MSIHANDLE hDatabase, LPSTR szTableName)
697 {
698     FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
699     return ERROR_CALL_NOT_IMPLEMENTED;
700 }
701
702 UINT WINAPI MsiDatabaseIsTablePersistentW(
703               MSIHANDLE hDatabase, LPWSTR szTableName)
704 {
705     FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
706     return ERROR_CALL_NOT_IMPLEMENTED;
707 }