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 struct distinct_set 39 { 40 UINT val; 41 UINT count; 42 UINT row; 43 struct distinct_set *nextrow; 44 struct distinct_set *nextcol; 45 }; 46 47 struct distinct_view 48 { 49 MSIVIEW view; 50 MSIDATABASE *db; 51 MSIVIEW *table; 52 UINT row_count; 53 UINT *translation; 54 }; 55 56 static struct distinct_set **distinct_insert( struct distinct_set **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 = malloc( sizeof(**x) ); 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( struct distinct_set *x ) 83 { 84 while( x ) 85 { 86 struct distinct_set *next = x->nextrow; 87 distinct_free( x->nextcol ); 88 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 struct distinct_view *dv = (struct distinct_view *)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 struct distinct_view *dv = (struct distinct_view *)view; 113 UINT r, i, j, r_count, c_count; 114 struct distinct_set *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 = malloc( 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 struct distinct_set **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 struct distinct_view *dv = (struct distinct_view *)view; 175 176 TRACE("%p\n", dv ); 177 178 if( !dv->table ) 179 return ERROR_FUNCTION_FAILED; 180 181 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 struct distinct_view *dv = (struct distinct_view *)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 struct distinct_view *dv = (struct distinct_view *)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 struct distinct_view *dv = (struct distinct_view *)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 struct distinct_view *dv = (struct distinct_view *)view; 237 238 TRACE("%p\n", dv ); 239 240 if( dv->table ) 241 dv->table->ops->delete( dv->table ); 242 243 free( dv->translation ); 244 msiobj_release( &dv->db->hdr ); 245 free( dv ); 246 247 return ERROR_SUCCESS; 248 } 249 250 static const MSIVIEWOPS distinct_ops = 251 { 252 DISTINCT_fetch_int, 253 NULL, 254 NULL, 255 NULL, 256 NULL, 257 NULL, 258 NULL, 259 NULL, 260 DISTINCT_execute, 261 DISTINCT_close, 262 DISTINCT_get_dimensions, 263 DISTINCT_get_column_info, 264 DISTINCT_modify, 265 DISTINCT_delete, 266 NULL, 267 NULL, 268 NULL, 269 NULL, 270 NULL, 271 }; 272 273 UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) 274 { 275 struct distinct_view *dv = NULL; 276 UINT count = 0, r; 277 278 TRACE("%p\n", dv ); 279 280 r = table->ops->get_dimensions( table, NULL, &count ); 281 if( r != ERROR_SUCCESS ) 282 { 283 ERR("can't get table dimensions\n"); 284 return r; 285 } 286 287 dv = calloc( 1, sizeof *dv ); 288 if( !dv ) 289 return ERROR_FUNCTION_FAILED; 290 291 /* fill the structure */ 292 dv->view.ops = &distinct_ops; 293 msiobj_addref( &db->hdr ); 294 dv->db = db; 295 dv->table = table; 296 dv->translation = NULL; 297 dv->row_count = 0; 298 *view = (MSIVIEW*) dv; 299 300 return ERROR_SUCCESS; 301 } 302