diff options
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/Makefile.am | 9 | ||||
-rw-r--r-- | libmysql/Makefile.shared | 7 | ||||
-rw-r--r-- | libmysql/errmsg.c | 23 | ||||
-rw-r--r-- | libmysql/libmysql.c | 435 | ||||
-rw-r--r-- | libmysql/net.c | 286 | ||||
-rw-r--r-- | libmysql/violite.c | 425 |
6 files changed, 664 insertions, 521 deletions
diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am index baf40a389a3..67b78f14ba2 100644 --- a/libmysql/Makefile.am +++ b/libmysql/Makefile.am @@ -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; \ diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index b6ea5832e04..4d3928c5bc5 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 \ get_password.c errmsg.c mystringsobjects = strmov.lo strxmov.lo strnmov.lo strmake.lo strend.lo \ @@ -59,8 +59,10 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.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 CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ $(target) DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ @@ -75,6 +77,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/errmsg.c b/libmysql/errmsg.c index 67cfe874f77..55e2f8164ed 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -16,7 +16,7 @@ MA 02111-1307, USA */ /* Error messages for MySQL clients */ -/* error messages for the demon is in share/language/errmsg.sys */ +/* error messages for the daemon is in share/language/errmsg.sys */ #include <global.h> #include <my_sys.h> @@ -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/libmysql.c b/libmysql/libmysql.c index 9221812ea65..d6f5b7c523f 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -109,6 +109,12 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, #define reset_sigpipe(mysql) #endif +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, + const char* user, + const char* passwd); + + /**************************************************************************** * A modified version of connect(). connect2() allows you to specify * a timeout value, in seconds, that we should wait until we @@ -678,7 +684,8 @@ static const char *default_options[]= "init-command", "host", "database", "debug", "return-found-rows", "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", "character-set-dir", "default-character-set", "interactive-timeout", - "connect_timeout", + "connect_timeout", "replication-probe", "enable-reads-from-master", + "repl-parse-query", NullS }; @@ -812,6 +819,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])); } @@ -987,6 +1003,273 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) return 0; } +/* perform query on master */ +int STDCALL mysql_master_query(MYSQL *mysql, const char *q, + unsigned int length) +{ + if(mysql_master_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); +} + +int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned int length) +{ + MYSQL*master = mysql->master; + if (!length) + length = strlen(q); + if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) + return 1; + mysql->last_used_con = master; + return simple_command(master, COM_QUERY, q, length, 1); +} + + +/* perform query on slave */ +int STDCALL mysql_slave_query(MYSQL *mysql, const char *q, + unsigned int length) +{ + if(mysql_slave_send_query(mysql, q, length)) + return 1; + return mysql_read_query_result(mysql); +} + +int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned int length) +{ + MYSQL* last_used_slave, *slave_to_use = 0; + + if((last_used_slave = mysql->last_used_slave)) + slave_to_use = last_used_slave->next_slave; + else + slave_to_use = mysql->next_slave; + /* next_slave is always safe to use - we have a circular list of slaves + if there are no slaves, mysql->next_slave == mysql + */ + mysql->last_used_con = mysql->last_used_slave = slave_to_use; + if(!length) + length = strlen(q); + if(!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, + 0,0,0,0)) + return 1; + return simple_command(slave_to_use, COM_QUERY, q, length, 1); +} + + +/* enable/disable parsing of all queries to decide + if they go on master or slave */ +void STDCALL mysql_enable_rpl_parse(MYSQL* mysql) +{ + mysql->options.rpl_parse = 1; +} + +void STDCALL mysql_disable_rpl_parse(MYSQL* mysql) +{ + mysql->options.rpl_parse = 0; +} + +/* get the value of the parse flag */ +int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql) +{ + return mysql->options.rpl_parse; +} + +/* enable/disable reads from master */ +void STDCALL mysql_enable_reads_from_master(MYSQL* mysql) +{ + mysql->options.no_master_reads = 0; +} + +void STDCALL mysql_disable_reads_from_master(MYSQL* mysql) +{ + mysql->options.no_master_reads = 1; +} + +/* get the value of the master read flag */ +int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql) +{ + return !(mysql->options.no_master_reads); +} + +/* We may get an error while doing replication internals. + In this case, we add a special explanation to the original + error +*/ +static inline void expand_error(MYSQL* mysql, int error) +{ + char tmp[MYSQL_ERRMSG_SIZE]; + char* p, *tmp_end; + tmp_end = strnmov(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE); + p = strnmov(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE); + memcpy(p, tmp, tmp_end - tmp); + mysql->net.last_errno = error; +} + +/* This function assumes we have just called SHOW SLAVE STATUS and have + read the given result and row +*/ +static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row) +{ + MYSQL* master; + if(mysql_num_fields(res) < 3) + return 1; /* safety */ + + /* use the same username and password as the original connection */ + if(!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0))) + return 1; + mysql->master = master; + return 0; +} + +/* assuming we already know that mysql points to a master connection, + retrieve all the slaves +*/ +static inline int get_slaves_from_master(MYSQL* mysql) +{ + MYSQL_RES* res = 0; + MYSQL_ROW row; + int error = 1; + int has_auth_info; + if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0)) + { + expand_error(mysql, CR_PROBE_MASTER_CONNECT); + return 1; + } + + if (mysql_query(mysql, "SHOW SLAVE HOSTS") || + !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_HOSTS); + return 1; + } + + switch (mysql_num_fields(res)) + { + case 3: has_auth_info = 0; break; + case 5: has_auth_info = 1; break; + default: + goto err; + } + + while ((row = mysql_fetch_row(res))) + { + MYSQL* slave; + const char* tmp_user, *tmp_pass; + + if (has_auth_info) + { + tmp_user = row[3]; + tmp_pass = row[4]; + } + else + { + tmp_user = mysql->user; + tmp_pass = mysql->passwd; + } + + if(!(slave = spawn_init(mysql, row[1], atoi(row[2]), + tmp_user, tmp_pass))) + goto err; + + /* Now add slave into the circular linked list */ + slave->next_slave = mysql->next_slave; + mysql->next_slave = slave; + } + error = 0; +err: + if(res) + mysql_free_result(res); + return error; +} + +int STDCALL mysql_rpl_probe(MYSQL* mysql) +{ + MYSQL_RES* res = 0; + MYSQL_ROW row; + int error = 1; + /* first determine the replication role of the server we connected to + the most reliable way to do this is to run SHOW SLAVE STATUS and see + if we have a non-empty master host. This is still not fool-proof - + it is not a sin to have a master that has a dormant slave thread with + a non-empty master host. However, it is more reliable to check + for empty master than whether the slave thread is actually running + */ + if (mysql_query(mysql, "SHOW SLAVE STATUS") || + !(res = mysql_store_result(mysql))) + { + expand_error(mysql, CR_PROBE_SLAVE_STATUS); + return 1; + } + + if (!(row = mysql_fetch_row(res))) + goto err; + + /* check master host for emptiness/NULL */ + if (row[0] && *(row[0])) + { + /* this is a slave, ask it for the master */ + if (get_master(mysql, res, row) || get_slaves_from_master(mysql)) + goto err; + } + else + { + mysql->master = mysql; + if (get_slaves_from_master(mysql)) + goto err; + } + + error = 0; +err: + if(res) + mysql_free_result(res); + return error; +} + + +/* make a not so fool-proof decision on where the query should go, to + the master or the slave. Ideally the user should always make this + decision himself with mysql_master_query() or mysql_slave_query(). + However, to be able to more easily port the old code, we support the + option of an educated guess - this should work for most applications, + however, it may make the wrong decision in some particular cases. If + that happens, the user would have to change the code to call + mysql_master_query() or mysql_slave_query() explicitly in the place + where we have made the wrong decision +*/ +enum mysql_rpl_type +STDCALL mysql_rpl_query_type(const char* q, int len) +{ + const char* q_end; + q_end = (len) ? q + len : strend(q); + for(; q < q_end; ++q) + { + char c; + if(isalpha(c=*q)) + switch(tolower(c)) + { + case 'i': /* insert */ + case 'u': /* update or unlock tables */ + case 'l': /* lock tables or load data infile */ + case 'd': /* drop or delete */ + case 'a': /* alter */ + return MYSQL_RPL_MASTER; + case 'c': /* create or check */ + return tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_MASTER ; + case 's': /* select or show */ + return tolower(q[1] == 'h') ? MYSQL_RPL_ADMIN : MYSQL_RPL_SLAVE; + case 'f': /* flush */ + case 'r': /* repair */ + case 'g': /* grant */ + return MYSQL_RPL_ADMIN; + default: + return MYSQL_RPL_SLAVE; + } + } + + return 0; +} + + /**************************************************************************** ** Init MySQL structure or allocate one ****************************************************************************/ @@ -1005,6 +1288,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) if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) (void) signal(SIGPIPE,pipe_sig_handler); @@ -1070,13 +1359,15 @@ mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, 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.use_ssl = TRUE; + mysql->connector_fd = (gptr)new_VioSSLConnectorFd(key, cert, ca, capath); + DBUG_PRINT("info",("mysql_ssl_set, context: %p",((struct st_VioSSLConnectorFd *)(mysql->connector_fd))->ssl_context_)); + return 0; } /************************************************************************** -**************************************************************************/ +************************************************************************** char * STDCALL mysql_ssl_cipher(MYSQL *mysql) @@ -1085,10 +1376,10 @@ mysql_ssl_cipher(MYSQL *mysql) } -/************************************************************************** +************************************************************************** ** 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) @@ -1105,7 +1396,7 @@ mysql_ssl_clear(MYSQL *mysql) mysql->connector_fd->delete(); mysql->connector_fd = 0; return 0; -} +}*/ #endif /* HAVE_OPENSSL */ /************************************************************************** @@ -1496,11 +1787,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_)); + mysql->net.vio = sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd),mysql->net.vio); + DBUG_PRINT("info", ("IO layer change done!")); } #endif /* HAVE_OPENSSL */ @@ -1542,6 +1831,9 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, mysql->reconnect=reconnect; } + if (mysql->options.rpl_probe && mysql_rpl_probe(mysql)) + goto error; + DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); reset_sigpipe(mysql); DBUG_RETURN(mysql); @@ -1680,9 +1972,24 @@ mysql_close(MYSQL *mysql) bzero((char*) &mysql->options,sizeof(mysql->options)); mysql->net.vio = 0; #ifdef HAVE_OPENSSL - ((VioConnectorFd*)(mysql->connector_fd))->delete(); - mysql->connector_fd = 0; +/* ((VioConnectorFd*)(mysql->connector_fd))->delete(); + mysql->connector_fd = 0;*/ #endif /* HAVE_OPENSSL */ + + /* free/close slave list */ + if (mysql->rpl_pivot) + { + MYSQL* tmp; + for (tmp = mysql->next_slave; tmp != mysql; ) + { + /* trick to avoid following freed pointer */ + MYSQL* tmp1 = tmp->next_slave; + mysql_close(tmp); + tmp = tmp1; + } + } + if(mysql != mysql->master) + mysql_close(mysql->master); if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } @@ -1701,6 +2008,67 @@ mysql_query(MYSQL *mysql, const char *query) return mysql_real_query(mysql,query, (uint) strlen(query)); } +static MYSQL* spawn_init(MYSQL* parent, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + MYSQL* child; + if (!(child = mysql_init(0))) + return 0; + + child->options.user = my_strdup((user) ? user : + (parent->user ? parent->user : + parent->options.user), MYF(0)); + child->options.password = my_strdup((passwd) ? passwd : (parent->passwd ? + parent->passwd : + parent->options.password), MYF(0)); + child->options.port = port; + child->options.host = my_strdup((host) ? host : (parent->host ? + parent->host : + parent->options.host), MYF(0)); + if(parent->db) + child->options.db = my_strdup(parent->db, MYF(0)); + else if(parent->options.db) + child->options.db = my_strdup(parent->options.db, MYF(0)); + + child->options.rpl_parse = child->options.rpl_probe = child->rpl_pivot = 0; + + return child; +} + + +int +STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + if (mysql->master != mysql && !mysql->master->rpl_pivot) + mysql_close(mysql->master); + if(!(mysql->master = spawn_init(mysql, host, port, user, passwd))) + return 1; + mysql->master->rpl_pivot = 0; + mysql->master->options.rpl_parse = 0; + mysql->master->options.rpl_probe = 0; + return 0; +} + +int +STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd) +{ + MYSQL* slave; + if(!(slave = spawn_init(mysql, host, port, user, passwd))) + return 1; + slave->next_slave = mysql->next_slave; + mysql->next_slave = slave; + return 0; +} + + /* Send the query and return so we can do something else. Needs to be followed by mysql_read_query_result() when we want to @@ -1710,6 +2078,20 @@ mysql_query(MYSQL *mysql, const char *query) int STDCALL mysql_send_query(MYSQL* mysql, const char* query, uint length) { + if (mysql->options.rpl_parse && mysql->rpl_pivot) + { + switch (mysql_rpl_query_type(query, length)) + { + case MYSQL_RPL_MASTER: + return mysql_master_send_query(mysql, query, length); + case MYSQL_RPL_SLAVE: + return mysql_slave_send_query(mysql, query, length); + case MYSQL_RPL_ADMIN: /*fall through */ + } + } + + mysql->last_used_con = mysql; + return simple_command(mysql, COM_QUERY, query, length, 1); } @@ -1721,6 +2103,11 @@ int STDCALL mysql_read_query_result(MYSQL *mysql) uint length; DBUG_ENTER("mysql_read_query_result"); + /* read from the connection which we actually used, which + could differ from the original connection if we have slaves + */ + mysql = mysql->last_used_con; + if ((length = net_safe_read(mysql)) == packet_error) DBUG_RETURN(-1); free_old_query(mysql); /* Free old result */ @@ -1767,7 +2154,8 @@ mysql_real_query(MYSQL *mysql, const char *query, uint length) DBUG_ENTER("mysql_real_query"); DBUG_PRINT("enter",("handle: %lx",mysql)); DBUG_PRINT("query",("Query = \"%s\"",query)); - if (simple_command(mysql,COM_QUERY,query,length,1)) + + if (mysql_send_query(mysql,query,length)) DBUG_RETURN(-1); DBUG_RETURN(mysql_read_query_result(mysql)); } @@ -1839,6 +2227,9 @@ mysql_store_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("mysql_store_result"); + /* read from the actually used connection */ + mysql = mysql->last_used_con; + if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -1891,6 +2282,8 @@ mysql_use_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("mysql_use_result"); + mysql = mysql->last_used_con; + if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -2344,32 +2737,32 @@ uint STDCALL mysql_field_tell(MYSQL_RES *res) unsigned int STDCALL mysql_field_count(MYSQL *mysql) { - return mysql->field_count; + return mysql->last_used_con->field_count; } my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) { - return (mysql)->affected_rows; + return mysql->last_used_con->affected_rows; } my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) { - return (mysql)->insert_id; + return mysql->last_used_con->insert_id; } uint STDCALL mysql_errno(MYSQL *mysql) { - return (mysql)->net.last_errno; + return mysql->net.last_errno; } char * STDCALL mysql_error(MYSQL *mysql) { - return (mysql)->net.last_error; + return mysql->net.last_error; } char *STDCALL mysql_info(MYSQL *mysql) { - return (mysql)->info; + return mysql->info; } ulong STDCALL mysql_thread_id(MYSQL *mysql) diff --git a/libmysql/net.c b/libmysql/net.c index 11497cc7077..0fc5cf8b50c 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,10 +22,16 @@ ** 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 <mysql_com.h> #include <violite.h> #include <my_sys.h> #include <m_string.h> @@ -34,13 +40,21 @@ #include <signal.h> #include <errno.h> #include <sys/types.h> +#include <assert.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 @@ -73,7 +87,7 @@ typedef my_bool ALARM; #define thr_alarm_in_use(A) (*(A)) #define thr_end_alarm(A) #define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) -static inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) +inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) { *A=1; return 0; @@ -83,28 +97,25 @@ static inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM #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; #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(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; @@ -151,8 +162,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, pkt_length + + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) { net->error=1; #ifdef MYSQL_SERVER @@ -208,18 +223,34 @@ 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]= (net->compress) ? 0 : (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++); if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE)) @@ -227,23 +258,54 @@ my_net_write(NET *net,const char *packet,ulong len) 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) { - uchar buff[NET_HEADER_SIZE+1]; uint length=len+1; /* 1 extra byte for command */ + uchar buff[NET_HEADER_SIZE+1]; + 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]= (net->compress) ? 0 : (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)); + 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); @@ -262,7 +324,11 @@ 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) @@ -270,7 +336,7 @@ net_real_write(NET *net,const char *packet,ulong len) int length; char *pos,*end; thr_alarm_t alarmed; -#if !defined(__WIN__) && !defined(__EMX__) +#if !defined(__WIN__) ALARM alarm_buff; #endif uint retry_count=0; @@ -426,19 +492,25 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) { my_bool interrupted = vio_should_retry(net->vio); if (!thr_got_alarm(&alarmed) && interrupted) - { /* Probably in MIT threads */ + { /* Probably in MIT threads */ if (retry_count++ < RETRY_COUNT) continue; } return; } - remain -=(ulong) length; - statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + remain -= length; + statistic_add(bytes_received,length,&LOCK_bytes_received); } } #endif /* MYSQL_SERVER */ +/* + 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 uint my_real_read(NET *net, ulong *complen) { @@ -575,12 +647,13 @@ 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) @@ -605,7 +678,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; @@ -614,65 +701,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 */ + int 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= buf_length - start_of_packet; + len = ((uint) (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/violite.c b/libmysql/violite.c deleted file mode 100644 index 25ab5db31d2..00000000000 --- a/libmysql/violite.c +++ /dev/null @@ -1,425 +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 - -#if defined(__EMX__) -#include <sys/ioctl.h> -#define ioctlsocket ioctl -#endif /* defined(__EMX__) */ - -#if defined(MSDOS) || defined(__WIN__) -#ifdef __WIN__ -#undef errno -#undef EINTR -#undef EAGAIN -#define errno WSAGetLastError() -#define EINTR WSAEINTR -#define EAGAIN WSAEINPROGRESS -#endif /* __WIN__ */ -#define O_NONBLOCK 1 /* For emulation of fcntl() */ -#endif -#ifndef EWOULDBLOCK -#define EWOULDBLOCK 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__) -#if !defined(NO_FCNTL_NONBLOCK) - vio->fcntl_mode = fcntl(sd, F_GETFL); -#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 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, buf=%p, size=%d", vio->sd, buf, size)); -#ifdef __WIN__ - if (vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; - if (!ReadFile(vio->hPipe, buf, size, &length, NULL)) - DBUG_RETURN(-1); - 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("error", ("Got error %d during read",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, buf=%p, size=%d", vio->sd, buf, size)); -#ifdef __WIN__ - if ( vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; - if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL)) - DBUG_RETURN(-1); - 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("error", ("Got error on write: %d",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__) -#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 = errno; - return en == EAGAIN || en == EINTR || en == 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("error", ("close() failed, error: %d",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", 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 */ |