Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
c2b9fdb0
Commit
c2b9fdb0
authored
Mar 05, 2010
by
Ramil Kalimullin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix for bug#32426: "FEDERATED query returns corrupt results
for ORDER BY on a TEXT or VARCHAR field" backported to 5.1.
parent
2e9045c8
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
172 additions
and
84 deletions
+172
-84
mysql-test/suite/federated/federated.result
mysql-test/suite/federated/federated.result
+23
-0
mysql-test/suite/federated/federated.test
mysql-test/suite/federated/federated.test
+22
-0
storage/federated/ha_federated.cc
storage/federated/ha_federated.cc
+118
-84
storage/federated/ha_federated.h
storage/federated/ha_federated.h
+9
-0
No files found.
mysql-test/suite/federated/federated.result
View file @
c2b9fdb0
...
@@ -2153,6 +2153,29 @@ DROP TABLE t1;
...
@@ -2153,6 +2153,29 @@ DROP TABLE t1;
End of 5.0 tests
End of 5.0 tests
create server 's1' foreign data wrapper 'mysql' options (port 3306);
create server 's1' foreign data wrapper 'mysql' options (port 3306);
drop server 's1';
drop server 's1';
#
# Bug #32426: FEDERATED query returns corrupt results for ORDER BY on a TEXT
#
CREATE TABLE federated.t1(a TEXT);
INSERT INTO federated.t1 VALUES('abc'), ('gh'), ('f'), ('ijk'), ('de');
CREATE TABLE federated.t1(a TEXT) ENGINE=FEDERATED
CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1';
SELECT * FROM federated.t1 ORDER BY A;
a
abc
de
f
gh
ijk
SELECT * FROM federated.t1 ORDER BY A DESC;
a
ijk
gh
f
de
abc
DROP TABLE federated.t1;
DROP TABLE federated.t1;
End of 5.1 tests
End of 5.1 tests
SET @@GLOBAL.CONCURRENT_INSERT= @OLD_MASTER_CONCURRENT_INSERT;
SET @@GLOBAL.CONCURRENT_INSERT= @OLD_MASTER_CONCURRENT_INSERT;
SET @@GLOBAL.CONCURRENT_INSERT= @OLD_SLAVE_CONCURRENT_INSERT;
SET @@GLOBAL.CONCURRENT_INSERT= @OLD_SLAVE_CONCURRENT_INSERT;
...
...
mysql-test/suite/federated/federated.test
View file @
c2b9fdb0
...
@@ -1971,6 +1971,28 @@ connection default;
...
@@ -1971,6 +1971,28 @@ connection default;
create
server
's1'
foreign
data
wrapper
'mysql'
options
(
port
3306
);
create
server
's1'
foreign
data
wrapper
'mysql'
options
(
port
3306
);
drop
server
's1'
;
drop
server
's1'
;
--
echo
#
--
echo
# Bug #32426: FEDERATED query returns corrupt results for ORDER BY on a TEXT
--
echo
#
connection
slave
;
CREATE
TABLE
federated
.
t1
(
a
TEXT
);
INSERT
INTO
federated
.
t1
VALUES
(
'abc'
),
(
'gh'
),
(
'f'
),
(
'ijk'
),
(
'de'
);
connection
master
;
--
replace_result
$SLAVE_MYPORT
SLAVE_PORT
eval
CREATE
TABLE
federated
.
t1
(
a
TEXT
)
ENGINE
=
FEDERATED
CONNECTION
=
'mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'
;
SELECT
*
FROM
federated
.
t1
ORDER
BY
A
;
SELECT
*
FROM
federated
.
t1
ORDER
BY
A
DESC
;
DROP
TABLE
federated
.
t1
;
connection
slave
;
DROP
TABLE
federated
.
t1
;
connection
default
;
--
echo
End
of
5.1
tests
--
echo
End
of
5.1
tests
SET
@@
GLOBAL
.
CONCURRENT_INSERT
=
@
OLD_MASTER_CONCURRENT_INSERT
;
SET
@@
GLOBAL
.
CONCURRENT_INSERT
=
@
OLD_MASTER_CONCURRENT_INSERT
;
connection
slave
;
connection
slave
;
...
...
storage/federated/ha_federated.cc
View file @
c2b9fdb0
...
@@ -1621,11 +1621,10 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked)
...
@@ -1621,11 +1621,10 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked)
DBUG_ASSERT
(
mysql
==
NULL
);
DBUG_ASSERT
(
mysql
==
NULL
);
ref_length
=
(
table
->
s
->
primary_key
!=
MAX_KEY
?
ref_length
=
sizeof
(
MYSQL_RES
*
)
+
sizeof
(
MYSQL_ROW_OFFSET
);
table
->
key_info
[
table
->
s
->
primary_key
].
key_length
:
table
->
s
->
reclength
);
DBUG_PRINT
(
"info"
,
(
"ref_length: %u"
,
ref_length
));
DBUG_PRINT
(
"info"
,
(
"ref_length: %u"
,
ref_length
));
my_init_dynamic_array
(
&
results
,
sizeof
(
MYSQL_RES
*
),
4
,
4
);
reset
();
reset
();
DBUG_RETURN
(
0
);
DBUG_RETURN
(
0
);
...
@@ -1645,21 +1644,17 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked)
...
@@ -1645,21 +1644,17 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked)
int
ha_federated
::
close
(
void
)
int
ha_federated
::
close
(
void
)
{
{
int
retval
;
DBUG_ENTER
(
"ha_federated::close"
);
DBUG_ENTER
(
"ha_federated::close"
);
/* free the result set */
free_result
();
if
(
stored_result
)
{
delete_dynamic
(
&
results
);
mysql_free_result
(
stored_result
);
stored_result
=
0
;
}
/* Disconnect from mysql */
/* Disconnect from mysql */
mysql_close
(
mysql
);
mysql_close
(
mysql
);
mysql
=
NULL
;
mysql
=
NULL
;
retval
=
free_share
(
share
);
DBUG_RETURN
(
retval
);
DBUG_RETURN
(
free_share
(
share
));
}
}
/*
/*
...
@@ -2326,8 +2321,7 @@ int ha_federated::index_read(uchar *buf, const uchar *key,
...
@@ -2326,8 +2321,7 @@ int ha_federated::index_read(uchar *buf, const uchar *key,
{
{
DBUG_ENTER
(
"ha_federated::index_read"
);
DBUG_ENTER
(
"ha_federated::index_read"
);
if
(
stored_result
)
free_result
();
mysql_free_result
(
stored_result
);
DBUG_RETURN
(
index_read_idx_with_result_set
(
buf
,
active_index
,
key
,
DBUG_RETURN
(
index_read_idx_with_result_set
(
buf
,
active_index
,
key
,
key_len
,
find_flag
,
key_len
,
find_flag
,
&
stored_result
));
&
stored_result
));
...
@@ -2359,7 +2353,8 @@ int ha_federated::index_read_idx(uchar *buf, uint index, const uchar *key,
...
@@ -2359,7 +2353,8 @@ int ha_federated::index_read_idx(uchar *buf, uint index, const uchar *key,
&
mysql_result
)))
&
mysql_result
)))
DBUG_RETURN
(
retval
);
DBUG_RETURN
(
retval
);
mysql_free_result
(
mysql_result
);
mysql_free_result
(
mysql_result
);
DBUG_RETURN
(
retval
);
results
.
elements
--
;
DBUG_RETURN
(
0
);
}
}
...
@@ -2415,18 +2410,20 @@ int ha_federated::index_read_idx_with_result_set(uchar *buf, uint index,
...
@@ -2415,18 +2410,20 @@ int ha_federated::index_read_idx_with_result_set(uchar *buf, uint index,
retval
=
ER_QUERY_ON_FOREIGN_DATA_SOURCE
;
retval
=
ER_QUERY_ON_FOREIGN_DATA_SOURCE
;
goto
error
;
goto
error
;
}
}
if
(
!
(
*
result
=
mysql_
store_result
(
mysql
)))
if
(
!
(
*
result
=
store_result
(
mysql
)))
{
{
retval
=
HA_ERR_END_OF_FILE
;
retval
=
HA_ERR_END_OF_FILE
;
goto
error
;
goto
error
;
}
}
if
(
!
(
retval
=
read_next
(
buf
,
*
result
)))
if
((
retval
=
read_next
(
buf
,
*
result
)))
DBUG_RETURN
(
retval
);
{
mysql_free_result
(
*
result
);
mysql_free_result
(
*
result
);
results
.
elements
--
;
*
result
=
0
;
*
result
=
0
;
table
->
status
=
STATUS_NOT_FOUND
;
table
->
status
=
STATUS_NOT_FOUND
;
DBUG_RETURN
(
retval
);
DBUG_RETURN
(
retval
);
}
DBUG_RETURN
(
0
);
error:
error:
table
->
status
=
STATUS_NOT_FOUND
;
table
->
status
=
STATUS_NOT_FOUND
;
...
@@ -2486,12 +2483,6 @@ int ha_federated::read_range_first(const key_range *start_key,
...
@@ -2486,12 +2483,6 @@ int ha_federated::read_range_first(const key_range *start_key,
create_where_from_key
(
&
sql_query
,
create_where_from_key
(
&
sql_query
,
&
table
->
key_info
[
active_index
],
&
table
->
key_info
[
active_index
],
start_key
,
end_key
,
0
,
eq_range_arg
);
start_key
,
end_key
,
0
,
eq_range_arg
);
if
(
stored_result
)
{
mysql_free_result
(
stored_result
);
stored_result
=
0
;
}
if
(
real_query
(
sql_query
.
ptr
(),
sql_query
.
length
()))
if
(
real_query
(
sql_query
.
ptr
(),
sql_query
.
length
()))
{
{
retval
=
ER_QUERY_ON_FOREIGN_DATA_SOURCE
;
retval
=
ER_QUERY_ON_FOREIGN_DATA_SOURCE
;
...
@@ -2499,14 +2490,13 @@ int ha_federated::read_range_first(const key_range *start_key,
...
@@ -2499,14 +2490,13 @@ int ha_federated::read_range_first(const key_range *start_key,
}
}
sql_query
.
length
(
0
);
sql_query
.
length
(
0
);
if
(
!
(
stored_result
=
mysql_
store_result
(
mysql
)))
if
(
!
(
stored_result
=
store_result
(
mysql
)))
{
{
retval
=
HA_ERR_END_OF_FILE
;
retval
=
HA_ERR_END_OF_FILE
;
goto
error
;
goto
error
;
}
}
retval
=
read_next
(
table
->
record
[
0
],
stored_result
);
DBUG_RETURN
(
read_next
(
table
->
record
[
0
],
stored_result
));
DBUG_RETURN
(
retval
);
error:
error:
table
->
status
=
STATUS_NOT_FOUND
;
table
->
status
=
STATUS_NOT_FOUND
;
...
@@ -2516,10 +2506,8 @@ int ha_federated::read_range_first(const key_range *start_key,
...
@@ -2516,10 +2506,8 @@ int ha_federated::read_range_first(const key_range *start_key,
int
ha_federated
::
read_range_next
()
int
ha_federated
::
read_range_next
()
{
{
int
retval
;
DBUG_ENTER
(
"ha_federated::read_range_next"
);
DBUG_ENTER
(
"ha_federated::read_range_next"
);
retval
=
rnd_next
(
table
->
record
[
0
]);
DBUG_RETURN
(
rnd_next
(
table
->
record
[
0
]));
DBUG_RETURN
(
retval
);
}
}
...
@@ -2585,23 +2573,11 @@ int ha_federated::rnd_init(bool scan)
...
@@ -2585,23 +2573,11 @@ int ha_federated::rnd_init(bool scan)
if
(
scan
)
if
(
scan
)
{
{
if
(
stored_result
)
if
(
real_query
(
share
->
select_query
,
strlen
(
share
->
select_query
))
||
{
!
(
stored_result
=
store_result
(
mysql
)))
mysql_free_result
(
stored_result
);
DBUG_RETURN
(
stash_remote_error
());
stored_result
=
0
;
}
if
(
real_query
(
share
->
select_query
,
strlen
(
share
->
select_query
)))
goto
error
;
stored_result
=
mysql_store_result
(
mysql
);
if
(
!
stored_result
)
goto
error
;
}
}
DBUG_RETURN
(
0
);
DBUG_RETURN
(
0
);
error:
DBUG_RETURN
(
stash_remote_error
());
}
}
...
@@ -2615,11 +2591,7 @@ int ha_federated::rnd_end()
...
@@ -2615,11 +2591,7 @@ int ha_federated::rnd_end()
int
ha_federated
::
index_end
(
void
)
int
ha_federated
::
index_end
(
void
)
{
{
DBUG_ENTER
(
"ha_federated::index_end"
);
DBUG_ENTER
(
"ha_federated::index_end"
);
if
(
stored_result
)
free_result
();
{
mysql_free_result
(
stored_result
);
stored_result
=
0
;
}
active_index
=
MAX_KEY
;
active_index
=
MAX_KEY
;
DBUG_RETURN
(
0
);
DBUG_RETURN
(
0
);
}
}
...
@@ -2680,6 +2652,9 @@ int ha_federated::read_next(uchar *buf, MYSQL_RES *result)
...
@@ -2680,6 +2652,9 @@ int ha_federated::read_next(uchar *buf, MYSQL_RES *result)
table
->
status
=
STATUS_NOT_FOUND
;
// For easier return
table
->
status
=
STATUS_NOT_FOUND
;
// For easier return
/* Save current data cursor position. */
current_position
=
result
->
data_cursor
;
/* Fetch a row, insert it back in a row format. */
/* Fetch a row, insert it back in a row format. */
if
(
!
(
row
=
mysql_fetch_row
(
result
)))
if
(
!
(
row
=
mysql_fetch_row
(
result
)))
DBUG_RETURN
(
HA_ERR_END_OF_FILE
);
DBUG_RETURN
(
HA_ERR_END_OF_FILE
);
...
@@ -2691,24 +2666,38 @@ int ha_federated::read_next(uchar *buf, MYSQL_RES *result)
...
@@ -2691,24 +2666,38 @@ int ha_federated::read_next(uchar *buf, MYSQL_RES *result)
}
}
/*
/**
store reference to current row so that we can later find it for
@brief Store a reference to current row.
a re-read, update or delete.
@details During a query execution we may have different result sets (RS),
In case of federated, a reference is either a primary key or
e.g. for different ranges. All the RS's used are stored in
the whole record.
memory and placed in @c results dynamic array. At the end of
execution all stored RS's are freed at once in the
Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
@c ha_federated::reset().
So, in case of federated, a reference to current row is a
stored result address and current data cursor position.
As we keep all RS in memory during a query execution,
we can get any record using the reference any time until
@c ha_federated::reset() is called.
TODO: we don't have to store all RS's rows but only those
we call @c ha_federated::position() for, so we can free memory
where we store other rows in the @c ha_federated::index_end().
@param[in] record record data (unused)
*/
*/
void
ha_federated
::
position
(
const
uchar
*
record
)
void
ha_federated
::
position
(
const
uchar
*
record
__attribute__
((
unused
))
)
{
{
DBUG_ENTER
(
"ha_federated::position"
);
DBUG_ENTER
(
"ha_federated::position"
);
if
(
table
->
s
->
primary_key
!=
MAX_KEY
)
key_copy
(
ref
,
(
uchar
*
)
record
,
table
->
key_info
+
table
->
s
->
primary_key
,
DBUG_ASSERT
(
stored_result
);
ref_length
);
else
position_called
=
TRUE
;
memcpy
(
ref
,
record
,
ref_length
);
/* Store result set address. */
memcpy_fixed
(
ref
,
&
stored_result
,
sizeof
(
MYSQL_RES
*
));
/* Store data cursor position. */
memcpy_fixed
(
ref
+
sizeof
(
MYSQL_RES
*
),
&
current_position
,
sizeof
(
MYSQL_ROW_OFFSET
));
DBUG_VOID_RETURN
;
DBUG_VOID_RETURN
;
}
}
...
@@ -2724,23 +2713,19 @@ void ha_federated::position(const uchar *record)
...
@@ -2724,23 +2713,19 @@ void ha_federated::position(const uchar *record)
int
ha_federated
::
rnd_pos
(
uchar
*
buf
,
uchar
*
pos
)
int
ha_federated
::
rnd_pos
(
uchar
*
buf
,
uchar
*
pos
)
{
{
int
result
;
MYSQL_RES
*
result
;
DBUG_ENTER
(
"ha_federated::rnd_pos"
);
DBUG_ENTER
(
"ha_federated::rnd_pos"
);
ha_statistic_increment
(
&
SSV
::
ha_read_rnd_count
);
ha_statistic_increment
(
&
SSV
::
ha_read_rnd_count
);
if
(
table
->
s
->
primary_key
!=
MAX_KEY
)
{
/* Get stored result set. */
/* We have a primary key, so use index_read_idx to find row */
memcpy_fixed
(
&
result
,
pos
,
sizeof
(
MYSQL_RES
*
));
result
=
index_read_idx
(
buf
,
table
->
s
->
primary_key
,
pos
,
DBUG_ASSERT
(
result
);
ref_length
,
HA_READ_KEY_EXACT
);
/* Set data cursor position. */
}
memcpy_fixed
(
&
result
->
data_cursor
,
pos
+
sizeof
(
MYSQL_RES
*
),
else
sizeof
(
MYSQL_ROW_OFFSET
));
{
/* Read a row. */
/* otherwise, get the old record ref as obtained in ::position */
DBUG_RETURN
(
read_next
(
buf
,
result
));
memcpy
(
buf
,
pos
,
ref_length
);
result
=
0
;
}
table
->
status
=
result
?
STATUS_NOT_FOUND
:
0
;
DBUG_RETURN
(
result
);
}
}
...
@@ -2943,6 +2928,16 @@ int ha_federated::reset(void)
...
@@ -2943,6 +2928,16 @@ int ha_federated::reset(void)
insert_dup_update
=
FALSE
;
insert_dup_update
=
FALSE
;
ignore_duplicates
=
FALSE
;
ignore_duplicates
=
FALSE
;
replace_duplicates
=
FALSE
;
replace_duplicates
=
FALSE
;
/* Free stored result sets. */
for
(
uint
i
=
0
;
i
<
results
.
elements
;
i
++
)
{
MYSQL_RES
*
result
;
get_dynamic
(
&
results
,
(
uchar
*
)
&
result
,
i
);
mysql_free_result
(
result
);
}
reset_dynamic
(
&
results
);
return
0
;
return
0
;
}
}
...
@@ -3206,6 +3201,45 @@ bool ha_federated::get_error_message(int error, String* buf)
...
@@ -3206,6 +3201,45 @@ bool ha_federated::get_error_message(int error, String* buf)
DBUG_RETURN
(
FALSE
);
DBUG_RETURN
(
FALSE
);
}
}
/**
@brief Store a result set.
@details Call @c mysql_store_result() to save a result set then
append it to the stored results array.
@param[in] mysql MySLQ connection structure.
@return Stored result set (MYSQL_RES object).
*/
MYSQL_RES
*
ha_federated
::
store_result
(
MYSQL
*
mysql
)
{
MYSQL_RES
*
result
=
mysql_store_result
(
mysql
);
DBUG_ENTER
(
"ha_federated::store_result"
);
if
(
result
)
{
(
void
)
insert_dynamic
(
&
results
,
(
uchar
*
)
&
result
);
}
position_called
=
FALSE
;
DBUG_RETURN
(
result
);
}
void
ha_federated
::
free_result
()
{
DBUG_ENTER
(
"ha_federated::free_result"
);
if
(
stored_result
&&
!
position_called
)
{
mysql_free_result
(
stored_result
);
stored_result
=
0
;
if
(
results
.
elements
>
0
)
results
.
elements
--
;
}
DBUG_VOID_RETURN
;
}
int
ha_federated
::
external_lock
(
THD
*
thd
,
int
lock_type
)
int
ha_federated
::
external_lock
(
THD
*
thd
,
int
lock_type
)
{
{
int
error
=
0
;
int
error
=
0
;
...
...
storage/federated/ha_federated.h
View file @
c2b9fdb0
...
@@ -84,6 +84,11 @@ class ha_federated: public handler
...
@@ -84,6 +84,11 @@ class ha_federated: public handler
FEDERATED_SHARE
*
share
;
/* Shared lock info */
FEDERATED_SHARE
*
share
;
/* Shared lock info */
MYSQL
*
mysql
;
/* MySQL connection */
MYSQL
*
mysql
;
/* MySQL connection */
MYSQL_RES
*
stored_result
;
MYSQL_RES
*
stored_result
;
/**
Array of all stored results we get during a query execution.
*/
DYNAMIC_ARRAY
results
;
bool
position_called
;
uint
fetch_num
;
// stores the fetch num
uint
fetch_num
;
// stores the fetch num
MYSQL_ROW_OFFSET
current_position
;
// Current position used by ::position()
MYSQL_ROW_OFFSET
current_position
;
// Current position used by ::position()
int
remote_error_number
;
int
remote_error_number
;
...
@@ -251,6 +256,10 @@ class ha_federated: public handler
...
@@ -251,6 +256,10 @@ class ha_federated: public handler
THR_LOCK_DATA
**
store_lock
(
THD
*
thd
,
THR_LOCK_DATA
**
to
,
THR_LOCK_DATA
**
store_lock
(
THD
*
thd
,
THR_LOCK_DATA
**
to
,
enum
thr_lock_type
lock_type
);
//required
enum
thr_lock_type
lock_type
);
//required
bool
get_error_message
(
int
error
,
String
*
buf
);
bool
get_error_message
(
int
error
,
String
*
buf
);
MYSQL_RES
*
store_result
(
MYSQL
*
mysql
);
void
free_result
();
int
external_lock
(
THD
*
thd
,
int
lock_type
);
int
external_lock
(
THD
*
thd
,
int
lock_type
);
int
connection_commit
();
int
connection_commit
();
int
connection_rollback
();
int
connection_rollback
();
...
...
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