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