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