Implement transforms. This still includes some debugging code which
can be enabled by setting debug_transform to 1 in the relevant places.
This commit is contained in:
parent
0989cfae25
commit
1cd26d25fc
|
@ -67,6 +67,7 @@ static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
|
|||
DWORD r;
|
||||
|
||||
free_cached_tables( db );
|
||||
msi_free_transforms( db );
|
||||
msi_destroy_stringtable( db->strings );
|
||||
r = IStorage_Release( db->storage );
|
||||
if( r )
|
||||
|
@ -156,6 +157,7 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
|
|||
db->storage = stg;
|
||||
db->mode = szMode;
|
||||
list_init( &db->tables );
|
||||
list_init( &db->transforms );
|
||||
|
||||
db->strings = load_string_table( stg );
|
||||
if( !db->strings )
|
||||
|
|
|
@ -67,6 +67,7 @@ typedef struct tagMSIDATABASE
|
|||
string_table *strings;
|
||||
LPCWSTR mode;
|
||||
struct list tables;
|
||||
struct list transforms;
|
||||
} MSIDATABASE;
|
||||
|
||||
typedef struct tagMSIVIEW MSIVIEW;
|
||||
|
@ -279,6 +280,7 @@ extern void msiobj_lock(MSIOBJECTHDR *);
|
|||
extern void msiobj_unlock(MSIOBJECTHDR *);
|
||||
|
||||
extern void free_cached_tables( MSIDATABASE *db );
|
||||
extern void msi_free_transforms( MSIDATABASE *db );
|
||||
extern string_table *load_string_table( IStorage *stg );
|
||||
extern UINT MSI_CommitTables( MSIDATABASE *db );
|
||||
extern HRESULT init_string_table( IStorage *stg );
|
||||
|
|
393
dlls/msi/table.c
393
dlls/msi/table.c
|
@ -56,6 +56,11 @@ struct tagMSITABLE
|
|||
WCHAR name[1];
|
||||
};
|
||||
|
||||
typedef struct tagMSITRANSFORM {
|
||||
struct list entry;
|
||||
IStorage *stg;
|
||||
} MSITRANSFORM;
|
||||
|
||||
#define MAX_STREAM_NAME 0x1f
|
||||
|
||||
static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
|
||||
|
@ -276,14 +281,23 @@ UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
|
|||
|
||||
r = IStorage_OpenStream(db->storage, encname, NULL,
|
||||
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
|
||||
msi_free( encname );
|
||||
if( FAILED( r ) )
|
||||
{
|
||||
WARN("open stream failed r = %08lx - empty table?\n",r);
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
MSITRANSFORM *transform;
|
||||
|
||||
LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
|
||||
{
|
||||
TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
|
||||
r = IStorage_OpenStream( transform->stg, encname, NULL,
|
||||
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
|
||||
if (SUCCEEDED(r))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
msi_free( encname );
|
||||
|
||||
return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
|
||||
|
@ -1288,7 +1302,8 @@ static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
|
|||
debugstr_w(tv->columns[i].colname) );
|
||||
|
||||
val = 0;
|
||||
if( tv->columns[i].type & MSITYPE_STRING )
|
||||
if( (tv->columns[i].type & MSITYPE_STRING ) &&
|
||||
(tv->columns[i].type & 0xff ) )
|
||||
{
|
||||
/* keys can't be null */
|
||||
str = MSI_RecordGetString( rec, i+1 );
|
||||
|
@ -1321,38 +1336,32 @@ static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
|
||||
static UINT msi_table_modify_row( MSITABLEVIEW *tv, MSIRECORD *rec,
|
||||
UINT row, UINT mask )
|
||||
{
|
||||
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
|
||||
UINT n, type, val, r, row, col_count = 0;
|
||||
UINT i, val, r = ERROR_SUCCESS;
|
||||
|
||||
r = TABLE_get_dimensions( view, NULL, &col_count );
|
||||
if( r )
|
||||
return r;
|
||||
TRACE("%p %p %u %08x\n", tv, rec, row, mask );
|
||||
|
||||
row = -1;
|
||||
r = table_create_new_row( view, &row );
|
||||
TRACE("insert_row returned %08x\n", r);
|
||||
if( r )
|
||||
return r;
|
||||
|
||||
for( n = 1; n <= col_count; n++ )
|
||||
for( i = 0; i < tv->num_cols; i++ )
|
||||
{
|
||||
r = TABLE_get_column_info( view, n, NULL, &type );
|
||||
if( r )
|
||||
break;
|
||||
/* set keys or values specified in the mask */
|
||||
if( (~tv->columns[i].type & MSITYPE_KEY) && (~mask & (1<<i)) )
|
||||
continue;
|
||||
|
||||
if( type & MSITYPE_STRING )
|
||||
if( (tv->columns[i].type & MSITYPE_STRING) &&
|
||||
! MSITYPE_IS_BINARY(tv->columns[i].type) )
|
||||
{
|
||||
const WCHAR *str = MSI_RecordGetString( rec, n );
|
||||
const WCHAR *str = MSI_RecordGetString( rec, i+1 );
|
||||
val = msi_addstringW( tv->db->strings, 0, str, -1, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
val = MSI_RecordGetInteger( rec, n );
|
||||
val ^= 0x8000;
|
||||
val = MSI_RecordGetInteger( rec, i+1 );
|
||||
if ( 2 == bytes_per_column( &tv->columns[i] ) )
|
||||
val ^= 0x8000;
|
||||
}
|
||||
r = TABLE_set_int( view, row, n, val );
|
||||
r = TABLE_set_int( &tv->view, row, i+1, val );
|
||||
if( r )
|
||||
break;
|
||||
}
|
||||
|
@ -1360,6 +1369,21 @@ static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
|
|||
return r;
|
||||
}
|
||||
|
||||
static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
|
||||
{
|
||||
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
|
||||
UINT r, row = -1;
|
||||
|
||||
TRACE("%p %p\n", tv, rec );
|
||||
|
||||
r = table_create_new_row( view, &row );
|
||||
TRACE("insert_row returned %08x\n", r);
|
||||
if( r != ERROR_SUCCESS )
|
||||
return r;
|
||||
|
||||
return msi_table_modify_row( tv, rec, row, ~0 );
|
||||
}
|
||||
|
||||
static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
|
||||
MSIRECORD *rec)
|
||||
{
|
||||
|
@ -1533,10 +1557,297 @@ UINT MSI_CommitTables( MSIDATABASE *db )
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
|
||||
{
|
||||
UINT i, val, ofs = 0;
|
||||
USHORT mask = *rawdata++;
|
||||
MSICOLUMNINFO *columns = tv->columns;
|
||||
MSIRECORD *rec;
|
||||
const int debug_transform = 0;
|
||||
|
||||
rec = MSI_CreateRecord( tv->num_cols );
|
||||
if( !rec )
|
||||
return rec;
|
||||
|
||||
if( debug_transform ) MESSAGE("row -> ");
|
||||
for( i=0; i<tv->num_cols; i++ )
|
||||
{
|
||||
UINT n = bytes_per_column( &columns[i] );
|
||||
|
||||
if ( (mask&1) && (i>=(mask>>8)) )
|
||||
break;
|
||||
/* all keys must be present */
|
||||
if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
|
||||
continue;
|
||||
|
||||
switch( n )
|
||||
{
|
||||
case 2:
|
||||
val = rawdata[ofs];
|
||||
if( (columns[i].type & MSITYPE_STRING) &&
|
||||
! MSITYPE_IS_BINARY(tv->columns[i].type) )
|
||||
{
|
||||
LPCWSTR sval = msi_string_lookup_id( st, val );
|
||||
MSI_RecordSetStringW( rec, i+1, sval );
|
||||
if( debug_transform ) MESSAGE("[%s]", debugstr_w(sval));
|
||||
}
|
||||
else
|
||||
{
|
||||
val ^= 0x8000;
|
||||
MSI_RecordSetInteger( rec, i+1, val );
|
||||
if( debug_transform) MESSAGE("[0x%04x]", val );
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
val = rawdata[ofs] + (rawdata[ofs + 1]<<16);
|
||||
/* val ^= 0x80000000; */
|
||||
MSI_RecordSetInteger( rec, i+1, val );
|
||||
if( debug_transform ) MESSAGE("[0x%08x]", val );
|
||||
break;
|
||||
default:
|
||||
ERR("oops - unknown column width %d\n", n);
|
||||
break;
|
||||
}
|
||||
ofs += n/2;
|
||||
}
|
||||
if( debug_transform) MESSAGE("\n");
|
||||
return rec;
|
||||
}
|
||||
|
||||
static void dump_record( MSIRECORD *rec )
|
||||
{
|
||||
UINT i, n;
|
||||
|
||||
MESSAGE("row -> ");
|
||||
n = MSI_RecordGetFieldCount( rec );
|
||||
for( i=1; i<=n; i++ )
|
||||
{
|
||||
LPCWSTR sval = MSI_RecordGetString( rec, i );
|
||||
|
||||
if( MSI_RecordIsNull( rec, i ) )
|
||||
MESSAGE("[]");
|
||||
else if( (sval = MSI_RecordGetString( rec, i )) )
|
||||
MESSAGE("[%s]", debugstr_w(sval));
|
||||
else
|
||||
MESSAGE("[0x%08x]", MSI_RecordGetInteger( rec, i ) );
|
||||
}
|
||||
MESSAGE("\n");
|
||||
}
|
||||
|
||||
static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
|
||||
{
|
||||
LPCWSTR sval;
|
||||
UINT i;
|
||||
|
||||
for( i=0; i<(rawsize/2); i++ )
|
||||
{
|
||||
sval = msi_string_lookup_id( st, rawdata[i] );
|
||||
if( !sval ) sval = (WCHAR[]) {0};
|
||||
MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
|
||||
}
|
||||
}
|
||||
|
||||
static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
|
||||
{
|
||||
LPCWSTR str;
|
||||
UINT i, r, *data;
|
||||
|
||||
data = msi_alloc( tv->num_cols *sizeof (UINT) );
|
||||
for( i=0; i<tv->num_cols; i++ )
|
||||
{
|
||||
data[i] = 0;
|
||||
|
||||
if ( ~tv->columns[i].type & MSITYPE_KEY )
|
||||
continue;
|
||||
|
||||
/* turn the transform column value into a row value */
|
||||
if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
|
||||
! MSITYPE_IS_BINARY(tv->columns[i].type) )
|
||||
{
|
||||
str = MSI_RecordGetString( rec, i+1 );
|
||||
r = msi_string2idW( tv->db->strings, str, &data[i] );
|
||||
|
||||
/* if there's no matching string in the string table,
|
||||
these keys can't match any record, so fail now. */
|
||||
if( ERROR_SUCCESS != r )
|
||||
{
|
||||
msi_free( data );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
data[i] = MSI_RecordGetInteger( rec, i+1 );
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
|
||||
{
|
||||
UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
|
||||
|
||||
for( i=0; i<tv->num_cols; i++ )
|
||||
{
|
||||
if ( ~tv->columns[i].type & MSITYPE_KEY )
|
||||
continue;
|
||||
|
||||
/* turn the transform column value into a row value */
|
||||
r = TABLE_fetch_int( &tv->view, row, i+1, &x );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
{
|
||||
ERR("TABLE_fetch_int shouldn't fail here\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* if this key matches, move to the next column */
|
||||
if ( x != data[i] )
|
||||
{
|
||||
ret = ERROR_FUNCTION_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
|
||||
{
|
||||
UINT i, r = ERROR_FUNCTION_FAILED, *data;
|
||||
|
||||
data = msi_record_to_row( tv, rec );
|
||||
if( !data )
|
||||
return r;
|
||||
for( i=0; i<tv->table->row_count; i++ )
|
||||
{
|
||||
r = msi_row_matches( tv, i, data );
|
||||
if( r == ERROR_SUCCESS )
|
||||
{
|
||||
*row = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
msi_free( data );
|
||||
return r;
|
||||
}
|
||||
|
||||
static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
|
||||
{
|
||||
UINT i;
|
||||
for( i=1; i<=tv->num_cols; i++ )
|
||||
tv->view.ops->set_int( &tv->view, row, i, 0 );
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
|
||||
string_table *st, LPCWSTR name )
|
||||
{
|
||||
FIXME("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
|
||||
UINT rawsize = 0;
|
||||
USHORT *rawdata = NULL;
|
||||
MSITABLEVIEW *tv = NULL;
|
||||
UINT r, n, sz, i, mask;
|
||||
MSIRECORD *rec = NULL;
|
||||
const int debug_transform = 0;
|
||||
|
||||
TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
|
||||
|
||||
/* create a table view */
|
||||
r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
|
||||
if( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
r = tv->view.ops->execute( &tv->view, NULL );
|
||||
if( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
/* read the transform data */
|
||||
r = ERROR_FUNCTION_FAILED;
|
||||
read_stream_data( stg, name, &rawdata, &rawsize );
|
||||
if( !rawdata || (rawsize < 2) )
|
||||
{
|
||||
ERR("odd sized transform for table %s\n", debugstr_w(name));
|
||||
goto err;
|
||||
}
|
||||
|
||||
TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
|
||||
debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
|
||||
|
||||
/* interpret the data */
|
||||
r = ERROR_SUCCESS;
|
||||
for( n=0; n < (rawsize/2); )
|
||||
{
|
||||
mask = rawdata[n];
|
||||
|
||||
if (mask&1)
|
||||
{
|
||||
/*
|
||||
* if the low bit is set, columns are continuous and
|
||||
* the number of columns is specified in the high byte
|
||||
*/
|
||||
sz = 2 + tv->row_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If the low bit is not set, rowdata[n] is a bitmask.
|
||||
* Excepting for key fields, which are always present,
|
||||
* each bit indicates that a field is present in the transform record.
|
||||
*
|
||||
* rawdata[n] == 0 is a special case ... only the keys will be present
|
||||
* and it means that this row should be deleted.
|
||||
*/
|
||||
sz = 2;
|
||||
for( i=0; i<tv->num_cols; i++ )
|
||||
{
|
||||
if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
|
||||
sz += bytes_per_column( &tv->columns[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/* check we didn't run of the end of the table */
|
||||
if ( (n+sz) > rawsize )
|
||||
{
|
||||
ERR("borked.\n");
|
||||
dump_table( st, rawdata, rawsize );
|
||||
break;
|
||||
}
|
||||
|
||||
rec = msi_get_transform_record( tv, st, &rawdata[n] );
|
||||
if (rec)
|
||||
{
|
||||
UINT row = 0;
|
||||
|
||||
r = msi_table_find_row( tv, rec, &row );
|
||||
|
||||
if( rawdata[n] & 1)
|
||||
{
|
||||
if( debug_transform ) MESSAGE("insert [%d]: ", row);
|
||||
TABLE_insert_row( &tv->view, rec );
|
||||
}
|
||||
else if( mask & 0xff )
|
||||
{
|
||||
if( debug_transform ) MESSAGE("modify [%d]: ", row);
|
||||
msi_table_modify_row( tv, rec, row, mask );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( debug_transform ) MESSAGE("delete [%d]: ", row);
|
||||
msi_delete_row( tv, row );
|
||||
}
|
||||
if( debug_transform ) dump_record( rec );
|
||||
msiobj_release( &rec->hdr );
|
||||
}
|
||||
|
||||
n += sz/2;
|
||||
|
||||
}
|
||||
|
||||
err:
|
||||
/* no need to free the table, it's associated with the database */
|
||||
msi_free( rawdata );
|
||||
if( tv )
|
||||
tv->view.ops->delete( &tv->view );
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1555,6 +1866,8 @@ UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
|
|||
string_table *strings;
|
||||
UINT ret = ERROR_FUNCTION_FAILED;
|
||||
|
||||
TRACE("%p %p\n", db, stg );
|
||||
|
||||
strings = load_string_table( stg );
|
||||
if( !strings )
|
||||
goto end;
|
||||
|
@ -1574,12 +1887,22 @@ UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
|
|||
break;
|
||||
decode_streamname( stat.pwcsName, name );
|
||||
if( ( name[0] == 0x4840 ) && ( name[1] != '_' ) )
|
||||
r = msi_table_load_transform( db, stg, strings, name+1 );
|
||||
ret = msi_table_load_transform( db, stg, strings, name+1 );
|
||||
else
|
||||
TRACE("non-table stream %s\n", debugstr_w(name) );
|
||||
TRACE("transform contains stream %s\n", debugstr_w(name));
|
||||
n++;
|
||||
}
|
||||
|
||||
if ( ret == ERROR_SUCCESS )
|
||||
{
|
||||
MSITRANSFORM *t;
|
||||
|
||||
t = msi_alloc( sizeof *t );
|
||||
t->stg = stg;
|
||||
IStorage_AddRef( stg );
|
||||
list_add_tail( &db->transforms, &t->entry );
|
||||
}
|
||||
|
||||
end:
|
||||
if ( stgenum )
|
||||
IEnumSTATSTG_Release( stgenum );
|
||||
|
@ -1588,3 +1911,15 @@ end:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void msi_free_transforms( MSIDATABASE *db )
|
||||
{
|
||||
while( !list_empty( &db->transforms ) )
|
||||
{
|
||||
MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
|
||||
MSITRANSFORM, entry );
|
||||
list_remove( &t->entry );
|
||||
IStorage_Release( t->stg );
|
||||
msi_free( t );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue