1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00
This commit is contained in:
tim@black.box
2001-09-16 22:10:50 -04:00
10 changed files with 541 additions and 42 deletions

View File

@ -98,6 +98,11 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
#define set_sigpipe(mysql)
#define reset_sigpipe(mysql)
static MYSQL* spawn_init(MYSQL* parent, const char* host,
unsigned int port,
const char* user,
const char* passwd);
/*****************************************************************************
** read a packet from server. Give error message if socket was down
** or packet is an error message
@ -762,6 +767,301 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
return 0;
}
/* perform query on master */
int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
unsigned int 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;
if((last_used_slave = mysql->last_used_slave))
slave_to_use = last_used_slave->next_slave;
else
slave_to_use = mysql->next_slave;
/* 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_con = mysql->last_used_slave = slave_to_use;
if(!length)
length = strlen(q);
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)
{
mysql->options.rpl_parse = 1;
}
void STDCALL mysql_disable_rpl_parse(MYSQL* mysql)
{
mysql->options.rpl_parse = 0;
}
/* get the value of the parse flag */
int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql)
{
return mysql->options.rpl_parse;
}
/* enable/disable reads from master */
void STDCALL mysql_enable_reads_from_master(MYSQL* mysql)
{
mysql->options.no_master_reads = 0;
}
void STDCALL mysql_disable_reads_from_master(MYSQL* mysql)
{
mysql->options.no_master_reads = 1;
}
/* get the value of the master read flag */
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_fields(res) < 3)
return 1; /* safety */
/* use the same username and password as the original connection */
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)
{
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;
}
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 = 0;
MYSQL_ROW row;
int error = 1;
/* first determine the replication role of the server we connected to
the most reliable way to do this is to run SHOW SLAVE STATUS and see
if we have a non-empty master host. This is still not fool-proof -
it is not a sin to have a master that has a dormant slave thread with
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") ||
!(res = mysql_store_result(mysql)))
{
expand_error(mysql, CR_PROBE_SLAVE_STATUS);
return 1;
}
if (!(row = mysql_fetch_row(res)))
goto err;
/* check master host for emptiness/NULL */
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))
goto err;
}
else
{
mysql->master = mysql;
if (get_slaves_from_master(mysql))
goto err;
}
error = 0;
err:
if(res)
mysql_free_result(res);
return error;
}
/* make a not so fool-proof decision on where the query should go, to
the master or the slave. Ideally the user should always make this
decision himself with mysql_master_query() or mysql_slave_query().
However, to be able to more easily port the old code, we support the
option of an educated guess - this should work for most applications,
however, it may make the wrong decision in some particular cases. If
that happens, the user would have to change the code to call
mysql_master_query() or mysql_slave_query() explicitly in the place
where we have made the wrong decision
*/
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);
for(; q < q_end; ++q)
{
char c;
if(isalpha(c=*q))
switch(tolower(c))
{
case 'i': /* insert */
case 'u': /* update or unlock tables */
case 'l': /* lock tables or load data infile */
case 'd': /* drop or delete */
case 'a': /* alter */
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 MYSQL_RPL_SLAVE;
}
}
return 0;
}
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;
}
/****************************************************************************
** Init MySQL structure or allocate one
****************************************************************************/