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