From 613daa0d211f0d1f545a8ed295db976a33617736 Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Thu, 13 Mar 2003 23:10:50 +0200 Subject: [PATCH 1/5] ha_innobase.cc: Fix bug: MySQL could erroneously return Empty set if InnoDB estimated index range size to 0 records though the range was not empty; MySQL also failed to do the next-key locking in the case of an empty index range --- sql/ha_innobase.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sql/ha_innobase.cc b/sql/ha_innobase.cc index 5b2af70f34e..c0aea197b1f 100644 --- a/sql/ha_innobase.cc +++ b/sql/ha_innobase.cc @@ -3098,6 +3098,16 @@ ha_innobase::records_in_range( my_free((char*) key_val_buff2, MYF(0)); + /* The MySQL optimizer seems to believe an estimate of 0 rows is + always accurate and may return the result 'Empty set' based on that. + The accuracy is not guaranteed, and even if it were, for a locking + read we should anyway perform the search to set the next-key lock. + Add 1 to the value to make sure MySQL does not make the assumption! */ + + if (n_rows == 0) { + n_rows = 1; + } + DBUG_RETURN((ha_rows) n_rows); } From 5ee1dbbe583e3269eddc6a5c16eb1bd0a8cc6a07 Mon Sep 17 00:00:00 2001 From: "monty@mashka.mysql.fi" <> Date: Fri, 14 Mar 2003 17:08:42 +0200 Subject: [PATCH 2/5] Check for empty table/column names --- mysql-test/t/create.test | 8 ++++++++ sql/sql_parse.cc | 3 +-- sql/sql_table.cc | 12 +++++------- sql/table.cc | 7 ++++++- tests/grant.pl | 10 ++++++++++ tests/grant.res | 5 +++++ 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 1a829eec6a3..b11c07e5b8d 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -59,6 +59,14 @@ create table test_$1.test2$ (a int); drop table test_$1.test2$; drop database test_$1; +--error 1103 +create table `` (a int); +--error 1103 +drop table if exists ``; +--error 1166 +create table t1 (`` int); +drop table if exists t1; + # # Test of CREATE ... SELECT with indexes # diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8daba09174e..62c2f18c882 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2758,8 +2758,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, if (!table) DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; - if (table->table.length > NAME_LEN || - check_table_name(table->table.str,table->table.length) || + if (check_table_name(table->table.str,table->table.length) || table->db.str && check_db_name(table->db.str)) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,table->table.str); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2ff7c9c1a75..f273821c5e0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -244,6 +244,11 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; + if (check_column_name(sql_field->field_name)) + { + my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name); + DBUG_RETURN(-1); + } while ((dup_field=it2++) != sql_field) { if (my_strcasecmp(sql_field->field_name, dup_field->field_name) == 0) @@ -688,13 +693,6 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, while ((item=it++)) { create_field *cr_field; - if (strlen(item->name) > NAME_LEN || - check_column_name(item->name)) - { - my_error(ER_WRONG_COLUMN_NAME,MYF(0),item->name); - DBUG_RETURN(0); - } - Field *field=create_tmp_field(&tmp_table,item,item->type(), (Item_result_field***) 0, &tmp_field,0,0); if (!field || diff --git a/sql/table.cc b/sql/table.cc index 3afadec3801..5503dddf085 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1099,6 +1099,8 @@ bool check_db_name(char *name) bool check_table_name(const char *name, uint length) { const char *end= name+length; + if (!length || length > NAME_LEN) + return 1; while (name != end) { @@ -1122,6 +1124,8 @@ bool check_table_name(const char *name, uint length) bool check_column_name(const char *name) { + const char *start= name; + while (*name) { #if defined(USE_MB) && defined(USE_MB_IDENT) @@ -1139,7 +1143,8 @@ bool check_column_name(const char *name) return 1; name++; } - return 0; + /* Error if empty or too long column name */ + return (name == start || (uint) (name - start) > NAME_LEN); } /* diff --git a/tests/grant.pl b/tests/grant.pl index c41b22157bd..bf67ce5e790 100644 --- a/tests/grant.pl +++ b/tests/grant.pl @@ -207,6 +207,16 @@ user_query("delete from $opt_database.test where a=1",1); user_query("update $opt_database.test set b=3 where b=1",1); user_query("update $opt_database.test set b=b+1",1); +# +# Test global SELECT privilege combined with table level privileges +# + +safe_query("grant SELECT on *.* to $user"); +user_connect(0); +user_query("update $opt_database.test set b=b+1"); +safe_query("revoke SELECT on *.* from $user"); +user_connect(0); + # Add one privilege at a time until the user has all privileges user_query("select * from test",1); safe_query("grant select on $opt_database.test to $user"); diff --git a/tests/grant.res b/tests/grant.res index 1c74e5b1d1b..44e20db555f 100644 --- a/tests/grant.res +++ b/tests/grant.res @@ -192,6 +192,11 @@ update grant_test.test set b=3 where b=1 Error in execute: select command denied to user: 'grant_user@localhost' for column 'b' in table 'test' update grant_test.test set b=b+1 Error in execute: select command denied to user: 'grant_user@localhost' for column 'b' in table 'test' +grant SELECT on *.* to grant_user@localhost +Connecting grant_user +update grant_test.test set b=b+1 +revoke SELECT on *.* from grant_user@localhost +Connecting grant_user select * from test Error in execute: select command denied to user: 'grant_user@localhost' for table 'test' grant select on grant_test.test to grant_user@localhost From 369da47836dc6da2c187697aa6b99edf8c4e7310 Mon Sep 17 00:00:00 2001 From: "monty@mashka.mysql.fi" <> Date: Fri, 14 Mar 2003 23:06:55 +0200 Subject: [PATCH 3/5] Code cleanup --- sql/filesort.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sql/filesort.cc b/sql/filesort.cc index 6ea97eb219a..029d9b1212f 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -95,7 +95,7 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, ha_rows *examined_rows) { int error; - uint memavl,old_memavl,maxbuffer,skr; + uint memavl,old_memavl,min_sort_memory,maxbuffer,skr; BUFFPEK *buffpek; ha_rows records; uchar **sort_keys; @@ -162,7 +162,8 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, #endif memavl=sortbuff_size; - while (memavl >= MIN_SORT_MEMORY) + min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2); + while (memavl >= min_sort_memory) { if ((ulonglong) (records+1)*(param.sort_length+sizeof(char*))+sizeof(BUFFPEK)*10 < (ulonglong) memavl) @@ -192,18 +193,13 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, else my_free((gptr) sort_keys,MYF(0)); old_memavl=memavl; - if ((memavl=memavl/4*3) < MIN_SORT_MEMORY && old_memavl > MIN_SORT_MEMORY) - memavl=MIN_SORT_MEMORY; + if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory) + memavl=min_sort_memory; } param.keys--; maxbuffer+=10; /* Some extra range */ - if (memavl < param.sort_length*MERGEBUFF2) - { - my_error(ER_OUT_OF_SORTMEMORY,MYF(0)); - goto err; - } - if (memavl < MIN_SORT_MEMORY) + if (memavl < min_sort_memory) { my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),sortbuff_size); goto err; From a6482b1304e8a5f1cde9465fd9bf187376ddceea Mon Sep 17 00:00:00 2001 From: "monty@mashka.mysql.fi" <> Date: Sat, 15 Mar 2003 04:34:06 +0200 Subject: [PATCH 4/5] Code cleanup --- sql/sql_parse.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 24643f18ac7..8fe5922baf0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1521,11 +1521,6 @@ mysql_execute_command(void) if (error) goto error; } - if (strlen(tables->real_name) > NAME_LEN) - { - net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->real_name); - break; - } LOCK_ACTIVE_MI; // fetch_master_table will send the error to the client on failure if (!fetch_master_table(thd, tables->db, tables->real_name, From b879a40120c2b39c32f2312acc7cf286d030bd4d Mon Sep 17 00:00:00 2001 From: "monty@mashka.mysql.fi" <> Date: Sun, 16 Mar 2003 16:28:30 +0200 Subject: [PATCH 5/5] Write binlog before commit when doing INSERT ... SELECT --- mysql-test/r/create.result | 7 +++++++ sql/sql_insert.cc | 14 ++++++++------ sql/sql_update.cc | 13 ++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index c3083dbfb03..0cc98c38d49 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -58,6 +58,13 @@ a$1 $b c$ create table test_$1.test2$ (a int); drop table test_$1.test2$; drop database test_$1; +create table `` (a int); +Incorrect table name '' +drop table if exists ``; +Incorrect table name '' +create table t1 (`` int); +Incorrect column name '' +drop table if exists t1; create table t1 (a int auto_increment not null primary key, B CHAR(20)); insert into t1 (b) values ("hello"),("my"),("world"); create table t2 (key (b)) select * from t1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9f1a0e93cb9..ace15771449 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1361,6 +1361,14 @@ bool select_insert::send_eof() if (!(error=table->file->extra(HA_EXTRA_NO_CACHE))) error=table->file->activate_all_index(thd); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + + /* Write to binlog before commiting transaction */ + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + table->file->has_transactions()); + mysql_bin_log.write(&qinfo); + } if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) error=error2; if (info.copied || info.deleted) @@ -1386,12 +1394,6 @@ bool select_insert::send_eof() thd->insert_id(last_insert_id); // For update log ::send_ok(&thd->net,info.copied,last_insert_id,buff); mysql_update_log.write(thd,thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, - table->file->has_transactions()); - mysql_bin_log.write(&qinfo); - } return 0; } } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index adb60adb7d6..d8842855093 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -618,7 +618,18 @@ bool multi_update::send_data(List ¬_used_values) for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) { TABLE *table= cur_table->table; - /* Check if we are using outer join and we didn't find the row */ + /* + Check if we are using outer join and we didn't find the row + or if we have already updated this row in the previous call to this + function. + + The same row may be presented here several times in a join of type + UPDATE t1 FROM t1,t2 SET t1.a=t2.a + + In this case we will do the update for the first found row combination. + The join algorithm guarantees that we will not find the a row in + t1 several times. + */ if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue;