diff options
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/Makefile.am | 19 | ||||
-rw-r--r-- | libmysql/Makefile.shared | 13 | ||||
-rw-r--r-- | libmysql/dll.c | 2 | ||||
-rw-r--r-- | libmysql/errmsg.c | 25 | ||||
-rw-r--r-- | libmysql/get_password.c | 2 | ||||
-rw-r--r-- | libmysql/libmysql.c | 621 | ||||
-rw-r--r-- | libmysql/libmysql.def | 76 | ||||
-rw-r--r-- | libmysql/manager.c | 278 | ||||
-rw-r--r-- | libmysql/net.c | 365 | ||||
-rw-r--r-- | libmysql/password.c | 2 | ||||
-rw-r--r-- | libmysql/violite.c | 443 |
11 files changed, 1188 insertions, 658 deletions
diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am index 68c2022223e..2eaf9709a36 100644 --- a/libmysql/Makefile.am +++ b/libmysql/Makefile.am @@ -18,10 +18,10 @@ # This file is public domain and comes with NO WARRANTY of any kind target = libmysqlclient.la -target_defs = -DUNDEF_THREADS_HACK -DDONT_USE_RAID @LIB_EXTRA_CCFLAGS@ +target_defs = -DUNDEF_THREADS_HACK -DDONT_USE_RAID @LIB_EXTRA_CCFLAGS@ -DMYSQL_CLIENT LIBS = @CLIENT_LIBS@ INCLUDES = -I$(srcdir)/../include -I../include \ - -I$(srcdir)/.. -I$(top_srcdir) -I.. + -I$(srcdir)/.. -I$(top_srcdir) -I.. $(openssl_includes) include $(srcdir)/Makefile.shared @@ -36,14 +36,23 @@ link_sources: ss=`echo $(mystringsobjects) | sed "s;\.lo;.c;g"`; \ ds=`echo $(dbugobjects) | sed "s;\.lo;.c;g"`; \ ms=`echo $(mysysobjects) | sed "s;\.lo;.c;g"`; \ + vs=`echo $(vio_objects) | sed "s;\.lo;.c;g"`; \ for f in $$ss; do \ rm -f $(srcdir)/$$f; \ @LN_CP_F@ $(srcdir)/../strings/$$f $(srcdir)/$$f; \ done; \ + for f in $$vs; do \ + rm -f $(srcdir)/$$f; \ + @LN_CP_F@ $(srcdir)/../vio/$$f $(srcdir)/$$f; \ + done; \ for f in $(mystringsextra); do \ rm -f $(srcdir)/$$f; \ @LN_CP_F@ $(srcdir)/../strings/$$f $(srcdir)/$$f; \ done; \ + for f in $$qs; do \ + rm -f $(srcdir)/$$f; \ + @LN_CP_F@ $(srcdir)/../sql/$$f $(srcdir)/$$f; \ + done; \ for f in $$ds; do \ rm -f $(srcdir)/$$f; \ @LN_CP_F@ $(srcdir)/../dbug/$$f $(srcdir)/$$f; \ @@ -62,11 +71,11 @@ link_sources: # keep only the stubs for safemalloc.c and debug.c # # A list of needed headers collected from the deps information 000213 -nh = global.h config-win32.h dbug.h errmsg.h global.h \ +nh = my_global.h config-win32.h dbug.h errmsg.h \ m_ctype.h m_string.h \ my_alarm.h my_config.h my_dir.h my_list.h my_net.h my_sys.h \ - mysql.h mysql_com.h mysql_version.h mysqld_error.h mysys_err.h \ - my_pthread.h thr_alarm.h violite.h hash.h + mysql.h mysql_com.h mysql_version.h mysqld_error.h \ + mysys_err.h my_pthread.h thr_alarm.h violite.h hash.h # Get a list of the needed objects lobjs = $(mysysobjects1) $(dbugobjects) $(mystringsobjects) diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index e9e100e38b1..d661d69a339 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -31,7 +31,7 @@ noinst_PROGRAMS = conf_to_src CHARSET_OBJS=@CHARSET_OBJS@ LTCHARSET_OBJS= ${CHARSET_OBJS:.o=.lo} -target_sources = libmysql.c net.c violite.c password.c \ +target_sources = libmysql.c net.c password.c manager.c \ get_password.c errmsg.c mystringsobjects = strmov.lo strxmov.lo strnmov.lo strmake.lo strend.lo \ @@ -55,13 +55,17 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \ mf_loadpath.lo my_pthread.lo my_thr_init.lo \ thr_mutex.lo mulalloc.lo string.lo default.lo \ my_compress.lo array.lo my_once.lo list.lo my_net.lo \ - charset.lo hash.lo mf_iocache.lo my_seek.lo \ - my_pread.lo mf_cache.lo + charset.lo hash.lo mf_iocache.lo \ + mf_iocache2.lo my_seek.lo \ + my_pread.lo mf_cache.lo my_vsnprintf.lo md5.lo + # Not needed in the minimum library mysysobjects2 = getopt.lo getopt1.lo getvar.lo my_lib.lo mysysobjects = $(mysysobjects1) $(mysysobjects2) -target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) +target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \ + $(vio_objects) target_ldflags = -version-info @SHARED_LIB_VERSION@ +vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ $(target) DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ @@ -76,6 +80,7 @@ clean-local: rm -f `echo $(mystringsobjects) | sed "s;\.lo;.c;g"` \ `echo $(dbugobjects) | sed "s;\.lo;.c;g"` \ `echo $(mysysobjects) | sed "s;\.lo;.c;g"` \ + `echo $(vio_objects) | sed "s;\.lo;.c;g"` \ $(mystringsextra) $(mysysheaders) ctype_extra_sources.c \ ../linked_client_sources diff --git a/libmysql/dll.c b/libmysql/dll.c index d1a23794025..5cd01e9c5df 100644 --- a/libmysql/dll.c +++ b/libmysql/dll.c @@ -19,7 +19,7 @@ ** Handling initialization of the dll library */ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <my_pthread.h> diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 67cfe874f77..8a1069a4afb 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -16,9 +16,9 @@ MA 02111-1307, USA */ /* Error messages for MySQL clients */ -/* error messages for the demon is in share/language/errmsg.sys */ +/* error messages for the daemon is in share/language/errmsg.sys */ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include "errmsg.h" @@ -45,7 +45,12 @@ const char *client_errors[]= "Kann Named Pipe nicht oeffnen. Host: %-.64s pipe: %-.32s (%lu)", "Kann den Status der Named Pipe nicht setzen. Host: %-.64s pipe: %-.32s (%lu)", "Can't initialize character set %-.64s (path: %-.64s)", - "Got packet bigger than 'max_allowed_packet'" + "Got packet bigger than 'max_allowed_packet'", + "Embedded server", + "Error on SHOW SLAVE STATUS:", + "Error on SHOW SLAVE HOSTS:", + "Error connecting to slave:", + "Error connecting to master:" }; /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ @@ -73,7 +78,12 @@ const char *client_errors[]= "Não pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", "Não pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", "Não pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", - "Obteve pacote maior do que 'max_allowed_packet'" + "Obteve pacote maior do que 'max_allowed_packet'", + "Embedded server" + "Error on SHOW SLAVE STATUS:", + "Error on SHOW SLAVE HOSTS:", + "Error connecting to slave:", + "Error connecting to master:" }; #else /* ENGLISH */ @@ -99,7 +109,12 @@ const char *client_errors[]= "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)", "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)", "Can't initialize character set %-.64s (path: %-.64s)", - "Got packet bigger than 'max_allowed_packet'" + "Got packet bigger than 'max_allowed_packet'", + "Embedded server", + "Error on SHOW SLAVE STATUS:", + "Error on SHOW SLAVE HOSTS:", + "Error connecting to slave:", + "Error connecting to master:" }; #endif diff --git a/libmysql/get_password.c b/libmysql/get_password.c index 989de9dd11a..4941c9b43d3 100644 --- a/libmysql/get_password.c +++ b/libmysql/get_password.c @@ -19,7 +19,7 @@ ** Ask for a password from tty ** This is an own file to avoid conflicts with curses */ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include "mysql.h" #include <m_string.h> diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 4a5e534389e..02921480abd 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -15,7 +15,7 @@ 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> @@ -70,7 +70,7 @@ 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> @@ -91,6 +91,32 @@ 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); +int STDCALL mysql_server_init(int argc __attribute__((unused)), + char **argv __attribute__((unused)), + char **groups __attribute__((unused))) +{ + return 0; +} + +void STDCALL mysql_server_end() +{} + +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,17 +132,23 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, #define reset_sigpipe(mysql) #endif +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, + const char* user, + const char* passwd); + + /**************************************************************************** * A modified version of connect(). connect2() allows you to specify * a timeout value, in seconds, that we should wait until we * derermine we can't connect to a particular host. If timeout is 0, -* connect2() will behave exactly like connect(). +* 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 s, const struct sockaddr *name, uint namelen, + uint timeout) { #if defined(__WIN__) || defined(OS2) return connect(s, (struct sockaddr*) name, namelen); @@ -284,11 +316,11 @@ HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, ** 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 */ @@ -306,26 +338,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 { @@ -416,7 +440,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; @@ -647,8 +671,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 */ @@ -673,9 +697,10 @@ mysql_free_result(MYSQL_RES *result) static const char *default_options[]= {"port","socket","compress","password","pipe", "timeout", "user", "init-command", "host", "database", "debug", "return-found-rows", - "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", + "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", "ssl-cipher" "character-set-dir", "default-character-set", "interactive-timeout", - "connect_timeout", + "connect_timeout", "replication-probe", "enable-reads-from-master", + "repl-parse-query", NullS }; @@ -809,6 +834,15 @@ static void mysql_read_default_options(struct st_mysql_options *options, case 19: /* Interactive-timeout */ options->client_flag|=CLIENT_INTERACTIVE; break; + case 21: /* replication probe */ + options->rpl_probe = 1; + break; + case 22: /* enable-reads-from-master */ + options->rpl_parse = 1; + break; + case 23: /* repl-parse-query */ + options->no_master_reads = 0; + break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); } @@ -832,13 +866,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]; @@ -870,7 +905,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; @@ -879,7 +915,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)))) @@ -956,7 +992,7 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) ulong pkt_len,len; uchar *pos,*prev_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 */ @@ -984,6 +1020,280 @@ 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) +{ + if (mysql_master_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); +} + +int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned long length) +{ + MYSQL*master = mysql->master; + if (!length) + length = strlen(q); + if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) + return 1; + mysql->last_used_con = master; + return simple_command(master, COM_QUERY, q, length, 1); +} + + +/* perform query on slave */ +int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, + unsigned long length) +{ + if (mysql_slave_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); +} + +int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned long length) +{ + MYSQL* last_used_slave, *slave_to_use = 0; + + if ((last_used_slave = mysql->last_used_slave)) + slave_to_use = last_used_slave->next_slave; + else + slave_to_use = mysql->next_slave; + /* next_slave is always safe to use - we have a circular list of slaves + if there are no slaves, mysql->next_slave == mysql + */ + mysql->last_used_con = mysql->last_used_slave = slave_to_use; + if (!length) + length = strlen(q); + if (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, + 0,0,0,0)) + return 1; + return simple_command(slave_to_use, COM_QUERY, q, length, 1); +} + + +/* enable/disable parsing of all queries to decide + if they go on master or slave */ +void STDCALL mysql_enable_rpl_parse(MYSQL* mysql) +{ + mysql->options.rpl_parse = 1; +} + +void STDCALL mysql_disable_rpl_parse(MYSQL* mysql) +{ + mysql->options.rpl_parse = 0; +} + +/* get the value of the parse flag */ +int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql) +{ + return mysql->options.rpl_parse; +} + +/* enable/disable reads from master */ +void STDCALL mysql_enable_reads_from_master(MYSQL* mysql) +{ + mysql->options.no_master_reads = 0; +} + +void STDCALL mysql_disable_reads_from_master(MYSQL* mysql) +{ + mysql->options.no_master_reads = 1; +} + +/* get the value of the master read flag */ +int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql) +{ + return !(mysql->options.no_master_reads); +} + +/* We may get an error while doing replication internals. + In this case, we add a special explanation to the original + error +*/ +static inline void expand_error(MYSQL* mysql, int error) +{ + char tmp[MYSQL_ERRMSG_SIZE]; + char* p, *tmp_end; + tmp_end = strnmov(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE); + p = strnmov(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE); + memcpy(p, tmp, tmp_end - tmp); + mysql->net.last_errno = error; +} + +/* This function assumes we have just called SHOW SLAVE STATUS and have + read the given result and row +*/ +static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row) +{ + MYSQL* master; + if (mysql_num_fields(res) < 3) + return 1; /* safety */ + + /* use the same username and password as the original connection */ + if (!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0))) + return 1; + mysql->master = master; + return 0; +} + +/* assuming we already know that mysql points to a master connection, + retrieve all the slaves +*/ +static inline int get_slaves_from_master(MYSQL* mysql) +{ + MYSQL_RES* res = 0; + MYSQL_ROW row; + int error = 1; + int has_auth_info; + int port_ind; + + if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0)) + { + expand_error(mysql, CR_PROBE_MASTER_CONNECT); + return 1; + } + + if (mysql_query(mysql, "SHOW SLAVE HOSTS") || + !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_HOSTS); + return 1; + } + + switch (mysql_num_fields(res)) + { + case 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); + return error; +} + +int STDCALL mysql_rpl_probe(MYSQL* mysql) +{ + MYSQL_RES* res = 0; + MYSQL_ROW row; + int error = 1; + /* first determine the replication role of the server we connected to + the most reliable way to do this is to run SHOW SLAVE STATUS and see + if we have a non-empty master host. This is still not fool-proof - + it is not a sin to have a master that has a dormant slave thread with + a non-empty master host. However, it is more reliable to check + for empty master than whether the slave thread is actually running + */ + if (mysql_query(mysql, "SHOW SLAVE STATUS") || + !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_STATUS); + return 1; + } + + if (!(row = mysql_fetch_row(res))) + goto err; + + /* check master host for emptiness/NULL */ + if (row[0] && *(row[0])) + { + /* this is a slave, ask it for the master */ + if (get_master(mysql, res, row) || get_slaves_from_master(mysql)) + goto err; + } + else + { + mysql->master = mysql; + if (get_slaves_from_master(mysql)) + goto err; + } + + error = 0; +err: + if (res) + mysql_free_result(res); + return error; +} + + +/* make a not so fool-proof decision on where the query should go, to + the master or the slave. Ideally the user should always make this + decision himself with mysql_master_query() or mysql_slave_query(). + However, to be able to more easily port the old code, we support the + option of an educated guess - this should work for most applications, + however, it may make the wrong decision in some particular cases. If + that happens, the user would have to change the code to call + mysql_master_query() or mysql_slave_query() explicitly in the place + where we have made the wrong decision +*/ +enum mysql_rpl_type +STDCALL mysql_rpl_query_type(const char* q, int len) +{ + const char* q_end; + q_end = (len) ? q + len : strend(q); + for(; q < q_end; ++q) + { + char c; + if (isalpha(c=*q)) + switch(tolower(c)) + { + case 'i': /* insert */ + case 'u': /* update or unlock tables */ + case 'l': /* lock tables or load data infile */ + case 'd': /* drop or delete */ + case 'a': /* alter */ + return MYSQL_RPL_MASTER; + case 'c': /* create or check */ + return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_MASTER ; + case 's': /* select or show */ + return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_SLAVE; + case 'f': /* flush */ + case 'r': /* repair */ + case 'g': /* grant */ + return MYSQL_RPL_ADMIN; + default: + return MYSQL_RPL_SLAVE; + } + } + return MYSQL_RPL_MASTER; /* By default, send to master */ +} + + /**************************************************************************** ** Init MySQL structure or allocate one ****************************************************************************/ @@ -1002,6 +1312,12 @@ mysql_init(MYSQL *mysql) else bzero((char*) (mysql),sizeof(*(mysql))); mysql->options.connect_timeout=CONNECT_TIMEOUT; + mysql->last_used_con = mysql->next_slave = mysql->master = mysql; + mysql->last_used_slave = 0; + /* By default, we are a replication pivot. The caller must reset it + after we return if this is not the case. + */ + mysql->rpl_pivot = 1; #if defined(SIGPIPE) && defined(THREAD) && !defined(__WIN__) if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) (void) signal(SIGPIPE,pipe_sig_handler); @@ -1053,63 +1369,65 @@ static void mysql_once_init() #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_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))) { +#ifdef HAVE_OPENSSL 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); + mysql->options.ssl_cipher = cipher==0 ? 0 : my_strdup(cipher,MYF(0)); + mysql->options.use_ssl = TRUE; + mysql->connector_fd = (gptr)new_VioSSLConnectorFd(key, cert, ca, capath, cipher); + DBUG_PRINT("info",("mysql_ssl_set, context: %p",((struct st_VioSSLConnectorFd *)(mysql->connector_fd))->ssl_context_)); +#endif return 0; } -/************************************************************************** -**************************************************************************/ - -char * STDCALL -mysql_ssl_cipher(MYSQL *mysql) -{ - return (char *)mysql->net.vio->cipher_description(); -} - - -/************************************************************************** +/* +*************************************************************************** ** 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) +mysql_ssl_clear(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)); 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; + my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); mysql->connector_fd = 0; +#endif /* HAVE_OPENSSL */ return 0; } -#endif /* HAVE_OPENSSL */ /************************************************************************** ** 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) @@ -1126,6 +1444,7 @@ mysql_connect(MYSQL *mysql,const char *host, DBUG_RETURN(res); } } +#endif /* @@ -1143,7 +1462,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, my_socket sock; uint32 ip_addr; struct sockaddr_in sock_addr; - uint pkt_length; + ulong pkt_length; NET *net= &mysql->net; #ifdef __WIN__ HANDLE hPipe=INVALID_HANDLE_VALUE; @@ -1219,7 +1538,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); UNIXaddr.sun_family = AF_UNIX; strmov(UNIXaddr.sun_path, unix_socket); - if (connect2(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr), + 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)); @@ -1319,7 +1638,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, } #endif 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)); @@ -1357,8 +1676,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, @@ -1394,7 +1712,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,11 +1811,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, goto error; /* Do the SSL layering. */ 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); + DBUG_PRINT("info", ("IO context %p",((struct st_VioSSLConnectorFd*)mysql->connector_fd)->ssl_context_)); + sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd),mysql->net.vio, (long)(mysql->options.connect_timeout)); + DBUG_PRINT("info", ("IO layer change done!")); } #endif /* HAVE_OPENSSL */ @@ -1522,7 +1838,7 @@ 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) || + if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net) || net_safe_read(mysql) == packet_error) goto error; if (client_flag & CLIENT_COMPRESS) /* We will use compression */ @@ -1539,6 +1855,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); @@ -1557,6 +1876,23 @@ error: DBUG_RETURN(0); } +/* 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) { @@ -1566,13 +1902,14 @@ 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; 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)) @@ -1581,6 +1918,7 @@ static my_bool mysql_reconnect(MYSQL *mysql) 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); @@ -1606,7 +1944,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)); @@ -1631,7 +1969,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)); @@ -1672,14 +2010,29 @@ 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_clear(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)); } @@ -1698,6 +2051,68 @@ mysql_query(MYSQL *mysql, const char *query) return mysql_real_query(mysql,query, (uint) strlen(query)); } + +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, const char* user, + const char* passwd) +{ + MYSQL* child; + if (!(child = mysql_init(0))) + return 0; + + child->options.user = my_strdup((user) ? user : + (parent->user ? parent->user : + parent->options.user), MYF(0)); + child->options.password = my_strdup((passwd) ? passwd : + (parent->passwd ? + parent->passwd : + parent->options.password), MYF(0)); + child->options.port = port; + child->options.host = my_strdup((host) ? host : + (parent->host ? + parent->host : + parent->options.host), MYF(0)); + if (parent->db) + child->options.db = my_strdup(parent->db, MYF(0)); + else if (parent->options.db) + child->options.db = my_strdup(parent->options.db, MYF(0)); + + child->options.rpl_parse = child->options.rpl_probe = child->rpl_pivot = 0; + + return child; +} + + +int +STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, const char* user, + const char* passwd) +{ + if (mysql->master != mysql && !mysql->master->rpl_pivot) + mysql_close(mysql->master); + if (!(mysql->master = spawn_init(mysql, host, port, user, passwd))) + return 1; + mysql->master->rpl_pivot = 0; + mysql->master->options.rpl_parse = 0; + mysql->master->options.rpl_probe = 0; + return 0; +} + +int +STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + MYSQL* slave; + if (!(slave = spawn_init(mysql, host, port, user, passwd))) + return 1; + slave->next_slave = mysql->next_slave; + mysql->next_slave = slave; + return 0; +} + + /* Send the query and return so we can do something else. Needs to be followed by mysql_read_query_result() when we want to @@ -1705,19 +2120,38 @@ 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) { + if (mysql->options.rpl_parse && mysql->rpl_pivot) + { + switch (mysql_rpl_query_type(query, length)) { + case MYSQL_RPL_MASTER: + return mysql_master_send_query(mysql, query, length); + case MYSQL_RPL_SLAVE: + return mysql_slave_send_query(mysql, query, length); + case MYSQL_RPL_ADMIN: + break; /* fall through */ + } + } + + mysql->last_used_con = mysql; 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 */ @@ -1754,21 +2188,24 @@ 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)) + + 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) { @@ -1836,6 +2273,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) @@ -1845,8 +2285,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; @@ -1888,6 +2329,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) @@ -2000,7 +2443,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; } @@ -2101,7 +2544,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); @@ -2151,12 +2594,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)); } @@ -2165,8 +2609,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 @@ -2341,32 +2786,32 @@ uint STDCALL mysql_field_tell(MYSQL_RES *res) unsigned int STDCALL mysql_field_count(MYSQL *mysql) { - return mysql->field_count; + return mysql->last_used_con->field_count; } my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) { - return (mysql)->affected_rows; + return mysql->last_used_con->affected_rows; } my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) { - return (mysql)->insert_id; + return mysql->last_used_con->insert_id; } uint STDCALL mysql_errno(MYSQL *mysql) { - return (mysql)->net.last_errno; + return mysql->net.last_errno; } char * STDCALL mysql_error(MYSQL *mysql) { - return (mysql)->net.last_error; + return mysql->net.last_error; } char *STDCALL mysql_info(MYSQL *mysql) { - return (mysql)->info; + return mysql->info; } ulong STDCALL mysql_thread_id(MYSQL *mysql) diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def new file mode 100644 index 00000000000..47e182b6de4 --- /dev/null +++ b/libmysql/libmysql.def @@ -0,0 +1,76 @@ +LIBRARY LIBMYSQL +DESCRIPTION 'MySQL 4.0 Client Library' +VERSION 4.0 +EXPORTS + + mysql_num_rows + mysql_num_fields + mysql_eof + mysql_fetch_field_direct + mysql_fetch_fields + mysql_row_tell + mysql_field_tell + mysql_field_count + mysql_affected_rows + mysql_insert_id + mysql_errno + mysql_error + mysql_info + mysql_thread_id + mysql_character_set_name + mysql_init + mysql_ssl_set + mysql_ssl_clear + mysql_change_user + mysql_real_connect + mysql_close + mysql_select_db + mysql_query + mysql_send_query + mysql_read_query_result + mysql_real_query + mysql_master_query + mysql_master_send_query + mysql_slave_query + mysql_slave_send_query + mysql_enable_rpl_parse + mysql_disable_rpl_parse + mysql_rpl_parse_enabled + mysql_enable_reads_from_master + mysql_disable_reads_from_master + mysql_reads_from_master_enabled + mysql_rpl_query_type + mysql_rpl_probe + mysql_set_master + mysql_add_slave + mysql_shutdown + mysql_dump_debug_info + mysql_refresh + mysql_kill + mysql_ping + mysql_stat + mysql_get_server_info + mysql_get_client_info + mysql_get_host_info + mysql_get_proto_info + mysql_list_dbs + mysql_list_tables + mysql_list_fields + mysql_list_processes + mysql_store_result + mysql_use_result + mysql_options + mysql_free_result + mysql_data_seek + mysql_row_seek + mysql_field_seek + mysql_fetch_row + mysql_fetch_lengths + mysql_fetch_field + mysql_escape_string + mysql_real_escape_string + mysql_debug + mysql_odbc_escape_string + myodbc_remove_escape + mysql_thread_safe + diff --git a/libmysql/manager.c b/libmysql/manager.c new file mode 100644 index 00000000000..a1262a46754 --- /dev/null +++ b/libmysql/manager.c @@ -0,0 +1,278 @@ +/* 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, + 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 */ + +#include <my_global.h> +#if defined(THREAD) +#include <my_pthread.h> /* because of signal() */ +#endif +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include <my_sys.h> +#include <mysys_err.h> +#include <m_string.h> +#include <m_ctype.h> +#include <my_net.h> +#include <errmsg.h> +#include <violite.h> +#include <sys/stat.h> +#include <signal.h> +#include <errno.h> + +#if defined(OS2) +# include <sys/un.h> +#elif !defined( __WIN__) +#include <sys/resource.h> +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif +#include <netdb.h> +#ifdef HAVE_SELECT_H +# include <select.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/utsname.h> +#endif /* __WIN__ */ + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +#define RES_BUF_SHIFT 5 +#ifndef __WIN__ +#define SOCKET_ERROR -1 +#endif +#define NET_BUF_SIZE 2048 + +MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con) +{ + int net_buf_size=NET_BUF_SIZE; + if (!con) + { + if (!(con=(MYSQL_MANAGER*)my_malloc(sizeof(*con)+net_buf_size, + MYF(MY_WME|MY_ZEROFILL)))) + return 0; + con->free_me=1; + con->net_buf=(char*)con+sizeof(*con); + } + else + { + bzero((char*)con,sizeof(*con)); + if (!(con->net_buf=my_malloc(net_buf_size,MYF(0)))) + return 0; + } + con->net_buf_pos=con->net_data_end=con->net_buf; + con->net_buf_size=net_buf_size; + return con; +} + +MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con, + const char* host, + const char* user, + const char* passwd, + unsigned int port) +{ + my_socket sock; + struct sockaddr_in sock_addr; + uint32 ip_addr; + char msg_buf[MAX_MYSQL_MANAGER_MSG]; + int msg_len; + Vio* vio; + + if (!host) + host="localhost"; + if (!user) + user="root"; + if (!passwd) + passwd=""; + + if ((sock=(my_socket)socket(AF_INET,SOCK_STREAM,0)) == SOCKET_ERROR) + { + con->last_errno=errno; + strmov(con->last_error,"Cannot create socket"); + goto err; + } + if (!(vio=vio_new(sock,VIO_TYPE_TCPIP,FALSE))) + { + con->last_errno=ENOMEM; + strmov(con->last_error,"Cannot create network I/O object"); + goto err; + } + vio_blocking(vio,TRUE); + my_net_init(&con->net,vio); + bzero((char*) &sock_addr,sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) + { + memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); + } + else +#if defined(HAVE_GETHOSTBYNAME_R) && defined(_REENTRANT) && defined(THREAD) + { + int tmp_errno; + struct hostent tmp_hostent,*hp; + char buff2[GETHOSTBYNAME_BUFF_SIZE]; + hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), + &tmp_errno); + if (!hp) + { + con->last_errno=tmp_errno; + sprintf(con->last_error,"Could not resolve host '%s'",host); + goto err; + } + memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); + } +#else + { + struct hostent *hp; + if (!(hp=gethostbyname(host))) + { + con->last_errno=socket_errno; + sprintf(con->last_error, "Could not resolve host '%s'", host); + goto err; + } + memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); + } +#endif + sock_addr.sin_port = (ushort) htons((ushort) port); + if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), + 0) <0) + { + con->last_errno=errno; + sprintf(con->last_error ,"Could not connect to %-.64s", host); + goto err; + } + /* read the greating */ + if (my_net_read(&con->net) == packet_error) + { + con->last_errno=errno; + strmov(con->last_error,"Read error on socket"); + goto err; + } + sprintf(msg_buf,"%-.16s %-.16s\n",user,passwd); + msg_len=strlen(msg_buf); + if (my_net_write(&con->net,msg_buf,msg_len) || net_flush(&con->net)) + { + con->last_errno=con->net.last_errno; + strmov(con->last_error,"Write error on socket"); + goto err; + } + if (my_net_read(&con->net) == packet_error) + { + con->last_errno=errno; + strmov(con->last_error,"Read error on socket"); + goto err; + } + if ((con->cmd_status=atoi((char*) con->net.read_pos)) != MANAGER_OK) + { + strmov(con->last_error,"Access denied"); + goto err; + } + if (!my_multi_malloc(MYF(0), &con->host, (uint)strlen(host)+1, + &con->user, (uint)strlen(user)+1, + &con->passwd, (uint)strlen(passwd)+1, + NullS)) + { + con->last_errno=ENOMEM; + strmov(con->last_error,"Out of memory"); + goto err; + } + strmov(con->host,host); + strmov(con->user,user); + strmov(con->passwd,passwd); + return con; +err: + { + my_bool free_me=con->free_me; + con->free_me=0; + mysql_manager_close(con); + con->free_me=free_me; + } + return 0; +} + +void STDCALL mysql_manager_close(MYSQL_MANAGER* con) +{ + my_free((gptr)con->host,MYF(MY_ALLOW_ZERO_PTR)); + /* no need to free con->user and con->passwd, because they were + allocated in my_multimalloc() along with con->host, freeing + con->hosts frees the whole block + */ + net_end(&con->net); + if (con->free_me) + my_free((gptr)con,MYF(0)); +} + +int STDCALL mysql_manager_command(MYSQL_MANAGER* con,const char* cmd, + int cmd_len) +{ + if (!cmd_len) + cmd_len=strlen(cmd); + if (my_net_write(&con->net,(char*)cmd,cmd_len) || net_flush(&con->net)) + { + con->last_errno=errno; + strmov(con->last_error,"Write error on socket"); + return 1; + } + con->eof=0; + return 0; +} + +int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con, char* res_buf, + int res_buf_size) +{ + char* res_buf_end=res_buf+res_buf_size; + char* net_buf=(char*) con->net.read_pos, *net_buf_end; + int res_buf_shift=RES_BUF_SHIFT; + uint num_bytes; + + if (res_buf_size<RES_BUF_SHIFT) + { + con->last_errno=ENOMEM; + strmov(con->last_error,"Result buffer too small"); + return 1; + } + + if ((num_bytes=my_net_read(&con->net)) == packet_error) + { + con->last_errno=errno; + strmov(con->last_error,"socket read failed"); + return 1; + } + + net_buf_end=net_buf+num_bytes; + + if ((con->eof=(net_buf[3]==' '))) + res_buf_shift--; + net_buf+=res_buf_shift; + res_buf_end[-1]=0; + for (;net_buf<net_buf_end && res_buf < res_buf_end;res_buf++,net_buf++) + { + if((*res_buf=*net_buf) == '\r') + { + *res_buf=0; + break; + } + } + return 0; +} + + + + diff --git a/libmysql/net.c b/libmysql/net.c index 24e4da3561a..5a39b071b4f 100644 --- a/libmysql/net.c +++ b/libmysql/net.c @@ -1,15 +1,15 @@ /* 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, 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, @@ -22,45 +22,47 @@ ** 3 byte length & 1 byte package-number. */ +#ifdef EMBEDDED_LIBRARY +#define net_read_timeout net_read_timeout1 +#define net_write_timeout net_write_timeout1 +#endif + #ifdef __WIN__ #include <winsock.h> #endif -#include <global.h> -#include <violite.h> +#include <my_global.h> +#include <mysql.h> +#include <mysql_embed.h> +#include <mysql_com.h> +#include <mysqld_error.h> #include <my_sys.h> #include <m_string.h> -#include "mysql.h" -#include "mysqld_error.h" +#include <my_net.h> +#include <violite.h> #include <signal.h> #include <errno.h> -#include <sys/types.h> -#include <violite.h> #ifdef MYSQL_SERVER ulong max_allowed_packet=65536; extern ulong net_read_timeout,net_write_timeout; extern uint test_flags; #else -ulong max_allowed_packet=16*1024*1024L; + +/* +** Give error if a too big packet is found +** The server can change this with the -O switch, but because the client +** can't normally do this the client should have a bigger max_allowed_packet. +*/ + +ulong max_allowed_packet=~0L; ulong net_read_timeout= NET_READ_TIMEOUT; ulong net_write_timeout= NET_WRITE_TIMEOUT; #endif ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */ -#if !defined(__WIN__) && !defined(MSDOS) -#include <sys/socket.h> -#else +#if defined(__WIN__) || defined(MSDOS) #undef MYSQL_SERVER /* Win32 can't handle interrupts */ #endif -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) -#include <netinet/in_systm.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> -#endif -#endif -#include "mysqld_error.h" #ifdef MYSQL_SERVER #include "my_pthread.h" #include "thr_alarm.h" @@ -77,7 +79,7 @@ extern ulong mysqld_net_retry_count; typedef my_bool thr_alarm_t; typedef my_bool ALARM; #define thr_alarm_init(A) (*(A))=0 -#define thr_alarm_in_use(A) (*(A)!= 0) +#define thr_alarm_in_use(A) (*(A) != 0) #define thr_end_alarm(A) #define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) @@ -90,28 +92,26 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #endif #ifdef MYSQL_SERVER -extern ulong bytes_sent, bytes_received; +extern ulong bytes_sent, bytes_received; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else #undef statistic_add #define statistic_add(A,B,C) #endif -/* -** Give error if a too big packet is found -** The server can change this with the -O switch, but because the client -** can't normally do this the client should have a bigger max-buffer. -*/ - #define TEST_BLOCKING 8 -static int net_write_buff(NET *net,const char *packet,uint len); +static int net_write_buff(NET *net,const char *packet,ulong len); +#define MAX_THREE_BYTES 255L*255L*255L /* Init with packet info */ int my_net_init(NET *net, Vio* vio) { - if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME)))) + if (!(net->buff=(uchar*) my_malloc((uint32) net_buffer_length+ + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) return 1; if (net_buffer_length > max_allowed_packet) max_allowed_packet=net_buffer_length; @@ -120,12 +120,13 @@ int my_net_init(NET *net, Vio* vio) net->no_send_ok = 0; net->error=0; net->return_errno=0; net->return_status=0; net->timeout=(uint) net_read_timeout; /* Timeout for read */ - net->pkt_nr=0; + net->pkt_nr=net->compress_pkt_nr=0; net->write_pos=net->read_pos = net->buff; net->last_error[0]=0; net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; + net->query_cache_query=0; if (vio != 0) /* If real connection */ { @@ -158,8 +159,12 @@ static my_bool net_realloc(NET *net, ulong length) net->last_errno=ER_NET_PACKET_TOO_LARGE; return 1; } - pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); - if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME)))) + pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); + /* We must allocate some extra bytes for the end 0 and to be able to + read big compressed blocks */ + if (!(buff=(uchar*) my_realloc((char*) net->buff, (uint32) pkt_length + + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) { net->error=1; #ifdef MYSQL_SERVER @@ -177,21 +182,21 @@ static my_bool net_realloc(NET *net, ulong length) void net_clear(NET *net) { #ifndef EXTRA_DEBUG - int count; + int count; /* One may get 'unused' warn */ bool is_blocking=vio_is_blocking(net->vio); if (is_blocking) vio_blocking(net->vio, FALSE); if (!vio_is_blocking(net->vio)) /* Safety if SSL */ { while ( (count = vio_read(net->vio, (char*) (net->buff), - net->max_packet)) > 0) + (uint32) net->max_packet)) > 0) DBUG_PRINT("info",("skipped %d bytes from file: %s", count,vio_description(net->vio))); if (is_blocking) vio_blocking(net->vio, TRUE); } #endif /* EXTRA_DEBUG */ - net->pkt_nr=0; /* Ready for new command */ + net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; } @@ -204,9 +209,12 @@ int net_flush(NET *net) if (net->buff != net->write_pos) { error=net_real_write(net,(char*) net->buff, - (uint) (net->write_pos - net->buff)); + (ulong) (net->write_pos - net->buff)); net->write_pos=net->buff; } + /* Sync packet number if using compression */ + if (net->compress) + net->pkt_nr=net->compress_pkt_nr; DBUG_RETURN(error); } @@ -215,44 +223,91 @@ int net_flush(NET *net) ** Write something to server/client buffer *****************************************************************************/ - /* ** Write a logical packet with packet header ** Format: Packet length (3 bytes), packet number(1 byte) ** When compression is used a 3 byte compression length is added -** NOTE: If compression is used the original package is destroyed! +** NOTE: If compression is used the original package is modified! */ int my_net_write(NET *net,const char *packet,ulong len) { uchar buff[NET_HEADER_SIZE]; + /* + Big packets are handled by splitting them in packets of MAX_THREE_BYTES + length. The last packet is always a packet that is < MAX_THREE_BYTES. + (The last packet may even have a lengt of 0) + */ + while (len >= MAX_THREE_BYTES) + { + const ulong z_size = MAX_THREE_BYTES; + int3store(buff, z_size); + buff[3]= (uchar) net->pkt_nr++; + if (net_write_buff(net, (char*) buff, NET_HEADER_SIZE) || + net_write_buff(net, packet, z_size)) + return 1; + packet += z_size; + len-= z_size; + } + /* Write last packet */ int3store(buff,len); - buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++); + buff[3]= (uchar) net->pkt_nr++; if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE)) return 1; return net_write_buff(net,packet,len); } +/* + Send a command to the server. + As the command is part of the first data packet, we have to do some data + juggling to put the command in there, without having to create a new + packet. + This function will split big packets into sub-packets if needed. + (Each sub packet can only be 2^24 bytes) +*/ + int net_write_command(NET *net,uchar command,const char *packet,ulong len) { + ulong length=len+1; /* 1 extra byte for command */ uchar buff[NET_HEADER_SIZE+1]; - uint length=len+1; /* 1 extra byte for command */ + uint header_size=NET_HEADER_SIZE+1; + buff[4]=command; /* For first packet */ + if (length >= MAX_THREE_BYTES) + { + /* Take into account that we have the command in the first header */ + len= MAX_THREE_BYTES -1; + do + { + int3store(buff, MAX_THREE_BYTES); + buff[3]= (uchar) net->pkt_nr++; + if (net_write_buff(net,(char*) buff, header_size) || + net_write_buff(net,packet,len)) + return 1; + packet+= len; + length-= MAX_THREE_BYTES; + len=MAX_THREE_BYTES; + header_size=NET_HEADER_SIZE; + } while (length >= MAX_THREE_BYTES); + len=length; /* Data left to be written */ + } int3store(buff,length); - buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++); - buff[4]=command; - if (net_write_buff(net,(char*) buff,5)) - return 1; - return test(net_write_buff(net,packet,len) || net_flush(net)); + buff[3]= (uchar) net->pkt_nr++; + return test(net_write_buff(net,(char*) buff,header_size) || + net_write_buff(net,packet,len) || net_flush(net)); } +/* + Caching the data in a local buffer before sending it. + One can force the buffer to be flushed with 'net_flush'. +*/ static int -net_write_buff(NET *net,const char *packet,uint len) +net_write_buff(NET *net,const char *packet,ulong len) { - uint left_length=(uint) (net->buff_end - net->write_pos); + ulong left_length=(ulong) (net->buff_end - net->write_pos); while (len > left_length) { @@ -269,12 +324,16 @@ net_write_buff(NET *net,const char *packet,uint len) return 0; } -/* Read and write using timeouts */ + +/* + Read and write one packet using timeouts. + If needed, the packet is compressed before sending. +*/ int net_real_write(NET *net,const char *packet,ulong len) { - int length; + long int length; char *pos,*end; thr_alarm_t alarmed; #if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) @@ -284,6 +343,10 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); +#ifdef MYSQL_SERVER + query_cache_insert(net, packet, len); +#endif + if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -294,8 +357,8 @@ net_real_write(NET *net,const char *packet,ulong len) ulong complen; uchar *b; uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE; - if (!(b=(uchar*) my_malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE, - MYF(MY_WME)))) + if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE + + COMP_HEADER_SIZE, MYF(MY_WME)))) { #ifdef MYSQL_SERVER net->last_errno=ER_OUT_OF_RESOURCES; @@ -314,7 +377,7 @@ net_real_write(NET *net,const char *packet,ulong len) } int3store(&b[NET_HEADER_SIZE],complen); int3store(b,len); - b[3]=(uchar) (net->pkt_nr++); + b[3]=(uchar) (net->compress_pkt_nr++); len+= header_length; packet= (char*) b; } @@ -332,7 +395,7 @@ net_real_write(NET *net,const char *packet,ulong len) pos=(char*) packet; end=pos+len; while (pos != end) { - if ((int) (length=vio_write(net->vio,pos,(int) (end-pos))) <= 0) + if ((long) (length=vio_write(net->vio,pos,(uint32) (end-pos))) <= 0) { my_bool interrupted = vio_should_retry(net->vio); #if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) @@ -416,7 +479,7 @@ net_real_write(NET *net,const char *packet,ulong len) big packet */ -static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) +static void my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed) { ALARM alarm_buff; uint retry_count=0; @@ -432,21 +495,27 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L) { my_bool interrupted = vio_should_retry(net->vio); - if (!thr_got_alarm(alarmed) && interrupted) + if (!thr_got_alarm(&alarmed) && interrupted) { /* Probably in MIT threads */ if (retry_count++ < RETRY_COUNT) continue; } return; } - remain -=(ulong) length; - statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + remain -= (uint32) length; + statistic_add(bytes_received,length,&LOCK_bytes_received); } } #endif /* MYSQL_SERVER */ -static uint +/* + Reads one packet to net->buff + net->where_b + Returns length of packet. Long packets are handled by my_net_read(). + This function reallocates the net->buff buffer if necessary. +*/ + +static ulong my_real_read(NET *net, ulong *complen) { uchar *pos; @@ -458,8 +527,8 @@ my_real_read(NET *net, ulong *complen) ALARM alarm_buff; #endif my_bool net_blocking=vio_is_blocking(net->vio); - ulong remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : - NET_HEADER_SIZE); + uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : + NET_HEADER_SIZE); *complen = 0; net->reading_or_writing=1; @@ -536,7 +605,7 @@ my_real_read(NET *net, ulong *complen) continue; } #endif - DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); + DBUG_PRINT("error",("Couldn't read packet: remain: %lu errno: %d length: %ld alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); len= packet_error; net->error=2; /* Close socket */ #ifdef MYSQL_SERVER @@ -545,7 +614,7 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - remain -= (ulong) length; + remain -= (uint32) length; pos+= (ulong) length; statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); } @@ -557,9 +626,9 @@ my_real_read(NET *net, ulong *complen) if (net->buff[net->where_b] != (uchar) 255) { DBUG_PRINT("error", - ("Packets out of order (Found: %d, expected %d)", + ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], - (uint) (uchar) net->pkt_nr)); + net->pkt_nr)); #ifdef EXTRA_DEBUG fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", (int) net->buff[net->where_b + 3], @@ -572,7 +641,7 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - net->pkt_nr++; + net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS if (net->compress) { @@ -582,23 +651,24 @@ my_real_read(NET *net, ulong *complen) #endif len=uint3korr(net->buff+net->where_b); + if (!len) /* End of big multi-packet */ + goto end; helping = max(len,*complen) + net->where_b; /* The necessary size of net->buff */ if (helping >= net->max_packet) { - /* We must allocate one extra byte for the end null */ - if (net_realloc(net,helping+1)) + if (net_realloc(net,helping)) { #ifdef MYSQL_SERVER if (i == 1) - my_net_skip_rest(net, len, &alarmed); + my_net_skip_rest(net, (uint32) len, &alarmed); #endif len= packet_error; /* Return error */ goto end; } } pos=net->buff + net->where_b; - remain = len; + remain = (uint32) len; } } @@ -612,7 +682,21 @@ end: return(len); } -uint + +/* + Read a packet from the client/server and return it without the internal + package header. + If the packet is the first packet of a multi-packet packet + (which is indicated by the length of the packet = 0xffffff) then + all sub packets are read and concatenated. + If the packet was compressed, its uncompressed and the length of the + uncompressed packet is returned. + + The function returns the length of the found packet or packet_error. + net->read_pos points to the read data. +*/ + +ulong my_net_read(NET *net) { ulong len,complen; @@ -621,65 +705,126 @@ my_net_read(NET *net) if (!net->compress) { #endif - len = my_real_read (net,&complen); + len = my_real_read(net,&complen); + if (len == MAX_THREE_BYTES) + { + /* First packet of a multi-packet. Concatenate the packets */ + ulong save_pos = net->where_b; + ulong total_length=0; + do + { + net->where_b += len; + total_length += len; + len = my_real_read (net,&complen); + } while (len == MAX_THREE_BYTES); + if (len != packet_error) + len+= total_length; + net->where_b = save_pos; + } net->read_pos = net->buff + net->where_b; if (len != packet_error) net->read_pos[len]=0; /* Safeguard for mysql_use_result */ return len; #ifdef HAVE_COMPRESS } - if (net->remain_in_buf) - net->buff[net->buf_length - net->remain_in_buf]=net->save_char; - for (;;) + else { + /* We are using the compressed protocol */ + + ulong buf_length= net->buf_length; + ulong start_of_packet= net->buf_length - net->remain_in_buf; + ulong first_packet_offset=start_of_packet; + uint read_length, multi_byte_packet=0; + if (net->remain_in_buf) { - uchar *pos = net->buff + net->buf_length - net->remain_in_buf; - if (net->remain_in_buf >= 4) + /* Restore the character that was overwritten by the end 0 */ + net->buff[start_of_packet]=net->save_char; + } + else + { + /* reuse buffer, as there is noting in it that we need */ + buf_length=start_of_packet=first_packet_offset=0; + } + for (;;) + { + ulong packet_len; + + if (buf_length - start_of_packet >= NET_HEADER_SIZE) { - net->length = uint3korr(pos); - if (net->length <= net->remain_in_buf - 4) + read_length = uint3korr(net->buff+start_of_packet); + if (!read_length) + { + /* End of multi-byte packet */ + start_of_packet += NET_HEADER_SIZE; + break; + } + if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet) { - /* We have a full packet */ - len=net->length; - net->remain_in_buf -= net->length + 4; - net->read_pos=pos + 4; - break; /* We have a full packet */ + if (multi_byte_packet) + { + /* Remove packet header for second packet */ + memmove(net->buff + first_packet_offset + start_of_packet, + net->buff + first_packet_offset + start_of_packet + + NET_HEADER_SIZE, + buf_length - start_of_packet); + start_of_packet += read_length; + buf_length -= NET_HEADER_SIZE; + } + else + start_of_packet+= read_length + NET_HEADER_SIZE; + + if (read_length != MAX_THREE_BYTES) /* last package */ + { + multi_byte_packet= 0; // No last zero length packet + break; + } + multi_byte_packet= NET_HEADER_SIZE; + /* Move data down to read next data packet after current one */ + if (first_packet_offset) + { + memmove(net->buff,net->buff+first_packet_offset, + buf_length-first_packet_offset); + buf_length-=first_packet_offset; + start_of_packet -= first_packet_offset; + first_packet_offset=0; + } + continue; } } /* Move data down to read next data packet after current one */ - if (net->buf_length != net->remain_in_buf) + if (first_packet_offset) { - memmove(net->buff,pos,net->remain_in_buf); - net->buf_length=net->remain_in_buf; + memmove(net->buff,net->buff+first_packet_offset, + buf_length-first_packet_offset); + buf_length-=first_packet_offset; + start_of_packet -= first_packet_offset; + first_packet_offset=0; } - net->where_b=net->buf_length; - } - else - { - net->where_b=0; - net->buf_length=0; - } - if ((len = my_real_read(net,&complen)) == packet_error) - break; - if (my_uncompress((byte*) net->buff + net->where_b, &len, &complen)) - { - len= packet_error; - net->error=2; /* caller will close socket */ + net->where_b=buf_length; + if ((packet_len = my_real_read(net,&complen)) == packet_error) + return packet_error; + if (my_uncompress((byte*) net->buff + net->where_b, &packet_len, + &complen)) + { + net->error=2; /* caller will close socket */ #ifdef MYSQL_SERVER - net->last_errno=ER_NET_UNCOMPRESS_ERROR; + net->last_errno=ER_NET_UNCOMPRESS_ERROR; #endif - break; + return packet_error; + } + buf_length+=packet_len; } - net->buf_length+=len; - net->remain_in_buf+=len; - } - if (len != packet_error) - { + + net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE; + net->buf_length= buf_length; + net->remain_in_buf= (ulong) (buf_length - start_of_packet); + len = ((ulong) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE - + multi_byte_packet); net->save_char= net->read_pos[len]; /* Must be saved */ net->read_pos[len]=0; /* Safeguard for mysql_use_result */ } +#endif /* HAVE_COMPRESS */ return len; -#endif } diff --git a/libmysql/password.c b/libmysql/password.c index 0fd5861873a..cb658f32022 100644 --- a/libmysql/password.c +++ b/libmysql/password.c @@ -35,7 +35,7 @@ This saves a hashed number as a string in the password field. *****************************************************************************/ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #include "mysql.h" diff --git a/libmysql/violite.c b/libmysql/violite.c deleted file mode 100644 index 37fee6fad3d..00000000000 --- a/libmysql/violite.c +++ /dev/null @@ -1,443 +0,0 @@ -/* 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, - 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 */ - -/* - Note that we can't have assertion on file descriptors; The reason for - this is that during mysql shutdown, another thread can close a file - we are working on. In this case we should just return read errors from - the file descriptior. -*/ - -#include <global.h> - -#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */ - -#include <errno.h> -#include <assert.h> -#include <violite.h> -#include <my_sys.h> -#include <my_net.h> -#include <m_string.h> -#ifdef HAVE_POLL -#include <sys/poll.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__FreeBSD__) -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> -#endif -#endif - -#if defined(__EMX__) || defined(OS2) -#define ioctlsocket ioctl -#endif /* defined(__EMX__) */ - -#if defined(MSDOS) || defined(__WIN__) -#define O_NONBLOCK 1 /* For emulation of fcntl() */ -#endif -#ifndef EWOULDBLOCK -#define SOCKET_EWOULDBLOCK SOCKET_EAGAIN -#endif - -#ifndef __WIN__ -#define HANDLE void * -#endif - -struct st_vio -{ - my_socket sd; /* my_socket - real or imaginary */ - HANDLE hPipe; - my_bool localhost; /* Are we from localhost? */ - int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */ - struct sockaddr_in local; /* Local internet address */ - struct sockaddr_in remote; /* Remote internet address */ - enum enum_vio_type type; /* Type of connection */ - char desc[30]; /* String description */ -}; - -typedef void *vio_ptr; -typedef char *vio_cstring; - -/* - * Helper to fill most of the Vio* with defaults. - */ - -static void vio_reset(Vio* vio, enum enum_vio_type type, - my_socket sd, HANDLE hPipe, - my_bool localhost) -{ - bzero((char*) vio, sizeof(*vio)); - vio->type = type; - vio->sd = sd; - vio->hPipe = hPipe; - vio->localhost= localhost; -} - -/* Open the socket or TCP/IP connection and read the fnctl() status */ - -Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost) -{ - Vio *vio; - DBUG_ENTER("vio_new"); - DBUG_PRINT("enter", ("sd=%d", sd)); - if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME)))) - { - vio_reset(vio, type, sd, 0, localhost); - sprintf(vio->desc, - (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"), - vio->sd); -#if !defined(___WIN__) && !defined(__EMX__) && !defined(OS2) -#if !defined(NO_FCNTL_NONBLOCK) - vio->fcntl_mode = fcntl(sd, F_GETFL); -#elif defined(HAVE_SYS_IOCTL_H) /* hpux */ - /* Non blocking sockets doesn't work good on HPUX 11.0 */ - (void) ioctl(sd,FIOSNBIO,0); -#endif -#else /* !defined(__WIN__) && !defined(__EMX__) */ - { - /* set to blocking mode by default */ - ulong arg=0, r; - r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg, sizeof(arg)); - } -#endif - } - DBUG_RETURN(vio); -} - - -#ifdef __WIN__ - -Vio *vio_new_win32pipe(HANDLE hPipe) -{ - Vio *vio; - DBUG_ENTER("vio_new_handle"); - if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME)))) - { - vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE); - strmov(vio->desc, "named pipe"); - } - DBUG_RETURN(vio); -} - -#endif - -void vio_delete(Vio * vio) -{ - /* It must be safe to delete null pointers. */ - /* This matches the semantics of C++'s delete operator. */ - if (vio) - { - if (vio->type != VIO_CLOSED) - vio_close(vio); - my_free((gptr) vio,MYF(0)); - } -} - -int vio_errno(Vio *vio __attribute__((unused))) -{ - return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ -} - - -int vio_read(Vio * vio, gptr buf, int size) -{ - int r; - DBUG_ENTER("vio_read"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if (vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosRead((HFILE)vio->hPipe, buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!ReadFile(vio->hPipe, buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - DBUG_RETURN(length); - } - r = recv(vio->sd, buf, size,0); -#else - errno=0; /* For linux */ - r = read(vio->sd, buf, size); -#endif /* __WIN__ */ -#ifndef DBUG_OFF - if (r < 0) - { - DBUG_PRINT("vio_error", ("Got error %d during read",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - - -int vio_write(Vio * vio, const gptr buf, int size) -{ - int r; - DBUG_ENTER("vio_write"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if ( vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosWrite((HFILE)vio->hPipe, (char*) buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - DBUG_RETURN(length); - } - r = send(vio->sd, buf, size,0); -#else - r = write(vio->sd, buf, size); -#endif /* __WIN__ */ -#ifndef DBUG_OFF - if (r < 0) - { - DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - - -int vio_blocking(Vio * vio, my_bool set_blocking_mode) -{ - int r=0; - DBUG_ENTER("vio_blocking"); - DBUG_PRINT("enter", ("set_blocking_mode: %d", (int) set_blocking_mode)); - -#if !defined(___WIN__) && !defined(__EMX__) && !defined(OS2) -#if !defined(NO_FCNTL_NONBLOCK) - - if (vio->sd >= 0) - { - int old_fcntl=vio->fcntl_mode; - if (set_blocking_mode) - vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */ - else - vio->fcntl_mode |= O_NONBLOCK; /* set bit */ - if (old_fcntl != vio->fcntl_mode) - r = fcntl(vio->sd, F_SETFL, vio->fcntl_mode); - } -#endif /* !defined(NO_FCNTL_NONBLOCK) */ -#else /* !defined(__WIN__) && !defined(__EMX__) */ -#ifndef __EMX__ - if (vio->type != VIO_TYPE_NAMEDPIPE) -#endif - { - ulong arg; - int old_fcntl=vio->fcntl_mode; - if (set_blocking_mode) - { - arg = 0; - vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */ - } - else - { - arg = 1; - vio->fcntl_mode |= O_NONBLOCK; /* set bit */ - } - if (old_fcntl != vio->fcntl_mode) - r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg, sizeof(arg)); - } -#endif /* !defined(__WIN__) && !defined(__EMX__) */ - DBUG_RETURN(r); -} - -my_bool -vio_is_blocking(Vio * vio) -{ - my_bool r; - DBUG_ENTER("vio_is_blocking"); - r = !(vio->fcntl_mode & O_NONBLOCK); - DBUG_PRINT("exit", ("%d", (int) r)); - DBUG_RETURN(r); -} - - -int vio_fastsend(Vio * vio __attribute__((unused))) -{ - int r=0; - DBUG_ENTER("vio_fastsend"); - -#ifdef IPTOS_THROUGHPUT - { -#ifndef __EMX__ - int tos = IPTOS_THROUGHPUT; - if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos))) -#endif /* !__EMX__ */ - { - int nodelay = 1; - if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay, - sizeof(nodelay))) { - DBUG_PRINT("warning", - ("Couldn't set socket option for fast send")); - r= -1; - } - } - } -#endif /* IPTOS_THROUGHPUT */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - -int vio_keepalive(Vio* vio, my_bool set_keep_alive) -{ - int r=0; - uint opt = 0; - DBUG_ENTER("vio_keepalive"); - DBUG_PRINT("enter", ("sd=%d set_keep_alive=%d", vio->sd, (int) - set_keep_alive)); - if (vio->type != VIO_TYPE_NAMEDPIPE) - { - if (set_keep_alive) - opt = 1; - r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, - sizeof(opt)); - } - DBUG_RETURN(r); -} - - -my_bool -vio_should_retry(Vio * vio __attribute__((unused))) -{ - int en = socket_errno; - return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_EWOULDBLOCK; -} - - -int vio_close(Vio * vio) -{ - int r; - DBUG_ENTER("vio_close"); -#ifdef __WIN__ - if (vio->type == VIO_TYPE_NAMEDPIPE) - { -#if defined(__NT__) && defined(MYSQL_SERVER) - CancelIo(vio->hPipe); - DisconnectNamedPipe(vio->hPipe); -#endif - r=CloseHandle(vio->hPipe); - } - else if (vio->type != VIO_CLOSED) -#endif /* __WIN__ */ - { - r=0; - if (shutdown(vio->sd,2)) - r= -1; - if (closesocket(vio->sd)) - r= -1; - } - if (r) - { - DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno)); - /* FIXME: error handling (not critical for MySQL) */ - } - vio->type= VIO_CLOSED; - vio->sd= -1; - DBUG_RETURN(r); -} - - -const char *vio_description(Vio * vio) -{ - return vio->desc; -} - -enum enum_vio_type vio_type(Vio* vio) -{ - return vio->type; -} - -my_socket vio_fd(Vio* vio) -{ - return vio->sd; -} - - -my_bool vio_peer_addr(Vio * vio, char *buf) -{ - DBUG_ENTER("vio_peer_addr"); - DBUG_PRINT("enter", ("sd=%d", vio->sd)); - if (vio->localhost) - { - strmov(buf,"127.0.0.1"); - } - else - { - size_socket addrLen = sizeof(struct sockaddr); - if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)), - &addrLen) != 0) - { - DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno)); - DBUG_RETURN(1); - } - my_inet_ntoa(vio->remote.sin_addr,buf); - } - DBUG_PRINT("exit", ("addr=%s", buf)); - DBUG_RETURN(0); -} - - -void vio_in_addr(Vio *vio, struct in_addr *in) -{ - DBUG_ENTER("vio_in_addr"); - if (vio->localhost) - bzero((char*) in, sizeof(*in)); /* This should never be executed */ - else - *in=vio->remote.sin_addr; - DBUG_VOID_RETURN; -} - - -/* Return 0 if there is data to be read */ - -my_bool vio_poll_read(Vio *vio,uint timeout) -{ -#ifndef HAVE_POLL - return 0; -#else - struct pollfd fds; - int res; - DBUG_ENTER("vio_poll"); - fds.fd=vio->sd; - fds.events=POLLIN; - fds.revents=0; - if ((res=poll(&fds,1,(int) timeout*1000)) <= 0) - { - DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */ - } - DBUG_RETURN(fds.revents & POLLIN ? 0 : 1); -#endif -} - -#endif /* HAVE_VIO */ |