Fix and test MsiViewGetColumnInfo and binary fields.
This commit is contained in:
parent
3c37734f96
commit
8e74308a10
|
@ -34,11 +34,12 @@
|
||||||
|
|
||||||
#define MSI_DATASIZEMASK 0x00ff
|
#define MSI_DATASIZEMASK 0x00ff
|
||||||
#define MSITYPE_VALID 0x0100
|
#define MSITYPE_VALID 0x0100
|
||||||
|
#define MSITYPE_LOCALIZABLE 0x200
|
||||||
#define MSITYPE_STRING 0x0800
|
#define MSITYPE_STRING 0x0800
|
||||||
#define MSITYPE_NULLABLE 0x1000
|
#define MSITYPE_NULLABLE 0x1000
|
||||||
#define MSITYPE_KEY 0x2000
|
#define MSITYPE_KEY 0x2000
|
||||||
|
|
||||||
#define MSITYPE_BINARY 0x8900
|
#define MSITYPE_IS_BINARY(type) (((type) & ~MSITYPE_NULLABLE) == (MSITYPE_STRING|MSITYPE_VALID))
|
||||||
|
|
||||||
struct tagMSITABLE;
|
struct tagMSITABLE;
|
||||||
typedef struct tagMSITABLE MSITABLE;
|
typedef struct tagMSITABLE MSITABLE;
|
||||||
|
|
|
@ -294,8 +294,7 @@ UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
|
||||||
ERR("Error getting column type for %d\n", i );
|
ERR("Error getting column type for %d\n", i );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
|
if (!MSITYPE_IS_BINARY(type))
|
||||||
MSITYPE_NULLABLE)))
|
|
||||||
{
|
{
|
||||||
ret = view->ops->fetch_int( view, query->row, i, &ival );
|
ret = view->ops->fetch_int( view, query->row, i, &ival );
|
||||||
if( ret )
|
if( ret )
|
||||||
|
@ -449,6 +448,29 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
|
||||||
|
{
|
||||||
|
static const WCHAR fmt[] = { '%','d',0 };
|
||||||
|
WCHAR szType[0x10];
|
||||||
|
|
||||||
|
if (MSITYPE_IS_BINARY(type))
|
||||||
|
szType[0] = 'v';
|
||||||
|
else if (type & MSITYPE_LOCALIZABLE)
|
||||||
|
szType[0] = 'l';
|
||||||
|
else if (type & MSITYPE_STRING)
|
||||||
|
szType[0] = 's';
|
||||||
|
else
|
||||||
|
szType[0] = 'i';
|
||||||
|
if (type & MSITYPE_NULLABLE)
|
||||||
|
szType[0] &= ~0x20;
|
||||||
|
|
||||||
|
sprintfW( &szType[1], fmt, (type&0xff) );
|
||||||
|
|
||||||
|
TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
|
||||||
|
|
||||||
|
return MSI_RecordSetStringW( rec, field, szType );
|
||||||
|
}
|
||||||
|
|
||||||
UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
|
UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
|
||||||
{
|
{
|
||||||
MSIVIEW *view = NULL;
|
MSIVIEW *view = NULL;
|
||||||
|
@ -459,6 +481,12 @@ UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hR
|
||||||
|
|
||||||
TRACE("%ld %d %p\n", hView, info, hRec);
|
TRACE("%ld %d %p\n", hView, info, hRec);
|
||||||
|
|
||||||
|
if( !hRec )
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
|
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
|
||||||
if( !query )
|
if( !query )
|
||||||
return ERROR_INVALID_HANDLE;
|
return ERROR_INVALID_HANDLE;
|
||||||
|
@ -492,7 +520,10 @@ UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hR
|
||||||
r = view->ops->get_column_info( view, i+1, &name, &type );
|
r = view->ops->get_column_info( view, i+1, &name, &type );
|
||||||
if( r != ERROR_SUCCESS )
|
if( r != ERROR_SUCCESS )
|
||||||
continue;
|
continue;
|
||||||
MSI_RecordSetStringW( rec, i+1, name );
|
if (info == MSICOLINFO_NAMES)
|
||||||
|
MSI_RecordSetStringW( rec, i+1, name );
|
||||||
|
else
|
||||||
|
msi_set_record_type_string( rec, i+1, type);
|
||||||
msi_free( name );
|
msi_free( name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -258,19 +258,18 @@ column_and_type:
|
||||||
column column_type
|
column column_type
|
||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
$$->type = $2;
|
$$->type = $2 | MSITYPE_VALID;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
column_type:
|
column_type:
|
||||||
data_type_l
|
data_type_l
|
||||||
{
|
{
|
||||||
$$ = $1 | MSITYPE_VALID;
|
$$ = $1;
|
||||||
}
|
}
|
||||||
| data_type_l TK_LOCALIZABLE
|
| data_type_l TK_LOCALIZABLE
|
||||||
{
|
{
|
||||||
FIXME("LOCALIZABLE ignored\n");
|
$$ = $1 | MSITYPE_LOCALIZABLE;
|
||||||
$$ = $1 | MSITYPE_VALID;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -312,7 +311,7 @@ data_type:
|
||||||
}
|
}
|
||||||
| TK_OBJECT
|
| TK_OBJECT
|
||||||
{
|
{
|
||||||
$$ = 0;
|
$$ = MSITYPE_STRING | MSITYPE_VALID;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -817,13 +817,13 @@ static const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 };
|
||||||
static const WCHAR szType[] = { 'T','y','p','e',0 };
|
static const WCHAR szType[] = { 'T','y','p','e',0 };
|
||||||
|
|
||||||
static const MSICOLUMNINFO _Columns_cols[4] = {
|
static const MSICOLUMNINFO _Columns_cols[4] = {
|
||||||
{ szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 32, 0 },
|
{ szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
|
||||||
{ szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 },
|
{ szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 },
|
||||||
{ szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 32, 4 },
|
{ szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
|
||||||
{ szColumns, 4, szType, MSITYPE_VALID | 2, 6 },
|
{ szColumns, 4, szType, MSITYPE_VALID | 2, 6 },
|
||||||
};
|
};
|
||||||
static const MSICOLUMNINFO _Tables_cols[1] = {
|
static const MSICOLUMNINFO _Tables_cols[1] = {
|
||||||
{ szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | 32, 0 },
|
{ szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
|
static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
|
||||||
|
@ -930,7 +930,7 @@ static UINT get_tablecolumns( MSIDATABASE *db,
|
||||||
colinfo[n].tablename = MSI_makestring( db, table_id );
|
colinfo[n].tablename = MSI_makestring( db, table_id );
|
||||||
colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
|
colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
|
||||||
colinfo[n].colname = MSI_makestring( db, id );
|
colinfo[n].colname = MSI_makestring( db, id );
|
||||||
colinfo[n].type = table->data[ i ] [ 3 ];
|
colinfo[n].type = table->data[ i ] [ 3 ] ^ 0x8000;
|
||||||
/* this assumes that columns are in order in the table */
|
/* this assumes that columns are in order in the table */
|
||||||
if( n )
|
if( n )
|
||||||
colinfo[n].offset = colinfo[n-1].offset
|
colinfo[n].offset = colinfo[n-1].offset
|
||||||
|
|
|
@ -480,6 +480,231 @@ static void test_viewmodify(void)
|
||||||
ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
|
ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MSIHANDLE create_db(void)
|
||||||
|
{
|
||||||
|
MSIHANDLE hdb = 0;
|
||||||
|
UINT res;
|
||||||
|
|
||||||
|
DeleteFile(msifile);
|
||||||
|
|
||||||
|
/* create an empty database */
|
||||||
|
res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
|
||||||
|
ok( res == ERROR_SUCCESS , "Failed to create database\n" );
|
||||||
|
if( res != ERROR_SUCCESS )
|
||||||
|
return hdb;
|
||||||
|
|
||||||
|
res = MsiDatabaseCommit( hdb );
|
||||||
|
ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
|
||||||
|
|
||||||
|
return hdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_getcolinfo(void)
|
||||||
|
{
|
||||||
|
MSIHANDLE hdb, hview = 0, rec = 0;
|
||||||
|
UINT r;
|
||||||
|
DWORD sz;
|
||||||
|
char buffer[0x20];
|
||||||
|
|
||||||
|
/* create an empty db */
|
||||||
|
hdb = create_db();
|
||||||
|
ok( hdb, "failed to create db\n");
|
||||||
|
|
||||||
|
/* tables should be present */
|
||||||
|
r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to open query\n");
|
||||||
|
|
||||||
|
r = MsiViewExecute(hview, 0);
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to execute query\n");
|
||||||
|
|
||||||
|
/* check that NAMES works */
|
||||||
|
rec = 0;
|
||||||
|
r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to get names\n");
|
||||||
|
sz = sizeof buffer;
|
||||||
|
r = MsiRecordGetString(rec, 1, buffer, &sz );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to get string\n");
|
||||||
|
ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
|
||||||
|
r = MsiCloseHandle( rec );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to close record handle\n");
|
||||||
|
|
||||||
|
/* check that TYPES works */
|
||||||
|
rec = 0;
|
||||||
|
r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to get names\n");
|
||||||
|
sz = sizeof buffer;
|
||||||
|
r = MsiRecordGetString(rec, 1, buffer, &sz );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to get string\n");
|
||||||
|
ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
|
||||||
|
r = MsiCloseHandle( rec );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to close record handle\n");
|
||||||
|
|
||||||
|
/* check that invalid values fail */
|
||||||
|
rec = 0;
|
||||||
|
r = MsiViewGetColumnInfo( hview, 100, &rec );
|
||||||
|
ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
|
||||||
|
ok( rec == 0, "returned a record\n");
|
||||||
|
|
||||||
|
r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
|
||||||
|
ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
|
||||||
|
|
||||||
|
r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
|
||||||
|
ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
|
||||||
|
|
||||||
|
r = MsiViewClose(hview);
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to close view\n");
|
||||||
|
r = MsiCloseHandle(hview);
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to close view handle\n");
|
||||||
|
r = MsiCloseHandle(hdb);
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to close database\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
|
||||||
|
{
|
||||||
|
MSIHANDLE hview = 0, rec = 0;
|
||||||
|
UINT r;
|
||||||
|
|
||||||
|
r = MsiDatabaseOpenView(hdb, query, &hview);
|
||||||
|
if( r != ERROR_SUCCESS )
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = MsiViewExecute(hview, 0);
|
||||||
|
if( r == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
MsiViewGetColumnInfo( hview, type, &rec );
|
||||||
|
MsiViewClose(hview);
|
||||||
|
}
|
||||||
|
MsiCloseHandle(hview);
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
|
||||||
|
{
|
||||||
|
MSIHANDLE hview = 0, rec = 0;
|
||||||
|
UINT r, type = 0;
|
||||||
|
char query[0x100];
|
||||||
|
|
||||||
|
sprintf(query, "select * from `_Columns` where `Table` = '%s'", table );
|
||||||
|
|
||||||
|
r = MsiDatabaseOpenView(hdb, query, &hview);
|
||||||
|
if( r != ERROR_SUCCESS )
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = MsiViewExecute(hview, 0);
|
||||||
|
if( r == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
r = MsiViewFetch( hview, &rec );
|
||||||
|
if( r != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
r = MsiRecordGetInteger( rec, 2 );
|
||||||
|
if (r == field)
|
||||||
|
type = MsiRecordGetInteger( rec, 4 );
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
}
|
||||||
|
|
||||||
|
MsiViewClose(hview);
|
||||||
|
}
|
||||||
|
MsiCloseHandle(hview);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL check_record( MSIHANDLE rec, UINT field, LPSTR val )
|
||||||
|
{
|
||||||
|
CHAR buffer[0x20];
|
||||||
|
UINT r;
|
||||||
|
DWORD sz;
|
||||||
|
|
||||||
|
sz = sizeof buffer;
|
||||||
|
r = MsiRecordGetString( rec, field, buffer, &sz );
|
||||||
|
return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_viewgetcolumninfo(void)
|
||||||
|
{
|
||||||
|
MSIHANDLE hdb = 0, rec;
|
||||||
|
UINT r;
|
||||||
|
|
||||||
|
hdb = create_db();
|
||||||
|
ok( hdb, "failed to create db\n");
|
||||||
|
|
||||||
|
r = run_query( hdb,
|
||||||
|
"CREATE TABLE `Properties` "
|
||||||
|
"( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
|
||||||
|
ok( r == ERROR_SUCCESS , "Failed to create table\n" );
|
||||||
|
|
||||||
|
/* check the column types */
|
||||||
|
rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
|
||||||
|
ok( check_record( rec, 1, "S255"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "S1"), "wrong record type\n");
|
||||||
|
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
/* check the type in _Columns */
|
||||||
|
ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
|
||||||
|
ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
|
||||||
|
|
||||||
|
/* now try the names */
|
||||||
|
rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
|
||||||
|
ok( check_record( rec, 1, "Property"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "Value"), "wrong record type\n");
|
||||||
|
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
r = run_query( hdb,
|
||||||
|
"CREATE TABLE `Binary` "
|
||||||
|
"( `Name` CHAR(255), `Data` OBJECT PRIMARY KEY `Name`)" );
|
||||||
|
ok( r == ERROR_SUCCESS , "Failed to create table\n" );
|
||||||
|
|
||||||
|
/* check the column types */
|
||||||
|
rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
|
||||||
|
ok( check_record( rec, 1, "S255"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "V0"), "wrong record type\n");
|
||||||
|
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
/* check the type in _Columns */
|
||||||
|
ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
|
||||||
|
ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
|
||||||
|
|
||||||
|
/* now try the names */
|
||||||
|
rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
|
||||||
|
ok( check_record( rec, 1, "Name"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "Data"), "wrong record type\n");
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
r = run_query( hdb,
|
||||||
|
"CREATE TABLE `UIText` "
|
||||||
|
"( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
|
||||||
|
ok( r == ERROR_SUCCESS , "Failed to create table\n" );
|
||||||
|
|
||||||
|
ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
|
||||||
|
ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
|
||||||
|
|
||||||
|
rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
ok( check_record( rec, 1, "Key"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "Text"), "wrong record type\n");
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
|
||||||
|
ok( rec, "failed to get column info record\n" );
|
||||||
|
ok( check_record( rec, 1, "s72"), "wrong record type\n");
|
||||||
|
ok( check_record( rec, 2, "L255"), "wrong record type\n");
|
||||||
|
MsiCloseHandle( rec );
|
||||||
|
|
||||||
|
MsiCloseHandle( hdb );
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(db)
|
START_TEST(db)
|
||||||
{
|
{
|
||||||
test_msidatabase();
|
test_msidatabase();
|
||||||
|
@ -487,4 +712,6 @@ START_TEST(db)
|
||||||
test_msidecomposedesc();
|
test_msidecomposedesc();
|
||||||
test_msibadqueries();
|
test_msibadqueries();
|
||||||
test_viewmodify();
|
test_viewmodify();
|
||||||
|
test_viewgetcolumninfo();
|
||||||
|
test_getcolinfo();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue