From 07c7aadf444eeea8463eef2d31cfacd129620e8e Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 17 Oct 2004 13:59:46 +0400 Subject: [PATCH 1/7] Fix for bug #6081 "Call to deprecated mysql_create_db() function crashes server". Altough mysql_create_db()/mysql_drop_db() API calls are deprecated since 4.0, they should not crash server and should not stall connection in case of errors. sql/sql_parse.cc: Handling of COM_CREATE_DB, COM_DROP_DB: mysql_create_db() requires from its second parameter to be non-zero. We also should call send_error() if mysql_create_db or mysql_drop_db return error (like we do it for SQL versions of these commands). tests/client_test.c: Added test for bug #6081 "Execution of deprecated mysql_create_db() crashes server". --- sql/sql_parse.cc | 10 ++++++++-- tests/client_test.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e7a013e19ea..7b182c4cfd9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1556,6 +1556,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CREATE_DB: // QQ: To be removed { char *db=thd->strdup(packet), *alias; + HA_CREATE_INFO create_info; statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status); // null test to handle EOM @@ -1567,7 +1568,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); - mysql_create_db(thd,(lower_case_table_names == 2 ? alias : db),0,0); + bzero(&create_info, sizeof(create_info)); + if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + &create_info, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } case COM_DROP_DB: // QQ: To be removed @@ -1588,7 +1592,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } mysql_log.write(thd,command,db); - mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), 0, 0); + if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), + 0, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } #ifndef EMBEDDED_LIBRARY diff --git a/tests/client_test.c b/tests/client_test.c index 0b30cc3386d..a8d63d2ed86 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -10541,6 +10541,33 @@ static void test_bug5315() } +/* + Altough mysql_create_db(), mysql_rm_db() are deprecated since 4.0 they + should not crash server and should not hang in case of errors. + + Since those functions can't be seen in modern API (unless client library + was compiled with USE_OLD_FUNCTIONS define) we use simple_command() macro. +*/ +static void test_bug6081() +{ + int rc; + myheader("test_bug6081"); + + rc= simple_command(mysql, COM_DROP_DB, current_db, + (ulong)strlen(current_db), 0); + myquery(rc); + rc= simple_command(mysql, COM_DROP_DB, current_db, + (ulong)strlen(current_db), 0); + myquery_r(rc); + rc= simple_command(mysql, COM_CREATE_DB, current_db, + (ulong)strlen(current_db), 0); + myquery(rc); + rc= simple_command(mysql, COM_CREATE_DB, current_db, + (ulong)strlen(current_db), 0); + myquery_r(rc); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -10851,6 +10878,7 @@ int main(int argc, char **argv) test_bug5194(); /* bulk inserts in prepared mode */ test_bug5315(); /* check that mysql_change_user closes all prepared statements */ + test_bug6081(); /* test of mysql_create_db()/mysql_rm_db() */ /* XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. From 272ff4536225df845b85a2a668a0c89e461ccee6 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Oct 2004 20:25:47 -0500 Subject: [PATCH 2/7] sql_acl.cc: BUG #5831 Revoke privileges in a loop until no more privileges are revoked, because acl_dbs and column_priv_hash can re-organize during privilege removal. sql/sql_acl.cc: BUG #5831 Revoke privileges in a loop until no more privileges are revoked, because acl_dbs and column_priv_hash can re-organize during privilege removal. --- sql/sql_acl.cc | 122 +++++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index fc252c1f5ac..07647f4eb82 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3623,67 +3623,91 @@ int mysql_revoke_all(THD *thd, List &list) } /* Remove db access privileges */ - for (counter= 0 ; counter < acl_dbs.elements ; ) + /* + Because acl_dbs and column_priv_hash shrink and may re-order + as privileges are removed, removal occurs in a repeated loop + until no more privileges are revoked. + */ + while (1) { - const char *user,*host; - - acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - if (!(user=acl_db->user)) - user= ""; - if (!(host=acl_db->host.hostname)) - host= ""; - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + int revoke= 0; + for (counter= 0 ; counter < acl_dbs.elements ; ) { - if (replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) - result= -1; - else - continue; + const char *user,*host; + + acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); + if (!(user=acl_db->user)) + user= ""; + if (!(host=acl_db->host.hostname)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + if (replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) + result= -1; + else + { + revoke= 1; + continue; + } + } + ++counter; } - ++counter; + if (!revoke) + break; } /* Remove column access */ - for (counter= 0 ; counter < column_priv_hash.records ; ) + while (1) { - const char *user,*host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, - counter); - if (!(user=grant_table->user)) - user= ""; - if (!(host=grant_table->host)) - host= ""; - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + int revoke= 0; + for (counter= 0 ; counter < column_priv_hash.records ; ) { - if (replace_table_table(thd,grant_table,tables[2].table,*lex_user, - grant_table->db, - grant_table->tname, - ~0, 0, 1)) + const char *user,*host; + GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash, + counter); + if (!(user=grant_table->user)) + user= ""; + if (!(host=grant_table->host)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - result= -1; - } - else - { - if (grant_table->cols) - { - List columns; - if (replace_column_table(grant_table,tables[3].table, *lex_user, - columns, - grant_table->db, - grant_table->tname, - ~0, 1)) - result= -1; - else - continue; - } + if (replace_table_table(thd,grant_table,tables[2].table,*lex_user, + grant_table->db, + grant_table->tname, + ~0, 0, 1)) + result= -1; else - continue; + { + if (grant_table->cols) + { + List columns; + if (replace_column_table(grant_table,tables[3].table, *lex_user, + columns, + grant_table->db, + grant_table->tname, + ~0, 1)) + result= -1; + else + { + revoke= 1; + continue; + } + } + else + { + revoke= 1; + continue; + } + } } + ++counter; } - ++counter; + if (!revoke) + break; } } From 9df51cf3c39b3a83cd09e27dedd2c2c57b43f21a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 13:41:40 -0500 Subject: [PATCH 3/7] sql_acl.cc: BUG #5831 Post-review changes, merging improvements from Monty. sql/sql_acl.cc: BUG #5831 Post-review changes, merging improvements from Monty. --- sql/sql_acl.cc | 67 +++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 07647f4eb82..b44b4e9ada5 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3590,7 +3590,7 @@ int mysql_drop_user(THD *thd, List &list) int mysql_revoke_all(THD *thd, List &list) { - uint counter; + uint counter, revoked; int result; ACL_DB *acl_db; TABLE_LIST tables[4]; @@ -3628,10 +3628,9 @@ int mysql_revoke_all(THD *thd, List &list) as privileges are removed, removal occurs in a repeated loop until no more privileges are revoked. */ - while (1) + do { - int revoke= 0; - for (counter= 0 ; counter < acl_dbs.elements ; ) + for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; ) { const char *user,*host; @@ -3644,25 +3643,25 @@ int mysql_revoke_all(THD *thd, List &list) if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - if (replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) - result= -1; - else + if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) { - revoke= 1; + /* + Don't increment counter as replace_db_table deleted the + current element in acl_dbs. + */ + revoked= 1; continue; } + result= -1; // Something went wrong } - ++counter; + counter++; } - if (!revoke) - break; - } + } while (revoked); /* Remove column access */ - while (1) + do { - int revoke= 0; - for (counter= 0 ; counter < column_priv_hash.records ; ) + for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; ) { const char *user,*host; GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash, @@ -3679,36 +3678,32 @@ int mysql_revoke_all(THD *thd, List &list) grant_table->db, grant_table->tname, ~0, 0, 1)) + { result= -1; + } else { - if (grant_table->cols) + if (!grant_table->cols) { - List columns; - if (replace_column_table(grant_table,tables[3].table, *lex_user, - columns, - grant_table->db, - grant_table->tname, - ~0, 1)) - result= -1; - else - { - revoke= 1; - continue; - } - } - else - { - revoke= 1; + revoked= 1; continue; } + List columns; + if (!replace_column_table(grant_table,tables[3].table, *lex_user, + columns, + grant_table->db, + grant_table->tname, + ~0, 1)) + { + revoked= 1; + continue; + } + result= -1; } } - ++counter; + counter++; } - if (!revoke) - break; - } + } while (revoked); } VOID(pthread_mutex_unlock(&acl_cache->lock)); From 9b5cf9464c18c250f45e89a5291676571487cfe5 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 22:51:16 +0400 Subject: [PATCH 4/7] A fix and test case for Bug#6088 "FOUND_ROWS returns wrong values for prepared statements when LIMIT is used" and post-review comments. The fix changes the approach we calculate the need for ORDER BY in UNION: the previous was not PS friendly, as it damaged SELECT_LEX options in case of single select. mysql-test/r/ps.result: Test results fixed: the test case for Bug#6088 mysql-test/r/subselect.result: Test results fixed: now we don't perform ORDER BY for parts of UNION if there is no LIMIT clause. mysql-test/t/ps.test: A test case for Bug#6088 "FOUND_ROWS returns wrong values for prepared statements when LIMIT is used". sql/sql_union.cc: The actual fix for Bug#6088: - don't modify SELECT_LEX'es - use boolean variable can_skip_order_by to check if we can skip ORDER BY in UNION --- mysql-test/r/ps.result | 24 ++++++++++++++++++++++++ mysql-test/r/subselect.result | 6 +++--- mysql-test/t/ps.test | 14 ++++++++++++++ sql/sql_union.cc | 28 +++++++++++++--------------- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 0950a066e64..036d50efbba 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -410,3 +410,27 @@ a a 1.1 1.2 2.1 2.2 deallocate prepare stmt; +create table t1 (a int); +insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +prepare stmt from "select sql_calc_found_rows * from t1 limit 2"; +execute stmt; +a +1 +2 +select found_rows(); +found_rows() +10 +execute stmt; +a +1 +2 +select found_rows(); +found_rows() +10 +execute stmt; +a +1 +2 +select found_rows(); +found_rows() +10 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 8bcf7f9bff2..04c5f685f58 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -176,17 +176,17 @@ a b a b 1 7 2 7 -3 8 4 8 +3 8 explain extended (select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1)) union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using filesort -3 UNION t4 ALL NULL NULL NULL NULL 3 Using where; Using filesort +3 UNION t4 ALL NULL NULL NULL NULL 3 Using where 4 SUBQUERY t2 ALL NULL NULL NULL NULL 2 NULL UNION RESULT ALL NULL NULL NULL NULL NULL Warnings: -Note 1003 (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 where (test.t2.b = (select test.t3.a AS `a` from test.t3 order by test.t3.a desc limit 1))) union (select test.t4.a AS `a`,test.t4.b AS `b` from test.t4 where (test.t4.b = (select (max(test.t2.a) * 4) AS `max(t2.a)*4` from test.t2)) order by test.t4.a) +Note 1003 (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 where (test.t2.b = (select test.t3.a AS `a` from test.t3 order by test.t3.a desc limit 1))) union (select test.t4.a AS `a`,test.t4.b AS `b` from test.t4 where (test.t4.b = (select (max(test.t2.a) * 4) AS `max(t2.a)*4` from test.t2)) order by a) select (select a from t3 where alex->current_select; SELECT_LEX *sl, *first_select; select_result *tmp_result; + bool is_union; DBUG_ENTER("st_select_lex_unit::prepare"); describe= test(additional_options & SELECT_DESCRIBE); @@ -183,10 +184,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_select= first_select_in_union(); found_rows_for_union= first_select->options & OPTION_FOUND_ROWS; + is_union= test(first_select->next_select()); /* Global option */ - if (first_select->next_select()) + if (is_union) { if (!(tmp_result= union_result= new select_union(0))) goto err; @@ -195,14 +197,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, tmp_result= sel_result; } else - { tmp_result= sel_result; - // single select should be processed like select in p[arantses - first_select->braces= 1; - } for (;sl; sl= sl->next_select()) { + bool can_skip_order_by; sl->options|= SELECT_NO_UNLOCK; JOIN *join= new JOIN(thd_arg, sl->item_list, sl->options | thd_arg->options | additional_options, @@ -217,14 +216,17 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, select_limit_cnt= HA_POS_ERROR; // no limit if (select_limit_cnt == HA_POS_ERROR || sl->braces) sl->options&= ~OPTION_FOUND_ROWS; - + + can_skip_order_by= is_union && + (!sl->braces || select_limit_cnt == HA_POS_ERROR); + res= join->prepare(&sl->ref_pointer_array, (TABLE_LIST*) sl->table_list.first, sl->with_wild, sl->where, - ((sl->braces) ? sl->order_list.elements : 0) + - sl->group_list.elements, - (sl->braces) ? - (ORDER *)sl->order_list.first : (ORDER *) 0, + (can_skip_order_by ? 0 : sl->order_list.elements) + + sl->group_list.elements, + can_skip_order_by ? + (ORDER*) 0 : (ORDER *)sl->order_list.first, (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, @@ -264,10 +266,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } - if (first_select->next_select()) + if (is_union) { - /* This is not a single select */ - /* Check that it was possible to aggregate all collations together for UNION. @@ -364,8 +364,6 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } } - else - first_select->braces= 0; // remove our changes thd_arg->lex->current_select= lex_select_save; From 0798c99db1be457d718155b6ea0e5e52a2651087 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 23:05:48 +0400 Subject: [PATCH 5/7] Post merge fix (test results) --- mysql-test/r/ps.result | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 036d50efbba..e1391a496c6 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -411,6 +411,20 @@ a a 2.1 2.2 deallocate prepare stmt; create table t1 (a int); +insert into t1 values (1),(2),(3); +create table t2 select * from t1; +prepare stmt FROM 'create table t2 select * from t1'; +drop table t2; +execute stmt; +drop table t2; +execute stmt; +execute stmt; +ERROR 42S01: Table 't2' already exists +drop table t2; +execute stmt; +drop table t1,t2; +deallocate prepare stmt; +create table t1 (a int); insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); prepare stmt from "select sql_calc_found_rows * from t1 limit 2"; execute stmt; @@ -434,3 +448,5 @@ a select found_rows(); found_rows() 10 +deallocate prepare stmt; +drop table t1; From 0c7ac260e0624a7fc5990b3420ee7588b7cee440 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 14:32:23 -0500 Subject: [PATCH 6/7] sql_acl.cc: Correcting problem with merge sql/sql_acl.cc: Correcting problem with merge --- sql/sql_acl.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 1f7a0e3a378..ff2dfedc30b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3683,6 +3683,7 @@ int mysql_revoke_all(THD *thd, List &list) result= -1; } else + { if (!grant_table->cols) { revoked= 1; @@ -3690,10 +3691,10 @@ int mysql_revoke_all(THD *thd, List &list) } List columns; if (!replace_column_table(grant_table,tables[3].table, *lex_user, - columns, - grant_table->db, - grant_table->tname, - ~0, 1)) + columns, + grant_table->db, + grant_table->tname, + ~0, 1)) { revoked= 1; continue; @@ -3705,14 +3706,14 @@ int mysql_revoke_all(THD *thd, List &list) } } while (revoked); } - + VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); close_thread_tables(thd); - + if (result) my_error(ER_REVOKE_GRANTS, MYF(0)); - + DBUG_RETURN(result); } From 060fc681abdbec17c1a5aac109e4a02e8bcd7bda Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 22:26:57 +0200 Subject: [PATCH 7/7] - applied portability fix for Windows (1ULL -> ULL(1)) to myisampack.c myisam/myisampack.c: - applied portability fix for Windows (1ULL -> ULL(1)) --- myisam/myisampack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myisam/myisampack.c b/myisam/myisampack.c index b4b4169965d..eae75b07760 100644 --- a/myisam/myisampack.c +++ b/myisam/myisampack.c @@ -2050,7 +2050,7 @@ static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length, share->state.dellink= HA_OFFSET_ERROR; share->state.split=(ha_rows) mrg->records; share->state.version=(ulong) time((time_t*) 0); - if (share->state.key_map != (1ULL << share->base.keys) - 1) + if (share->state.key_map != (ULL(1) << share->base.keys) - 1) { /* Some indexes are disabled, cannot use current key_file_length value