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