=== modified file 'driver/catalog.c' --- driver/catalog.c 2009-12-08 23:09:10 +0000 +++ driver/catalog.c 2010-02-08 01:13:29 +0000 @@ -237,6 +237,75 @@ /* +@type : internal +@purpose : adding table name to a (I_S) query. catalog functions usually have + similar rules for this parameter. +*/ +static char * add_table_name(HSTMT hstmt, char * pos, SQLCHAR * table, + SQLSMALLINT table_len) +{ + STMT FAR *stmt= (STMT FAR*) hstmt; + SQLUINTEGER metadata_id; + + MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); + + if (!metadata_id) + pos= strmov(pos, " BINARY "); + + pos= strmov(pos, "'"); + pos+= mysql_real_escape_string(&stmt->dbc->mysql, pos, (char *)table, table_len); + pos= strmov(pos, "'"); + + return pos; +} + + +/* +@type : internal +@purpose : adding catalog name to a (I_S) query. catalog functions usually have + similar rules for this parameter. +*/ +static char * add_catalog_name(HSTMT hstmt, char * pos, SQLCHAR * catalog, + SQLSMALLINT catalog_len) +{ + /* Currently catalog plays schema' role. Empty catalog string means + "those tables that do not have catalogs". For mysql that means empty resultset + (after translating of the term "catalog" to "schema"). + */ + if (catalog) + { + STMT FAR *stmt= (STMT FAR*) hstmt; + + if (catalog_len == SQL_NTS) + catalog_len= strlen((char *)catalog); + + if(catalog_len > 0) + { + SQLUINTEGER metadata_id; + MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); + + if (!metadata_id) + pos= strmov(pos, " BINARY "); + } + + pos= strmov(pos, "'"); + pos+= mysql_real_escape_string(&stmt->dbc->mysql, pos, (char *)catalog, + catalog_len); + pos= strmov(pos, "' "); + } + else + /* NULL catalog traditionally means current db */ + pos= strmov(pos, "DATABASE() "); + + return pos; +} + + +/* non-I_S routines for old servers, that have I_S versions*/ +#include "catalog_oldsvr.c" + + +/* **************************************************************************** SQLTables **************************************************************************** @@ -641,6 +710,7 @@ SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) + { STMT *stmt= (STMT *)hstmt; MYSQL_RES *res; @@ -993,101 +1063,52 @@ SQLTablePrivileges **************************************************************************** */ - -/* - @type : internal - @purpose : checks for the grantability -*/ - -static my_bool is_grantable(char *grant_list) -{ - char *grant=dupp_str(grant_list,SQL_NTS);; - if ( grant_list && grant_list[0] ) - { - char seps[] = ","; - char *token; - token = strtok( grant, seps ); - while ( token != NULL ) - { - if ( !strcmp(token,"Grant") ) - { - x_free(grant); - return(1); - } - token = strtok( NULL, seps ); - } - } - x_free(grant); - return(0); -} - -/* - @type : internal - @purpose : returns a table privileges result, NULL on error -*/ -static MYSQL_RES *mysql_list_table_priv(DBC *dbc, - SQLCHAR *catalog, - SQLSMALLINT catalog_len, - SQLCHAR *table, - SQLSMALLINT table_len) -{ - MYSQL *mysql= &dbc->mysql; +/* + @type : internal + @purpose : fetches data from I_S table_privileges. returns SQLRETURN of the operation +*/ +SQLRETURN i_s_list_table_priv(SQLHSTMT hstmt, + SQLCHAR * catalog, + SQLSMALLINT catalog_len, + SQLCHAR * schema __attribute__((unused)), + SQLSMALLINT schema_len __attribute__((unused)), + SQLCHAR * table, + SQLSMALLINT table_len) +{ + STMT FAR *stmt=(STMT FAR*) hstmt; + MYSQL *mysql= &stmt->dbc->mysql; char buff[255+2*NAME_LEN+1], *pos; + SQLRETURN rc; + /* Db,User,Table_name,"NULL" as Grantor,Table_priv*/ pos= strxmov(buff, - "SELECT Db,User,Table_name,Grantor,Table_priv ", - "FROM mysql.tables_priv WHERE Table_name LIKE '", + "SELECT TABLE_CATALOG as TABLE_CAT, TABLE_SCHEMA as TABLE_SCHEM," + "TABLE_NAME, NULL as GRANTOR, GRANTEE," + "PRIVILEGE_TYPE as PRIVILEGE, IS_GRANTABLE " + "FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES " + "WHERE TABLE_NAME LIKE ", NullS); - pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); - - pos= strxmov(pos, "' AND Db = ", NullS); - if (catalog_len) - { - pos= strmov(pos, "'"); - pos+= mysql_real_escape_string(mysql, pos, (char *)catalog, catalog_len); - pos= strmov(pos, "'"); - } - else - pos= strmov(pos, "DATABASE()"); - - pos= strxmov(pos, " ORDER BY Db, Table_name, Table_priv, User", NullS); - - MYLOG_DBC_QUERY(dbc, buff); - if (mysql_query(mysql,buff)) - return NULL; - - return mysql_store_result(mysql); + + pos= add_table_name(hstmt, pos, table, table_len ); + + pos= strxmov(pos, " AND TABLE_SCHEMA = ", NullS); + pos= add_catalog_name(hstmt, pos, catalog, catalog_len); + + pos= strxmov(pos, " ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, PRIVILEGE, GRANTEE", NullS); + + if( !SQL_SUCCEEDED(rc= MySQLPrepare(hstmt, (SQLCHAR *)buff, SQL_NTS, FALSE))) + return rc; + + return my_SQLExecute(hstmt); } -#define MY_MAX_TABPRIV_COUNT 21 -#define MY_MAX_COLPRIV_COUNT 3 - -char *SQLTABLES_priv_values[]= -{ - NULL,"",NULL,NULL,NULL,NULL,NULL -}; - -MYSQL_FIELD SQLTABLES_priv_fields[]= -{ - MYODBC_FIELD_STRING("TABLE_CAT", NAME_LEN, 0), - MYODBC_FIELD_STRING("TABLE_SCHEM", NAME_LEN, 0), - MYODBC_FIELD_STRING("TABLE_NAME", NAME_LEN, NOT_NULL_FLAG), - MYODBC_FIELD_STRING("GRANTOR", NAME_LEN, 0), - MYODBC_FIELD_STRING("GRANTEE", NAME_LEN, NOT_NULL_FLAG), - MYODBC_FIELD_STRING("PRIVILEGE", NAME_LEN, NOT_NULL_FLAG), - MYODBC_FIELD_STRING("IS_GRANTABLE", NAME_LEN, 0), -}; - -const uint SQLTABLES_PRIV_FIELDS= array_elements(SQLTABLES_priv_values); - /* @type : ODBC 1.0 API @purpose : returns a list of tables and the privileges associated with each table. The driver returns the information as a result set on the specified statement. */ - SQLRETURN SQL_API MySQLTablePrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, @@ -1095,72 +1116,26 @@ SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { - char **data, **row; - MEM_ROOT *alloc; STMT *stmt= (STMT *)hstmt; - uint row_count; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); - pthread_mutex_lock(&stmt->dbc->lock); - stmt->result= mysql_list_table_priv(stmt->dbc, catalog, catalog_len, - table, table_len); - if (!stmt->result) - { - SQLRETURN rc= handle_connection_error(stmt); - pthread_mutex_unlock(&stmt->dbc->lock); - return rc; - } - pthread_mutex_unlock(&stmt->dbc->lock); - - /* Allocate max buffers, to avoid reallocation */ - stmt->result_array= (char**) my_malloc(sizeof(char*)* SQLTABLES_PRIV_FIELDS * - (ulong)stmt->result->row_count * - MY_MAX_TABPRIV_COUNT, - MYF(MY_ZEROFILL)); - if (!stmt->result_array) - { - set_mem_error(&stmt->dbc->mysql); - return handle_connection_error(stmt); - } - alloc= &stmt->result->field_alloc; - data= stmt->result_array; - row_count= 0; - while ( (row= mysql_fetch_row(stmt->result)) ) - { - char *grants= row[4]; - char token[NAME_LEN+1]; - const char *grant= (const char *)grants; - - for ( ;; ) - { - data[0]= row[0]; - data[1]= ""; - data[2]= row[2]; - data[3]= row[3]; - data[4]= row[1]; - data[6]= is_grantable(row[4]) ? "YES" : "NO"; - row_count++; - - if ( !(grant= my_next_token(grant,&grants,token,',')) ) - { - /* End of grants .. */ - data[5]= strdup_root(alloc,grants); - data+= SQLTABLES_PRIV_FIELDS; - break; - } - data[5]= strdup_root(alloc,token); - data+= SQLTABLES_PRIV_FIELDS; - } - } - stmt->result->row_count= row_count; - mysql_link_fields(stmt,SQLTABLES_priv_fields,SQLTABLES_PRIV_FIELDS); - return SQL_SUCCESS; + /* For 5.0 and later, use INFORMATION_SCHEMA. */ + if (is_minimum_version(stmt->dbc->mysql.server_version, "5.0", 3)) + { + /* Since mysql is also the name of the system db, using here i_s prefix to + distinct functions */ + return i_s_list_table_priv(hstmt, catalog, catalog_len, schema, schema_len, + table, table_len); + } + else + { + return mysql_list_table_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); + } } - /* @type : internal @purpose : returns a column privileges result, NULL on error @@ -1555,8 +1530,6 @@ SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { - char Qualifier_buff[NAME_LEN+1],Table_buff[NAME_LEN+1], - *TableQualifier,*TableName; STMT FAR *stmt= (STMT FAR*) hstmt; MYSQL_ROW row; char **data; === added file 'driver/catalog_oldsvr.c' --- driver/catalog_oldsvr.c 1970-01-01 00:00:00 +0000 +++ driver/catalog_oldsvr.c 2010-02-08 01:15:26 +0000 @@ -0,0 +1,196 @@ +/* + Copyright (C) 2000-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the exception + in file LICENSE.exceptions in the top-level directory of this software + distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + @file catalog_oldsvr.c + @brief Catalog functions for server versions that doesn't have I_S. + Currently designed for simple inclusion into main catalog file +*/ + + + +/**************************************************************************** +SQLTablePrivileges +**************************************************************************** +*/ +/* + @type : internal + @purpose : checks for the grantability +*/ +static my_bool is_grantable(char *grant_list) +{ + char *grant=dupp_str(grant_list,SQL_NTS);; + if ( grant_list && grant_list[0] ) + { + char seps[] = ","; + char *token; + token = strtok( grant, seps ); + while ( token != NULL ) + { + if ( !strcmp(token,"Grant") ) + { + x_free(grant); + return(1); + } + token = strtok( NULL, seps ); + } + } + x_free(grant); + return(0); +} + + +/* +@type : internal +@purpose : returns a table privileges result, NULL on error. Uses mysql db tables +*/ +static MYSQL_RES *table_privs_raw_data(DBC *dbc, + SQLCHAR *catalog, + SQLSMALLINT catalog_len, + SQLCHAR *table, + SQLSMALLINT table_len) +{ + MYSQL *mysql= &dbc->mysql; + char buff[255+2*NAME_LEN+1], *pos; + + pos= strxmov(buff, + "SELECT Db,User,Table_name,Grantor,Table_priv ", + "FROM mysql.tables_priv WHERE Table_name LIKE '", + NullS); + pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); + + pos= strxmov(pos, "' AND Db = ", NullS); + if (catalog_len) + { + pos= strmov(pos, "'"); + pos+= mysql_real_escape_string(mysql, pos, (char *)catalog, catalog_len); + pos= strmov(pos, "'"); + } + else + pos= strmov(pos, "DATABASE()"); + + pos= strxmov(pos, " ORDER BY Db, Table_name, Table_priv, User", NullS); + + MYLOG_DBC_QUERY(dbc, buff); + if (mysql_query(mysql,buff)) + return NULL; + + return mysql_store_result(mysql); +} + + +#define MY_MAX_TABPRIV_COUNT 21 +#define MY_MAX_COLPRIV_COUNT 3 + +char *SQLTABLES_priv_values[]= +{ + NULL,"",NULL,NULL,NULL,NULL,NULL +}; + +MYSQL_FIELD SQLTABLES_priv_fields[]= +{ + MYODBC_FIELD_STRING("TABLE_CAT", NAME_LEN, 0), + MYODBC_FIELD_STRING("TABLE_SCHEM", NAME_LEN, 0), + MYODBC_FIELD_STRING("TABLE_NAME", NAME_LEN, NOT_NULL_FLAG), + MYODBC_FIELD_STRING("GRANTOR", NAME_LEN, 0), + MYODBC_FIELD_STRING("GRANTEE", NAME_LEN, NOT_NULL_FLAG), + MYODBC_FIELD_STRING("PRIVILEGE", NAME_LEN, NOT_NULL_FLAG), + MYODBC_FIELD_STRING("IS_GRANTABLE", NAME_LEN, 0), +}; + +const uint SQLTABLES_PRIV_FIELDS= array_elements(SQLTABLES_priv_values); + +/* + @type : ODBC 1.0 API + @purpose : returns a list of tables and the privileges associated with + each table. The driver returns the information as a result + set on the specified statement. +*/ + +SQLRETURN SQL_API +mysql_list_table_priv(SQLHSTMT hstmt, + SQLCHAR *catalog, SQLSMALLINT catalog_len, + SQLCHAR *schema __attribute__((unused)), + SQLSMALLINT schema_len __attribute__((unused)), + SQLCHAR *table, SQLSMALLINT table_len) +{ + STMT *stmt= (STMT *)hstmt; + + char **data, **row; + MEM_ROOT *alloc; + uint row_count; + + pthread_mutex_lock(&stmt->dbc->lock); + stmt->result= table_privs_raw_data(stmt->dbc, catalog, catalog_len, + table, table_len); + if (!stmt->result) + { + SQLRETURN rc= handle_connection_error(stmt); + pthread_mutex_unlock(&stmt->dbc->lock); + return rc; + } + pthread_mutex_unlock(&stmt->dbc->lock); + + /* Allocate max buffers, to avoid reallocation */ + stmt->result_array= (char**) my_malloc(sizeof(char*)* SQLTABLES_PRIV_FIELDS * + (ulong)stmt->result->row_count * + MY_MAX_TABPRIV_COUNT, + MYF(MY_ZEROFILL)); + if (!stmt->result_array) + { + set_mem_error(&stmt->dbc->mysql); + return handle_connection_error(stmt); + } + alloc= &stmt->result->field_alloc; + data= stmt->result_array; + row_count= 0; + while ( (row= mysql_fetch_row(stmt->result)) ) + { + char *grants= row[4]; + char token[NAME_LEN+1]; + const char *grant= (const char *)grants; + + for ( ;; ) + { + data[0]= row[0]; + data[1]= ""; + data[2]= row[2]; + data[3]= row[3]; + data[4]= row[1]; + data[6]= is_grantable(row[4]) ? "YES" : "NO"; + row_count++; + + if ( !(grant= my_next_token(grant,&grants,token,',')) ) + { + /* End of grants .. */ + data[5]= strdup_root(alloc,grants); + data+= SQLTABLES_PRIV_FIELDS; + break; + } + data[5]= strdup_root(alloc,token); + data+= SQLTABLES_PRIV_FIELDS; + } + } + stmt->result->row_count= row_count; + mysql_link_fields(stmt,SQLTABLES_priv_fields,SQLTABLES_PRIV_FIELDS); + return SQL_SUCCESS; +} === modified file 'test/my_catalog.c' --- test/my_catalog.c 2008-11-21 20:57:00 +0000 +++ test/my_catalog.c 2010-02-08 00:06:03 +0000 @@ -1437,6 +1437,59 @@ } +/* + Bug #50195 - SQLTablePrivileges requires select priveleges +*/ +DECLARE_TEST(t_bug50195) +{ + SQLHDBC hdbc1; + SQLHSTMT hstmt1; + const char expected_privs[][12]= {"ALTER", "CREATE", "CREATE VIEW", "DELETE", "DROP", "INDEX", + "INSERT", "REFERENCES", "SHOW VIEW", "TRIGGER", "UPDATE"}; + int i; + SQLCHAR priv[12]; + SQLLEN len; + + (void)SQLExecDirect(hstmt, (SQLCHAR *)"DROP USER bug50195@localhost", SQL_NTS); + + ok_sql(hstmt, "grant all on *.* to bug50195@localhost IDENTIFIED BY 'a'"); + ok_sql(hstmt, "revoke select on *.* from bug50195@localhost"); + + /* revoking "global" select is enough, but revoking smth from mysql.tables_priv + to have not empty result of SQLTablePrivileges */ + ok_sql(hstmt, "grant all on mysql.tables_priv to bug50195@localhost"); + ok_sql(hstmt, "revoke select on mysql.tables_priv from bug50195@localhost"); + + ok_sql(hstmt, "FLUSH PRIVILEGES"); + + ok_env(henv, SQLAllocConnect(henv, &hdbc1)); + + ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, "bug50195", SQL_NTS, "a", SQL_NTS)); + + ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); + + ok_stmt(hstmt1, SQLTablePrivileges(hstmt1, "mysql", SQL_NTS, 0, 0, "tables_priv", SQL_NTS)); + + /* Testing SQLTablePrivileges a bit, as we don't have separate test of it */ + + for(i= 0; i < 11; ++i) + { + ok_stmt(hstmt1, SQLFetch(hstmt1)); + ok_stmt(hstmt1, SQLGetData(hstmt1, 6, SQL_C_CHAR, priv, sizeof(priv), &len)); + is_str(priv, expected_privs[i], len); + } + + expect_stmt(hstmt1, SQLFetch(hstmt1),100); + + ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); + ok_con(hdbc1, SQLDisconnect(hdbc1)); + ok_con(hdbc1, SQLFreeConnect(hdbc1)); + + ok_sql(hstmt, "DROP USER bug50195@localhost"); + + return OK; +} + BEGIN_TESTS ADD_TEST(my_columns_null) ADD_TEST(my_drop_table) @@ -1467,6 +1520,7 @@ ADD_TEST(t_bug30770) ADD_TEST(t_bug36275) ADD_TEST(t_bug39957) + ADD_TEST(t_bug50195) END_TESTS === modified file 'test/odbctap.h' --- test/odbctap.h 2009-01-14 21:47:51 +0000 +++ test/odbctap.h 2010-02-02 14:51:03 +0000 @@ -1094,8 +1094,8 @@ */ int using_unixodbc_version(SQLHANDLE henv, SQLCHAR *ver) { +#ifdef SQL_ATTR_UNIXODBC_VERSION SQLCHAR buf[10]; -#ifdef SQL_ATTR_UNIXODBC_VERSION if(SQLGetEnvAttr(henv, SQL_ATTR_UNIXODBC_VERSION, buf, 10, NULL) != SQL_SUCCESS) return 0; if(!strcmp(buf, ver))