diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index dd8bd8231791..2a15a5299a7a 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -47,6 +47,7 @@ static const BOOL is_64bit = sizeof(void *) > sizeof(int); #define MSITYPE_NULLABLE 0x1000 #define MSITYPE_KEY 0x2000 #define MSITYPE_TEMPORARY 0x4000 +#define MSITYPE_UNKNOWN 0x8000 #define MAX_STREAM_NAME_LEN 62 #define LONG_STR_BYTES 3 diff --git a/dlls/msi/msiquery.c b/dlls/msi/msiquery.c index 0f2e3777dc32..433b3c772f6f 100644 --- a/dlls/msi/msiquery.c +++ b/dlls/msi/msiquery.c @@ -501,6 +501,8 @@ static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, szType[0] = 'v'; else if (type & MSITYPE_LOCALIZABLE) szType[0] = 'l'; + else if (type & MSITYPE_UNKNOWN) + szType[0] = 'f'; else if (type & MSITYPE_STRING) { if (temporary) diff --git a/dlls/msi/select.c b/dlls/msi/select.c index a0184cb6f91e..982cf6a93697 100644 --- a/dlls/msi/select.c +++ b/dlls/msi/select.c @@ -57,11 +57,15 @@ static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT if( !sv->table ) return ERROR_FUNCTION_FAILED; - if( (col==0) || (col>sv->num_cols) ) + if( !col || col > sv->num_cols ) return ERROR_FUNCTION_FAILED; col = sv->cols[ col - 1 ]; - + if( !col ) + { + *val = 0; + return ERROR_SUCCESS; + } return sv->table->ops->fetch_int( sv->table, row, col, val ); } @@ -74,11 +78,15 @@ static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IS if( !sv->table ) return ERROR_FUNCTION_FAILED; - if( (col==0) || (col>sv->num_cols) ) + if( !col || col > sv->num_cols ) return ERROR_FUNCTION_FAILED; col = sv->cols[ col - 1 ]; - + if( !col ) + { + *stm = NULL; + return ERROR_SUCCESS; + } return sv->table->ops->fetch_stream( sv->table, row, col, stm ); } @@ -218,11 +226,18 @@ static UINT SELECT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *na if( !sv->table ) return ERROR_FUNCTION_FAILED; - if( (n==0) || (n>sv->num_cols) ) + if( !n || n > sv->num_cols ) return ERROR_FUNCTION_FAILED; n = sv->cols[ n - 1 ]; - + if( !n ) + { + if (name) *name = szEmpty; + if (type) *type = MSITYPE_UNKNOWN | MSITYPE_VALID; + if (temporary) *temporary = FALSE; + if (table_name) *table_name = szEmpty; + return ERROR_SUCCESS; + } return sv->table->ops->get_column_info( sv->table, n, name, type, temporary, table_name ); } @@ -360,7 +375,7 @@ static const MSIVIEWOPS select_ops = static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name, LPCWSTR table_name ) { - UINT r, n=0; + UINT r, n; MSIVIEW *table; TRACE("%p adding %s.%s\n", sv, debugstr_w( table_name ), @@ -380,9 +395,13 @@ static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name, if( sv->num_cols >= sv->max_cols ) return ERROR_FUNCTION_FAILED; - r = VIEW_find_column( table, name, table_name, &n ); - if( r != ERROR_SUCCESS ) - return r; + if ( !name[0] ) n = 0; + else + { + r = VIEW_find_column( table, name, table_name, &n ); + if( r != ERROR_SUCCESS ) + return r; + } sv->cols[sv->num_cols] = n; TRACE("Translating column %s from %d -> %d\n", diff --git a/dlls/msi/sql.y b/dlls/msi/sql.y index 8de696d3ae82..548e14290ce4 100644 --- a/dlls/msi/sql.y +++ b/dlls/msi/sql.y @@ -111,8 +111,8 @@ static struct expr * EXPR_wildcard( void *info ); %nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION COLUMN AGG_FUNCTION. -%type table tablelist id -%type selcollist column column_and_type column_def table_def +%type table tablelist id string +%type selcollist collist selcolumn column column_and_type column_def table_def %type column_assignment update_assign_list constlist %type query from selectfrom unorderdfrom %type oneupdate onedelete oneselect onequery onecreate oneinsert onealter onedrop @@ -120,7 +120,6 @@ static struct expr * EXPR_wildcard( void *info ); %type column_type data_type data_type_l data_count %type number alterop -/* Reference: http://mates.ms.mff.cuni.cz/oracle/doc/ora815nt/server.815/a67779/operator.htm */ %left TK_OR %left TK_AND %left TK_NOT @@ -148,7 +147,7 @@ onequery: ; oneinsert: - TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP + TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP { SQL_input *sql = (SQL_input*) info; MSIVIEW *insert = NULL; @@ -159,7 +158,7 @@ oneinsert: PARSER_BUBBLE_UP_VIEW( sql, $$, insert ); } - | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY + | TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY { SQL_input *sql = (SQL_input*) info; MSIVIEW *insert = NULL; @@ -307,7 +306,7 @@ onedrop: ; table_def: - column_def TK_PRIMARY TK_KEY selcollist + column_def TK_PRIMARY TK_KEY collist { if( SQL_MarkPrimaryKeys( &$1, $4 ) ) $$ = $1; @@ -448,8 +447,20 @@ selectfrom: ; selcollist: + selcolumn + | selcolumn TK_COMMA selcollist + { + $1->next = $3; + } + | TK_STAR + { + $$ = NULL; + } + ; + +collist: column - | column TK_COMMA selcollist + | column TK_COMMA collist { $1->next = $3; } @@ -472,7 +483,7 @@ from: PARSER_BUBBLE_UP_VIEW( sql, $$, table ); } - | unorderdfrom TK_ORDER TK_BY selcollist + | unorderdfrom TK_ORDER TK_BY collist { UINT r; @@ -520,8 +531,7 @@ tablelist: { $$ = $1; } - | - table TK_COMMA tablelist + | table TK_COMMA tablelist { $$ = parser_add_table( info, $3, $1 ); if (!$$) @@ -689,6 +699,27 @@ column: } ; +selcolumn: + table TK_DOT id + { + $$ = parser_alloc_column( info, $1, $3 ); + if( !$$ ) + YYABORT; + } + | id + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + | string + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + ; + table: id { @@ -704,6 +735,14 @@ id: } ; +string: + TK_STRING + { + if ( SQL_getstring( info, &$1, &$$ ) != ERROR_SUCCESS || !$$ ) + YYABORT; + } + ; + number: TK_INTEGER { diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c index b915fa07ff5d..6ecd642a7fa9 100644 --- a/dlls/msi/tests/db.c +++ b/dlls/msi/tests/db.c @@ -9346,6 +9346,255 @@ static void test_embedded_nulls(void) DeleteFileA( msifile ); } +static void test_select_column_names(void) +{ + MSIHANDLE hdb = 0, rec, rec2, view; + char buffer[32]; + UINT r, size; + + DeleteFile(msifile); + + r = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( r == ERROR_SUCCESS , "failed to open database: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `t` (`a` CHAR NOT NULL, `b` CHAR PRIMARY KEY `a`)"); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b` FROM `t` WHERE `t`.`b` = `x`" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT '', `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT *, `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 'b', `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x' ORDER BY `b`" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x' ORDER BY 'b'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 't'.'b' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 'b' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "INSERT INTO `t` ( `a`, `b` ) VALUES( '1', '2' )" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "INSERT INTO `t` ( `a`, `b` ) VALUES( '3', '4' )" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = MsiDatabaseOpenView( hdb, "SELECT '' FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 1, "got %u\n", r ); + r = MsiViewGetColumnInfo( view, MSICOLINFO_NAMES, &rec2 ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec2 ); + ok( r == 1, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec2, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec2 ); + r = MsiViewGetColumnInfo( view, MSICOLINFO_TYPES, &rec2 ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec2 ); + ok( r == 1, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec2, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "f0" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec2 ); + + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT `a`, '' FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 2, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT '', `a` FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 2, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "3" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT `a`, '', `b` FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 3, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 3, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "2" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "3" ), "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 3, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "4" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = try_query( hdb, "SELECT '' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `b` FROM 't' WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `b` FROM `t` WHERE 'b' = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, `` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS , "failed to close database: %u\n", r); +} + START_TEST(db) { test_msidatabase(); @@ -9400,4 +9649,5 @@ START_TEST(db) test_createtable(); test_collation(); test_embedded_nulls(); + test_select_column_names(); }