📄 table.c
字号:
MSICOLUMNINFO *columns;
UINT num_cols;
UINT row_size;
WCHAR name[1];
} MSITABLEVIEW;
static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
UINT offset, num_rows, n;
if( !tv->table )
return ERROR_INVALID_PARAMETER;
if( (col==0) || (col>tv->num_cols) )
return ERROR_INVALID_PARAMETER;
/* how many rows are there ? */
num_rows = tv->table->row_count;
if( row >= num_rows )
return ERROR_NO_MORE_ITEMS;
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;
}
offset = row + (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;
*val = tv->table->data[row][offset] +
(tv->table->data[row][offset + 1] << 16);
break;
case 2:
offset = tv->columns[col-1].offset/2;
*val = tv->table->data[row][offset];
break;
default:
ERR("oops! what is %d bytes per column?\n", n );
return ERROR_FUNCTION_FAILED;
}
/* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
return ERROR_SUCCESS;
}
/*
* We need a special case for streams, as we need to reference column with
* the name of the stream in the same table, and the table name
* which may not be available at higher levels of the query
*/
static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
UINT ival = 0, refcol = 0, r;
LPCWSTR sval;
LPWSTR full_name;
DWORD len;
static const WCHAR szDot[] = { '.', 0 };
WCHAR number[0x20];
if( !view->ops->fetch_int )
return ERROR_INVALID_PARAMETER;
/*
* The column marked with the type stream data seems to have a single number
* which references the column containing the name of the stream data
*
* Fetch the column to reference first.
*/
r = view->ops->fetch_int( view, row, col, &ival );
if( r != ERROR_SUCCESS )
return r;
/* check the column value is in range */
if (ival < 0 || ival > tv->num_cols || ival == col)
{
ERR("bad column ref (%u) for stream\n", ival);
return ERROR_FUNCTION_FAILED;
}
if ( tv->columns[ival - 1].type & MSITYPE_STRING )
{
/* now get the column with the name of the stream */
r = view->ops->fetch_int( view, row, ival, &refcol );
if ( r != ERROR_SUCCESS )
return r;
/* lookup the string value from the string table */
sval = msi_string_lookup_id( tv->db->strings, refcol );
if ( !sval )
return ERROR_INVALID_PARAMETER;
}
else
{
static const WCHAR fmt[] = { '%','d',0 };
sprintfW( number, fmt, ival );
sval = number;
}
len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
full_name = msi_alloc( len*sizeof(WCHAR) );
lstrcpyW( full_name, tv->name );
lstrcatW( full_name, szDot );
lstrcatW( full_name, sval );
r = db_get_raw_stream( tv->db, full_name, stm );
if( r )
ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
msi_free( full_name );
return r;
}
static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
{
UINT offset, n;
if( !tv->table )
return ERROR_INVALID_PARAMETER;
if( (col==0) || (col>tv->num_cols) )
return ERROR_INVALID_PARAMETER;
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;
}
n = bytes_per_column( &tv->columns[col-1] );
switch( n )
{
case 4:
offset = tv->columns[col-1].offset/2;
tv->table->data[row][offset] = val & 0xffff;
tv->table->data[row][offset + 1] = (val>>16)&0xffff;
break;
case 2:
offset = tv->columns[col-1].offset/2;
tv->table->data[row][offset] = val;
break;
default:
ERR("oops! what is %d bytes per column?\n", n );
return ERROR_FUNCTION_FAILED;
}
return ERROR_SUCCESS;
}
static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
UINT i, val, r = ERROR_SUCCESS;
if ( !tv->table )
return ERROR_INVALID_PARAMETER;
/* test if any of the mask bits are invalid */
if ( mask >= (1<<tv->num_cols) )
return ERROR_INVALID_PARAMETER;
for ( i = 0; i < tv->num_cols; i++ )
{
/* only update the fields specified in the mask */
if ( !(mask&(1<<i)) )
continue;
/* FIXME: should we allow updating keys? */
val = 0;
if ( !MSI_RecordIsNull( rec, i + 1 ) )
{
if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
{
val = 1; /* refers to the first key column */
}
else if ( tv->columns[i].type & MSITYPE_STRING )
{
LPCWSTR sval = MSI_RecordGetString( rec, i + 1 );
val = msi_addstringW( tv->db->strings, 0, sval, -1, 1 );
}
else if ( 2 == bytes_per_column( &tv->columns[ i ] ) )
{
val = 0x8000 + MSI_RecordGetInteger( rec, i + 1 );
if ( val & 0xffff0000 )
{
ERR("field %u value %d out of range\n", i+1, val - 0x8000 );
return ERROR_FUNCTION_FAILED;
}
}
else
{
INT ival = MSI_RecordGetInteger( rec, i + 1 );
val = ival ^ 0x80000000;
}
}
r = TABLE_set_int( tv, row, i+1, val );
if ( r != ERROR_SUCCESS )
break;
}
return r;
}
static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
USHORT **p, *row;
UINT sz;
TRACE("%p\n", view);
if( !tv->table )
return ERROR_INVALID_PARAMETER;
row = msi_alloc_zero( tv->row_size );
if( !row )
return ERROR_NOT_ENOUGH_MEMORY;
sz = (tv->table->row_count + 1) * sizeof (UINT*);
if( tv->table->data )
p = msi_realloc( tv->table->data, sz );
else
p = msi_alloc( sz );
if( !p )
{
msi_free( row );
return ERROR_NOT_ENOUGH_MEMORY;
}
tv->table->data = p;
tv->table->data[tv->table->row_count] = row;
*num = tv->table->row_count;
tv->table->row_count++;
return ERROR_SUCCESS;
}
static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
TRACE("%p %p\n", tv, record);
TRACE("There are %d columns\n", tv->num_cols );
tv->table = get_table( tv->db, tv->name, tv->columns, tv->num_cols );
if( !tv->table )
return ERROR_FUNCTION_FAILED;
return ERROR_SUCCESS;
}
static UINT TABLE_close( struct tagMSIVIEW *view )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
TRACE("%p\n", view );
if( !tv->table )
return ERROR_FUNCTION_FAILED;
tv->table = NULL;
return ERROR_SUCCESS;
}
static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
TRACE("%p %p %p\n", view, rows, cols );
if( cols )
*cols = tv->num_cols;
if( rows )
{
if( !tv->table )
return ERROR_INVALID_PARAMETER;
*rows = tv->table->row_count;
}
return ERROR_SUCCESS;
}
static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
TRACE("%p %d %p %p\n", tv, n, name, type );
if( ( n == 0 ) || ( n > tv->num_cols ) )
return ERROR_INVALID_PARAMETER;
if( name )
{
*name = strdupW( tv->columns[n-1].colname );
if( !*name )
return ERROR_FUNCTION_FAILED;
}
if( type )
*type = tv->columns[n-1].type;
return ERROR_SUCCESS;
}
static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
{
UINT r, row, i;
/* check there's no null values where they're not allowed */
for( i = 0; i < tv->num_cols; i++ )
{
if ( tv->columns[i].type & MSITYPE_NULLABLE )
continue;
if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
TRACE("skipping binary column\n");
else if ( tv->columns[i].type & MSITYPE_STRING )
{
LPCWSTR str;
str = MSI_RecordGetString( rec, i+1 );
if (str == NULL || str[0] == 0)
return ERROR_INVALID_DATA;
}
else
{
UINT n;
n = MSI_RecordGetInteger( rec, i+1 );
if (n == MSI_NULL_INTEGER)
return ERROR_INVALID_DATA;
}
}
/* check there's no duplicate keys */
r = msi_table_find_row( tv, rec, &row );
if (r == ERROR_SUCCESS)
return ERROR_INVALID_DATA;
return ERROR_SUCCESS;
}
static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
UINT r, row = -1;
TRACE("%p %p\n", tv, rec );
/* check that the key is unique - can we find a matching row? */
r = table_validate_new( tv, rec );
if( r != ERROR_SUCCESS )
return ERROR_FUNCTION_FAILED;
r = table_create_new_row( view, &row );
TRACE("insert_row returned %08x\n", r);
if( r != ERROR_SUCCESS )
return r;
return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
}
static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec)
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
UINT r;
TRACE("%p %d %p\n", view, eModifyMode, rec );
if (!tv->table)
{
r = TABLE_execute( view, NULL );
if( r )
return r;
}
switch (eModifyMode)
{
case MSIMODIFY_VALIDATE_NEW:
r = table_validate_new( tv, rec );
break;
case MSIMODIFY_INSERT_TEMPORARY:
r = table_validate_new( tv, rec );
if (r != ERROR_SUCCESS)
break;
r = TABLE_insert_row( view, rec );
break;
case MSIMODIFY_REFRESH:
case MSIMODIFY_INSERT:
case MSIMODIFY_UPDATE:
case MSIMODIFY_ASSIGN:
case MSIMODIFY_REPLACE:
case MSIMODIFY_MERGE:
case MSIMODIFY_DELETE:
case MSIMODIFY_VALIDATE:
case MSIMODIFY_VALIDATE_FIELD:
case MSIMODIFY_VALIDATE_DELETE:
FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
r = ERROR_CALL_NOT_IMPLEMENTED;
break;
default:
r = ERROR_INVALID_DATA;
}
return r;
}
static UINT TABLE_delete( struct tagMSIVIEW *view )
{
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
TRACE("%p\n", view );
tv->table = NULL;
if( tv->columns )
{
msi_free_colinfo( tv->columns, tv->num_cols );
msi_free( tv->columns );
}
tv->columns = NULL;
msi_free( tv );
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 = (*handle)->next;
while (entry && entry->value != val)
entry = entry->next;
*handle = entry;
if (!entry)
return ERROR_NO_MORE_ITEMS;
*row = entry->row;
return ERROR_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -