1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2002 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 "msipriv.h" 22 23 WINE_DEFAULT_DEBUG_CHANNEL(msidb); 24 25 typedef struct tagDISTINCTSET 26 { 27 UINT val; 28 UINT count; 29 UINT row; 30 struct tagDISTINCTSET *nextrow; 31 struct tagDISTINCTSET *nextcol; 32 } DISTINCTSET; 33 34 typedef struct tagMSIDISTINCTVIEW 35 { 36 MSIVIEW view; 37 MSIDATABASE *db; 38 MSIVIEW *table; 39 UINT row_count; 40 UINT *translation; 41 } MSIDISTINCTVIEW; 42 43 static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row ) 44 { 45 /* horrible O(n) find */ 46 while( *x ) 47 { 48 if( (*x)->val == val ) 49 { 50 (*x)->count++; 51 return x; 52 } 53 x = &(*x)->nextrow; 54 } 55 56 /* nothing found, so add one */ 57 *x = msi_alloc( sizeof (DISTINCTSET) ); 58 if( *x ) 59 { 60 (*x)->val = val; 61 (*x)->count = 1; 62 (*x)->row = row; 63 (*x)->nextrow = NULL; 64 (*x)->nextcol = NULL; 65 } 66 return x; 67 } 68 69 static void distinct_free( DISTINCTSET *x ) 70 { 71 while( x ) 72 { 73 DISTINCTSET *next = x->nextrow; 74 distinct_free( x->nextcol ); 75 msi_free( x ); 76 x = next; 77 } 78 } 79 80 static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) 81 { 82 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 83 84 TRACE("%p %d %d %p\n", dv, row, col, val ); 85 86 if( !dv->table ) 87 return ERROR_FUNCTION_FAILED; 88 89 if( row >= dv->row_count ) 90 return ERROR_INVALID_PARAMETER; 91 92 row = dv->translation[ row ]; 93 94 return dv->table->ops->fetch_int( dv->table, row, col, val ); 95 } 96 97 static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) 98 { 99 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 100 UINT r, i, j, r_count, c_count; 101 DISTINCTSET *rowset = NULL; 102 103 TRACE("%p %p\n", dv, record); 104 105 if( !dv->table ) 106 return ERROR_FUNCTION_FAILED; 107 108 r = dv->table->ops->execute( dv->table, record ); 109 if( r != ERROR_SUCCESS ) 110 return r; 111 112 r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count ); 113 if( r != ERROR_SUCCESS ) 114 return r; 115 116 dv->translation = msi_alloc( r_count*sizeof(UINT) ); 117 if( !dv->translation ) 118 return ERROR_FUNCTION_FAILED; 119 120 /* build it */ 121 for( i=0; i<r_count; i++ ) 122 { 123 DISTINCTSET **x = &rowset; 124 125 for( j=1; j<=c_count; j++ ) 126 { 127 UINT val = 0; 128 r = dv->table->ops->fetch_int( dv->table, i, j, &val ); 129 if( r != ERROR_SUCCESS ) 130 { 131 ERR("Failed to fetch int at %d %d\n", i, j ); 132 distinct_free( rowset ); 133 return r; 134 } 135 x = distinct_insert( x, val, i ); 136 if( !*x ) 137 { 138 ERR("Failed to insert at %d %d\n", i, j ); 139 distinct_free( rowset ); 140 return ERROR_FUNCTION_FAILED; 141 } 142 if( j != c_count ) 143 x = &(*x)->nextcol; 144 } 145 146 /* check if it was distinct and if so, include it */ 147 if( (*x)->row == i ) 148 { 149 TRACE("Row %d -> %d\n", dv->row_count, i); 150 dv->translation[dv->row_count++] = i; 151 } 152 } 153 154 distinct_free( rowset ); 155 156 return ERROR_SUCCESS; 157 } 158 159 static UINT DISTINCT_close( struct tagMSIVIEW *view ) 160 { 161 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 162 163 TRACE("%p\n", dv ); 164 165 if( !dv->table ) 166 return ERROR_FUNCTION_FAILED; 167 168 msi_free( dv->translation ); 169 dv->translation = NULL; 170 dv->row_count = 0; 171 172 return dv->table->ops->close( dv->table ); 173 } 174 175 static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) 176 { 177 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 178 179 TRACE("%p %p %p\n", dv, rows, cols ); 180 181 if( !dv->table ) 182 return ERROR_FUNCTION_FAILED; 183 184 if( rows ) 185 { 186 if( !dv->translation ) 187 return ERROR_FUNCTION_FAILED; 188 *rows = dv->row_count; 189 } 190 191 return dv->table->ops->get_dimensions( dv->table, NULL, cols ); 192 } 193 194 static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, 195 UINT *type, BOOL *temporary, LPCWSTR *table_name ) 196 { 197 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 198 199 TRACE("%p %d %p %p %p %p\n", dv, n, name, type, temporary, table_name ); 200 201 if( !dv->table ) 202 return ERROR_FUNCTION_FAILED; 203 204 return dv->table->ops->get_column_info( dv->table, n, name, 205 type, temporary, table_name ); 206 } 207 208 static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, 209 MSIRECORD *rec, UINT row ) 210 { 211 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 212 213 TRACE("%p %d %p\n", dv, eModifyMode, rec ); 214 215 if( !dv->table ) 216 return ERROR_FUNCTION_FAILED; 217 218 return dv->table->ops->modify( dv->table, eModifyMode, rec, row ); 219 } 220 221 static UINT DISTINCT_delete( struct tagMSIVIEW *view ) 222 { 223 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 224 225 TRACE("%p\n", dv ); 226 227 if( dv->table ) 228 dv->table->ops->delete( dv->table ); 229 230 msi_free( dv->translation ); 231 msiobj_release( &dv->db->hdr ); 232 msi_free( dv ); 233 234 return ERROR_SUCCESS; 235 } 236 237 static UINT DISTINCT_find_matching_rows( struct tagMSIVIEW *view, UINT col, 238 UINT val, UINT *row, MSIITERHANDLE *handle ) 239 { 240 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 241 UINT r; 242 243 TRACE("%p, %d, %u, %p\n", view, col, val, *handle); 244 245 if( !dv->table ) 246 return ERROR_FUNCTION_FAILED; 247 248 r = dv->table->ops->find_matching_rows( dv->table, col, val, row, handle ); 249 250 if( *row > dv->row_count ) 251 return ERROR_NO_MORE_ITEMS; 252 253 *row = dv->translation[ *row ]; 254 255 return r; 256 } 257 258 static const MSIVIEWOPS distinct_ops = 259 { 260 DISTINCT_fetch_int, 261 NULL, 262 NULL, 263 NULL, 264 NULL, 265 NULL, 266 DISTINCT_execute, 267 DISTINCT_close, 268 DISTINCT_get_dimensions, 269 DISTINCT_get_column_info, 270 DISTINCT_modify, 271 DISTINCT_delete, 272 DISTINCT_find_matching_rows, 273 NULL, 274 NULL, 275 NULL, 276 NULL, 277 NULL, 278 NULL, 279 }; 280 281 UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) 282 { 283 MSIDISTINCTVIEW *dv = NULL; 284 UINT count = 0, r; 285 286 TRACE("%p\n", dv ); 287 288 r = table->ops->get_dimensions( table, NULL, &count ); 289 if( r != ERROR_SUCCESS ) 290 { 291 ERR("can't get table dimensions\n"); 292 return r; 293 } 294 295 dv = msi_alloc_zero( sizeof *dv ); 296 if( !dv ) 297 return ERROR_FUNCTION_FAILED; 298 299 /* fill the structure */ 300 dv->view.ops = &distinct_ops; 301 msiobj_addref( &db->hdr ); 302 dv->db = db; 303 dv->table = table; 304 dv->translation = NULL; 305 dv->row_count = 0; 306 *view = (MSIVIEW*) dv; 307 308 return ERROR_SUCCESS; 309 } 310