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