From 43073630790990ffd6d1b6e970f8e6d3698663ea Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sun, 3 Nov 2002 10:15:14 +0200 Subject: [PATCH 01/21] new innodb query cache behaviour (recommit because of problem with 4.1 repository pushing) fixed bug in quqry cache dbd transaction processing --- mysql-test/r/bdb_cache.result | 100 ++++++++++++++++++++++++++++++ mysql-test/r/innodb_cache.result | 6 +- mysql-test/t/bdb_cache-master.opt | 1 + mysql-test/t/bdb_cache.test | 50 +++++++++++++++ sql/ha_berkeley.h | 1 + sql/ha_innodb.cc | 2 +- sql/ha_innodb.h | 5 +- sql/handler.cc | 13 +++- sql/handler.h | 15 +++++ sql/sql_cache.cc | 45 ++++++++++---- sql/sql_cache.h | 26 +++++--- 11 files changed, 237 insertions(+), 27 deletions(-) create mode 100644 mysql-test/r/bdb_cache.result create mode 100644 mysql-test/t/bdb_cache-master.opt create mode 100644 mysql-test/t/bdb_cache.test diff --git a/mysql-test/r/bdb_cache.result b/mysql-test/r/bdb_cache.result new file mode 100644 index 00000000000..e5c6923162a --- /dev/null +++ b/mysql-test/r/bdb_cache.result @@ -0,0 +1,100 @@ +drop table if exists t1, t2, t3; +flush status; +set autocommit=0; +create table t1 (a int not null) type=bdb; +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t1; +commit; +set autocommit=1; +begin; +create table t1 (a int not null) type=bdb; +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t1; +commit; +create table t1 (a int not null) type=bdb; +create table t2 (a int not null) type=bdb; +create table t3 (a int not null) type=bdb; +insert into t1 values (1),(2); +insert into t2 values (1),(2); +insert into t3 values (1),(2); +select * from t1; +a +1 +2 +select * from t2; +a +1 +2 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +begin; +select * from t1; +a +1 +2 +select * from t2; +a +1 +2 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +insert into t1 values (3); +insert into t2 values (3); +insert into t1 values (4); +select * from t1; +a +1 +2 +3 +4 +select * from t2; +a +1 +2 +3 +select * from t3; +a +1 +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +commit; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 diff --git a/mysql-test/r/innodb_cache.result b/mysql-test/r/innodb_cache.result index eaa030046da..6c79c2c7b34 100644 --- a/mysql-test/r/innodb_cache.result +++ b/mysql-test/r/innodb_cache.result @@ -10,7 +10,7 @@ a 3 show status like "Qcache_queries_in_cache"; Variable_name Value -Qcache_queries_in_cache 0 +Qcache_queries_in_cache 1 drop table t1; commit; set autocommit=1; @@ -24,7 +24,7 @@ a 3 show status like "Qcache_queries_in_cache"; Variable_name Value -Qcache_queries_in_cache 0 +Qcache_queries_in_cache 1 drop table t1; commit; create table t1 (a int not null) type=innodb; @@ -97,4 +97,4 @@ Qcache_hits 0 commit; show status like "Qcache_queries_in_cache"; Variable_name Value -Qcache_queries_in_cache 1 +Qcache_queries_in_cache 3 diff --git a/mysql-test/t/bdb_cache-master.opt b/mysql-test/t/bdb_cache-master.opt new file mode 100644 index 00000000000..5f0ebff98f6 --- /dev/null +++ b/mysql-test/t/bdb_cache-master.opt @@ -0,0 +1 @@ +--set-variable=query_cache_size=1M diff --git a/mysql-test/t/bdb_cache.test b/mysql-test/t/bdb_cache.test new file mode 100644 index 00000000000..aa5572886c5 --- /dev/null +++ b/mysql-test/t/bdb_cache.test @@ -0,0 +1,50 @@ +-- source include/have_bdb.inc +-- source include/have_query_cache.inc + +# +# Without auto_commit. +# +drop table if exists t1, t2, t3; +flush status; +set autocommit=0; +create table t1 (a int not null) type=bdb; +insert into t1 values (1),(2),(3); +select * from t1; +show status like "Qcache_queries_in_cache"; +drop table t1; +commit; +set autocommit=1; +begin; +create table t1 (a int not null) type=bdb; +insert into t1 values (1),(2),(3); +select * from t1; +show status like "Qcache_queries_in_cache"; +drop table t1; +commit; +create table t1 (a int not null) type=bdb; +create table t2 (a int not null) type=bdb; +create table t3 (a int not null) type=bdb; +insert into t1 values (1),(2); +insert into t2 values (1),(2); +insert into t3 values (1),(2); +select * from t1; +select * from t2; +select * from t3; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_hits"; +begin; +select * from t1; +select * from t2; +select * from t3; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_hits"; +insert into t1 values (3); +insert into t2 values (3); +insert into t1 values (4); +select * from t1; +select * from t2; +select * from t3; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_hits"; +commit; +show status like "Qcache_queries_in_cache"; \ No newline at end of file diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index 198664d0c06..06776240751 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -164,6 +164,7 @@ class ha_berkeley: public handler } longlong get_auto_increment(); void print_error(int error, myf errflag); + uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; } }; extern bool berkeley_skip, berkeley_shared_data; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 9aa63cc1435..c624cee2954 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -462,7 +462,7 @@ If thd is not in the autocommit state, this function also starts a new transaction for thd if there is no active trx yet, and assigns a consistent read view to it if there is no read view yet. */ -my_bool +bool innobase_query_caching_of_table_permitted( /*======================================*/ /* out: TRUE if permitted, FALSE if not; diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 357fb31b5e3..9055c8b8239 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -168,6 +168,7 @@ class ha_innobase: public handler enum thr_lock_type lock_type); void init_table_handle_for_HANDLER(); longlong get_auto_increment(); + uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; } }; extern bool innodb_skip; @@ -206,5 +207,5 @@ int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); int innodb_show_status(THD* thd); -my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, - uint full_name_len); +bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, + uint full_name_len); diff --git a/sql/handler.cc b/sql/handler.cc index f60c3449075..10313201bf2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -294,7 +294,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) error=1; } else - transaction_commited= 1; + if (!(thd->options & OPTION_BEGIN)) + transaction_commited= 1; trans->bdb_tid=0; } #endif @@ -838,6 +839,16 @@ int handler::delete_all_rows() return (my_errno=HA_ERR_WRONG_COMMAND); } +bool handler::caching_allowed(THD* thd, char* table_key, + uint key_length, uint8 cache_type) +{ + if (cache_type == HA_CACHE_TBL_ASKTRANSACT) + return innobase_query_caching_of_table_permitted(thd, table_key, + key_length); + else + return 1; +} + /**************************************************************************** ** Some general functions that isn't in the handler class ****************************************************************************/ diff --git a/sql/handler.h b/sql/handler.h index fbc27f4729b..7fffd1a0cb9 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -116,6 +116,11 @@ #define HA_OPTION_NO_DELAY_KEY_WRITE (1L << 18) #define HA_MAX_REC_LENGTH 65535 +/* Table caching type */ +#define HA_CACHE_TBL_NONTRANSACT 0 +#define HA_CACHE_TBL_ASKTRANSACT 1 +#define HA_CACHE_TBL_TRANSACT 2 + enum db_type { DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1, DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM, DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM, @@ -343,6 +348,16 @@ public: virtual THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type)=0; + + /* Type of table for caching query */ + virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } + /* + Is query with this cable cachable (have sense only for ASKTRANSACT + tables) + */ + static bool caching_allowed(THD* thd, char* table_key, + uint key_length, uint8 cahe_type); + }; /* Some extern variables used with handlers */ diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 5bdefbaaa30..e1a8e64b08d 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -757,8 +757,10 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) if (query_cache_size == 0) DBUG_VOID_RETURN; + uint8 tables_type= 0; if ((tables = is_cacheable(thd, thd->query_length, - thd->query, &thd->lex, tables_used))) + thd->query, &thd->lex, tables_used, + &tables_type))) { NET *net = &thd->net; byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); @@ -837,6 +839,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) net->query_cache_query= (gptr) query_block; header->writer(net); + header->tables_type(tables_type); // init_n_lock make query block locked BLOCK_UNLOCK_WR(query_block); } @@ -884,6 +887,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) Query_cache_block_table *block_table, *block_table_end; ulong tot_length; byte flags; + bool check_tables; DBUG_ENTER("Query_cache::send_result_to_client"); if (query_cache_size == 0 || @@ -975,6 +979,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) } DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query)); + check_tables= query->tables_type() & HA_CACHE_TBL_ASKTRANSACT; // Check access; block_table= query_block->table(0); block_table_end= block_table+query_block->n_tables; @@ -1005,6 +1010,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) thd->safe_to_cache_query=0; // Don't try to cache this goto err_unlock; // Parse query } + if (check_tables && !handler::caching_allowed(thd, table->db(), + table->key_length(), + table->type())) + { + DBUG_PRINT("qcache", ("Handler not allow caching for %s.%s", + table_list.db, table_list.alias)); + BLOCK_UNLOCK_RD(query_block); + thd->safe_to_cache_query=0; // Don't try to cache this + goto err_unlock; // Parse query + } } move_to_query_list_end(query_block); hits++; @@ -1062,7 +1077,8 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, { DBUG_ASSERT(!using_transactions || tables_used->table!=0); if (using_transactions && - tables_used->table->file->has_transactions()) + (tables_used->table->file->table_cache_type() == + HA_CACHE_TBL_TRANSACT)) /* Tables_used->table can't be 0 in transaction. Only 'drop' invalidate not opened table, but 'drop' @@ -1116,7 +1132,8 @@ void Query_cache::invalidate(THD *thd, TABLE *table, { using_transactions = using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - if (using_transactions && table->file->has_transactions()) + if (using_transactions && + (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) thd->add_changed_table(table); else invalidate_table(table); @@ -1925,7 +1942,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, block_table->n=n; if (!insert_table(tables_used->table->key_length, tables_used->table->table_cache_key, block_table, - tables_used->db_length)) + tables_used->db_length, + tables_used->table->file->table_cache_type())) break; if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) @@ -1938,11 +1956,12 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, { char key[MAX_DBKEY_LENGTH]; uint32 db_length; - uint key_length =filename_2_table_key(key, table->table->filename, + uint key_length= filename_2_table_key(key, table->table->filename, &db_length); (++block_table)->n= ++n; if (!insert_table(key_length, key, block_table, - db_length)) + db_length, + tables_used->table->file->table_cache_type())) goto err; } } @@ -1969,7 +1988,7 @@ err: my_bool Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length) + uint32 db_length, uint8 cache_type) { DBUG_ENTER("Query_cache::insert_table"); DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", @@ -2007,6 +2026,8 @@ Query_cache::insert_table(uint key_len, char *key, } char *db = header->db(); header->table(db + db_length + 1); + header->key_length(key_len); + header->type(cache_type); } Query_cache_block_table *list_root = table_block->table(0); @@ -2437,7 +2458,9 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail, TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, - LEX *lex, TABLE_LIST *tables_used) + LEX *lex, + TABLE_LIST *tables_used, + uint8 *tables_type) { TABLE_COUNTER_TYPE tables = 0; DBUG_ENTER("Query_cache::is_cacheable"); @@ -2448,7 +2471,6 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, OPTION_TO_QUERY_CACHE))) && thd->safe_to_cache_query) { - my_bool has_transactions = 0; DBUG_PRINT("qcache", ("options %lx %lx, type %u", OPTION_TO_QUERY_CACHE, lex->select->options, @@ -2460,8 +2482,7 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, DBUG_PRINT("qcache", ("table %s, db %s, type %u", tables_used->real_name, tables_used->db, tables_used->table->db_type)); - has_transactions = (has_transactions || - tables_used->table->file->has_transactions()); + *tables_type|= tables_used->table->file->table_cache_type(); if (tables_used->table->db_type == DB_TYPE_MRG_ISAM || tables_used->table->tmp_table != NO_TMP_TABLE || @@ -2485,7 +2506,7 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, } if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && - has_transactions) + ((*tables_type)&HA_CACHE_TBL_TRANSACT)) { DBUG_PRINT("qcache", ("not in autocommin mode")); DBUG_RETURN(0); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index f19e6630da5..7726fc3aef1 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -115,18 +115,21 @@ struct Query_cache_query Query_cache_block *res; NET *wri; ulong len; + uint8 tbls_type; inline void init_n_lock(); void unlock_n_destroy(); inline ulonglong found_rows() { return limit_found_rows; } - inline void found_rows(ulonglong rows) { limit_found_rows = rows; } + inline void found_rows(ulonglong rows) { limit_found_rows= rows; } inline Query_cache_block *result() { return res; } - inline void result(Query_cache_block *p) { res=p; } + inline void result(Query_cache_block *p) { res= p; } inline NET *writer() { return wri; } - inline void writer(NET *p) { wri=p; } + inline void writer(NET *p) { wri= p; } + inline uint8 tables_type() { return tbls_type; } + inline void tables_type(uint8 type) { tbls_type= type; } inline ulong length() { return len; } - inline ulong add(ulong packet_len) { return(len += packet_len); } - inline void length(ulong length) { len = length; } + inline ulong add(ulong packet_len) { return(len+= packet_len); } + inline void length(ulong length) { len= length; } inline gptr query() { return (gptr)(((byte*)this)+ @@ -144,10 +147,16 @@ struct Query_cache_query struct Query_cache_table { char *tbl; + uint key_len; + uint8 table_type; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } - inline void table(char *table) { tbl = table; } + inline void table(char *table) { tbl= table; } + inline uint key_length() { return key_len; } + inline void key_length(uint len) { key_len= len; } + inline uint8 type() { return table_type; } + inline void type(uint8 t) { table_type= t; } inline gptr data() { return (gptr)(((byte*)this)+ @@ -276,7 +285,7 @@ protected: TABLE_COUNTER_TYPE tables); my_bool insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length); + uint32 db_length, uint8 cache_type); void unlink_table(Query_cache_block_table *node); Query_cache_block *get_free_block (ulong len, my_bool not_less, ulong min); @@ -334,7 +343,8 @@ protected: (query without tables not cached) */ TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, - LEX *lex, TABLE_LIST *tables_used); + LEX *lex, TABLE_LIST *tables_used, + uint8 *tables_type); public: Query_cache(ulong query_cache_limit = ULONG_MAX, From d939f36e47d19c2d3643588eeac33223369cacb2 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 6 Nov 2002 21:26:27 +0200 Subject: [PATCH 02/21] Fix to make working new innodb query cache beahaviour Fix grammar Fix debuging methods to be workable with switched off query cache (2Heikki: it is for you) --- sql/sql_cache.cc | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 956b354d480..5b3582c12da 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -890,13 +890,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) bool check_tables; DBUG_ENTER("Query_cache::send_result_to_client"); - if (query_cache_size == 0 || - /* - it is not possible to check has_transactions() function of handler - because tables not opened yet - */ - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) || - thd->variables.query_cache_type == 0) + if (query_cache_size == 0 || thd->variables.query_cache_type == 0) goto err; @@ -1014,12 +1008,15 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) table->key_length(), table->type())) { - DBUG_PRINT("qcache", ("Handler not allow caching for %s.%s", + DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); thd->safe_to_cache_query=0; // Don't try to cache this goto err_unlock; // Parse query } + else + DBUG_PRINT("qcache", ("handler allow caching (%d) %s,%s", + check_tables, table_list.db, table_list.alias)); } move_to_query_list_end(query_block); hits++; @@ -2947,7 +2944,7 @@ void Query_cache::bins_dump() { uint i; - if (!initialized) + if (!initialized || query_cache_size == 0) { DBUG_PRINT("qcache", ("Query Cache not initialized")); return; @@ -2988,7 +2985,7 @@ void Query_cache::bins_dump() void Query_cache::cache_dump() { - if (!initialized) + if (!initialized || query_cache_size == 0) { DBUG_PRINT("qcache", ("Query Cache not initialized")); return; @@ -3077,7 +3074,7 @@ void Query_cache::queries_dump() void Query_cache::tables_dump() { - if (!initialized) + if (!initialized || query_cache_size == 0) { DBUG_PRINT("qcache", ("Query Cache not initialized")); return; From 316097e17bafaa61007611fd809e9d824919dfda Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Tue, 18 Feb 2003 16:27:04 +0200 Subject: [PATCH 03/21] aftermerging fix --- sql/sql_cache.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 8bc74942bc0..99bd3d64585 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -757,6 +757,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_ENTER("Query_cache::store_query"); if (query_cache_size == 0) DBUG_VOID_RETURN; + uint8 tables_type= 0; if ((local_tables = is_cacheable(thd, thd->query_length, thd->query, &thd->lex, tables_used, @@ -1001,7 +1002,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); - thd->lex.safe_to_cache_query=0; // Don't try to cache this + thd->lex.safe_to_cache_query= 0; // Don't try to cache this goto err_unlock; // Parse query } if (check_tables && !handler::caching_allowed(thd, table->db(), @@ -1011,7 +1012,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); - thd->safe_to_cache_query=0; // Don't try to cache this + thd->lex.safe_to_cache_query= 0; // Don't try to cache this goto err_unlock; // Parse query } else From c96e5884905f02f007f47242ae052a2532b27c04 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Tue, 11 Mar 2003 01:06:28 +0200 Subject: [PATCH 04/21] processing of subselect in global ORDER BY (fifed crash of server) this implementation have limitation: prohibited subselect in ORDER BY dependence of most outer query (will be solved after removing passing first select_lex as fake select for global mysql_select()) --- mysql-test/r/subselect.result | 8 +++++ mysql-test/t/subselect.test | 5 ++++ sql/sql_lex.cc | 55 +++++++++++++++++++++++++++++------ sql/sql_lex.h | 8 +++++ sql/sql_parse.cc | 11 +++++-- sql/sql_yacc.yy | 2 +- 6 files changed, 76 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 47f24a340cd..3a93d942b38 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -458,6 +458,14 @@ Subselect returns more than 1 record select numeropost as a FROM t1 ORDER BY (SELECT 1 FROM t1 HAVING a=1); Subselect returns more than 1 record drop table t1; +create table t1 (a int); +insert into t1 values (1),(2),(3); +(select * from t1) union (select * from t1) order by (select a from t1 limit 1); +a +1 +2 +3 +drop table t1; CREATE TABLE t1 (field char(1) NOT NULL DEFAULT 'b'); INSERT INTO t1 VALUES (); SELECT field FROM t1 WHERE 1=(SELECT 1 UNION ALL SELECT 1 FROM (SELECT 1) a HAVING field='b'); diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1841e9f109a..8229ab84acf 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -244,6 +244,11 @@ select numeropost as a FROM t1 GROUP BY (SELECT 1 FROM t1 HAVING a=1); select numeropost as a FROM t1 ORDER BY (SELECT 1 FROM t1 HAVING a=1); drop table t1; +create table t1 (a int); +insert into t1 values (1),(2),(3); +(select * from t1) union (select * from t1) order by (select a from t1 limit 1); +drop table t1; + #iftest CREATE TABLE t1 (field char(1) NOT NULL DEFAULT 'b'); INSERT INTO t1 VALUES (); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 94c06d41634..61114247fed 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1240,23 +1240,60 @@ TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, } ulong st_select_lex_node::get_table_join_options() { return 0; } -/* - This is used for UNION & subselect to create a new table list of all used - tables. - The table_list->table entry in all used tables are set to point - to the entries in this list. -*/ -// interface +/* + Interface method of table list creation for query + + SYNOPSIS + st_select_lex_unit::create_total_list() + thd THD pointer + result pointer on result list of tables pointer + check_derived force derived table chacking (used for creating + table list for derived query) + DESCRIPTION + This is used for UNION & subselect to create a new table list of all used + tables. + The table_list->table entry in all used tables are set to point + to the entries in this list. + + RETURN + 0 - OK + !0 - error +*/ bool st_select_lex_unit::create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result, bool check_derived) { *result= 0; - return create_total_list_n_last_return(thd, lex, &result, check_derived); + for (SELECT_LEX_UNIT *unit= this; unit; unit= unit->next_unit()) + { + if ((res= unit->create_total_list_n_last_return(thd, lex, &result, + check_derived))) + return res; + } + return 0; } -// list creator +/* + Table list creation for query + + SYNOPSIS + st_select_lex_unit::create_total_list() + thd THD pointer + lex pointer on LEX stricture + result pointer on pointer on result list of tables pointer + check_derived force derived table chacking (used for creating + table list for derived query) + DESCRIPTION + This is used for UNION & subselect to create a new table list of all used + tables. + The table_list->table entry in all used tables are set to point + to the entries in this list. + + RETURN + 0 - OK + !0 - error +*/ bool st_select_lex_unit::create_total_list_n_last_return(THD *thd, st_lex *lex, TABLE_LIST ***result, bool check_derived) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c0f24d6940e..23152c88c3b 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -243,6 +243,7 @@ public: virtual st_select_lex_unit* master_unit()= 0; virtual st_select_lex* outer_select()= 0; + virtual st_select_lex_node* return_after_parsing()= 0; virtual bool set_braces(bool value); virtual bool inc_in_sum_expr(); @@ -297,6 +298,8 @@ public: global parameters for union */ st_select_lex_node *global_parameters; + //node on wich we should return current_select pointer after parsing subquery + st_select_lex_node *return_to; /* LIMIT clause runtime counters */ ha_rows select_limit_cnt, offset_limit_cnt; /* not NULL if union used in subselect, point to subselect item */ @@ -317,6 +320,7 @@ public: (st_select_lex*) slave->next : (st_select_lex*) slave; } st_select_lex_unit* next_unit() { return (st_select_lex_unit*) next; } + st_select_lex_node* return_after_parsing() { return return_to; } void exclude_level(); /* UNION methods */ @@ -379,6 +383,10 @@ public: { return &link_next; } + st_select_lex_node* return_after_parsing() + { + return master_unit()->return_after_parsing(); + } bool set_braces(bool value); bool inc_in_sum_expr(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f0c04688a6b..85c962e7948 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3204,7 +3204,8 @@ mysql_init_query(THD *thd) lex->select_lex.init_query(); lex->value_list.empty(); lex->param_list.empty(); - lex->unit.next= lex->unit.master= lex->unit.link_next= 0; + lex->unit.next= lex->unit.master= lex->unit.return_to= + lex->unit.link_next= 0; lex->unit.prev= lex->unit.link_prev= 0; lex->unit.global_parameters= lex->unit.slave= lex->current_select= lex->all_selects_list= &lex->select_lex; @@ -3252,9 +3253,9 @@ bool mysql_new_select(LEX *lex, bool move_down) { SELECT_LEX *select_lex = new SELECT_LEX(); - select_lex->select_number= ++lex->thd->select_number; if (!select_lex) return 1; + select_lex->select_number= ++lex->thd->select_number; select_lex->init_query(); select_lex->init_select(); if (move_down) @@ -3266,9 +3267,13 @@ mysql_new_select(LEX *lex, bool move_down) unit->init_query(); unit->init_select(); unit->thd= lex->thd; - unit->include_down(lex->current_select); + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + unit->include_neighbour(lex->current_select); + else + unit->include_down(lex->current_select); unit->link_next= 0; unit->link_prev= 0; + unit->return_to= lex->current_select; select_lex->include_down(unit); } else diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 99cf0986e30..bc3a853a2e2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4855,5 +4855,5 @@ subselect_end: ')' { LEX *lex=Lex; - lex->current_select = lex->current_select->outer_select(); + lex->current_select = lex->current_select->return_after_parsing(); }; From fc61731b69e75cb02ad417d863ad0139e2eabd51 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 17 Apr 2003 16:24:56 +0300 Subject: [PATCH 05/21] new result of Query Cache with Innodb --- mysql-test/r/innodb_cache.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/innodb_cache.result b/mysql-test/r/innodb_cache.result index 04540bebd1a..c422dca36f1 100644 --- a/mysql-test/r/innodb_cache.result +++ b/mysql-test/r/innodb_cache.result @@ -69,7 +69,7 @@ Variable_name Value Qcache_queries_in_cache 3 show status like "Qcache_hits"; Variable_name Value -Qcache_hits 0 +Qcache_hits 3 insert into t1 values (3); insert into t2 values (3); insert into t1 values (4); @@ -93,7 +93,7 @@ Variable_name Value Qcache_queries_in_cache 3 show status like "Qcache_hits"; Variable_name Value -Qcache_hits 0 +Qcache_hits 4 commit; show status like "Qcache_queries_in_cache"; Variable_name Value From 12181f6f6e42c7273441e6b671eb6f2a4b0cf7a2 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sun, 20 Apr 2003 22:18:32 +0300 Subject: [PATCH 06/21] fixed bug in processing transaction in query cache (inserted lost lines) --- sql/sql_cache.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index b7c6ff66856..d8265a1b359 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -968,6 +968,15 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) } DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query)); + if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + (query->tables_type() & HA_CACHE_TBL_TRANSACT)) + { + DBUG_PRINT("qcache", + ("we are in transaction and have transaction tables in query")); + BLOCK_UNLOCK_RD(query_block); + goto err_unlock; + } + check_tables= query->tables_type() & HA_CACHE_TBL_ASKTRANSACT; // Check access; block_table= query_block->table(0); From 5610c4dea61bac4f7a0db0b911a941f2f869096a Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Mon, 21 Apr 2003 12:44:29 +0500 Subject: [PATCH 07/21] my_vsnprintf.c: The last character was not printed into out string --- strings/my_vsnprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c index a9fdc3718a8..deb9448857e 100644 --- a/strings/my_vsnprintf.c +++ b/strings/my_vsnprintf.c @@ -84,7 +84,7 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) if (*fmt == 's') /* String parameter */ { reg2 char *par = va_arg(ap, char *); - uint plen,left_len = (uint)(end-to); + uint plen,left_len = (uint)(end-to)+1; if (!par) par = (char*)"(null)"; plen = (uint) strlen(par); if (left_len <= plen) From 292f7954bd636b34245a79f4054f4a6d6140f544 Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Tue, 22 Apr 2003 12:01:28 +0500 Subject: [PATCH 08/21] item_cmpfunc.cc: Turbo() is not used anymore for multibyte charsets. --- sql/item_cmpfunc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 09f0aeefb09..ac2d0b4f847 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1870,7 +1870,7 @@ bool Item_func_like::fix_fields(THD *thd, TABLE_LIST *tlist, Item ** ref) { const char* tmp = first + 1; for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ; - canDoTurboBM = tmp == last; + canDoTurboBM = (tmp == last) && !use_mb(args[0]->charset()); } if (canDoTurboBM) From a3d08eaaf97c013569516294ae97b8118b3db0b0 Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Tue, 22 Apr 2003 12:27:35 +0500 Subject: [PATCH 09/21] I renamed system variable literal_collation into connection_collation --- sql/item.cc | 2 +- sql/mysqld.cc | 4 ++-- sql/set_var.cc | 26 +++++++++++++------------- sql/set_var.h | 10 +++++----- sql/sql_class.h | 2 +- sql/sql_yacc.yy | 8 ++++---- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index fee2f742330..2e9866fcd58 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -172,7 +172,7 @@ bool Item::get_time(TIME *ltime) CHARSET_INFO * Item::default_charset() const { - return current_thd->db_charset; + return current_thd->variables.connection_collation; } bool Item::set_charset(CHARSET_INFO *cs1, enum coercion co1, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 47c9c3f8331..f04ca371c60 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2094,7 +2094,7 @@ static int init_common_variables(const char *conf_file_name, int argc, return 1; global_system_variables.result_collation= default_charset_info; global_system_variables.client_collation= default_charset_info; - global_system_variables.literal_collation= default_charset_info; + global_system_variables.connection_collation= default_charset_info; charsets_list= list_charsets(MYF(MY_CS_COMPILED | MY_CS_CONFIG)); @@ -4577,7 +4577,7 @@ static void set_options(void) /* Set default values for some variables */ global_system_variables.result_collation= default_charset_info; global_system_variables.client_collation= default_charset_info; - global_system_variables.literal_collation= default_charset_info; + global_system_variables.connection_collation= default_charset_info; global_system_variables.table_type= DB_TYPE_MYISAM; global_system_variables.tx_isolation= ISO_REPEATABLE_READ; global_system_variables.select_limit= (ulonglong) HA_POS_ERROR; diff --git a/sql/set_var.cc b/sql/set_var.cc index d03b91ef83b..46fc35e11cf 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -110,6 +110,7 @@ sys_var_bool_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", &connect_timeout); +sys_var_connection_collation sys_connection_collation("connection_collation"); sys_var_enum sys_delay_key_write("delay_key_write", &delay_key_write_options, &delay_key_write_typelib, @@ -131,7 +132,6 @@ sys_var_thd_ulong sys_join_buffer_size("join_buffer_size", sys_var_ulonglong_ptr sys_key_buffer_size("key_buffer_size", &keybuff_size, fix_key_buffer_size); -sys_var_literal_collation sys_literal_collation("literal_collation"); sys_var_bool_ptr sys_local_infile("local_infile", &opt_local_infile); sys_var_thd_bool sys_log_warnings("log_warnings", &SV::log_warnings); @@ -346,6 +346,7 @@ sys_var *sys_variables[]= &sys_client_collation, &sys_concurrent_insert, &sys_connect_timeout, + &sys_connection_collation, &sys_default_week_format, &sys_delay_key_write, &sys_delayed_insert_limit, @@ -362,7 +363,6 @@ sys_var *sys_variables[]= &sys_interactive_timeout, &sys_join_buffer_size, &sys_key_buffer_size, - &sys_literal_collation, &sys_last_insert_id, &sys_local_infile, &sys_log_binlog, @@ -458,6 +458,7 @@ struct show_var_st init_vars[]= { {sys_client_collation.name, (char*) &sys_client_collation, SHOW_SYS}, {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, + {sys_connection_collation.name,(char*) &sys_connection_collation, SHOW_SYS}, {"datadir", mysql_real_data_home, SHOW_CHAR}, {"default_week_format", (char*) &sys_default_week_format, SHOW_SYS}, {sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS}, @@ -508,7 +509,6 @@ struct show_var_st init_vars[]= { {sys_key_buffer_size.name, (char*) &sys_key_buffer_size, SHOW_SYS}, {"language", language, SHOW_CHAR}, {"large_files_support", (char*) &opt_large_files, SHOW_BOOL}, - {sys_literal_collation.name,(char*) &sys_literal_collation, SHOW_SYS}, {sys_local_infile.name, (char*) &sys_local_infile, SHOW_SYS}, #ifdef HAVE_MLOCKALL {"locked_in_memory", (char*) &locked_in_memory, SHOW_BOOL}, @@ -1242,29 +1242,29 @@ void sys_var_client_collation::set_default(THD *thd, enum_var_type type) } -bool sys_var_literal_collation::update(THD *thd, set_var *var) +bool sys_var_connection_collation::update(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) - global_system_variables.literal_collation= var->save_result.charset; + global_system_variables.connection_collation= var->save_result.charset; else - thd->variables.literal_collation= var->save_result.charset; + thd->variables.connection_collation= var->save_result.charset; return 0; } -byte *sys_var_literal_collation::value_ptr(THD *thd, enum_var_type type) +byte *sys_var_connection_collation::value_ptr(THD *thd, enum_var_type type) { CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? - global_system_variables.literal_collation : - thd->variables.literal_collation); + global_system_variables.connection_collation : + thd->variables.connection_collation); return cs ? (byte*) cs->name : (byte*) ""; } -void sys_var_literal_collation::set_default(THD *thd, enum_var_type type) +void sys_var_connection_collation::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.literal_collation= default_charset_info; + global_system_variables.connection_collation= default_charset_info; else - thd->variables.literal_collation= global_system_variables.literal_collation; + thd->variables.connection_collation= global_system_variables.connection_collation; } bool sys_var_result_collation::update(THD *thd, set_var *var) @@ -1305,7 +1305,7 @@ int set_var_client_collation::check(THD *thd) int set_var_client_collation::update(THD *thd) { thd->variables.client_collation= client_collation; - thd->variables.literal_collation= literal_collation; + thd->variables.connection_collation= connection_collation; thd->variables.result_collation= result_collation; thd->protocol_simple.init(thd); thd->protocol_prep.init(thd); diff --git a/sql/set_var.h b/sql/set_var.h index 69e6bc05a67..f236f8f850c 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -429,10 +429,10 @@ public: byte *value_ptr(THD *thd, enum_var_type type); }; -class sys_var_literal_collation :public sys_var_collation +class sys_var_connection_collation :public sys_var_collation { public: - sys_var_literal_collation(const char *name_arg) :sys_var_collation(name_arg) {} + sys_var_connection_collation(const char *name_arg) :sys_var_collation(name_arg) {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type); @@ -556,14 +556,14 @@ public: class set_var_client_collation: public set_var_base { CHARSET_INFO *client_collation; - CHARSET_INFO *literal_collation; + CHARSET_INFO *connection_collation; CHARSET_INFO *result_collation; public: set_var_client_collation(CHARSET_INFO *client_coll_arg, - CHARSET_INFO *literal_coll_arg, + CHARSET_INFO *connection_coll_arg, CHARSET_INFO *result_coll_arg) :client_collation(client_coll_arg), - literal_collation(literal_coll_arg), + connection_collation(connection_coll_arg), result_collation(result_coll_arg) {} int check(THD *thd); diff --git a/sql/sql_class.h b/sql/sql_class.h index 4846f5fe9fa..58f27bf2bcb 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -383,7 +383,7 @@ struct system_variables my_bool new_mode; CHARSET_INFO *client_collation; - CHARSET_INFO *literal_collation; + CHARSET_INFO *connection_collation; CHARSET_INFO *result_collation; }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 36ee4e54e0b..d312bc5571f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3924,7 +3924,7 @@ text_literal: TEXT_STRING_literal { THD *thd= YYTHD; - $$ = new Item_string($1.str,$1.length,thd->variables.literal_collation); + $$ = new Item_string($1.str,$1.length,thd->variables.connection_collation); } | NCHAR_STRING { $$= new Item_string($1.str,$1.length,national_charset_info); } @@ -3936,7 +3936,7 @@ text_literal: text_string: TEXT_STRING_literal - { $$= new String($1.str,$1.length,YYTHD->variables.literal_collation); } + { $$= new String($1.str,$1.length,YYTHD->variables.connection_collation); } | HEX_NUM { Item *tmp = new Item_varbinary($1.str,$1.length); @@ -4106,14 +4106,14 @@ TEXT_STRING_literal: TEXT_STRING { THD *thd= YYTHD; - if (my_charset_same(thd->charset(),thd->variables.literal_collation)) + if (my_charset_same(thd->charset(),thd->variables.connection_collation)) { $$=$1; } else { String ident; - ident.copy($1.str,$1.length,thd->charset(),thd->variables.literal_collation); + ident.copy($1.str,$1.length,thd->charset(),thd->variables.connection_collation); $$.str= thd->strmake(ident.ptr(),ident.length()); $$.length= ident.length(); } From 58f08024e702dee45c8ab2770f28f454e8257b0a Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Tue, 22 Apr 2003 19:01:24 +0500 Subject: [PATCH 10/21] sql_base.cc: Table name cache is case-sensitive now like in 4.0. --- sql/sql_base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2063e8b3f08..d92b2ba16ff 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -49,7 +49,7 @@ extern "C" byte *table_cache_key(const byte *record,uint *length, void table_cache_init(void) { - VOID(hash_init(&open_cache,system_charset_info, + VOID(hash_init(&open_cache,&my_charset_bin, table_cache_size+16,0,0,table_cache_key, (hash_free_key) free_cache_entry,0)); mysql_rm_tmp_tables(); From 3beca2dbb12b5421156580cc86550dd5ae8d28a5 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 23 Apr 2003 00:01:19 +0300 Subject: [PATCH 11/21] fixed bug 185 (constant IN (SELECT field ...) do not return NULL correctly) --- mysql-test/r/subselect.result | 12 +++++++++--- mysql-test/t/subselect.test | 8 ++++++++ sql/item.h | 1 + sql/item_cmpfunc.cc | 37 +++++++++++++++++++++++++++++++++++ sql/item_cmpfunc.h | 21 ++++++++++++++++++-- sql/item_subselect.cc | 28 +++++++++++++++++++++++--- sql/item_subselect.h | 1 + sql/sql_select.cc | 5 +++-- 8 files changed, 103 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 078584b25d0..846e54f12ef 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -810,7 +810,7 @@ a t1.a in (select t2.a from t2) explain SELECT t1.a, t1.a in (select t2.a from t2) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 Using index -2 DEPENDENT SUBQUERY t2 index NULL a 5 NULL 3 Using where; Using index +2 DEPENDENT SUBQUERY t2 index a a 5 NULL 3 Using where; Using index drop table t1,t2; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); @@ -1062,7 +1062,7 @@ SELECT 0 IN (SELECT 1 FROM t1 a); EXPLAIN SELECT 0 IN (SELECT 1 FROM t1 a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) @@ -1070,7 +1070,7 @@ SELECT 0 IN (SELECT 1 FROM t1 a); EXPLAIN SELECT 0 IN (SELECT 1 FROM t1 a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables drop table t1; CREATE TABLE `t1` ( `i` int(11) NOT NULL default '0', @@ -1099,3 +1099,9 @@ id name 2 lenka 1 lenka drop table t1,t2; +create table t1 (a int, unique index indexa (a)); +insert into t1 values (-1), (-4), (-2), (NULL); +select -10 IN (select a from t1 FORCE INDEX (indexa)); +-10 IN (select a from t1 FORCE INDEX (indexa)) +NULL +drop table t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index e6ae86aa839..97c3b0523b4 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -690,3 +690,11 @@ INSERT INTO t2 VALUES (4,'vita'), (1,'vita'), (2,'vita'), (1,'vita'); update t1, t2 set t2.name='lenka' where t2.id in (select id from t1); select * from t2; drop table t1,t2; + +# +# correct NULL in IN (SELECT ...) +# +create table t1 (a int, unique index indexa (a)); +insert into t1 values (-1), (-4), (-2), (NULL); +select -10 IN (select a from t1 FORCE INDEX (indexa)); +drop table t1; diff --git a/sql/item.h b/sql/item.h index 6a7ebd506ac..4862ad21fbe 100644 --- a/sql/item.h +++ b/sql/item.h @@ -603,6 +603,7 @@ public: item(it) {} bool fix_fields(THD *, struct st_table_list *, Item ** ref); + Item **storage() {return &item;} }; /* diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 09f0aeefb09..ffe89dbb84c 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1781,6 +1781,43 @@ longlong Item_func_isnull::val_int() return args[0]->is_null() ? 1: 0; } +longlong Item_is_not_null_test::val_int() +{ + DBUG_ENTER("Item_is_not_null_test::val_int"); + if (!used_tables_cache) + { + owner->was_null|= (!cached_value); + DBUG_PRINT("info", ("cached :%d", cached_value)); + DBUG_RETURN(cached_value); + } + if (args[0]->is_null()) + { + DBUG_PRINT("info", ("null")) + owner->was_null|= 1; + DBUG_RETURN(0); + } + else + DBUG_RETURN(1); +} + +/* Optimize case of not_null_column IS NULL */ +void Item_is_not_null_test::update_used_tables() +{ + if (!args[0]->maybe_null) + { + used_tables_cache= 0; /* is always true */ + cached_value= (longlong) 1; + } + else + { + args[0]->update_used_tables(); + if (!(used_tables_cache=args[0]->used_tables())) + { + /* Remember if the value is always NULL or never NULL */ + cached_value= (longlong) !args[0]->is_null(); + } + } +} longlong Item_func_isnotnull::val_int() { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index e7670755396..5e246e3e285 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -648,6 +648,7 @@ class Item_func_in :public Item_int_func class Item_func_isnull :public Item_bool_func { +protected: longlong cached_value; public: Item_func_isnull(Item *a) :Item_bool_func(a) {} @@ -656,11 +657,11 @@ public: void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; - Item_func_isnull::update_used_tables(); + update_used_tables(); } const char *func_name() const { return "isnull"; } /* Optimize case of not_null_column IS NULL */ - void update_used_tables() + virtual void update_used_tables() { if (!args[0]->maybe_null) { @@ -680,6 +681,22 @@ public: optimize_type select_optimize() const { return OPTIMIZE_NULL; } }; +/* Functions used by HAVING for rewriting IN subquery */ + +class Item_in_subselect; +class Item_is_not_null_test :public Item_func_isnull +{ + Item_in_subselect* owner; +public: + Item_is_not_null_test(Item_in_subselect* ow, Item *a) + :Item_func_isnull(a), owner(ow) + {} + longlong val_int(); + const char *func_name() const { return "is_not_null_test"; } + void update_used_tables(); +}; + + class Item_func_isnotnull :public Item_bool_func { public: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 76451680b59..6c0b799b4de 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -508,9 +508,31 @@ void Item_in_subselect::single_value_transformer(THD *thd, sl->item_list.push_back(new Item_int("Not_used", (longlong) 1, 21)); if (sl->table_list.elements) { - item= (*func)(expr, new Item_asterisk_remover(this, item, - (char *)"", - (char*)"")); + Item *having= item, *isnull= item; + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field_name[0] == '*') + { + Item_asterisk_remover *remover; + item= remover= new Item_asterisk_remover(this, item, + (char*)"", + (char*)""); + having= + new Item_is_not_null_test(this, + new Item_ref(remover->storage(), + (char*)"", + (char*)"")); + isnull= + new Item_is_not_null_test(this, + new Item_ref(remover->storage(), + (char*)"", + (char*)"")); + } + having= new Item_is_not_null_test(this, having); + sl->having= (sl->having ? + new Item_cond_and(having, sl->having) : + having); + item= new Item_cond_or((*func)(expr, item), + new Item_func_isnull(isnull)); sl->where= and_items(sl->where, item); } else diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 351c4af7f33..fc4dad5a6b3 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -183,6 +183,7 @@ public: friend class Item_asterisk_remover; friend class Item_ref_null_helper; + friend class Item_is_not_null_test; }; /* ALL/ANY/SOME subselect */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3fdff4e96a4..f2dc2b2afd6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -299,7 +299,7 @@ JOIN::prepare(Item ***rref_pointer_array, DBUG_RETURN(-1); /* purecov: inspected */ ref_pointer_array= *rref_pointer_array; - + if (having) { thd->where="having clause"; @@ -313,6 +313,7 @@ JOIN::prepare(Item ***rref_pointer_array, if (having->with_sum_func) having->split_sum_func(ref_pointer_array, all_fields); } + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); /* @@ -426,7 +427,7 @@ JOIN::optimize() #ifdef HAVE_REF_TO_FIELDS // Not done yet /* Add HAVING to WHERE if possible */ - if (having && !group_list && ! sum_func_count) + if (having && !group_list && !sum_func_count) { if (!conds) { From 11666b528126986e7f4b590aa58626bef761e54a Mon Sep 17 00:00:00 2001 From: "vva@eagle.mysql.r18.ru" <> Date: Tue, 22 Apr 2003 18:41:47 -0400 Subject: [PATCH 12/21] small compress/uncompress modification after monty's review --- mysql-test/r/func_compress.result | 17 ++++++++++---- mysql-test/t/func_compress.test | 9 +++++-- sql/item_create.cc | 8 ++++++- sql/item_create.h | 2 -- sql/item_func.cc | 3 ++- sql/item_strfunc.cc | 39 ++++++++++++------------------- sql/lex.h | 4 ---- 7 files changed, 43 insertions(+), 39 deletions(-) diff --git a/mysql-test/r/func_compress.result b/mysql-test/r/func_compress.result index ac48c8b9494..c4d2eacf363 100644 --- a/mysql-test/r/func_compress.result +++ b/mysql-test/r/func_compress.result @@ -7,11 +7,6 @@ length(@test_compress_string) select uncompress(compress(@test_compress_string)); uncompress(compress(@test_compress_string)) string for test compress function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -select uncompress(@test_compress_string); -uncompress(@test_compress_string) -NULL -Warnings: -Error 1254 Too big size of uncompressed data. The maximum size is 8192. (probably, length of uncompressed data was corrupted) select uncompressed_length(compress(@test_compress_string))=length(@test_compress_string); uncompressed_length(compress(@test_compress_string))=length(@test_compress_string) 1 @@ -33,3 +28,15 @@ select concat('|',c,'|') from t1; concat('|',c,'|') |d| drop table t1; +select compress(""); +compress("") + +select uncompress(""); +uncompress("") + +select uncompress(compress("")); +uncompress(compress("")) + +select uncompressed_length(""); +uncompressed_length("") +0 diff --git a/mysql-test/t/func_compress.test b/mysql-test/t/func_compress.test index 2f86a414792..826721a4053 100644 --- a/mysql-test/t/func_compress.test +++ b/mysql-test/t/func_compress.test @@ -7,7 +7,6 @@ select @test_compress_string:='string for test compress function aaaaaaaaaaaaaaa select length(@test_compress_string); select uncompress(compress(@test_compress_string)); -select uncompress(@test_compress_string); select uncompressed_length(compress(@test_compress_string))=length(@test_compress_string); select uncompressed_length(compress(@test_compress_string)); select length(compress(@test_compress_string))c_ptr()); + if (res->is_empty()) return 0; + return uint4korr(res->c_ptr()) & 0x3FFFFFFF; } #endif /* HAVE_COMPRESS */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 5fb6af958fb..56e3eb2cb5f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2924,6 +2924,8 @@ ret: String *Item_func_compress::val_str(String *str) { String *res= args[0]->val_str(str); + if (res->is_empty()) return res; + int err= Z_OK; int code; @@ -2939,14 +2941,13 @@ String *Item_func_compress::val_str(String *str) compress(compress(compress(...))) I.e. zlib give number 'at least'.. */ - uLongf new_size= (uLongf)((res->length()*120)/100)+12; + ulong new_size= (ulong)((res->length()*120)/100)+12; - buffer.realloc((uint32)new_size+sizeof(int32)+sizeof(char)); - - Byte *body= ((Byte*)buffer.c_ptr())+sizeof(int32); - err= compress(body, &new_size,(const Bytef*)res->c_ptr(), res->length()); + buffer.realloc((uint32)new_size + 4); + Byte *body= ((Byte*)buffer.c_ptr()) + 4; - if (err != Z_OK) + if ((err= compress(body, &new_size, + (const Bytef*)res->c_ptr(), res->length())) != Z_OK) { code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR; push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code)); @@ -2954,18 +2955,8 @@ String *Item_func_compress::val_str(String *str) return 0; } - int4store(buffer.c_ptr(),res->length()); - buffer.length((uint32)new_size+sizeof(int32)); - - /* This is for the stupid char fields which trimm ' ': */ - char *last_char= ((char*)body)+new_size-1; - if (*last_char == ' ') - { - *++last_char= '.'; - new_size++; - } - - buffer.length((uint32)new_size+sizeof(int32)); + int4store(buffer.c_ptr(),res->length() & 0x3FFFFFFF); + buffer.length((uint32)new_size + 4); return &buffer; } @@ -2973,7 +2964,9 @@ String *Item_func_compress::val_str(String *str) String *Item_func_uncompress::val_str(String *str) { String *res= args[0]->val_str(str); - uLongf new_size= uint4korr(res->c_ptr()); + if (res->is_empty()) return res; + + ulong new_size= uint4korr(res->c_ptr()) & 0x3FFFFFFF; int err= Z_OK; uint code; @@ -2982,16 +2975,14 @@ String *Item_func_uncompress::val_str(String *str) push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, ER_TOO_BIG_FOR_UNCOMPRESS, ER(ER_TOO_BIG_FOR_UNCOMPRESS),MAX_BLOB_WIDTH); - null_value= 1; + null_value= 0; return 0; } buffer.realloc((uint32)new_size); - err= uncompress((Byte*)buffer.c_ptr(), &new_size, - ((const Bytef*)res->c_ptr())+sizeof(int32),res->length()); - - if (err == Z_OK) + if ((err= uncompress((Byte*)buffer.c_ptr(), &new_size, + ((const Bytef*)res->c_ptr())+4,res->length())) == Z_OK) { buffer.length((uint32)new_size); return &buffer; diff --git a/sql/lex.h b/sql/lex.h index b5a81a30991..e51b3efff87 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -448,9 +448,7 @@ static SYMBOL sql_functions[] = { { "CHARACTER_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "COALESCE", SYM(COALESCE),0,0}, { "COERCIBILITY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_coercibility)}, -#ifdef HAVE_COMPRESS { "COMPRESS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_compress)}, -#endif { "CONCAT", SYM(CONCAT),0,0}, { "CONCAT_WS", SYM(CONCAT_WS),0,0}, { "CONNECTION_ID", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, @@ -627,10 +625,8 @@ static SYMBOL sql_functions[] = { { "TOUCHES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_touches)}, { "TRIM", SYM(TRIM),0,0}, { "UCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, -#ifdef HAVE_COMPRESS { "UNCOMPRESS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompress)}, { "UNCOMPRESSED_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompressed_length)}, -#endif { "UNIQUE_USERS", SYM(UNIQUE_USERS),0,0}, { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP),0,0}, { "UPPER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, From 8c8ebd5c14400e36f9280fcac728b26589cc95b0 Mon Sep 17 00:00:00 2001 From: "jani@hynda.(none)" <> Date: Wed, 23 Apr 2003 03:29:03 +0300 Subject: [PATCH 13/21] Added --delimiter option to mysql --- client/client_priv.h | 2 +- client/mysql.cc | 114 +++++++++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 38 deletions(-) diff --git a/client/client_priv.h b/client/client_priv.h index fa36eb084d5..eede1a0a39d 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -40,4 +40,4 @@ enum options { OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, OPT_DELETE_MASTER_LOGS, OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL, OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION, - OPT_COMPATIBLE, OPT_RECONNECT }; + OPT_COMPATIBLE, OPT_RECONNECT, OPT_DELIMITER }; diff --git a/client/mysql.cc b/client/mysql.cc index 5367b5d7e91..2623660033b 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -44,7 +44,7 @@ #include #endif -const char *VER= "13.5"; +const char *VER= "14.0"; /* Don't try to make a nice table if the data is too big */ #define MAX_COLUMN_LENGTH 1024 @@ -108,6 +108,7 @@ extern "C" { #include "completion_hash.h" #define PROMPT_CHAR '\\' +#define DEFAULT_DELIMITER ";" typedef struct st_status { @@ -137,8 +138,8 @@ static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0; static my_string opt_mysql_unix_port=0; static int connect_flag=CLIENT_INTERACTIVE; static char *current_host,*current_db,*current_user=0,*opt_password=0, - *current_prompt=0, - *default_charset= (char*) MYSQL_CHARSET; + *current_prompt=0, *delimiter_str= 0, + *default_charset= (char*) MYSQL_CHARSET; static char *histfile; static String glob_buffer,old_buffer; static String processed_prompt; @@ -160,6 +161,8 @@ static char pager[FN_REFLEN], outfile[FN_REFLEN]; static FILE *PAGER, *OUTFILE; static MEM_ROOT hash_mem_root; static uint prompt_counter; +static char delimiter[16]= DEFAULT_DELIMITER; +static uint delimiter_length= 1; #ifdef HAVE_SMEM static char *shared_memory_base_name=0; @@ -187,7 +190,7 @@ static int com_quit(String *str,char*), com_use(String *str,char*), com_source(String *str, char*), com_rehash(String *str, char*), com_tee(String *str, char*), com_notee(String *str, char*), - com_prompt(String *str, char*); + com_prompt(String *str, char*), com_delimiter(String *str, char*); #ifdef USE_POPEN static int com_nopager(String *str, char*), com_pager(String *str, char*), @@ -255,7 +258,8 @@ static COMMANDS commands[] = { "Set outfile [to_outfile]. Append everything into given outfile." }, { "use", 'u', com_use, 1, "Use another database. Takes database name as argument." }, - + { "delimiter", 'd', com_delimiter, 1, + "Set query delimiter. " }, /* Get bash-like expansion for some commands */ { "create table", 0, 0, 0, ""}, { "create database", 0, 0, 0, ""}, @@ -312,7 +316,8 @@ int main(int argc,char *argv[]) MY_INIT(argv[0]); DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); - + + delimiter_str= delimiter; default_prompt = my_strdup(getenv("MYSQL_PS1") ? getenv("MYSQL_PS1") : "mysql> ",MYF(MY_WME)); @@ -482,6 +487,8 @@ static struct my_option my_long_options[] = #endif {"database", 'D', "Database to use.", (gptr*) ¤t_db, (gptr*) ¤t_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (gptr*) &delimiter_str, + (gptr*) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"execute", 'e', "Execute command and quit. (Output like with --batch).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"vertical", 'E', "Print the output of a query (rows) vertically.", @@ -642,6 +649,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), strmov(mysql_charsets_dir, argument); charsets_dir = mysql_charsets_dir; break; + case OPT_DELIMITER: + if (argument == disabled_my_option) + strmov(delimiter, DEFAULT_DELIMITER); + else + strmake(delimiter, argument, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + delimiter_str= delimiter; + break; case OPT_LOCAL_INFILE: using_opt_local_infile=1; break; @@ -925,7 +940,7 @@ static COMMANDS *find_command (char *name,char cmd_char) { while (my_isspace(charset_info,*name)) name++; - if (strchr(name,';') || strstr(name,"\\g")) + if (strstr(name, delimiter) || strstr(name, "\\g")) return ((COMMANDS *) 0); if ((end=strcont(name," \t"))) { @@ -958,7 +973,7 @@ static bool add_line(String &buffer,char *line,char *in_string, bool *ml_comment) { uchar inchar; - char buff[80],*pos,*out; + char buff[80], *pos, *out; COMMANDS *com; if (!line[0] && buffer.is_empty()) @@ -987,7 +1002,9 @@ static bool add_line(String &buffer,char *line,char *in_string, } #endif if (!*ml_comment && inchar == '\\') - { // mSQL or postgreSQL style command ? + { + // Found possbile one character command like \c + if (!(inchar = (uchar) *++pos)) break; // readline adds one '\' if (*in_string || inchar == 'N') // \N is short for NULL @@ -1004,9 +1021,14 @@ static bool add_line(String &buffer,char *line,char *in_string, return 1; // Quit if (com->takes_params) { - for (pos++ ; *pos && *pos != ';' ; pos++) ; // Remove parameters + for (pos++ ; + *pos && (*pos != *delimiter || + !is_prefix(pos + 1, delimiter + 1)) ; pos++) + ; // Remove parameters if (!*pos) pos--; + else + pos+= delimiter_length - 1; // Point at last delim char } out=line; } @@ -1020,31 +1042,37 @@ static bool add_line(String &buffer,char *line,char *in_string, continue; } } - else if (!*ml_comment && inchar == ';' && !*in_string) - { // ';' is end of command + + else if (!*ml_comment && (*pos == *delimiter && + is_prefix(pos + 1, delimiter + 1)) && + !*in_string) + { + uint old_delimiter_length= delimiter_length; if (out != line) - buffer.append(line,(uint) (out-line)); // Add this line - if ((com=find_command(buffer.c_ptr(),0))) + buffer.append(line, (uint) (out - line)); // Add this line + if ((com= find_command(buffer.c_ptr(), 0))) { - if ((*com->func)(&buffer,buffer.c_ptr()) > 0) + if ((*com->func)(&buffer, buffer.c_ptr()) > 0) return 1; // Quit } else { - int error=com_go(&buffer,0); + int error= com_go(&buffer, 0); if (error) { return error < 0 ? 0 : 1; // < 0 is not fatal } } buffer.length(0); - out=line; + out= line; + pos+= old_delimiter_length - 1; } else if (!*ml_comment && (!*in_string && (inchar == '#' || inchar == '-' && pos[1] == '-' && my_isspace(charset_info,pos[2])))) break; // comment to end of line - else if (!*in_string && inchar == '/' && *(pos+1) == '*' && *(pos+2) != '!') + else if (!*in_string && inchar == '/' && *(pos+1) == '*' && + *(pos+2) != '!') { pos++; *ml_comment= 1; @@ -2353,6 +2381,26 @@ static int com_source(String *buffer, char *line) } + /* ARGSUSED */ +static int +com_delimiter(String *buffer __attribute__((unused)), char *line) +{ + char buff[256], *tmp; + + strmake(buff, line, sizeof(buff) - 1); + tmp= get_arg(buff, 0); + + if (!tmp || !*tmp) + { + put_info("DELIMITER must be followed by a 'delimiter' character or string", + INFO_ERROR); + return 0; + } + strmake(delimiter, tmp, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + return 0; +} + /* ARGSUSED */ static int com_use(String *buffer __attribute__((unused)), char *line) @@ -2416,16 +2464,15 @@ com_use(String *buffer __attribute__((unused)), char *line) char *get_arg(char *line, my_bool get_next_arg) { - char *ptr; + char *ptr, *start; my_bool quoted= 0, valid_arg= 0; - uint count= 0; char qtype= 0; ptr= line; if (get_next_arg) { - for (; ptr && *ptr; ptr++); - if ((ptr + 1) && *(ptr + 1)) + for (; *ptr; ptr++) ; + if (*(ptr + 1)) ptr++; } else @@ -2448,18 +2495,12 @@ char *get_arg(char *line, my_bool get_next_arg) quoted= 1; ptr++; } - for (; ptr && *ptr; ptr++, count++) + for (start=ptr ; *ptr; ptr++) { - if (*ptr == '\\') // escaped character + if (*ptr == '\\' && ptr[1]) // escaped character { - // jump over the backslash - char *tmp_ptr, tmp_buff[256]; - tmp_ptr= strmov(tmp_buff, (ptr - count)); - tmp_ptr-= (strlen(tmp_buff) - count); - strmov(tmp_ptr, (ptr + 1)); - strmov(line, tmp_buff); - ptr= line; - ptr+= count; + // Remove the backslash + strmov(ptr, ptr+1); } else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype)) { @@ -2467,10 +2508,8 @@ char *get_arg(char *line, my_bool get_next_arg) break; } } - for (ptr-= count; ptr && *ptr; ptr++) - if (!my_isspace(charset_info, *ptr)) - valid_arg= 1; - return valid_arg ? ptr - count : '\0'; + valid_arg= ptr != start; + return valid_arg ? start : NullS; } @@ -2512,7 +2551,7 @@ sql_real_connect(char *host,char *database,char *user,char *password, } if (!mysql_real_connect(&mysql, host, user, password, database, opt_mysql_port, opt_mysql_unix_port, - connect_flag)) + connect_flag | CLIENT_MULTI_QUERIES)) { if (!silent || (mysql_errno(&mysql) != CR_CONN_HOST_ERROR && @@ -2615,6 +2654,7 @@ com_status(String *buffer __attribute__((unused)), tee_fprintf(stdout, "Current pager:\t\t%s\n", pager); tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : ""); #endif + tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter); tee_fprintf(stdout, "Server version:\t\t%s\n", mysql_get_server_info(&mysql)); tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql)); tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql)); From 8379f5c4f9e5003b6459d3b8cde7844f183ad697 Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Wed, 23 Apr 2003 17:10:24 +0500 Subject: [PATCH 14/21] Fix bug#68: ssl_test.c does not compile/work --- client/Makefile.am | 2 +- client/ssl_test.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index 92c46519275..cead290f446 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -22,7 +22,7 @@ LIBS = @CLIENT_LIBS@ LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow \ mysqldump mysqlimport mysqltest mysqlbinlog mysqlmanagerc mysqlmanager-pwgen -noinst_PROGRAMS = insert_test select_test thread_test +noinst_PROGRAMS = insert_test select_test thread_test ssl_test noinst_HEADERS = sql_string.h completion_hash.h my_readline.h \ client_priv.h mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc diff --git a/client/ssl_test.c b/client/ssl_test.c index 279c1e95fdc..b18e493c267 100644 --- a/client/ssl_test.c +++ b/client/ssl_test.c @@ -41,7 +41,9 @@ int main(int argc, char **argv) mysql_init(&mysql); #ifdef HAVE_OPENSSL - mysql_ssl_set(&mysql,"../SSL/MySQL-client-key.pem","../SSL/MySQL-client-cert.pem","../SSL/MySQL-ca-cert.pem","../SSL/"); + mysql_ssl_set(&mysql,"../SSL/MySQL-client-key.pem", + "../SSL/MySQL-client-cert.pem", + "../SSL/MySQL-ca-cert.pem", 0, 0); #endif if (!(sock = mysql_real_connect(&mysql,"127.0.0.1",0,0,argv[1],3306,NULL,0))) { @@ -49,7 +51,6 @@ int main(int argc, char **argv) perror(""); exit(1); } - printf("Cipher:%s\n",mysql_ssl_cipher(&mysql)); count = 0; num = atoi(argv[2]); while (count < num) @@ -62,7 +63,7 @@ int main(int argc, char **argv) } if (!(res=mysql_store_result(sock))) { - fprintf(stderr,"Couldn't get result from query failed\n", + fprintf(stderr,"Couldn't get result from query failed (%s)\n", mysql_error(sock)); exit(1); } From 6d5f527afd41e48499babe648b7c69ef0bef038d Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Wed, 23 Apr 2003 18:19:22 +0500 Subject: [PATCH 15/21] Variable names have been renamed: client_collation -> collation_client result_collation -> collation_results connection_collation -> collation_connection --- .bzrignore | 1 + mysql-test/r/ctype_collate.result | 40 +++++++-------- mysql-test/r/variables.result | 6 +-- mysql-test/t/ctype_collate.test | 20 ++++---- mysql-test/t/variables.test | 4 +- sql/item.cc | 2 +- sql/mysqld.cc | 12 ++--- sql/protocol.cc | 2 +- sql/set_var.cc | 82 +++++++++++++++---------------- sql/set_var.h | 28 +++++------ sql/sql_class.cc | 1 - sql/sql_class.h | 8 +-- sql/sql_yacc.yy | 27 +++++----- 13 files changed, 116 insertions(+), 117 deletions(-) diff --git a/.bzrignore b/.bzrignore index b3db91e7c59..c2cbca17dd3 100644 --- a/.bzrignore +++ b/.bzrignore @@ -605,3 +605,4 @@ vio/viotest-ssl myisam/ftbench/var/* myisam/ftbench/data myisam/ftbench/t +client/ssl_test diff --git a/mysql-test/r/ctype_collate.result b/mysql-test/r/ctype_collate.result index af231bb7930..c8163a8a289 100644 --- a/mysql-test/r/ctype_collate.result +++ b/mysql-test/r/ctype_collate.result @@ -511,59 +511,59 @@ SHOW FIELDS FROM t1; Field Type Collation Null Key Default Extra latin1_f char(32) latin1_bin YES NULL SET CHARACTER SET 'latin1'; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_swedish_ci +collation_client latin1_swedish_ci SET CHARACTER SET latin1; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_swedish_ci -SHOW VARIABLES LIKE 'client_collation'; +collation_client latin1_swedish_ci +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_swedish_ci +collation_client latin1_swedish_ci SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 SET CHARACTER SET latin1 COLLATE latin1_bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_bin +collation_client latin1_bin SET CHARACTER SET LATIN1 COLLATE Latin1_Bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_bin +collation_client latin1_bin SET CHARACTER SET 'latin1' COLLATE 'latin1_bin'; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_bin +collation_client latin1_bin SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 SET CHARACTER SET koi8r; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation koi8r_general_ci +collation_client koi8r_general_ci SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 SET CHARACTER SET koi8r COLLATE koi8r_bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation koi8r_bin +collation_client koi8r_bin SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 SET CHARACTER SET koi8r COLLATE DEFAULT; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation koi8r_general_ci +collation_client koi8r_general_ci SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 SET CHARACTER SET DEFAULT; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; Variable_name Value -client_collation latin1_swedish_ci +collation_client latin1_swedish_ci SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; charset('a') collation('a') coercibility('a') 'a'='A' latin1 latin1_swedish_ci 3 1 diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index b709d135ee1..8d45c5cf5e9 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -156,9 +156,9 @@ show variables like 'net_buffer_length'; Variable_name Value net_buffer_length 1048576 set character set cp1251_koi8; -show variables like "client_collation"; +show variables like "collation_client"; Variable_name Value -client_collation cp1251_bulgarian_ci +collation_client cp1251_bulgarian_ci select @@timestamp>0; @@timestamp>0 1 @@ -185,7 +185,7 @@ set SESSION query_cache_size=10000; Variable 'query_cache_size' is a GLOBAL variable and should be set with SET GLOBAL set GLOBAL table_type=DEFAULT; Variable 'table_type' doesn't have a default value -set client_collation=UNKNOWN_CHARACTER_SET; +set collation_client=UNKNOWN_CHARACTER_SET; Unknown character set: 'UNKNOWN_CHARACTER_SET' set global autocommit=1; Variable 'autocommit' is a LOCAL variable and can't be used with SET GLOBAL diff --git a/mysql-test/t/ctype_collate.test b/mysql-test/t/ctype_collate.test index 431799ac65c..0b32c3ccf88 100644 --- a/mysql-test/t/ctype_collate.test +++ b/mysql-test/t/ctype_collate.test @@ -128,29 +128,29 @@ SHOW CREATE TABLE t1; SHOW FIELDS FROM t1; SET CHARACTER SET 'latin1'; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SET CHARACTER SET latin1; -SHOW VARIABLES LIKE 'client_collation'; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; SET CHARACTER SET latin1 COLLATE latin1_bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SET CHARACTER SET LATIN1 COLLATE Latin1_Bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SET CHARACTER SET 'latin1' COLLATE 'latin1_bin'; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; SET CHARACTER SET koi8r; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; SET CHARACTER SET koi8r COLLATE koi8r_bin; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; SET CHARACTER SET koi8r COLLATE DEFAULT; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; SET CHARACTER SET DEFAULT; -SHOW VARIABLES LIKE 'client_collation'; +SHOW VARIABLES LIKE 'collation_client'; SELECT charset('a'),collation('a'),coercibility('a'),'a'='A'; --error 1251 SET CHARACTER SET latin1 COLLATE koi8r; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index bb94ab17b77..d1c8df64be2 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -92,7 +92,7 @@ set net_buffer_length=2000000000; show variables like 'net_buffer_length'; set character set cp1251_koi8; -show variables like "client_collation"; +show variables like "collation_client"; select @@timestamp>0; set @@rand_seed1=10000000,@@rand_seed2=1000000; @@ -119,7 +119,7 @@ set SESSION query_cache_size=10000; --error 1230 set GLOBAL table_type=DEFAULT; --error 1115 -set client_collation=UNKNOWN_CHARACTER_SET; +set collation_client=UNKNOWN_CHARACTER_SET; --error 1228 set global autocommit=1; --error 1228 diff --git a/sql/item.cc b/sql/item.cc index 2e9866fcd58..2bb9fa62ff4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -172,7 +172,7 @@ bool Item::get_time(TIME *ltime) CHARSET_INFO * Item::default_charset() const { - return current_thd->variables.connection_collation; + return current_thd->variables.collation_connection; } bool Item::set_charset(CHARSET_INFO *cs1, enum coercion co1, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f04ca371c60..ea0311bafc6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2092,9 +2092,9 @@ static int init_common_variables(const char *conf_file_name, int argc, #endif if (!(default_charset_info= get_charset_by_name(sys_charset.value, MYF(MY_WME)))) return 1; - global_system_variables.result_collation= default_charset_info; - global_system_variables.client_collation= default_charset_info; - global_system_variables.connection_collation= default_charset_info; + global_system_variables.collation_results= default_charset_info; + global_system_variables.collation_client= default_charset_info; + global_system_variables.collation_connection= default_charset_info; charsets_list= list_charsets(MYF(MY_CS_COMPILED | MY_CS_CONFIG)); @@ -4575,9 +4575,9 @@ static void set_options(void) sizeof(mysql_real_data_home)-1); /* Set default values for some variables */ - global_system_variables.result_collation= default_charset_info; - global_system_variables.client_collation= default_charset_info; - global_system_variables.connection_collation= default_charset_info; + global_system_variables.collation_results= default_charset_info; + global_system_variables.collation_client= default_charset_info; + global_system_variables.collation_connection= default_charset_info; global_system_variables.table_type= DB_TYPE_MYISAM; global_system_variables.tx_isolation= ISO_REPEATABLE_READ; global_system_variables.select_limit= (ulonglong) HA_POS_ERROR; diff --git a/sql/protocol.cc b/sql/protocol.cc index 99b815a7840..d00ecb5dbc4 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -724,7 +724,7 @@ bool Protocol_simple::store(const char *from, uint length, bool Protocol_simple::store(const char *from, uint length, CHARSET_INFO *fromcs) { - CHARSET_INFO *tocs= this->thd->variables.result_collation; + CHARSET_INFO *tocs= this->thd->variables.collation_results; #ifndef DEBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || diff --git a/sql/set_var.cc b/sql/set_var.cc index 46fc35e11cf..ea0b569f05d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -105,12 +105,13 @@ sys_var_str sys_charset("character_set", sys_check_charset, sys_update_charset, sys_set_default_charset); -sys_var_client_collation sys_client_collation("client_collation"); +sys_var_collation_client sys_collation_client("collation_client"); +sys_var_collation_connection sys_collation_connection("collation_connection"); +sys_var_collation_results sys_collation_results("collation_results"); sys_var_bool_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", &connect_timeout); -sys_var_connection_collation sys_connection_collation("connection_collation"); sys_var_enum sys_delay_key_write("delay_key_write", &delay_key_write_options, &delay_key_write_typelib, @@ -200,7 +201,6 @@ sys_var_thd_ulong sys_read_buff_size("read_buffer_size", &SV::read_buff_size); sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size", &SV::read_rnd_buff_size); -sys_var_result_collation sys_result_collation("result_collation"); sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank", &rpl_recovery_rank); sys_var_long_ptr sys_query_cache_size("query_cache_size", @@ -343,10 +343,11 @@ sys_var *sys_variables[]= &sys_binlog_cache_size, &sys_buffer_results, &sys_bulk_insert_buff_size, - &sys_client_collation, + &sys_collation_client, + &sys_collation_connection, + &sys_collation_results, &sys_concurrent_insert, &sys_connect_timeout, - &sys_connection_collation, &sys_default_week_format, &sys_delay_key_write, &sys_delayed_insert_limit, @@ -406,7 +407,6 @@ sys_var *sys_variables[]= &sys_rand_seed2, &sys_read_buff_size, &sys_read_rnd_buff_size, - &sys_result_collation, &sys_rpl_recovery_rank, &sys_safe_updates, &sys_select_limit, @@ -455,10 +455,11 @@ struct show_var_st init_vars[]= { {sys_bulk_insert_buff_size.name,(char*) &sys_bulk_insert_buff_size,SHOW_SYS}, {sys_charset.name, (char*) &sys_charset, SHOW_SYS}, {"character_sets", (char*) &charsets_list, SHOW_CHAR_PTR}, - {sys_client_collation.name, (char*) &sys_client_collation, SHOW_SYS}, + {sys_collation_client.name, (char*) &sys_collation_client, SHOW_SYS}, + {sys_collation_connection.name,(char*) &sys_collation_connection, SHOW_SYS}, + {sys_collation_results.name, (char*) &sys_collation_results, SHOW_SYS}, {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, - {sys_connection_collation.name,(char*) &sys_connection_collation, SHOW_SYS}, {"datadir", mysql_real_data_home, SHOW_CHAR}, {"default_week_format", (char*) &sys_default_week_format, SHOW_SYS}, {sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS}, @@ -562,7 +563,6 @@ struct show_var_st init_vars[]= { {sys_pseudo_thread_id.name, (char*) &sys_pseudo_thread_id, SHOW_SYS}, {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, - {sys_result_collation.name, (char*) &sys_result_collation, SHOW_SYS}, {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, #ifdef HAVE_QUERY_CACHE {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS}, @@ -1210,86 +1210,86 @@ bool sys_var_collation::check(THD *thd, set_var *var) return 0; } -bool sys_var_client_collation::update(THD *thd, set_var *var) +bool sys_var_collation_client::update(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) - global_system_variables.client_collation= var->save_result.charset; + global_system_variables.collation_client= var->save_result.charset; else { - thd->variables.client_collation= var->save_result.charset; + thd->variables.collation_client= var->save_result.charset; thd->protocol_simple.init(thd); thd->protocol_prep.init(thd); } return 0; } -byte *sys_var_client_collation::value_ptr(THD *thd, enum_var_type type) +byte *sys_var_collation_client::value_ptr(THD *thd, enum_var_type type) { CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? - global_system_variables.client_collation : - thd->variables.client_collation); + global_system_variables.collation_client : + thd->variables.collation_client); return cs ? (byte*) cs->name : (byte*) ""; } -void sys_var_client_collation::set_default(THD *thd, enum_var_type type) +void sys_var_collation_client::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.client_collation= default_charset_info; + global_system_variables.collation_client= default_charset_info; else { - thd->variables.client_collation= global_system_variables.client_collation; + thd->variables.collation_client= global_system_variables.collation_client; } } -bool sys_var_connection_collation::update(THD *thd, set_var *var) +bool sys_var_collation_connection::update(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) - global_system_variables.connection_collation= var->save_result.charset; + global_system_variables.collation_connection= var->save_result.charset; else - thd->variables.connection_collation= var->save_result.charset; + thd->variables.collation_connection= var->save_result.charset; return 0; } -byte *sys_var_connection_collation::value_ptr(THD *thd, enum_var_type type) +byte *sys_var_collation_connection::value_ptr(THD *thd, enum_var_type type) { CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? - global_system_variables.connection_collation : - thd->variables.connection_collation); + global_system_variables.collation_connection : + thd->variables.collation_connection); return cs ? (byte*) cs->name : (byte*) ""; } -void sys_var_connection_collation::set_default(THD *thd, enum_var_type type) +void sys_var_collation_connection::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.connection_collation= default_charset_info; + global_system_variables.collation_connection= default_charset_info; else - thd->variables.connection_collation= global_system_variables.connection_collation; + thd->variables.collation_connection= global_system_variables.collation_connection; } -bool sys_var_result_collation::update(THD *thd, set_var *var) +bool sys_var_collation_results::update(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) - global_system_variables.result_collation= var->save_result.charset; + global_system_variables.collation_results= var->save_result.charset; else - thd->variables.result_collation= var->save_result.charset; + thd->variables.collation_results= var->save_result.charset; return 0; } -byte *sys_var_result_collation::value_ptr(THD *thd, enum_var_type type) +byte *sys_var_collation_results::value_ptr(THD *thd, enum_var_type type) { CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? - global_system_variables.result_collation : - thd->variables.result_collation); + global_system_variables.collation_results : + thd->variables.collation_results); return cs ? (byte*) cs->name : (byte*) ""; } -void sys_var_result_collation::set_default(THD *thd, enum_var_type type) +void sys_var_collation_results::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.result_collation= default_charset_info; + global_system_variables.collation_results= default_charset_info; else - thd->variables.result_collation= global_system_variables.result_collation; + thd->variables.collation_results= global_system_variables.collation_results; } @@ -1297,16 +1297,16 @@ void sys_var_result_collation::set_default(THD *thd, enum_var_type type) Functions to handle SET NAMES and SET CHARACTER SET *****************************************************************************/ -int set_var_client_collation::check(THD *thd) +int set_var_collation_client::check(THD *thd) { return 0; } -int set_var_client_collation::update(THD *thd) +int set_var_collation_client::update(THD *thd) { - thd->variables.client_collation= client_collation; - thd->variables.connection_collation= connection_collation; - thd->variables.result_collation= result_collation; + thd->variables.collation_client= collation_client; + thd->variables.collation_connection= collation_connection; + thd->variables.collation_results= collation_results; thd->protocol_simple.init(thd); thd->protocol_prep.init(thd); return 0; diff --git a/sql/set_var.h b/sql/set_var.h index f236f8f850c..fbd20228d24 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -420,28 +420,28 @@ SHOW_TYPE type() { return SHOW_CHAR; } virtual void set_default(THD *thd, enum_var_type type)= 0; }; -class sys_var_client_collation :public sys_var_collation +class sys_var_collation_client :public sys_var_collation { public: - sys_var_client_collation(const char *name_arg) :sys_var_collation(name_arg) {} + sys_var_collation_client(const char *name_arg) :sys_var_collation(name_arg) {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type); }; -class sys_var_connection_collation :public sys_var_collation +class sys_var_collation_connection :public sys_var_collation { public: - sys_var_connection_collation(const char *name_arg) :sys_var_collation(name_arg) {} + sys_var_collation_connection(const char *name_arg) :sys_var_collation(name_arg) {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type); }; -class sys_var_result_collation :public sys_var_collation +class sys_var_collation_results :public sys_var_collation { public: - sys_var_result_collation(const char *name_arg) :sys_var_collation(name_arg) {} + sys_var_collation_results(const char *name_arg) :sys_var_collation(name_arg) {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type); @@ -553,18 +553,18 @@ public: /* For SET NAMES and SET CHARACTER SET */ -class set_var_client_collation: public set_var_base +class set_var_collation_client: public set_var_base { - CHARSET_INFO *client_collation; - CHARSET_INFO *connection_collation; - CHARSET_INFO *result_collation; + CHARSET_INFO *collation_client; + CHARSET_INFO *collation_connection; + CHARSET_INFO *collation_results; public: - set_var_client_collation(CHARSET_INFO *client_coll_arg, + set_var_collation_client(CHARSET_INFO *client_coll_arg, CHARSET_INFO *connection_coll_arg, CHARSET_INFO *result_coll_arg) - :client_collation(client_coll_arg), - connection_collation(connection_coll_arg), - result_collation(result_coll_arg) + :collation_client(client_coll_arg), + collation_connection(connection_coll_arg), + collation_results(result_coll_arg) {} int check(THD *thd); int update(THD *thd); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 31159dc259f..f579b02ee50 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -207,7 +207,6 @@ void THD::init(void) { pthread_mutex_lock(&LOCK_global_system_variables); variables= global_system_variables; - variables.client_collation= default_charset_info; pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; options= thd_startup_options; diff --git a/sql/sql_class.h b/sql/sql_class.h index 58f27bf2bcb..d3cb843ad85 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -382,9 +382,9 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; - CHARSET_INFO *client_collation; - CHARSET_INFO *connection_collation; - CHARSET_INFO *result_collation; + CHARSET_INFO *collation_client; + CHARSET_INFO *collation_connection; + CHARSET_INFO *collation_results; }; void free_tmp_table(THD *thd, TABLE *entry); @@ -665,7 +665,7 @@ public: net.report_error= 1; DBUG_PRINT("error",("Fatal error set")); } - inline CHARSET_INFO *charset() { return variables.client_collation; } + inline CHARSET_INFO *charset() { return variables.collation_client; } }; /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5664c46349a..71035a75084 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3924,7 +3924,7 @@ text_literal: TEXT_STRING_literal { THD *thd= YYTHD; - $$ = new Item_string($1.str,$1.length,thd->variables.connection_collation); + $$ = new Item_string($1.str,$1.length,thd->variables.collation_connection); } | NCHAR_STRING { $$= new Item_string($1.str,$1.length,national_charset_info); } @@ -3936,7 +3936,7 @@ text_literal: text_string: TEXT_STRING_literal - { $$= new String($1.str,$1.length,YYTHD->variables.connection_collation); } + { $$= new String($1.str,$1.length,YYTHD->variables.collation_connection); } | HEX_NUM { Item *tmp = new Item_varbinary($1.str,$1.length); @@ -4106,14 +4106,14 @@ TEXT_STRING_literal: TEXT_STRING { THD *thd= YYTHD; - if (my_charset_same(thd->charset(),thd->variables.connection_collation)) + if (my_charset_same(thd->charset(),thd->variables.collation_connection)) { $$=$1; } else { String ident; - ident.copy($1.str,$1.length,thd->charset(),thd->variables.connection_collation); + ident.copy($1.str,$1.length,thd->charset(),thd->variables.collation_connection); $$.str= thd->strmake(ident.ptr(),ident.length()); $$.length= ident.length(); } @@ -4405,28 +4405,27 @@ option_value: { THD *thd= YYTHD; LEX *lex= Lex; - $2= $2 ? $2: global_system_variables.client_collation; + $2= $2 ? $2: global_system_variables.collation_client; $3= $3 ? $3 : $2; if (!my_charset_same($2,$3)) { - net_printf(YYTHD,ER_COLLATION_CHARSET_MISMATCH, - $3->name,$2->csname); - YYABORT; + net_printf(thd,ER_COLLATION_CHARSET_MISMATCH,$3->name,$2->csname); + YYABORT; } - lex->var_list.push_back(new set_var_client_collation($3,thd->db_charset,$3)); + lex->var_list.push_back(new set_var_collation_client($3,thd->db_charset,$3)); } | NAMES_SYM charset_name_or_default opt_collate { + THD *thd= YYTHD; LEX *lex= Lex; - $2= $2 ? $2 : global_system_variables.client_collation; + $2= $2 ? $2 : global_system_variables.collation_client; $3= $3 ? $3 : $2; if (!my_charset_same($2,$3)) { - net_printf(YYTHD,ER_COLLATION_CHARSET_MISMATCH, - $3->name,$2->csname); - YYABORT; + net_printf(thd,ER_COLLATION_CHARSET_MISMATCH,$3->name,$2->csname); + YYABORT; } - lex->var_list.push_back(new set_var_client_collation($3,$3,$3)); + lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); } | PASSWORD equal text_or_password { From d1609043946efdc85c0e98dc760011fab74cd6e3 Mon Sep 17 00:00:00 2001 From: "bar@bar.mysql.r18.ru" <> Date: Wed, 23 Apr 2003 18:38:54 +0500 Subject: [PATCH 16/21] set_var.cc: We don't need this code any more. It was used to initialize CONVERT structure in 4.0. --- sql/set_var.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sql/set_var.cc b/sql/set_var.cc index ea0b569f05d..d39a506c82d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1215,11 +1215,7 @@ bool sys_var_collation_client::update(THD *thd, set_var *var) if (var->type == OPT_GLOBAL) global_system_variables.collation_client= var->save_result.charset; else - { thd->variables.collation_client= var->save_result.charset; - thd->protocol_simple.init(thd); - thd->protocol_prep.init(thd); - } return 0; } @@ -1236,9 +1232,7 @@ void sys_var_collation_client::set_default(THD *thd, enum_var_type type) if (type == OPT_GLOBAL) global_system_variables.collation_client= default_charset_info; else - { thd->variables.collation_client= global_system_variables.collation_client; - } } From 80615fade284e14119fc189f136d30fc475f8aec Mon Sep 17 00:00:00 2001 From: "igor@hundin.mysql.fi" <> Date: Thu, 24 Apr 2003 14:33:33 +0300 Subject: [PATCH 17/21] Many files: Added the MAX_LENGTH_FOR_SORT_DATA option filesort.cc: Added the MAX_LENGTH_FOR_SORT_DATA option --- sql/filesort.cc | 483 +++++++++++++++++++++++++++++++++------------- sql/mysqld.cc | 7 +- sql/opt_range.cc | 8 +- sql/records.cc | 101 +++++++++- sql/set_var.cc | 6 + sql/sql_base.cc | 13 +- sql/sql_class.h | 1 + sql/sql_delete.cc | 10 +- sql/sql_select.cc | 22 +-- sql/sql_sort.h | 33 +++- sql/sql_table.cc | 10 +- sql/sql_update.cc | 4 +- sql/structs.h | 1 + sql/table.h | 15 +- sql/uniques.cc | 10 +- 15 files changed, 534 insertions(+), 190 deletions(-) diff --git a/sql/filesort.cc b/sql/filesort.cc index 4c2ba1e1a59..928138b8d48 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1,4 +1,5 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,6 +52,10 @@ static int merge_index(SORTPARAM *param,uchar *sort_buffer, static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count); static uint sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset); +static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield, + uint sortlength, uint *plength); +static void unpack_addon_fields(struct st_sort_addon_field *addon_field, + byte *buff); /* Creates a set of pointers that can be used to read the rows @@ -82,16 +87,48 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_PUSH(""); /* No DBUG here */ #endif - outfile= table->io_cache; + outfile= table->sort.io_cache; my_b_clear(&tempfile); my_b_clear(&buffpek_pointers); buffpek=0; sort_keys= (uchar **) NULL; error= 1; bzero((char*) ¶m,sizeof(param)); + param.sort_length= sortlength(sortorder, s_length, &multi_byte_charset); param.ref_length= table->file->ref_length; - param.sort_length= (sortlength(sortorder,s_length, &multi_byte_charset)+ - param.ref_length); + param.addon_field= 0; + param.addon_length= 0; + if (!(table->tmp_table || table->fulltext_searched)) + { + /* + Get the descriptors of all fields whose values are appended + to sorted fields and get its total length in param.spack_length. + */ + param.addon_field= get_addon_fields(thd, table->field, + param.sort_length, + ¶m.addon_length); + } + table->sort.addon_buf= 0; + table->sort.addon_length= param.addon_length; + table->sort.addon_field= param.addon_field; + table->sort.unpack= unpack_addon_fields; + if (param.addon_field) + { + param.res_length= param.addon_length; + if (!(table->sort.addon_buf= (byte *) my_malloc(param.addon_length, + MYF(MY_WME)))) + goto err; + } + else + { + param.res_length= param.ref_length; + /* + The reference to the record is considered + as an additional sorted field + */ + param.sort_length+= param.ref_length; + } + param.rec_length= param.sort_length+param.addon_length; param.max_rows= max_rows; if (select && select->quick) @@ -115,7 +152,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, records=table->file->estimate_number_of_rows(); selected_records_file= 0; } - if (param.sort_length == param.ref_length && records > param.max_rows) + if (param.rec_length == param.ref_length && records > param.max_rows) records=param.max_rows; /* purecov: inspected */ if (multi_byte_charset && @@ -127,9 +164,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, while (memavl >= min_sort_memory) { ulong old_memavl; - ulong keys= memavl/(param.sort_length+sizeof(char*)); + ulong keys= memavl/(param.rec_length+sizeof(char*)); param.keys=(uint) min(records+1, keys); - if ((sort_keys= (uchar **) make_char_array(param.keys, param.sort_length, + if ((sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, MYF(0)))) break; old_memavl=memavl; @@ -176,8 +213,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, Use also the space previously used by string pointers in sort_buffer for temporary key storage. */ - param.keys=((param.keys*(param.sort_length+sizeof(char*))) / - param.sort_length-1); + param.keys=((param.keys*(param.rec_length+sizeof(char*))) / + param.rec_length-1); maxbuffer--; // Offset from 0 if (merge_many_buff(¶m,(uchar*) sort_keys,buffpek,&maxbuffer, &tempfile)) @@ -356,8 +393,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) DBUG_RETURN(HA_POS_ERROR); idx=0; - if (param->ref_length == param->sort_length && - my_b_tell(tempfile)/param->sort_length >= param->max_rows) + if (param->ref_length == param->rec_length && + my_b_tell(tempfile)/param->rec_length >= param->max_rows) { /* We are writing the result index file and have found all @@ -385,7 +422,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ DBUG_RETURN(my_b_inited(tempfile) ? - (ha_rows) (my_b_tell(tempfile)/param->sort_length) : + (ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx); } /* find_all_keys */ @@ -394,29 +431,30 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, - IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) { - uint sort_length; + uint sort_length, rec_length; uchar **end; BUFFPEK buffpek; DBUG_ENTER("write_keys"); - sort_length=param->sort_length; + sort_length= param->sort_length; + rec_length= param->rec_length; #ifdef MC68000 quicksort(sort_keys,count,sort_length); #else - my_string_ptr_sort((gptr) sort_keys,(uint) count,sort_length); + my_string_ptr_sort((gptr) sort_keys, (uint) count, sort_length); #endif if (!my_b_inited(tempfile) && - open_cached_file(tempfile,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE, - MYF(MY_WME))) - goto err; /* purecov: inspected */ - buffpek.file_pos=my_b_tell(tempfile); + open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE, + MYF(MY_WME))) + goto err; /* purecov: inspected */ + buffpek.file_pos= my_b_tell(tempfile); if ((ha_rows) count > param->max_rows) - count=(uint) param->max_rows; /* purecov: inspected */ + count=(uint) param->max_rows; /* purecov: inspected */ buffpek.count=(ha_rows) count; for (end=sort_keys+count ; sort_keys != end ; sort_keys++) - if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length)) + if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length)) goto err; if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek))) goto err; @@ -505,10 +543,10 @@ static void make_sortkey(register SORTPARAM *param, } else { - my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); - bzero((char *)to+length,diff); + my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); + bzero((char *)to+length,diff); } - break; + break; } case INT_RESULT: { @@ -577,29 +615,56 @@ static void make_sortkey(register SORTPARAM *param, else to+= sort_field->length; } - memcpy((byte*) to,ref_pos,(size_s) param->ref_length);/* Save filepos last */ + + if (param->addon_field) + { + /* + Save field values appended to sorted fields. + First null bit indicators are appended then field values follow. + In this implementation we use fixed layout for field values - + the same for all records. + */ + SORT_ADDON_FIELD *addonf= param->addon_field; + uchar *nulls= to; + DBUG_ASSERT(addonf); + bzero((char *) nulls, addonf->offset); + to+= addonf->offset; + for ( ; (field= addonf->field) ; addonf++) + { + if (addonf->null_bit && field->is_null()) + nulls[addonf->null_offset]|= addonf->null_bit; + else + field->pack((char *) to, field->ptr); + to+= addonf->length; + } + } + else + { + /* Save filepos last */ + memcpy((byte*) to, ref_pos, (size_s) param->ref_length); + } return; } static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count) { - uint offset,ref_length; + uint offset,res_length; byte *to; DBUG_ENTER("save_index"); - my_string_ptr_sort((gptr) sort_keys,(uint) count,param->sort_length); - ref_length=param->ref_length; - offset=param->sort_length-ref_length; + my_string_ptr_sort((gptr) sort_keys, (uint) count, param->sort_length); + res_length= param->res_length; + offset= param->rec_length-res_length; if ((ha_rows) count > param->max_rows) count=(uint) param->max_rows; - if (!(to=param->sort_form->record_pointers= - (byte*) my_malloc(ref_length*count,MYF(MY_WME)))) - DBUG_RETURN(1); /* purecov: inspected */ - for (uchar **end=sort_keys+count ; sort_keys != end ; sort_keys++) + if (!(to= param->sort_form->sort.record_pointers= + (byte*) my_malloc(res_length*count, MYF(MY_WME)))) + DBUG_RETURN(1); /* purecov: inspected */ + for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++) { - memcpy(to,*sort_keys+offset,ref_length); - to+=ref_length; + memcpy(to, *sort_keys+offset, res_length); + to+= res_length; } DBUG_RETURN(0); } @@ -654,7 +719,7 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, /* This returns (uint) -1 if something goes wrong */ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, - uint sort_length) + uint rec_length) { register uint count; uint length; @@ -662,33 +727,35 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count))) { if (my_pread(fromfile->file,(byte*) buffpek->base, - (length= sort_length*count),buffpek->file_pos,MYF_RW)) + (length= rec_length*count),buffpek->file_pos,MYF_RW)) return((uint) -1); /* purecov: inspected */ buffpek->key=buffpek->base; buffpek->file_pos+= length; /* New filepos */ buffpek->count-= count; buffpek->mem_count= count; } - return (count*sort_length); + return (count*rec_length); } /* read_to_buffer */ - /* Merge buffers to one buffer */ +/* + Merge buffers to one buffer +*/ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, - IO_CACHE *to_file, uchar *sort_buffer, - BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, - int flag) + IO_CACHE *to_file, uchar *sort_buffer, + BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, + int flag) { int error; - uint sort_length,offset; + uint rec_length,sort_length,res_length,offset; ulong maxcount; ha_rows max_rows,org_max_rows; my_off_t to_start_filepos; uchar *strpos; BUFFPEK *buffpek,**refpek; QUEUE queue; - qsort2_cmp cmp; + qsort2_cmp cmp; volatile bool *killed= ¤t_thd->killed; bool not_killable; DBUG_ENTER("merge_buffers"); @@ -697,29 +764,32 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, if (param->not_killable) { killed= ¬_killable; - not_killable=0; + not_killable= 0; } error=0; - offset=(sort_length=param->sort_length)-param->ref_length; - maxcount=(ulong) (param->keys/((uint) (Tb-Fb) +1)); - to_start_filepos=my_b_tell(to_file); - strpos=(uchar*) sort_buffer; - org_max_rows=max_rows=param->max_rows; + rec_length= param->rec_length; + res_length= param->res_length; + sort_length= param->sort_length; + offset= rec_length-res_length; + maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1)); + to_start_filepos= my_b_tell(to_file); + strpos= (uchar*) sort_buffer; + org_max_rows=max_rows= param->max_rows; - if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0, - (queue_compare) - (cmp=get_ptr_compare(sort_length)),(void*) &sort_length)) - DBUG_RETURN(1); /* purecov: inspected */ + if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0, + (queue_compare) (cmp= get_ptr_compare(sort_length)), + (void*) &sort_length)) + DBUG_RETURN(1); /* purecov: inspected */ for (buffpek= Fb ; buffpek <= Tb ; buffpek++) { buffpek->base= strpos; - buffpek->max_keys=maxcount; - strpos+= (uint) (error=(int) read_to_buffer(from_file,buffpek, - sort_length)); + buffpek->max_keys= maxcount; + strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek, + rec_length)); if (error == -1) - goto err; /* purecov: inspected */ - queue_insert(&queue,(byte*) buffpek); + goto err; /* purecov: inspected */ + queue_insert(&queue, (byte*) buffpek); } if (param->unique_buff) @@ -732,98 +802,101 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, This is safe as we know that there is always more than one element in each block to merge (This is guaranteed by the Unique:: algorithm */ - buffpek=(BUFFPEK*) queue_top(&queue); - memcpy(param->unique_buff, buffpek->key, sort_length); - if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) + buffpek= (BUFFPEK*) queue_top(&queue); + memcpy(param->unique_buff, buffpek->key, rec_length); + if (my_b_write(to_file, (byte*) buffpek->key, rec_length)) { - error=1; goto err; /* purecov: inspected */ + error=1; goto err; /* purecov: inspected */ } - buffpek->key+=sort_length; + buffpek->key+= rec_length; buffpek->mem_count--; if (!--max_rows) { - error=0; /* purecov: inspected */ - goto end; /* purecov: inspected */ + error= 0; /* purecov: inspected */ + goto end; /* purecov: inspected */ } - queue_replaced(&queue); // Top element has been used + queue_replaced(&queue); // Top element has been used } else - cmp=0; // Not unique + cmp= 0; // Not unique while (queue.elements > 1) { if (*killed) { - error=1; goto err; /* purecov: inspected */ + error= 1; goto err; /* purecov: inspected */ } for (;;) { - buffpek=(BUFFPEK*) queue_top(&queue); - if (cmp) // Remove duplicates + buffpek= (BUFFPEK*) queue_top(&queue); + if (cmp) // Remove duplicates { - if (!(*cmp)(&sort_length, &(param->unique_buff), - (uchar**) &buffpek->key)) - goto skip_duplicate; - memcpy(param->unique_buff, (uchar*) buffpek->key,sort_length); + if (!(*cmp)(&sort_length, &(param->unique_buff), + (uchar**) &buffpek->key)) + goto skip_duplicate; + memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length); } if (flag == 0) { - if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) - { - error=1; goto err; /* purecov: inspected */ - } + if (my_b_write(to_file,(byte*) buffpek->key, rec_length)) + { + error=1; goto err; /* purecov: inspected */ + } } else { - WRITE_REF(to_file,(byte*) buffpek->key+offset); + if (my_b_write(to_file, (byte*) buffpek->key+offset, res_length)) + { + error=1; goto err; /* purecov: inspected */ + } } if (!--max_rows) { - error=0; /* purecov: inspected */ - goto end; /* purecov: inspected */ + error= 0; /* purecov: inspected */ + goto end; /* purecov: inspected */ } skip_duplicate: - buffpek->key+=sort_length; + buffpek->key+= rec_length; if (! --buffpek->mem_count) { - if (!(error=(int) read_to_buffer(from_file,buffpek, - sort_length))) - { - uchar *base=buffpek->base; - ulong max_keys=buffpek->max_keys; + if (!(error= (int) read_to_buffer(from_file,buffpek, + rec_length))) + { + uchar *base= buffpek->base; + ulong max_keys= buffpek->max_keys; - VOID(queue_remove(&queue,0)); + VOID(queue_remove(&queue,0)); - /* Put room used by buffer to use in other buffer */ - for (refpek= (BUFFPEK**) &queue_top(&queue); - refpek <= (BUFFPEK**) &queue_end(&queue); - refpek++) - { - buffpek= *refpek; - if (buffpek->base+buffpek->max_keys*sort_length == base) - { - buffpek->max_keys+=max_keys; - break; - } - else if (base+max_keys*sort_length == buffpek->base) - { - buffpek->base=base; - buffpek->max_keys+=max_keys; - break; - } - } - break; /* One buffer have been removed */ - } - else if (error == -1) - goto err; /* purecov: inspected */ + /* Put room used by buffer to use in other buffer */ + for (refpek= (BUFFPEK**) &queue_top(&queue); + refpek <= (BUFFPEK**) &queue_end(&queue); + refpek++) + { + buffpek= *refpek; + if (buffpek->base+buffpek->max_keys*rec_length == base) + { + buffpek->max_keys+= max_keys; + break; + } + else if (base+max_keys*rec_length == buffpek->base) + { + buffpek->base= base; + buffpek->max_keys+= max_keys; + break; + } + } + break; /* One buffer have been removed */ + } + else if (error == -1) + goto err; /* purecov: inspected */ } - queue_replaced(&queue); /* Top element has been replaced */ + queue_replaced(&queue); /* Top element has been replaced */ } } - buffpek=(BUFFPEK*) queue_top(&queue); + buffpek= (BUFFPEK*) queue_top(&queue); buffpek->base= sort_buffer; - buffpek->max_keys=param->keys; + buffpek->max_keys= param->keys; /* As we know all entries in the buffer are unique, we only have to @@ -833,7 +906,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, { if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key)) { - buffpek->key+=sort_length; // Remove duplicate + buffpek->key+= rec_length; // Remove duplicate --buffpek->mem_count; } } @@ -841,37 +914,40 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, do { if ((ha_rows) buffpek->mem_count > max_rows) - { /* Don't write too many records */ - buffpek->mem_count=(uint) max_rows; - buffpek->count=0; /* Don't read more */ + { /* Don't write too many records */ + buffpek->mem_count= (uint) max_rows; + buffpek->count= 0; /* Don't read more */ } - max_rows-=buffpek->mem_count; + max_rows-= buffpek->mem_count; if (flag == 0) { if (my_b_write(to_file,(byte*) buffpek->key, - (sort_length*buffpek->mem_count))) + (rec_length*buffpek->mem_count))) { - error=1; goto err; /* purecov: inspected */ + error= 1; goto err; /* purecov: inspected */ } } else { register uchar *end; strpos= buffpek->key+offset; - for (end=strpos+buffpek->mem_count*sort_length; - strpos != end ; - strpos+=sort_length) - { - WRITE_REF(to_file,strpos); + for (end= strpos+buffpek->mem_count*rec_length ; + strpos != end ; + strpos+= rec_length) + { + if (my_b_write(to_file, (byte *) strpos, res_length)) + { + error=1; goto err; + } } } } - while ((error=(int) read_to_buffer(from_file,buffpek,sort_length)) - != -1 && error != 0); + while ((error=(int) read_to_buffer(from_file,buffpek, rec_length)) + != -1 && error != 0); end: - lastbuff->count=min(org_max_rows-max_rows,param->max_rows); - lastbuff->file_pos=to_start_filepos; + lastbuff->count= min(org_max_rows-max_rows, param->max_rows); + lastbuff->file_pos= to_start_filepos; err: delete_queue(&queue); DBUG_RETURN(error); @@ -925,7 +1001,6 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) sortorder->need_strxnfrm= 0; if (sortorder->field) { - if (sortorder->field->type() == FIELD_TYPE_BLOB) sortorder->length= thd->variables.max_sort_length; else @@ -947,7 +1022,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) case STRING_RESULT: sortorder->length=sortorder->item->max_length; if (use_strnxfrm((cs=sortorder->item->charset()))) - { + { sortorder->length= sortorder->length*cs->strxfrm_multiply; sortorder->need_strxnfrm= 1; *multi_byte_charset= 1; @@ -981,6 +1056,148 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) } +/* + Get descriptors of fields appended to sorted fields and + calculate its total length + + SYNOPSIS + get_addon_fields() + thd Current thread + ptabfields Array of references to the table fields + sortlength Total length of sorted fields + plength out: Total length of appended fields + + DESCRIPTION + The function first finds out what fields are used in the result set. + Then it calculates the length of the buffer to store the values of + these fields together with the value of sort values. + If the calculated length is not greater than max_length_for_sort_data + the function allocates memory for an array of descriptors containing + layouts for the values of the non-sorted fields in the buffer and + fills them. + + NOTES + The null bits for the appended values are supposed to be put together + and stored the buffer just ahead of the value of the first field. + + RETURN + Pointer to the layout descriptors for the appended fields, if any + NULL - if we do not store field values with sort data. +*/ + +static SORT_ADDON_FIELD * +get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) +{ + Field **pfield; + Field *field; + SORT_ADDON_FIELD *addonf; + uint length= 0; + uint fields= 0; + uint null_fields= 0; + + /* + If there is a reference to a field in the query add it + to the the set of appended fields. + Note for future refinement: + This this a too strong condition. + Actually we need only the fields referred in the + result set. And for some of them it makes sense to use + the values directly from sorted fields. + */ + *plength= 0; + /* + The following statement is added to avoid sorting in alter_table. + The fact is the filter 'field->query_id != thd->query_id' + doesn't work for alter table + */ + if (thd->lex.sql_command != SQLCOM_SELECT) + return 0; + for (pfield= ptabfield; (field= *pfield) ; pfield++) + { + if (field->query_id != thd->query_id) + continue; + if (field->flags & BLOB_FLAG) + return 0; + length+= field->max_packed_col_length(field->pack_length()); + if (field->maybe_null()) + null_fields++; + fields++; + } + if (!fields) + return 0; + length+= (null_fields+7)/8; + + if (length+sortlength > thd->variables.max_length_for_sort_data || + !(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)* + (fields+1), MYF(MY_WME)))) + return 0; + + *plength= length; + length= (null_fields+7)/8; + null_fields= 0; + for (pfield= ptabfield; (field= *pfield) ; pfield++) + { + if (field->query_id != thd->query_id) + continue; + addonf->field= field; + addonf->offset= length; + if (field->maybe_null()) + { + addonf->null_offset= null_fields/8; + addonf->null_bit= 1<<(null_fields & 7); + null_fields++; + } + else + { + addonf->null_offset= 0; + addonf->null_bit= 0; + } + addonf->length= field->max_packed_col_length(field->pack_length()); + length+= addonf->length; + addonf++; + } + addonf->field= 0; // Put end marker + + DBUG_PRINT("info",("addon_length: %d",length)); + return (addonf-fields); +} + + +/* + Copy (unpack) values appended to sorted fields from a buffer back to + their regular positions specified by the Field::ptr pointers. + + SYNOPSIS + unpack_addon_fields() + addon_field Array of descriptors for appended fields + buff Buffer which to unpack the value from + + NOTES + The function is supposed to be used only as a callback function + when getting field values for the sorted result set. + + RETURN + void. +*/ + +static void +unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff) +{ + Field *field; + SORT_ADDON_FIELD *addonf= addon_field; + + for ( ; (field= addonf->field) ; addonf++) + { + if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset])) + { + field->set_null(); + continue; + } + field->set_notnull(); + field->unpack(field->ptr, (char *) buff+addonf->offset); + } +} + /* ** functions to change a double or float to a sortable string ** The following should work for IEEE diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 47c9c3f8331..ecbc801a463 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3457,7 +3457,7 @@ enum options OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, - OPT_MAX_JOIN_SIZE, OPT_MAX_SORT_LENGTH, + OPT_MAX_JOIN_SIZE, OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_SORT_LENGTH, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, OPT_MAX_ERROR_COUNT, OPT_MAX_PREP_STMT, @@ -4159,6 +4159,11 @@ struct my_option my_long_options[] = (gptr*) &global_system_variables.max_join_size, (gptr*) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA, + "Max number of bytes in sorted records", + (gptr*) &global_system_variables.max_length_for_sort_data, + (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, + REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, {"max_prepared_statements", OPT_MAX_PREP_STMT, "Max number of prepared_statements for a thread", (gptr*) &global_system_variables.max_prep_stmt_count, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 92bab76bedd..7526772cd09 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -349,13 +349,13 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, select->head=head; select->cond=conds; - if (head->io_cache) + if (head->sort.io_cache) { - select->file= *head->io_cache; + select->file= *head->sort.io_cache; select->records=(ha_rows) (select->file.end_of_file/ head->file->ref_length); - my_free((gptr) (head->io_cache),MYF(0)); - head->io_cache=0; + my_free((gptr) (head->sort.io_cache),MYF(0)); + head->sort.io_cache=0; } DBUG_RETURN(select); } diff --git a/sql/records.cc b/sql/records.cc index 22c4d54550c..e6c6e62a516 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -1,3 +1,4 @@ + /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify @@ -22,6 +23,8 @@ static int rr_quick(READ_RECORD *info); static int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); +static int rr_unpack_from_tempfile(READ_RECORD *info); +static int rr_unpack_from_buffer(READ_RECORD *info); static int rr_from_pointers(READ_RECORD *info); static int rr_from_cache(READ_RECORD *info); static int init_rr_cache(READ_RECORD *info); @@ -41,8 +44,16 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->table=table; info->file= table->file; info->forms= &info->table; /* Only one table */ - info->record=table->record[0]; - info->ref_length=table->file->ref_length; + if (table->sort.addon_field) + { + info->rec_buf= table->sort.addon_buf; + info->ref_length= table->sort.addon_length; + } + else + { + info->record= table->record[0]; + info->ref_length= table->file->ref_length; + } info->select=select; info->print_error=print_error; info->ignore_not_found_rows= 0; @@ -51,11 +62,12 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (select && my_b_inited(&select->file)) tempfile= &select->file; else - tempfile= table->io_cache; + tempfile= table->sort.io_cache; if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used { DBUG_PRINT("info",("using rr_from_tempfile")); - info->read_record=rr_from_tempfile; + info->read_record= (table->sort.addon_field ? + rr_unpack_from_tempfile : rr_from_tempfile); info->io_cache=tempfile; reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0); info->ref_pos=table->file->ref; @@ -85,13 +97,15 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, DBUG_PRINT("info",("using rr_quick")); info->read_record=rr_quick; } - else if (table->record_pointers) + else if (table->sort.record_pointers) { DBUG_PRINT("info",("using record_pointers")); table->file->rnd_init(0); - info->cache_pos=table->record_pointers; - info->cache_end=info->cache_pos+ table->found_records*info->ref_length; - info->read_record= rr_from_pointers; + info->cache_pos=table->sort.record_pointers; + info->cache_end=info->cache_pos+ + table->sort.found_records*info->ref_length; + info->read_record= (table->sort.addon_field ? + rr_unpack_from_buffer : rr_from_pointers); } else { @@ -112,7 +126,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, void end_read_record(READ_RECORD *info) -{ /* free cache if used */ +{ /* free cache if used */ if (info->cache) { my_free_lock((char*) info->cache,MYF(0)); @@ -120,6 +134,19 @@ void end_read_record(READ_RECORD *info) } if (info->table) { + TABLE *table= info->table; + if (table->sort.record_pointers) + { + my_free((gptr) table->sort.record_pointers,MYF(0)); + table->sort.record_pointers=0; + } + if (table->sort.addon_buf) + { + my_free((char *) table->sort.addon_buf, MYF(0)); + my_free((char *) table->sort.addon_field, MYF(MY_ALLOW_ZERO_PTR)); + table->sort.addon_buf=0; + table->sort.addon_field=0; + } (void) info->file->extra(HA_EXTRA_NO_CACHE); (void) info->file->rnd_end(); info->table=0; @@ -200,6 +227,34 @@ tryNext: } /* rr_from_tempfile */ +/* + Read a result set record from a temporary file after sorting + + SYNOPSIS + rr_unpack_from_tempfile() + info Reference to the context including record descriptors + + DESCRIPTION + The function first reads the next sorted record from the temporary file. + into a buffer. If a success it calls a callback function that unpacks + the fields values use in the result set from this buffer into their + positions in the regular record buffer. + + RETURN + 0 - Record successfully read. + -1 - There is no record to be read anymore. +*/ + +static int rr_unpack_from_tempfile(READ_RECORD *info) +{ + if (my_b_read(info->io_cache, info->rec_buf, info->ref_length)) + return -1; + TABLE *table= info->table; + (*table->sort.unpack)(table->sort.addon_field, info->rec_buf); + + return 0; +} + static int rr_from_pointers(READ_RECORD *info) { int tmp; @@ -228,6 +283,34 @@ tryNext: return tmp; } +/* + Read a result set record from a buffer after sorting + + SYNOPSIS + rr_unpack_from_buffer() + info Reference to the context including record descriptors + + DESCRIPTION + The function first reads the next sorted record from the sort buffer. + If a success it calls a callback function that unpacks + the fields values use in the result set from this buffer into their + positions in the regular record buffer. + + RETURN + 0 - Record successfully read. + -1 - There is no record to be read anymore. +*/ + +static int rr_unpack_from_buffer(READ_RECORD *info) +{ + if (info->cache_pos == info->cache_end) + return -1; /* End of buffer */ + TABLE *table= info->table; + (*table->sort.unpack)(table->sort.addon_field, info->cache_pos); + info->cache_pos+= info->ref_length; + + return 0; +} /* cacheing of records from a database */ static int init_rr_cache(READ_RECORD *info) diff --git a/sql/set_var.cc b/sql/set_var.cc index d03b91ef83b..efe0fcff25d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -166,6 +166,8 @@ sys_var_thd_ulong sys_pseudo_thread_id("pseudo_thread_id", sys_var_thd_ha_rows sys_max_join_size("max_join_size", &SV::max_join_size, fix_max_join_size); +sys_var_thd_ulong sys_max_length_for_sort_data("max_length_for_sort_data", + &SV::max_length_for_sort_data); #ifndef TO_BE_DELETED /* Alias for max_join_size */ sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size", &SV::max_join_size, @@ -380,6 +382,7 @@ sys_var *sys_variables[]= &sys_max_error_count, &sys_max_heap_table_size, &sys_max_join_size, + &sys_max_length_for_sort_data, &sys_max_prep_stmt_count, &sys_max_sort_length, &sys_max_tmp_tables, @@ -533,6 +536,9 @@ struct show_var_st init_vars[]= { {sys_max_delayed_threads.name,(char*) &sys_max_delayed_threads, SHOW_SYS}, {sys_max_heap_table_size.name,(char*) &sys_max_heap_table_size, SHOW_SYS}, {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, + {sys_max_length_for_sort_data.name, + (char*) &sys_max_length_for_sort_data, + SHOW_SYS}, {sys_max_prep_stmt_count.name,(char*) &sys_max_prep_stmt_count, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2063e8b3f08..088a1fa630c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -245,16 +245,11 @@ static void free_cache_entry(TABLE *table) void free_io_cache(TABLE *table) { DBUG_ENTER("free_io_cache"); - if (table->io_cache) + if (table->sort.io_cache) { - close_cached_file(table->io_cache); - my_free((gptr) table->io_cache,MYF(0)); - table->io_cache=0; - } - if (table->record_pointers) - { - my_free((gptr) table->record_pointers,MYF(0)); - table->record_pointers=0; + close_cached_file(table->sort.io_cache); + my_free((gptr) table->sort.io_cache,MYF(0)); + table->sort.io_cache=0; } DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 4846f5fe9fa..85548b4dff8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -352,6 +352,7 @@ struct system_variables ulong max_allowed_packet; ulong max_error_count; ulong max_heap_table_size; + ulong max_length_for_sort_data; ulong max_prep_stmt_count; ulong max_sort_length; ulong max_tmp_tables; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 05f84616a4c..1a143b061e0 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -124,13 +124,13 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, bzero((char*) &tables,sizeof(tables)); tables.table = table; - table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), - MYF(MY_FAE | MY_ZEROFILL)); + table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); if (setup_order(thd, 0, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (table->found_records = filesort(thd, table, sortorder, length, - (SQL_SELECT *) 0, HA_POS_ERROR, - &examined_rows)) + (table->sort.found_records = filesort(thd, table, sortorder, length, + (SQL_SELECT *) 0, HA_POS_ERROR, + &examined_rows)) == HA_POS_ERROR) { delete select; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3fdff4e96a4..d860b5c5bdf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4009,7 +4009,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item_sum::AVG_FUNC: /* Place for sum & count */ if (group) return new Field_string(sizeof(double)+sizeof(longlong), - maybe_null, item->name,table,&my_charset_bin); + 0, item->name,table,&my_charset_bin); else return new Field_double(item_sum->max_length,maybe_null, item->name, table, item_sum->decimals); @@ -4017,7 +4017,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item_sum::STD_FUNC: if (group) return new Field_string(sizeof(double)*2+sizeof(longlong), - maybe_null, item->name,table,&my_charset_bin); + 0, item->name,table,&my_charset_bin); else return new Field_double(item_sum->max_length, maybe_null, item->name,table,item_sum->decimals); @@ -5621,11 +5621,11 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), TABLE *table=jt->table; join->select_options ^= OPTION_FOUND_ROWS; - if (table->record_pointers || - (table->io_cache && my_b_inited(table->io_cache))) + if (table->sort.record_pointers || + (table->sort.io_cache && my_b_inited(table->sort.io_cache))) { /* Using filesort */ - join->send_records= table->found_records; + join->send_records= table->sort.found_records; } else { @@ -6460,8 +6460,8 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, if (!(sortorder=make_unireg_sortorder(order,&length))) goto err; /* purecov: inspected */ /* It's not fatal if the following alloc fails */ - table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_WME | MY_ZEROFILL)); + table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_WME | MY_ZEROFILL)); table->status=0; // May be wrong if quick_select // If table has a range, move it to select @@ -6490,9 +6490,9 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, } if (table->tmp_table) table->file->info(HA_STATUS_VARIABLE); // Get record count - table->found_records=filesort(thd, table,sortorder, length, - select, filesort_limit, &examined_rows); - tab->records=table->found_records; // For SQL_CALC_ROWS + table->sort.found_records=filesort(thd, table,sortorder, length, + select, filesort_limit, &examined_rows); + tab->records=table->sort.found_records; // For SQL_CALC_ROWS delete select; // filesort did select tab->select=0; tab->select_cond=0; @@ -6504,7 +6504,7 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, table->key_read=0; table->file->extra(HA_EXTRA_NO_KEYREAD); } - DBUG_RETURN(table->found_records == HA_POS_ERROR); + DBUG_RETURN(table->sort.found_records == HA_POS_ERROR); err: DBUG_RETURN(-1); } diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 14463a67a28..9f95ffa4884 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -19,6 +19,30 @@ #define MERGEBUFF 7 #define MERGEBUFF2 15 +/* + The structure SORT_ADDON_FIELD describes a fixed layout + for field values appended to sorted values in records to be sorted + in the sort buffer. + Only fixed layout is supported now. + Null bit maps for the appended values is placed before the values + themselves. Offsets are from the last sorted field, that is from the + record referefence, which is still last component of sorted records. + It is preserved for backward compatiblility. + The structure is used tp store values of the additional fields + in the sort buffer. It is used also when these values are read + from a temporary file/buffer. As the reading procedures are beyond the + scope of the 'filesort' code the values have to be retrieved via + the callback function 'unpack_addon_fields'. +*/ + +typedef struct st_sort_addon_field { /* Sort addon packed field */ + Field *field; /* Original field */ + uint offset; /* Offset from the last sorted field */ + uint null_offset; /* Offset to to null bit from the last sorted field */ + uint length; /* Length in the sort buffer */ + uint8 null_bit; /* Null bit mask for the field */ +} SORT_ADDON_FIELD; + typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ my_off_t file_pos; /* Where we are in the sort file */ uchar *base,*key; /* key pointers */ @@ -27,15 +51,18 @@ typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ ulong max_keys; /* Max keys in buffert */ } BUFFPEK; - typedef struct st_sort_param { - uint sort_length; /* Length of sort columns */ - uint keys; /* Max keys / buffert */ + uint rec_length; /* Length of sorted records */ + uint sort_length; /* Length of sorted columns */ uint ref_length; /* Length of record ref. */ + uint addon_length; /* Length of added packed fields */ + uint res_length; /* Length of records in final sorted file/buffer */ + uint keys; /* Max keys / buffer */ ha_rows max_rows,examined_rows; TABLE *sort_form; /* For quicker make_sortkey */ SORT_FIELD *local_sortorder; SORT_FIELD *end; + SORT_ADDON_FIELD *addon_field; /* Descriptors for companion fields */ uchar *unique_buff; bool not_killable; char* tmp_buffer; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f4bcd6bd684..a4e526d403f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2342,8 +2342,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (order) { - from->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_FAE | MY_ZEROFILL)); + from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); bzero((char*) &tables,sizeof(tables)); tables.table = from; tables.alias = tables.real_name= from->real_name; @@ -2355,9 +2355,9 @@ copy_data_between_tables(TABLE *from,TABLE *to, setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (from->found_records = filesort(thd, from, sortorder, length, - (SQL_SELECT *) 0, HA_POS_ERROR, - &examined_rows)) + (from->sort.found_records = filesort(thd, from, sortorder, length, + (SQL_SELECT *) 0, HA_POS_ERROR, + &examined_rows)) == HA_POS_ERROR) goto err; }; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3e72f79da62..50fdfac7087 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -201,14 +201,14 @@ int mysql_update(THD *thd, bzero((char*) &tables,sizeof(tables)); tables.table = table; - table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); if (setup_ref_array(thd, &thd->lex.select_lex.ref_pointer_array, order_num)|| setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (table->found_records = filesort(thd, table, sortorder, length, + (table->sort.found_records = filesort(thd, table, sortorder, length, (SQL_SELECT *) 0, HA_POS_ERROR, &examined_rows)) == HA_POS_ERROR) diff --git a/sql/structs.h b/sql/structs.h index 77c852673d5..05ebdba7a37 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -104,6 +104,7 @@ typedef struct st_read_record { /* Parameter to read_record */ uint index; byte *ref_pos; /* pointer to form->refpos */ byte *record; + byte *rec_buf; /* to read field values after filesort */ byte *cache,*cache_pos,*cache_end,*read_positions; IO_CACHE *io_cache; bool print_error, ignore_not_found_rows; diff --git a/sql/table.h b/sql/table.h index 33e2db98d5a..55bc48db604 100644 --- a/sql/table.h +++ b/sql/table.h @@ -44,6 +44,17 @@ typedef struct st_grant_info enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2}; +typedef struct st_filesort_info +{ + IO_CACHE *io_cache; /* If sorted through filebyte */ + byte *addon_buf; /* Pointer to a buffer if sorted with fields */ + uint addon_length; /* Length of the buffer */ + struct st_sort_addon_field *addon_field; /* Pointer to the fields info */ + void (*unpack)(struct st_sort_addon_field *, byte *); /* To unpack back */ + byte *record_pointers; /* If sorted in memory */ + ha_rows found_records; /* How many records in sort */ +} FILESORT_INFO; + /* Table cache entry struct */ class Field_timestamp; @@ -120,9 +131,7 @@ struct st_table { table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; - IO_CACHE *io_cache; /* If sorted trough filebyte */ - byte *record_pointers; /* If sorted in memory */ - ha_rows found_records; /* How many records in sort */ + FILESORT_INFO sort; ORDER *group; ha_rows quick_rows[MAX_KEY]; uint quick_key_parts[MAX_KEY]; diff --git a/sql/uniques.cc b/sql/uniques.cc index ed256a4b791..c6fb0f25643 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -95,12 +95,12 @@ bool Unique::flush() bool Unique::get(TABLE *table) { SORTPARAM sort_param; - table->found_records=elements+tree.elements_in_tree; + table->sort.found_records=elements+tree.elements_in_tree; if (my_b_tell(&file) == 0) { /* Whole tree is in memory; Don't use disk if you don't need to */ - if ((record_pointers=table->record_pointers= (byte*) + if ((record_pointers=table->sort.record_pointers= (byte*) my_malloc(tree.size_of_element * tree.elements_in_tree, MYF(0)))) { (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, @@ -112,7 +112,7 @@ bool Unique::get(TABLE *table) if (flush()) return 1; - IO_CACHE *outfile=table->io_cache; + IO_CACHE *outfile=table->sort.io_cache; BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer; uint maxbuffer= file_ptrs.elements - 1; uchar *sort_buffer; @@ -120,8 +120,8 @@ bool Unique::get(TABLE *table) bool error=1; /* Open cached file if it isn't open */ - outfile=table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_ZEROFILL)); + outfile=table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_ZEROFILL)); if (!outfile || ! my_b_inited(outfile) && open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, From 4b78926bb372aa3f4a5c2a3575129453c9860c41 Mon Sep 17 00:00:00 2001 From: "igor@hundin.mysql.fi" <> Date: Thu, 24 Apr 2003 14:49:01 +0300 Subject: [PATCH 18/21] func_gconcat.result, ctype_many.result: Added the MAX_LENGTH_FOR_SORT_DATA option ctype_collate.result: Added the MAX_LENGTH_FOR_SORT_DATA option --- mysql-test/r/ctype_collate.result | 84 +++++++++++++++---------------- mysql-test/r/ctype_many.result | 2 +- mysql-test/r/func_gconcat.result | 2 +- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/mysql-test/r/ctype_collate.result b/mysql-test/r/ctype_collate.result index af231bb7930..48907957d1a 100644 --- a/mysql-test/r/ctype_collate.result +++ b/mysql-test/r/ctype_collate.result @@ -40,26 +40,26 @@ A a AD ad -AE ae +AE AF af -B b +B SS ss -U u +U UE ue -Ü -ü Y y +ü +Ü Z z -Å å +Å Ä ä ß @@ -69,26 +69,26 @@ A a AD ad -AE ae +AE AF af -B b +B SS ss -U u +U UE ue -Ü -ü Y y +ü +Ü Z z -Å å +Å Ä ä ß @@ -100,23 +100,23 @@ a å AD ad -AE -ae Ä +ae +AE ä -AF af -B +AF b -SS -ss +B ß +ss +SS U u -UE ue -Ü +UE ü +Ü Y y Z @@ -129,23 +129,23 @@ AD ad AE ae -AF af +AF Ä ä Å å -B b -SS +B ss +SS ß U u UE ue -Ü ü +Ü Y y Z @@ -187,26 +187,26 @@ A a AD ad -AE ae +AE AF af -B b +B SS ss -U u +U UE ue -Ü -ü Y y +ü +Ü Z z -Å å +Å Ä ä ß @@ -218,23 +218,23 @@ a å AD ad -AE -ae Ä +ae +AE ä -AF af -B +AF b -SS -ss +B ß +ss +SS U u -UE ue -Ü +UE ü +Ü Y y Z @@ -247,23 +247,23 @@ AD ad AE ae -AF af +AF Ä ä Å å -B b -SS +B ss +SS ß U u UE ue -Ü ü +Ü Y y Z diff --git a/mysql-test/r/ctype_many.result b/mysql-test/r/ctype_many.result index d6b6429d021..a1a1bd75db6 100644 --- a/mysql-test/r/ctype_many.result +++ b/mysql-test/r/ctype_many.result @@ -753,10 +753,10 @@ lower(utf8_f) Ú Ö Ö -Å £ Å £ +Å Ä Ä Ç diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 0136f72a817..0dc84f090f1 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -146,7 +146,7 @@ select grp,group_concat(c) from t1 group by grp; grp group_concat(c) 1 NULL 2 b -3 E,D,D +3 D,D,E 4 5 NULL Warnings: From b03ec0d7db3dbdc2a4c02bcdfb0522275b5fe492 Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Thu, 24 Apr 2003 15:29:25 +0200 Subject: [PATCH 19/21] Replication: new code to not modify in-memory log positions until the COMMIT is executed, even if the transaction spans on >=2 relay logs (bug #53). New variable relay_log_purge =0|1 New test to verify bug #53 --- mysql-test/r/rpl_relayrotate.result | 18 +++ mysql-test/t/rpl_relayrotate-slave.opt | 4 + mysql-test/t/rpl_relayrotate.test | 61 ++++++++ sql/log.cc | 167 ++++++++++++-------- sql/log_event.cc | 80 ++++++---- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 56 +++++-- sql/repl_failsafe.cc | 11 +- sql/set_var.cc | 10 ++ sql/slave.cc | 204 ++++++++++++------------- sql/slave.h | 91 ++++++----- sql/sql_class.h | 10 +- sql/sql_parse.cc | 2 +- sql/sql_repl.cc | 41 ++--- 14 files changed, 474 insertions(+), 283 deletions(-) create mode 100644 mysql-test/r/rpl_relayrotate.result create mode 100644 mysql-test/t/rpl_relayrotate-slave.opt create mode 100644 mysql-test/t/rpl_relayrotate.test diff --git a/mysql-test/r/rpl_relayrotate.result b/mysql-test/r/rpl_relayrotate.result new file mode 100644 index 00000000000..45f425a3532 --- /dev/null +++ b/mysql-test/r/rpl_relayrotate.result @@ -0,0 +1,18 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +stop slave; +create table t1 (a int) type=innodb; +reset slave; +start slave; +stop slave; +start slave; +select master_pos_wait('master-bin.001',3000,120)=-1; +master_pos_wait('master-bin.001',3000,120)=-1 +0 +select * from t1 where a=8000; +a +8000 diff --git a/mysql-test/t/rpl_relayrotate-slave.opt b/mysql-test/t/rpl_relayrotate-slave.opt new file mode 100644 index 00000000000..8b671423363 --- /dev/null +++ b/mysql-test/t/rpl_relayrotate-slave.opt @@ -0,0 +1,4 @@ +-O max_binlog_size=16384 +--innodb +--log-warnings + diff --git a/mysql-test/t/rpl_relayrotate.test b/mysql-test/t/rpl_relayrotate.test new file mode 100644 index 00000000000..4c330b8c9a2 --- /dev/null +++ b/mysql-test/t/rpl_relayrotate.test @@ -0,0 +1,61 @@ +# When the relay log gets rotated while the I/O thread +# is reading a transaction, the transaction spans on two or more +# relay logs. If STOP SLAVE occurs while the SQL thread is +# executing a part of the transaction in the non-first relay logs, +# we test if START SLAVE will resume in the beginning of the +# transaction (i.e., step back to the first relay log) + +# The slave is started with max_binlog_size=16384 bytes, +# to force many rotations (approximately 30 rotations) + +# If the master or slave does not support InnoDB, this test will pass + +source include/master-slave.inc; +connection slave; +stop slave; +connection master; +create table t1 (a int) type=innodb; +let $1=8000; +disable_query_log; +begin; +while ($1) +{ +# eval means expand $ expressions + eval insert into t1 values( $1 ); + dec $1; +} +commit; +# This will generate a 500kB master's binlog, +# which corresponds to 30 slave's relay logs. +enable_query_log; +save_master_pos; +connection slave; +reset slave; +start slave; +# We wait 1 sec for the SQL thread to be somewhere in +# the middle of the transaction, hopefully not in +# the first relay log, and hopefully before the COMMIT. +# Usually it stops when the SQL thread is around the 15th relay log. +# We cannot use MASTER_POS_WAIT() as master's position +# increases only when the slave executes the COMMIT. +system sleep 1; +stop slave; +# We suppose the SQL thread stopped before COMMIT. +# If so the transaction was rolled back +# and the table is now empty. +# Now restart +start slave; +# And see if the table contains '8000' +# which proves that the transaction restarted at +# the right place. +# We must wait for the transaction to commit before +# reading, MASTER_POS_WAIT() will do it for sure +# (the only statement with position>=3000 is COMMIT). +# Older versions of MySQL would hang forever in MASTER_POS_WAIT +# because COMMIT was said to be position 0 in the master's log (bug). +# Detect this with timeout. +select master_pos_wait('master-bin.001',3000,120)=-1; +select * from t1 where a=8000; +# Note that the simple fact to have less than around 30 slave's binlogs +# (the slave is started with --log-slave-updates) is already +# a proof that the transaction was not properly resumed. diff --git a/sql/log.cc b/sql/log.cc index 51b1c572601..50471041ee1 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -592,24 +592,32 @@ err: /* - Delete the current log file, remove it from index file and start on next + Delete relay log files prior to rli->group_relay_log_name + (i.e. all logs which are not involved in a non-finished group + (transaction)), remove them from the index file and start on next relay log. SYNOPSIS purge_first_log() - rli Relay log information - + rli Relay log information + included If false, all relay logs that are strictly before + rli->group_relay_log_name are deleted ; if true, the latter is + deleted too (i.e. all relay logs + read by the SQL slave thread are deleted). + NOTE - This is only called from the slave-execute thread when it has read - all commands from a log and want to switch to a new log. - - When this happens, we should never be in an active transaction as - a transaction is always written as a single block to the binary log. + all commands from a relay log and want to switch to a new relay log. + - When this happens, we can be in an active transaction as + a transaction can span over two relay logs + (although it is always written as a single block to the master's binary + log, hence cannot span over two master's binary logs). IMPLEMENTATION - Protects index file with LOCK_index - - Delete first log file, - - Copy all file names after this one to the front of the index file + - Delete relevant relay log files + - Copy all file names after these ones to the front of the index file - If the OS has truncate, truncate the file, else fill it with \n' - - Read the first file name from the index file and store in rli->linfo + - Read the next file name from the index file and store in rli->linfo RETURN VALUES 0 ok @@ -620,66 +628,68 @@ err: #ifdef HAVE_REPLICATION -int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli) +int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) { int error; DBUG_ENTER("purge_first_log"); - /* - Test pre-conditions. - - Assume that we have previously read the first log and - stored it in rli->relay_log_name - */ DBUG_ASSERT(is_open()); DBUG_ASSERT(rli->slave_running == 1); - DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->relay_log_name)); - DBUG_ASSERT(rli->linfo.index_file_offset == - strlen(rli->relay_log_name) + 1); + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name)); - /* We have already processed the relay log, so it's safe to delete it */ - my_delete(rli->relay_log_name, MYF(0)); pthread_mutex_lock(&LOCK_index); - if (copy_up_file_and_fill(&index_file, rli->linfo.index_file_offset)) - { - error= LOG_INFO_IO; - goto err; - } + pthread_mutex_lock(&rli->log_space_lock); + rli->relay_log.purge_logs(rli->group_relay_log_name, included, + 0, 0, &rli->log_space_total); + // Tell the I/O thread to take the relay_log_space_limit into account + rli->ignore_log_space_limit= 0; + pthread_mutex_unlock(&rli->log_space_lock); /* - Update the space counter used by all relay logs Ok to broadcast after the critical region as there is no risk of the mutex being destroyed by this thread later - this helps save context switches */ - pthread_mutex_lock(&rli->log_space_lock); - rli->log_space_total -= rli->relay_log_pos; - //tell the I/O thread to take the relay_log_space_limit into account - rli->ignore_log_space_limit= 0; - pthread_mutex_unlock(&rli->log_space_lock); pthread_cond_broadcast(&rli->log_space_cond); /* Read the next log file name from the index file and pass it back to the caller + If included is true, we want the first relay log; + otherwise we want the one after event_relay_log_name. */ - if ((error=find_log_pos(&rli->linfo, NullS, 0 /*no mutex*/))) + if ((included && (error=find_log_pos(&rli->linfo, NullS, 0))) || + (!included && + ((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) || + (error=find_next_log(&rli->linfo, 0))))) { char buff[22]; - sql_print_error("next log error: %d offset: %s log: %s", - error, - llstr(rli->linfo.index_file_offset,buff), - rli->linfo.log_file_name); + sql_print_error("next log error: %d offset: %s log: %s included: %d", + error, + llstr(rli->linfo.index_file_offset,buff), + rli->group_relay_log_name, + included); goto err; } + /* - Reset position to current log. This involves setting both of the - position variables: + Reset rli's coordinates to the current log. */ - rli->relay_log_pos = BIN_LOG_HEADER_SIZE; - rli->pending = 0; - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); + + /* + If we removed the rli->group_relay_log_name file, + we must update the rli->group* coordinates, otherwise do not touch it as the + group's execution is not finished (e.g. COMMIT not executed) + */ + if (included) + { + rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE; + strmake(rli->group_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->group_relay_log_name)-1); + } /* Store where we are in the new file for the execution thread */ flush_relay_log_info(rli); @@ -693,13 +703,14 @@ err: Update log index_file */ -int MYSQL_LOG::update_log_index(LOG_INFO* log_info) +int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) { if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset)) return LOG_INFO_IO; // now update offsets in index file for running threads - adjust_linfo_offsets(log_info->index_file_start_offset); + if (need_update_threads) + adjust_linfo_offsets(log_info->index_file_start_offset); return 0; } @@ -708,9 +719,13 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info) SYNOPSIS purge_logs() - thd Thread pointer - to_log Delete all log file name before this file. This file is not - deleted + to_log Delete all log file name before this file. + included If true, to_log is deleted too. + need_mutex + need_update_threads If we want to update the log coordinates of + all threads. False for relay logs, true otherwise. + freed_log_space If not null, decrement this variable of + the amount of log space freed NOTES If any of the logs before the deleted one is in use, @@ -722,31 +737,59 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info) LOG_INFO_EOF to_log not found */ -int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) +int MYSQL_LOG::purge_logs(const char *to_log, + bool included, + bool need_mutex, + bool need_update_threads, + ulonglong *decrease_log_space) { int error; + bool exit_loop= 0; LOG_INFO log_info; DBUG_ENTER("purge_logs"); + DBUG_PRINT("info",("to_log= %s",to_log)); if (no_rotate) DBUG_RETURN(LOG_INFO_PURGE_NO_ROTATE); - pthread_mutex_lock(&LOCK_index); + if (need_mutex) + pthread_mutex_lock(&LOCK_index); if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) goto err; /* - File name exists in index file; Delete until we find this file + File name exists in index file; delete until we find this file or a file that is used. */ if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))) goto err; - while (strcmp(to_log,log_info.log_file_name) && - !log_in_use(log_info.log_file_name)) + while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && + !log_in_use(log_info.log_file_name)) { - /* It's not fatal even if we can't delete a log file */ - my_delete(log_info.log_file_name, MYF(0)); - if (find_next_log(&log_info, 0)) + ulong tmp; + if (decrease_log_space) //stat the file we want to delete + { + MY_STAT s; + if (my_stat(log_info.log_file_name,&s,MYF(0))) + tmp= s.st_size; + else + { + /* + If we could not stat, we can't know the amount + of space that deletion will free. In most cases, + deletion won't work either, so it's not a problem. + */ + tmp= 0; + } + } + /* + It's not fatal if we can't delete a log file ; + if we could delete it, take its size into account + */ + DBUG_PRINT("info",("purging %s",log_info.log_file_name)); + if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space) + *decrease_log_space-= tmp; + if (find_next_log(&log_info, 0) || exit_loop) break; } @@ -754,10 +797,11 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) If we get killed -9 here, the sysadmin would have to edit the log index file after restart - otherwise, this should be safe */ - error= update_log_index(&log_info); + error= update_log_index(&log_info, need_update_threads); err: - pthread_mutex_unlock(&LOCK_index); + if (need_mutex) + pthread_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } @@ -779,7 +823,7 @@ err: LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated */ -int MYSQL_LOG::purge_logs_before_date(THD* thd, time_t purge_time) +int MYSQL_LOG::purge_logs_before_date(time_t purge_time) { int error; LOG_INFO log_info; @@ -816,7 +860,7 @@ int MYSQL_LOG::purge_logs_before_date(THD* thd, time_t purge_time) If we get killed -9 here, the sysadmin would have to edit the log index file after restart - otherwise, this should be safe */ - error= update_log_index(&log_info); + error= update_log_index(&log_info, 1); err: pthread_mutex_unlock(&LOCK_index); @@ -1269,7 +1313,7 @@ err: { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - error= purge_logs_before_date(current_thd, purge_time); + error= purge_logs_before_date(purge_time); } #endif @@ -1534,7 +1578,6 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, If you don't do it this way, you will get a deadlock in THD::awake() */ - void MYSQL_LOG:: wait_for_update(THD* thd) { safe_mutex_assert_owner(&LOCK_log); diff --git a/sql/log_event.cc b/sql/log_event.cc index d4efb65bf42..39db264d898 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -310,11 +310,36 @@ int Log_event::exec_event(struct st_relay_log_info* rli) */ if (rli) { - if (rli->inside_transaction) - rli->inc_pending(get_event_len()); + /* + If in a transaction, and if the slave supports transactions, + just inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN + (not OPTION_NOT_AUTOCOMMIT) as transactions are logged + with BEGIN/COMMIT, not with SET AUTOCOMMIT= . + + CAUTION: opt_using_transactions means + innodb || bdb ; suppose the master supports InnoDB and BDB, + but the slave supports only BDB, problems + will arise: + - suppose an InnoDB table is created on the master, + - then it will be MyISAM on the slave + - but as opt_using_transactions is true, the slave will believe he is + transactional with the MyISAM table. And problems will come when one + does START SLAVE; STOP SLAVE; START SLAVE; (the slave will resume at BEGIN + whereas there has not been any rollback). This is the problem of + using opt_using_transactions instead of a finer + "does the slave support _the_transactional_handler_used_on_the_master_". + + More generally, we'll have problems when a query mixes a transactional + handler and MyISAM and STOP SLAVE is issued in the middle of the + "transaction". START SLAVE will resume at BEGIN while the MyISAM table has + already been updated. + + */ + if ((thd->options & OPTION_BEGIN) && opt_using_transactions) + rli->inc_event_relay_log_pos(get_event_len()); else { - rli->inc_pos(get_event_len(),log_pos); + rli->inc_group_relay_log_pos(get_event_len(),log_pos); flush_relay_log_info(rli); } } @@ -878,9 +903,13 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) thd->db = rewrite_db((char*)db); /* - InnoDB internally stores the master log position it has processed so far; - position to store is really pos + pending + event_len - since we must store the pos of the END of the current log event + InnoDB internally stores the master log position it has executed so far, + i.e. the position just after the COMMIT event. + When InnoDB will want to store, the positions in rli won't have + been updated yet, so group_master_log_* will point to old BEGIN + and event_master_log* will point to the beginning of current COMMIT. + So the position to store is event_master_log_pos + event_len + since we must store the pos of the END of the current log event (COMMIT). */ rli->event_len= get_event_len(); @@ -909,18 +938,6 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) DBUG_PRINT("query",("%s",thd->query)); mysql_parse(thd, thd->query, q_len); - /* - Set a flag if we are inside an transaction so that we can restart - the transaction from the start if we are killed - - This will only be done if we are supporting transactional tables - in the slave. - */ - if (!strcmp(thd->query,"BEGIN")) - rli->inside_transaction= opt_using_transactions; - else if (!strcmp(thd->query,"COMMIT")) - rli->inside_transaction=0; - DBUG_PRINT("info",("expected_error: %d last_errno: %d", expected_error, thd->net.last_errno)); if ((expected_error != (actual_error= thd->net.last_errno)) && @@ -1776,14 +1793,15 @@ int Rotate_log_event::write_data(IO_CACHE* file) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Rotate_log_event::exec_event(struct st_relay_log_info* rli) { - char* log_name = rli->master_log_name; + char* log_name = rli->group_master_log_name; DBUG_ENTER("Rotate_log_event::exec_event"); pthread_mutex_lock(&rli->data_lock); memcpy(log_name, new_log_ident, ident_len+1); - rli->master_log_pos = pos; - rli->relay_log_pos += get_event_len(); - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) rli->master_log_pos)); + rli->group_master_log_pos = pos; + rli->event_relay_log_pos += get_event_len(); + rli->group_relay_log_pos = rli->event_relay_log_pos; + DBUG_PRINT("info", ("group_master_log_pos: %d", (ulong) rli->group_master_log_pos)); pthread_mutex_unlock(&rli->data_lock); pthread_cond_broadcast(&rli->data_cond); flush_relay_log_info(rli); @@ -1905,7 +1923,7 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) thd->next_insert_id = val; break; } - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif @@ -1967,7 +1985,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) { thd->rand.seed1= (ulong) seed1; thd->rand.seed2= (ulong) seed2; - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif // !MYSQL_CLIENT @@ -2199,7 +2217,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) e.update_hash(val, val_len, type, charset, Item::COER_NOCOLL); free_root(&thd->mem_root,0); - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif // !MYSQL_CLIENT @@ -2241,7 +2259,7 @@ Slave_log_event::Slave_log_event(THD* thd_arg, pthread_mutex_lock(&mi->data_lock); pthread_mutex_lock(&rli->data_lock); master_host_len = strlen(mi->host); - master_log_len = strlen(rli->master_log_name); + master_log_len = strlen(rli->group_master_log_name); // on OOM, just do not initialize the structure and print the error if ((mem_pool = (char*)my_malloc(get_data_size() + 1, MYF(MY_WME)))) @@ -2249,9 +2267,9 @@ Slave_log_event::Slave_log_event(THD* thd_arg, master_host = mem_pool + SL_MASTER_HOST_OFFSET ; memcpy(master_host, mi->host, master_host_len + 1); master_log = master_host + master_host_len + 1; - memcpy(master_log, rli->master_log_name, master_log_len + 1); + memcpy(master_log, rli->group_master_log_name, master_log_len + 1); master_port = mi->port; - master_pos = rli->master_log_pos; + master_pos = rli->group_master_log_pos; DBUG_PRINT("info", ("master_log: %s pos: %d", master_log, (ulong) master_pos)); } @@ -2381,19 +2399,19 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db) int Stop_log_event::exec_event(struct st_relay_log_info* rli) { // do not clean up immediately after rotate event - if (rli->master_log_pos > BIN_LOG_HEADER_SIZE) + if (rli->group_master_log_pos > BIN_LOG_HEADER_SIZE) { close_temporary_tables(thd); cleanup_load_tmpdir(); } /* We do not want to update master_log pos because we get a rotate event - before stop, so by now master_log_name is set to the next log. + before stop, so by now group_master_log_name is set to the next log. If we updated it, we will have incorrect master coordinates and this could give false triggers in MASTER_POS_WAIT() that we have reached the target position when in fact we have not. */ - rli->inc_pos(get_event_len(), 0); + rli->inc_group_relay_log_pos(get_event_len(), 0); flush_relay_log_info(rli); return 0; } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4203d440667..d17faa3cea5 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -721,7 +721,7 @@ extern ulong max_binlog_size, rpl_recovery_rank, thread_cache_size; extern ulong com_stat[(uint) SQLCOM_END], com_other, back_log; extern ulong specialflag, current_pid; extern ulong expire_logs_days; - +extern my_bool relay_log_purge; extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version,dropping_tables; extern uint delay_key_write_options; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ea0311bafc6..5a6ea939878 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -314,7 +314,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0; -my_bool opt_console= 0; +my_bool opt_console= 0, opt_bdb, opt_innodb; volatile bool mqh_used = 0; FILE *bootstrap_file=0; @@ -442,6 +442,7 @@ const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; const char *sql_mode_str="OFF"; ulong rpl_recovery_rank=0; +my_bool relay_log_purge=1; my_string mysql_unix_port=NULL, opt_mysql_tmpdir=NULL; MY_TMPDIR mysql_tmpdir_list; @@ -2208,7 +2209,7 @@ static int init_server_components() { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - mysql_bin_log.purge_logs_before_date(current_thd, purge_time); + mysql_bin_log.purge_logs_before_date(purge_time); } #endif } @@ -3400,7 +3401,7 @@ enum options OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, OPT_BDB_HOME, OPT_BDB_LOG, OPT_BDB_TMP, OPT_BDB_NOSYNC, - OPT_BDB_LOCK, OPT_BDB_SKIP, + OPT_BDB_LOCK, OPT_BDB, OPT_BDB_NO_RECOVER, OPT_BDB_SHARED, OPT_MASTER_HOST, OPT_MASTER_USER, OPT_MASTER_PASSWORD, OPT_MASTER_PORT, @@ -3430,7 +3431,7 @@ enum options OPT_INNODB_FLUSH_METHOD, OPT_INNODB_FAST_SHUTDOWN, OPT_SAFE_SHOW_DB, - OPT_INNODB_SKIP, OPT_SKIP_SAFEMALLOC, + OPT_INNODB, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, @@ -3468,7 +3469,7 @@ enum options OPT_OPEN_FILES_LIMIT, OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE, OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, - OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, + OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE, OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, OPT_SORT_BUFFER, OPT_TABLE_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, @@ -3529,8 +3530,10 @@ struct my_option my_long_options[] = (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_BERKELEY_DB */ - {"skip-bdb", OPT_BDB_SKIP, "Don't use berkeley db (will save memory)", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb", OPT_BDB, "Enable Berkeley DB (if this version of MySQL supports it). \ +Disable with --skip-bdb (will save memory)", + (gptr*) &opt_bdb, (gptr*) &opt_bdb, 0, GET_BOOL, NO_ARG, 1, 0, 0, + 0, 0, 0}, {"big-tables", OPT_BIG_TABLES, "Allow big result sets by saving all temporary sets on file (Solves most 'table full' errors)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -3885,8 +3888,10 @@ struct my_option my_long_options[] = "Start without grant tables. This gives all users FULL ACCESS to all tables!", (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-innodb", OPT_INNODB_SKIP, "Don't use Innodb (will save memory)", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb", OPT_INNODB, "Enable InnoDB (if this version of MySQL supports it). \ +Disable with --skip-innodb (will save memory)", + (gptr*) &opt_innodb, (gptr*) &opt_innodb, 0, GET_BOOL, NO_ARG, 1, 0, 0, + 0, 0, 0}, {"skip-locking", OPT_SKIP_LOCK, "Deprecated option, use --skip-external-locking instead", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -4272,6 +4277,11 @@ struct my_option my_long_options[] = (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, #ifdef HAVE_REPLICATION + {"relay_log_purge", OPT_RELAY_LOG_PURGE, + "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", + (gptr*) &relay_log_purge, + (gptr*) &relay_log_purge, 0, GET_BOOL, NO_ARG, + 1, 0, 1, 0, 1, 0}, {"relay_log_space_limit", OPT_RELAY_LOG_SPACE_LIMIT, "Max space to use for all relay logs", (gptr*) &relay_log_space_limit, @@ -5016,16 +5026,32 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), berkeley_shared_data=1; break; #endif /* HAVE_BERKELEY_DB */ - case OPT_BDB_SKIP: + case OPT_BDB: #ifdef HAVE_BERKELEY_DB - berkeley_skip=1; - have_berkeley_db=SHOW_OPTION_DISABLED; + if (opt_bdb) + { + berkeley_skip=0; + have_berkeley_db=SHOW_OPTION_YES; + } + else + { + berkeley_skip=1; + have_berkeley_db=SHOW_OPTION_DISABLED; + } #endif break; - case OPT_INNODB_SKIP: + case OPT_INNODB: #ifdef HAVE_INNOBASE_DB - innodb_skip=1; - have_innodb=SHOW_OPTION_DISABLED; + if (opt_innodb) + { + innodb_skip=0; + have_innodb=SHOW_OPTION_YES; + } + else + { + innodb_skip=1; + have_innodb=SHOW_OPTION_DISABLED; + } #endif break; case OPT_INNODB_DATA_FILE_PATH: diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 46791c13219..58769827bed 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -873,7 +873,6 @@ int load_master_data(THD* thd) // don't hit the magic number if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; - active_mi->rli.pending = 0; flush_master_info(active_mi); } mc_mysql_free_result(master_status_res); @@ -897,9 +896,13 @@ int load_master_data(THD* thd) return 1; } pthread_mutex_lock(&active_mi->rli.data_lock); - active_mi->rli.master_log_pos = active_mi->master_log_pos; - strmake(active_mi->rli.master_log_name,active_mi->master_log_name, - sizeof(active_mi->rli.master_log_name)-1); + active_mi->rli.group_master_log_pos = active_mi->master_log_pos; + strmake(active_mi->rli.group_master_log_name,active_mi->master_log_name, + sizeof(active_mi->rli.group_master_log_name)-1); + /* + No need to update rli.event* coordinates, they will be when the slave + threads start ; only rli.group* coordinates are necessary here. + */ flush_relay_log_info(&active_mi->rli); pthread_cond_broadcast(&active_mi->rli.data_cond); pthread_mutex_unlock(&active_mi->rli.data_lock); diff --git a/sql/set_var.cc b/sql/set_var.cc index d39a506c82d..dc6a2f71133 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -201,6 +201,10 @@ sys_var_thd_ulong sys_read_buff_size("read_buffer_size", &SV::read_buff_size); sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size", &SV::read_rnd_buff_size); +#ifdef HAVE_REPLICATION +sys_var_bool_ptr sys_relay_log_purge("relay_log_purge", + &relay_log_purge); +#endif sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank", &rpl_recovery_rank); sys_var_long_ptr sys_query_cache_size("query_cache_size", @@ -407,6 +411,9 @@ sys_var *sys_variables[]= &sys_rand_seed2, &sys_read_buff_size, &sys_read_rnd_buff_size, +#ifdef HAVE_REPLICATION + &sys_relay_log_purge, +#endif &sys_rpl_recovery_rank, &sys_safe_updates, &sys_select_limit, @@ -563,6 +570,9 @@ struct show_var_st init_vars[]= { {sys_pseudo_thread_id.name, (char*) &sys_pseudo_thread_id, SHOW_SYS}, {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, +#ifdef HAVE_REPLICATION + {sys_relay_log_purge.name, (char*) &sys_relay_log_purge, SHOW_SYS}, +#endif {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, #ifdef HAVE_QUERY_CACHE {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS}, diff --git a/sql/slave.cc b/sql/slave.cc index fa7ab180eaa..eae795ae760 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -217,11 +217,7 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len, - If not, open the 'log' binary file. TODO - - check proper initialization of master_log_name/master_log_pos - - We may always want to delete all logs before 'log'. - Currently if we are not calling this with 'log' as NULL or the first - log we will never delete relay logs. - If we want this we should not set skip_log_purge to 1. + - check proper initialization of group_master_log_name/group_master_log_pos RETURN VALUES 0 ok @@ -248,7 +244,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, rli->cur_log_fd = -1; } - rli->relay_log_pos = pos; + rli->group_relay_log_pos = rli->event_relay_log_pos = pos; /* Test to see if the previous run was with the skip of purging @@ -260,18 +256,15 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, goto err; } - if (log) // If not first log + if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1)) { - if (strcmp(log, rli->linfo.log_file_name)) - rli->skip_log_purge= 1; // Different name; Don't purge - if (rli->relay_log.find_log_pos(&rli->linfo, log, 1)) - { - *errmsg="Could not find target log during relay log initialization"; - goto err; - } + *errmsg="Could not find target log during relay log initialization"; + goto err; } - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + strmake(rli->group_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); if (rli->relay_log.is_active(rli->linfo.log_file_name)) { /* @@ -302,7 +295,7 @@ err: If we don't purge, we can't honour relay_log_space_limit ; silently discard it */ - if (rli->skip_log_purge) + if (!relay_log_purge) rli->log_space_limit= 0; pthread_cond_broadcast(&rli->data_cond); if (need_data_lock) @@ -383,9 +376,8 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, to display fine in any case. */ - rli->master_log_name[0]= 0; - rli->master_log_pos= 0; - rli->pending= 0; + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; if (!rli->inited) DBUG_RETURN(0); @@ -402,16 +394,18 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, goto err; } /* Save name of used relay log file */ - strmake(rli->relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->relay_log_name)-1); + strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->event_relay_log_name)-1); // Just first log with magic number and nothing else rli->log_space_total= BIN_LOG_HEADER_SIZE; - rli->relay_log_pos= BIN_LOG_HEADER_SIZE; + rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; rli->relay_log.reset_bytes_written(); if (!just_reset) - error= init_relay_log_pos(rli, rli->relay_log_name, rli->relay_log_pos, - 0 /* do not need data lock */, errmsg); - + error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos, + 0 /* do not need data lock */, errmsg); + err: #ifndef DBUG_OFF char buf[22]; @@ -1238,13 +1232,11 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) fn_format(fname, info_fname, mysql_data_home, "", 4+32); pthread_mutex_lock(&rli->data_lock); info_fd = rli->info_fd; - rli->pending = 0; rli->cur_log_fd = -1; rli->slave_skip_counter=0; rli->abort_pos_wait=0; - rli->skip_log_purge=0; - rli->log_space_limit = relay_log_space_limit; - rli->log_space_total = 0; + rli->log_space_limit= relay_log_space_limit; + rli->log_space_total= 0; // TODO: make this work with multi-master if (!opt_relay_logname) @@ -1285,8 +1277,8 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, &msg)) goto err; - rli->master_log_name[0]= 0; - rli->master_log_pos= 0; + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; rli->info_fd= info_fd; } else // file exists @@ -1307,31 +1299,33 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) rli->info_fd = info_fd; int relay_log_pos, master_log_pos; - if (init_strvar_from_file(rli->relay_log_name, - sizeof(rli->relay_log_name), &rli->info_file, + if (init_strvar_from_file(rli->group_relay_log_name, + sizeof(rli->group_relay_log_name), &rli->info_file, "") || init_intvar_from_file(&relay_log_pos, &rli->info_file, BIN_LOG_HEADER_SIZE) || - init_strvar_from_file(rli->master_log_name, - sizeof(rli->master_log_name), &rli->info_file, + init_strvar_from_file(rli->group_master_log_name, + sizeof(rli->group_master_log_name), &rli->info_file, "") || init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) { msg="Error reading slave log configuration"; goto err; } - rli->relay_log_pos= relay_log_pos; - rli->master_log_pos= master_log_pos; + strmake(rli->event_relay_log_name,rli->group_relay_log_name, + sizeof(rli->event_relay_log_name)-1); + rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos; + rli->group_master_log_pos= master_log_pos; if (init_relay_log_pos(rli, - rli->relay_log_name, - rli->relay_log_pos, + rli->group_relay_log_name, + rli->group_relay_log_pos, 0 /* no data lock*/, &msg)) goto err; } - DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); /* Now change the cache from READ to WRITE - must do this before flush_relay_log_info @@ -1407,7 +1401,7 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) { LOG_INFO linfo; DBUG_ENTER("count_relay_log_space"); - rli->log_space_total = 0; + rli->log_space_total= 0; if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) { sql_print_error("Could not find first log while counting relay log space"); @@ -1631,10 +1625,10 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((uint32) mi->connect_retry); protocol->store(mi->master_log_name, &my_charset_bin); protocol->store((ulonglong) mi->master_log_pos); - protocol->store(mi->rli.relay_log_name + - dirname_length(mi->rli.relay_log_name), &my_charset_bin); - protocol->store((ulonglong) mi->rli.relay_log_pos); - protocol->store(mi->rli.master_log_name, &my_charset_bin); + protocol->store(mi->rli.group_relay_log_name + + dirname_length(mi->rli.group_relay_log_name), &my_charset_bin); + protocol->store((ulonglong) mi->rli.group_relay_log_pos); + protocol->store(mi->rli.group_master_log_name, &my_charset_bin); protocol->store(mi->slave_running ? "Yes":"No", &my_charset_bin); protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin); protocol->store(&replicate_do_db); @@ -1642,7 +1636,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((uint32) mi->rli.last_slave_errno); protocol->store(mi->rli.last_slave_error, &my_charset_bin); protocol->store((uint32) mi->rli.slave_skip_counter); - protocol->store((ulonglong) mi->rli.master_log_pos); + protocol->store((ulonglong) mi->rli.group_master_log_pos); protocol->store((ulonglong) mi->rli.log_space_total); pthread_mutex_unlock(&mi->rli.data_lock); pthread_mutex_unlock(&mi->data_lock); @@ -1673,17 +1667,15 @@ bool flush_master_info(MASTER_INFO* mi) st_relay_log_info::st_relay_log_info() - :info_fd(-1), cur_log_fd(-1), master_log_pos(0), save_temporary_tables(0), - cur_log_old_open_count(0), log_space_total(0), ignore_log_space_limit(0), - slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), - sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), - slave_running(0), skip_log_purge(0), - inside_transaction(0) /* the default is autocommit=1 */ + :info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), + cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), + ignore_log_space_limit(0), slave_skip_counter(0), abort_pos_wait(0), + slave_run_id(0), sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), + slave_running(0) { - relay_log_name[0] = master_log_name[0] = 0; + group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; last_slave_error[0]=0; - bzero(&info_file,sizeof(info_file)); bzero(&cache_buf, sizeof(cache_buf)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); @@ -1745,8 +1737,8 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, set_timespec(abstime,timeout); DBUG_ENTER("wait_for_pos"); - DBUG_PRINT("enter",("master_log_name: '%s' pos: %lu timeout: %ld", - master_log_name, (ulong) master_log_pos, + DBUG_PRINT("enter",("group_master_log_name: '%s' pos: %lu timeout: %ld", + group_master_log_name, (ulong) group_master_log_pos, (long) timeout)); pthread_mutex_lock(&data_lock); @@ -1796,10 +1788,10 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, { bool pos_reached; int cmp_result= 0; - DBUG_ASSERT(*master_log_name || master_log_pos == 0); - if (*master_log_name) + DBUG_ASSERT(*group_master_log_name || group_master_log_pos == 0); + if (*group_master_log_name) { - char *basename= master_log_name + dirname_length(master_log_name); + char *basename= group_master_log_name + dirname_length(group_master_log_name); /* First compare the parts before the extension. Find the dot in the master's log basename, @@ -1814,13 +1806,13 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, } // Now compare extensions. char *q_end; - ulong master_log_name_extension= strtoul(q, &q_end, 10); - if (master_log_name_extension < log_name_extension) + ulong group_master_log_name_extension= strtoul(q, &q_end, 10); + if (group_master_log_name_extension < log_name_extension) cmp_result = -1 ; else - cmp_result= (master_log_name_extension > log_name_extension) ? 1 : 0 ; + cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ; } - pos_reached = ((!cmp_result && master_log_pos >= (ulonglong)log_pos) || + pos_reached = ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) || cmp_result > 0); if (pos_reached || thd->killed) break; @@ -2127,7 +2119,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) (rli->slave_skip_counter && type_code != ROTATE_EVENT)) { /* TODO: I/O thread should not even log events with the same server id */ - rli->inc_pos(ev->get_event_len(), + rli->inc_group_relay_log_pos(ev->get_event_len(), type_code != STOP_EVENT ? ev->log_pos : LL(0), 1/* skip lock*/); flush_relay_log_info(rli); @@ -2497,15 +2489,13 @@ slave_begin: rli->abort_slave = 0; pthread_mutex_unlock(&rli->run_lock); pthread_cond_broadcast(&rli->start_cond); - // This should always be set to 0 when the slave thread is started - rli->pending = 0; //tell the I/O thread to take relay_log_space_limit into account from now on rli->ignore_log_space_limit= 0; if (init_relay_log_pos(rli, - rli->relay_log_name, - rli->relay_log_pos, + rli->group_relay_log_name, + rli->group_relay_log_pos, 1 /*need data lock*/, &errmsg)) { sql_print_error("Error initializing relay log position: %s", @@ -2513,18 +2503,18 @@ slave_begin: goto err; } THD_CHECK_SENTRY(thd); - DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); DBUG_ASSERT(rli->sql_thd == thd); DBUG_PRINT("master_info",("log_file_name: %s position: %s", - rli->master_log_name, - llstr(rli->master_log_pos,llbuff))); + rli->group_master_log_name, + llstr(rli->group_master_log_pos,llbuff))); if (global_system_variables.log_warnings) sql_print_error("Slave SQL thread initialized, starting replication in \ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, - llstr(rli->master_log_pos,llbuff),rli->relay_log_name, - llstr(rli->relay_log_pos,llbuff1)); + llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name, + llstr(rli->group_relay_log_pos,llbuff1)); /* Read queries from the IO/THREAD until this thread is killed */ @@ -2541,7 +2531,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, Error running query, slave SQL thread aborted. Fix the problem, and restart \ the slave SQL thread with \"SLAVE START\". We stopped at log \ '%s' position %s", - RPL_LOG_NAME, llstr(rli->master_log_pos, llbuff)); + RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff)); goto err; } } @@ -2549,7 +2539,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ /* Thread stopped. Print the current replication position to the log */ sql_print_error("Slave SQL thread exiting, replication stopped in log \ '%s' at position %s", - RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff)); + RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); err: VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -2699,7 +2689,7 @@ err: rev The rotate log event read from the binary log DESCRIPTION - Updates the master info and relay data with the place in the next binary + Updates the master info with the place in the next binary log where we should start reading. NOTES @@ -3073,18 +3063,14 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) IO_CACHE *file = &rli->info_file; char buff[FN_REFLEN*2+22*2+4], *pos; - /* sql_thd is not set when calling from init_slave() */ - if ((rli->sql_thd && rli->sql_thd->options & OPTION_BEGIN)) - return 0; // Wait for COMMIT - my_b_seek(file, 0L); - pos=strmov(buff, rli->relay_log_name); + pos=strmov(buff, rli->group_relay_log_name); *pos++='\n'; - pos=longlong2str(rli->relay_log_pos, pos, 10); + pos=longlong2str(rli->group_relay_log_pos, pos, 10); *pos++='\n'; - pos=strmov(pos, rli->master_log_name); + pos=strmov(pos, rli->group_master_log_name); *pos++='\n'; - pos=longlong2str(rli->master_log_pos, pos, 10); + pos=longlong2str(rli->group_master_log_pos, pos, 10); *pos='\n'; if (my_b_write(file, (byte*) buff, (ulong) (pos-buff)+1)) error=1; @@ -3107,7 +3093,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) DBUG_ENTER("reopen_relay_log"); IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf; - if ((rli->cur_log_fd=open_binlog(cur_log,rli->relay_log_name, + if ((rli->cur_log_fd=open_binlog(cur_log,rli->event_relay_log_name, errmsg)) <0) DBUG_RETURN(0); /* @@ -3115,7 +3101,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) relay_log_pos Current log pos pending Number of bytes already processed from the event */ - my_b_seek(cur_log,rli->relay_log_pos + rli->pending); + my_b_seek(cur_log,rli->event_relay_log_pos); DBUG_RETURN(cur_log); } @@ -3124,7 +3110,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) { Log_event* ev; IO_CACHE* cur_log = rli->cur_log; - pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); + pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); const char* errmsg=0; THD* thd = rli->sql_thd; DBUG_ENTER("next_event"); @@ -3173,7 +3159,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) } } DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(cur_log) == rli->relay_log_pos + rli->pending); + DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos); /* Relay log is always in new format - if the master is 3.23, the I/O thread will convert the format for us @@ -3240,8 +3226,8 @@ Log_event* next_event(RELAY_LOG_INFO* rli) // prevent the I/O thread from blocking next times rli->ignore_log_space_limit= 1; // If the I/O thread is blocked, unblock it - pthread_cond_broadcast(&rli->log_space_cond); pthread_mutex_unlock(&rli->log_space_lock); + pthread_cond_broadcast(&rli->log_space_cond); // Note that wait_for_update unlocks lock_log ! rli->relay_log.wait_for_update(rli->sql_thd); // re-acquire data lock since we released it earlier @@ -3258,16 +3244,25 @@ Log_event* next_event(RELAY_LOG_INFO* rli) my_close(rli->cur_log_fd, MYF(MY_WME)); rli->cur_log_fd = -1; - /* - TODO: make skip_log_purge a start-up option. At this point this - is not critical priority - */ - if (!rli->skip_log_purge) + if (relay_log_purge) { - // purge_first_log will properly set up relay log coordinates in rli - if (rli->relay_log.purge_first_log(rli)) + /* + purge_first_log will properly set up relay log coordinates in rli. + If the group's coordinates are equal to the event's coordinates + (i.e. the relay log was not rotated in the middle of a group), + we can purge this relay log too. + We do ulonglong and string comparisons, this may be slow but + - purging the last relay log is nice (it can save 1GB of disk), so we + like to detect the case where we can do it, and given this, + - I see no better detection method + - purge_first_log is not called that often + */ + if (rli->relay_log.purge_first_log + (rli, + rli->group_relay_log_pos == rli->event_relay_log_pos + && !strcmp(rli->group_relay_log_name,rli->event_relay_log_name))) { - errmsg = "Error purging processed log"; + errmsg = "Error purging processed logs"; goto err; } } @@ -3285,10 +3280,9 @@ Log_event* next_event(RELAY_LOG_INFO* rli) errmsg = "error switching to the next log"; goto err; } - rli->relay_log_pos = BIN_LOG_HEADER_SIZE; - rli->pending=0; - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); flush_relay_log_info(rli); } @@ -3336,7 +3330,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) event(errno: %d cur_log->error: %d)", my_errno,cur_log->error); // set read position to the beginning of the event - my_b_seek(cur_log,rli->relay_log_pos+rli->pending); + my_b_seek(cur_log,rli->event_relay_log_pos); /* otherwise, we have had a partial read */ errmsg = "Aborting slave SQL thread because of partial event read"; break; // To end of function diff --git a/sql/slave.h b/sql/slave.h index a4db7388be5..16ba7f80471 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -92,12 +92,6 @@ typedef struct st_relay_log_info cur_log_fd - file descriptor of the current read relay log */ File info_fd,cur_log_fd; - /* name of current read relay log */ - char relay_log_name[FN_REFLEN]; - /* master log name corresponding to current read position */ - char master_log_name[FN_REFLEN]; - /* original log position of last processed event */ - volatile my_off_t master_log_pos; /* Protected with internal locks. @@ -142,20 +136,36 @@ typedef struct st_relay_log_info uint32 cur_log_old_open_count; /* - relay_log_pos - Current offset in the relay log. - pending - In some cases we do not increment offset immediately - after processing an event, because the following event - needs to be processed atomically together with this one - such as: - - Intvar_event - sets auto_increment value - Rand_event - sets the random seed - - However, once both events have been processed, we need to - increment by the cumulative offset. 'pending' stores the - extra offset to be added to the position. + Let's call a group (of events) : + - a transaction + or + - an autocommiting query + its associated events (INSERT_ID, + TIMESTAMP...) + We need these rli coordinates : + - relay log name and position of the beginning of the group we currently are + executing. Needed to know where we have to restart when replication has + stopped in the middle of a group (which has been rolled back by the slave). + - relay log name and position just after the event we have just + executed. This event is part of the current group. + Formerly we only had the immediately above coordinates, plus a 'pending' + variable, but this dealt wrong with the case of a transaction starting on a + relay log and finishing (commiting) on another relay log. Case which can + happen when, for example, the relay log gets rotated because of + max_binlog_size. */ - ulonglong relay_log_pos, pending; + char group_relay_log_name[FN_REFLEN]; + ulonglong group_relay_log_pos; + char event_relay_log_name[FN_REFLEN]; + ulonglong event_relay_log_pos; + /* + Original log name and position of the group we're currently executing + (whose coordinates are group_relay_log_name/pos in the relay log) + in the master's binlog. These concern the *group*, because in the master's + binlog the log_pos that comes with each event is the position of the + beginning of the group. + */ + char group_master_log_name[FN_REFLEN]; + volatile my_off_t group_master_log_pos; /* Handling of the relay_log_space_limit optional constraint. @@ -193,38 +203,39 @@ typedef struct st_relay_log_info /* if not set, the value of other members of the structure are undefined */ bool inited; volatile bool abort_slave, slave_running; - bool skip_log_purge; - bool inside_transaction; st_relay_log_info(); ~st_relay_log_info(); - inline void inc_pending(ulonglong val) + + inline void inc_event_relay_log_pos(ulonglong val) { - pending += val; + event_relay_log_pos+= val; } - /* TODO: this probably needs to be fixed */ - inline void inc_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) + + void inc_group_relay_log_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) { if (!skip_lock) pthread_mutex_lock(&data_lock); - relay_log_pos += val+pending; - pending = 0; - if (log_pos) - master_log_pos = log_pos+ val; + inc_event_relay_log_pos(val); + group_relay_log_pos= event_relay_log_pos; + strmake(group_relay_log_name,event_relay_log_name, + sizeof(group_relay_log_name)-1); + /* + If the slave does not support transactions and replicates a transaction, + users should not trust group_master_log_pos (which they can display with + SHOW SLAVE STATUS or read from relay-log.info), because to compute + group_master_log_pos the slave relies on log_pos stored in the master's + binlog, but if we are in a master's transaction these positions are always + the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does + not advance as it should on the non-transactional slave (it advances by + big leaps, whereas it should advance by small leaps). + */ + if (log_pos) // 3.23 binlogs don't have log_posx + group_master_log_pos= log_pos+ val; pthread_cond_broadcast(&data_cond); if (!skip_lock) pthread_mutex_unlock(&data_lock); } - /* - thread safe read of position - not needed if we are in the slave thread, - but required otherwise as var is a longlong - */ - inline void read_pos(ulonglong& var) - { - pthread_mutex_lock(&data_lock); - var = relay_log_pos; - pthread_mutex_unlock(&data_lock); - } int wait_for_pos(THD* thd, String* log_name, longlong log_pos, longlong timeout); @@ -334,7 +345,7 @@ typedef struct st_table_rule_ent #define TABLE_RULE_ARR_SIZE 16 #define MAX_SLAVE_ERRMSG 1024 -#define RPL_LOG_NAME (rli->master_log_name[0] ? rli->master_log_name :\ +#define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ "FIRST") diff --git a/sql/sql_class.h b/sql/sql_class.h index d3cb843ad85..7688d21c33e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -145,10 +145,12 @@ public: int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); - int update_log_index(LOG_INFO* linfo); - int purge_logs(THD* thd, const char* to_log); - int purge_logs_before_date(THD* thd, time_t purge_time); - int purge_first_log(struct st_relay_log_info* rli); + int update_log_index(LOG_INFO* linfo, bool need_update_threads); + int purge_logs(const char *to_log, bool included, + bool need_mutex, bool need_update_threads, + ulonglong *decrease_log_space); + int purge_logs_before_date(time_t purge_time); + int purge_first_log(struct st_relay_log_info* rli, bool included); bool reset_logs(THD* thd); // if we are exiting, we also want to close the index file void close(bool exiting = 0); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d0a970c98b7..ba8a4af794a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3980,7 +3980,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - mysql_bin_log.purge_logs_before_date(thd, purge_time); + mysql_bin_log.purge_logs_before_date(purge_time); } #endif mysql_slow_log.new_file(1); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 7e9b6aea7b5..0eb444b85c0 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -292,7 +292,7 @@ int purge_master_logs(THD* thd, const char* to_log) char search_file_name[FN_REFLEN]; mysql_bin_log.make_log_name(search_file_name, to_log); - int res = mysql_bin_log.purge_logs(thd, search_file_name); + int res = mysql_bin_log.purge_logs(search_file_name, 0, 1, 1, NULL); return purge_error_message(thd, res); } @@ -300,7 +300,7 @@ int purge_master_logs(THD* thd, const char* to_log) int purge_master_logs_before_date(THD* thd, time_t purge_time) { - int res = mysql_bin_log.purge_logs_before_date(thd, purge_time); + int res = mysql_bin_log.purge_logs_before_date(purge_time); return purge_error_message(thd ,res); } @@ -776,24 +776,25 @@ int reset_slave(THD *thd, MASTER_INFO* mi) error=1; goto err; } - //delete relay logs, clear relay log coordinates + // delete relay logs, clear relay log coordinates if ((error= purge_relay_logs(&mi->rli, thd, 1 /* just reset */, &errmsg))) goto err; - //Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) + // Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) mi->master_log_name[0]= 0; mi->master_log_pos= BIN_LOG_HEADER_SIZE; - //close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 + // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 end_master_info(mi); - //and delete these two files + // and delete these two files fn_format(fname, master_info_file, mysql_data_home, "", 4+32); if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) { error=1; goto err; } + // delete relay_log_info_file fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32); if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) { @@ -874,7 +875,6 @@ int change_master(THD* thd, MASTER_INFO* mi) // if we change host or port, we must reset the postion mi->master_log_name[0] = 0; mi->master_log_pos= BIN_LOG_HEADER_SIZE; - mi->rli.pending = 0; } if (lex_mi->log_file_name) @@ -883,7 +883,6 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->pos) { mi->master_log_pos= lex_mi->pos; - mi->rli.pending = 0; } DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); @@ -901,20 +900,22 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->relay_log_name) { need_relay_log_purge= 0; - strmake(mi->rli.relay_log_name,lex_mi->relay_log_name, - sizeof(mi->rli.relay_log_name)-1); + strmake(mi->rli.group_relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.group_relay_log_name)-1); + strmake(mi->rli.event_relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.event_relay_log_name)-1); } if (lex_mi->relay_log_pos) { need_relay_log_purge= 0; - mi->rli.relay_log_pos=lex_mi->relay_log_pos; + mi->rli.group_relay_log_pos= mi->rli.event_relay_log_pos= lex_mi->relay_log_pos; } flush_master_info(mi); if (need_relay_log_purge) { - mi->rli.skip_log_purge= 0; + relay_log_purge= 1; thd->proc_info="purging old relay logs"; if (purge_relay_logs(&mi->rli, thd, 0 /* not only reset, but also reinit */, @@ -928,11 +929,11 @@ int change_master(THD* thd, MASTER_INFO* mi) else { const char* msg; - mi->rli.skip_log_purge= 1; + relay_log_purge= 0; /* Relay log is already initialized */ if (init_relay_log_pos(&mi->rli, - mi->rli.relay_log_name, - mi->rli.relay_log_pos, + mi->rli.group_relay_log_name, + mi->rli.group_relay_log_pos, 0 /*no data lock*/, &msg)) { @@ -941,12 +942,12 @@ int change_master(THD* thd, MASTER_INFO* mi) DBUG_RETURN(1); } } - mi->rli.master_log_pos = mi->master_log_pos; + mi->rli.group_master_log_pos = mi->master_log_pos; DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); - strmake(mi->rli.master_log_name,mi->master_log_name, - sizeof(mi->rli.master_log_name)-1); - if (!mi->rli.master_log_name[0]) // uninitialized case - mi->rli.master_log_pos=0; + strmake(mi->rli.group_master_log_name,mi->master_log_name, + sizeof(mi->rli.group_master_log_name)-1); + if (!mi->rli.group_master_log_name[0]) // uninitialized case + mi->rli.group_master_log_pos=0; pthread_mutex_lock(&mi->rli.data_lock); mi->rli.abort_pos_wait++; From 0f33b2f0cb4f3ee3e6f1ba8739152cfcff109dca Mon Sep 17 00:00:00 2001 From: "vva@eagle.mysql.r18.ru" <> Date: Thu, 24 Apr 2003 11:37:55 -0400 Subject: [PATCH 20/21] added '.' to end of compressed string --- sql/item_strfunc.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 56e3eb2cb5f..bfc050b8a83 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2943,7 +2943,7 @@ String *Item_func_compress::val_str(String *str) */ ulong new_size= (ulong)((res->length()*120)/100)+12; - buffer.realloc((uint32)new_size + 4); + buffer.realloc((uint32)new_size + 4 + 1); Byte *body= ((Byte*)buffer.c_ptr()) + 4; if ((err= compress(body, &new_size, @@ -2956,6 +2956,15 @@ String *Item_func_compress::val_str(String *str) } int4store(buffer.c_ptr(),res->length() & 0x3FFFFFFF); + + /* This is for the stupid char fields which trim ' ': */ + char *last_char= ((char*)body)+new_size-1; + if (*last_char == ' ') + { + *++last_char= '.'; + new_size++; + } + buffer.length((uint32)new_size + 4); return &buffer; From 050d5b54879c9a29c20dad6441cd622b834fd0e6 Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Fri, 25 Apr 2003 12:31:51 +0200 Subject: [PATCH 21/21] forgot to commit this one in cset 1.1561 (replication) --- sql/ha_innodb.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index aa5d88bf177..4f955d8f79e 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -916,11 +916,11 @@ innobase_commit_low( /* Update the replication position info inside InnoDB */ trx->mysql_master_log_file_name - = active_mi->rli.master_log_name; + = active_mi->rli.group_master_log_name; trx->mysql_master_log_pos = ((ib_longlong) - (active_mi->rli.master_log_pos + - active_mi->rli.event_len + - active_mi->rli.pending)); + (active_mi->rli.group_master_log_pos + + active_mi->rli.event_len + )); } #endif /* HAVE_REPLICATION */ trx_commit_for_mysql(trx);