2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-2004 Mike McCormack for CodeWeavers
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.
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.
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
26 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(msidb
);
39 /* below is the query interface to a table */
41 typedef struct tagMSISELECTVIEW
51 static UINT
translate_record( MSISELECTVIEW
*sv
, MSIRECORD
*in
, MSIRECORD
**out
)
56 if ((r
= sv
->table
->ops
->get_dimensions( sv
->table
, NULL
, &col_count
)))
59 if (!(object
= MSI_CreateRecord( col_count
)))
60 return ERROR_OUTOFMEMORY
;
62 for (i
= 0; i
< sv
->num_cols
; i
++)
64 if ((r
= MSI_RecordCopyField( in
, i
+ 1, object
, sv
->cols
[i
] )))
66 msiobj_release( &object
->hdr
);
75 static UINT
SELECT_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
77 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
79 TRACE("%p %d %d %p\n", sv
, row
, col
, val
);
82 return ERROR_FUNCTION_FAILED
;
84 if( !col
|| col
> sv
->num_cols
)
85 return ERROR_FUNCTION_FAILED
;
87 col
= sv
->cols
[ col
- 1 ];
93 return sv
->table
->ops
->fetch_int( sv
->table
, row
, col
, val
);
96 static UINT
SELECT_fetch_stream( struct tagMSIVIEW
*view
, UINT row
, UINT col
, IStream
**stm
)
98 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
100 TRACE("%p %d %d %p\n", sv
, row
, col
, stm
);
103 return ERROR_FUNCTION_FAILED
;
105 if( !col
|| col
> sv
->num_cols
)
106 return ERROR_FUNCTION_FAILED
;
108 col
= sv
->cols
[ col
- 1 ];
112 return ERROR_SUCCESS
;
114 return sv
->table
->ops
->fetch_stream( sv
->table
, row
, col
, stm
);
117 static UINT
SELECT_set_row( struct tagMSIVIEW
*view
, UINT row
, MSIRECORD
*rec
, UINT mask
)
119 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
120 UINT i
, expanded_mask
= 0, r
= ERROR_SUCCESS
, col_count
= 0;
123 TRACE("%p %d %p %08x\n", sv
, row
, rec
, mask
);
126 return ERROR_FUNCTION_FAILED
;
128 /* test if any of the mask bits are invalid */
129 if ( mask
>= (1<<sv
->num_cols
) )
130 return ERROR_INVALID_PARAMETER
;
132 /* find the number of columns in the table below */
133 r
= sv
->table
->ops
->get_dimensions( sv
->table
, NULL
, &col_count
);
137 /* expand the record to the right size for the underlying table */
138 expanded
= MSI_CreateRecord( col_count
);
140 return ERROR_FUNCTION_FAILED
;
142 /* move the right fields across */
143 for ( i
=0; i
<sv
->num_cols
; i
++ )
145 r
= MSI_RecordCopyField( rec
, i
+1, expanded
, sv
->cols
[ i
] );
146 if (r
!= ERROR_SUCCESS
)
148 expanded_mask
|= (1<<(sv
->cols
[i
]-1));
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
);
155 msiobj_release( &expanded
->hdr
);
159 static UINT
SELECT_insert_row( struct tagMSIVIEW
*view
, MSIRECORD
*record
, UINT row
, BOOL temporary
)
161 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
165 TRACE("%p %p\n", sv
, record
);
168 return ERROR_FUNCTION_FAILED
;
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
)
175 if ((r
= translate_record( sv
, record
, &outrec
)))
178 r
= sv
->table
->ops
->insert_row( sv
->table
, outrec
, row
, temporary
);
180 msiobj_release( &outrec
->hdr
);
184 static UINT
SELECT_execute( struct tagMSIVIEW
*view
, MSIRECORD
*record
)
186 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
188 TRACE("%p %p\n", sv
, record
);
191 return ERROR_FUNCTION_FAILED
;
193 return sv
->table
->ops
->execute( sv
->table
, record
);
196 static UINT
SELECT_close( struct tagMSIVIEW
*view
)
198 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
203 return ERROR_FUNCTION_FAILED
;
205 return sv
->table
->ops
->close( sv
->table
);
208 static UINT
SELECT_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
210 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
212 TRACE("%p %p %p\n", sv
, rows
, cols
);
215 return ERROR_FUNCTION_FAILED
;
218 *cols
= sv
->num_cols
;
220 return sv
->table
->ops
->get_dimensions( sv
->table
, rows
, NULL
);
223 static UINT
SELECT_get_column_info( struct tagMSIVIEW
*view
, UINT n
, LPCWSTR
*name
,
224 UINT
*type
, BOOL
*temporary
, LPCWSTR
*table_name
)
226 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
228 TRACE("%p %d %p %p %p %p\n", sv
, n
, name
, type
, temporary
, table_name
);
231 return ERROR_FUNCTION_FAILED
;
233 if( !n
|| n
> sv
->num_cols
)
234 return ERROR_FUNCTION_FAILED
;
236 n
= sv
->cols
[ n
- 1 ];
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
;
245 return sv
->table
->ops
->get_column_info( sv
->table
, n
, name
,
246 type
, temporary
, table_name
);
249 UINT
msi_select_update(MSIVIEW
*view
, MSIRECORD
*rec
, UINT row
)
251 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
252 UINT r
, i
, col
, type
, val
;
256 for (i
= 0; i
< sv
->num_cols
; i
++)
260 r
= SELECT_get_column_info(view
, i
+ 1, NULL
, &type
, NULL
, NULL
);
261 if (r
!= ERROR_SUCCESS
)
263 ERR("Failed to get column information: %d\n", r
);
267 if (MSITYPE_IS_BINARY(type
))
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
);
273 else if (type
& MSITYPE_STRING
)
276 str
= msi_record_get_string(rec
, i
+ 1, &len
);
277 r
= sv
->table
->ops
->set_string(sv
->table
, row
, col
, str
, len
);
281 val
= MSI_RecordGetInteger(rec
, i
+ 1);
282 r
= sv
->table
->ops
->set_int(sv
->table
, row
, col
, val
);
285 if (r
!= ERROR_SUCCESS
)
287 ERR("Failed to modify record: %d\n", r
);
292 return ERROR_SUCCESS
;
295 static UINT
SELECT_modify( struct tagMSIVIEW
*view
, MSIMODIFY mode
,
296 MSIRECORD
*rec
, UINT row
)
298 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
299 MSIRECORD
*table_rec
;
302 TRACE("view %p, mode %d, rec %p, row %u.\n", view
, mode
, rec
, row
);
305 return ERROR_FUNCTION_FAILED
;
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. */
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
)))
326 r
= sv
->table
->ops
->modify( sv
->table
, mode
, table_rec
, row
);
327 msiobj_release( &table_rec
->hdr
);
329 case MSIMODIFY_DELETE
:
330 return sv
->table
->ops
->modify( sv
->table
, mode
, rec
, row
);
332 FIXME("unhandled mode %d\n", mode
);
333 return ERROR_FUNCTION_FAILED
;
337 static UINT
SELECT_delete( struct tagMSIVIEW
*view
)
339 MSISELECTVIEW
*sv
= (MSISELECTVIEW
*)view
;
344 sv
->table
->ops
->delete( sv
->table
);
349 return ERROR_SUCCESS
;
352 static const MSIVIEWOPS select_ops
=
364 SELECT_get_dimensions
,
365 SELECT_get_column_info
,
375 static UINT
SELECT_AddColumn( MSISELECTVIEW
*sv
, LPCWSTR name
,
381 TRACE("%p adding %s.%s\n", sv
, debugstr_w( table_name
),
384 if( sv
->view
.ops
!= &select_ops
)
385 return ERROR_FUNCTION_FAILED
;
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
;
395 if( sv
->num_cols
>= sv
->max_cols
)
396 return ERROR_FUNCTION_FAILED
;
398 if ( !name
[0] ) n
= 0;
401 r
= VIEW_find_column( table
, name
, table_name
, &n
);
402 if( r
!= ERROR_SUCCESS
)
406 sv
->cols
[sv
->num_cols
] = n
;
407 TRACE("Translating column %s from %d -> %d\n",
408 debugstr_w( name
), sv
->num_cols
, n
);
412 return ERROR_SUCCESS
;
415 static int select_count_columns( const column_info
*col
)
418 for (n
= 0; col
; col
= col
->next
)
423 UINT
SELECT_CreateView( MSIDATABASE
*db
, MSIVIEW
**view
, MSIVIEW
*table
,
424 const column_info
*columns
)
426 MSISELECTVIEW
*sv
= NULL
;
427 UINT count
= 0, r
= ERROR_SUCCESS
;
431 count
= select_count_columns( columns
);
433 sv
= msi_alloc_zero( FIELD_OFFSET( MSISELECTVIEW
, cols
[count
] ));
435 return ERROR_FUNCTION_FAILED
;
437 /* fill the structure */
438 sv
->view
.ops
= &select_ops
;
442 sv
->max_cols
= count
;
446 r
= SELECT_AddColumn( sv
, columns
->column
, columns
->table
);
449 columns
= columns
->next
;
452 if( r
== ERROR_SUCCESS
)