1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 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 "wine/unicode.h" 28 #include "msi.h" 29 #include "msiquery.h" 30 #include "objbase.h" 31 #include "objidl.h" 32 #include "msipriv.h" 33 #include "winnls.h" 34 35 #include "query.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(msidb); 38 39 40 /* below is the query interface to a table */ 41 42 typedef struct tagMSIINSERTVIEW 43 { 44 MSIVIEW view; 45 MSIVIEW *table; 46 MSIDATABASE *db; 47 BOOL bIsTemp; 48 MSIVIEW *sv; 49 column_info *vals; 50 } MSIINSERTVIEW; 51 52 static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) 53 { 54 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 55 56 TRACE("%p %d %d %p\n", iv, row, col, val ); 57 58 return ERROR_FUNCTION_FAILED; 59 } 60 61 /* 62 * msi_query_merge_record 63 * 64 * Merge a value_list and a record to create a second record. 65 * Replace wildcard entries in the valuelist with values from the record 66 */ 67 MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec ) 68 { 69 MSIRECORD *merged; 70 DWORD wildcard_count = 1, i; 71 72 merged = MSI_CreateRecord( fields ); 73 for( i=1; i <= fields; i++ ) 74 { 75 if( !vl ) 76 { 77 TRACE("Not enough elements in the list to insert\n"); 78 goto err; 79 } 80 switch( vl->val->type ) 81 { 82 case EXPR_SVAL: 83 TRACE("field %d -> %s\n", i, debugstr_w(vl->val->u.sval)); 84 MSI_RecordSetStringW( merged, i, vl->val->u.sval ); 85 break; 86 case EXPR_IVAL: 87 MSI_RecordSetInteger( merged, i, vl->val->u.ival ); 88 break; 89 case EXPR_WILDCARD: 90 if( !rec ) 91 goto err; 92 MSI_RecordCopyField( rec, wildcard_count, merged, i ); 93 wildcard_count++; 94 break; 95 default: 96 ERR("Unknown expression type %d\n", vl->val->type); 97 } 98 vl = vl->next; 99 } 100 101 return merged; 102 err: 103 msiobj_release( &merged->hdr ); 104 return NULL; 105 } 106 107 /* checks to see if the column order specified in the INSERT query 108 * matches the column order of the table 109 */ 110 static BOOL msi_columns_in_order(MSIINSERTVIEW *iv, UINT col_count) 111 { 112 LPCWSTR a, b; 113 UINT i; 114 115 for (i = 1; i <= col_count; i++) 116 { 117 iv->sv->ops->get_column_info(iv->sv, i, &a, NULL, NULL, NULL); 118 iv->table->ops->get_column_info(iv->table, i, &b, NULL, NULL, NULL); 119 120 if (strcmpW( a, b )) return FALSE; 121 } 122 return TRUE; 123 } 124 125 /* rearranges the data in the record to be inserted based on column order, 126 * and pads the record for any missing columns in the INSERT query 127 */ 128 static UINT msi_arrange_record(MSIINSERTVIEW *iv, MSIRECORD **values) 129 { 130 MSIRECORD *padded; 131 UINT col_count, val_count; 132 UINT r, i, colidx; 133 LPCWSTR a, b; 134 135 r = iv->table->ops->get_dimensions(iv->table, NULL, &col_count); 136 if (r != ERROR_SUCCESS) 137 return r; 138 139 val_count = MSI_RecordGetFieldCount(*values); 140 141 /* check to see if the columns are arranged already 142 * to avoid unnecessary copying 143 */ 144 if (col_count == val_count && msi_columns_in_order(iv, col_count)) 145 return ERROR_SUCCESS; 146 147 padded = MSI_CreateRecord(col_count); 148 if (!padded) 149 return ERROR_OUTOFMEMORY; 150 151 for (colidx = 1; colidx <= val_count; colidx++) 152 { 153 r = iv->sv->ops->get_column_info(iv->sv, colidx, &a, NULL, NULL, NULL); 154 if (r != ERROR_SUCCESS) 155 goto err; 156 157 for (i = 1; i <= col_count; i++) 158 { 159 r = iv->table->ops->get_column_info(iv->table, i, &b, NULL, 160 NULL, NULL); 161 if (r != ERROR_SUCCESS) 162 goto err; 163 164 if (!strcmpW( a, b )) 165 { 166 MSI_RecordCopyField(*values, colidx, padded, i); 167 break; 168 } 169 } 170 } 171 msiobj_release(&(*values)->hdr); 172 *values = padded; 173 return ERROR_SUCCESS; 174 175 err: 176 msiobj_release(&padded->hdr); 177 return r; 178 } 179 180 static BOOL row_has_null_primary_keys(MSIINSERTVIEW *iv, MSIRECORD *row) 181 { 182 UINT r, i, col_count, type; 183 184 r = iv->table->ops->get_dimensions( iv->table, NULL, &col_count ); 185 if (r != ERROR_SUCCESS) 186 return FALSE; 187 188 for (i = 1; i <= col_count; i++) 189 { 190 r = iv->table->ops->get_column_info(iv->table, i, NULL, &type, 191 NULL, NULL); 192 if (r != ERROR_SUCCESS) 193 return FALSE; 194 195 if (!(type & MSITYPE_KEY)) 196 continue; 197 198 if (MSI_RecordIsNull(row, i)) 199 return TRUE; 200 } 201 202 return FALSE; 203 } 204 205 static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) 206 { 207 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 208 UINT r, row = -1, col_count = 0; 209 MSIVIEW *sv; 210 MSIRECORD *values = NULL; 211 212 TRACE("%p %p\n", iv, record ); 213 214 sv = iv->sv; 215 if( !sv ) 216 return ERROR_FUNCTION_FAILED; 217 218 r = sv->ops->execute( sv, 0 ); 219 TRACE("sv execute returned %x\n", r); 220 if( r ) 221 return r; 222 223 r = sv->ops->get_dimensions( sv, NULL, &col_count ); 224 if( r ) 225 goto err; 226 227 /* 228 * Merge the wildcard values into the list of values provided 229 * in the query, and create a record containing both. 230 */ 231 values = msi_query_merge_record( col_count, iv->vals, record ); 232 if( !values ) 233 goto err; 234 235 r = msi_arrange_record( iv, &values ); 236 if( r != ERROR_SUCCESS ) 237 goto err; 238 239 /* rows with NULL primary keys are inserted at the beginning of the table */ 240 if( row_has_null_primary_keys( iv, values ) ) 241 row = 0; 242 243 r = iv->table->ops->insert_row( iv->table, values, row, iv->bIsTemp ); 244 245 err: 246 if( values ) 247 msiobj_release( &values->hdr ); 248 249 return r; 250 } 251 252 253 static UINT INSERT_close( struct tagMSIVIEW *view ) 254 { 255 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 256 MSIVIEW *sv; 257 258 TRACE("%p\n", iv); 259 260 sv = iv->sv; 261 if( !sv ) 262 return ERROR_FUNCTION_FAILED; 263 264 return sv->ops->close( sv ); 265 } 266 267 static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) 268 { 269 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 270 MSIVIEW *sv; 271 272 TRACE("%p %p %p\n", iv, rows, cols ); 273 274 sv = iv->sv; 275 if( !sv ) 276 return ERROR_FUNCTION_FAILED; 277 278 return sv->ops->get_dimensions( sv, rows, cols ); 279 } 280 281 static UINT INSERT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, 282 UINT *type, BOOL *temporary, LPCWSTR *table_name ) 283 { 284 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 285 MSIVIEW *sv; 286 287 TRACE("%p %d %p %p %p %p\n", iv, n, name, type, temporary, table_name ); 288 289 sv = iv->sv; 290 if( !sv ) 291 return ERROR_FUNCTION_FAILED; 292 293 return sv->ops->get_column_info( sv, n, name, type, temporary, table_name ); 294 } 295 296 static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) 297 { 298 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 299 300 TRACE("%p %d %p\n", iv, eModifyMode, rec ); 301 302 return ERROR_FUNCTION_FAILED; 303 } 304 305 static UINT INSERT_delete( struct tagMSIVIEW *view ) 306 { 307 MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; 308 MSIVIEW *sv; 309 310 TRACE("%p\n", iv ); 311 312 sv = iv->sv; 313 if( sv ) 314 sv->ops->delete( sv ); 315 msiobj_release( &iv->db->hdr ); 316 msi_free( iv ); 317 318 return ERROR_SUCCESS; 319 } 320 321 static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col, 322 UINT val, UINT *row, MSIITERHANDLE *handle ) 323 { 324 TRACE("%p, %d, %u, %p\n", view, col, val, *handle); 325 326 return ERROR_FUNCTION_FAILED; 327 } 328 329 330 static const MSIVIEWOPS insert_ops = 331 { 332 INSERT_fetch_int, 333 NULL, 334 NULL, 335 NULL, 336 NULL, 337 NULL, 338 INSERT_execute, 339 INSERT_close, 340 INSERT_get_dimensions, 341 INSERT_get_column_info, 342 INSERT_modify, 343 INSERT_delete, 344 INSERT_find_matching_rows, 345 NULL, 346 NULL, 347 NULL, 348 NULL, 349 NULL, 350 NULL, 351 }; 352 353 static UINT count_column_info( const column_info *ci ) 354 { 355 UINT n = 0; 356 for ( ; ci; ci = ci->next ) 357 n++; 358 return n; 359 } 360 361 UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, 362 column_info *columns, column_info *values, BOOL temp ) 363 { 364 MSIINSERTVIEW *iv = NULL; 365 UINT r; 366 MSIVIEW *tv = NULL, *sv = NULL; 367 368 TRACE("%p\n", iv ); 369 370 /* there should be one value for each column */ 371 if ( count_column_info( columns ) != count_column_info(values) ) 372 return ERROR_BAD_QUERY_SYNTAX; 373 374 r = TABLE_CreateView( db, table, &tv ); 375 if( r != ERROR_SUCCESS ) 376 return r; 377 378 r = SELECT_CreateView( db, &sv, tv, columns ); 379 if( r != ERROR_SUCCESS ) 380 { 381 if( tv ) 382 tv->ops->delete( tv ); 383 return r; 384 } 385 386 iv = msi_alloc_zero( sizeof *iv ); 387 if( !iv ) 388 return ERROR_FUNCTION_FAILED; 389 390 /* fill the structure */ 391 iv->view.ops = &insert_ops; 392 msiobj_addref( &db->hdr ); 393 iv->table = tv; 394 iv->db = db; 395 iv->vals = values; 396 iv->bIsTemp = temp; 397 iv->sv = sv; 398 *view = (MSIVIEW*) iv; 399 400 return ERROR_SUCCESS; 401 } 402