xref: /reactos/dll/win32/msi/select.c (revision f4be6dc3)
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., 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 "msi.h"
30 #include "msiquery.h"
31 #include "objbase.h"
32 #include "objidl.h"
33 #include "msipriv.h"
34 #include "winnls.h"
35 
36 #include "query.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
39 
40 
41 /* below is the query interface to a table */
42 
43 struct select_view
44 {
45     MSIVIEW        view;
46     MSIDATABASE   *db;
47     MSIVIEW       *table;
48     UINT           num_cols;
49     UINT           max_cols;
50     UINT           cols[1];
51 };
52 
translate_record(struct select_view * sv,MSIRECORD * in,MSIRECORD ** out)53 static UINT translate_record( struct select_view *sv, MSIRECORD *in, MSIRECORD **out )
54 {
55     UINT r, col_count, i;
56     MSIRECORD *object;
57 
58     if ((r = sv->table->ops->get_dimensions( sv->table, NULL, &col_count )))
59         return r;
60 
61     if (!(object = MSI_CreateRecord( col_count )))
62         return ERROR_OUTOFMEMORY;
63 
64     for (i = 0; i < sv->num_cols; i++)
65     {
66         if ((r = MSI_RecordCopyField( in, i + 1, object, sv->cols[i] )))
67         {
68             msiobj_release( &object->hdr );
69             return r;
70         }
71     }
72 
73     *out = object;
74     return ERROR_SUCCESS;
75 }
76 
SELECT_fetch_int(struct tagMSIVIEW * view,UINT row,UINT col,UINT * val)77 static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
78 {
79     struct select_view *sv = (struct select_view *)view;
80 
81     TRACE("%p %d %d %p\n", sv, row, col, val );
82 
83     if( !sv->table )
84          return ERROR_FUNCTION_FAILED;
85 
86     if( !col || col > sv->num_cols )
87          return ERROR_FUNCTION_FAILED;
88 
89     col = sv->cols[ col - 1 ];
90     if( !col )
91     {
92         *val = 0;
93         return ERROR_SUCCESS;
94     }
95     return sv->table->ops->fetch_int( sv->table, row, col, val );
96 }
97 
SELECT_fetch_stream(struct tagMSIVIEW * view,UINT row,UINT col,IStream ** stm)98 static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
99 {
100     struct select_view *sv = (struct select_view *)view;
101 
102     TRACE("%p %d %d %p\n", sv, row, col, stm );
103 
104     if( !sv->table )
105          return ERROR_FUNCTION_FAILED;
106 
107     if( !col || col > sv->num_cols )
108          return ERROR_FUNCTION_FAILED;
109 
110     col = sv->cols[ col - 1 ];
111     if( !col )
112     {
113         *stm = NULL;
114         return ERROR_SUCCESS;
115     }
116     return sv->table->ops->fetch_stream( sv->table, row, col, stm );
117 }
118 
SELECT_set_row(struct tagMSIVIEW * view,UINT row,MSIRECORD * rec,UINT mask)119 static UINT SELECT_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
120 {
121     struct select_view *sv = (struct select_view *)view;
122     UINT i, expanded_mask = 0, r = ERROR_SUCCESS, col_count = 0;
123     MSIRECORD *expanded;
124 
125     TRACE("%p %d %p %08x\n", sv, row, rec, mask );
126 
127     if ( !sv->table )
128          return ERROR_FUNCTION_FAILED;
129 
130     /* test if any of the mask bits are invalid */
131     if ( mask >= (1<<sv->num_cols) )
132         return ERROR_INVALID_PARAMETER;
133 
134     /* find the number of columns in the table below */
135     r = sv->table->ops->get_dimensions( sv->table, NULL, &col_count );
136     if( r )
137         return r;
138 
139     /* expand the record to the right size for the underlying table */
140     expanded = MSI_CreateRecord( col_count );
141     if ( !expanded )
142         return ERROR_FUNCTION_FAILED;
143 
144     /* move the right fields across */
145     for ( i=0; i<sv->num_cols; i++ )
146     {
147         r = MSI_RecordCopyField( rec, i+1, expanded, sv->cols[ i ] );
148         if (r != ERROR_SUCCESS)
149             break;
150         expanded_mask |= (1<<(sv->cols[i]-1));
151     }
152 
153     /* set the row in the underlying table */
154     if (r == ERROR_SUCCESS)
155         r = sv->table->ops->set_row( sv->table, row, expanded, expanded_mask );
156 
157     msiobj_release( &expanded->hdr );
158     return r;
159 }
160 
SELECT_insert_row(struct tagMSIVIEW * view,MSIRECORD * record,UINT row,BOOL temporary)161 static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, UINT row, BOOL temporary )
162 {
163     struct select_view *sv = (struct select_view *)view;
164     UINT table_cols, r;
165     MSIRECORD *outrec;
166 
167     TRACE("%p %p\n", sv, record );
168 
169     if ( !sv->table )
170         return ERROR_FUNCTION_FAILED;
171 
172     /* rearrange the record to suit the table */
173     r = sv->table->ops->get_dimensions( sv->table, NULL, &table_cols );
174     if (r != ERROR_SUCCESS)
175         return r;
176 
177     if ((r = translate_record( sv, record, &outrec )))
178         return r;
179 
180     r = sv->table->ops->insert_row( sv->table, outrec, row, temporary );
181 
182     msiobj_release( &outrec->hdr );
183     return r;
184 }
185 
SELECT_execute(struct tagMSIVIEW * view,MSIRECORD * record)186 static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
187 {
188     struct select_view *sv = (struct select_view *)view;
189 
190     TRACE("%p %p\n", sv, record);
191 
192     if( !sv->table )
193          return ERROR_FUNCTION_FAILED;
194 
195     return sv->table->ops->execute( sv->table, record );
196 }
197 
SELECT_close(struct tagMSIVIEW * view)198 static UINT SELECT_close( struct tagMSIVIEW *view )
199 {
200     struct select_view *sv = (struct select_view *)view;
201 
202     TRACE("%p\n", sv );
203 
204     if( !sv->table )
205          return ERROR_FUNCTION_FAILED;
206 
207     return sv->table->ops->close( sv->table );
208 }
209 
SELECT_get_dimensions(struct tagMSIVIEW * view,UINT * rows,UINT * cols)210 static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
211 {
212     struct select_view *sv = (struct select_view *)view;
213 
214     TRACE("%p %p %p\n", sv, rows, cols );
215 
216     if( !sv->table )
217          return ERROR_FUNCTION_FAILED;
218 
219     if( cols )
220         *cols = sv->num_cols;
221 
222     return sv->table->ops->get_dimensions( sv->table, rows, NULL );
223 }
224 
SELECT_get_column_info(struct tagMSIVIEW * view,UINT n,LPCWSTR * name,UINT * type,BOOL * temporary,LPCWSTR * table_name)225 static UINT SELECT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
226                                     UINT *type, BOOL *temporary, LPCWSTR *table_name )
227 {
228     struct select_view *sv = (struct select_view *)view;
229 
230     TRACE("%p %d %p %p %p %p\n", sv, n, name, type, temporary, table_name );
231 
232     if( !sv->table )
233          return ERROR_FUNCTION_FAILED;
234 
235     if( !n || n > sv->num_cols )
236          return ERROR_FUNCTION_FAILED;
237 
238     n = sv->cols[ n - 1 ];
239     if( !n )
240     {
241         if (name) *name = L"";
242         if (type) *type = MSITYPE_UNKNOWN | MSITYPE_VALID;
243         if (temporary) *temporary = FALSE;
244         if (table_name) *table_name = L"";
245         return ERROR_SUCCESS;
246     }
247     return sv->table->ops->get_column_info( sv->table, n, name,
248                                             type, temporary, table_name );
249 }
250 
msi_select_update(MSIVIEW * view,MSIRECORD * rec,UINT row)251 UINT msi_select_update(MSIVIEW *view, MSIRECORD *rec, UINT row)
252 {
253     struct select_view *sv = (struct select_view *)view;
254     UINT r, i, col, type, val;
255     IStream *stream;
256     LPCWSTR str;
257 
258     for (i = 0; i < sv->num_cols; i++)
259     {
260         col = sv->cols[i];
261 
262         r = SELECT_get_column_info(view, i + 1, NULL, &type, NULL, NULL);
263         if (r != ERROR_SUCCESS)
264         {
265             ERR("Failed to get column information: %d\n", r);
266             return r;
267         }
268 
269         if (MSITYPE_IS_BINARY(type))
270         {
271             if (MSI_RecordGetIStream(rec, i + 1, &stream))
272                 return ERROR_FUNCTION_FAILED;
273             r = sv->table->ops->set_stream(sv->table, row, col, stream);
274             IStream_Release(stream);
275         }
276         else if (type & MSITYPE_STRING)
277         {
278             int len;
279             str = msi_record_get_string(rec, i + 1, &len);
280             r = sv->table->ops->set_string(sv->table, row, col, str, len);
281         }
282         else
283         {
284             val = MSI_RecordGetInteger(rec, i + 1);
285             r = sv->table->ops->set_int(sv->table, row, col, val);
286         }
287 
288         if (r != ERROR_SUCCESS)
289         {
290             ERR("Failed to modify record: %d\n", r);
291             return r;
292         }
293     }
294 
295     return ERROR_SUCCESS;
296 }
297 
SELECT_modify(struct tagMSIVIEW * view,MSIMODIFY mode,MSIRECORD * rec,UINT row)298 static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY mode,
299                            MSIRECORD *rec, UINT row )
300 {
301     struct select_view *sv = (struct select_view *)view;
302     MSIRECORD *table_rec;
303     UINT r;
304 
305     TRACE("view %p, mode %d, rec %p, row %u.\n", view, mode, rec, row);
306 
307     if( !sv->table )
308          return ERROR_FUNCTION_FAILED;
309 
310     /* Tests demonstrate that UPDATE only affects the columns selected and that
311      * others are left unchanged; however, ASSIGN overwrites unselected columns
312      * to NULL. Similarly, MERGE matches all unselected columns as NULL rather
313      * than just ignoring them. */
314 
315     switch (mode)
316     {
317     case MSIMODIFY_REFRESH:
318         return msi_view_refresh_row(sv->db, view, row, rec);
319     case MSIMODIFY_UPDATE:
320         return msi_select_update(view, rec, row);
321     case MSIMODIFY_INSERT:
322     case MSIMODIFY_ASSIGN:
323     case MSIMODIFY_MERGE:
324     case MSIMODIFY_INSERT_TEMPORARY:
325     case MSIMODIFY_VALIDATE_NEW:
326         if ((r = translate_record( sv, rec, &table_rec )))
327             return r;
328 
329         r = sv->table->ops->modify( sv->table, mode, table_rec, row );
330         msiobj_release( &table_rec->hdr );
331         return r;
332     case MSIMODIFY_DELETE:
333         return sv->table->ops->modify( sv->table, mode, rec, row );
334     default:
335         FIXME("unhandled mode %d\n", mode);
336         return ERROR_FUNCTION_FAILED;
337     }
338 }
339 
SELECT_delete(struct tagMSIVIEW * view)340 static UINT SELECT_delete( struct tagMSIVIEW *view )
341 {
342     struct select_view *sv = (struct select_view *)view;
343 
344     TRACE("%p\n", sv );
345 
346     if( sv->table )
347         sv->table->ops->delete( sv->table );
348     sv->table = NULL;
349 
350     free( sv );
351 
352     return ERROR_SUCCESS;
353 }
354 
355 static const MSIVIEWOPS select_ops =
356 {
357     SELECT_fetch_int,
358     SELECT_fetch_stream,
359     NULL,
360     NULL,
361     NULL,
362     SELECT_set_row,
363     SELECT_insert_row,
364     NULL,
365     SELECT_execute,
366     SELECT_close,
367     SELECT_get_dimensions,
368     SELECT_get_column_info,
369     SELECT_modify,
370     SELECT_delete,
371     NULL,
372     NULL,
373     NULL,
374     NULL,
375     NULL,
376 };
377 
SELECT_AddColumn(struct select_view * sv,const WCHAR * name,const WCHAR * table_name)378 static UINT SELECT_AddColumn( struct select_view *sv, const WCHAR *name, const WCHAR *table_name )
379 {
380     UINT r, n;
381     MSIVIEW *table;
382 
383     TRACE("%p adding %s.%s\n", sv, debugstr_w( table_name ),
384           debugstr_w( name ));
385 
386     if( sv->view.ops != &select_ops )
387         return ERROR_FUNCTION_FAILED;
388 
389     table = sv->table;
390     if( !table )
391         return ERROR_FUNCTION_FAILED;
392     if( !table->ops->get_dimensions )
393         return ERROR_FUNCTION_FAILED;
394     if( !table->ops->get_column_info )
395         return ERROR_FUNCTION_FAILED;
396 
397     if( sv->num_cols >= sv->max_cols )
398         return ERROR_FUNCTION_FAILED;
399 
400     if ( !name[0] ) n = 0;
401     else
402     {
403         r = VIEW_find_column( table, name, table_name, &n );
404         if( r != ERROR_SUCCESS )
405             return r;
406     }
407 
408     sv->cols[sv->num_cols] = n;
409     TRACE("Translating column %s from %d -> %d\n",
410           debugstr_w( name ), sv->num_cols, n);
411 
412     sv->num_cols++;
413 
414     return ERROR_SUCCESS;
415 }
416 
select_count_columns(const column_info * col)417 static int select_count_columns( const column_info *col )
418 {
419     int n;
420     for (n = 0; col; col = col->next)
421         n++;
422     return n;
423 }
424 
SELECT_CreateView(MSIDATABASE * db,MSIVIEW ** view,MSIVIEW * table,const column_info * columns)425 UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
426                         const column_info *columns )
427 {
428     struct select_view *sv = NULL;
429     UINT count = 0, r = ERROR_SUCCESS;
430 
431     TRACE("%p\n", sv );
432 
433     count = select_count_columns( columns );
434 
435     sv = calloc( 1, offsetof( struct select_view, cols[count] ) );
436     if( !sv )
437         return ERROR_FUNCTION_FAILED;
438 
439     /* fill the structure */
440     sv->view.ops = &select_ops;
441     sv->db = db;
442     sv->table = table;
443     sv->num_cols = 0;
444     sv->max_cols = count;
445 
446     while( columns )
447     {
448         r = SELECT_AddColumn( sv, columns->column, columns->table );
449         if( r )
450             break;
451         columns = columns->next;
452     }
453 
454     if( r == ERROR_SUCCESS )
455         *view = &sv->view;
456     else
457         free( sv );
458 
459     return r;
460 }
461