Commit 17ba7419 authored by James Hawkins's avatar James Hawkins Committed by Alexandre Julliard

msi: Handle adding columns in transforms.

parent 9285351a
...@@ -1944,8 +1944,34 @@ static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row ) ...@@ -1944,8 +1944,34 @@ static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
return r; return r;
} }
static void msi_update_table_columns( MSIDATABASE *db, LPWSTR name )
{
MSITABLE *table;
UINT size, offset;
int n;
table = find_cached_table( db, name );
msi_free( table->colinfo );
table_get_column_info( db, name, &table->colinfo, &table->col_count );
size = msi_table_get_row_size( table->colinfo, table->col_count );
offset = table->colinfo[table->col_count - 1].offset;
for ( n = 0; n < table->row_count; n++ )
{
table->data[n] = msi_realloc( table->data[n], size );
table->data[n][offset] = (BYTE)MSI_NULL_INTEGER;
}
}
typedef struct
{
struct list entry;
LPWSTR name;
} TRANSFORMDATA;
static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
string_table *st, LPCWSTR name, string_table *st, TRANSFORMDATA *transform,
UINT bytes_per_strref ) UINT bytes_per_strref )
{ {
UINT rawsize = 0; UINT rawsize = 0;
...@@ -1955,6 +1981,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, ...@@ -1955,6 +1981,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
MSIRECORD *rec = NULL; MSIRECORD *rec = NULL;
UINT colcol = 0; UINT colcol = 0;
WCHAR coltable[32]; WCHAR coltable[32];
LPWSTR name = transform->name;
coltable[0] = 0; coltable[0] = 0;
TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) ); TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
...@@ -2038,34 +2065,41 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, ...@@ -2038,34 +2065,41 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
{ {
if ( mask & 1 ) if ( mask & 1 )
{ {
WCHAR table[32];
DWORD sz = 32;
UINT number = MSI_NULL_INTEGER;
TRACE("inserting record\n"); TRACE("inserting record\n");
/*
* Native msi seems writes nul into the
* Number (2nd) column of the _Columns table.
* Not sure that it's deliberate...
*/
if (!lstrcmpW(name, szColumns)) if (!lstrcmpW(name, szColumns))
{ {
WCHAR table[32];
DWORD sz = 32;
MSI_RecordGetStringW( rec, 1, table, &sz ); MSI_RecordGetStringW( rec, 1, table, &sz );
number = MSI_RecordGetInteger( rec, 2 );
/* reset the column number on a new table */ /*
if ( lstrcmpW(coltable, table) ) * Native msi seems writes nul into the Number (2nd) column of
* the _Columns table, only when the columns are from a new table
*/
if ( number == MSI_NULL_INTEGER )
{ {
colcol = 0; /* reset the column number on a new table */
lstrcpyW( coltable, table ); if ( lstrcmpW(coltable, table) )
{
colcol = 0;
lstrcpyW( coltable, table );
}
/* fix nul column numbers */
MSI_RecordSetInteger( rec, 2, ++colcol );
} }
/* fix nul column numbers */
MSI_RecordSetInteger( rec, 2, ++colcol );
} }
r = TABLE_insert_row( &tv->view, rec, FALSE ); r = TABLE_insert_row( &tv->view, rec, FALSE );
if (r != ERROR_SUCCESS) if (r != ERROR_SUCCESS)
ERR("insert row failed\n"); ERR("insert row failed\n");
if ( number != MSI_NULL_INTEGER && !lstrcmpW(name, szColumns) )
msi_update_table_columns( db, table );
} }
else else
{ {
...@@ -2108,11 +2142,12 @@ err: ...@@ -2108,11 +2142,12 @@ err:
*/ */
UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
{ {
struct list transforms;
IEnumSTATSTG *stgenum = NULL; IEnumSTATSTG *stgenum = NULL;
TRANSFORMDATA *transform;
TRANSFORMDATA *tables = NULL, *columns = NULL;
HRESULT r; HRESULT r;
STATSTG stat; STATSTG stat;
ULONG count;
WCHAR name[0x40];
string_table *strings; string_table *strings;
UINT ret = ERROR_FUNCTION_FAILED; UINT ret = ERROR_FUNCTION_FAILED;
UINT bytes_per_strref; UINT bytes_per_strref;
...@@ -2127,40 +2162,84 @@ UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) ...@@ -2127,40 +2162,84 @@ UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
if( FAILED( r ) ) if( FAILED( r ) )
goto end; goto end;
list_init(&transforms);
while ( TRUE )
{
MSITABLEVIEW *tv = NULL;
WCHAR name[0x40];
ULONG count = 0;
r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
if ( FAILED( r ) || !count )
break;
decode_streamname( stat.pwcsName, name );
if ( name[0] != 0x4840 )
continue;
if ( !lstrcmpW( name+1, szStringPool ) ||
!lstrcmpW( name+1, szStringData ) )
continue;
transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
if ( !transform )
break;
list_add_tail( &transforms, &transform->entry );
transform->name = strdupW( name + 1 );
if ( !lstrcmpW( transform->name, szTables ) )
tables = transform;
else if (!lstrcmpW( transform->name, szColumns ) )
columns = transform;
TRACE("transform contains stream %s\n", debugstr_w(name));
/* load the table */
r = TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv );
if( r != ERROR_SUCCESS )
continue;
r = tv->view.ops->execute( &tv->view, NULL );
if( r != ERROR_SUCCESS )
{
tv->view.ops->delete( &tv->view );
continue;
}
tv->view.ops->delete( &tv->view );
}
/* /*
* Apply _Tables and _Columns transforms first so that * Apply _Tables and _Columns transforms first so that
* the table metadata is correct, and empty tables exist. * the table metadata is correct, and empty tables exist.
*/ */
ret = msi_table_load_transform( db, stg, strings, szTables, bytes_per_strref ); ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref );
if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
goto end; goto end;
ret = msi_table_load_transform( db, stg, strings, szColumns, bytes_per_strref ); ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref );
if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
goto end; goto end;
ret = ERROR_SUCCESS; ret = ERROR_SUCCESS;
while( r == ERROR_SUCCESS ) while ( !list_empty( &transforms ) )
{ {
count = 0; transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
if( FAILED( r ) || !count )
break;
decode_streamname( stat.pwcsName, name );
if ( name[0] != 0x4840 )
continue;
TRACE("transform contains stream %s\n", debugstr_w(name));
if ( !lstrcmpW( name+1, szStringPool ) || if ( lstrcmpW( transform->name, szColumns ) &&
!lstrcmpW( name+1, szStringData ) || lstrcmpW( transform->name, szTables ) &&
!lstrcmpW( name+1, szColumns ) || ret == ERROR_SUCCESS )
!lstrcmpW( name+1, szTables ) ) {
continue; ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref );
}
ret = msi_table_load_transform( db, stg, strings, name+1, bytes_per_strref ); list_remove( &transform->entry );
msi_free( transform->name );
msi_free( transform );
} }
if ( ret == ERROR_SUCCESS ) if ( ret == ERROR_SUCCESS )
......
...@@ -2049,30 +2049,21 @@ static void test_try_transform(void) ...@@ -2049,30 +2049,21 @@ static void test_try_transform(void)
hrec = 0; hrec = 0;
query = "select `NOO`,`OOO` from `MOO` where `NOO` = 1 AND `OOO` = 'c'"; query = "select `NOO`,`OOO` from `MOO` where `NOO` = 1 AND `OOO` = 'c'";
r = do_query(hdb, query, &hrec); r = do_query(hdb, query, &hrec);
todo_wine ok(r == ERROR_SUCCESS, "select query failed\n");
{
ok(r == ERROR_SUCCESS, "select query failed\n");
}
MsiCloseHandle(hrec); MsiCloseHandle(hrec);
/* check unchanged value */ /* check unchanged value */
hrec = 0; hrec = 0;
query = "select `NOO`,`OOO` from `MOO` where `NOO` = 2 AND `OOO` = 'b'"; query = "select `NOO`,`OOO` from `MOO` where `NOO` = 2 AND `OOO` = 'b'";
r = do_query(hdb, query, &hrec); r = do_query(hdb, query, &hrec);
todo_wine ok(r == ERROR_SUCCESS, "select query failed\n");
{
ok(r == ERROR_SUCCESS, "select query failed\n");
}
MsiCloseHandle(hrec); MsiCloseHandle(hrec);
/* check deleted value */ /* check deleted value */
hrec = 0; hrec = 0;
query = "select * from `MOO` where `NOO` = 3"; query = "select * from `MOO` where `NOO` = 3";
r = do_query(hdb, query, &hrec); r = do_query(hdb, query, &hrec);
todo_wine ok(r == ERROR_NO_MORE_ITEMS, "select query failed\n");
{
ok(r == ERROR_NO_MORE_ITEMS, "select query failed\n");
}
if (hrec) MsiCloseHandle(hrec); if (hrec) MsiCloseHandle(hrec);
/* check added stream */ /* check added stream */
...@@ -2093,67 +2084,40 @@ static void test_try_transform(void) ...@@ -2093,67 +2084,40 @@ static void test_try_transform(void)
hrec = 0; hrec = 0;
query = "select * from `MOO`"; query = "select * from `MOO`";
r = MsiDatabaseOpenView(hdb, query, &hview); r = MsiDatabaseOpenView(hdb, query, &hview);
todo_wine ok(r == ERROR_SUCCESS, "open view failed\n");
{
ok(r == ERROR_SUCCESS, "open view failed\n");
}
r = MsiViewExecute(hview, 0); r = MsiViewExecute(hview, 0);
todo_wine ok(r == ERROR_SUCCESS, "view execute failed\n");
{
ok(r == ERROR_SUCCESS, "view execute failed\n");
}
r = MsiViewFetch(hview, &hrec); r = MsiViewFetch(hview, &hrec);
todo_wine ok(r == ERROR_SUCCESS, "view fetch failed\n");
{
ok(r == ERROR_SUCCESS, "view fetch failed\n");
}
r = MsiRecordGetInteger(hrec, 1); r = MsiRecordGetInteger(hrec, 1);
todo_wine ok(r == 1, "Expected 1, got %d\n", r);
{
ok(r == 1, "Expected 1, got %d\n", r);
}
sz = sizeof buffer; sz = sizeof buffer;
r = MsiRecordGetString(hrec, 2, buffer, &sz); r = MsiRecordGetString(hrec, 2, buffer, &sz);
todo_wine ok(r == ERROR_SUCCESS, "record get string failed\n");
{ ok(!lstrcmpA(buffer, "c"), "Expected c, got %s\n", buffer);
ok(r == ERROR_SUCCESS, "record get string failed\n");
ok(!lstrcmpA(buffer, "c"), "Expected c, got %s\n", buffer);
}
r = MsiRecordGetInteger(hrec, 3); r = MsiRecordGetInteger(hrec, 3);
ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r); ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r);
r = MsiRecordGetInteger(hrec, 4); r = MsiRecordGetInteger(hrec, 4);
todo_wine ok(r == 5, "Expected 5, got %d\n", r);
{
ok(r == 5, "Expected 5, got %d\n", r);
}
MsiCloseHandle(hrec); MsiCloseHandle(hrec);
r = MsiViewFetch(hview, &hrec); r = MsiViewFetch(hview, &hrec);
todo_wine ok(r == ERROR_SUCCESS, "view fetch failed\n");
{
ok(r == ERROR_SUCCESS, "view fetch failed\n");
}
r = MsiRecordGetInteger(hrec, 1); r = MsiRecordGetInteger(hrec, 1);
todo_wine ok(r == 2, "Expected 2, got %d\n", r);
{
ok(r == 2, "Expected 2, got %d\n", r);
}
sz = sizeof buffer; sz = sizeof buffer;
r = MsiRecordGetString(hrec, 2, buffer, &sz); r = MsiRecordGetString(hrec, 2, buffer, &sz);
todo_wine ok(r == ERROR_SUCCESS, "record get string failed\n");
{ ok(!lstrcmpA(buffer, "b"), "Expected b, got %s\n", buffer);
ok(r == ERROR_SUCCESS, "record get string failed\n");
ok(!lstrcmpA(buffer, "b"), "Expected b, got %s\n", buffer);
}
r = MsiRecordGetInteger(hrec, 3); r = MsiRecordGetInteger(hrec, 3);
ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r); ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r);
...@@ -2164,10 +2128,7 @@ static void test_try_transform(void) ...@@ -2164,10 +2128,7 @@ static void test_try_transform(void)
MsiCloseHandle(hrec); MsiCloseHandle(hrec);
r = MsiViewFetch(hview, &hrec); r = MsiViewFetch(hview, &hrec);
todo_wine ok(r == ERROR_NO_MORE_ITEMS, "view fetch succeeded\n");
{
ok(r == ERROR_NO_MORE_ITEMS, "view fetch succeeded\n");
}
MsiCloseHandle(hrec); MsiCloseHandle(hrec);
MsiCloseHandle(hview); MsiCloseHandle(hview);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment