auto-merge
This commit is contained in:
commit
fd7bf5bcf7
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
|||||||
|
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
&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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
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 @@ error:
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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()
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
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.
|
|
||||||
|
|
||||||
In case of federated, a reference is either a primary key or
|
@details During a query execution we may have different result sets (RS),
|
||||||
the whole record.
|
e.g. for different ranges. All the RS's used are stored in
|
||||||
|
memory and placed in @c results dynamic array. At the end of
|
||||||
|
execution all stored RS's are freed at once in the
|
||||||
|
@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().
|
||||||
|
|
||||||
Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
|
@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 *), ¤t_position,
|
||||||
|
sizeof(MYSQL_ROW_OFFSET));
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
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;
|
||||||
|
@ -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 @@ public:
|
|||||||
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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user