mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
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.
This commit is contained in:
parent
3aafd368a4
commit
24f89c754a
9 changed files with 435 additions and 53 deletions
|
@ -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=""
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
**********************************************************************/
|
||||
|
||||
#define MTEST_VERSION "1.8"
|
||||
#define MTEST_VERSION "1.9"
|
||||
|
||||
#include <global.h>
|
||||
#include <my_sys.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <global.h>
|
||||
#include <my_sys.h>
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
22
mysql-test/r/rpl_magic.result
Normal file
22
mysql-test/r/rpl_magic.result
Normal file
|
@ -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
|
30
mysql-test/t/rpl_magic.test
Normal file
30
mysql-test/t/rpl_magic.test
Normal file
|
@ -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;
|
Loading…
Reference in a new issue