[3/3] msi: Fix handling of single quoted column names in SELECT queries.

Hans Leidekker hans at codeweavers.com
Fri Dec 23 06:54:26 CST 2011


See http://bugs.winehq.org/show_bug.cgi?id=29413
---
 dlls/msi/msipriv.h  |    1 +
 dlls/msi/msiquery.c |    2 +
 dlls/msi/select.c   |   39 ++++++--
 dlls/msi/sql.y      |   59 ++++++++++--
 dlls/msi/tests/db.c |  250 +++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 331 insertions(+), 20 deletions(-)

diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index dd8bd82..2a15a52 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 0f2e377..433b3c7 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 a0184cb..982cf6a 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 8de696d..548e142 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 <string> table tablelist id
-%type <column_list> selcollist column column_and_type column_def table_def
+%type <string> table tablelist id string
+%type <column_list> selcollist collist selcolumn column column_and_type column_def table_def
 %type <column_list> column_assignment update_assign_list constlist
 %type <query> query from selectfrom unorderdfrom
 %type <query> oneupdate onedelete oneselect onequery onecreate oneinsert onealter onedrop
@@ -120,7 +120,6 @@ static struct expr * EXPR_wildcard( void *info );
 %type <column_type> column_type data_type data_type_l data_count
 %type <integer> 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 b915fa0..6ecd642 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();
 }
-- 
1.7.7.3






More information about the wine-patches mailing list