diff --git a/dlls/msi/delete.c b/dlls/msi/delete.c index 7f6675fe220..892605183a3 100644 --- a/dlls/msi/delete.c +++ b/dlls/msi/delete.c @@ -182,6 +182,14 @@ static UINT DELETE_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT DELETE_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + return ERROR_FUNCTION_FAILED; +} + MSIVIEWOPS delete_ops = { @@ -194,7 +202,8 @@ MSIVIEWOPS delete_ops = DELETE_get_dimensions, DELETE_get_column_info, DELETE_modify, - DELETE_delete + DELETE_delete, + DELETE_find_matching_rows }; UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) diff --git a/dlls/msi/distinct.c b/dlls/msi/distinct.c index f5b6bd225d8..d6650021f7e 100644 --- a/dlls/msi/distinct.c +++ b/dlls/msi/distinct.c @@ -246,6 +246,27 @@ static UINT DISTINCT_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT DISTINCT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + UINT r; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->find_matching_rows( dv->table, col, val, row, handle ); + + if( *row > dv->row_count ) + return ERROR_NO_MORE_ITEMS; + + *row = dv->translation[ *row ]; + + return r; +} + MSIVIEWOPS distinct_ops = { @@ -258,7 +279,8 @@ MSIVIEWOPS distinct_ops = DISTINCT_get_dimensions, DISTINCT_get_column_info, DISTINCT_modify, - DISTINCT_delete + DISTINCT_delete, + DISTINCT_find_matching_rows, }; UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) diff --git a/dlls/msi/insert.c b/dlls/msi/insert.c index 536daeac232..3f9dd9827ab 100644 --- a/dlls/msi/insert.c +++ b/dlls/msi/insert.c @@ -214,6 +214,14 @@ static UINT INSERT_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + return ERROR_FUNCTION_FAILED; +} + MSIVIEWOPS insert_ops = { @@ -226,7 +234,8 @@ MSIVIEWOPS insert_ops = INSERT_get_dimensions, INSERT_get_column_info, INSERT_modify, - INSERT_delete + INSERT_delete, + INSERT_find_matching_rows }; UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 575d1218230..6ef563a1a09 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -100,6 +100,8 @@ typedef struct tagMSIRECORD MSIFIELD fields[1]; /* nb. array size is count+1 */ } MSIRECORD; +typedef void *MSIITERHANDLE; + typedef struct tagMSIVIEWOPS { /* @@ -114,7 +116,7 @@ typedef struct tagMSIVIEWOPS UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val ); /* - * fetch_int - reads one integer from {row,col} in the table + * fetch_stream - gets a stream from {row,col} in the table * * This function is similar to fetch_int, except fetches a * stream instead of an integer. @@ -170,6 +172,18 @@ typedef struct tagMSIVIEWOPS */ UINT (*delete)( struct tagMSIVIEW * ); + /* + * find_matching_rows - iterates through rows that match a value + * + * If the column type is a string then a string ID should be passed in. + * If the value to be looked up is an integer then no transformation of + * the input value is required, except if the column is a string, in which + * case a string ID should be passed in. + * The handle is an input/output parameter that keeps track of the current + * position in the iteration. It must be initialised to zero before the + * first call and continued to be passed in to subsequent calls. + */ + UINT (*find_matching_rows)( struct tagMSIVIEW *, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle ); } MSIVIEWOPS; struct tagMSIVIEW diff --git a/dlls/msi/order.c b/dlls/msi/order.c index 27da7c64770..fd98e888b17 100644 --- a/dlls/msi/order.c +++ b/dlls/msi/order.c @@ -249,6 +249,24 @@ static UINT ORDER_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT ORDER_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + UINT r; + + TRACE("%p, %d, %u, %p\n", ov, col, val, *handle); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + r = ov->table->ops->find_matching_rows( ov->table, col, val, row, handle ); + + *row = ov->reorder[ *row ]; + + return r; +} + MSIVIEWOPS order_ops = { @@ -261,7 +279,8 @@ MSIVIEWOPS order_ops = ORDER_get_dimensions, ORDER_get_column_info, ORDER_modify, - ORDER_delete + ORDER_delete, + ORDER_find_matching_rows }; static UINT ORDER_AddColumn( MSIORDERVIEW *ov, LPCWSTR name ) diff --git a/dlls/msi/select.c b/dlls/msi/select.c index 7c792601cd4..c6e4eb8de81 100644 --- a/dlls/msi/select.c +++ b/dlls/msi/select.c @@ -196,6 +196,24 @@ static UINT SELECT_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT SELECT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->find_matching_rows( sv->table, col, val, row, handle ); +} + MSIVIEWOPS select_ops = { @@ -208,7 +226,8 @@ MSIVIEWOPS select_ops = SELECT_get_dimensions, SELECT_get_column_info, SELECT_modify, - SELECT_delete + SELECT_delete, + SELECT_find_matching_rows }; static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name ) diff --git a/dlls/msi/table.c b/dlls/msi/table.c index 7b4128bf33a..8ce24faf9ea 100644 --- a/dlls/msi/table.c +++ b/dlls/msi/table.c @@ -39,6 +39,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(msidb); +#define MSITABLE_HASH_TABLE_SIZE 37 + +typedef struct tagMSICOLUMNHASHENTRY +{ + struct tagMSICOLUMNHASHENTRY *next; + UINT value; + UINT row; +} MSICOLUMNHASHENTRY; + typedef struct tagMSICOLUMNINFO { LPCWSTR tablename; @@ -46,6 +55,7 @@ typedef struct tagMSICOLUMNINFO LPCWSTR colname; UINT type; UINT offset; + MSICOLUMNHASHENTRY **hash_table; } MSICOLUMNINFO; struct tagMSITABLE @@ -884,6 +894,7 @@ static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ) { msi_free( (LPWSTR) colinfo[i].tablename ); msi_free( (LPWSTR) colinfo[i].colname ); + msi_free( colinfo[i].hash_table ); } } @@ -931,7 +942,7 @@ static UINT get_tablecolumns( MSIDATABASE *db, return r; } - TRACE("Table id is %d\n", table_id); + TRACE("Table id is %d, row count is %d\n", table_id, table->row_count); count = table->row_count; for( i=0; idata[ i ][ 1 ] - (1<<15); colinfo[n].colname = MSI_makestring( db, id ); colinfo[n].type = table->data[ i ] [ 3 ] ^ 0x8000; + colinfo[n].hash_table = NULL; /* this assumes that columns are in order in the table */ if( n ) colinfo[n].offset = colinfo[n-1].offset @@ -1389,6 +1401,98 @@ static UINT TABLE_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + const MSICOLUMNHASHENTRY *entry; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col > tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + if( !tv->columns[col-1].hash_table ) + { + UINT i; + UINT num_rows = tv->table->row_count; + MSICOLUMNHASHENTRY **hash_table; + MSICOLUMNHASHENTRY *new_entry; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + /* allocate contiguous memory for the table and its entries so we + * don't have to do an expensive cleanup */ + hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) + + num_rows * sizeof(MSICOLUMNHASHENTRY)); + if (!hash_table) + return ERROR_OUTOFMEMORY; + + memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*)); + tv->columns[col-1].hash_table = hash_table; + + new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE); + + for (i = 0; i < num_rows; i++, new_entry++) + { + UINT row_value, n; + UINT offset = i + (tv->columns[col-1].offset/2) * num_rows; + n = bytes_per_column( &tv->columns[col-1] ); + switch( n ) + { + case 4: + offset = tv->columns[col-1].offset/2; + row_value = tv->table->data[i][offset] + + (tv->table->data[i][offset + 1] << 16); + break; + case 2: + offset = tv->columns[col-1].offset/2; + row_value = tv->table->data[i][offset]; + break; + default: + ERR("oops! what is %d bytes per column?\n", n ); + continue; + } + + new_entry->next = NULL; + new_entry->value = row_value; + new_entry->row = i; + if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE]) + { + MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE]; + while (prev_entry->next) + prev_entry = prev_entry->next; + prev_entry->next = new_entry; + } + else + hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry; + } + } + + if( !*handle ) + entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE]; + else + entry = ((const MSICOLUMNHASHENTRY *)*handle)->next; + + while (entry && entry->value != val) + entry = entry->next; + + *handle = (MSIITERHANDLE)entry; + if (!entry) + return ERROR_NO_MORE_ITEMS; + + *row = entry->row; + return ERROR_SUCCESS; +} + MSIVIEWOPS table_ops = { @@ -1401,7 +1505,8 @@ MSIVIEWOPS table_ops = TABLE_get_dimensions, TABLE_get_column_info, TABLE_modify, - TABLE_delete + TABLE_delete, + TABLE_find_matching_rows }; UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) diff --git a/dlls/msi/update.c b/dlls/msi/update.c index a36c5196483..81fd70f01b7 100644 --- a/dlls/msi/update.c +++ b/dlls/msi/update.c @@ -177,6 +177,13 @@ static UINT UPDATE_delete( struct tagMSIVIEW *view ) return ERROR_SUCCESS; } +static UINT UPDATE_find_matching_rows( struct tagMSIVIEW *view, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p %d %d %p\n", view, col, val, *handle ); + + return ERROR_FUNCTION_FAILED; +} + static MSIVIEWOPS update_ops = { @@ -189,7 +196,8 @@ static MSIVIEWOPS update_ops = UPDATE_get_dimensions, UPDATE_get_column_info, UPDATE_modify, - UPDATE_delete + UPDATE_delete, + UPDATE_find_matching_rows }; UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, diff --git a/dlls/msi/where.c b/dlls/msi/where.c index b11ebe79f13..917e1825742 100644 --- a/dlls/msi/where.c +++ b/dlls/msi/where.c @@ -248,6 +248,45 @@ static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) return ERROR_FUNCTION_FAILED; wv->row_count = 0; + if (wv->cond->type == EXPR_STRCMP) + { + MSIITERHANDLE handle = NULL; + UINT row, value, col; + struct expr *col_cond = wv->cond->u.expr.left; + struct expr *val_cond = wv->cond->u.expr.right; + + /* swap conditionals */ + if (col_cond->type != EXPR_COL_NUMBER_STRING) + { + val_cond = wv->cond->u.expr.left; + col_cond = wv->cond->u.expr.right; + } + + if ((col_cond->type == EXPR_COL_NUMBER_STRING) && (val_cond->type == EXPR_SVAL)) + { + col = col_cond->u.col_number; + r = msi_string2idW(wv->db->strings, val_cond->u.sval, &value); + if (r != ERROR_SUCCESS) + { + TRACE("no id for %s, assuming it doesn't exist in the table\n", debugstr_w(wv->cond->u.expr.right->u.sval)); + return ERROR_SUCCESS; + } + + do + { + r = table->ops->find_matching_rows(table, col, value, &row, &handle); + if (r == ERROR_SUCCESS) + wv->reorder[ wv->row_count ++ ] = row; + } while (r == ERROR_SUCCESS); + + if (r == ERROR_NO_MORE_ITEMS) + return ERROR_SUCCESS; + else + return r; + } + /* else fallback to slow case */ + } + for( i=0; itable ) + return ERROR_FUNCTION_FAILED; + + r = wv->table->ops->find_matching_rows( wv->table, col, val, row, handle ); + + if( *row > wv->row_count ) + return ERROR_NO_MORE_ITEMS; + + *row = wv->reorder[ *row ]; + + return r; +} + MSIVIEWOPS where_ops = { @@ -353,7 +413,8 @@ MSIVIEWOPS where_ops = WHERE_get_dimensions, WHERE_get_column_info, WHERE_modify, - WHERE_delete + WHERE_delete, + WHERE_find_matching_rows }; static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,