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