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, UINT n, LPCWSTR *name, 208 UINT *type, BOOL *temporary, LPCWSTR *table_name ) 209 { 210 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 211 212 TRACE("%p %d %p %p %p %p\n", dv, n, name, type, temporary, table_name ); 213 214 if( !dv->table ) 215 return ERROR_FUNCTION_FAILED; 216 217 return dv->table->ops->get_column_info( dv->table, n, name, 218 type, temporary, table_name ); 219 } 220 221 static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, 222 MSIRECORD *rec, UINT row ) 223 { 224 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 225 226 TRACE("%p %d %p\n", dv, eModifyMode, rec ); 227 228 if( !dv->table ) 229 return ERROR_FUNCTION_FAILED; 230 231 return dv->table->ops->modify( dv->table, eModifyMode, rec, row ); 232 } 233 234 static UINT DISTINCT_delete( struct tagMSIVIEW *view ) 235 { 236 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 237 238 TRACE("%p\n", dv ); 239 240 if( dv->table ) 241 dv->table->ops->delete( dv->table ); 242 243 msi_free( dv->translation ); 244 msiobj_release( &dv->db->hdr ); 245 msi_free( dv ); 246 247 return ERROR_SUCCESS; 248 } 249 250 static UINT DISTINCT_find_matching_rows( struct tagMSIVIEW *view, UINT col, 251 UINT val, UINT *row, MSIITERHANDLE *handle ) 252 { 253 MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; 254 UINT r; 255 256 TRACE("%p, %d, %u, %p\n", view, col, val, *handle); 257 258 if( !dv->table ) 259 return ERROR_FUNCTION_FAILED; 260 261 r = dv->table->ops->find_matching_rows( dv->table, col, val, row, handle ); 262 263 if( *row > dv->row_count ) 264 return ERROR_NO_MORE_ITEMS; 265 266 *row = dv->translation[ *row ]; 267 268 return r; 269 } 270 271 static const MSIVIEWOPS distinct_ops = 272 { 273 DISTINCT_fetch_int, 274 NULL, 275 NULL, 276 NULL, 277 NULL, 278 NULL, 279 DISTINCT_execute, 280 DISTINCT_close, 281 DISTINCT_get_dimensions, 282 DISTINCT_get_column_info, 283 DISTINCT_modify, 284 DISTINCT_delete, 285 DISTINCT_find_matching_rows, 286 NULL, 287 NULL, 288 NULL, 289 NULL, 290 NULL, 291 NULL, 292 }; 293 294 UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) 295 { 296 MSIDISTINCTVIEW *dv = NULL; 297 UINT count = 0, r; 298 299 TRACE("%p\n", dv ); 300 301 r = table->ops->get_dimensions( table, NULL, &count ); 302 if( r != ERROR_SUCCESS ) 303 { 304 ERR("can't get table dimensions\n"); 305 return r; 306 } 307 308 dv = msi_alloc_zero( sizeof *dv ); 309 if( !dv ) 310 return ERROR_FUNCTION_FAILED; 311 312 /* fill the structure */ 313 dv->view.ops = &distinct_ops; 314 msiobj_addref( &db->hdr ); 315 dv->db = db; 316 dv->table = table; 317 dv->translation = NULL; 318 dv->row_count = 0; 319 *view = (MSIVIEW*) dv; 320 321 return ERROR_SUCCESS; 322 } 323