Added parser template and made AVISplitter use it.
[wine] / dlls / msi / msiquery.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 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     return r;
216 }
217
218 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
219               LPCWSTR szQuery, MSIHANDLE *phView)
220 {
221     MSIDATABASE *db;
222     MSIQUERY *query = NULL;
223     UINT ret;
224
225     TRACE("%s %p\n", debugstr_w(szQuery), phView);
226
227     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
228     if( !db )
229         return ERROR_INVALID_HANDLE;
230
231     ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
232     if( ret == ERROR_SUCCESS )
233     {
234         *phView = alloc_msihandle( &query->hdr );
235         msiobj_release( &query->hdr );
236     }
237     msiobj_release( &db->hdr );
238
239     return ret;
240 }
241
242 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
243 {
244     MSIVIEW *view;
245     MSIRECORD *rec;
246     UINT row_count = 0, col_count = 0, i, ival, ret, type;
247
248     TRACE("%p %p\n", query, prec );
249
250     view = query->view;
251     if( !view )
252         return ERROR_FUNCTION_FAILED;
253
254     ret = view->ops->get_dimensions( view, &row_count, &col_count );
255     if( ret )
256         return ret;
257     if( !col_count )
258         return ERROR_INVALID_PARAMETER;
259
260     if( query->row >= row_count )
261         return ERROR_NO_MORE_ITEMS;
262
263     rec = MSI_CreateRecord( col_count );
264     if( !rec )
265         return ERROR_FUNCTION_FAILED;
266
267     for( i=1; i<=col_count; i++ )
268     {
269         ret = view->ops->get_column_info( view, i, NULL, &type );
270         if( ret )
271         {
272             ERR("Error getting column type for %d\n", i );
273             continue;
274         }
275         if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
276                                                    MSITYPE_NULLABLE)))
277         {
278             ret = view->ops->fetch_int( view, query->row, i, &ival );
279             if( ret )
280             {
281                 ERR("Error fetching data for %d\n", i );
282                 continue;
283             }
284             if( ! (type & MSITYPE_VALID ) )
285                 ERR("Invalid type!\n");
286
287             /* check if it's nul (0) - if so, don't set anything */
288             if( !ival )
289                 continue;
290
291             if( type & MSITYPE_STRING )
292             {
293                 LPWSTR sval;
294
295                 sval = MSI_makestring( query->db, ival );
296                 MSI_RecordSetStringW( rec, i, sval );
297                 HeapFree( GetProcessHeap(), 0, sval );
298             }
299             else
300             {
301                 if( (type & MSI_DATASIZEMASK) == 2 )
302                     MSI_RecordSetInteger( rec, i, ival - (1<<15) );
303                 else
304                     MSI_RecordSetInteger( rec, i, ival - (1<<31) );
305             }
306         }
307         else
308         {
309             IStream *stm = NULL;
310
311             ret = view->ops->fetch_stream( view, query->row, i, &stm );
312             if( ( ret == ERROR_SUCCESS ) && stm )
313             {
314                 MSI_RecordSetIStream( rec, i, stm );
315                 IStream_Release( stm );
316             }
317             else
318                 ERR("failed to get stream\n");
319         }
320     }
321     query->row ++;
322
323     *prec = rec;
324
325     return ERROR_SUCCESS;
326 }
327
328 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
329 {
330     MSIQUERY *query;
331     MSIRECORD *rec = NULL;
332     UINT ret;
333
334     TRACE("%ld %p\n", hView, record);
335
336     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
337     if( !query )
338         return ERROR_INVALID_HANDLE;
339     ret = MSI_ViewFetch( query, &rec );
340     if( ret == ERROR_SUCCESS )
341     {
342         *record = alloc_msihandle( &rec->hdr );
343         msiobj_release( &rec->hdr );
344     }
345     msiobj_release( &query->hdr );
346     return ret;
347 }
348
349 UINT MSI_ViewClose(MSIQUERY *query)
350 {
351     MSIVIEW *view;
352
353     TRACE("%p\n", query );
354
355     view = query->view;
356     if( !view )
357         return ERROR_FUNCTION_FAILED;
358     if( !view->ops->close )
359         return ERROR_FUNCTION_FAILED;
360
361     return view->ops->close( view );
362 }
363
364 UINT WINAPI MsiViewClose(MSIHANDLE hView)
365 {
366     MSIQUERY *query;
367     UINT ret;
368
369     TRACE("%ld\n", hView );
370
371     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
372     if( !query )
373         return ERROR_INVALID_HANDLE;
374
375     ret = MSI_ViewClose( query );
376     msiobj_release( &query->hdr );
377     return ret;
378 }
379
380 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
381 {
382     MSIVIEW *view;
383
384     TRACE("%p %p\n", query, rec);
385
386     view = query->view;
387     if( !view )
388         return ERROR_FUNCTION_FAILED;
389     if( !view->ops->execute )
390         return ERROR_FUNCTION_FAILED;
391     query->row = 0;
392
393     return view->ops->execute( view, rec );
394 }
395
396 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
397 {
398     MSIQUERY *query;
399     MSIRECORD *rec = NULL;
400     UINT ret;
401     
402     TRACE("%ld %ld\n", hView, hRec);
403
404     query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
405     if( !query )
406         return ERROR_INVALID_HANDLE;
407
408     if( hRec )
409     {
410         rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
411         if( !rec )
412         {
413             ret = ERROR_INVALID_HANDLE;
414             goto out;
415         }
416     }
417
418     msiobj_lock( &rec->hdr );
419     ret = MSI_ViewExecute( query, rec );
420     msiobj_unlock( &rec->hdr );
421
422 out:
423     if( query )
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;
434     MSIQUERY *query;
435     MSIHANDLE handle;
436     UINT ret, 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         return ERROR_FUNCTION_FAILED;
448
449     if( !view->ops->get_dimensions )
450         return ERROR_FUNCTION_FAILED;
451
452     ret = view->ops->get_dimensions( view, NULL, &count );
453     if( ret )
454         return ret;
455     if( !count )
456         return ERROR_INVALID_PARAMETER;
457
458     handle = MsiCreateRecord( count );
459     if( !handle )
460         return ERROR_FUNCTION_FAILED;
461
462     for( i=0; i<count; i++ )
463     {
464         name = NULL;
465         ret = view->ops->get_column_info( view, i+1, &name, &type );
466         if( ret != ERROR_SUCCESS )
467             continue;
468         MsiRecordSetStringW( handle, i+1, name );
469         HeapFree( GetProcessHeap(), 0, name );
470     }
471
472     *hRec = handle;
473
474     return ERROR_SUCCESS;
475 }
476
477 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
478                  LPCSTR szTransformFile, int iErrorCond)
479 {
480     FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
481     return ERROR_CALL_NOT_IMPLEMENTED;
482 }
483
484 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
485                  LPCWSTR szTransformFile, int iErrorCond)
486 {
487     FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
488     return ERROR_CALL_NOT_IMPLEMENTED;
489 }
490
491 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
492                  LPCSTR szTransformFile, int iReserved1, int iReserved2 )
493 {
494     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
495            debugstr_a(szTransformFile), iReserved1, iReserved2);
496     return ERROR_CALL_NOT_IMPLEMENTED;
497 }
498
499 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
500                  LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
501 {
502     FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
503            debugstr_w(szTransformFile), iReserved1, iReserved2);
504     return ERROR_CALL_NOT_IMPLEMENTED;
505 }
506
507 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
508 {
509     MSIDATABASE *db;
510     UINT r;
511
512     TRACE("%ld\n", hdb);
513
514     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
515     if( !db )
516         return ERROR_INVALID_HANDLE;
517
518     /* FIXME: lock the database */
519
520     r = MSI_CommitTables( db );
521
522     /* FIXME: unlock the database */
523
524     msiobj_release( &db->hdr );
525
526     return r;
527 }
528
529 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
530                     LPCSTR table, MSIHANDLE* rec)
531 {
532     FIXME("%ld %s %p\n", hdb, debugstr_a(table), rec);
533     return ERROR_CALL_NOT_IMPLEMENTED;
534 }
535
536 UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE hdb,
537                     LPCWSTR table, MSIHANDLE* rec)
538 {
539     FIXME("%ld %s %p\n", hdb, debugstr_w(table), rec);
540     return ERROR_CALL_NOT_IMPLEMENTED;
541 }
542
543 UINT WINAPI MsiViewModify(MSIHANDLE hView, MSIMODIFY eModifyMode, MSIHANDLE
544 hRecord)
545 {
546     FIXME("%ld %x %ld\n",hView, eModifyMode, hRecord);
547     return ERROR_CALL_NOT_IMPLEMENTED;
548 }
549
550 UINT WINAPI MsiDatabaseIsTablePersistentA(
551               MSIHANDLE hDatabase, LPSTR szTableName)
552 {
553     FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
554     return ERROR_CALL_NOT_IMPLEMENTED;
555 }
556
557 UINT WINAPI MsiDatabaseIsTablePersistentW(
558               MSIHANDLE hDatabase, LPWSTR szTableName)
559 {
560     FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
561     return ERROR_CALL_NOT_IMPLEMENTED;
562 }