=== modified file 'driver/catalog.c' --- driver/catalog.c 2009-12-08 23:09:10 +0000 +++ driver/catalog.c 2010-02-13 23:27:26 +0000 @@ -237,6 +237,100 @@ /* +@type : internal +@purpose : Adding name condition for arguments what depending on SQL_ATTR_METADATA_ID + are either ordinary argument(oa) or identifier string(id) + NULL _default parameter means that parameter is mandatory and error is generated +@returns : 1 if required parameter is NULL, 0 otherwise +*/ +int add_name_condition_oa_id(HSTMT hstmt, char ** pos, SQLCHAR * name, + SQLSMALLINT name_len, char * _default) +{ + SQLUINTEGER metadata_id; + + /* this shouldn't be very expensive, so no need to complicate things with additional parameter etc */ + MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); + + /* we can't rely here that column was checked and is not null */ + if (name) + { + STMT FAR *stmt= (STMT FAR*) hstmt; + + if (metadata_id) + { + *pos= strmov(*pos, "="); + /* Need also code to remove trailing blanks */ + } + else + *pos= strmov(*pos, "= BINARY "); + + *pos= strmov(*pos, "'"); + *pos+= mysql_real_escape_string(&stmt->dbc->mysql, *pos, (char *)name, name_len); + *pos= strmov(*pos, "' "); + } + else + { + /* According to http://msdn.microsoft.com/en-us/library/ms714579%28VS.85%29.aspx + identifier argument cannot be NULL with one exception not actual for mysql) */ + if (!metadata_id && _default) + *pos= strmov(*pos, _default); + else + return 1; + } + + return 0; +} + + +/* +@type : internal +@purpose : Adding name condition for arguments what depending on SQL_ATTR_METADATA_ID +are either pattern value(oa) or identifier string(id) +@returns : 1 if required parameter is NULL, 0 otherwise +*/ +int add_name_condition_pv_id(HSTMT hstmt, char ** pos, SQLCHAR * name, + SQLSMALLINT name_len, char * _default) +{ + SQLUINTEGER metadata_id; + /* this shouldn't be very expensive, so no need to complicate things with additional parameter etc */ + MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); + + /* we can't rely here that column was checked and is not null */ + if (name) + { + STMT FAR *stmt= (STMT FAR*) hstmt; + + if (metadata_id) + { + *pos= strmov(*pos, "="); + /* Need also code to remove trailing blanks */ + } + else + *pos= strmov(*pos, " LIKE BINARY "); + + *pos= strmov(*pos, "'"); + *pos+= mysql_real_escape_string(&stmt->dbc->mysql, *pos, (char *)name, name_len); + *pos= strmov(*pos, "' "); + } + else + { + /* According to http://msdn.microsoft.com/en-us/library/ms714579%28VS.85%29.aspx + identifier argument cannot be NULL with one exception not actual for mysql) */ + if (!metadata_id && _default) + *pos= strmov(*pos, _default); + else + return 1; + } + + return 0; +} + + +/* non-I_S routines for old servers, that have I_S versions*/ +#include "catalog_oldsvr.c" + + +/* **************************************************************************** SQLTables **************************************************************************** @@ -641,6 +735,7 @@ SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) + { STMT *stmt= (STMT *)hstmt; MYSQL_RES *res; @@ -993,93 +1088,46 @@ SQLTablePrivileges **************************************************************************** */ - /* @type : internal - @purpose : checks for the grantability + @purpose : fetches data from I_S table_privileges. returns SQLRETURN of the operation */ - -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, +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) { - MYSQL *mysql= &dbc->mysql; + 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", 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); + add_name_condition_pv_id(hstmt, &pos, table, table_len, " LIKE '%'" ); - MYLOG_DBC_QUERY(dbc, buff); - if (mysql_query(mysql,buff)) - return NULL; - - return mysql_store_result(mysql); -} + pos= strxmov(pos, " AND TABLE_SCHEMA", NullS); + add_name_condition_oa_id(hstmt, &pos, catalog, catalog_len, "=DATABASE()"); + /* TABLE_CAT is always NULL in mysql I_S */ + pos= strxmov(pos, " ORDER BY /*TABLE_CAT,*/ TABLE_SCHEM, TABLE_NAME, PRIVILEGE, GRANTEE", NullS); -#define MY_MAX_TABPRIV_COUNT 21 -#define MY_MAX_COLPRIV_COUNT 3 - -char *SQLTABLES_priv_values[]= -{ - NULL,"",NULL,NULL,NULL,NULL,NULL -}; + if( !SQL_SUCCEEDED(rc= MySQLPrepare(hstmt, (SQLCHAR *)buff, SQL_NTS, FALSE))) + return rc; -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), -}; + return my_SQLExecute(stmt); +} -const uint SQLTABLES_PRIV_FIELDS= array_elements(SQLTABLES_priv_values); /* @type : ODBC 1.0 API @@ -1087,7 +1135,6 @@ 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,135 +1142,73 @@ 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) + /* For 5.0 and later, use INFORMATION_SCHEMA. */ + if (is_minimum_version(stmt->dbc->mysql.server_version, "5.0", 3)) { - 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); + /* 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); } - 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 ( ;; ) + else { - 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; + return mysql_list_table_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } } - stmt->result->row_count= row_count; - mysql_link_fields(stmt,SQLTABLES_priv_fields,SQLTABLES_PRIV_FIELDS); - return SQL_SUCCESS; -} - /* @type : internal @purpose : returns a column privileges result, NULL on error */ -static MYSQL_RES *mysql_list_column_priv(MYSQL *mysql, +static SQLRETURN i_s_list_column_priv(HSTMT * hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, - SQLCHAR *table, SQLSMALLINT table_len, + SQLCHAR * schema, + SQLSMALLINT schema_len, + SQLCHAR * table, + SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len) { - char buff[255+3*NAME_LEN+1], *pos; - - pos= strmov(buff, - "SELECT c.Db, c.User, c.Table_name, c.Column_name," - "t.Grantor, c.Column_priv, t.Table_priv " - "FROM mysql.columns_priv AS c, mysql.tables_priv AS t " - "WHERE c.Table_name = '"); - pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); - - pos= strmov(pos, "' AND c.Db = "); - 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()"); + STMT FAR *stmt=(STMT FAR*) hstmt; + MYSQL *mysql= &stmt->dbc->mysql; + char buff[255+2*NAME_LEN+1], *pos; + SQLRETURN rc; - pos= strmov(pos, "AND c.Column_name LIKE '"); - pos+= mysql_real_escape_string(mysql, pos, (char *)column, column_len); + /* Db,User,Table_name,"NULL" as Grantor,Table_priv*/ + pos= strxmov(buff, + "SELECT TABLE_CATALOG as TABLE_CAT, TABLE_SCHEMA as TABLE_SCHEM," + "TABLE_NAME, COLUMN_NAME, NULL as GRANTOR, GRANTEE," + "PRIVILEGE_TYPE as PRIVILEGE, IS_GRANTABLE " + "FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES " + "WHERE TABLE_NAME", + NullS); - pos= strmov(pos, - "' AND c.Table_name = t.Table_name " - "ORDER BY c.Db, c.Table_name, c.Column_name, c.Column_priv"); + if(add_name_condition_oa_id(hstmt, &pos, table, table_len, NULL)) + return set_stmt_error(stmt, "HY009", "Invalid use of NULL pointer(table is required parameter)", 0); - if (mysql_query(mysql, buff)) - return NULL; + pos= strxmov(pos, " AND TABLE_SCHEMA", NullS); + add_name_condition_oa_id(hstmt, &pos, catalog, catalog_len, "=DATABASE()"); - return mysql_store_result(mysql); -} + pos= strmov(pos, "AND COLUMN_NAME"); + add_name_condition_pv_id(hstmt, &pos, column, column_len, " LIKE '%'"); -char *SQLCOLUMNS_priv_values[]= -{ - NULL,"",NULL,NULL,NULL,NULL,NULL,NULL -}; -MYSQL_FIELD SQLCOLUMNS_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("COLUMN_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), -}; + /* TABLE_CAT is always NULL in mysql I_S */ + pos= strxmov(pos, " ORDER BY /*TABLE_CAT,*/ TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, PRIVILEGE", NullS); + if( !SQL_SUCCEEDED(rc= MySQLPrepare(hstmt, (SQLCHAR *)buff, SQL_NTS, FALSE))) + return rc; -const uint SQLCOLUMNS_PRIV_FIELDS= array_elements(SQLCOLUMNS_priv_values); + return my_SQLExecute(stmt); +} SQLRETURN SQL_API @@ -1235,77 +1220,23 @@ SQLCHAR *column, SQLSMALLINT column_len) { STMT *stmt=(STMT *) hstmt; - char **row, **data; - MEM_ROOT *alloc; - uint row_count; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); - if (catalog_len == SQL_NTS) - catalog_len= catalog ? strlen((char *)catalog) : 0; - if (table_len == SQL_NTS) - table_len= table ? strlen((char *)table) : 0; - if (column_len == SQL_NTS) - column_len= column ? strlen((char *)column) : 0; - - pthread_mutex_lock(&stmt->dbc->lock); - stmt->result= mysql_list_column_priv(&stmt->dbc->mysql, - catalog, catalog_len, - table, table_len, - column, column_len); - if (!stmt->result) - { - SQLRETURN rc= handle_connection_error(stmt); - pthread_mutex_unlock(&stmt->dbc->lock); - return rc; - } - pthread_mutex_unlock(&stmt->dbc->lock); - - stmt->result_array= (char **)my_malloc(sizeof(char *) * - SQLCOLUMNS_PRIV_FIELDS * - (ulong) stmt->result->row_count * - MY_MAX_COLPRIV_COUNT, - MYF(MY_ZEROFILL)); - if (!stmt->result_array) + /* For 5.0 and later, use INFORMATION_SCHEMA. */ + if (is_minimum_version(stmt->dbc->mysql.server_version, "5.0", 3)) { - set_mem_error(&stmt->dbc->mysql); - return handle_connection_error(stmt); + /* Since mysql is also the name of the system db, using here i_s prefix to + distinct functions */ + return i_s_list_column_priv(hstmt, catalog, catalog_len, schema, schema_len, + table, table_len, column, column_len); } - alloc= &stmt->result->field_alloc; - data= stmt->result_array; - row_count= 0; - while ( (row= mysql_fetch_row(stmt->result)) ) - { - char *grants= row[5]; - char token[NAME_LEN+1]; - const char *grant= (const char *)grants; - - for ( ;; ) + else { - data[0]= row[0]; - data[1]= ""; - data[2]= row[2]; - data[3]= row[3]; - data[4]= row[4]; - data[5]= row[1]; - data[7]= is_grantable(row[6]) ? "YES":"NO"; - row_count++; - - if ( !(grant= my_next_token(grant,&grants,token,',')) ) - { - /* End of grants .. */ - data[6]= strdup_root(alloc,grants); - data+= SQLCOLUMNS_PRIV_FIELDS; - break; - } - data[6]= strdup_root(alloc,token); - data+= SQLCOLUMNS_PRIV_FIELDS; - } + return mysql_list_column_priv(hstmt, catalog, catalog_len, schema, schema_len, + table, table_len, column, column_len); } - stmt->result->row_count= row_count; - mysql_link_fields(stmt,SQLCOLUMNS_priv_fields,SQLCOLUMNS_PRIV_FIELDS); - return SQL_SUCCESS; } @@ -1555,8 +1486,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 17:49:49 +0000 @@ -0,0 +1,350 @@ +/* + 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 +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; +} + + +/* +**************************************************************************** +SQLColumnPrivileges +**************************************************************************** +*/ +/* +@type : internal +@purpose : returns a column privileges result, NULL on error +*/ +static MYSQL_RES *column_privs_raw_data( MYSQL * mysql, + SQLCHAR * catalog, + SQLSMALLINT catalog_len, + SQLCHAR * table, + SQLSMALLINT table_len, + SQLCHAR * column, + SQLSMALLINT column_len) +{ + char buff[255+3*NAME_LEN+1], *pos; + + pos= strmov(buff, + "SELECT c.Db, c.User, c.Table_name, c.Column_name," + "t.Grantor, c.Column_priv, t.Table_priv " + "FROM mysql.columns_priv AS c, mysql.tables_priv AS t " + "WHERE c.Table_name = '"); + pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); + + pos= strmov(pos, "' AND c.Db = "); + 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= strmov(pos, "AND c.Column_name LIKE '"); + pos+= mysql_real_escape_string(mysql, pos, (char *)column, column_len); + + pos= strmov(pos, + "' AND c.Table_name = t.Table_name " + "ORDER BY c.Db, c.Table_name, c.Column_name, c.Column_priv"); + + if (mysql_query(mysql, buff)) + return NULL; + + return mysql_store_result(mysql); +} + + +char *SQLCOLUMNS_priv_values[]= +{ + NULL,"",NULL,NULL,NULL,NULL,NULL,NULL +}; + +MYSQL_FIELD SQLCOLUMNS_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("COLUMN_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 SQLCOLUMNS_PRIV_FIELDS= array_elements(SQLCOLUMNS_priv_values); + + +SQLRETURN +mysql_list_column_priv(SQLHSTMT hstmt, + SQLCHAR *catalog, SQLSMALLINT catalog_len, + SQLCHAR *schema __attribute__((unused)), + SQLSMALLINT schema_len __attribute__((unused)), + SQLCHAR *table, SQLSMALLINT table_len, + SQLCHAR *column, SQLSMALLINT column_len) +{ + STMT *stmt=(STMT *) hstmt; + char **row, **data; + MEM_ROOT *alloc; + uint row_count; + + CLEAR_STMT_ERROR(hstmt); + my_SQLFreeStmt(hstmt,MYSQL_RESET); + + if (catalog_len == SQL_NTS) + catalog_len= catalog ? strlen((char *)catalog) : 0; + if (table_len == SQL_NTS) + table_len= table ? strlen((char *)table) : 0; + if (column_len == SQL_NTS) + column_len= column ? strlen((char *)column) : 0; + + pthread_mutex_lock(&stmt->dbc->lock); + stmt->result= column_privs_raw_data(&stmt->dbc->mysql, + catalog, catalog_len, + table, table_len, + column, column_len); + if (!stmt->result) + { + SQLRETURN rc= handle_connection_error(stmt); + pthread_mutex_unlock(&stmt->dbc->lock); + return rc; + } + pthread_mutex_unlock(&stmt->dbc->lock); + + stmt->result_array= (char **)my_malloc(sizeof(char *) * + SQLCOLUMNS_PRIV_FIELDS * + (ulong) stmt->result->row_count * + MY_MAX_COLPRIV_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[5]; + 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[4]; + data[5]= row[1]; + data[7]= is_grantable(row[6]) ? "YES":"NO"; + row_count++; + + if ( !(grant= my_next_token(grant,&grants,token,',')) ) + { + /* End of grants .. */ + data[6]= strdup_root(alloc,grants); + data+= SQLCOLUMNS_PRIV_FIELDS; + break; + } + data[6]= strdup_root(alloc,token); + data+= SQLCOLUMNS_PRIV_FIELDS; + } + } + stmt->result->row_count= row_count; + mysql_link_fields(stmt,SQLCOLUMNS_priv_fields,SQLCOLUMNS_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) { - SQLCHAR buf[10]; #ifdef SQL_ATTR_UNIXODBC_VERSION + SQLCHAR buf[10]; if(SQLGetEnvAttr(henv, SQL_ATTR_UNIXODBC_VERSION, buf, 10, NULL) != SQL_SUCCESS) return 0; if(!strcmp(buf, ver))