From 24f89c754ae9d7b8c40c7ae89857ce5d928fad9e Mon Sep 17 00:00:00 2001 From: "sasha@mysql.sashanet.com" <> Date: Fri, 8 Jun 2001 19:24:41 -0600 Subject: [PATCH] auto-magic replication redirection logic support in the client, compiles, passes test suite, does magic, has bugs, but none that I know at this point. Have not tested everything yet, though. Changed a lot of code in the client, but normal stuff appears to be working. --- BUILD/SETUP.sh | 3 +- client/mysqltest.c | 49 +++++- include/errmsg.h | 7 + include/mysql.h | 39 ++++- libmysql/errmsg.c | 16 +- libmysql/libmysql.c | 288 +++++++++++++++++++++++++++++----- mysql-test/mysql-test-run.sh | 34 +++- mysql-test/r/rpl_magic.result | 22 +++ mysql-test/t/rpl_magic.test | 30 ++++ 9 files changed, 435 insertions(+), 53 deletions(-) create mode 100644 mysql-test/r/rpl_magic.result create mode 100644 mysql-test/t/rpl_magic.test diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 778625e9e75..cf5405565b8 100644 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -52,7 +52,8 @@ debug_cflags="-DEXTRA_DEBUG -DFORCE_INIT_OF_VARS -DSAFEMALLOC -DSAFE_MUTEX -O2" base_cxxflags="-felide-constructors -fno-exceptions -fno-rtti" -base_configs="--prefix=/usr/local/mysql --enable-assembler --with-extra-charsets=complex --enable-thread-safe-client --with-mysqld-ldflags=-all-static" +base_configs="--prefix=/usr/local/mysql --enable-assembler --with-extra-charsets=complex --enable-thread-safe-client --with-mysqld-ldflags=-all-static \ + --with-client-ldflags=-all-static" alpha_configs="" # Not used yet pentium_configs="" sparc_configs="" diff --git a/client/mysqltest.c b/client/mysqltest.c index f1fcae6bf6d..cffac398255 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -43,7 +43,7 @@ **********************************************************************/ -#define MTEST_VERSION "1.8" +#define MTEST_VERSION "1.9" #include #include @@ -159,6 +159,8 @@ Q_SYNC_WITH_MASTER, Q_ERROR, Q_SEND, Q_REAP, Q_DIRTY_CLOSE, Q_REPLACE, Q_PING, Q_EVAL, +Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, +Q_DISABLE_RPL_PARSE, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ Q_COMMENT_WITH_COMMAND @@ -188,6 +190,8 @@ const char *command_names[] = { "send", "reap", "dirty_close", "replace_result", "ping", "eval", + "rpl_probe", "enable_rpl_parse", + "disable_rpl_parse", 0 }; @@ -642,6 +646,11 @@ int do_sync_with_master(struct st_query* q) char query_buf[FN_REFLEN+128]; int offset = 0; char* p = q->first_argument; + int rpl_parse; + + rpl_parse = mysql_rpl_parse_enabled(mysql); + mysql_disable_rpl_parse(mysql); + if(*p) offset = atoi(p); @@ -658,7 +667,10 @@ int do_sync_with_master(struct st_query* q) if(!row[0]) die("Error on slave while syncing with master"); mysql_free_result(res); - + + if(rpl_parse) + mysql_enable_rpl_parse(mysql); + return 0; } @@ -667,6 +679,11 @@ int do_save_master_pos() MYSQL_RES* res; MYSQL_ROW row; MYSQL* mysql = &cur_con->mysql; + int rpl_parse; + + rpl_parse = mysql_rpl_parse_enabled(mysql); + mysql_disable_rpl_parse(mysql); + if(mysql_query(mysql, "show master status")) die("At line %u: failed in show master status: %d: %s", start_lineno, mysql_errno(mysql), mysql_error(mysql)); @@ -678,6 +695,9 @@ int do_save_master_pos() strncpy(master_pos.file, row[0], sizeof(master_pos.file)); master_pos.pos = strtoul(row[1], (char**) 0, 10); mysql_free_result(res); + + if(rpl_parse) + mysql_enable_rpl_parse(mysql); return 0; } @@ -702,6 +722,26 @@ int do_let(struct st_query* q) return var_set(var_name, var_name_end, var_val_start, p); } +int do_rpl_probe(struct st_query* __attribute__((unused)) q) +{ + if(mysql_rpl_probe(&cur_con->mysql)) + die("Failed in mysql_rpl_probe(): %s", mysql_error(&cur_con->mysql)); + return 0; +} + +int do_enable_rpl_parse(struct st_query* __attribute__((unused)) q) +{ + mysql_enable_rpl_parse(&cur_con->mysql); + return 0; +} + +int do_disable_rpl_parse(struct st_query* __attribute__((unused)) q) +{ + mysql_disable_rpl_parse(&cur_con->mysql); + return 0; +} + + int do_sleep(struct st_query* q) { char* p=q->first_argument; @@ -1825,6 +1865,9 @@ int main(int argc, char** argv) case Q_DISCONNECT: case Q_DIRTY_CLOSE: close_connection(q); break; + case Q_RPL_PROBE: do_rpl_probe(q); break; + case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break; + case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break; case Q_SOURCE: do_source(q); break; case Q_SLEEP: do_sleep(q); break; case Q_INC: do_inc(q); break; @@ -1892,7 +1935,7 @@ int main(int argc, char** argv) case Q_SAVE_MASTER_POS: do_save_master_pos(); break; case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break; case Q_COMMENT: /* Ignore row */ - case Q_COMMENT_WITH_COMMAND: + case Q_COMMENT_WITH_COMMAND: case Q_PING: (void) mysql_ping(&cur_con->mysql); break; diff --git a/include/errmsg.h b/include/errmsg.h index 12a3ee5557a..427174ffa53 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -54,3 +54,10 @@ extern const char *client_errors[]; /* Error messages */ #define CR_CANT_READ_CHARSET 2019 #define CR_NET_PACKET_TOO_LARGE 2020 #define CR_EMBEDDED_CONNECTION 2021 +#define CR_PROBE_SLAVE_STATUS 2022 +#define CR_PROBE_SLAVE_HOSTS 2023 +#define CR_PROBE_SLAVE_CONNECT 2024 +#define CR_PROBE_MASTER_CONNECT 2025 + + + diff --git a/include/mysql.h b/include/mysql.h index 5d06f96d223..4ce7e80bcb9 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -154,6 +154,14 @@ enum mysql_option { MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, enum mysql_status { MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT, MYSQL_STATUS_USE_RESULT}; +/* there are three types of queries - the ones that have to go to + the master, the ones that go to a slave, and the adminstrative + type which must happen on the pivot connectioin +*/ +enum mysql_rpl_type { MYSQL_RPL_MASTER, MYSQL_RPL_SLAVE, + MYSQL_RPL_ADMIN }; + + typedef struct st_mysql { NET net; /* Communication parameters */ gptr connector_fd; /* ConnectorFd for SSL */ @@ -183,7 +191,15 @@ typedef struct st_mysql { struct st_mysql* master, *next_slave; struct st_mysql* last_used_slave; /* needed for round-robin slave pick */ - my_bool is_slave; /* will be false for a lone connection */ + struct st_mysql* last_used_con; /* needed for send/read/store/use + result to work + correctly with replication + */ + my_bool rpl_pivot; /* set if this is the original connection, + not a master or a slave we have added though + mysql_rpl_probe() or mysql_set_master()/ + mysql_add_slave() + */ } MYSQL; @@ -261,9 +277,13 @@ int STDCALL mysql_real_query(MYSQL *mysql, const char *q, /* perform query on master */ int STDCALL mysql_master_query(MYSQL *mysql, const char *q, unsigned int length); +int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned int length); /* perform query on slave */ int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, unsigned int length); +int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned int length); /* enable/disable parsing of all queries to decide if they go on master or slave */ @@ -278,11 +298,22 @@ void STDCALL mysql_disable_reads_from_master(MYSQL* mysql); /* get the value of the master read flag */ int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql); -int STDCALL mysql_query_goes_to_master(const char* q, int len); +enum mysql_rpl_type STDCALL mysql_rpl_query_type(const char* q, int len); /* discover the master and its slaves */ -int STDCALL mysql_rpl_probe(MYSQL* mysql); - +int STDCALL mysql_rpl_probe(MYSQL* mysql); + +/* set the master, close/free the old one, if it is not a pivot */ +int STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); +int STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); + + int STDCALL mysql_create_db(MYSQL *mysql, const char *DB); int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); int STDCALL mysql_shutdown(MYSQL *mysql); diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 71fc25fd77c..f0a1692453a 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -16,7 +16,7 @@ MA 02111-1307, USA */ /* Error messages for MySQL clients */ -/* error messages for the demon is in share/language/errmsg.sys */ +/* error messages for the daemon is in share/language/errmsg.sys */ #include #include @@ -47,6 +47,10 @@ const char *client_errors[]= "Can't initialize character set %-.64s (path: %-.64s)", "Got packet bigger than 'max_allowed_packet'", "Embedded server", + "Error on SHOW SLAVE STATUS: %-.64s", + "Error on SHOW SLAVE HOSTS: %-.64s", + "Error connecting to slave: %-.64s", + "Error connecting to master: %-.64s" }; /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ @@ -76,6 +80,10 @@ const char *client_errors[]= "Não pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", "Obteve pacote maior do que 'max_allowed_packet'", "Embedded server" + "Error on SHOW SLAVE STATUS: %-.64s", + "Error on SHOW SLAVE HOSTS: %-.64s", + "Error connecting to slave: %-.64s", + "Error connecting to master: %-.64s" }; #else /* ENGLISH */ @@ -102,7 +110,11 @@ const char *client_errors[]= "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)", "Can't initialize character set %-.64s (path: %-.64s)", "Got packet bigger than 'max_allowed_packet'", - "Embedded server" + "Embedded server", + "Error on SHOW SLAVE STATUS:", + "Error on SHOW SLAVE HOSTS:", + "Error connecting to slave:", + "Error connecting to master:" }; #endif diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index c45dce981a8..d6f5b7c523f 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -109,6 +109,12 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, #define reset_sigpipe(mysql) #endif +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, + const char* user, + const char* passwd); + + /**************************************************************************** * A modified version of connect(). connect2() allows you to specify * a timeout value, in seconds, that we should wait until we @@ -1001,14 +1007,35 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) int STDCALL mysql_master_query(MYSQL *mysql, const char *q, unsigned int length) { - if(!length) - length = strlen(q); - return mysql_real_query(mysql->master, q, length); + if(mysql_master_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); } +int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned int length) +{ + MYSQL*master = mysql->master; + if (!length) + length = strlen(q); + if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) + return 1; + mysql->last_used_con = master; + return simple_command(master, COM_QUERY, q, length, 1); +} + + /* perform query on slave */ int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, unsigned int length) +{ + if(mysql_slave_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); +} + +int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned int length) { MYSQL* last_used_slave, *slave_to_use = 0; @@ -1019,12 +1046,16 @@ int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, /* next_slave is always safe to use - we have a circular list of slaves if there are no slaves, mysql->next_slave == mysql */ - mysql->last_used_slave = slave_to_use; + mysql->last_used_con = mysql->last_used_slave = slave_to_use; if(!length) length = strlen(q); - return mysql_real_query(slave_to_use, q, length); + if(!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, + 0,0,0,0)) + return 1; + return simple_command(slave_to_use, COM_QUERY, q, length, 1); } + /* enable/disable parsing of all queries to decide if they go on master or slave */ void STDCALL mysql_enable_rpl_parse(MYSQL* mysql) @@ -1060,40 +1091,100 @@ int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql) return !(mysql->options.no_master_reads); } +/* We may get an error while doing replication internals. + In this case, we add a special explanation to the original + error +*/ +static inline void expand_error(MYSQL* mysql, int error) +{ + char tmp[MYSQL_ERRMSG_SIZE]; + char* p, *tmp_end; + tmp_end = strnmov(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE); + p = strnmov(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE); + memcpy(p, tmp, tmp_end - tmp); + mysql->net.last_errno = error; +} + /* This function assumes we have just called SHOW SLAVE STATUS and have read the given result and row */ static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row) { MYSQL* master; - if(mysql_num_rows(res) < 3) + if(mysql_num_fields(res) < 3) return 1; /* safety */ - if(!(master = mysql_init(0))) - return 1; - + /* use the same username and password as the original connection */ - master->user = mysql->user; - master->passwd = mysql->passwd; - master->host = row[0]; - master->port = atoi(row[2]); - master->db = mysql->db; + if(!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0))) + return 1; mysql->master = master; return 0; } +/* assuming we already know that mysql points to a master connection, + retrieve all the slaves +*/ static inline int get_slaves_from_master(MYSQL* mysql) { - if(!mysql->net.vio && !mysql_real_connect(mysql, mysql->host, mysql->user, - mysql->passwd, mysql->db, mysql->port, - mysql->unix_socket, mysql->client_flag)) + MYSQL_RES* res = 0; + MYSQL_ROW row; + int error = 1; + int has_auth_info; + if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0)) + { + expand_error(mysql, CR_PROBE_MASTER_CONNECT); return 1; - /* more to be written */ - return 0; + } + + if (mysql_query(mysql, "SHOW SLAVE HOSTS") || + !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_HOSTS); + return 1; + } + + switch (mysql_num_fields(res)) + { + case 3: has_auth_info = 0; break; + case 5: has_auth_info = 1; break; + default: + goto err; + } + + while ((row = mysql_fetch_row(res))) + { + MYSQL* slave; + const char* tmp_user, *tmp_pass; + + if (has_auth_info) + { + tmp_user = row[3]; + tmp_pass = row[4]; + } + else + { + tmp_user = mysql->user; + tmp_pass = mysql->passwd; + } + + if(!(slave = spawn_init(mysql, row[1], atoi(row[2]), + tmp_user, tmp_pass))) + goto err; + + /* Now add slave into the circular linked list */ + slave->next_slave = mysql->next_slave; + mysql->next_slave = slave; + } + error = 0; +err: + if(res) + mysql_free_result(res); + return error; } int STDCALL mysql_rpl_probe(MYSQL* mysql) { - MYSQL_RES* res; + MYSQL_RES* res = 0; MYSQL_ROW row; int error = 1; /* first determine the replication role of the server we connected to @@ -1103,30 +1194,34 @@ int STDCALL mysql_rpl_probe(MYSQL* mysql) a non-empty master host. However, it is more reliable to check for empty master than whether the slave thread is actually running */ - if(mysql_query(mysql, "SHOW SLAVE STATUS") || + if (mysql_query(mysql, "SHOW SLAVE STATUS") || !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_STATUS); return 1; - - if(!(row = mysql_fetch_row(res))) + } + + if (!(row = mysql_fetch_row(res))) goto err; /* check master host for emptiness/NULL */ - if(row[0] && *(row[0])) + if (row[0] && *(row[0])) { /* this is a slave, ask it for the master */ - if(get_master(mysql, res, row) || get_slaves_from_master(mysql)) + if (get_master(mysql, res, row) || get_slaves_from_master(mysql)) goto err; } else { mysql->master = mysql; - if(get_slaves_from_master(mysql)) + if (get_slaves_from_master(mysql)) goto err; } error = 0; err: - mysql_free_result(res); + if(res) + mysql_free_result(res); return error; } @@ -1141,7 +1236,8 @@ err: mysql_master_query() or mysql_slave_query() explicitly in the place where we have made the wrong decision */ -int STDCALL mysql_query_goes_to_master(const char* q, int len) +enum mysql_rpl_type +STDCALL mysql_rpl_query_type(const char* q, int len) { const char* q_end; q_end = (len) ? q + len : strend(q); @@ -1156,9 +1252,17 @@ int STDCALL mysql_query_goes_to_master(const char* q, int len) case 'l': /* lock tables or load data infile */ case 'd': /* drop or delete */ case 'a': /* alter */ - return 1; + return MYSQL_RPL_MASTER; + case 'c': /* create or check */ + return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_MASTER ; + case 's': /* select or show */ + return tolower(q[1] == 'h') ? MYSQL_RPL_ADMIN : MYSQL_RPL_SLAVE; + case 'f': /* flush */ + case 'r': /* repair */ + case 'g': /* grant */ + return MYSQL_RPL_ADMIN; default: - return 0; + return MYSQL_RPL_SLAVE; } } @@ -1184,8 +1288,12 @@ mysql_init(MYSQL *mysql) else bzero((char*) (mysql),sizeof(*(mysql))); mysql->options.connect_timeout=CONNECT_TIMEOUT; - mysql->next_slave = mysql->master = mysql; + mysql->last_used_con = mysql->next_slave = mysql->master = mysql; mysql->last_used_slave = 0; + /* By default, we are a replication pivot. The caller must reset it + after we return if this is not the case. + */ + mysql->rpl_pivot = 1; #if defined(SIGPIPE) && defined(THREAD) if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) (void) signal(SIGPIPE,pipe_sig_handler); @@ -1723,6 +1831,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, mysql->reconnect=reconnect; } + if (mysql->options.rpl_probe && mysql_rpl_probe(mysql)) + goto error; + DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); reset_sigpipe(mysql); DBUG_RETURN(mysql); @@ -1864,6 +1975,21 @@ mysql_close(MYSQL *mysql) /* ((VioConnectorFd*)(mysql->connector_fd))->delete(); mysql->connector_fd = 0;*/ #endif /* HAVE_OPENSSL */ + + /* free/close slave list */ + if (mysql->rpl_pivot) + { + MYSQL* tmp; + for (tmp = mysql->next_slave; tmp != mysql; ) + { + /* trick to avoid following freed pointer */ + MYSQL* tmp1 = tmp->next_slave; + mysql_close(tmp); + tmp = tmp1; + } + } + if(mysql != mysql->master) + mysql_close(mysql->master); if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } @@ -1882,6 +2008,67 @@ mysql_query(MYSQL *mysql, const char *query) return mysql_real_query(mysql,query, (uint) strlen(query)); } +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + MYSQL* child; + if (!(child = mysql_init(0))) + return 0; + + child->options.user = my_strdup((user) ? user : + (parent->user ? parent->user : + parent->options.user), MYF(0)); + child->options.password = my_strdup((passwd) ? passwd : (parent->passwd ? + parent->passwd : + parent->options.password), MYF(0)); + child->options.port = port; + child->options.host = my_strdup((host) ? host : (parent->host ? + parent->host : + parent->options.host), MYF(0)); + if(parent->db) + child->options.db = my_strdup(parent->db, MYF(0)); + else if(parent->options.db) + child->options.db = my_strdup(parent->options.db, MYF(0)); + + child->options.rpl_parse = child->options.rpl_probe = child->rpl_pivot = 0; + + return child; +} + + +int +STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + if (mysql->master != mysql && !mysql->master->rpl_pivot) + mysql_close(mysql->master); + if(!(mysql->master = spawn_init(mysql, host, port, user, passwd))) + return 1; + mysql->master->rpl_pivot = 0; + mysql->master->options.rpl_parse = 0; + mysql->master->options.rpl_probe = 0; + return 0; +} + +int +STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + MYSQL* slave; + if(!(slave = spawn_init(mysql, host, port, user, passwd))) + return 1; + slave->next_slave = mysql->next_slave; + mysql->next_slave = slave; + return 0; +} + + /* Send the query and return so we can do something else. Needs to be followed by mysql_read_query_result() when we want to @@ -1891,6 +2078,20 @@ mysql_query(MYSQL *mysql, const char *query) int STDCALL mysql_send_query(MYSQL* mysql, const char* query, uint length) { + if (mysql->options.rpl_parse && mysql->rpl_pivot) + { + switch (mysql_rpl_query_type(query, length)) + { + case MYSQL_RPL_MASTER: + return mysql_master_send_query(mysql, query, length); + case MYSQL_RPL_SLAVE: + return mysql_slave_send_query(mysql, query, length); + case MYSQL_RPL_ADMIN: /*fall through */ + } + } + + mysql->last_used_con = mysql; + return simple_command(mysql, COM_QUERY, query, length, 1); } @@ -1902,6 +2103,11 @@ int STDCALL mysql_read_query_result(MYSQL *mysql) uint length; DBUG_ENTER("mysql_read_query_result"); + /* read from the connection which we actually used, which + could differ from the original connection if we have slaves + */ + mysql = mysql->last_used_con; + if ((length = net_safe_read(mysql)) == packet_error) DBUG_RETURN(-1); free_old_query(mysql); /* Free old result */ @@ -1948,7 +2154,8 @@ mysql_real_query(MYSQL *mysql, const char *query, uint length) DBUG_ENTER("mysql_real_query"); DBUG_PRINT("enter",("handle: %lx",mysql)); DBUG_PRINT("query",("Query = \"%s\"",query)); - if (simple_command(mysql,COM_QUERY,query,length,1)) + + if (mysql_send_query(mysql,query,length)) DBUG_RETURN(-1); DBUG_RETURN(mysql_read_query_result(mysql)); } @@ -2020,6 +2227,9 @@ mysql_store_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("mysql_store_result"); + /* read from the actually used connection */ + mysql = mysql->last_used_con; + if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -2072,6 +2282,8 @@ mysql_use_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("mysql_use_result"); + mysql = mysql->last_used_con; + if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -2525,32 +2737,32 @@ uint STDCALL mysql_field_tell(MYSQL_RES *res) unsigned int STDCALL mysql_field_count(MYSQL *mysql) { - return mysql->field_count; + return mysql->last_used_con->field_count; } my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) { - return (mysql)->affected_rows; + return mysql->last_used_con->affected_rows; } my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) { - return (mysql)->insert_id; + return mysql->last_used_con->insert_id; } uint STDCALL mysql_errno(MYSQL *mysql) { - return (mysql)->net.last_errno; + return mysql->net.last_errno; } char * STDCALL mysql_error(MYSQL *mysql) { - return (mysql)->net.last_error; + return mysql->net.last_error; } char *STDCALL mysql_info(MYSQL *mysql) { - return (mysql)->info; + return mysql->info; } ulong STDCALL mysql_thread_id(MYSQL *mysql) diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 969255d5e6e..93bb00687a5 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -124,6 +124,7 @@ USE_RUNNING_SERVER=1 DO_GCOV="" DO_GDB="" DO_DDD="" +DO_CLIENT_GDB="" SLEEP_TIME=2 DBUSER="" @@ -165,13 +166,19 @@ while test $# -gt 0; do ;; --gdb ) if [ x$BINARY_DIST = x1 ] ; then - $ECHO "Note: you will get more meaningful output on a source distribution compiled with debugging option when running tests with -gdb option" + $ECHO "Note: you will get more meaningful output on a source distribution compiled with debugging option when running tests with --gdb option" fi DO_GDB=1 ;; + --client-gdb ) + if [ x$BINARY_DIST = x1 ] ; then + $ECHO "Note: you will get more meaningful output on a source distribution compiled with debugging option when running tests with --client-gdb option" + fi + DO_CLIENT_GDB=1 + ;; --ddd ) if [ x$BINARY_DIST = x1 ] ; then - $ECHO "Note: you will get more meaningful output on a source distribution compiled with debugging option when running tests with -gdb option" + $ECHO "Note: you will get more meaningful output on a source distribution compiled with debugging option when running tests with --ddd option" fi DO_DDD=1 ;; @@ -273,7 +280,10 @@ then fi -MYSQL_TEST="$MYSQL_TEST --no-defaults --socket=$MASTER_MYSOCK --database=$DB --user=$DBUSER --password=$DBPASSWD --silent -v --tmpdir=$MYSQL_TMP_DIR" +MYSQL_TEST_ARGS="--no-defaults --socket=$MASTER_MYSOCK --database=$DB --user=$DBUSER --password=$DBPASSWD --silent -v --tmpdir=$MYSQL_TMP_DIR" +MYSQL_TEST_BIN=$MYSQL_TEST +MYSQL_TEST="$MYSQL_TEST $MYSQL_TEST_ARGS" +GDB_CLIENT_INIT=$MYSQL_TMP_DIR/gdbinit.client GDB_MASTER_INIT=$MYSQL_TMP_DIR/gdbinit.master GDB_SLAVE_INIT=$MYSQL_TMP_DIR/gdbinit.slave GCOV_MSG=$MYSQL_TMP_DIR/mysqld-gcov.out @@ -316,6 +326,15 @@ show_failed_diff () fi } +do_gdb_test () +{ + mysql_test_args="$MYSQL_TEST_ARGS $1" + $ECHO "set args $mysql_test_args < $2" > $GDB_CLIENT_INIT + echo "Set breakpoints ( if needed) and type 'run' in gdb window" + #this xterm should not be backgrounded + xterm -title "Client" -e gdb -x $GDB_CLIENT_INIT $MYSQL_TEST_BIN +} + error () { $ECHO "Error: $1" exit 1 @@ -694,8 +713,13 @@ run_testcase () if [ -f $tf ] ; then $RM -f r/$tname.*reject - mytime=`$TIME -p $MYSQL_TEST -R r/$tname.result $EXTRA_MYSQL_TEST_OPT \ - < $tf 2> $TIMEFILE` + mysql_test_args="-R r/$tname.result $EXTRA_MYSQL_TEST_OPT" + if [ -z "$DO_CLIENT_GDB" ] ; then + mytime=`$TIME -p $MYSQL_TEST $mysql_test_args < $tf 2> $TIMEFILE` + else + do_gdb_test "$mysql_test_args" "$tf" + fi + res=$? if [ $res = 0 ]; then diff --git a/mysql-test/r/rpl_magic.result b/mysql-test/r/rpl_magic.result new file mode 100644 index 00000000000..449a6bca68c --- /dev/null +++ b/mysql-test/r/rpl_magic.result @@ -0,0 +1,22 @@ +n +1 +2 +3 +4 +5 +n +1 +2 +3 +4 +n +1 +2 +3 +4 +n +1 +2 +3 +4 +5 diff --git a/mysql-test/t/rpl_magic.test b/mysql-test/t/rpl_magic.test new file mode 100644 index 00000000000..18f1cea34a3 --- /dev/null +++ b/mysql-test/t/rpl_magic.test @@ -0,0 +1,30 @@ +source include/master-slave.inc; + +#first, make sure the slave has had enough time to register +connection master; +save_master_pos; +connection slave; +sync_with_master; + +#discover slaves +connection master; +rpl_probe; + +#turn on master/slave query direction auto-magic +enable_rpl_parse; +drop table if exists t1; +create table t1 ( n int); +insert into t1 values (1),(2),(3),(4); +disable_rpl_parse; +save_master_pos; +enable_rpl_parse; +connection slave; +sync_with_master; +insert into t1 values(5); +connection master; +select * from t1; +select * from t1; +disable_rpl_parse; +select * from t1; +connection slave; +select * from t1;