From: Ramil Kalimullin Date: March 5 2010 10:51am Subject: bzr commit into mysql-5.1-bugteam branch (ramil:3369) Bug#32426 List-Archive: http://lists.mysql.com/commits/102419 X-Bug: 32426 Message-Id: <0KYT00A4Q26UIY50@fe-emea-10.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Boundary_(ID_CyBBpFdD3xcGwbAzAsoMBA)" --Boundary_(ID_CyBBpFdD3xcGwbAzAsoMBA) MIME-version: 1.0 Content-type: text/plain; CHARSET=US-ASCII Content-transfer-encoding: 7BIT Content-disposition: inline #At file:///home/ram/mysql/b32426-5.1-bugteam/ based on revid:joro@stripped 3369 Ramil Kalimullin 2010-03-05 Fix for bug#32426: "FEDERATED query returns corrupt results for ORDER BY on a TEXT or VARCHAR field" backported to 5.1. modified: mysql-test/suite/federated/federated.result mysql-test/suite/federated/federated.test storage/federated/ha_federated.cc storage/federated/ha_federated.h === modified file 'mysql-test/suite/federated/federated.result' --- a/mysql-test/suite/federated/federated.result 2009-03-19 08:49:51 +0000 +++ b/mysql-test/suite/federated/federated.result 2010-03-05 10:51:37 +0000 @@ -2153,6 +2153,29 @@ DROP TABLE t1; End of 5.0 tests create server 's1' foreign data wrapper 'mysql' options (port 3306); 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@stripped: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 SET @@GLOBAL.CONCURRENT_INSERT= @OLD_MASTER_CONCURRENT_INSERT; SET @@GLOBAL.CONCURRENT_INSERT= @OLD_SLAVE_CONCURRENT_INSERT; === modified file 'mysql-test/suite/federated/federated.test' --- a/mysql-test/suite/federated/federated.test 2009-03-19 08:49:51 +0000 +++ b/mysql-test/suite/federated/federated.test 2010-03-05 10:51:37 +0000 @@ -1971,6 +1971,28 @@ connection default; create server 's1' foreign data wrapper 'mysql' options (port 3306); 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@stripped:$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 SET @@GLOBAL.CONCURRENT_INSERT= @OLD_MASTER_CONCURRENT_INSERT; connection slave; === modified file 'storage/federated/ha_federated.cc' --- a/storage/federated/ha_federated.cc 2009-06-17 14:56:44 +0000 +++ b/storage/federated/ha_federated.cc 2010-03-05 10:51:37 +0000 @@ -1621,11 +1621,10 @@ int ha_federated::open(const char *name, DBUG_ASSERT(mysql == NULL); - ref_length= (table->s->primary_key != MAX_KEY ? - table->key_info[table->s->primary_key].key_length : - table->s->reclength); + ref_length= sizeof(MYSQL_RES *) + sizeof(MYSQL_ROW_OFFSET); DBUG_PRINT("info", ("ref_length: %u", ref_length)); + my_init_dynamic_array(&results, sizeof(MYSQL_RES *), 4, 4); reset(); DBUG_RETURN(0); @@ -1645,21 +1644,17 @@ int ha_federated::open(const char *name, int ha_federated::close(void) { - int retval; DBUG_ENTER("ha_federated::close"); - /* free the result set */ - if (stored_result) - { - mysql_free_result(stored_result); - stored_result= 0; - } + free_result(); + + delete_dynamic(&results); + /* Disconnect from mysql */ mysql_close(mysql); 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, { DBUG_ENTER("ha_federated::index_read"); - if (stored_result) - mysql_free_result(stored_result); + free_result(); DBUG_RETURN(index_read_idx_with_result_set(buf, active_index, key, key_len, find_flag, &stored_result)); @@ -2359,7 +2353,8 @@ int ha_federated::index_read_idx(uchar * &mysql_result))) DBUG_RETURN(retval); mysql_free_result(mysql_result); - DBUG_RETURN(retval); + results.elements--; + DBUG_RETURN(0); } @@ -2415,18 +2410,20 @@ int ha_federated::index_read_idx_with_re retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE; goto error; } - if (!(*result= mysql_store_result(mysql))) + if (!(*result= store_result(mysql))) { retval= HA_ERR_END_OF_FILE; goto error; } - if (!(retval= read_next(buf, *result))) + if ((retval= read_next(buf, *result))) + { + mysql_free_result(*result); + results.elements--; + *result= 0; + table->status= STATUS_NOT_FOUND; DBUG_RETURN(retval); - - mysql_free_result(*result); - *result= 0; - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(retval); + } + DBUG_RETURN(0); error: table->status= STATUS_NOT_FOUND; @@ -2486,12 +2483,6 @@ int ha_federated::read_range_first(const create_where_from_key(&sql_query, &table->key_info[active_index], 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())) { retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE; @@ -2499,14 +2490,13 @@ int ha_federated::read_range_first(const } sql_query.length(0); - if (!(stored_result= mysql_store_result(mysql))) + if (!(stored_result= store_result(mysql))) { retval= HA_ERR_END_OF_FILE; goto error; } - - retval= read_next(table->record[0], stored_result); - DBUG_RETURN(retval); + + DBUG_RETURN(read_next(table->record[0], stored_result)); error: table->status= STATUS_NOT_FOUND; @@ -2516,10 +2506,8 @@ error: int ha_federated::read_range_next() { - int retval; DBUG_ENTER("ha_federated::read_range_next"); - retval= rnd_next(table->record[0]); - DBUG_RETURN(retval); + DBUG_RETURN(rnd_next(table->record[0])); } @@ -2585,23 +2573,11 @@ int ha_federated::rnd_init(bool scan) if (scan) { - if (stored_result) - { - mysql_free_result(stored_result); - 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; + if (real_query(share->select_query, strlen(share->select_query)) || + !(stored_result= store_result(mysql))) + DBUG_RETURN(stash_remote_error()); } DBUG_RETURN(0); - -error: - DBUG_RETURN(stash_remote_error()); } @@ -2615,11 +2591,7 @@ int ha_federated::rnd_end() int ha_federated::index_end(void) { DBUG_ENTER("ha_federated::index_end"); - if (stored_result) - { - mysql_free_result(stored_result); - stored_result= 0; - } + free_result(); active_index= MAX_KEY; DBUG_RETURN(0); } @@ -2679,6 +2651,9 @@ int ha_federated::read_next(uchar *buf, DBUG_ENTER("ha_federated::read_next"); 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. */ if (!(row= mysql_fetch_row(result))) @@ -2691,24 +2666,38 @@ int ha_federated::read_next(uchar *buf, } -/* - store reference to current row so that we can later find it for - a re-read, update or delete. - - In case of federated, a reference is either a primary key or - the whole record. - - Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc. +/** + @brief Store a reference to current row. + + @details During a query execution we may have different result sets (RS), + 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(). + + @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"); - if (table->s->primary_key != MAX_KEY) - key_copy(ref, (uchar *)record, table->key_info + table->s->primary_key, - ref_length); - else - memcpy(ref, record, ref_length); + + DBUG_ASSERT(stored_result); + + position_called= TRUE; + /* 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; } @@ -2724,23 +2713,19 @@ void ha_federated::position(const uchar int ha_federated::rnd_pos(uchar *buf, uchar *pos) { - int result; + MYSQL_RES *result; DBUG_ENTER("ha_federated::rnd_pos"); + ha_statistic_increment(&SSV::ha_read_rnd_count); - if (table->s->primary_key != MAX_KEY) - { - /* We have a primary key, so use index_read_idx to find row */ - result= index_read_idx(buf, table->s->primary_key, pos, - ref_length, HA_READ_KEY_EXACT); - } - else - { - /* otherwise, get the old record ref as obtained in ::position */ - memcpy(buf, pos, ref_length); - result= 0; - } - table->status= result ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(result); + + /* Get stored result set. */ + memcpy_fixed(&result, pos, sizeof(MYSQL_RES *)); + DBUG_ASSERT(result); + /* Set data cursor position. */ + memcpy_fixed(&result->data_cursor, pos + sizeof(MYSQL_RES *), + sizeof(MYSQL_ROW_OFFSET)); + /* Read a row. */ + DBUG_RETURN(read_next(buf, result)); } @@ -2943,6 +2928,16 @@ int ha_federated::reset(void) insert_dup_update= FALSE; ignore_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; } @@ -3206,6 +3201,45 @@ bool ha_federated::get_error_message(int 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 error= 0; === modified file 'storage/federated/ha_federated.h' --- a/storage/federated/ha_federated.h 2009-02-13 16:41:47 +0000 +++ b/storage/federated/ha_federated.h 2010-03-05 10:51:37 +0000 @@ -84,6 +84,11 @@ class ha_federated: public handler FEDERATED_SHARE *share; /* Shared lock info */ MYSQL *mysql; /* MySQL connection */ 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 MYSQL_ROW_OFFSET current_position; // Current position used by ::position() int remote_error_number; @@ -251,6 +256,10 @@ public: THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); //required 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 connection_commit(); int connection_rollback(); --Boundary_(ID_CyBBpFdD3xcGwbAzAsoMBA) MIME-version: 1.0 Content-type: text/bzr-bundle; CHARSET=US-ASCII; name="bzr/ramil@stripped" Content-transfer-encoding: 7BIT Content-disposition: inline; filename="bzr/ramil@stripped" # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: ramil@stripped # target_branch: file:///home/ram/mysql/b32426-5.1-bugteam/ # testament_sha1: d98d327d17a03be5585a4bbdb57a3d46e81a2a8d # timestamp: 2010-03-05 14:52:01 +0400 # base_revision_id: joro@stripped # # Begin bundle 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 KEgAakGjAA== --Boundary_(ID_CyBBpFdD3xcGwbAzAsoMBA)--