summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Kopytov <Alexey.Kopytov@Sun.com>2010-05-11 12:27:53 +0400
committerAlexey Kopytov <Alexey.Kopytov@Sun.com>2010-05-11 12:27:53 +0400
commitd9a5541a34f273c390f088504dc41daf6cdfaa0a (patch)
treea082c9a70425283b10f95421a2d3c2f680aa939c
parentcb421d43cdc0eaa5f342ac3c75c1a4c4a4156f6c (diff)
parent6d0425b18df7468036a1bbf6d781d624a28e2fb3 (diff)
downloadmariadb-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.result44
-rw-r--r--mysql-test/t/range.test41
-rw-r--r--sql-common/client.c27
-rw-r--r--sql/opt_range.cc93
-rw-r--r--sql/opt_range.h83
-rw-r--r--tests/mysql_client_test.c82
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 }
};