Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Kirill Smelkov
mariadb
Commits
de31a724
Commit
de31a724
authored
14 years ago
by
Alexey Kopytov
Browse files
Options
Download
Plain Diff
Manual merge from mysql-5.1-bugteam to mysql-trunk-merge.
Conflicts: Text conflict in tests/mysql_client_test.c
parents
d8b458ae
6ed697bb
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
301 additions
and
69 deletions
+301
-69
mysql-test/r/range.result
mysql-test/r/range.result
+44
-0
mysql-test/t/range.test
mysql-test/t/range.test
+41
-0
sql-common/client.c
sql-common/client.c
+20
-7
sql/opt_range.cc
sql/opt_range.cc
+33
-60
sql/opt_range.h
sql/opt_range.h
+81
-2
tests/mysql_client_test.c
tests/mysql_client_test.c
+82
-0
No files found.
mysql-test/r/range.result
View file @
de31a724
...
...
@@ -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
This diff is collapsed.
Click to expand it.
mysql-test/t/range.test
View file @
de31a724
...
...
@@ -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
This diff is collapsed.
Click to expand it.
sql-common/client.c
View file @
de31a724
...
...
@@ -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
);
...
...
This diff is collapsed.
Click to expand it.
sql/opt_range.cc
View file @
de31a724
...
...
@@ -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
_
part
s
,
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
;
}
...
...
This diff is collapsed.
Click to expand it.
sql/opt_range.h
View file @
de31a724
...
...
@@ -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
_
part
s
,
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 */
...
...
This diff is collapsed.
Click to expand it.
tests/mysql_client_test.c
View file @
de31a724
...
...
@@ -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 }
};
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment