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.
parent 3aafd368
...@@ -52,7 +52,8 @@ debug_cflags="-DEXTRA_DEBUG -DFORCE_INIT_OF_VARS -DSAFEMALLOC -DSAFE_MUTEX -O2" ...@@ -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_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 alpha_configs="" # Not used yet
pentium_configs="" pentium_configs=""
sparc_configs="" sparc_configs=""
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
**********************************************************************/ **********************************************************************/
#define MTEST_VERSION "1.8" #define MTEST_VERSION "1.9"
#include <global.h> #include <global.h>
#include <my_sys.h> #include <my_sys.h>
...@@ -159,6 +159,8 @@ Q_SYNC_WITH_MASTER, Q_ERROR, ...@@ -159,6 +159,8 @@ Q_SYNC_WITH_MASTER, Q_ERROR,
Q_SEND, Q_REAP, Q_SEND, Q_REAP,
Q_DIRTY_CLOSE, Q_REPLACE, Q_DIRTY_CLOSE, Q_REPLACE,
Q_PING, Q_EVAL, Q_PING, Q_EVAL,
Q_RPL_PROBE, Q_ENABLE_RPL_PARSE,
Q_DISABLE_RPL_PARSE,
Q_UNKNOWN, /* Unknown command. */ Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */ Q_COMMENT, /* Comments, ignored. */
Q_COMMENT_WITH_COMMAND Q_COMMENT_WITH_COMMAND
...@@ -188,6 +190,8 @@ const char *command_names[] = { ...@@ -188,6 +190,8 @@ const char *command_names[] = {
"send", "reap", "send", "reap",
"dirty_close", "replace_result", "dirty_close", "replace_result",
"ping", "eval", "ping", "eval",
"rpl_probe", "enable_rpl_parse",
"disable_rpl_parse",
0 0
}; };
...@@ -642,6 +646,11 @@ int do_sync_with_master(struct st_query* q) ...@@ -642,6 +646,11 @@ int do_sync_with_master(struct st_query* q)
char query_buf[FN_REFLEN+128]; char query_buf[FN_REFLEN+128];
int offset = 0; int offset = 0;
char* p = q->first_argument; char* p = q->first_argument;
int rpl_parse;
rpl_parse = mysql_rpl_parse_enabled(mysql);
mysql_disable_rpl_parse(mysql);
if(*p) if(*p)
offset = atoi(p); offset = atoi(p);
...@@ -658,7 +667,10 @@ int do_sync_with_master(struct st_query* q) ...@@ -658,7 +667,10 @@ int do_sync_with_master(struct st_query* q)
if(!row[0]) if(!row[0])
die("Error on slave while syncing with master"); die("Error on slave while syncing with master");
mysql_free_result(res); mysql_free_result(res);
if(rpl_parse)
mysql_enable_rpl_parse(mysql);
return 0; return 0;
} }
...@@ -667,6 +679,11 @@ int do_save_master_pos() ...@@ -667,6 +679,11 @@ int do_save_master_pos()
MYSQL_RES* res; MYSQL_RES* res;
MYSQL_ROW row; MYSQL_ROW row;
MYSQL* mysql = &cur_con->mysql; 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")) if(mysql_query(mysql, "show master status"))
die("At line %u: failed in show master status: %d: %s", start_lineno, die("At line %u: failed in show master status: %d: %s", start_lineno,
mysql_errno(mysql), mysql_error(mysql)); mysql_errno(mysql), mysql_error(mysql));
...@@ -678,6 +695,9 @@ int do_save_master_pos() ...@@ -678,6 +695,9 @@ int do_save_master_pos()
strncpy(master_pos.file, row[0], sizeof(master_pos.file)); strncpy(master_pos.file, row[0], sizeof(master_pos.file));
master_pos.pos = strtoul(row[1], (char**) 0, 10); master_pos.pos = strtoul(row[1], (char**) 0, 10);
mysql_free_result(res); mysql_free_result(res);
if(rpl_parse)
mysql_enable_rpl_parse(mysql);
return 0; return 0;
} }
...@@ -702,6 +722,26 @@ int do_let(struct st_query* q) ...@@ -702,6 +722,26 @@ int do_let(struct st_query* q)
return var_set(var_name, var_name_end, var_val_start, p); 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) int do_sleep(struct st_query* q)
{ {
char* p=q->first_argument; char* p=q->first_argument;
...@@ -1825,6 +1865,9 @@ int main(int argc, char** argv) ...@@ -1825,6 +1865,9 @@ int main(int argc, char** argv)
case Q_DISCONNECT: case Q_DISCONNECT:
case Q_DIRTY_CLOSE: case Q_DIRTY_CLOSE:
close_connection(q); break; 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_SOURCE: do_source(q); break;
case Q_SLEEP: do_sleep(q); break; case Q_SLEEP: do_sleep(q); break;
case Q_INC: do_inc(q); break; case Q_INC: do_inc(q); break;
...@@ -1892,7 +1935,7 @@ int main(int argc, char** argv) ...@@ -1892,7 +1935,7 @@ int main(int argc, char** argv)
case Q_SAVE_MASTER_POS: do_save_master_pos(); break; case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break; case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
case Q_COMMENT: /* Ignore row */ case Q_COMMENT: /* Ignore row */
case Q_COMMENT_WITH_COMMAND: case Q_COMMENT_WITH_COMMAND:
case Q_PING: case Q_PING:
(void) mysql_ping(&cur_con->mysql); (void) mysql_ping(&cur_con->mysql);
break; break;
......
...@@ -54,3 +54,10 @@ extern const char *client_errors[]; /* Error messages */ ...@@ -54,3 +54,10 @@ extern const char *client_errors[]; /* Error messages */
#define CR_CANT_READ_CHARSET 2019 #define CR_CANT_READ_CHARSET 2019
#define CR_NET_PACKET_TOO_LARGE 2020 #define CR_NET_PACKET_TOO_LARGE 2020
#define CR_EMBEDDED_CONNECTION 2021 #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
...@@ -154,6 +154,14 @@ enum mysql_option { MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, ...@@ -154,6 +154,14 @@ enum mysql_option { MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS,
enum mysql_status { MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT, enum mysql_status { MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,
MYSQL_STATUS_USE_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 { typedef struct st_mysql {
NET net; /* Communication parameters */ NET net; /* Communication parameters */
gptr connector_fd; /* ConnectorFd for SSL */ gptr connector_fd; /* ConnectorFd for SSL */
...@@ -183,7 +191,15 @@ typedef struct st_mysql { ...@@ -183,7 +191,15 @@ typedef struct st_mysql {
struct st_mysql* master, *next_slave; struct st_mysql* master, *next_slave;
struct st_mysql* last_used_slave; /* needed for round-robin slave pick */ 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; } MYSQL;
...@@ -261,9 +277,13 @@ int STDCALL mysql_real_query(MYSQL *mysql, const char *q, ...@@ -261,9 +277,13 @@ int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
/* perform query on master */ /* perform query on master */
int STDCALL mysql_master_query(MYSQL *mysql, const char *q, int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
unsigned int length); unsigned int length);
int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
unsigned int length);
/* perform query on slave */ /* perform query on slave */
int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
unsigned int length); 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 /* enable/disable parsing of all queries to decide
if they go on master or slave */ if they go on master or slave */
...@@ -278,11 +298,22 @@ void STDCALL mysql_disable_reads_from_master(MYSQL* mysql); ...@@ -278,11 +298,22 @@ void STDCALL mysql_disable_reads_from_master(MYSQL* mysql);
/* get the value of the master read flag */ /* get the value of the master read flag */
int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql); 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 */ /* 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_create_db(MYSQL *mysql, const char *DB);
int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
int STDCALL mysql_shutdown(MYSQL *mysql); int STDCALL mysql_shutdown(MYSQL *mysql);
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
MA 02111-1307, USA */ MA 02111-1307, USA */
/* Error messages for MySQL clients */ /* 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 <global.h> #include <global.h>
#include <my_sys.h> #include <my_sys.h>
...@@ -47,6 +47,10 @@ const char *client_errors[]= ...@@ -47,6 +47,10 @@ const char *client_errors[]=
"Can't initialize character set %-.64s (path: %-.64s)", "Can't initialize character set %-.64s (path: %-.64s)",
"Got packet bigger than 'max_allowed_packet'", "Got packet bigger than 'max_allowed_packet'",
"Embedded server", "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 */ /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */
...@@ -76,6 +80,10 @@ const char *client_errors[]= ...@@ -76,6 +80,10 @@ const char *client_errors[]=
"No pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", "No pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)",
"Obteve pacote maior do que 'max_allowed_packet'", "Obteve pacote maior do que 'max_allowed_packet'",
"Embedded server" "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 */ #else /* ENGLISH */
...@@ -102,7 +110,11 @@ const char *client_errors[]= ...@@ -102,7 +110,11 @@ const char *client_errors[]=
"Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)", "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
"Can't initialize character set %-.64s (path: %-.64s)", "Can't initialize character set %-.64s (path: %-.64s)",
"Got packet bigger than 'max_allowed_packet'", "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 #endif
......
...@@ -109,6 +109,12 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, ...@@ -109,6 +109,12 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
#define reset_sigpipe(mysql) #define reset_sigpipe(mysql)
#endif #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 modified version of connect(). connect2() allows you to specify
* a timeout value, in seconds, that we should wait until we * 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) ...@@ -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, int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
unsigned int length) unsigned int length)
{ {
if(!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); length = strlen(q);
return mysql_real_query(mysql->master, q, length); 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 */ /* perform query on slave */
int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
unsigned int length) 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; MYSQL* last_used_slave, *slave_to_use = 0;
...@@ -1019,12 +1046,16 @@ int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, ...@@ -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 /* next_slave is always safe to use - we have a circular list of slaves
if there are no slaves, mysql->next_slave == mysql 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) if(!length)
length = strlen(q); 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 /* enable/disable parsing of all queries to decide
if they go on master or slave */ if they go on master or slave */
void STDCALL mysql_enable_rpl_parse(MYSQL* mysql) void STDCALL mysql_enable_rpl_parse(MYSQL* mysql)
...@@ -1060,40 +1091,100 @@ int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql) ...@@ -1060,40 +1091,100 @@ int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql)
return !(mysql->options.no_master_reads); 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 /* This function assumes we have just called SHOW SLAVE STATUS and have
read the given result and row read the given result and row
*/ */
static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row) static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
{ {
MYSQL* master; MYSQL* master;
if(mysql_num_rows(res) < 3) if(mysql_num_fields(res) < 3)
return 1; /* safety */ return 1; /* safety */
if(!(master = mysql_init(0)))
return 1;
/* use the same username and password as the original connection */ /* use the same username and password as the original connection */
master->user = mysql->user; if(!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0)))
master->passwd = mysql->passwd; return 1;
master->host = row[0];
master->port = atoi(row[2]);
master->db = mysql->db;
mysql->master = master; mysql->master = master;
return 0; 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) static inline int get_slaves_from_master(MYSQL* mysql)
{ {
if(!mysql->net.vio && !mysql_real_connect(mysql, mysql->host, mysql->user, MYSQL_RES* res = 0;
mysql->passwd, mysql->db, mysql->port, MYSQL_ROW row;
mysql->unix_socket, mysql->client_flag)) 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; 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) int STDCALL mysql_rpl_probe(MYSQL* mysql)
{ {
MYSQL_RES* res; MYSQL_RES* res = 0;
MYSQL_ROW row; MYSQL_ROW row;
int error = 1; int error = 1;
/* first determine the replication role of the server we connected to /* first determine the replication role of the server we connected to
...@@ -1103,30 +1194,34 @@ int STDCALL mysql_rpl_probe(MYSQL* mysql) ...@@ -1103,30 +1194,34 @@ int STDCALL mysql_rpl_probe(MYSQL* mysql)
a non-empty master host. However, it is more reliable to check a non-empty master host. However, it is more reliable to check
for empty master than whether the slave thread is actually running 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))) !(res = mysql_store_result(mysql)))
{
expand_error(mysql, CR_PROBE_SLAVE_STATUS);
return 1; return 1;
}
if(!(row = mysql_fetch_row(res)))
if (!(row = mysql_fetch_row(res)))
goto err; goto err;
/* check master host for emptiness/NULL */ /* 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 */ /* 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; goto err;
} }
else else
{ {
mysql->master = mysql; mysql->master = mysql;
if(get_slaves_from_master(mysql)) if (get_slaves_from_master(mysql))
goto err; goto err;
} }
error = 0; error = 0;
err: err:
mysql_free_result(res); if(res)
mysql_free_result(res);
return error; return error;
} }
...@@ -1141,7 +1236,8 @@ err: ...@@ -1141,7 +1236,8 @@ err:
mysql_master_query() or mysql_slave_query() explicitly in the place mysql_master_query() or mysql_slave_query() explicitly in the place
where we have made the wrong decision 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; const char* q_end;
q_end = (len) ? q + len : strend(q); q_end = (len) ? q + len : strend(q);
...@@ -1156,9 +1252,17 @@ int STDCALL mysql_query_goes_to_master(const char* q, int len) ...@@ -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 'l': /* lock tables or load data infile */
case 'd': /* drop or delete */ case 'd': /* drop or delete */
case 'a': /* alter */ 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: default:
return 0; return MYSQL_RPL_SLAVE;
} }
} }
...@@ -1184,8 +1288,12 @@ mysql_init(MYSQL *mysql) ...@@ -1184,8 +1288,12 @@ mysql_init(MYSQL *mysql)
else else
bzero((char*) (mysql),sizeof(*(mysql))); bzero((char*) (mysql),sizeof(*(mysql)));
mysql->options.connect_timeout=CONNECT_TIMEOUT; 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; 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 defined(SIGPIPE) && defined(THREAD)
if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE))
(void) signal(SIGPIPE,pipe_sig_handler); (void) signal(SIGPIPE,pipe_sig_handler);
...@@ -1723,6 +1831,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -1723,6 +1831,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->reconnect=reconnect; mysql->reconnect=reconnect;
} }
if (mysql->options.rpl_probe && mysql_rpl_probe(mysql))
goto error;
DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
reset_sigpipe(mysql); reset_sigpipe(mysql);
DBUG_RETURN(mysql); DBUG_RETURN(mysql);
...@@ -1864,6 +1975,21 @@ mysql_close(MYSQL *mysql) ...@@ -1864,6 +1975,21 @@ mysql_close(MYSQL *mysql)
/* ((VioConnectorFd*)(mysql->connector_fd))->delete(); /* ((VioConnectorFd*)(mysql->connector_fd))->delete();
mysql->connector_fd = 0;*/ mysql->connector_fd = 0;*/
#endif /* HAVE_OPENSSL */ #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) if (mysql->free_me)
my_free((gptr) mysql,MYF(0)); my_free((gptr) mysql,MYF(0));
} }
...@@ -1882,6 +2008,67 @@ mysql_query(MYSQL *mysql, const char *query) ...@@ -1882,6 +2008,67 @@ mysql_query(MYSQL *mysql, const char *query)
return mysql_real_query(mysql,query, (uint) strlen(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. Send the query and return so we can do something else.
Needs to be followed by mysql_read_query_result() when we want to Needs to be followed by mysql_read_query_result() when we want to
...@@ -1891,6 +2078,20 @@ mysql_query(MYSQL *mysql, const char *query) ...@@ -1891,6 +2078,20 @@ mysql_query(MYSQL *mysql, const char *query)
int STDCALL int STDCALL
mysql_send_query(MYSQL* mysql, const char* query, uint length) 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); return simple_command(mysql, COM_QUERY, query, length, 1);
} }
...@@ -1902,6 +2103,11 @@ int STDCALL mysql_read_query_result(MYSQL *mysql) ...@@ -1902,6 +2103,11 @@ int STDCALL mysql_read_query_result(MYSQL *mysql)
uint length; uint length;
DBUG_ENTER("mysql_read_query_result"); 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) if ((length = net_safe_read(mysql)) == packet_error)
DBUG_RETURN(-1); DBUG_RETURN(-1);
free_old_query(mysql); /* Free old result */ free_old_query(mysql); /* Free old result */
...@@ -1948,7 +2154,8 @@ mysql_real_query(MYSQL *mysql, const char *query, uint length) ...@@ -1948,7 +2154,8 @@ mysql_real_query(MYSQL *mysql, const char *query, uint length)
DBUG_ENTER("mysql_real_query"); DBUG_ENTER("mysql_real_query");
DBUG_PRINT("enter",("handle: %lx",mysql)); DBUG_PRINT("enter",("handle: %lx",mysql));
DBUG_PRINT("query",("Query = \"%s\"",query)); 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(-1);
DBUG_RETURN(mysql_read_query_result(mysql)); DBUG_RETURN(mysql_read_query_result(mysql));
} }
...@@ -2020,6 +2227,9 @@ mysql_store_result(MYSQL *mysql) ...@@ -2020,6 +2227,9 @@ mysql_store_result(MYSQL *mysql)
MYSQL_RES *result; MYSQL_RES *result;
DBUG_ENTER("mysql_store_result"); DBUG_ENTER("mysql_store_result");
/* read from the actually used connection */
mysql = mysql->last_used_con;
if (!mysql->fields) if (!mysql->fields)
DBUG_RETURN(0); DBUG_RETURN(0);
if (mysql->status != MYSQL_STATUS_GET_RESULT) if (mysql->status != MYSQL_STATUS_GET_RESULT)
...@@ -2072,6 +2282,8 @@ mysql_use_result(MYSQL *mysql) ...@@ -2072,6 +2282,8 @@ mysql_use_result(MYSQL *mysql)
MYSQL_RES *result; MYSQL_RES *result;
DBUG_ENTER("mysql_use_result"); DBUG_ENTER("mysql_use_result");
mysql = mysql->last_used_con;
if (!mysql->fields) if (!mysql->fields)
DBUG_RETURN(0); DBUG_RETURN(0);
if (mysql->status != MYSQL_STATUS_GET_RESULT) if (mysql->status != MYSQL_STATUS_GET_RESULT)
...@@ -2525,32 +2737,32 @@ uint STDCALL mysql_field_tell(MYSQL_RES *res) ...@@ -2525,32 +2737,32 @@ uint STDCALL mysql_field_tell(MYSQL_RES *res)
unsigned int STDCALL mysql_field_count(MYSQL *mysql) 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) 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) 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) uint STDCALL mysql_errno(MYSQL *mysql)
{ {
return (mysql)->net.last_errno; return mysql->net.last_errno;
} }
char * STDCALL mysql_error(MYSQL *mysql) char * STDCALL mysql_error(MYSQL *mysql)
{ {
return (mysql)->net.last_error; return mysql->net.last_error;
} }
char *STDCALL mysql_info(MYSQL *mysql) char *STDCALL mysql_info(MYSQL *mysql)
{ {
return (mysql)->info; return mysql->info;
} }
ulong STDCALL mysql_thread_id(MYSQL *mysql) ulong STDCALL mysql_thread_id(MYSQL *mysql)
......
...@@ -124,6 +124,7 @@ USE_RUNNING_SERVER=1 ...@@ -124,6 +124,7 @@ USE_RUNNING_SERVER=1
DO_GCOV="" DO_GCOV=""
DO_GDB="" DO_GDB=""
DO_DDD="" DO_DDD=""
DO_CLIENT_GDB=""
SLEEP_TIME=2 SLEEP_TIME=2
DBUSER="" DBUSER=""
...@@ -165,13 +166,19 @@ while test $# -gt 0; do ...@@ -165,13 +166,19 @@ while test $# -gt 0; do
;; ;;
--gdb ) --gdb )
if [ x$BINARY_DIST = x1 ] ; then 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 fi
DO_GDB=1 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 ) --ddd )
if [ x$BINARY_DIST = x1 ] ; then 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 fi
DO_DDD=1 DO_DDD=1
;; ;;
...@@ -273,7 +280,10 @@ then ...@@ -273,7 +280,10 @@ then
fi 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_MASTER_INIT=$MYSQL_TMP_DIR/gdbinit.master
GDB_SLAVE_INIT=$MYSQL_TMP_DIR/gdbinit.slave GDB_SLAVE_INIT=$MYSQL_TMP_DIR/gdbinit.slave
GCOV_MSG=$MYSQL_TMP_DIR/mysqld-gcov.out GCOV_MSG=$MYSQL_TMP_DIR/mysqld-gcov.out
...@@ -316,6 +326,15 @@ show_failed_diff () ...@@ -316,6 +326,15 @@ show_failed_diff ()
fi 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 () { error () {
$ECHO "Error: $1" $ECHO "Error: $1"
exit 1 exit 1
...@@ -694,8 +713,13 @@ run_testcase () ...@@ -694,8 +713,13 @@ run_testcase ()
if [ -f $tf ] ; then if [ -f $tf ] ; then
$RM -f r/$tname.*reject $RM -f r/$tname.*reject
mytime=`$TIME -p $MYSQL_TEST -R r/$tname.result $EXTRA_MYSQL_TEST_OPT \ mysql_test_args="-R r/$tname.result $EXTRA_MYSQL_TEST_OPT"
< $tf 2> $TIMEFILE` 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=$? res=$?
if [ $res = 0 ]; then if [ $res = 0 ]; then
......
n
1
2
3
4
5
n
1
2
3
4
n
1
2
3
4
n
1
2
3
4
5
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;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment