summaryrefslogtreecommitdiff
path: root/libmysql/libmysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r--libmysql/libmysql.c1151
1 files changed, 895 insertions, 256 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index c50193c5e2c..607d8af6e50 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -1,21 +1,20 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <global.h>
+#include <my_global.h>
#if defined(__WIN__) || defined(_WIN32) || defined(_WIN64)
#include <winsock.h>
#include <odbcinst.h>
@@ -41,14 +40,17 @@
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SELECT_H
-# include <select.h>
+#include <select.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
+#ifdef HAVE_POLL
+#include <sys/poll.h>
#endif
+#endif /* !defined(MSDOS) && !defined(__WIN__) */
#ifdef HAVE_SYS_UN_H
-# include <sys/un.h>
+#include <sys/un.h>
#endif
#if defined(THREAD) && !defined(__WIN__)
#include <my_pthread.h> /* because of signal() */
@@ -60,6 +62,10 @@
static my_bool mysql_client_init=0;
uint mysql_port=0;
my_string mysql_unix_port=0;
+ulong net_buffer_length=8192;
+ulong max_allowed_packet= 1024L*1024L*1024L;
+ulong net_read_timeout= CLIENT_NET_READ_TIMEOUT;
+ulong net_write_timeout= CLIENT_NET_WRITE_TIMEOUT;
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS)
@@ -70,14 +76,13 @@ my_string mysql_unix_port=0;
#endif
#if defined(MSDOS) || defined(__WIN__)
-// socket_errno is defined in global.h for all platforms
+/* socket_errno is defined in my_global.h for all platforms */
#define perror(A)
#else
#include <errno.h>
#define SOCKET_ERROR -1
#endif /* __WIN__ */
-static void mysql_once_init(void);
static MYSQL_DATA *read_rows (MYSQL *mysql,MYSQL_FIELD *fields,
uint field_count);
static int read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row,
@@ -91,6 +96,47 @@ static sig_handler pipe_sig_handler(int sig);
static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
const char *from, ulong length);
+static my_bool org_my_init_done=0;
+
+int STDCALL mysql_server_init(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ char **groups __attribute__((unused)))
+{
+ return (int) mysql_once_init();
+}
+
+void STDCALL mysql_server_end()
+{
+ /* If library called my_init(), free memory allocated by it */
+ if (!org_my_init_done)
+ {
+ my_end(0);
+#ifndef THREAD
+ /* Remove TRACING, if enabled by mysql_debug() */
+ DBUG_POP();
+#endif
+ }
+ else
+ mysql_thread_end();
+ mysql_client_init= org_my_init_done= 0;
+}
+
+my_bool STDCALL mysql_thread_init()
+{
+#ifdef THREAD
+ return my_thread_init();
+#else
+ return 0;
+#endif
+}
+
+void STDCALL mysql_thread_end()
+{
+#ifdef THREAD
+ my_thread_end();
+#endif
+}
+
/*
Let the user specify that we don't want SIGPIPE; This doesn't however work
with threaded applications as we can have multiple read in progress.
@@ -106,26 +152,31 @@ 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);
+
+#if !(defined(__WIN__) || defined(OS2) || defined(__NETWARE__))
+static int wait_for_data(my_socket fd, uint timeout);
+#endif
+
/****************************************************************************
-* A modified version of connect(). connect2() allows you to specify
-* a timeout value, in seconds, that we should wait until we
-* derermine we can't connect to a particular host. If timeout is 0,
-* connect2() will behave exactly like connect().
-*
-* Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
+ A modified version of connect(). my_connect() allows you to specify
+ a timeout value, in seconds, that we should wait until we
+ derermine we can't connect to a particular host. If timeout is 0,
+ my_connect() will behave exactly like connect().
+
+ Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
*****************************************************************************/
-static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
- uint timeout)
+int my_connect(my_socket fd, const struct sockaddr *name, uint namelen,
+ uint timeout)
{
-#if defined(__WIN__) || defined(OS2)
- return connect(s, (struct sockaddr*) name, namelen);
+#if defined(__WIN__) || defined(OS2) || defined(__NETWARE__)
+ return connect(fd, (struct sockaddr*) name, namelen);
#else
int flags, res, s_err;
- SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint);
- fd_set sfds;
- struct timeval tv;
- time_t start_time, now_time;
/*
If they passed us a timeout of zero, we should behave
@@ -133,30 +184,68 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
*/
if (timeout == 0)
- return connect(s, (struct sockaddr*) name, namelen);
+ return connect(fd, (struct sockaddr*) name, namelen);
- flags = fcntl(s, F_GETFL, 0); /* Set socket to not block */
+ flags = fcntl(fd, F_GETFL, 0); /* Set socket to not block */
#ifdef O_NONBLOCK
- fcntl(s, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
#endif
- res = connect(s, (struct sockaddr*) name, namelen);
- s_err = errno; /* Save the error... */
- fcntl(s, F_SETFL, flags);
+ res= connect(fd, (struct sockaddr*) name, namelen);
+ s_err= errno; /* Save the error... */
+ fcntl(fd, F_SETFL, flags);
if ((res != 0) && (s_err != EINPROGRESS))
{
- errno = s_err; /* Restore it */
+ errno= s_err; /* Restore it */
return(-1);
}
if (res == 0) /* Connected quickly! */
return(0);
+ return wait_for_data(fd, timeout);
+#endif
+}
+
+
+/*
+ Wait up to timeout seconds for a connection to be established.
+
+ We prefer to do this with poll() as there is no limitations with this.
+ If not, we will use select()
+*/
+
+#if !(defined(__WIN__) || defined(OS2) || defined(__NETWARE__))
+
+static int wait_for_data(my_socket fd, uint timeout)
+{
+#ifdef HAVE_POLL
+ struct pollfd ufds;
+ int res;
+
+ ufds.fd= fd;
+ ufds.events= POLLIN | POLLPRI;
+ if (!(res= poll(&ufds, 1, (int) timeout*1000)))
+ {
+ errno= EINTR;
+ return -1;
+ }
+ if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI)))
+ return -1;
+ return 0;
+#else
+ SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint);
+ fd_set sfds;
+ struct timeval tv;
+ time_t start_time, now_time;
+ int res, s_err;
+
+ if (fd >= FD_SETSIZE) /* Check if wrong error */
+ return 0; /* Can't use timeout */
/*
- Otherwise, our connection is "in progress." We can use
- the select() call to wait up to a specified period of time
- for the connection to suceed. If select() returns 0
- (after waiting howevermany seconds), our socket never became
- writable (host is probably unreachable.) Otherwise, if
+ Our connection is "in progress." We can use the select() call to wait
+ up to a specified period of time for the connection to suceed.
+ If select() returns 0 (after waiting howevermany seconds), our socket
+ never became writable (host is probably unreachable.) Otherwise, if
select() returns 1, then one of two conditions exist:
1. An error occured. We use getsockopt() to check for this.
@@ -169,7 +258,7 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
*/
FD_ZERO(&sfds);
- FD_SET(s, &sfds);
+ FD_SET(fd, &sfds);
/*
select could be interrupted by a signal, and if it is,
the timeout should be adjusted and the select restarted
@@ -182,11 +271,11 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
{
tv.tv_sec = (long) timeout;
tv.tv_usec = 0;
-#if defined(HPUX) && defined(THREAD)
- if ((res = select(s+1, NULL, (int*) &sfds, NULL, &tv)) > 0)
+#if defined(HPUX10) && defined(THREAD)
+ if ((res = select(fd+1, NULL, (int*) &sfds, NULL, &tv)) > 0)
break;
#else
- if ((res = select(s+1, NULL, &sfds, NULL, &tv)) > 0)
+ if ((res = select(fd+1, NULL, &sfds, NULL, &tv)) > 0)
break;
#endif
if (res == 0) /* timeout */
@@ -204,7 +293,7 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
*/
s_err=0;
- if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
return(-1);
if (s_err)
@@ -213,12 +302,12 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen,
return(-1); /* but return an error... */
}
return (0); /* ok */
-
-#endif
+#endif /* HAVE_POLL */
}
+#endif /* defined(__WIN__) || defined(OS2) || defined(__NETWARE__) */
/*
-** Create a named pipe connection
+ Create a named pipe connection
*/
#ifdef __WIN__
@@ -291,15 +380,15 @@ HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host,
/*****************************************************************************
-** read a packet from server. Give error message if socket was down
-** or packet is an error message
+ read a packet from server. Give error message if socket was down
+ or packet is an error message
*****************************************************************************/
-uint
+ulong
net_safe_read(MYSQL *mysql)
{
NET *net= &mysql->net;
- uint len=0;
+ ulong len=0;
init_sigpipe_variables
/* Don't give sigpipe errors if the client doesn't want them */
@@ -317,26 +406,18 @@ net_safe_read(MYSQL *mysql)
CR_NET_PACKET_TOO_LARGE:
CR_SERVER_LOST);
strmov(net->last_error,ER(net->last_errno));
- return(packet_error);
+ return (packet_error);
}
if (net->read_pos[0] == 255)
{
if (len > 3)
{
char *pos=(char*) net->read_pos+1;
- if (mysql->protocol_version > 9)
- { /* New client protocol */
- net->last_errno=uint2korr(pos);
- pos+=2;
- len-=2;
- }
- else
- {
- net->last_errno=CR_UNKNOWN_ERROR;
- len--;
- }
+ net->last_errno=uint2korr(pos);
+ pos+=2;
+ len-=2;
(void) strmake(net->last_error,(char*) pos,
- min(len,sizeof(net->last_error)-1));
+ min((uint) len,(uint) sizeof(net->last_error)-1));
}
else
{
@@ -427,7 +508,7 @@ static void free_rows(MYSQL_DATA *cur)
int
simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
- uint length, my_bool skipp_check)
+ ulong length, my_bool skipp_check)
{
NET *net= &mysql->net;
int result= -1;
@@ -438,11 +519,7 @@ simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
if (mysql->net.vio == 0)
{ /* Do reconnect if possible */
if (mysql_reconnect(mysql))
- {
- net->last_errno=CR_SERVER_GONE_ERROR;
- strmov(net->last_error,ER(net->last_errno));
goto end;
- }
}
if (mysql->status != MYSQL_STATUS_READY)
{
@@ -461,7 +538,8 @@ simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
if (net_write_command(net,(uchar) command,arg,
length ? length : (ulong) strlen(arg)))
{
- DBUG_PRINT("error",("Can't send command to server. Error: %d",socket_errno));
+ DBUG_PRINT("error",("Can't send command to server. Error: %d",
+ socket_errno));
if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
{
net->last_errno=CR_NET_PACKET_TOO_LARGE;
@@ -474,7 +552,7 @@ simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
if (net_write_command(net,(uchar) command,arg,
length ? length : (ulong) strlen(arg)))
{
- net->last_errno= CR_SERVER_GONE_ERROR;
+ net->last_errno=CR_SERVER_GONE_ERROR;
strmov(net->last_error,ER(net->last_errno));
goto end;
}
@@ -505,7 +583,17 @@ struct passwd *getpwuid(uid_t);
char* getlogin(void);
#endif
-#if !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) && !defined(OS2)
+
+#if defined(__NETWARE__)
+/* default to "root" on NetWare */
+static void read_user_name(char *name)
+{
+ char *str=getenv("USER");
+ strmake(name, str ? str : "UNKNOWN_USER", USERNAME_LENGTH);
+}
+
+#elif !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) && !defined(OS2)
+
static void read_user_name(char *name)
{
DBUG_ENTER("read_user_name");
@@ -553,7 +641,7 @@ static my_bool is_NT(void)
#endif
/*
-** Expand wildcard to a sql string
+ Expand wildcard to a sql string
*/
static void
@@ -579,7 +667,7 @@ append_wild(char *to, char *end, const char *wild)
/**************************************************************************
-** Init debugging if MYSQL_DEBUG environment variable is found
+ Init debugging if MYSQL_DEBUG environment variable is found
**************************************************************************/
void STDCALL
@@ -616,7 +704,7 @@ mysql_debug(const char *debug __attribute__((unused)))
/**************************************************************************
-** Close the server connection if we get a SIGPIPE
+ Close the server connection if we get a SIGPIPE
ARGSUSED
**************************************************************************/
@@ -631,7 +719,7 @@ pipe_sig_handler(int sig __attribute__((unused)))
/**************************************************************************
-** Shut down connection
+ Shut down connection
**************************************************************************/
static void
@@ -665,8 +753,8 @@ mysql_free_result(MYSQL_RES *result)
DBUG_PRINT("warning",("Not all rows in set were read; Ignoring rows"));
for (;;)
{
- uint pkt_len;
- if ((pkt_len=(uint) net_safe_read(result->handle)) == packet_error)
+ ulong pkt_len;
+ if ((pkt_len=net_safe_read(result->handle)) == packet_error)
break;
if (pkt_len == 1 && result->handle->net.read_pos[0] == 254)
break; /* End of data */
@@ -685,7 +773,7 @@ mysql_free_result(MYSQL_RES *result)
/****************************************************************************
-** Get options from my.cnf
+ Get options from my.cnf
****************************************************************************/
static const char *default_options[]=
@@ -695,7 +783,9 @@ static const char *default_options[]=
"ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath",
"character-sets-dir", "default-character-set", "interactive-timeout",
"connect-timeout", "local-infile", "disable-local-infile",
- NullS
+ "replication-probe", "enable-reads-from-master", "repl-parse-query",
+ "ssl-cipher", "max-allowed-packet",
+ NullS
};
static TYPELIB option_types={array_elements(default_options)-1,
@@ -839,6 +929,19 @@ static void mysql_read_default_options(struct st_mysql_options *options,
break;
case 22:
options->client_flag&= CLIENT_LOCAL_FILES;
+ break;
+ case 23: /* replication probe */
+ options->rpl_probe= 1;
+ break;
+ case 24: /* enable-reads-from-master */
+ options->no_master_reads= 0;
+ break;
+ case 25: /* repl-parse-query */
+ options->rpl_parse= 1;
+ break;
+ case 27:
+ if (opt_arg)
+ options->max_allowed_packet= atoi(opt_arg);
break;
default:
DBUG_PRINT("warning",("unknown option: %s",option[0]));
@@ -852,7 +955,7 @@ static void mysql_read_default_options(struct st_mysql_options *options,
/***************************************************************************
-** Change field rows to field structs
+ Change field rows to field structs
***************************************************************************/
static MYSQL_FIELD *
@@ -863,13 +966,14 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
MYSQL_FIELD *field,*result;
DBUG_ENTER("unpack_fields");
- field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields);
+ field=result=(MYSQL_FIELD*) alloc_root(alloc,
+ (uint) sizeof(MYSQL_FIELD)*fields);
if (!result)
DBUG_RETURN(0);
for (row=data->data; row ; row = row->next,field++)
{
- field->table= strdup_root(alloc,(char*) row->data[0]);
+ field->org_table= field->table= strdup_root(alloc,(char*) row->data[0]);
field->name= strdup_root(alloc,(char*) row->data[1]);
field->length= (uint) uint3korr(row->data[2]);
field->type= (enum enum_field_types) (uchar) row->data[3][0];
@@ -901,7 +1005,8 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
uint fields)
{
- uint field,pkt_len;
+ uint field;
+ ulong pkt_len;
ulong len;
uchar *cp;
char *to, *end_to;
@@ -910,7 +1015,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
NET *net = &mysql->net;
DBUG_ENTER("read_rows");
- if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error)
+ if ((pkt_len= net_safe_read(mysql)) == packet_error)
DBUG_RETURN(0);
if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA),
MYF(MY_WME | MY_ZEROFILL))))
@@ -955,7 +1060,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
if (len > (ulong) (end_to - to))
{
free_rows(result);
- net->last_errno=CR_UNKNOWN_ERROR;
+ net->last_errno=CR_MALFORMED_PACKET;
strmov(net->last_error,ER(net->last_errno));
DBUG_RETURN(0);
}
@@ -983,8 +1088,8 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
/*
-** Read one row. Uses packet buffer as storage for fields.
-** When next packet is read, the previous field values are destroyed
+ Read one row. Uses packet buffer as storage for fields.
+ When next packet is read, the previous field values are destroyed
*/
@@ -995,7 +1100,7 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
ulong pkt_len,len;
uchar *pos,*prev_pos, *end_pos;
- if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error)
+ if ((pkt_len=net_safe_read(mysql)) == packet_error)
return -1;
if (pkt_len == 1 && mysql->net.read_pos[0] == 254)
return 1; /* End of data */
@@ -1030,28 +1135,324 @@ 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 long length)
+{
+ DBUG_ENTER("mysql_master_query");
+ if (mysql_master_send_query(mysql, q, length))
+ DBUG_RETURN(1);
+ DBUG_RETURN(mysql_read_query_result(mysql));
+}
+
+int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
+ unsigned long length)
+{
+ MYSQL *master = mysql->master;
+ DBUG_ENTER("mysql_master_send_query");
+ if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0))
+ DBUG_RETURN(1);
+ mysql->last_used_con = master;
+ DBUG_RETURN(simple_command(master, COM_QUERY, q, length, 1));
+}
+
+
+/* perform query on slave */
+int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
+ unsigned long length)
+{
+ DBUG_ENTER("mysql_slave_query");
+ if (mysql_slave_send_query(mysql, q, length))
+ DBUG_RETURN(1);
+ DBUG_RETURN(mysql_read_query_result(mysql));
+}
+
+int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q,
+ unsigned long length)
+{
+ MYSQL* last_used_slave, *slave_to_use = 0;
+ DBUG_ENTER("mysql_slave_send_query");
+
+ 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 (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0,
+ 0,0,0,0))
+ DBUG_RETURN(1);
+ DBUG_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 void expand_error(MYSQL* mysql, int error)
+{
+ char tmp[MYSQL_ERRMSG_SIZE];
+ char *p;
+ uint err_length;
+ strmake(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE-1);
+ p = strmake(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE-1);
+ err_length= (uint) (p - mysql->net.last_error);
+ strmake(p, tmp, MYSQL_ERRMSG_SIZE-1 - err_length);
+ mysql->net.last_errno = error;
+}
+
+/*
+ This function assumes we have just called SHOW SLAVE STATUS and have
+ read the given result and row
+*/
+
+static int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
+{
+ MYSQL* master;
+ DBUG_ENTER("get_master");
+ if (mysql_num_fields(res) < 3)
+ DBUG_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)))
+ DBUG_RETURN(1);
+ mysql->master = master;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Assuming we already know that mysql points to a master connection,
+ retrieve all the slaves
+*/
+
+static int get_slaves_from_master(MYSQL* mysql)
+{
+ MYSQL_RES* res = 0;
+ MYSQL_ROW row;
+ int error = 1;
+ int has_auth_info;
+ int port_ind;
+ DBUG_ENTER("get_slaves_from_master");
+
+ if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0))
+ {
+ expand_error(mysql, CR_PROBE_MASTER_CONNECT);
+ DBUG_RETURN(1);
+ }
+
+ if (mysql_query(mysql, "SHOW SLAVE HOSTS") ||
+ !(res = mysql_store_result(mysql)))
+ {
+ expand_error(mysql, CR_PROBE_SLAVE_HOSTS);
+ DBUG_RETURN(1);
+ }
+
+ switch (mysql_num_fields(res)) {
+ case 5:
+ has_auth_info = 0;
+ port_ind=2;
+ break;
+ case 7:
+ has_auth_info = 1;
+ port_ind=4;
+ 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[2];
+ tmp_pass = row[3];
+ }
+ else
+ {
+ tmp_user = mysql->user;
+ tmp_pass = mysql->passwd;
+ }
+
+ if (!(slave = spawn_init(mysql, row[1], atoi(row[port_ind]),
+ 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);
+ DBUG_RETURN(error);
+}
+
+
+int STDCALL mysql_rpl_probe(MYSQL* mysql)
+{
+ MYSQL_RES *res= 0;
+ MYSQL_ROW row;
+ int error = 1;
+ DBUG_ENTER("mysql_rpl_probe");
+
+ /*
+ 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);
+ DBUG_RETURN(1);
+ }
+
+ row= mysql_fetch_row(res);
+ /*
+ Check master host for emptiness/NULL
+ For MySQL 4.0 it's enough to check for row[0]
+ */
+ if (row && 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);
+ DBUG_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 + len;
+ 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 MYSQL_RPL_MASTER; /* By default, send to master */
+}
+
+
/****************************************************************************
-** Init MySQL structure or allocate one
+ Init MySQL structure or allocate one
****************************************************************************/
MYSQL * STDCALL
mysql_init(MYSQL *mysql)
{
- mysql_once_init();
+ if (mysql_once_init())
+ return 0;
if (!mysql)
{
if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
return 0;
mysql->free_me=1;
- mysql->net.vio = 0;
}
else
bzero((char*) (mysql),sizeof(*(mysql)));
mysql->options.connect_timeout=CONNECT_TIMEOUT;
-#if defined(SIGPIPE) && defined(THREAD) && !defined(__WIN__)
- if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE))
- (void) signal(SIGPIPE,pipe_sig_handler);
-#endif
+ mysql->last_used_con = mysql->next_slave = mysql->master = mysql;
+
+ /*
+ 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;
/*
Only enable LOAD DATA INFILE by default if configured with
@@ -1064,12 +1465,31 @@ mysql_init(MYSQL *mysql)
}
-static void mysql_once_init()
+/*
+ Initialize the MySQL library
+
+ SYNOPSIS
+ mysql_once_init()
+
+ NOTES
+ Can't be static on NetWare
+ This function is called by mysql_init() and indirectly called
+ by mysql_query(), so one should never have to call this from an
+ outside program.
+
+ RETURN
+ 0 ok
+ 1 could not initialize environment (out of memory or thread keys)
+*/
+
+int mysql_once_init(void)
{
if (!mysql_client_init)
{
mysql_client_init=1;
- my_init(); /* Will init threads */
+ org_my_init_done=my_init_done;
+ if (my_init()) /* Will init threads */
+ return 1;
init_client_errs();
if (!mysql_port)
{
@@ -1097,73 +1517,79 @@ static void mysql_once_init()
mysql_unix_port = env;
}
mysql_debug(NullS);
-#if defined(SIGPIPE) && !defined(THREAD) && !defined(__WIN__)
- (void) signal(SIGPIPE,SIG_IGN);
+#if defined(SIGPIPE) && !defined(__WIN__)
+ (void) signal(SIGPIPE, SIG_IGN);
#endif
}
#ifdef THREAD
else
- my_thread_init(); /* Init if new thread */
+ {
+ if (my_thread_init()) /* Init if new thread */
+ return 1;
+ }
#endif
-}
-
-#ifdef HAVE_OPENSSL
-/**************************************************************************
-** Fill in SSL part of MYSQL structure and set 'use_ssl' flag.
-** NB! Errors are not reported until you do mysql_real_connect.
-**************************************************************************/
-
-int STDCALL
-mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
- const char *ca, const char *capath)
-{
- mysql->options.ssl_key = key==0 ? 0 : my_strdup(key,MYF(0));
- mysql->options.ssl_cert = cert==0 ? 0 : my_strdup(cert,MYF(0));
- mysql->options.ssl_ca = ca==0 ? 0 : my_strdup(ca,MYF(0));
- mysql->options.ssl_capath = capath==0 ? 0 : my_strdup(capath,MYF(0));
- mysql->options.use_ssl = true;
- mysql->connector_fd = new_VioSSLConnectorFd(key, cert, ca, capath);
return 0;
}
+
/**************************************************************************
+ Fill in SSL part of MYSQL structure and set 'use_ssl' flag.
+ NB! Errors are not reported until you do mysql_real_connect.
**************************************************************************/
-char * STDCALL
-mysql_ssl_cipher(MYSQL *mysql)
+#define strdup_if_not_null(A) (A) == 0 ? 0 : my_strdup((A),MYF(MY_WME))
+
+int STDCALL
+mysql_ssl_set(MYSQL *mysql __attribute__((unused)) ,
+ const char *key __attribute__((unused)),
+ const char *cert __attribute__((unused)),
+ const char *ca __attribute__((unused)),
+ const char *capath __attribute__((unused)),
+ const char *cipher __attribute__((unused)))
{
- return (char *)mysql->net.vio->cipher_description();
+#ifdef HAVE_OPENSSL
+ mysql->options.ssl_key= strdup_if_not_null(key);
+ mysql->options.ssl_cert= strdup_if_not_null(cert);
+ mysql->options.ssl_ca= strdup_if_not_null(ca);
+ mysql->options.ssl_capath= strdup_if_not_null(capath);
+ mysql->options.ssl_cipher= strdup_if_not_null(cipher);
+#endif
+ return 0;
}
/**************************************************************************
-** Free strings in the SSL structure and clear 'use_ssl' flag.
-** NB! Errors are not reported until you do mysql_real_connect.
+ Free strings in the SSL structure and clear 'use_ssl' flag.
+ NB! Errors are not reported until you do mysql_real_connect.
**************************************************************************/
-int STDCALL
-mysql_ssl_clear(MYSQL *mysql)
+static int
+mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
{
+#ifdef HAVE_OPENSSL
my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR));
mysql->options.ssl_key = 0;
mysql->options.ssl_cert = 0;
mysql->options.ssl_ca = 0;
mysql->options.ssl_capath = 0;
- mysql->options.use_ssl = false;
- mysql->connector_fd->delete();
+ mysql->options.ssl_cipher= 0;
+ mysql->options.use_ssl = FALSE;
mysql->connector_fd = 0;
+#endif /* HAVE_OPENSSL */
return 0;
}
-#endif /* HAVE_OPENSSL */
/**************************************************************************
-** Connect to sql server
-** If host == 0 then use localhost
+ Connect to sql server
+ If host == 0 then use localhost
**************************************************************************/
+#ifdef USE_OLD_FUNCTIONS
MYSQL * STDCALL
mysql_connect(MYSQL *mysql,const char *host,
const char *user, const char *passwd)
@@ -1180,11 +1606,24 @@ mysql_connect(MYSQL *mysql,const char *host,
DBUG_RETURN(res);
}
}
+#endif
/*
-** Note that the mysql argument must be initialized with mysql_init()
-** before calling mysql_real_connect !
+ The following union is used to force a struct to be double allgined.
+ This is to avoid warings with gethostname_r() on Linux itanium systems
+*/
+
+typedef union
+{
+ double tmp;
+ char buff[GETHOSTBYNAME_BUFF_SIZE];
+} gethostbyname_buff;
+
+
+/*
+ Note that the mysql argument must be initialized with mysql_init()
+ before calling mysql_real_connect !
*/
MYSQL * STDCALL
@@ -1195,9 +1634,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
char buff[NAME_LEN+USERNAME_LENGTH+100],charset_name_buff[16];
char *end,*host_info,*charset_name;
my_socket sock;
- uint32 ip_addr;
+ in_addr_t ip_addr;
struct sockaddr_in sock_addr;
- uint pkt_length;
+ ulong pkt_length;
NET *net= &mysql->net;
#ifdef __WIN__
HANDLE hPipe=INVALID_HANDLE_VALUE;
@@ -1207,6 +1646,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
#endif
init_sigpipe_variables
DBUG_ENTER("mysql_real_connect");
+ LINT_INIT(host_info);
DBUG_PRINT("enter",("host: %s db: %s user: %s",
host ? host : "(Null)",
@@ -1272,8 +1712,8 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE);
bzero((char*) &UNIXaddr,sizeof(UNIXaddr));
UNIXaddr.sun_family = AF_UNIX;
- strmov(UNIXaddr.sun_path, unix_socket);
- if (connect2(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
+ strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1);
+ if (my_connect(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
mysql->options.connect_timeout) <0)
{
DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno));
@@ -1304,7 +1744,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
if (mysql->options.named_pipe ||
(host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) ||
(unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE)))
+ {
+ net->last_errno= CR_SERVER_LOST;
+ strmov(net->last_error,ER(net->last_errno));
goto error; /* User only requested named pipes */
+ }
/* Try also with TCP/IP */
}
else
@@ -1348,21 +1792,21 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
{
int tmp_errno;
struct hostent tmp_hostent,*hp;
- char buff2[GETHOSTBYNAME_BUFF_SIZE];
- hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
+ gethostbyname_buff buff2;
+ hp = my_gethostbyname_r(host,&tmp_hostent,buff2.buff,sizeof(buff2),
&tmp_errno);
if (!hp)
{
+ my_gethostbyname_r_free();
net->last_errno=CR_UNKNOWN_HOST;
sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, tmp_errno);
- my_gethostbyname_r_free();
goto error;
}
memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
my_gethostbyname_r_free();
}
sock_addr.sin_port = (ushort) htons((ushort) port);
- if (connect2(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr),
+ if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr),
mysql->options.connect_timeout) <0)
{
DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno,host));
@@ -1400,8 +1844,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
DBUG_DUMP("packet",(char*) net->read_pos,10);
DBUG_PRINT("info",("mysql protocol version %d, server=%d",
PROTOCOL_VERSION, mysql->protocol_version));
- if (mysql->protocol_version != PROTOCOL_VERSION &&
- mysql->protocol_version != PROTOCOL_VERSION-1)
+ if (mysql->protocol_version != PROTOCOL_VERSION)
{
net->last_errno= CR_VERSION_ERROR;
sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version,
@@ -1437,7 +1880,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
charset_name=charset_name_buff;
sprintf(charset_name,"%d",mysql->server_language); /* In case of errors */
if (!(mysql->charset =
- get_charset((uint8) mysql->server_language, MYF(MY_WME))))
+ get_charset((uint8) mysql->server_language, MYF(0))))
mysql->charset = default_charset_info; /* shouldn't be fatal */
}
@@ -1493,6 +1936,10 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
client_flag|=CLIENT_CAPABILITIES;
#ifdef HAVE_OPENSSL
+ if (mysql->options.ssl_key || mysql->options.ssl_cert ||
+ mysql->options.ssl_ca || mysql->options.ssl_capath ||
+ mysql->options.ssl_cipher)
+ mysql->options.use_ssl= 1;
if (mysql->options.use_ssl)
client_flag|=CLIENT_SSL;
#endif /* HAVE_OPENSSL */
@@ -1527,19 +1974,40 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->client_flag=client_flag;
#ifdef HAVE_OPENSSL
- /* Oops.. are we careful enough to not send ANY information */
- /* without encryption? */
+ /*
+ Oops.. are we careful enough to not send ANY information without
+ encryption?
+ */
if (client_flag & CLIENT_SSL)
{
+ struct st_mysql_options *options= &mysql->options;
if (my_net_write(net,buff,(uint) (2)) || net_flush(net))
+ {
+ net->last_errno= CR_SERVER_LOST;
+ strmov(net->last_error,ER(net->last_errno));
goto error;
+ }
/* Do the SSL layering. */
+ if (!(mysql->connector_fd=
+ (gptr) new_VioSSLConnectorFd(options->ssl_key,
+ options->ssl_cert,
+ options->ssl_ca,
+ options->ssl_capath,
+ options->ssl_cipher)))
+ {
+ net->last_errno= CR_SSL_CONNECTION_ERROR;
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
DBUG_PRINT("info", ("IO layer change in progress..."));
- VioSSLConnectorFd* connector_fd = (VioSSLConnectorFd*)
- (mysql->connector_fd);
- VioSocket* vio_socket = (VioSocket*)(mysql->net.vio);
- VioSSL* vio_ssl = connector_fd->connect(vio_socket);
- mysql->net.vio = (NetVio*)(vio_ssl);
+ if(sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd),
+ mysql->net.vio, (long) (mysql->options.connect_timeout)))
+ {
+ net->last_errno= CR_SSL_CONNECTION_ERROR;
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ DBUG_PRINT("info", ("IO layer change done!"));
}
#endif /* HAVE_OPENSSL */
@@ -1547,6 +2015,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_version,mysql->server_capabilities,
mysql->server_status, client_flag));
+ /* This needs to be changed as it's not useful with big packets */
int3store(buff+2,max_allowed_packet);
if (user && user[0])
strmake(buff+5,user,32); /* Max user name */
@@ -1564,11 +2033,18 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->db=my_strdup(db,MYF(MY_WME));
db=0;
}
- if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net) ||
- net_safe_read(mysql) == packet_error)
+ if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
+ {
+ net->last_errno= CR_SERVER_LOST;
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ if (net_safe_read(mysql) == packet_error)
goto error;
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1;
+ if (mysql->options.max_allowed_packet)
+ net->max_packet_size= mysql->options.max_allowed_packet;
if (db && mysql_select_db(mysql,db))
goto error;
if (mysql->options.init_command)
@@ -1581,6 +2057,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);
@@ -1600,6 +2079,26 @@ error:
}
+/* needed when we move MYSQL structure to a different address */
+
+static void mysql_fix_pointers(MYSQL* mysql, MYSQL* old_mysql)
+{
+ MYSQL *tmp, *tmp_prev;
+ if (mysql->master == old_mysql)
+ mysql->master = mysql;
+ if (mysql->last_used_con == old_mysql)
+ mysql->last_used_con = mysql;
+ if (mysql->last_used_slave == old_mysql)
+ mysql->last_used_slave = mysql;
+ for (tmp_prev = mysql, tmp = mysql->next_slave;
+ tmp != old_mysql;tmp = tmp->next_slave)
+ {
+ tmp_prev = tmp;
+ }
+ tmp_prev->next_slave = mysql;
+}
+
+
static my_bool mysql_reconnect(MYSQL *mysql)
{
MYSQL tmp_mysql;
@@ -1608,21 +2107,29 @@ static my_bool mysql_reconnect(MYSQL *mysql)
if (!mysql->reconnect ||
(mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info)
{
- /* Allov reconnect next time */
+ /* Allow reconnect next time */
mysql->server_status&= ~SERVER_STATUS_IN_TRANS;
+ mysql->net.last_errno=CR_SERVER_GONE_ERROR;
+ strmov(mysql->net.last_error,ER(mysql->net.last_errno));
DBUG_RETURN(1);
}
mysql_init(&tmp_mysql);
tmp_mysql.options=mysql->options;
bzero((char*) &mysql->options,sizeof(mysql->options));
+ tmp_mysql.rpl_pivot = mysql->rpl_pivot;
if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd,
mysql->db, mysql->port, mysql->unix_socket,
mysql->client_flag))
+ {
+ mysql->net.last_errno= tmp_mysql.net.last_errno;
+ strmov(mysql->net.last_error, tmp_mysql.net.last_error);
DBUG_RETURN(1);
+ }
tmp_mysql.free_me=mysql->free_me;
mysql->free_me=0;
mysql_close(mysql);
*mysql=tmp_mysql;
+ mysql_fix_pointers(mysql, &tmp_mysql); /* adjust connection pointers */
net_clear(&mysql->net);
mysql->affected_rows= ~(my_ulonglong) 0;
DBUG_RETURN(0);
@@ -1630,7 +2137,7 @@ static my_bool mysql_reconnect(MYSQL *mysql)
/**************************************************************************
-** Change user and database
+ Change user and database
**************************************************************************/
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
@@ -1648,7 +2155,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
pos=scramble(pos, mysql->scramble_buff, passwd,
(my_bool) (mysql->protocol_version == 9));
pos=strmov(pos+1,db ? db : "");
- if (simple_command(mysql,COM_CHANGE_USER, buff,(uint) (pos-buff),0))
+ if (simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (pos-buff),0))
DBUG_RETURN(1);
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
@@ -1663,7 +2170,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/**************************************************************************
-** Set current database
+ Set current database
**************************************************************************/
int STDCALL
@@ -1673,7 +2180,7 @@ mysql_select_db(MYSQL *mysql, const char *db)
DBUG_ENTER("mysql_select_db");
DBUG_PRINT("enter",("db: '%s'",db));
- if ((error=simple_command(mysql,COM_INIT_DB,db,(uint) strlen(db),0)))
+ if ((error=simple_command(mysql,COM_INIT_DB,db,(ulong) strlen(db),0)))
DBUG_RETURN(error);
my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
mysql->db=my_strdup(db,MYF(MY_WME));
@@ -1682,8 +2189,8 @@ mysql_select_db(MYSQL *mysql, const char *db)
/*************************************************************************
-** Send a QUIT to the server and close the connection
-** If handle is alloced by mysql connect free it.
+ Send a QUIT to the server and close the connection
+ If handle is alloced by mysql connect free it.
*************************************************************************/
void STDCALL
@@ -1698,7 +2205,7 @@ mysql_close(MYSQL *mysql)
mysql->status=MYSQL_STATUS_READY; /* Force command */
mysql->reconnect=0;
simple_command(mysql,COM_QUIT,NullS,0,1);
- end_server(mysql);
+ end_server(mysql); /* Sets mysql->net.vio= 0 */
}
my_free((gptr) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
@@ -1714,14 +2221,28 @@ mysql_close(MYSQL *mysql)
my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR));
+#ifdef HAVE_OPENSSL
+ mysql_ssl_free(mysql);
+#endif /* HAVE_OPENSSL */
/* Clear pointers for better safety */
mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
bzero((char*) &mysql->options,sizeof(mysql->options));
- mysql->net.vio = 0;
-#ifdef HAVE_OPENSSL
- ((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;
+ }
+ mysql->rpl_pivot=0;
+ }
+ if (mysql != mysql->master)
+ mysql_close(mysql->master);
if (mysql->free_me)
my_free((gptr) mysql,MYF(0));
}
@@ -1730,8 +2251,8 @@ mysql_close(MYSQL *mysql)
/**************************************************************************
-** Do a query. If query returned rows, free old rows.
-** Read data by mysql_store_result or by repeat call of mysql_fetch_row
+ Do a query. If query returned rows, free old rows.
+ Read data by mysql_store_result or by repeat call of mysql_fetch_row
**************************************************************************/
int STDCALL
@@ -1740,6 +2261,69 @@ 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;
+ DBUG_ENTER("spawn_init");
+ if (!(child= mysql_init(0)))
+ DBUG_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));
+
+ /*
+ rpl_pivot is set to 1 in mysql_init(); Reset it as we are not doing
+ replication here
+ */
+ child->rpl_pivot= 0;
+ DBUG_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;
+ 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
@@ -1747,19 +2331,42 @@ mysql_query(MYSQL *mysql, const char *query)
*/
int STDCALL
-mysql_send_query(MYSQL* mysql, const char* query, uint length)
+mysql_send_query(MYSQL* mysql, const char* query, ulong length)
{
- return simple_command(mysql, COM_QUERY, query, length, 1);
+ DBUG_ENTER("mysql_send_query");
+ DBUG_PRINT("enter",("rpl_parse: %d rpl_pivot: %d",
+ mysql->options.rpl_parse, mysql->rpl_pivot));
+
+ if (mysql->options.rpl_parse && mysql->rpl_pivot)
+ {
+ switch (mysql_rpl_query_type(query, length)) {
+ case MYSQL_RPL_MASTER:
+ DBUG_RETURN(mysql_master_send_query(mysql, query, length));
+ case MYSQL_RPL_SLAVE:
+ DBUG_RETURN(mysql_slave_send_query(mysql, query, length));
+ case MYSQL_RPL_ADMIN:
+ break; /* fall through */
+ }
+ }
+
+ mysql->last_used_con = mysql;
+ DBUG_RETURN(simple_command(mysql, COM_QUERY, query, length, 1));
}
+
int STDCALL mysql_read_query_result(MYSQL *mysql)
{
uchar *pos;
ulong field_count;
MYSQL_DATA *fields;
- uint length;
+ ulong 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 */
@@ -1796,80 +2403,86 @@ get_info:
CLIENT_LONG_FLAG))))
DBUG_RETURN(-1);
mysql->status=MYSQL_STATUS_GET_RESULT;
- mysql->field_count=field_count;
+ mysql->field_count= (uint) field_count;
DBUG_RETURN(0);
}
+
int STDCALL
-mysql_real_query(MYSQL *mysql, const char *query, uint length)
+mysql_real_query(MYSQL *mysql, const char *query, ulong 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))
+ DBUG_PRINT("query",("Query = '%-.4096s'",query));
+
+ if (mysql_send_query(mysql,query,length))
DBUG_RETURN(-1);
DBUG_RETURN(mysql_read_query_result(mysql));
}
+
static int
send_file_to_server(MYSQL *mysql, const char *filename)
{
- int fd, readcount;
- char buf[IO_SIZE*15],*tmp_name;
+ int fd, readcount, result= -1;
+ uint packet_length=MY_ALIGN(mysql->net.max_packet-16,IO_SIZE);
+ char *buf, tmp_name[FN_REFLEN];
DBUG_ENTER("send_file_to_server");
- fn_format(buf,filename,"","",4); /* Convert to client format */
- if (!(tmp_name=my_strdup(buf,MYF(0))))
+ if (!(buf=my_malloc(packet_length,MYF(0))))
{
strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY));
DBUG_RETURN(-1);
}
+
+ fn_format(tmp_name,filename,"","",4); /* Convert to client format */
if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0)
{
+ my_net_write(&mysql->net,"",0); /* Server needs one packet */
+ net_flush(&mysql->net);
mysql->net.last_errno=EE_FILENOTFOUND;
- sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno);
- strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1);
- my_net_write(&mysql->net,"",0); net_flush(&mysql->net);
- my_free(tmp_name,MYF(0));
- DBUG_RETURN(-1);
+ my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1,
+ EE(mysql->net.last_errno),tmp_name, errno);
+ goto err;
}
- while ((readcount = (int) my_read(fd,buf,sizeof(buf),MYF(0))) > 0)
+ while ((readcount = (int) my_read(fd,(byte*) buf,packet_length,MYF(0))) > 0)
{
if (my_net_write(&mysql->net,buf,readcount))
{
+ DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file"));
mysql->net.last_errno=CR_SERVER_LOST;
strmov(mysql->net.last_error,ER(mysql->net.last_errno));
- DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file"));
- (void) my_close(fd,MYF(0));
- my_free(tmp_name,MYF(0));
- DBUG_RETURN(-1);
+ goto err;
}
}
- (void) my_close(fd,MYF(0));
/* Send empty packet to mark end of file */
if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net))
{
mysql->net.last_errno=CR_SERVER_LOST;
- sprintf(mysql->net.last_error,ER(mysql->net.last_errno),socket_errno);
- my_free(tmp_name,MYF(0));
- DBUG_RETURN(-1);
+ sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno);
+ goto err;
}
if (readcount < 0)
{
mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */
- sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno);
- strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1);
- my_free(tmp_name,MYF(0));
- DBUG_RETURN(-1);
+ my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1,
+ tmp_name,errno);
+ goto err;
}
- DBUG_RETURN(0);
+ result=0; /* Ok */
+
+err:
+ if (fd >= 0)
+ (void) my_close(fd,MYF(0));
+ my_free(buf,MYF(0));
+ DBUG_RETURN(result);
}
/**************************************************************************
-** Alloc result struct for buffered results. All rows are read to buffer.
-** mysql_data_seek may be used.
+ Alloc result struct for buffered results. All rows are read to buffer.
+ mysql_data_seek may be used.
**************************************************************************/
MYSQL_RES * STDCALL
@@ -1878,6 +2491,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)
@@ -1887,8 +2503,9 @@ mysql_store_result(MYSQL *mysql)
DBUG_RETURN(0);
}
mysql->status=MYSQL_STATUS_READY; /* server is ready */
- if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+
- sizeof(ulong)*mysql->field_count,
+ if (!(result=(MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+
+ sizeof(ulong) *
+ mysql->field_count),
MYF(MY_WME | MY_ZEROFILL))))
{
mysql->net.last_errno=CR_OUT_OF_MEMORY;
@@ -1915,13 +2532,13 @@ mysql_store_result(MYSQL *mysql)
/**************************************************************************
-** Alloc struct for use with unbuffered reads. Data is fetched by domand
-** when calling to mysql_fetch_row.
-** mysql_data_seek is a noop.
-**
-** No other queries may be specified with the same MYSQL handle.
-** There shouldn't be much processing per row because mysql server shouldn't
-** have to wait for the client (and will not wait more than 30 sec/packet).
+ Alloc struct for use with unbuffered reads. Data is fetched by domand
+ when calling to mysql_fetch_row.
+ mysql_data_seek is a noop.
+
+ No other queries may be specified with the same MYSQL handle.
+ There shouldn't be much processing per row because mysql server shouldn't
+ have to wait for the client (and will not wait more than 30 sec/packet).
**************************************************************************/
MYSQL_RES * STDCALL
@@ -1930,6 +2547,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)
@@ -1963,7 +2582,7 @@ mysql_use_result(MYSQL *mysql)
/**************************************************************************
-** Return next field of the query results
+ Return next field of the query results
**************************************************************************/
MYSQL_FIELD * STDCALL
@@ -1976,7 +2595,7 @@ mysql_fetch_field(MYSQL_RES *result)
/**************************************************************************
-** Return next row of the query results
+ Return next row of the query results
**************************************************************************/
MYSQL_ROW STDCALL
@@ -2017,9 +2636,9 @@ mysql_fetch_row(MYSQL_RES *res)
}
/**************************************************************************
-** Get column lengths of the current row
-** If one uses mysql_use_result, res->lengths contains the length information,
-** else the lengths are calculated from the offset between pointers.
+ Get column lengths of the current row
+ If one uses mysql_use_result, res->lengths contains the length information,
+ else the lengths are calculated from the offset between pointers.
**************************************************************************/
ulong * STDCALL
@@ -2044,7 +2663,7 @@ mysql_fetch_lengths(MYSQL_RES *res)
continue;
}
if (start) /* Found end of prev string */
- *prev_length= (uint) (*column-start-1);
+ *prev_length= (ulong) (*column-start-1);
start= *column;
prev_length=lengths;
}
@@ -2053,7 +2672,7 @@ mysql_fetch_lengths(MYSQL_RES *res)
}
/**************************************************************************
-** Move to a specific row and column
+ Move to a specific row and column
**************************************************************************/
void STDCALL
@@ -2068,9 +2687,9 @@ mysql_data_seek(MYSQL_RES *result, my_ulonglong row)
}
/*************************************************************************
-** put the row or field cursor one a position one got from mysql_row_tell()
-** This doesn't restore any data. The next mysql_fetch_row or
-** mysql_fetch_field will return the next row or field after the last used
+ put the row or field cursor one a position one got from mysql_row_tell()
+ This doesn't restore any data. The next mysql_fetch_row or
+ mysql_fetch_field will return the next row or field after the last used
*************************************************************************/
MYSQL_ROW_OFFSET STDCALL
@@ -2092,7 +2711,7 @@ mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset)
}
/*****************************************************************************
-** List all databases
+ List all databases
*****************************************************************************/
MYSQL_RES * STDCALL
@@ -2109,8 +2728,8 @@ mysql_list_dbs(MYSQL *mysql, const char *wild)
/*****************************************************************************
-** List all tables in a database
-** If wild is given then only the tables matching wild is returned
+ List all tables in a database
+ If wild is given then only the tables matching wild is returned
*****************************************************************************/
MYSQL_RES * STDCALL
@@ -2127,10 +2746,10 @@ mysql_list_tables(MYSQL *mysql, const char *wild)
/**************************************************************************
-** List all fields in a table
-** If wild is given then only the fields matching wild is returned
-** Instead of this use query:
-** show fields in 'table' like "wild"
+ List all fields in a table
+ If wild is given then only the fields matching wild is returned
+ Instead of this use query:
+ show fields in 'table' like "wild"
**************************************************************************/
MYSQL_RES * STDCALL
@@ -2145,7 +2764,7 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
LINT_INIT(query);
end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128);
- if (simple_command(mysql,COM_FIELD_LIST,buff,(uint) (end-buff),1) ||
+ if (simple_command(mysql,COM_FIELD_LIST,buff,(ulong) (end-buff),1) ||
!(query = read_rows(mysql,(MYSQL_FIELD*) 0,6)))
DBUG_RETURN(NULL);
@@ -2195,12 +2814,13 @@ mysql_list_processes(MYSQL *mysql)
}
+#ifdef USE_OLD_FUNCTIONS
int STDCALL
mysql_create_db(MYSQL *mysql, const char *db)
{
DBUG_ENTER("mysql_createdb");
DBUG_PRINT("enter",("db: %s",db));
- DBUG_RETURN(simple_command(mysql,COM_CREATE_DB,db, (uint) strlen(db),0));
+ DBUG_RETURN(simple_command(mysql,COM_CREATE_DB,db, (ulong) strlen(db),0));
}
@@ -2209,8 +2829,9 @@ mysql_drop_db(MYSQL *mysql, const char *db)
{
DBUG_ENTER("mysql_drop_db");
DBUG_PRINT("enter",("db: %s",db));
- DBUG_RETURN(simple_command(mysql,COM_DROP_DB,db,(uint) strlen(db),0));
+ DBUG_RETURN(simple_command(mysql,COM_DROP_DB,db,(ulong) strlen(db),0));
}
+#endif
int STDCALL
@@ -2247,7 +2868,7 @@ mysql_dump_debug_info(MYSQL *mysql)
DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0));
}
-char * STDCALL
+const char * STDCALL
mysql_stat(MYSQL *mysql)
{
DBUG_ENTER("mysql_stat");
@@ -2272,14 +2893,14 @@ mysql_ping(MYSQL *mysql)
}
-char * STDCALL
+const char * STDCALL
mysql_get_server_info(MYSQL *mysql)
{
return((char*) mysql->server_version);
}
-char * STDCALL
+const char * STDCALL
mysql_get_host_info(MYSQL *mysql)
{
return(mysql->host_info);
@@ -2292,12 +2913,17 @@ mysql_get_proto_info(MYSQL *mysql)
return (mysql->protocol_version);
}
-char * STDCALL
+const char * STDCALL
mysql_get_client_info(void)
{
return (char*) MYSQL_SERVER_VERSION;
}
+ulong STDCALL mysql_get_client_version(void)
+{
+ return MYSQL_VERSION_ID;
+}
+
int STDCALL
mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg)
@@ -2347,8 +2973,8 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg)
}
/****************************************************************************
-** Functions to get information from the MySQL structure
-** These are functions to make shared libraries more usable.
+ Functions to get information from the MySQL structure
+ These are functions to make shared libraries more usable.
****************************************************************************/
/* MYSQL_RES */
@@ -2377,12 +3003,12 @@ MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res)
return (res)->fields;
}
-MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res)
+MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res)
{
return res->data_cursor;
}
-uint STDCALL mysql_field_tell(MYSQL_RES *res)
+MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res)
{
return (res)->current_field;
}
@@ -2391,32 +3017,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)
+const char * STDCALL mysql_error(MYSQL *mysql)
{
- return (mysql)->net.last_error;
+ return mysql->net.last_error;
}
-char *STDCALL mysql_info(MYSQL *mysql)
+const char *STDCALL mysql_info(MYSQL *mysql)
{
- return (mysql)->info;
+ return mysql->info;
}
ulong STDCALL mysql_thread_id(MYSQL *mysql)
@@ -2440,13 +3066,26 @@ uint STDCALL mysql_thread_safe(void)
}
/****************************************************************************
-** Some support functions
+ Some support functions
****************************************************************************/
/*
-** Add escape characters to a string (blob?) to make it suitable for a insert
-** to should at least have place for length*2+1 chars
-** Returns the length of the to string
+ Functions called my my_net_init() to set some application specific variables
+*/
+
+void my_net_local_init(NET *net)
+{
+ net->max_packet= (uint) net_buffer_length;
+ net->read_timeout= (uint) net_read_timeout;
+ net->write_timeout=(uint) net_write_timeout;
+ net->retry_count= 1;
+ net->max_packet_size= max(net_buffer_length, max_allowed_packet);
+}
+
+/*
+ Add escape characters to a string (blob?) to make it suitable for a insert
+ to should at least have place for length*2+1 chars
+ Returns the length of the to string
*/
ulong STDCALL