diff options
author | Alexey Kopytov <Alexey.Kopytov@Sun.com> | 2010-05-11 12:27:53 +0400 |
---|---|---|
committer | Alexey Kopytov <Alexey.Kopytov@Sun.com> | 2010-05-11 12:27:53 +0400 |
commit | d9a5541a34f273c390f088504dc41daf6cdfaa0a (patch) | |
tree | a082c9a70425283b10f95421a2d3c2f680aa939c | |
parent | cb421d43cdc0eaa5f342ac3c75c1a4c4a4156f6c (diff) | |
parent | 6d0425b18df7468036a1bbf6d781d624a28e2fb3 (diff) | |
download | mariadb-git-d9a5541a34f273c390f088504dc41daf6cdfaa0a.tar.gz |
Manual merge from mysql-5.1-bugteam to mysql-trunk-merge.
Conflicts:
Text conflict in tests/mysql_client_test.c
-rw-r--r-- | mysql-test/r/range.result | 44 | ||||
-rw-r--r-- | mysql-test/t/range.test | 41 | ||||
-rw-r--r-- | sql-common/client.c | 27 | ||||
-rw-r--r-- | sql/opt_range.cc | 93 | ||||
-rw-r--r-- | sql/opt_range.h | 83 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 82 |
6 files changed, 301 insertions, 69 deletions
diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index aa04bfc25ea..4587a60ca0d 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -1653,4 +1653,48 @@ a b 0 0 1 1 DROP TABLE t1; +# +# Bug#50939: Loose Index Scan unduly relies on engine to remember range +# endpoints +# +CREATE TABLE t1 ( +a INT, +b INT, +KEY ( a, b ) +) PARTITION BY HASH (a) PARTITIONS 1; +CREATE TABLE t2 ( +a INT, +b INT, +KEY ( a, b ) +); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5); +INSERT INTO t1 SELECT a + 5, b + 5 FROM t1; +INSERT INTO t1 SELECT a + 10, b + 10 FROM t1; +INSERT INTO t1 SELECT a + 20, b + 20 FROM t1; +INSERT INTO t1 SELECT a + 40, b + 40 FROM t1; +INSERT INTO t2 SELECT * FROM t1; +# plans should be identical +EXPLAIN SELECT a, MAX(b) FROM t1 WHERE a IN (10,100) GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 1 Using where; Using index for group-by +EXPLAIN SELECT a, MAX(b) FROM t2 WHERE a IN (10,100) GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range a a 5 NULL 2 Using where; Using index for group-by +FLUSH status; +SELECT a, MAX(b) FROM t1 WHERE a IN (10, 100) GROUP BY a; +a MAX(b) +10 10 +# Should be no more than 4 reads. +SHOW status LIKE 'handler_read_key'; +Variable_name Value +Handler_read_key 4 +FLUSH status; +SELECT a, MAX(b) FROM t2 WHERE a IN (10, 100) GROUP BY a; +a MAX(b) +10 10 +# Should be no more than 4 reads. +SHOW status LIKE 'handler_read_key'; +Variable_name Value +Handler_read_key 4 +DROP TABLE t1, t2; End of 5.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 5d5ad180f1a..2d2a6f75d73 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -1313,4 +1313,45 @@ SELECT * FROM t1 FORCE INDEX (PRIMARY) DROP TABLE t1; +--echo # +--echo # Bug#50939: Loose Index Scan unduly relies on engine to remember range +--echo # endpoints +--echo # +CREATE TABLE t1 ( + a INT, + b INT, + KEY ( a, b ) +) PARTITION BY HASH (a) PARTITIONS 1; + +CREATE TABLE t2 ( + a INT, + b INT, + KEY ( a, b ) +); + +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5); + +INSERT INTO t1 SELECT a + 5, b + 5 FROM t1; +INSERT INTO t1 SELECT a + 10, b + 10 FROM t1; +INSERT INTO t1 SELECT a + 20, b + 20 FROM t1; +INSERT INTO t1 SELECT a + 40, b + 40 FROM t1; + +INSERT INTO t2 SELECT * FROM t1; + +--echo # plans should be identical +EXPLAIN SELECT a, MAX(b) FROM t1 WHERE a IN (10,100) GROUP BY a; +EXPLAIN SELECT a, MAX(b) FROM t2 WHERE a IN (10,100) GROUP BY a; + +FLUSH status; +SELECT a, MAX(b) FROM t1 WHERE a IN (10, 100) GROUP BY a; +--echo # Should be no more than 4 reads. +SHOW status LIKE 'handler_read_key'; + +FLUSH status; +SELECT a, MAX(b) FROM t2 WHERE a IN (10, 100) GROUP BY a; +--echo # Should be no more than 4 reads. +SHOW status LIKE 'handler_read_key'; + +DROP TABLE t1, t2; + --echo End of 5.1 tests diff --git a/sql-common/client.c b/sql-common/client.c index 173d8397bd9..bb7bd4d2633 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2884,6 +2884,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, goto error; } + /* + Using init_commands is not supported when connecting from within the + server. + */ +#ifndef MYSQL_SERVER if (mysql->options.init_commands) { DYNAMIC_ARRAY *init_commands= mysql->options.init_commands; @@ -2895,18 +2900,26 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, for (; ptr < end_command; ptr++) { - MYSQL_RES *res; + int status; + if (mysql_real_query(mysql,*ptr, (ulong) strlen(*ptr))) goto error; - if (mysql->fields) - { - if (!(res= cli_use_result(mysql))) - goto error; - mysql_free_result(res); - } + + do { + if (mysql->fields) + { + MYSQL_RES *res; + if (!(res= cli_use_result(mysql))) + goto error; + mysql_free_result(res); + } + if ((status= mysql_next_result(mysql)) > 0) + goto error; + } while (status == 0); } mysql->reconnect=reconnect; } +#endif DBUG_PRINT("exit", ("Mysql handler: 0x%lx", (long) mysql)); reset_sigpipe(mysql); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index a46bfcf2d0e..5e985625c78 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8717,8 +8717,6 @@ int QUICK_RANGE_SELECT::get_next() { int result; KEY_MULTI_RANGE *mrange; - key_range *start_key; - key_range *end_key; DBUG_ENTER("QUICK_RANGE_SELECT::get_next"); DBUG_ASSERT(multi_range_length && multi_range && (cur_range >= (QUICK_RANGE**) ranges.buffer) && @@ -8758,26 +8756,9 @@ int QUICK_RANGE_SELECT::get_next() mrange_slot < mrange_end; mrange_slot++) { - start_key= &mrange_slot->start_key; - end_key= &mrange_slot->end_key; last_range= *(cur_range++); - - start_key->key= (const uchar*) last_range->min_key; - start_key->length= last_range->min_length; - start_key->flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (last_range->flag & EQ_RANGE) ? - HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - start_key->keypart_map= last_range->min_keypart_map; - end_key->key= (const uchar*) last_range->max_key; - end_key->length= last_range->max_length; - /* - We use HA_READ_AFTER_KEY here because if we are reading on a key - prefix. We want to find all keys with this prefix. - */ - end_key->flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : - HA_READ_AFTER_KEY); - end_key->keypart_map= last_range->max_keypart_map; - + last_range->make_min_endpoint(&mrange_slot->start_key); + last_range->make_max_endpoint(&mrange_slot->end_key); mrange_slot->range_flag= last_range->flag; } @@ -8801,49 +8782,52 @@ end: /* Get the next record with a different prefix. - SYNOPSIS - QUICK_RANGE_SELECT::get_next_prefix() - prefix_length length of cur_prefix - cur_prefix prefix of a key to be searched for + @param prefix_length length of cur_prefix + @param group_key_parts The number of key parts in the group prefix + @param cur_prefix prefix of a key to be searched for - DESCRIPTION - Each subsequent call to the method retrieves the first record that has a - prefix with length prefix_length different from cur_prefix, such that the - record with the new prefix is within the ranges described by - this->ranges. The record found is stored into the buffer pointed by - this->record. - The method is useful for GROUP-BY queries with range conditions to - discover the prefix of the next group that satisfies the range conditions. + Each subsequent call to the method retrieves the first record that has a + prefix with length prefix_length and which is different from cur_prefix, + such that the record with the new prefix is within the ranges described by + this->ranges. The record found is stored into the buffer pointed by + this->record. The method is useful for GROUP-BY queries with range + conditions to discover the prefix of the next group that satisfies the range + conditions. + + @todo - TODO This method is a modified copy of QUICK_RANGE_SELECT::get_next(), so both methods should be unified into a more general one to reduce code duplication. - RETURN - 0 on success - HA_ERR_END_OF_FILE if returned all keys - other if some error occurred + @retval 0 on success + @retval HA_ERR_END_OF_FILE if returned all keys + @retval other if some error occurred */ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, - key_part_map keypart_map, + uint group_key_parts, uchar *cur_prefix) { DBUG_ENTER("QUICK_RANGE_SELECT::get_next_prefix"); + const key_part_map keypart_map= make_prev_keypart_map(group_key_parts); for (;;) { int result; - key_range start_key, end_key; if (last_range) { /* Read the next record in the same range with prefix after cur_prefix. */ - DBUG_ASSERT(cur_prefix != 0); + DBUG_ASSERT(cur_prefix != NULL); result= file->index_read_map(record, cur_prefix, keypart_map, HA_READ_AFTER_KEY); - if (result || (file->compare_key(file->end_range) <= 0)) + if (result || last_range->max_keypart_map == 0) DBUG_RETURN(result); + + key_range previous_endpoint; + last_range->make_max_endpoint(&previous_endpoint, prefix_length, keypart_map); + if (file->compare_key(&previous_endpoint) <= 0) + DBUG_RETURN(0); } uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer); @@ -8855,21 +8839,9 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, } last_range= *(cur_range++); - start_key.key= (const uchar*) last_range->min_key; - start_key.length= min(last_range->min_length, prefix_length); - start_key.keypart_map= last_range->min_keypart_map & keypart_map; - start_key.flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (last_range->flag & EQ_RANGE) ? - HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - end_key.key= (const uchar*) last_range->max_key; - end_key.length= min(last_range->max_length, prefix_length); - end_key.keypart_map= last_range->max_keypart_map & keypart_map; - /* - We use READ_AFTER_KEY here because if we are reading on a key - prefix we want to find all keys with this prefix - */ - end_key.flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : - HA_READ_AFTER_KEY); + key_range start_key, end_key; + last_range->make_min_endpoint(&start_key, prefix_length, keypart_map); + last_range->make_max_endpoint(&end_key, prefix_length, keypart_map); result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0, last_range->max_keypart_map ? &end_key : 0, @@ -8964,9 +8936,9 @@ bool QUICK_RANGE_SELECT::row_in_ranges() } /* - This is a hack: we inherit from QUICK_SELECT so that we can use the + This is a hack: we inherit from QUICK_RANGE_SELECT so that we can use the get_next() interface, but we have to hold a pointer to the original - QUICK_SELECT because its data are used all over the place. What + QUICK_RANGE_SELECT because its data are used all over the place. What should be done is to factor out the data that is needed into a base class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC) which handle the ranges and implement the get_next() function. But @@ -11159,7 +11131,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() { uchar *cur_prefix= seen_first_key ? group_prefix : NULL; if ((result= quick_prefix_select->get_next_prefix(group_prefix_len, - make_prev_keypart_map(group_key_parts), cur_prefix))) + group_key_parts, + cur_prefix))) DBUG_RETURN(result); seen_first_key= TRUE; } diff --git a/sql/opt_range.h b/sql/opt_range.h index 3b3b36f7689..85d59671b42 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -79,6 +79,85 @@ class QUICK_RANGE :public Sql_alloc { dummy=0; #endif } + + /** + Initalizes a key_range object for communication with storage engine. + + This function facilitates communication with the Storage Engine API by + translating the minimum endpoint of the interval represented by this + QUICK_RANGE into an index range endpoint specifier for the engine. + + @param Pointer to an uninitialized key_range C struct. + + @param prefix_length The length of the search key prefix to be used for + lookup. + + @param keypart_map A set (bitmap) of keyparts to be used. + */ + void make_min_endpoint(key_range *kr, uint prefix_length, + key_part_map keypart_map) { + make_min_endpoint(kr); + kr->length= min(kr->length, prefix_length); + kr->keypart_map&= keypart_map; + } + + /** + Initalizes a key_range object for communication with storage engine. + + This function facilitates communication with the Storage Engine API by + translating the minimum endpoint of the interval represented by this + QUICK_RANGE into an index range endpoint specifier for the engine. + + @param Pointer to an uninitialized key_range C struct. + */ + void make_min_endpoint(key_range *kr) { + kr->key= (const uchar*)min_key; + kr->length= min_length; + kr->keypart_map= min_keypart_map; + kr->flag= ((flag & NEAR_MIN) ? HA_READ_AFTER_KEY : + (flag & EQ_RANGE) ? HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); + } + + /** + Initalizes a key_range object for communication with storage engine. + + This function facilitates communication with the Storage Engine API by + translating the maximum endpoint of the interval represented by this + QUICK_RANGE into an index range endpoint specifier for the engine. + + @param Pointer to an uninitialized key_range C struct. + + @param prefix_length The length of the search key prefix to be used for + lookup. + + @param keypart_map A set (bitmap) of keyparts to be used. + */ + void make_max_endpoint(key_range *kr, uint prefix_length, + key_part_map keypart_map) { + make_max_endpoint(kr); + kr->length= min(kr->length, prefix_length); + kr->keypart_map&= keypart_map; + } + + /** + Initalizes a key_range object for communication with storage engine. + + This function facilitates communication with the Storage Engine API by + translating the maximum endpoint of the interval represented by this + QUICK_RANGE into an index range endpoint specifier for the engine. + + @param Pointer to an uninitialized key_range C struct. + */ + void make_max_endpoint(key_range *kr) { + kr->key= (const uchar*)max_key; + kr->length= max_length; + kr->keypart_map= max_keypart_map; + /* + We use READ_AFTER_KEY here because if we are reading on a key + prefix we want to find all keys with this prefix + */ + kr->flag= (flag & NEAR_MAX ? HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY); + } }; @@ -345,7 +424,7 @@ public: int reset(void); int get_next(); void range_end(); - int get_next_prefix(uint prefix_length, key_part_map keypart_map, + int get_next_prefix(uint prefix_length, uint group_key_parts, uchar *cur_prefix); bool reverse_sorted() { return 0; } bool unique_key_range(); @@ -625,7 +704,7 @@ private: uchar *record; /* Buffer where the next record is returned. */ uchar *tmp_record; /* Temporary storage for next_min(), next_max(). */ uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */ - uint group_prefix_len; /* Length of the group prefix. */ + const uint group_prefix_len; /* Length of the group prefix. */ uint group_key_parts; /* A number of keyparts in the group prefix */ uchar *last_prefix; /* Prefix of the last group for detecting EOF. */ bool have_min; /* Specify whether we are computing */ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 82f5af19e14..b518b617535 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18979,6 +18979,87 @@ static void test_bug53371() } + +/** + Bug#42373: libmysql can mess a connection at connect +*/ +static void test_bug42373() +{ + int rc; + MYSQL con; + MYSQL_STMT *stmt; + + DBUG_ENTER("test_bug42373"); + myheader("test_42373"); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE PROCEDURE p1()" + " BEGIN" + " SELECT 1;" + " INSERT INTO t1 VALUES (2);" + "END;"); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)"); + myquery(rc); + + /* Try with a stored procedure. */ + DIE_UNLESS(mysql_client_init(&con)); + + mysql_options(&con, MYSQL_INIT_COMMAND, "CALL p1()"); + + DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password, + current_db, opt_port, opt_unix_socket, + CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS)); + + stmt= mysql_simple_prepare(&con, "SELECT a FROM t1"); + check_stmt(stmt); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= my_process_stmt_result(stmt); + DIE_UNLESS(rc == 1); + + mysql_stmt_close(stmt); + + /* Now try with a multi-statement. */ + DIE_UNLESS(mysql_client_init(&con)); + + mysql_options(&con, MYSQL_INIT_COMMAND, + "SELECT 3; INSERT INTO t1 VALUES (4)"); + + DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password, + current_db, opt_port, opt_unix_socket, + CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS)); + + stmt= mysql_simple_prepare(&con, "SELECT a FROM t1"); + check_stmt(stmt); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= my_process_stmt_result(stmt); + DIE_UNLESS(rc == 2); + + mysql_stmt_close(stmt); + mysql_close(&con); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + + /* Bug#49972: Crash in prepared statements. @@ -19419,6 +19500,7 @@ static struct my_tests_st my_tests[]= { { "test_bug41078", test_bug41078 }, { "test_bug44495", test_bug44495 }, { "test_bug49972", test_bug49972 }, + { "test_bug42373", test_bug42373 }, { 0, 0 } }; |