diff options
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r-- | sql/sql_acl.cc | 1209 |
1 files changed, 771 insertions, 438 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7592986ef81..0b33abcfba2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -26,16 +26,131 @@ #include "mysql_priv.h" #include "hash_filo.h" -#ifdef HAVE_REPLICATION -#include "sql_repl.h" //for tables_ok() -#endif #include <m_ctype.h> #include <stdarg.h> #include "sp_head.h" #include "sp.h" +time_t mysql_db_table_last_check= 0L; + +TABLE_FIELD_W_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { + { + { C_STRING_WITH_LEN("Host") }, + { C_STRING_WITH_LEN("char(60)") }, + {NULL, 0} + }, + { + { C_STRING_WITH_LEN("Db") }, + { C_STRING_WITH_LEN("char(64)") }, + {NULL, 0} + }, + { + { C_STRING_WITH_LEN("User") }, + { C_STRING_WITH_LEN("char(16)") }, + {NULL, 0} + }, + { + { C_STRING_WITH_LEN("Select_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Insert_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Update_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Delete_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Create_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Drop_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Grant_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("References_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Index_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Alter_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Create_tmp_table_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Lock_tables_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Create_view_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Show_view_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Create_routine_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Alter_routine_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Execute_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Event_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Trigger_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } + } +}; + + #ifndef NO_EMBEDDED_ACCESS_CHECKS +#define FIRST_NON_YN_FIELD 26 + class acl_entry :public hash_filo_element { public: @@ -45,11 +160,11 @@ public: }; -static byte* acl_entry_get_key(acl_entry *entry,uint *length, - my_bool not_used __attribute__((unused))) +static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, + my_bool not_used __attribute__((unused))) { *length=(uint) entry->length; - return (byte*) entry->key; + return (uchar*) entry->key; } #define IP_ADDR_STRLEN (3+1+3+1+3+1+3) @@ -77,7 +192,7 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); static my_bool acl_load(THD *thd, TABLE_LIST *tables); -static my_bool grant_load(TABLE_LIST *tables); +static my_bool grant_load(THD *thd, TABLE_LIST *tables); /* Convert scrambled password to binary form, according to scramble type, @@ -162,6 +277,7 @@ my_bool acl_init(bool dont_read_acl_tables) DBUG_RETURN(1); /* purecov: inspected */ thd->thread_stack= (char*) &thd; thd->store_globals(); + lex_start(thd); /* It is safe to call acl_reload() since acl_* arrays and hashes which will be freed there are global static objects and thus are initialized @@ -198,14 +314,18 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[NAME_LEN+1]; int password_length; + ulong old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("acl_load"); + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + grant_version++; /* Privileges updated */ acl_cache->clear(1); // Clear locked hostname cache init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); while (!(read_record_info.read_record(&read_record_info))) { @@ -246,14 +366,15 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; } #endif - VOID(push_dynamic(&acl_hosts,(gptr) &host)); + VOID(push_dynamic(&acl_hosts,(uchar*) &host)); } - my_qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements, - sizeof(ACL_HOST),(qsort_cmp) acl_compare); + my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements, + sizeof(ACL_HOST),(qsort_cmp) acl_compare); end_read_record(&read_record_info); freeze_size(&acl_hosts); init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; @@ -357,6 +478,20 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (table->s->fields <= 36 && (user.access & GRANT_ACL)) user.access|= CREATE_USER_ACL; + + /* + if it is pre 5.1.6 privilege table then map CREATE privilege on + CREATE|ALTER|DROP|EXECUTE EVENT + */ + if (table->s->fields <= 37 && (user.access & SUPER_ACL)) + user.access|= EVENT_ACL; + + /* + if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. + */ + if (table->s->fields <= 38 && (user.access & SUPER_ACL)) + user.access|= TRIGGER_ACL; + user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); @@ -415,30 +550,31 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.access|= SUPER_ACL | EXECUTE_ACL; #endif } - VOID(push_dynamic(&acl_users,(gptr) &user)); + VOID(push_dynamic(&acl_users,(uchar*) &user)); if (!user.host.hostname || (user.host.hostname[0] == wild_many && !user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect } } - my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, - sizeof(ACL_USER),(qsort_cmp) acl_compare); + my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, + sizeof(ACL_USER),(qsort_cmp) acl_compare); end_read_record(&read_record_info); freeze_size(&acl_users); init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100)); while (!(read_record_info.read_record(&read_record_info))) { ACL_DB db; - update_hostname(&db.host,get_field(&mem, table->field[0])); - db.db=get_field(&mem, table->field[1]); + update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST])); + db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]); if (!db.db) { sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped"); continue; } - db.user=get_field(&mem, table->field[2]); + db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]); if (check_no_resolve && hostname_requires_resolving(db.host.hostname)) { sql_print_warning("'db' entry '%s %s@%s' " @@ -477,10 +613,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; } #endif - VOID(push_dynamic(&acl_dbs,(gptr) &db)); + VOID(push_dynamic(&acl_dbs,(uchar*) &db)); } - my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, - sizeof(ACL_DB),(qsort_cmp) acl_compare); + my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, + sizeof(ACL_DB),(qsort_cmp) acl_compare); end_read_record(&read_record_info); freeze_size(&acl_dbs); init_check_host(); @@ -489,6 +625,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) return_val=0; end: + thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(return_val); } @@ -558,11 +695,13 @@ my_bool acl_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[1].next_local= tables[1].next_global= tables+2; tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; + tables[0].skip_temporary= tables[1].skip_temporary= + tables[2].skip_temporary= TRUE; if (simple_open_n_lock_tables(thd, tables)) { sql_print_error("Fatal error: Can't open and lock privilege tables: %s", - thd->net.last_error); + thd->main_da.message()); goto end; } @@ -628,7 +767,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) Field **pos; for (pos=form->field+fieldnr, bit=1; - *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && + *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM && ((Field_enum*) (*pos))->typelib->count == 2 ; pos++, fieldnr++, bit<<=1) { @@ -1023,11 +1162,11 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, DBUG_RETURN(res); } -static byte* check_get_key(ACL_USER *buff,uint *length, - my_bool not_used __attribute__((unused))) +static uchar* check_get_key(ACL_USER *buff, size_t *length, + my_bool not_used __attribute__((unused))) { *length=buff->hostname_length; - return (byte*) buff->host.hostname; + return (uchar*) buff->host.hostname; } @@ -1108,12 +1247,12 @@ static void acl_insert_user(const char *user, const char *host, set_user_salt(&acl_user, password, password_len); - VOID(push_dynamic(&acl_users,(gptr) &acl_user)); + VOID(push_dynamic(&acl_users,(uchar*) &acl_user)); if (!acl_user.host.hostname || (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect /* purecov: tested */ - my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, - sizeof(ACL_USER),(qsort_cmp) acl_compare); + my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, + sizeof(ACL_USER),(qsort_cmp) acl_compare); /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); @@ -1174,9 +1313,9 @@ static void acl_insert_db(const char *user, const char *host, const char *db, acl_db.db=strdup_root(&mem,db); acl_db.access=privileges; acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); - VOID(push_dynamic(&acl_dbs,(gptr) &acl_db)); - my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, - sizeof(ACL_DB),(qsort_cmp) acl_compare); + VOID(push_dynamic(&acl_dbs,(uchar*) &acl_db)); + my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, + sizeof(ACL_DB),(qsort_cmp) acl_compare); } @@ -1192,7 +1331,8 @@ ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern) { ulong host_access= ~(ulong)0, db_access= 0; - uint i,key_length; + uint i; + size_t key_length; char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; DBUG_ENTER("acl_get"); @@ -1204,8 +1344,9 @@ ulong acl_get(const char *host, const char *ip, my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } - key_length=(uint) (end-key); - if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search(key,key_length))) + key_length= (size_t) (end-key); + if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key, + key_length))) { db_access=entry->access; VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -1259,7 +1400,7 @@ exit: { entry->access=(db_access & host_access); entry->length=key_length; - memcpy((gptr) entry->key,key,key_length); + memcpy((uchar*) entry->key,key,key_length); acl_cache->add(entry); } VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -1301,12 +1442,12 @@ static void init_check_host(void) break; // already stored } if (j == acl_wild_hosts.elements) // If new - (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host); + (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host); } - else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname, - (uint) strlen(acl_user->host.hostname))) + else if (!hash_search(&acl_check_hosts,(uchar*) acl_user->host.hostname, + strlen(acl_user->host.hostname))) { - if (my_hash_insert(&acl_check_hosts,(byte*) acl_user)) + if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user)) { // End of memory allow_all_hosts=1; // Should never happen DBUG_VOID_RETURN; @@ -1344,8 +1485,8 @@ bool acl_check_host(const char *host, const char *ip) return 0; VOID(pthread_mutex_lock(&acl_cache->lock)); - if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) || - ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip))) + if (host && hash_search(&acl_check_hosts,(uchar*) host,strlen(host)) || + ip && hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))) { VOID(pthread_mutex_unlock(&acl_cache->lock)); return 0; // Found host @@ -1404,7 +1545,7 @@ int check_change_password(THD *thd, const char *host, const char *user, MYF(0)); return(1); } - uint len=strlen(new_password); + size_t len= strlen(new_password); if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH && len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { @@ -1438,7 +1579,7 @@ bool change_password(THD *thd, const char *host, const char *user, /* Buffer should be extended when password length is extended. */ char buff[512]; ulong query_length; - uint new_password_len= strlen(new_password); + uint new_password_len= (uint) strlen(new_password); bool result= 1; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", @@ -1457,7 +1598,7 @@ bool change_password(THD *thd, const char *host, const char *user, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on) + if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -1465,12 +1606,12 @@ bool change_password(THD *thd, const char *host, const char *user, */ tables.updating= 1; /* Thanks to bzero, tables.next==0 */ - if (!tables_ok(thd, &tables)) + if (!(thd->spcont || rpl_filter->tables_ok(0, &tables))) DBUG_RETURN(0); } #endif - if (!(table= open_ltable(thd, &tables, TL_WRITE))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(1); VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -1505,8 +1646,7 @@ bool change_password(THD *thd, const char *host, const char *user, acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); thd->clear_error(); - Query_log_event qinfo(thd, buff, query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length, FALSE, FALSE); } end: close_thread_tables(thd); @@ -1640,8 +1780,8 @@ bool hostname_requires_resolving(const char *hostname) char cur; if (!hostname) return FALSE; - int namelen= strlen(hostname); - int lhlen= strlen(my_localhost); + size_t namelen= strlen(hostname); + size_t lhlen= strlen(my_localhost); if ((namelen == lhlen) && !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen, (const uchar *)my_localhost, strlen(my_localhost))) @@ -1678,15 +1818,15 @@ static bool update_user_table(THD *thd, TABLE *table, DBUG_ENTER("update_user_table"); DBUG_PRINT("enter",("user: %s host: %s",user,host)); + table->use_all_columns(); table->field[0]->store(host,(uint) strlen(host), system_charset_info); table->field[1]->store(user,(uint) strlen(user), system_charset_info); - key_copy((byte *) user_key, table->record[0], table->key_info, + key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read_idx(table->record[0], 0, - (byte *) user_key, table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, + (uchar *) user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ @@ -1694,7 +1834,8 @@ static bool update_user_table(THD *thd, TABLE *table, } store_record(table,record[1]); table->field[2]->store(new_password, new_password_len, system_charset_info); - if ((error=table->file->update_row(table->record[1],table->record[0]))) + if ((error=table->file->ha_update_row(table->record[1],table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) { table->file->print_error(error,MYF(0)); /* purecov: deadcode */ DBUG_RETURN(1); @@ -1750,7 +1891,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, const char *password= ""; uint password_len= 0; char what= (revoke_grant) ? 'N' : 'Y'; - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; LEX *lex= thd->lex; DBUG_ENTER("replace_user_table"); @@ -1768,15 +1909,17 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, password=combo.password.str; } - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); - table->field[1]->store(combo.user.str,combo.user.length, system_charset_info); + table->use_all_columns(); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); + table->field[1]->store(combo.user.str,combo.user.length, + system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read_idx(table->record[0], 0, - user_key, table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* what == 'N' means revoke */ if (what == 'N') @@ -1837,7 +1980,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, ulong priv; uint next_field; for (tmp_field= table->field+3, priv = SELECT_ACL; - *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM && + *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM && ((Field_enum*) (*tmp_field))->typelib->count == 2 ; tmp_field++, priv <<= 1) { @@ -1909,19 +2052,23 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, We should NEVER delete from the user table, as a uses can still use mysqld even if he doesn't have any privileges in the user table! */ - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (cmp_record(table,record[1]) && - (error=table->file->update_row(table->record[1],table->record[0]))) - { // This should never happen - table->file->print_error(error,MYF(0)); /* purecov: deadcode */ - error= -1; /* purecov: deadcode */ - goto end; /* purecov: deadcode */ + if (cmp_record(table,record[1])) + { + if ((error= + table->file->ha_update_row(table->record[1],table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) + { // This should never happen + table->file->print_error(error,MYF(0)); /* purecov: deadcode */ + error= -1; /* purecov: deadcode */ + goto end; /* purecov: deadcode */ + } + else + error= 0; } } - else if ((error=table->file->write_row(table->record[0]))) // insert + else if ((error=table->file->ha_write_row(table->record[0]))) // insert { // This should never happen - if (error && error != HA_ERR_FOUND_DUPP_KEY && - error != HA_ERR_FOUND_DUPP_UNIQUE) /* purecov: inspected */ + if (table->file->is_fatal_error(error, HA_CHECK_DUP)) { table->file->print_error(error,MYF(0)); /* purecov: deadcode */ error= -1; /* purecov: deadcode */ @@ -1969,7 +2116,7 @@ static int replace_db_table(TABLE *table, const char *db, bool old_row_exists=0; int error; char what= (revoke_grant) ? 'N' : 'Y'; - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; DBUG_ENTER("replace_db_table"); if (!initialized) @@ -1985,16 +2132,18 @@ static int replace_db_table(TABLE *table, const char *db, DBUG_RETURN(-1); } - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->use_all_columns(); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read_idx(table->record[0],0, - user_key, table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0],0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -2003,9 +2152,11 @@ static int replace_db_table(TABLE *table, const char *db, } old_row_exists = 0; restore_record(table, s->default_values); - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); } else { @@ -2027,19 +2178,20 @@ static int replace_db_table(TABLE *table, const char *db, /* update old existing row */ if (rights) { - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if ((error=table->file->update_row(table->record[1],table->record[0]))) + if ((error= table->file->ha_update_row(table->record[1], + table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) goto table_error; /* purecov: deadcode */ } else /* must have been a revoke of all privileges */ { - if ((error = table->file->delete_row(table->record[1]))) + if ((error= table->file->ha_delete_row(table->record[1]))) goto table_error; /* purecov: deadcode */ } } - else if (rights && (error=table->file->write_row(table->record[0]))) + else if (rights && (error= table->file->ha_write_row(table->record[0]))) { - if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */ + if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) goto table_error; /* purecov: deadcode */ } @@ -2068,16 +2220,16 @@ public: uint key_length; GRANT_COLUMN(String &c, ulong y) :rights (y) { - column= memdup_root(&memex,c.ptr(), key_length=c.length()); + column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length()); } }; -static byte* get_key_column(GRANT_COLUMN *buff,uint *length, +static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length, my_bool not_used __attribute__((unused))) { *length=buff->key_length; - return (byte*) buff->column; + return (uchar*) buff->column; } @@ -2088,7 +2240,7 @@ public: char *db, *user, *tname, *hash_key; ulong privs; ulong sort; - uint key_length; + size_t key_length; GRANT_NAME(const char *h, const char *d,const char *u, const char *t, ulong p); GRANT_NAME (TABLE *form); @@ -2127,8 +2279,8 @@ GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, my_casedn_str(files_charset_info, db); my_casedn_str(files_charset_info, tname); } - key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; - hash_key = (char*) alloc_root(&memex,key_length); + key_length= strlen(d) + strlen(u)+ strlen(t)+3; + hash_key= (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); } @@ -2137,7 +2289,7 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) :GRANT_NAME(h,d,u,t,p), cols(c) { - (void) hash_init(&hash_columns,system_charset_info, + (void) hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); } @@ -2162,9 +2314,8 @@ GRANT_NAME::GRANT_NAME(TABLE *form) my_casedn_str(files_charset_info, db); my_casedn_str(files_charset_info, tname); } - key_length = ((uint) strlen(db) + (uint) strlen(user) + - (uint) strlen(tname) + 3); - hash_key = (char*) alloc_root(&memex,key_length); + key_length= (strlen(db) + strlen(user) + strlen(tname) + 3); + hash_key= (char*) alloc_root(&memex, key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (ulong) form->field[6]->val_int(); privs = fix_rights_for_table(privs); @@ -2174,7 +2325,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form) GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) :GRANT_NAME(form) { - byte key[MAX_KEY_LENGTH]; + uchar key[MAX_KEY_LENGTH]; if (!db || !tname) { @@ -2186,14 +2337,15 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) cols= (ulong) form->field[7]->val_int(); cols = fix_rights_for_column(cols); - (void) hash_init(&hash_columns,system_charset_info, + (void) hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); if (cols) { uint key_prefix_len; KEY_PART_INFO *key_part= col_privs->key_info->key_part; col_privs->field[0]->store(host.hostname, - host.hostname ? (uint) strlen(host.hostname) : 0, + host.hostname ? (uint) strlen(host.hostname) : + 0, system_charset_info); col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info); col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info); @@ -2206,10 +2358,9 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len); col_privs->field[4]->store("",0, &my_charset_latin1); - col_privs->file->ha_index_init(0); - if (col_privs->file->index_read(col_privs->record[0], - (byte*) key, - key_prefix_len, HA_READ_KEY_EXACT)) + col_privs->file->ha_index_init(0, 1); + if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key, + (key_part_map)15, HA_READ_KEY_EXACT)) { cols = 0; /* purecov: deadcode */ col_privs->file->ha_index_end(); @@ -2229,7 +2380,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) privs = cols = 0; /* purecov: deadcode */ return; /* purecov: deadcode */ } - my_hash_insert(&hash_columns, (byte *) mem_check); + my_hash_insert(&hash_columns, (uchar *) mem_check); } while (!col_privs->file->index_next(col_privs->record[0]) && !key_cmp_if_same(col_privs,key,0,key_prefix_len)); col_privs->file->ha_index_end(); @@ -2243,11 +2394,11 @@ GRANT_TABLE::~GRANT_TABLE() } -static byte* get_grant_table(GRANT_NAME *buff,uint *length, +static uchar* get_grant_table(GRANT_NAME *buff, size_t *length, my_bool not_used __attribute__((unused))) { *length=buff->key_length; - return (byte*) buff->hash_key; + return (uchar*) buff->hash_key; } @@ -2260,10 +2411,10 @@ void free_grant_table(GRANT_TABLE *grant_table) /* Search after a matching grant. Prefer exact grants before not exact ones */ static GRANT_NAME *name_hash_search(HASH *name_hash, - const char *host,const char* ip, - const char *db, - const char *user, const char *tname, - bool exact) + const char *host,const char* ip, + const char *db, + const char *user, const char *tname, + bool exact) { char helping [NAME_LEN*2+USERNAME_LENGTH+3]; uint len; @@ -2271,10 +2422,10 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, HASH_SEARCH_STATE state; len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; - for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping, + for (grant_name= (GRANT_NAME*) hash_first(name_hash, (uchar*) helping, len, &state); grant_name ; - grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping, + grant_name= (GRANT_NAME*) hash_next(name_hash,(uchar*) helping, len, &state)) { if (exact) @@ -2319,7 +2470,7 @@ table_hash_search(const char *host, const char *ip, const char *db, inline GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, uint length) { - return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length); + return (GRANT_COLUMN*) hash_search(&t->hash_columns, (uchar*) cname,length); } @@ -2330,11 +2481,12 @@ static int replace_column_table(GRANT_TABLE *g_t, ulong rights, bool revoke_grant) { int error=0,result=0; - byte key[MAX_KEY_LENGTH]; + uchar key[MAX_KEY_LENGTH]; uint key_prefix_length; KEY_PART_INFO *key_part= table->key_info->key_part; DBUG_ENTER("replace_column_table"); + table->use_all_columns(); table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); table->field[1]->store(db,(uint) strlen(db), @@ -2344,7 +2496,7 @@ static int replace_column_table(GRANT_TABLE *g_t, table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info); - /* Get length of 3 first key parts */ + /* Get length of 4 first key parts */ key_prefix_length= (key_part[0].store_length + key_part[1].store_length + key_part[2].store_length + key_part[3].store_length); key_copy(key, table->record[0], table->key_info, key_prefix_length); @@ -2355,12 +2507,12 @@ static int replace_column_table(GRANT_TABLE *g_t, List_iterator <LEX_COLUMN> iter(columns); class LEX_COLUMN *column; - table->file->ha_index_init(0); + table->file->ha_index_init(0, 1); while ((column= iter++)) { ulong privileges= column->rights; bool old_row_exists=0; - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; key_restore(table->record[0],key,table->key_info, key_prefix_length); @@ -2370,10 +2522,8 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read(table->record[0], user_key, - table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (revoke_grant) { @@ -2409,15 +2559,17 @@ static int replace_column_table(GRANT_TABLE *g_t, { GRANT_COLUMN *grant_column; if (privileges) - error=table->file->update_row(table->record[1],table->record[0]); + error=table->file->ha_update_row(table->record[1],table->record[0]); else - error=table->file->delete_row(table->record[1]); - if (error) + error=table->file->ha_delete_row(table->record[1]); + if (error && error != HA_ERR_RECORD_IS_THE_SAME) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ result= -1; /* purecov: inspected */ goto end; /* purecov: inspected */ } + else + error= 0; grant_column= column_hash_search(g_t, column->column.ptr(), column->column.length()); if (grant_column) // Should always be true @@ -2426,14 +2578,14 @@ static int replace_column_table(GRANT_TABLE *g_t, else // new grant { GRANT_COLUMN *grant_column; - if ((error=table->file->write_row(table->record[0]))) + if ((error=table->file->ha_write_row(table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ result= -1; /* purecov: inspected */ goto end; /* purecov: inspected */ } grant_column= new GRANT_COLUMN(column->column,privileges); - my_hash_insert(&g_t->hash_columns,(byte*) grant_column); + my_hash_insert(&g_t->hash_columns,(uchar*) grant_column); } } @@ -2444,14 +2596,13 @@ static int replace_column_table(GRANT_TABLE *g_t, if (revoke_grant) { - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read(table->record[0], user_key, - key_prefix_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], user_key, + (key_part_map)15, + HA_READ_KEY_EXACT)) goto end; /* Scan through all rows with the same host,db,user and table */ @@ -2478,8 +2629,9 @@ static int replace_column_table(GRANT_TABLE *g_t, if (privileges) { int tmp_error; - if ((tmp_error=table->file->update_row(table->record[1], - table->record[0]))) + if ((tmp_error=table->file->ha_update_row(table->record[1], + table->record[0])) && + tmp_error != HA_ERR_RECORD_IS_THE_SAME) { /* purecov: deadcode */ table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */ result= -1; /* purecov: deadcode */ @@ -2491,14 +2643,14 @@ static int replace_column_table(GRANT_TABLE *g_t, else { int tmp_error; - if ((tmp_error = table->file->delete_row(table->record[1]))) + if ((tmp_error = table->file->ha_delete_row(table->record[1]))) { /* purecov: deadcode */ table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */ result= -1; /* purecov: deadcode */ goto end; /* purecov: deadcode */ } if (grant_column) - hash_delete(&g_t->hash_columns,(byte*) grant_column); + hash_delete(&g_t->hash_columns,(uchar*) grant_column); } } } while (!table->file->index_next(table->record[0]) && @@ -2521,7 +2673,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, int old_row_exists = 1; int error=0; ulong store_table_rights, store_col_rights; - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; DBUG_ENTER("replace_table_table"); strxmov(grantor, thd->security_ctx->user, "@", @@ -2538,19 +2690,22 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, DBUG_RETURN(-1); /* purecov: deadcode */ } + table->use_all_columns(); restore_record(table, s->default_values); // Get empty record - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); - table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); + table->field[3]->store(table_name,(uint) strlen(table_name), + system_charset_info); store_record(table,record[1]); // store at pos 1 key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - if (table->file->index_read_idx(table->record[0], 0, - user_key, table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -2599,16 +2754,18 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, { if (store_table_rights || store_col_rights) { - if ((error=table->file->update_row(table->record[1],table->record[0]))) + if ((error=table->file->ha_update_row(table->record[1], + table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) goto table_error; /* purecov: deadcode */ } - else if ((error = table->file->delete_row(table->record[1]))) + else if ((error = table->file->ha_delete_row(table->record[1]))) goto table_error; /* purecov: deadcode */ } else { - error=table->file->write_row(table->record[0]); - if (error && error != HA_ERR_FOUND_DUPP_KEY) + error=table->file->ha_write_row(table->record[0]); + if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) goto table_error; /* purecov: deadcode */ } @@ -2619,7 +2776,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } else { - hash_delete(&column_priv_hash,(byte*) grant_table); + hash_delete(&column_priv_hash,(uchar*) grant_table); } DBUG_RETURN(0); @@ -2630,6 +2787,10 @@ table_error: } +/** + @retval 0 success + @retval -1 error +*/ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TABLE *table, const LEX_USER &combo, const char *db, const char *routine_name, @@ -2651,29 +2812,28 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, thd->security_ctx->host_or_ip, NullS); /* - The following should always succeed as new users are created before - this function is called! + New users are created before this function is called. + + There may be some cases where a routine's definer is removed but the + routine remains. */ - if (!find_acl_user(combo.host.str, combo.user.str, FALSE)) - { - my_error(ER_PASSWORD_NO_MATCH,MYF(0)); - DBUG_RETURN(-1); - } + table->use_all_columns(); restore_record(table, s->default_values); // Get empty record table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[3]->store(routine_name,(uint) strlen(routine_name), &my_charset_latin1); - table->field[4]->store((longlong)(is_proc ? + table->field[4]->store((longlong)(is_proc ? TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION), TRUE); store_record(table,record[1]); // store at pos 1 - if (table->file->index_read_idx(table->record[0],0, - (byte*) table->field[0]->ptr,0, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -2716,16 +2876,18 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, { if (store_proc_rights) { - if ((error=table->file->update_row(table->record[1],table->record[0]))) + if ((error=table->file->ha_update_row(table->record[1], + table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) goto table_error; } - else if ((error= table->file->delete_row(table->record[1]))) + else if ((error= table->file->ha_delete_row(table->record[1]))) goto table_error; } else { - error=table->file->write_row(table->record[0]); - if (error && error != HA_ERR_FOUND_DUPP_KEY) + error=table->file->ha_write_row(table->record[0]); + if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) goto table_error; } @@ -2735,7 +2897,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, } else { - hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name); + hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name); } DBUG_RETURN(0); @@ -2825,9 +2987,10 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (!(rights & CREATE_ACL)) { char buf[FN_REFLEN]; - sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db, - table_list->table_name); - fn_format(buf,buf,"","",4+16+32); + build_table_filename(buf, sizeof(buf), table_list->db, + table_list->table_name, reg_ext, 0); + fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS | + MY_RETURN_REAL_PATH | MY_APPEND_EXT); if (access(buf,F_OK)) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); @@ -2863,19 +3026,26 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on) + if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into account in tests. */ tables[0].updating= tables[1].updating= tables[2].updating= 1; - if (!tables_ok(thd, tables)) + if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) DBUG_RETURN(FALSE); } #endif @@ -2949,7 +3119,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, result= TRUE; /* purecov: deadcode */ continue; /* purecov: deadcode */ } - my_hash_insert(&column_priv_hash,(byte*) grant_table); + my_hash_insert(&column_priv_hash,(uchar*) grant_table); } /* If revoke_grant, calculate the new column privilege for tables_priv */ @@ -3004,24 +3174,18 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } } } - thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); if (!result) /* success */ { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); } rw_unlock(&LOCK_grant); if (!result) /* success */ - send_ok(thd); + my_ok(thd); /* Tables are automatically closed */ thd->lex->restore_backup_query_tables_list(&backup); @@ -3087,19 +3251,26 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on) + if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into account in tests. */ tables[0].updating= tables[1].updating= 1; - if (!tables_ok(thd, tables)) + if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) DBUG_RETURN(FALSE); } #endif @@ -3162,33 +3333,28 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, result= TRUE; continue; } - my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name); + my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name); } if (replace_routine_table(thd, grant_name, tables[1].table, *Str, - db_name, table_name, is_proc, rights, revoke_grant)) + db_name, table_name, is_proc, rights, + revoke_grant) != 0) { result= TRUE; continue; } } - thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); if (!result && !no_error) { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); } rw_unlock(&LOCK_grant); if (!result && !no_error) - send_ok(thd); + my_ok(thd); /* Tables are automatically closed */ DBUG_RETURN(result); @@ -3226,19 +3392,26 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on) + if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into account in tests. */ tables[0].updating= tables[1].updating= 1; - if (!tables_ok(thd, tables)) + if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) DBUG_RETURN(FALSE); } #endif @@ -3290,19 +3463,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, if (!result) { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); } rw_unlock(&LOCK_grant); close_thread_tables(thd); if (!result) - send_ok(thd); + my_ok(thd); DBUG_RETURN(result); } @@ -3313,7 +3481,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, void grant_free(void) { DBUG_ENTER("grant_free"); - grant_option = FALSE; hash_free(&column_priv_hash); hash_free(&proc_priv_hash); hash_free(&func_priv_hash); @@ -3322,16 +3489,13 @@ void grant_free(void) } -/* - Initialize structures responsible for table/column-level privilege checking - and load information for them from tables in the 'mysql' database. - - SYNOPSIS - grant_init() +/** + @brief Initialize structures responsible for table/column-level privilege + checking and load information for them from tables in the 'mysql' database. - RETURN VALUES - 0 ok - 1 Could not initialize grant's + @return Error status + @retval 0 OK + @retval 1 Could not initialize grant subsystem. */ my_bool grant_init() @@ -3344,107 +3508,159 @@ my_bool grant_init() DBUG_RETURN(1); /* purecov: deadcode */ thd->thread_stack= (char*) &thd; thd->store_globals(); + lex_start(thd); return_val= grant_reload(thd); delete thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); - /* Set the grant option flag so we will check grants */ - grant_option= TRUE; DBUG_RETURN(return_val); } -/* - Initialize structures responsible for table/column-level privilege - checking and load information about grants from open privilege tables. +/** + @brief Helper function to grant_reload_procs_priv - SYNOPSIS - grant_load() - thd Current thread - tables List containing open "mysql.tables_priv" and - "mysql.columns_priv" tables. + Reads the procs_priv table into memory hash. - RETURN VALUES - FALSE - success - TRUE - error + @param table A pointer to the procs_priv table structure. + + @see grant_reload + @see grant_reload_procs_priv + + @return Error state + @retval TRUE An error occurred + @retval FALSE Success */ -static my_bool grant_load(TABLE_LIST *tables) +static my_bool grant_load_procs_priv(TABLE *p_table) { MEM_ROOT *memex_ptr; my_bool return_val= 1; - TABLE *t_table, *c_table, *p_table; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); - DBUG_ENTER("grant_load"); - - (void) hash_init(&column_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - (hash_free_key) free_grant_table,0); + DBUG_ENTER("grant_load_procs_priv"); (void) hash_init(&proc_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - 0,0); + 0,0,0, (hash_get_key) get_grant_table, + 0,0); (void) hash_init(&func_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - 0,0); - init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); + 0,0,0, (hash_get_key) get_grant_table, + 0,0); + p_table->file->ha_index_init(0, 1); + p_table->use_all_columns(); - t_table = tables[0].table; c_table = tables[1].table; - p_table= tables[2].table; - t_table->file->ha_index_init(0); - p_table->file->ha_index_init(0); - if (!t_table->file->index_first(t_table->record[0])) + if (!p_table->file->index_first(p_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); do { - GRANT_TABLE *mem_check; - if (!(mem_check=new GRANT_TABLE(t_table,c_table))) + GRANT_NAME *mem_check; + HASH *hash; + if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table))) { - /* This could only happen if we are out memory */ - grant_option= FALSE; - goto end_unlock; + /* This could only happen if we are out memory */ + goto end_unlock; } if (check_no_resolve) { if (hostname_requires_resolving(mem_check->host.hostname)) { - sql_print_warning("'tables_priv' entry '%s %s@%s' " + sql_print_warning("'procs_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - mem_check->tname, - mem_check->user ? mem_check->user : "", + mem_check->tname, mem_check->user, mem_check->host.hostname ? mem_check->host.hostname : ""); - continue; - } + continue; + } + } + if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE) + { + hash= &proc_priv_hash; + } + else + if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION) + { + hash= &func_priv_hash; + } + else + { + sql_print_warning("'procs_priv' entry '%s' " + "ignored, bad routine type", + mem_check->tname); + continue; } + mem_check->privs= fix_rights_for_procedure(mem_check->privs); if (! mem_check->ok()) - delete mem_check; - else if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) + delete mem_check; + else if (my_hash_insert(hash, (uchar*) mem_check)) { - delete mem_check; - grant_option= FALSE; - goto end_unlock; + delete mem_check; + goto end_unlock; } } - while (!t_table->file->index_next(t_table->record[0])); + while (!p_table->file->index_next(p_table->record[0])); } - if (!p_table->file->index_first(p_table->record[0])) + /* Return ok */ + return_val= 0; + +end_unlock: + p_table->file->ha_index_end(); + my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); + DBUG_RETURN(return_val); +} + + +/** + @brief Initialize structures responsible for table/column-level privilege + checking and load information about grants from open privilege tables. + + @param thd Current thread + @param tables List containing open "mysql.tables_priv" and + "mysql.columns_priv" tables. + + @see grant_reload + + @return Error state + @retval FALSE Success + @retval TRUE Error +*/ + +static my_bool grant_load(THD *thd, TABLE_LIST *tables) +{ + MEM_ROOT *memex_ptr; + my_bool return_val= 1; + TABLE *t_table= 0, *c_table= 0; + bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; + MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, + THR_MALLOC); + ulong old_sql_mode= thd->variables.sql_mode; + DBUG_ENTER("grant_load"); + + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + (void) hash_init(&column_priv_hash,system_charset_info, + 0,0,0, (hash_get_key) get_grant_table, + (hash_free_key) free_grant_table,0); + + t_table = tables[0].table; + c_table = tables[1].table; + t_table->file->ha_index_init(0, 1); + t_table->use_all_columns(); + c_table->use_all_columns(); + + if (!t_table->file->index_first(t_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); do { - GRANT_NAME *mem_check; - HASH *hash; - if (!(mem_check=new GRANT_NAME(p_table))) + GRANT_TABLE *mem_check; + if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table))) { /* This could only happen if we are out memory */ - grant_option= FALSE; goto end_unlock; } @@ -3452,75 +3668,113 @@ static my_bool grant_load(TABLE_LIST *tables) { if (hostname_requires_resolving(mem_check->host.hostname)) { - sql_print_warning("'procs_priv' entry '%s %s@%s' " + sql_print_warning("'tables_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - mem_check->tname, mem_check->user, + mem_check->tname, + mem_check->user ? mem_check->user : "", mem_check->host.hostname ? mem_check->host.hostname : ""); continue; } } - if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE) - { - hash= &proc_priv_hash; - } - else - if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION) - { - hash= &func_priv_hash; - } - else - { - sql_print_warning("'procs_priv' entry '%s' " - "ignored, bad routine type", - mem_check->tname); - continue; - } - mem_check->privs= fix_rights_for_procedure(mem_check->privs); if (! mem_check->ok()) delete mem_check; - else if (my_hash_insert(hash, (byte*) mem_check)) + else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check)) { delete mem_check; - grant_option= FALSE; goto end_unlock; } } - while (!p_table->file->index_next(p_table->record[0])); + while (!t_table->file->index_next(t_table->record[0])); } + return_val=0; // Return ok end_unlock: + thd->variables.sql_mode= old_sql_mode; t_table->file->ha_index_end(); - p_table->file->ha_index_end(); my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); DBUG_RETURN(return_val); } -/* - Reload information about table and column level privileges if possible. +/** + @brief Helper function to grant_reload. Reloads procs_priv table is it + exists. - SYNOPSIS - grant_reload() - thd Current thread + @param thd A pointer to the thread handler object. - NOTES - Locked tables are checked by acl_reload() and doesn't have to be checked - in this call. - This function is also used for initialization of structures responsible - for table/column-level privilege checking. + @see grant_reload - RETURN VALUE - FALSE Success - TRUE Error + @return Error state + @retval FALSE Success + @retval TRUE An error has occurred. +*/ + +static my_bool grant_reload_procs_priv(THD *thd) +{ + HASH old_proc_priv_hash, old_func_priv_hash; + TABLE_LIST table; + my_bool return_val= FALSE; + DBUG_ENTER("grant_reload_procs_priv"); + + bzero((char*) &table, sizeof(table)); + table.alias= table.table_name= (char*) "procs_priv"; + table.db= (char *) "mysql"; + table.lock_type= TL_READ; + table.skip_temporary= 1; + + if (simple_open_n_lock_tables(thd, &table)) + { + close_thread_tables(thd); + DBUG_RETURN(TRUE); + } + + /* Save a copy of the current hash if we need to undo the grant load */ + old_proc_priv_hash= proc_priv_hash; + old_func_priv_hash= func_priv_hash; + + rw_wrlock(&LOCK_grant); + if ((return_val= grant_load_procs_priv(table.table))) + { + /* Error; Reverting to old hash */ + DBUG_PRINT("error",("Reverting to old privileges")); + grant_free(); + proc_priv_hash= old_proc_priv_hash; + func_priv_hash= old_func_priv_hash; + } + else + { + hash_free(&old_proc_priv_hash); + hash_free(&old_func_priv_hash); + } + rw_unlock(&LOCK_grant); + + close_thread_tables(thd); + DBUG_RETURN(return_val); +} + + +/** + @brief Reload information about table and column level privileges if possible + + @param thd Current thread + + Locked tables are checked by acl_reload() and doesn't have to be checked + in this call. + This function is also used for initialization of structures responsible + for table/column-level privilege checking. + + @return Error state + @retval FALSE Success + @retval TRUE Error */ my_bool grant_reload(THD *thd) { - TABLE_LIST tables[3]; - HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash; + TABLE_LIST tables[2]; + HASH old_column_priv_hash; MEM_ROOT old_mem; my_bool return_val= 1; DBUG_ENTER("grant_reload"); @@ -3532,12 +3786,10 @@ my_bool grant_reload(THD *thd) bzero((char*) tables, sizeof(tables)); tables[0].alias= tables[0].table_name= (char*) "tables_priv"; tables[1].alias= tables[1].table_name= (char*) "columns_priv"; - tables[2].alias= tables[2].table_name= (char*) "procs_priv"; - tables[0].db= tables[1].db= tables[2].db= (char *) "mysql"; + tables[0].db= tables[1].db= (char *) "mysql"; tables[0].next_local= tables[0].next_global= tables+1; - tables[1].next_local= tables[1].next_global= tables+2; - tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= TL_READ; - + tables[0].lock_type= tables[1].lock_type= TL_READ; + tables[0].skip_temporary= tables[1].skip_temporary= TRUE; /* To avoid deadlocks we should obtain table locks before obtaining LOCK_grant rwlock. @@ -3546,35 +3798,45 @@ my_bool grant_reload(THD *thd) goto end; rw_wrlock(&LOCK_grant); - grant_version++; old_column_priv_hash= column_priv_hash; - old_proc_priv_hash= proc_priv_hash; - old_func_priv_hash= func_priv_hash; + + /* + Create a new memory pool but save the current memory pool to make an undo + opertion possible in case of failure. + */ old_mem= memex; + init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); - if ((return_val= grant_load(tables))) + if ((return_val= grant_load(thd, tables))) { // Error. Revert to old hash DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ - proc_priv_hash= old_proc_priv_hash; - func_priv_hash= old_func_priv_hash; memex= old_mem; /* purecov: deadcode */ } else { hash_free(&old_column_priv_hash); - hash_free(&old_proc_priv_hash); - hash_free(&old_func_priv_hash); free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); -end: close_thread_tables(thd); + + /* + It is OK failing to load procs_priv table because we may be + working with 4.1 privilege tables. + */ + if (grant_reload_procs_priv(thd)) + return_val= 1; + + rw_wrlock(&LOCK_grant); + grant_version++; + rw_unlock(&LOCK_grant); + +end: DBUG_RETURN(return_val); } - /**************************************************************************** Check table level grants @@ -3623,7 +3885,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, of other queries). For simple queries first_not_own_table is 0. */ for (i= 0, table= tables; - table != first_not_own_table && i < number; + i < number && table != first_not_own_table; table= table->next_global, i++) { /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ @@ -3831,8 +4093,8 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, /* Normal or temporary table. */ TABLE *table= table_ref->table; grant= &(table->grant); - db_name= table->s->db; - table_name= table->s->table_name; + db_name= table->s->db.str; + table_name= table->s->table_name.str; } if (grant->want_privilege) @@ -3866,60 +4128,55 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, ulong want_access= want_access_arg; const char *table_name= NULL; - if (grant_option) - { - const char* db_name; - GRANT_INFO *grant; - /* Initialized only to make gcc happy */ - GRANT_TABLE *grant_table= NULL; + const char* db_name; + GRANT_INFO *grant; + /* Initialized only to make gcc happy */ + GRANT_TABLE *grant_table= NULL; - rw_rdlock(&LOCK_grant); + rw_rdlock(&LOCK_grant); - for (; !fields->end_of_fields(); fields->next()) - { - const char *field_name= fields->name(); + for (; !fields->end_of_fields(); fields->next()) + { + const char *field_name= fields->name(); - if (table_name != fields->table_name()) + if (table_name != fields->table_name()) + { + table_name= fields->table_name(); + db_name= fields->db_name(); + grant= fields->grant(); + /* get a fresh one for each table */ + want_access= want_access_arg & ~grant->privilege; + if (want_access) { - table_name= fields->table_name(); - db_name= fields->db_name(); - grant= fields->grant(); - /* get a fresh one for each table */ - want_access= want_access_arg & ~grant->privilege; - if (want_access) + /* reload table if someone has modified any grants */ + if (grant->version != grant_version) { - /* reload table if someone has modified any grants */ - if (grant->version != grant_version) - { - grant->grant_table= - table_hash_search(sctx->host, sctx->ip, db_name, - sctx->priv_user, - table_name, 0); /* purecov: inspected */ - grant->version= grant_version; /* purecov: inspected */ - } - - grant_table= grant->grant_table; - DBUG_ASSERT (grant_table); + grant->grant_table= + table_hash_search(sctx->host, sctx->ip, db_name, + sctx->priv_user, + table_name, 0); /* purecov: inspected */ + grant->version= grant_version; /* purecov: inspected */ } - } - if (want_access) - { - GRANT_COLUMN *grant_column= - column_hash_search(grant_table, field_name, - (uint) strlen(field_name)); - if (!grant_column || (~grant_column->rights & want_access)) - goto err; + grant_table= grant->grant_table; + DBUG_ASSERT (grant_table); } } - rw_unlock(&LOCK_grant); - return 0; -err: - rw_unlock(&LOCK_grant); + if (want_access) + { + GRANT_COLUMN *grant_column= + column_hash_search(grant_table, field_name, + (uint) strlen(field_name)); + if (!grant_column || (~grant_column->rights & want_access)) + goto err; + } } - else - table_name= fields->table_name(); + rw_unlock(&LOCK_grant); + return 0; + +err: + rw_unlock(&LOCK_grant); char command[128]; get_privilege_desc(command, sizeof(command), want_access); @@ -4079,18 +4336,15 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc) { bool no_routine_acl= 1; - if (grant_option) - { - GRANT_NAME *grant_proc; - Security_context *sctx= thd->security_ctx; - rw_rdlock(&LOCK_grant); - if ((grant_proc= routine_hash_search(sctx->priv_host, - sctx->ip, db, - sctx->priv_user, - name, is_proc, 0))) - no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); - rw_unlock(&LOCK_grant); - } + GRANT_NAME *grant_proc; + Security_context *sctx= thd->security_ctx; + rw_rdlock(&LOCK_grant); + if ((grant_proc= routine_hash_search(sctx->priv_host, + sctx->ip, db, + sctx->priv_user, + name, is_proc, 0))) + no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); + rw_unlock(&LOCK_grant); return no_routine_acl; } @@ -4199,13 +4453,13 @@ static const char *command_array[]= "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", - "CREATE USER" + "CREATE USER", "EVENT", "TRIGGER" }; static uint command_lengths[]= { 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, - 14, 13, 11 + 14, 13, 11, 5, 7 }; @@ -4577,7 +4831,7 @@ end: VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); - send_eof(thd); + my_eof(thd); DBUG_RETURN(error); } @@ -4762,7 +5016,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && table_rules_on) + if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -4770,7 +5024,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) */ tables[0].updating=tables[1].updating=tables[2].updating= tables[3].updating=tables[4].updating=1; - if (!tables_ok(thd, tables)) + if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) DBUG_RETURN(1); tables[0].updating=tables[1].updating=tables[2].updating= tables[3].updating=tables[4].updating=0;; @@ -4847,13 +5101,17 @@ static int modify_grant_table(TABLE *table, Field *host_field, system_charset_info); user_field->store(user_to->user.str, user_to->user.length, system_charset_info); - if ((error= table->file->update_row(table->record[1], table->record[0]))) + if ((error= table->file->ha_update_row(table->record[1], + table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) table->file->print_error(error, MYF(0)); + else + error= 0; } else { /* delete */ - if ((error=table->file->delete_row(table->record[0]))) + if ((error=table->file->ha_delete_row(table->record[0]))) table->file->print_error(error, MYF(0)); } @@ -4904,11 +5162,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, char *user_str= user_from->user.str; const char *host; const char *user; - byte user_key[MAX_KEY_LENGTH]; + uchar user_key[MAX_KEY_LENGTH]; uint key_prefix_length; DBUG_ENTER("handle_grant_table"); THD *thd= current_thd; + table->use_all_columns(); if (! table_no) // mysql.user table { /* @@ -4921,7 +5180,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, by the searched record, if it exists. */ DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'", - table->s->table_name, user_str, host_str)); + table->s->table_name.str, user_str, host_str)); host_field->store(host_str, user_from->host.length, system_charset_info); user_field->store(user_str, user_from->user.length, system_charset_info); @@ -4929,11 +5188,11 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, table->key_info->key_part[1].store_length); key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if ((error= table->file->index_read_idx(table->record[0], 0, - user_key, key_prefix_length, - HA_READ_KEY_EXACT))) + if ((error= table->file->index_read_idx_map(table->record[0], 0, + user_key, (key_part_map)3, + HA_READ_KEY_EXACT))) { - if (error != HA_ERR_KEY_NOT_FOUND) + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { table->file->print_error(error, MYF(0)); result= -1; @@ -4964,7 +5223,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, { #ifdef EXTRA_DEBUG DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'", - table->s->table_name, user_str, host_str)); + table->s->table_name.str, user_str, host_str)); #endif while ((error= table->file->rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) @@ -5056,6 +5315,8 @@ static int handle_grant_struct(uint struct_no, bool drop, LINT_INIT(acl_user); LINT_INIT(acl_db); LINT_INIT(grant_name); + LINT_INIT(user); + LINT_INIT(host); safe_mutex_assert_owner(&acl_cache->lock); @@ -5130,8 +5391,7 @@ static int handle_grant_struct(uint struct_no, bool drop, result= 1; /* At least one element found. */ if ( drop ) { - switch ( struct_no ) - { + switch ( struct_no ) { case 0: delete_dynamic_element(&acl_users, idx); break; @@ -5141,11 +5401,11 @@ static int handle_grant_struct(uint struct_no, bool drop, break; case 2: - hash_delete(&column_priv_hash, (byte*) grant_name); + hash_delete(&column_priv_hash, (uchar*) grant_name); break; case 3: - hash_delete(&proc_priv_hash, (byte*) grant_name); + hash_delete(&proc_priv_hash, (uchar*) grant_name); break; } elements--; @@ -5345,6 +5605,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) bool some_users_created= FALSE; DBUG_ENTER("mysql_create_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5385,11 +5652,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) if (result) my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe()); - if (some_users_created && mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + if (some_users_created) + write_bin_log(thd, FALSE, thd->query, thd->query_length); rw_unlock(&LOCK_grant); close_thread_tables(thd); @@ -5420,6 +5684,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) bool some_users_deleted= FALSE; DBUG_ENTER("mysql_drop_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5452,14 +5723,8 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) if (result) my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe()); - DBUG_PRINT("info", ("thd->net.last_errno: %d", thd->net.last_errno)); - DBUG_PRINT("info", ("thd->net.last_error: %s", thd->net.last_error)); - - if (some_users_deleted && mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + if (some_users_deleted) + write_bin_log(thd, FALSE, thd->query, thd->query_length); rw_unlock(&LOCK_grant); close_thread_tables(thd); @@ -5491,6 +5756,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) bool some_users_renamed= FALSE; DBUG_ENTER("mysql_rename_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5536,10 +5808,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe()); if (some_users_renamed && mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, FALSE, thd->query, thd->query_length); rw_unlock(&LOCK_grant); close_thread_tables(thd); @@ -5569,6 +5838,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5618,7 +5894,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { - if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1)) + if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, + ~(ulong)0, 1)) { /* Don't increment counter as replace_db_table deleted the @@ -5695,11 +5972,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { - if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user, + if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user, grant_proc->db, grant_proc->tname, is_proc, - ~(ulong)0, 1)) + ~(ulong)0, 1) == 0) { revoked= 1; continue; @@ -5713,11 +5990,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) VOID(pthread_mutex_unlock(&acl_cache->lock)); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, FALSE, thd->query, thd->query_length); rw_unlock(&LOCK_grant); close_thread_tables(thd); @@ -5729,17 +6002,73 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } -/* - Revoke privileges for all users on a stored procedure - SYNOPSIS - sp_revoke_privileges() + +/** + If the defining user for a routine does not exist, then the ACL lookup + code should raise two errors which we should intercept. We convert the more + descriptive error into a warning, and consume the other. + + If any other errors are raised, then we set a flag that should indicate + that there was some failure we should complain at a higher level. +*/ +class Silence_routine_definer_errors : public Internal_error_handler +{ +public: + Silence_routine_definer_errors() + : is_grave(FALSE) + {} + + virtual ~Silence_routine_definer_errors() + {} + + virtual bool handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd); + + bool has_errors() { return is_grave; } + +private: + bool is_grave; +}; + +bool +Silence_routine_definer_errors::handle_error(uint sql_errno, + const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd) +{ + if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + { + switch (sql_errno) + { + case ER_NONEXISTING_PROC_GRANT: + /* Convert the error into a warning. */ + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message); + return TRUE; + default: + is_grave= TRUE; + } + } + + return FALSE; +} + + +/** + Revoke privileges for all users on a stored procedure. Use an error handler + that converts errors about missing grants into warnings. + + @param thd The current thread. + @param db DB of the stored procedure + @param name Name of the stored procedure - RETURN + @retval 0 OK. + @retval < 0 Error. Error message not yet sent. */ @@ -5750,14 +6079,25 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, int result; TABLE_LIST tables[GRANT_TABLES]; HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); + /* Be sure to pop this before exiting this scope! */ + thd->push_internal_handler(&error_handler); + rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* Remove procedure access */ do { @@ -5774,14 +6114,14 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, grant_proc->host.hostname : (char*)""; lex_user.host.length= grant_proc->host.hostname ? strlen(grant_proc->host.hostname) : 0; - if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user, - grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1)) + + if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user, + grant_proc->db, grant_proc->tname, + is_proc, ~(ulong)0, 1) == 0) { revoked= 1; continue; } - result= -1; // Something went wrong } counter++; } @@ -5791,10 +6131,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_unlock(&LOCK_grant); close_thread_tables(thd); - if (result) - my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); + thd->pop_internal_handler(); - DBUG_RETURN(result); + DBUG_RETURN(error_handler.has_errors()); } @@ -5961,16 +6300,16 @@ void update_schema_privilege(TABLE *table, char *buff, const char* db, int i= 2; CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); - table->field[0]->store(buff, strlen(buff), cs); + table->field[0]->store(buff, (uint) strlen(buff), cs); if (db) - table->field[i++]->store(db, strlen(db), cs); + table->field[i++]->store(db, (uint) strlen(db), cs); if (t_name) - table->field[i++]->store(t_name, strlen(t_name), cs); + table->field[i++]->store(t_name, (uint) strlen(t_name), cs); if (column) table->field[i++]->store(column, col_length, cs); table->field[i++]->store(priv, priv_length, cs); - table->field[i]->store(is_grantable, strlen(is_grantable), cs); - table->file->write_row(table->record[0]); + table->field[i]->store(is_grantable, (uint) strlen(is_grantable), cs); + table->file->ha_write_row(table->record[0]); } @@ -6283,12 +6622,6 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, /* db privileges */ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); - if (!grant_option) - { - DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); - DBUG_VOID_RETURN; - } - /* table privileges */ rw_rdlock(&LOCK_grant); if (grant->version != grant_version) |