diff options
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r-- | libmysql/libmysql.c | 1151 |
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 |