Commit db9672cf authored by Alexey Kopytov's avatar Alexey Kopytov

Manual merge from mysql-5.1-bugteam to mysql-trunk-merge.

Conflicts:

Text conflict in tests/mysql_client_test.c
parents d83cb8c7 1c5200f6
...@@ -1653,4 +1653,48 @@ a b ...@@ -1653,4 +1653,48 @@ a b
0 0 0 0
1 1 1 1
DROP TABLE t1; 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 End of 5.1 tests
...@@ -1313,4 +1313,45 @@ SELECT * FROM t1 FORCE INDEX (PRIMARY) ...@@ -1313,4 +1313,45 @@ SELECT * FROM t1 FORCE INDEX (PRIMARY)
DROP TABLE t1; 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 --echo End of 5.1 tests
...@@ -2884,6 +2884,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ...@@ -2884,6 +2884,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
goto error; goto error;
} }
/*
Using init_commands is not supported when connecting from within the
server.
*/
#ifndef MYSQL_SERVER
if (mysql->options.init_commands) if (mysql->options.init_commands)
{ {
DYNAMIC_ARRAY *init_commands= 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, ...@@ -2895,18 +2900,26 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
for (; ptr < end_command; ptr++) for (; ptr < end_command; ptr++)
{ {
MYSQL_RES *res; int status;
if (mysql_real_query(mysql,*ptr, (ulong) strlen(*ptr))) if (mysql_real_query(mysql,*ptr, (ulong) strlen(*ptr)))
goto error; goto error;
do {
if (mysql->fields) if (mysql->fields)
{ {
MYSQL_RES *res;
if (!(res= cli_use_result(mysql))) if (!(res= cli_use_result(mysql)))
goto error; goto error;
mysql_free_result(res); mysql_free_result(res);
} }
if ((status= mysql_next_result(mysql)) > 0)
goto error;
} while (status == 0);
} }
mysql->reconnect=reconnect; mysql->reconnect=reconnect;
} }
#endif
DBUG_PRINT("exit", ("Mysql handler: 0x%lx", (long) mysql)); DBUG_PRINT("exit", ("Mysql handler: 0x%lx", (long) mysql));
reset_sigpipe(mysql); reset_sigpipe(mysql);
......
...@@ -8717,8 +8717,6 @@ int QUICK_RANGE_SELECT::get_next() ...@@ -8717,8 +8717,6 @@ int QUICK_RANGE_SELECT::get_next()
{ {
int result; int result;
KEY_MULTI_RANGE *mrange; KEY_MULTI_RANGE *mrange;
key_range *start_key;
key_range *end_key;
DBUG_ENTER("QUICK_RANGE_SELECT::get_next"); DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
DBUG_ASSERT(multi_range_length && multi_range && DBUG_ASSERT(multi_range_length && multi_range &&
(cur_range >= (QUICK_RANGE**) ranges.buffer) && (cur_range >= (QUICK_RANGE**) ranges.buffer) &&
...@@ -8758,26 +8756,9 @@ int QUICK_RANGE_SELECT::get_next() ...@@ -8758,26 +8756,9 @@ int QUICK_RANGE_SELECT::get_next()
mrange_slot < mrange_end; mrange_slot < mrange_end;
mrange_slot++) mrange_slot++)
{ {
start_key= &mrange_slot->start_key;
end_key= &mrange_slot->end_key;
last_range= *(cur_range++); last_range= *(cur_range++);
last_range->make_min_endpoint(&mrange_slot->start_key);
start_key->key= (const uchar*) last_range->min_key; last_range->make_max_endpoint(&mrange_slot->end_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;
mrange_slot->range_flag= last_range->flag; mrange_slot->range_flag= last_range->flag;
} }
...@@ -8801,49 +8782,52 @@ int QUICK_RANGE_SELECT::get_next() ...@@ -8801,49 +8782,52 @@ int QUICK_RANGE_SELECT::get_next()
/* /*
Get the next record with a different prefix. Get the next record with a different prefix.
SYNOPSIS @param prefix_length length of cur_prefix
QUICK_RANGE_SELECT::get_next_prefix() @param group_key_parts The number of key parts in the group prefix
prefix_length length of cur_prefix @param cur_prefix prefix of a key to be searched for
cur_prefix prefix of a key to be searched for
DESCRIPTION
Each subsequent call to the method retrieves the first record that has a 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 prefix with length prefix_length and which is different from cur_prefix,
record with the new prefix is within the ranges described by 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->ranges. The record found is stored into the buffer pointed by
this->record. this->record. The method is useful for GROUP-BY queries with range
The method is useful for GROUP-BY queries with range conditions to conditions to discover the prefix of the next group that satisfies the range
discover the prefix of the next group that satisfies the range conditions. conditions.
@todo
TODO
This method is a modified copy of QUICK_RANGE_SELECT::get_next(), so both 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 methods should be unified into a more general one to reduce code
duplication. duplication.
RETURN @retval 0 on success
0 on success @retval HA_ERR_END_OF_FILE if returned all keys
HA_ERR_END_OF_FILE if returned all keys @retval other if some error occurred
other if some error occurred
*/ */
int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length,
key_part_map keypart_map, uint group_key_parts,
uchar *cur_prefix) uchar *cur_prefix)
{ {
DBUG_ENTER("QUICK_RANGE_SELECT::get_next_prefix"); DBUG_ENTER("QUICK_RANGE_SELECT::get_next_prefix");
const key_part_map keypart_map= make_prev_keypart_map(group_key_parts);
for (;;) for (;;)
{ {
int result; int result;
key_range start_key, end_key;
if (last_range) if (last_range)
{ {
/* Read the next record in the same range with prefix after cur_prefix. */ /* 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, result= file->index_read_map(record, cur_prefix, keypart_map,
HA_READ_AFTER_KEY); HA_READ_AFTER_KEY);
if (result || (file->compare_key(file->end_range) <= 0)) if (result || last_range->max_keypart_map == 0)
DBUG_RETURN(result); 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); uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer);
...@@ -8855,21 +8839,9 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, ...@@ -8855,21 +8839,9 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length,
} }
last_range= *(cur_range++); last_range= *(cur_range++);
start_key.key= (const uchar*) last_range->min_key; key_range start_key, end_key;
start_key.length= min(last_range->min_length, prefix_length); last_range->make_min_endpoint(&start_key, prefix_length, keypart_map);
start_key.keypart_map= last_range->min_keypart_map & keypart_map; last_range->make_max_endpoint(&end_key, prefix_length, 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);
result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0, result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0,
last_range->max_keypart_map ? &end_key : 0, last_range->max_keypart_map ? &end_key : 0,
...@@ -8964,9 +8936,9 @@ bool QUICK_RANGE_SELECT::row_in_ranges() ...@@ -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 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 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) class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC)
which handle the ranges and implement the get_next() function. But which handle the ranges and implement the get_next() function. But
...@@ -11159,7 +11131,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() ...@@ -11159,7 +11131,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
{ {
uchar *cur_prefix= seen_first_key ? group_prefix : NULL; uchar *cur_prefix= seen_first_key ? group_prefix : NULL;
if ((result= quick_prefix_select->get_next_prefix(group_prefix_len, 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); DBUG_RETURN(result);
seen_first_key= TRUE; seen_first_key= TRUE;
} }
......
...@@ -79,6 +79,85 @@ class QUICK_RANGE :public Sql_alloc { ...@@ -79,6 +79,85 @@ class QUICK_RANGE :public Sql_alloc {
dummy=0; dummy=0;
#endif #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 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I ...@@ -345,7 +424,7 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I
int reset(void); int reset(void);
int get_next(); int get_next();
void range_end(); 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); uchar *cur_prefix);
bool reverse_sorted() { return 0; } bool reverse_sorted() { return 0; }
bool unique_key_range(); bool unique_key_range();
...@@ -625,7 +704,7 @@ class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I ...@@ -625,7 +704,7 @@ class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I
uchar *record; /* Buffer where the next record is returned. */ uchar *record; /* Buffer where the next record is returned. */
uchar *tmp_record; /* Temporary storage for next_min(), next_max(). */ uchar *tmp_record; /* Temporary storage for next_min(), next_max(). */
uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */ 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 */ uint group_key_parts; /* A number of keyparts in the group prefix */
uchar *last_prefix; /* Prefix of the last group for detecting EOF. */ uchar *last_prefix; /* Prefix of the last group for detecting EOF. */
bool have_min; /* Specify whether we are computing */ bool have_min; /* Specify whether we are computing */
......
...@@ -18979,6 +18979,87 @@ static void test_bug53371() ...@@ -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. Bug#49972: Crash in prepared statements.
...@@ -19419,6 +19500,7 @@ static struct my_tests_st my_tests[]= { ...@@ -19419,6 +19500,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug41078", test_bug41078 }, { "test_bug41078", test_bug41078 },
{ "test_bug44495", test_bug44495 }, { "test_bug44495", test_bug44495 },
{ "test_bug49972", test_bug49972 }, { "test_bug49972", test_bug49972 },
{ "test_bug42373", test_bug42373 },
{ 0, 0 } { 0, 0 }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment