diff options
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/Makefile.am | 18 | ||||
-rw-r--r-- | sql-common/client.c | 1331 | ||||
-rw-r--r-- | sql-common/client_plugin.c | 115 | ||||
-rw-r--r-- | sql-common/my_time.c | 275 | ||||
-rw-r--r-- | sql-common/my_user.c | 5 | ||||
-rw-r--r-- | sql-common/mysql_async.c | 45 |
6 files changed, 1186 insertions, 603 deletions
diff --git a/sql-common/Makefile.am b/sql-common/Makefile.am deleted file mode 100644 index 379cff832ce..00000000000 --- a/sql-common/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2003-2004, 2006 MySQL AB -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -## Process this file with automake to create Makefile.in -EXTRA_DIST = client.c pack.c my_time.c my_user.c client_plugin.c \ - mysql_async.c diff --git a/sql-common/client.c b/sql-common/client.c index b047d177830..2904ffa7b64 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* + Copyright (c) 2003, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +25,6 @@ mysql_real_connect() - Support for reading local file with LOAD DATA LOCAL - SHARED memory handling - - Protection against sigpipe - Prepared statements - Things that only works for the server @@ -38,6 +38,10 @@ #include "mysql.h" +#ifndef __WIN__ +#include <netdb.h> +#endif + /* Remove client convenience wrappers */ #undef max_allowed_packet #undef net_buffer_length @@ -66,9 +70,9 @@ my_bool net_flush(NET *net); #include "mysqld_error.h" #include "errmsg.h" #include <violite.h> -#if defined(THREAD) && !defined(__WIN__) +#if !defined(__WIN__) #include <my_pthread.h> /* because of signal() */ -#endif /* defined(THREAD) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ #include <sys/stat.h> #include <signal.h> @@ -76,7 +80,7 @@ my_bool net_flush(NET *net); #ifdef HAVE_PWD_H #include <pwd.h> #endif -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -87,12 +91,12 @@ my_bool net_flush(NET *net); #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#endif /*!defined(MSDOS) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #define perror(A) #else #include <errno.h> @@ -127,8 +131,9 @@ const char *def_shared_memory_base_name= default_shared_memory_base_name; static void mysql_close_free_options(MYSQL *mysql); static void mysql_close_free(MYSQL *mysql); static void mysql_prune_stmt_list(MYSQL *mysql); +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); -#if !(defined(__WIN__) || defined(__NETWARE__)) +#if !defined(__WIN__) static int wait_for_data(my_socket fd, uint timeout); #endif @@ -150,10 +155,13 @@ char mysql_server_last_error[MYSQL_ERRMSG_SIZE]; int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, uint timeout) { -#if defined(__WIN__) || defined(__NETWARE__) - return connect(fd, (struct sockaddr*) name, namelen); +#if defined(__WIN__) + DBUG_ENTER("my_connect"); + DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen)); #else int flags, res, s_err; + DBUG_ENTER("my_connect"); + DBUG_PRINT("enter", ("fd: %d timeout: %u", fd, timeout)); /* If they passed us a timeout of zero, we should behave @@ -161,24 +169,26 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, */ if (timeout == 0) - return connect(fd, (struct sockaddr*) name, namelen); + DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen)); flags = fcntl(fd, F_GETFL, 0); /* Set socket to not block */ #ifdef O_NONBLOCK fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */ #endif + DBUG_PRINT("info", ("connecting non-blocking")); res= connect(fd, (struct sockaddr*) name, namelen); + DBUG_PRINT("info", ("connect result: %d errno: %d", res, errno)); s_err= errno; /* Save the error... */ fcntl(fd, F_SETFL, flags); if ((res != 0) && (s_err != EINPROGRESS)) { errno= s_err; /* Restore it */ - return(-1); + DBUG_RETURN(-1); } if (res == 0) /* Connected quickly! */ - return(0); - return wait_for_data(fd, timeout); + DBUG_RETURN(0); + DBUG_RETURN(wait_for_data(fd, timeout)); #endif } @@ -190,33 +200,65 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, If not, we will use select() */ -#if !(defined(__WIN__) || defined(__NETWARE__)) +#if !defined(__WIN__) static int wait_for_data(my_socket fd, uint timeout) { #ifdef HAVE_POLL struct pollfd ufds; int res; + DBUG_ENTER("wait_for_data"); + DBUG_PRINT("info", ("polling")); ufds.fd= fd; ufds.events= POLLIN | POLLPRI; if (!(res= poll(&ufds, 1, (int) timeout*1000))) { + DBUG_PRINT("info", ("poll timed out")); errno= EINTR; - return -1; + DBUG_RETURN(-1); } + DBUG_PRINT("info", + ("poll result: %d errno: %d revents: 0x%02d events: 0x%02d", + res, errno, ufds.revents, ufds.events)); if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI))) - return -1; - return 0; + DBUG_RETURN(-1); + /* + At this point, we know that something happened on the socket. + But this does not means that everything is alright. + The connect might have failed. We need to retrieve the error code + from the socket layer. We must return success only if we are sure + that it was really a success. Otherwise we might prevent the caller + from trying another address to connect to. + */ + { + int s_err; + socklen_t s_len= sizeof(s_err); + + DBUG_PRINT("info", ("Get SO_ERROR from non-blocked connected socket.")); + res= getsockopt(fd, SOL_SOCKET, SO_ERROR, &s_err, &s_len); + DBUG_PRINT("info", ("getsockopt res: %d s_err: %d", res, s_err)); + if (res) + DBUG_RETURN(res); + /* getsockopt() was successful, check the retrieved status value. */ + if (s_err) + { + errno= s_err; + DBUG_RETURN(-1); + } + /* Status from connect() is zero. Socket is successfully connected. */ + } + DBUG_RETURN(0); #else SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint); fd_set sfds; struct timeval tv; time_t start_time, now_time; int res, s_err; + DBUG_ENTER("wait_for_data"); if (fd >= FD_SETSIZE) /* Check if wrong error */ - return 0; /* Can't use timeout */ + DBUG_RETURN(0); /* Can't use timeout */ /* Our connection is "in progress." We can use the select() call to wait @@ -248,7 +290,7 @@ static int wait_for_data(my_socket fd, uint timeout) { tv.tv_sec = (long) timeout; tv.tv_usec = 0; -#if defined(HPUX10) && defined(THREAD) +#if defined(HPUX10) if ((res = select(fd+1, NULL, (int*) &sfds, NULL, &tv)) > 0) break; #else @@ -256,11 +298,11 @@ static int wait_for_data(my_socket fd, uint timeout) break; #endif if (res == 0) /* timeout */ - return -1; + DBUG_RETURN(-1); now_time= my_time(0); timeout-= (uint) (now_time - start_time); if (errno != EINTR || (int) timeout <= 0) - return -1; + DBUG_RETURN(-1); } /* @@ -271,17 +313,17 @@ static int wait_for_data(my_socket fd, uint timeout) s_err=0; if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) - return(-1); + DBUG_RETURN(-1); if (s_err) { /* getsockopt could succeed */ errno = s_err; - return(-1); /* but return an error... */ + DBUG_RETURN(-1); /* but return an error... */ } - return (0); /* ok */ + DBUG_RETURN(0); /* ok */ #endif /* HAVE_POLL */ } -#endif /* defined(__WIN__) || defined(__NETWARE__) */ +#endif /* !defined(__WIN__) */ /** Set the internal error message to mysql handler @@ -653,8 +695,7 @@ err2: CloseHandle(handle_file_map); } err: - if (tmp) - my_free(tmp, MYF(0)); + my_free(tmp); if (error_allow) error_code = GetLastError(); if (event_connect_request) @@ -679,23 +720,24 @@ err: } #endif -/***************************************************************************** +/** Read a packet from server. Give error message if socket was down or packet is an error message -*****************************************************************************/ + + @retval packet_error An error occurred during reading. + Error message is set. + @retval +*/ ulong cli_safe_read(MYSQL *mysql) { NET *net= &mysql->net; ulong len=0; - init_sigpipe_variables - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); +restart: if (net->vio != 0) len=my_net_read(net); - reset_sigpipe(mysql); if (len == packet_error || len == 0) { @@ -714,13 +756,27 @@ cli_safe_read(MYSQL *mysql) { if (len > 3) { - char *pos=(char*) net->read_pos+1; - net->last_errno=uint2korr(pos); + uchar *pos= net->read_pos+1; + uint last_errno=uint2korr(pos); + + if (last_errno == 65535 && + (mysql->server_capabilities & CLIENT_PROGRESS)) + { + if (cli_report_progress(mysql, pos+2, (uint) (len-3))) + { + /* Wrong packet */ + set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate); + return (packet_error); + } + goto restart; + } + net->last_errno= last_errno; + pos+=2; len-=2; - if (protocol_41(mysql) && pos[0] == '#') + if (protocol_41(mysql) && (char) pos[0] == '#') { - strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH); + strmake(net->sqlstate, (char*) pos+1, SQLSTATE_LENGTH); pos+= SQLSTATE_LENGTH+1; } else @@ -763,7 +819,7 @@ void free_rows(MYSQL_DATA *cur) if (cur) { free_root(&cur->alloc,MYF(0)); - my_free((uchar*) cur,MYF(0)); + my_free(cur); } } @@ -775,13 +831,9 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, { NET *net= &mysql->net; my_bool result= 1; - init_sigpipe_variables my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_INIT_DONE : FALSE; DBUG_ENTER("cli_advanced_command"); - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); - if (mysql->net.vio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql) || stmt_skip) @@ -830,7 +882,6 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ? 1 : 0); end: - reset_sigpipe(mysql); DBUG_PRINT("exit",("result: %d", result)); DBUG_RETURN(result); } @@ -848,93 +899,177 @@ void free_old_query(MYSQL *mysql) DBUG_VOID_RETURN; } -/* - Flush result set sent from server + +/** + Finish reading of a partial result set from the server. + Get the EOF packet, and update mysql->status + and mysql->warning_count. + + @return TRUE if a communication or protocol error, an error + is set in this case, FALSE otherwise. */ -static void cli_flush_use_result(MYSQL *mysql) +my_bool flush_one_result(MYSQL *mysql) { - /* Clear the current execution status */ - DBUG_ENTER("cli_flush_use_result"); - DBUG_PRINT("warning",("Not all packets read, clearing them")); - for (;;) + ulong packet_length; + + DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY); + + do { - ulong pkt_len; - if ((pkt_len=cli_safe_read(mysql)) == packet_error) - break; - if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) - { - if (protocol_41(mysql)) - { - char *pos= (char*) mysql->net.read_pos + 1; - mysql->warning_count=uint2korr(pos); pos+=2; - mysql->server_status=uint2korr(pos); pos+=2; - } - break; /* End of data */ - } + packet_length= cli_safe_read(mysql); + /* + There is an error reading from the connection, + or (sic!) there were no error and no + data in the stream, i.e. no more data from the server. + Since we know our position in the stream (somewhere in + the middle of a result set), this latter case is an error too + -- each result set must end with a EOF packet. + cli_safe_read() has set an error for us, just return. + */ + if (packet_length == packet_error) + return TRUE; } - DBUG_VOID_RETURN; -} + while (packet_length > 8 || mysql->net.read_pos[0] != 254); + /* Analyze EOF packet of the result set. */ -#ifdef __WIN__ -static my_bool is_NT(void) -{ - char *os=getenv("OS"); - return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; + if (protocol_41(mysql)) + { + char *pos= (char*) mysql->net.read_pos + 1; + mysql->warning_count=uint2korr(pos); + pos+=2; + mysql->server_status=uint2korr(pos); + pos+=2; + } + return FALSE; } -#endif -#ifdef CHECK_LICENSE /** - Check server side variable 'license'. - - If the variable does not exist or does not contain 'Commercial', - we're talking to non-commercial server from commercial client. + Read a packet from network. If it's an OK packet, flush it. - @retval 0 success - @retval !0 network error or the server is not commercial. - Error code is saved in mysql->net.last_errno. + @return TRUE if error, FALSE otherwise. In case of + success, is_ok_packet is set to TRUE or FALSE, + based on what we got from network. */ -static int check_license(MYSQL *mysql) +my_bool opt_flush_ok_packet(MYSQL *mysql, my_bool *is_ok_packet) { - MYSQL_ROW row; - MYSQL_RES *res; - NET *net= &mysql->net; - static const char query[]= "SELECT @@license"; - static const char required_license[]= STRINGIFY_ARG(LICENSE); + ulong packet_length= cli_safe_read(mysql); - if (mysql_real_query(mysql, query, sizeof(query)-1)) + if (packet_length == packet_error) + return TRUE; + + /* cli_safe_read always reads a non-empty packet. */ + DBUG_ASSERT(packet_length); + + *is_ok_packet= mysql->net.read_pos[0] == 0; + if (*is_ok_packet) { - if (net->last_errno == ER_UNKNOWN_SYSTEM_VARIABLE) + uchar *pos= mysql->net.read_pos + 1; + + net_field_length_ll(&pos); /* affected rows */ + net_field_length_ll(&pos); /* insert id */ + + mysql->server_status=uint2korr(pos); + pos+=2; + + if (protocol_41(mysql)) { - set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate, - ER(CR_WRONG_LICENSE), required_license); + mysql->warning_count=uint2korr(pos); + pos+=2; } - return 1; } - if (!(res= mysql_use_result(mysql))) - return 1; - row= mysql_fetch_row(res); - /* - If no rows in result set, or column value is NULL (none of these - two is ever true for server variables now), or column value - mismatch, set wrong license error. - */ - if (!net->last_errno && - (!row || !row[0] || - strncmp(row[0], required_license, sizeof(required_license)))) + return FALSE; +} + + +/* + Flush result set sent from server +*/ + +static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results) +{ + /* Clear the current execution status */ + DBUG_ENTER("cli_flush_use_result"); + DBUG_PRINT("warning",("Not all packets read, clearing them")); + + if (flush_one_result(mysql)) + DBUG_VOID_RETURN; /* An error occurred */ + + if (! flush_all_results) + DBUG_VOID_RETURN; + + while (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { - set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate, - ER(CR_WRONG_LICENSE), required_license); + my_bool is_ok_packet; + if (opt_flush_ok_packet(mysql, &is_ok_packet)) + DBUG_VOID_RETURN; /* An error occurred. */ + if (is_ok_packet) + { + /* + Indeed what we got from network was an OK packet, and we + know that OK is the last one in a multi-result-set, so + just return. + */ + DBUG_VOID_RETURN; + } + /* + It's a result set, not an OK packet. A result set contains + of two result set subsequences: field metadata, terminated + with EOF packet, and result set data, again terminated with + EOF packet. Read and flush them. + */ + if (flush_one_result(mysql) || flush_one_result(mysql)) + DBUG_VOID_RETURN; /* An error occurred. */ } - mysql_free_result(res); - return net->last_errno; + + DBUG_VOID_RETURN; +} + + +/* + Report progress to the client + + RETURN VALUES + 0 ok + 1 error +*/ + +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length) +{ + uint stage, max_stage, proc_length; + double progress; + uchar *start= packet; + + if (length < 5) + return 1; /* Wrong packet */ + + if (!(mysql->options.extension && mysql->options.extension->report_progress)) + return 0; /* No callback, ignore packet */ + + packet++; /* Ignore number of strings */ + stage= (uint) *packet++; + max_stage= (uint) *packet++; + progress= uint3korr(packet)/1000.0; + packet+= 3; + proc_length= net_field_length(&packet); + if (packet + proc_length > start + length) + return 1; /* Wrong packet */ + (*mysql->options.extension->report_progress)(mysql, stage, max_stage, + progress, (char*) packet, + proc_length); + return 0; } -#endif /* CHECK_LICENSE */ +#ifdef __WIN__ +static my_bool is_NT(void) +{ + char *os=getenv("OS"); + return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; +} +#endif /************************************************************************** Shut down connection @@ -946,14 +1081,11 @@ void end_server(MYSQL *mysql) DBUG_ENTER("end_server"); if (mysql->net.vio != 0) { - init_sigpipe_variables DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio))); #ifdef MYSQL_SERVER slave_io_thread_detach_vio(); #endif - set_sigpipe(mysql); vio_delete(mysql->net.vio); - reset_sigpipe(mysql); mysql->net.vio= 0; /* Marker */ mysql_prune_stmt_list(mysql); } @@ -978,7 +1110,7 @@ mysql_free_result(MYSQL_RES *result) mysql->unbuffered_fetch_owner= 0; if (mysql->status == MYSQL_STATUS_USE_RESULT) { - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, FALSE); mysql->status=MYSQL_STATUS_READY; if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; @@ -987,9 +1119,8 @@ mysql_free_result(MYSQL_RES *result) free_rows(result->data); if (result->fields) free_root(&result->field_alloc,MYF(0)); - if (result->row) - my_free((uchar*) result->row,MYF(0)); - my_free((uchar*) result,MYF(0)); + my_free(result->row); + my_free(result); } DBUG_VOID_RETURN; } @@ -1005,7 +1136,6 @@ static const char *default_options[]= "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", "character-sets-dir", "default-character-set", "interactive-timeout", "connect-timeout", "local-infile", "disable-local-infile", - "replication-probe", "enable-reads-from-master", "repl-parse-query", "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", "report-data-truncation", "plugin-dir", "default-auth", @@ -1017,10 +1147,10 @@ enum option_id { OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath, OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout, OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile, - OPT_replication_probe, OPT_enable_reads_from_master, OPT_repl_parse_query, OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, + OPT_keep_this_one_last }; static TYPELIB option_types={array_elements(default_options)-1, @@ -1039,30 +1169,30 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd) { options->init_commands= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY), MYF(MY_WME)); - init_dynamic_array(options->init_commands,sizeof(char*),5,5 CALLER_INFO); + init_dynamic_array(options->init_commands,sizeof(char*),5,5); } if (!(tmp= my_strdup(cmd,MYF(MY_WME))) || insert_dynamic(options->init_commands, (uchar*)&tmp)) { - my_free(tmp, MYF(MY_ALLOW_ZERO_PTR)); + my_free(tmp); return 1; } return 0; } -#define extension_set(OPTS, X, VAL) \ +#define EXTENSION_SET(OPTS, X, VAL) \ if (!(OPTS)->extension) \ (OPTS)->extension= (struct st_mysql_options_extention *) \ my_malloc(sizeof(struct st_mysql_options_extention), \ MYF(MY_WME | MY_ZEROFILL)); \ (OPTS)->extension->X= VAL; -#define extension_set_string(OPTS, X, STR) \ +#define EXTENSION_SET_STRING(OPTS, X, STR) \ if ((OPTS)->extension) \ - my_free((OPTS)->extension->X, MYF(MY_ALLOW_ZERO_PTR)); \ - extension_set(OPTS, X, my_strdup((STR), MYF(MY_WME))); + my_free((OPTS)->extension->X); \ + EXTENSION_SET(OPTS, X, my_strdup((STR), MYF(MY_WME))); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group) @@ -1073,6 +1203,9 @@ void mysql_read_default_options(struct st_mysql_options *options, DBUG_ENTER("mysql_read_default_options"); DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); + compile_time_assert(OPT_keep_this_one_last == + array_elements(default_options)); + argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; groups[0]= (char*) "client"; groups[1]= (char*) "client-server"; @@ -1086,6 +1219,8 @@ void mysql_read_default_options(struct st_mysql_options *options, char **option=argv; while (*++option) { + if (my_getopt_is_args_separator(option[0])) /* skip arguments separator */ + continue; /* DBUG_PRINT("info",("option: %s",option[0])); */ if (option[0][0] == '-' && option[0][1] == '-') { @@ -1099,7 +1234,7 @@ void mysql_read_default_options(struct st_mysql_options *options, /* Change all '_' in variable name to '-' */ for (end= *option ; *(end= strcend(end,'_')) ; ) *end= '-'; - switch (find_type(*option+2,&option_types,2)) { + switch (find_type(*option + 2, &option_types, FIND_TYPE_BASIC)) { case OPT_port: if (opt_arg) options->port=atoi(opt_arg); @@ -1107,7 +1242,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_socket: if (opt_arg) { - my_free(options->unix_socket,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->unix_socket); options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1115,10 +1250,10 @@ void mysql_read_default_options(struct st_mysql_options *options, options->compress=1; options->client_flag|= CLIENT_COMPRESS; break; - case OPT_password: + case OPT_password: if (opt_arg) { - my_free(options->password,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->password); options->password=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1132,7 +1267,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_user: if (opt_arg) { - my_free(options->user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->user); options->user=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1142,14 +1277,14 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_host: if (opt_arg) { - my_free(options->host,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->host); options->host=my_strdup(opt_arg,MYF(MY_WME)); } break; case OPT_database: if (opt_arg) { - my_free(options->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->db); options->db=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1161,25 +1296,25 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_return_found_rows: options->client_flag|=CLIENT_FOUND_ROWS; break; -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) case OPT_ssl_key: - my_free(options->ssl_key, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_key); options->ssl_key = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_cert: - my_free(options->ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_cert); options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_ca: - my_free(options->ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_ca); options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_capath: - my_free(options->ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_capath); options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_cipher: - my_free(options->ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_cipher); options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME)); break; #else @@ -1189,13 +1324,13 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_ssl_capath: case OPT_ssl_cipher: break; -#endif /* HAVE_OPENSSL */ - case OPT_character_sets_dir: - my_free(options->charset_dir,MYF(MY_ALLOW_ZERO_PTR)); +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ + case OPT_character_sets_dir: + my_free(options->charset_dir); options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_default_character_set: - my_free(options->charset_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->charset_name); options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_interactive_timeout: @@ -1210,26 +1345,13 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_disable_local_infile: options->client_flag&= ~CLIENT_LOCAL_FILES; break; - case OPT_replication_probe: -#ifndef TO_BE_DELETED - options->rpl_probe= 1; -#endif - break; - case OPT_enable_reads_from_master: - options->no_master_reads= 0; - break; - case OPT_repl_parse_query: -#ifndef TO_BE_DELETED - options->rpl_parse= 1; -#endif - break; case OPT_max_allowed_packet: if (opt_arg) options->max_allowed_packet= atoi(opt_arg); break; case OPT_protocol: - if ((options->protocol= find_type(opt_arg, - &sql_protocol_typelib,0)) <= 0) + if ((options->protocol= find_type(opt_arg, &sql_protocol_typelib, + FIND_TYPE_BASIC)) <= 0) { fprintf(stderr, "Unknown option to protocol: %s\n", opt_arg); exit(1); @@ -1238,7 +1360,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_shared_memory_base_name: #ifdef HAVE_SMEM if (options->shared_memory_base_name != def_shared_memory_base_name) - my_free(options->shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->shared_memory_base_name); options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME)); #endif break; @@ -1256,10 +1378,22 @@ void mysql_read_default_options(struct st_mysql_options *options, options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; break; case OPT_plugin_dir: - extension_set_string(options, plugin_dir, opt_arg); + { + char buff[FN_REFLEN], buff2[FN_REFLEN]; + if (strlen(opt_arg) >= FN_REFLEN) + opt_arg[FN_REFLEN]= '\0'; + if (my_realpath(buff, opt_arg, 0)) + { + DBUG_PRINT("warning",("failed to normalize the plugin path: %s", + opt_arg)); + break; + } + convert_dirname(buff, buff2, NULL); + EXTENSION_SET_STRING(options, plugin_dir, buff2); + } break; case OPT_default_auth: - extension_set_string(options, default_auth, opt_arg); + EXTENSION_SET_STRING(options, default_auth, opt_arg); break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); @@ -1352,7 +1486,7 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, field->flags= uint2korr(pos+7); field->decimals= (uint) pos[9]; - if (INTERNAL_NUM_FIELD(field)) + if (IS_NUM(field->type)) field->flags|= NUM_FLAG; if (default_value && row->data[7]) { @@ -1393,7 +1527,7 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, field->flags= (uint) (uchar) row->data[4][0]; field->decimals=(uint) (uchar) row->data[4][1]; } - if (INTERNAL_NUM_FIELD(field)) + if (IS_NUM(field->type)) field->flags|= NUM_FLAG; if (default_value && row->data[5]) { @@ -1585,16 +1719,8 @@ 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->charset=default_client_charset_info; strmov(mysql->net.sqlstate, not_error_sqlstate); - /* - By default, we are a replication pivot. The caller must reset it - after we return if this is not the case. - */ -#ifndef TO_BE_DELETED - mysql->rpl_pivot = 1; -#endif /* Only enable LOAD DATA INFILE by default if configured with @@ -1651,17 +1777,17 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , { DBUG_ENTER("mysql_ssl_set"); #ifdef HAVE_OPENSSL - my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_key); + my_free(mysql->options.ssl_cert); + my_free(mysql->options.ssl_ca); + my_free(mysql->options.ssl_capath); + my_free(mysql->options.ssl_cipher); mysql->options.ssl_key= strdup_if_not_null(key); mysql->options.ssl_cert= strdup_if_not_null(cert); mysql->options.ssl_ca= strdup_if_not_null(ca); mysql->options.ssl_capath= strdup_if_not_null(capath); mysql->options.ssl_cipher= strdup_if_not_null(cipher); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_RETURN(0); } @@ -1671,7 +1797,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , NB! Errors are not reported until you do mysql_real_connect. */ -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) static void mysql_ssl_free(MYSQL *mysql __attribute__((unused))) @@ -1679,14 +1805,14 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; DBUG_ENTER("mysql_ssl_free"); - my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_key); + my_free(mysql->options.ssl_cert); + my_free(mysql->options.ssl_ca); + my_free(mysql->options.ssl_capath); + my_free(mysql->options.ssl_cipher); if (ssl_fd) SSL_CTX_free(ssl_fd->ssl_context); - my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->connector_fd); mysql->options.ssl_key = 0; mysql->options.ssl_cert = 0; mysql->options.ssl_ca = 0; @@ -1697,7 +1823,7 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) DBUG_VOID_RETURN; } -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ /* Return the SSL cipher (if any) used for current @@ -1713,10 +1839,10 @@ const char * STDCALL mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) { DBUG_ENTER("mysql_get_ssl_cipher"); -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (mysql->net.vio && mysql->net.vio->ssl_arg) DBUG_RETURN(SSL_get_cipher_name((SSL*)mysql->net.vio->ssl_arg)); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_RETURN(NULL); } @@ -1729,6 +1855,8 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) ssl_verify_server_cert() vio pointer to a SSL connected vio server_hostname name of the server that we connected to + errptr if we fail, we'll return (a pointer to a string + describing) the reason here RETURN VALUES 0 Success @@ -1736,9 +1864,9 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) */ -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) -static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) +static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const char **errptr) { SSL *ssl; X509 *server_cert; @@ -1749,19 +1877,19 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) if (!(ssl= (SSL*)vio->ssl_arg)) { - DBUG_PRINT("error", ("No SSL pointer found")); + *errptr= "No SSL pointer found"; DBUG_RETURN(1); } if (!server_hostname) { - DBUG_PRINT("error", ("No server hostname supplied")); + *errptr= "No server hostname supplied"; DBUG_RETURN(1); } if (!(server_cert= SSL_get_peer_certificate(ssl))) { - DBUG_PRINT("error", ("Could not get server certificate")); + *errptr= "Could not get server certificate"; DBUG_RETURN(1); } @@ -1790,11 +1918,11 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) DBUG_RETURN(0); } } - DBUG_PRINT("error", ("SSL certificate validation failure")); + *errptr= "SSL certificate validation failure"; DBUG_RETURN(1); } -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ /* @@ -1832,52 +1960,299 @@ static MYSQL_METHODS client_methods= #endif }; + + +typedef enum my_cs_match_type_enum +{ + /* MySQL and OS charsets are fully compatible */ + my_cs_exact, + /* MySQL charset is very close to OS charset */ + my_cs_approx, + /* + MySQL knows this charset, but it is not supported as client character set. + */ + my_cs_unsupp +} my_cs_match_type; + + +typedef struct str2str_st +{ + const char *os_name; + const char *my_name; + my_cs_match_type param; +} MY_CSET_OS_NAME; + +const MY_CSET_OS_NAME charsets[]= +{ +#ifdef __WIN__ + {"cp437", "cp850", my_cs_approx}, + {"cp850", "cp850", my_cs_exact}, + {"cp852", "cp852", my_cs_exact}, + {"cp858", "cp850", my_cs_approx}, + {"cp866", "cp866", my_cs_exact}, + {"cp874", "tis620", my_cs_approx}, + {"cp932", "cp932", my_cs_exact}, + {"cp936", "gbk", my_cs_approx}, + {"cp949", "euckr", my_cs_approx}, + {"cp950", "big5", my_cs_exact}, + {"cp1200", "utf16le", my_cs_unsupp}, + {"cp1201", "utf16", my_cs_unsupp}, + {"cp1250", "cp1250", my_cs_exact}, + {"cp1251", "cp1251", my_cs_exact}, + {"cp1252", "latin1", my_cs_exact}, + {"cp1253", "greek", my_cs_exact}, + {"cp1254", "latin5", my_cs_exact}, + {"cp1255", "hebrew", my_cs_approx}, + {"cp1256", "cp1256", my_cs_exact}, + {"cp1257", "cp1257", my_cs_exact}, + {"cp10000", "macroman", my_cs_exact}, + {"cp10001", "sjis", my_cs_approx}, + {"cp10002", "big5", my_cs_approx}, + {"cp10008", "gb2312", my_cs_approx}, + {"cp10021", "tis620", my_cs_approx}, + {"cp10029", "macce", my_cs_exact}, + {"cp12001", "utf32", my_cs_unsupp}, + {"cp20107", "swe7", my_cs_exact}, + {"cp20127", "latin1", my_cs_approx}, + {"cp20866", "koi8r", my_cs_exact}, + {"cp20932", "ujis", my_cs_exact}, + {"cp20936", "gb2312", my_cs_approx}, + {"cp20949", "euckr", my_cs_approx}, + {"cp21866", "koi8u", my_cs_exact}, + {"cp28591", "latin1", my_cs_approx}, + {"cp28592", "latin2", my_cs_exact}, + {"cp28597", "greek", my_cs_exact}, + {"cp28598", "hebrew", my_cs_exact}, + {"cp28599", "latin5", my_cs_exact}, + {"cp28603", "latin7", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_4579_IS_DONE + {"cp28605", "latin9", my_cs_exact}, +#endif + {"cp38598", "hebrew", my_cs_exact}, + {"cp51932", "ujis", my_cs_exact}, + {"cp51936", "gb2312", my_cs_exact}, + {"cp51949", "euckr", my_cs_exact}, + {"cp51950", "big5", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_WL_4024_IS_DONE + {"cp54936", "gb18030", my_cs_exact}, +#endif + {"cp65001", "utf8", my_cs_exact}, + +#else /* not Windows */ + + {"646", "latin1", my_cs_approx}, /* Default on Solaris */ + {"ANSI_X3.4-1968", "latin1", my_cs_approx}, + {"ansi1251", "cp1251", my_cs_exact}, + {"armscii8", "armscii8", my_cs_exact}, + {"armscii-8", "armscii8", my_cs_exact}, + {"ASCII", "latin1", my_cs_approx}, + {"Big5", "big5", my_cs_exact}, + {"cp1251", "cp1251", my_cs_exact}, + {"cp1255", "hebrew", my_cs_approx}, + {"CP866", "cp866", my_cs_exact}, + {"eucCN", "gb2312", my_cs_exact}, + {"euc-CN", "gb2312", my_cs_exact}, + {"eucJP", "ujis", my_cs_exact}, + {"euc-JP", "ujis", my_cs_exact}, + {"eucKR", "euckr", my_cs_exact}, + {"euc-KR", "euckr", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_WL_4024_IS_DONE + {"gb18030", "gb18030", my_cs_exact}, +#endif + {"gb2312", "gb2312", my_cs_exact}, + {"gbk", "gbk", my_cs_exact}, + {"georgianps", "geostd8", my_cs_exact}, + {"georgian-ps", "geostd8", my_cs_exact}, + {"IBM-1252", "cp1252", my_cs_exact}, + + {"iso88591", "latin1", my_cs_approx}, + {"ISO_8859-1", "latin1", my_cs_approx}, + {"ISO8859-1", "latin1", my_cs_approx}, + {"ISO-8859-1", "latin1", my_cs_approx}, + + {"iso885913", "latin7", my_cs_exact}, + {"ISO_8859-13", "latin7", my_cs_exact}, + {"ISO8859-13", "latin7", my_cs_exact}, + {"ISO-8859-13", "latin7", my_cs_exact}, + +#ifdef UNCOMMENT_THIS_WHEN_WL_4579_IS_DONE + {"iso885915", "latin9", my_cs_exact}, + {"ISO_8859-15", "latin9", my_cs_exact}, + {"ISO8859-15", "latin9", my_cs_exact}, + {"ISO-8859-15", "latin9", my_cs_exact}, +#endif + + {"iso88592", "latin2", my_cs_exact}, + {"ISO_8859-2", "latin2", my_cs_exact}, + {"ISO8859-2", "latin2", my_cs_exact}, + {"ISO-8859-2", "latin2", my_cs_exact}, + + {"iso88597", "greek", my_cs_exact}, + {"ISO_8859-7", "greek", my_cs_exact}, + {"ISO8859-7", "greek", my_cs_exact}, + {"ISO-8859-7", "greek", my_cs_exact}, + + {"iso88598", "hebrew", my_cs_exact}, + {"ISO_8859-8", "hebrew", my_cs_exact}, + {"ISO8859-8", "hebrew", my_cs_exact}, + {"ISO-8859-8", "hebrew", my_cs_exact}, + + {"iso88599", "latin5", my_cs_exact}, + {"ISO_8859-9", "latin5", my_cs_exact}, + {"ISO8859-9", "latin5", my_cs_exact}, + {"ISO-8859-9", "latin5", my_cs_exact}, + + {"koi8r", "koi8r", my_cs_exact}, + {"KOI8-R", "koi8r", my_cs_exact}, + {"koi8u", "koi8u", my_cs_exact}, + {"KOI8-U", "koi8u", my_cs_exact}, + + {"roman8", "hp8", my_cs_exact}, /* Default on HP UX */ + + {"Shift_JIS", "sjis", my_cs_exact}, + {"SJIS", "sjis", my_cs_exact}, + {"shiftjisx0213", "sjis", my_cs_exact}, + + {"tis620", "tis620", my_cs_exact}, + {"tis-620", "tis620", my_cs_exact}, + + {"ujis", "ujis", my_cs_exact}, + + {"US-ASCII", "latin1", my_cs_approx}, + + {"utf8", "utf8", my_cs_exact}, + {"utf-8", "utf8", my_cs_exact}, +#endif + {NULL, NULL, 0} +}; + + +static const char * +my_os_charset_to_mysql_charset(const char *csname) +{ + const MY_CSET_OS_NAME *csp; + for (csp= charsets; csp->os_name; csp++) + { + if (!my_strcasecmp(&my_charset_latin1, csp->os_name, csname)) + { + switch (csp->param) + { + case my_cs_exact: + return csp->my_name; + + case my_cs_approx: + /* + Maybe we should print a warning eventually: + character set correspondence is not exact. + */ + return csp->my_name; + + default: + my_printf_error(ER_UNKNOWN_ERROR, + "OS character set '%s'" + " is not supported by MySQL client", + MYF(0), csp->my_name); + goto def; + } + } + } + + my_printf_error(ER_UNKNOWN_ERROR, + "Unknown OS character set '%s'.", + MYF(0), csname); + +def: + csname= MYSQL_DEFAULT_CHARSET_NAME; + my_printf_error(ER_UNKNOWN_ERROR, + "Switching to the default character set '%s'.", + MYF(0), csname); + return csname; +} + + +#ifndef __WIN__ +#include <stdlib.h> /* for getenv() */ +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#endif /* __WIN__ */ + + +static int +mysql_autodetect_character_set(MYSQL *mysql) +{ + const char *csname= MYSQL_DEFAULT_CHARSET_NAME; + +#ifdef __WIN__ + char cpbuf[64]; + { + my_snprintf(cpbuf, sizeof(cpbuf), "cp%d", (int) GetConsoleCP()); + csname= my_os_charset_to_mysql_charset(cpbuf); + } +#elif defined(HAVE_SETLOCALE) && defined(HAVE_NL_LANGINFO) + { + if (setlocale(LC_CTYPE, "") && (csname= nl_langinfo(CODESET))) + csname= my_os_charset_to_mysql_charset(csname); + } +#endif + + my_free(mysql->options.charset_name); + if (!(mysql->options.charset_name= my_strdup(csname, MYF(MY_WME)))) + return 1; + return 0; +} + + +static void +mysql_set_character_set_with_default_collation(MYSQL *mysql) +{ + const char *save= charsets_dir; + if (mysql->options.charset_dir) + charsets_dir=mysql->options.charset_dir; + + if ((mysql->charset= get_charset_by_csname(mysql->options.charset_name, + MY_CS_PRIMARY, MYF(MY_WME)))) + { + /* Try to set compiled default collation when it's possible. */ + CHARSET_INFO *collation; + if ((collation= + get_charset_by_name(MYSQL_DEFAULT_COLLATION_NAME, MYF(MY_WME))) && + my_charset_same(mysql->charset, collation)) + { + mysql->charset= collation; + } + else + { + /* + Default compiled collation not found, or is not applicable + to the requested character set. + Continue with the default collation of the character set. + */ + } + } + charsets_dir= save; +} + + C_MODE_START int mysql_init_character_set(MYSQL *mysql) { - const char *default_collation_name; - /* Set character set */ if (!mysql->options.charset_name) { - default_collation_name= MYSQL_DEFAULT_COLLATION_NAME; if (!(mysql->options.charset_name= my_strdup(MYSQL_DEFAULT_CHARSET_NAME,MYF(MY_WME)))) - return 1; - } - else - default_collation_name= NULL; - - { - const char *save= charsets_dir; - if (mysql->options.charset_dir) - charsets_dir=mysql->options.charset_dir; - mysql->charset=get_charset_by_csname(mysql->options.charset_name, - MY_CS_PRIMARY, MYF(MY_WME)); - if (mysql->charset && default_collation_name) - { - CHARSET_INFO *collation; - if ((collation= - get_charset_by_name(default_collation_name, MYF(MY_WME)))) - { - if (!my_charset_same(mysql->charset, collation)) - { - my_printf_error(ER_UNKNOWN_ERROR, - "COLLATION %s is not valid for CHARACTER SET %s", - MYF(0), - default_collation_name, mysql->options.charset_name); - mysql->charset= NULL; - } - else - { - mysql->charset= collation; - } - } - else - mysql->charset= NULL; - } - charsets_dir= save; + return 1; } + else if (!strcmp(mysql->options.charset_name, + MYSQL_AUTODETECT_CHARSET_NAME) && + mysql_autodetect_character_set(mysql)) + return 1; + + mysql_set_character_set_with_default_collation(mysql); if (!mysql->charset) { @@ -1916,6 +2291,9 @@ static auth_plugin_t native_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Native MySQL authentication", {1, 0, 0}, + "GPL", + NULL, + NULL, NULL, NULL, native_password_auth_client @@ -1929,6 +2307,9 @@ static auth_plugin_t old_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Old MySQL-3.23 authentication", {1, 0, 0}, + "GPL", + NULL, + NULL, NULL, NULL, old_password_auth_client @@ -1941,8 +2322,6 @@ struct st_mysql_client_plugin *mysql_client_builtins[]= 0 }; - - /* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */ typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); @@ -1950,15 +2329,15 @@ typedef struct { void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); /* -= end of MYSQL_PLUGIN_VIO =- */ MYSQL *mysql; - auth_plugin_t *plugin; /**< what plugin we're under */ + auth_plugin_t *plugin; /**< what plugin we're under */ const char *db; struct { - uchar *pkt; /**< pointer into NET::buff */ + uchar *pkt; /**< pointer into NET::buff */ uint pkt_len; } cached_server_reply; - uint packets_read, packets_written; /**< counters for send/received packets */ - my_bool mysql_change_user; /**< if it's mysql_change_user() */ - int last_read_packet_len; /**< the length of the last *read* packet */ + int packets_read, packets_written; /**< counters for send/received packets */ + int mysql_change_user; /**< if it's mysql_change_user() */ + int last_read_packet_len; /**< the length of the last *read* packet */ } MCPVIO_EXT; /** @@ -1980,7 +2359,6 @@ typedef struct { @retval 0 ok @retval 1 error */ - static int send_change_user_packet(MCPVIO_EXT *mpvio, const uchar *data, int data_len) { @@ -2064,7 +2442,6 @@ error: @retval 0 ok @retval 1 error */ - static int send_client_reply_packet(MCPVIO_EXT *mpvio, const uchar *data, int data_len) { @@ -2122,7 +2499,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, /* Do the SSL layering. */ struct st_mysql_options *options= &mysql->options; struct st_VioSSLFd *ssl_fd; - char error_string[1024]; + enum enum_ssl_init_error ssl_init_error; + const char *cert_error; + unsigned long ssl_error; /* Send mysql->client_flag, max_packet_size - unencrypted otherwise @@ -2142,32 +2521,36 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, options->ssl_cert, options->ssl_ca, options->ssl_capath, - options->ssl_cipher))) + options->ssl_cipher, + &ssl_init_error))) { - set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), sslGetErrString(ssl_init_error)); goto error; } - mysql->connector_fd= (void*)ssl_fd; + mysql->connector_fd= (unsigned char *) ssl_fd; /* Connect to the server */ DBUG_PRINT("info", ("IO layer change in progress...")); if (sslconnect(ssl_fd, net->vio, - (long) (mysql->options.connect_timeout), - error_string)) - { - set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, - unknown_sqlstate, "SSL error: %s", - error_string[0] ? error_string : - ER(CR_SSL_CONNECTION_ERROR)); + (long) (mysql->options.connect_timeout), &ssl_error)) + { + char buf[512]; + ERR_error_string_n(ssl_error, buf, 512); + buf[511]= 0; + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), + buf); goto error; } DBUG_PRINT("info", ("IO layer change done!")); /* Verify server cert */ if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && - ssl_verify_server_cert(net->vio, mysql->host)) + ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { - set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), cert_error); goto error; } } @@ -2240,7 +2623,6 @@ error: This function is called by a client authentication plugin, when it wants to read data from the server. */ - static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) { MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; @@ -2303,7 +2685,6 @@ static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) It transparently wraps the data into a change user or authentication handshake packet, if neccessary. */ - static int client_mpvio_write_packet(struct st_plugin_vio *mpv, const uchar *pkt, int pkt_len) { @@ -2339,7 +2720,6 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv, fills MYSQL_PLUGIN_VIO_INFO structure with the information about the connection */ - void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) { bzero(info, sizeof(*info)); @@ -2370,7 +2750,9 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) return; case VIO_TYPE_SHARED_MEMORY: info->protocol= MYSQL_VIO_MEMORY; +#ifdef HAVE_SMEM info->handle= vio->handle_file_map; /* or what ? */ +#endif return; #endif default: DBUG_ASSERT(0); @@ -2402,7 +2784,6 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, @retval 0 ok @retval 1 error */ - int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *data_plugin, const char *db) { @@ -2412,6 +2793,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ulong pkt_length; int res; + DBUG_ENTER ("run_plugin_auth"); /* determine the default/initial plugin to use */ if (mysql->options.extension && mysql->options.extension->default_auth && mysql->server_capabilities & CLIENT_PLUGIN_AUTH) @@ -2419,7 +2801,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, auth_plugin_name= mysql->options.extension->default_auth; if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) - return 1; /* oops, not found */ + DBUG_RETURN (1); /* oops, not found */ } else { @@ -2428,6 +2810,8 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, auth_plugin_name= auth_plugin->name; } + DBUG_PRINT ("info", ("using plugin %s", auth_plugin_name)); + mysql->net.last_errno= 0; /* just in case */ if (data_plugin && strcmp(data_plugin, auth_plugin_name)) @@ -2449,6 +2833,11 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, mpvio.plugin= auth_plugin; res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + DBUG_PRINT ("info", ("authenticate_user returned %s", + res == CR_OK ? "CR_OK" : + res == CR_ERROR ? "CR_ERROR" : + res == CR_OK_HANDSHAKE_COMPLETE ? + "CR_OK_HANDSHAKE_COMPLETE" : "error")); compile_time_assert(CR_OK == -1); compile_time_assert(CR_ERROR == 0); @@ -2459,12 +2848,13 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, unless the error code is CR_ERROR and mysql->net.last_errno is already set (the plugin has done it) */ + DBUG_PRINT ("info", ("res=%d", res)); if (res > CR_ERROR) set_mysql_error(mysql, res, unknown_sqlstate); else if (!mysql->net.last_errno) set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); - return 1; + DBUG_RETURN (1); } /* read the OK packet (or use the cached value in mysql->net.read_pos */ @@ -2473,6 +2863,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, else /* res == CR_OK_HANDSHAKE_COMPLETE */ pkt_length= mpvio.last_read_packet_len; + DBUG_PRINT ("info", ("OK packet length=%lu", pkt_length)); if (pkt_length == packet_error) { if (mysql->net.last_errno == CR_SERVER_LOST) @@ -2480,7 +2871,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ER(CR_SERVER_LOST_EXTENDED), "reading authorization packet", errno); - return 1; + DBUG_RETURN (1); } if (mysql->net.read_pos[0] == 254) @@ -2489,6 +2880,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, if (pkt_length == 1) { /* old "use short scramble" packet */ + DBUG_PRINT ("info", ("old use short scramble packet from server")); auth_plugin_name= old_password_plugin_name; mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble; mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; @@ -2501,15 +2893,22 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */ mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; + DBUG_PRINT ("info", ("change plugin packet from server for plugin %s", + auth_plugin_name)); } if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) - return 1; + DBUG_RETURN (1); mpvio.plugin= auth_plugin; res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + DBUG_PRINT ("info", ("second authenticate_user returned %s", + res == CR_OK ? "CR_OK" : + res == CR_ERROR ? "CR_ERROR" : + res == CR_OK_HANDSHAKE_COMPLETE ? + "CR_OK_HANDSHAKE_COMPLETE" : "error")); if (res > CR_OK) { if (res > CR_ERROR) @@ -2517,7 +2916,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, else if (!mysql->net.last_errno) set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); - return 1; + DBUG_RETURN (1); } if (res != CR_OK_HANDSHAKE_COMPLETE) @@ -2530,7 +2929,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ER(CR_SERVER_LOST_EXTENDED), "reading final connect information", errno); - return 1; + DBUG_RETURN (1); } } } @@ -2538,7 +2937,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, net->read_pos[0] should always be 0 here if the server implements the protocol correctly */ - return mysql->net.read_pos[0] != 0; + DBUG_RETURN (mysql->net.read_pos[0] != 0); } @@ -2564,13 +2963,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, uint port, const char *unix_socket,ulong client_flag) { char buff[NAME_LEN+USERNAME_LENGTH+100]; - int scramble_data_len, pkt_scramble_len; - char *end, *host_info=0, *server_version_end, *pkt_end; + int scramble_data_len, pkt_scramble_len= 0; + char *end,*host_info= 0, *server_version_end, *pkt_end; char *scramble_data; const char *scramble_plugin; - my_socket sock; - in_addr_t ip_addr; - struct sockaddr_in sock_addr; ulong pkt_length; NET *net= &mysql->net; #ifdef MYSQL_SERVER @@ -2583,17 +2979,21 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, #ifdef HAVE_SYS_UN_H struct sockaddr_un UNIXaddr; #endif - init_sigpipe_variables DBUG_ENTER("mysql_real_connect"); LINT_INIT(pkt_scramble_len); - DBUG_PRINT("enter",("host: %s db: %s user: %s", + DBUG_PRINT("enter",("host: %s db: %s user: %s (client)", host ? host : "(Null)", db ? db : "(Null)", user ? user : "(Null)")); - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); + /* Test whether we're already connected */ + if (net->vio) + { + set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate); + DBUG_RETURN(0); + } + mysql->methods= &client_methods; net->vio = 0; /* If something goes wrong */ mysql->client_flag=0; /* For handshake */ @@ -2605,8 +3005,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (mysql->options.my_cnf_file ? mysql->options.my_cnf_file : "my"), mysql->options.my_cnf_group); - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; } @@ -2637,6 +3037,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, unix_socket=mysql->options.unix_socket; mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + DBUG_PRINT("info", ("Connecting")); /* Part 0: Grab a socket and connect it to the server @@ -2647,6 +3048,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (!host || !strcmp(host,LOCAL_HOST)) && mysql->options.shared_memory_base_name) { + DBUG_PRINT("info", ("Using shared memory")); if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) == INVALID_HANDLE_VALUE) { @@ -2669,7 +3071,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, else { mysql->options.protocol=MYSQL_PROTOCOL_MEMORY; - sock=0; unix_socket = 0; host=mysql->options.shared_memory_base_name; my_snprintf(host_info=buff, sizeof(buff)-1, @@ -2684,12 +3085,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (unix_socket || mysql_unix_port) && (!host || !strcmp(host,LOCAL_HOST))) { - host=LOCAL_HOST; - if (!unix_socket) - unix_socket=mysql_unix_port; - host_info=(char*) ER(CR_LOCALHOST_CONNECTION); - DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket)); - if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) + my_socket sock= socket(AF_UNIX, SOCK_STREAM, 0); + DBUG_PRINT("info", ("Using socket")); + if (sock == SOCKET_ERROR) { set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, @@ -2697,10 +3095,25 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, socket_errno); goto error; } + net->vio= vio_new(sock, VIO_TYPE_SOCKET, VIO_LOCALHOST | VIO_BUFFERED_READ); - bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); - UNIXaddr.sun_family = AF_UNIX; + if (!net->vio) + { + DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol)); + set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate); + closesocket(sock); + goto error; + } + + host= LOCAL_HOST; + if (!unix_socket) + unix_socket= mysql_unix_port; + host_info= (char*) ER(CR_LOCALHOST_CONNECTION); + DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket)); + + bzero((char*) &UNIXaddr, sizeof(UNIXaddr)); + UNIXaddr.sun_family= AF_UNIX; strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1); if (connect_sync_or_async(mysql, net, sock, (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr))) @@ -2711,6 +3124,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, unknown_sqlstate, ER(CR_CONNECTION_ERROR), unix_socket, socket_errno); + vio_delete(net->vio); + net->vio= 0; goto error; } mysql->options.protocol=MYSQL_PROTOCOL_SOCKET; @@ -2721,7 +3136,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || (! have_tcpip && (unix_socket || !host && is_NT())))) { - sock=0; if ((hPipe= create_named_pipe(mysql, mysql->options.connect_timeout, (char**) &host, (char**) &unix_socket)) == INVALID_HANDLE_VALUE) @@ -2745,95 +3159,131 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } } #endif + DBUG_PRINT("info", ("net->vio: %p protocol: %d", + net->vio, mysql->options.protocol)); if (!net->vio && (!mysql->options.protocol || mysql->options.protocol == MYSQL_PROTOCOL_TCP)) { - int status= -1; + struct addrinfo *res_lst, hints, *t_res; + int gai_errno; + char port_buf[NI_MAXSERV]; + my_socket sock= SOCKET_ERROR; + int saved_error= 0, status= -1; + unix_socket=0; /* This is not used */ + if (!port) - port=mysql_port; + port= mysql_port; + if (!host) - host=LOCAL_HOST; - my_snprintf(host_info=buff,sizeof(buff)-1,ER(CR_TCP_CONNECTION),host); - DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); + host= LOCAL_HOST; + + my_snprintf(host_info=buff, sizeof(buff)-1, ER(CR_TCP_CONNECTION), host); + DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host, port)); #ifdef MYSQL_SERVER thr_alarm_init(&alarmed); thr_alarm(&alarmed, mysql->options.connect_timeout, &alarm_buff); #endif - /* _WIN64 ; Assume that the (int) range is enough for socket() */ - sock = (my_socket) socket(AF_INET,SOCK_STREAM,0); + + DBUG_PRINT("info",("IP '%s'", "client")); + #ifdef MYSQL_SERVER thr_end_alarm(&alarmed); #endif - if (sock == SOCKET_ERROR) - { - set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, - ER(CR_IPSOCK_ERROR), socket_errno); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype= SOCK_STREAM; + hints.ai_protocol= IPPROTO_TCP; + hints.ai_family= AF_UNSPEC; + + DBUG_PRINT("info",("IPV6 getaddrinfo %s", host)); + my_snprintf(port_buf, NI_MAXSERV, "%d", port); + gai_errno= getaddrinfo(host, port_buf, &hints, &res_lst); + + if (gai_errno != 0) + { + /* + For DBUG we are keeping the right message but for client we default to + historical error message. + */ + DBUG_PRINT("info",("IPV6 getaddrinfo error %d", gai_errno)); + set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, + ER(CR_UNKNOWN_HOST), host, errno); + goto error; } - net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); - bzero((char*) &sock_addr,sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_port = (ushort) htons((ushort) port); /* - The server name may be a host name or IP address + A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the + list of IP addresses until a successful connection can be established. */ - - if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) + DBUG_PRINT("info", ("Try connect on all addresses for host.")); + for (t_res= res_lst; t_res; t_res= t_res->ai_next) { - memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); - status= connect_sync_or_async(mysql, net, sock, - (struct sockaddr *) &sock_addr, - sizeof(sock_addr)); - } - else - { - int i, tmp_errno; - struct hostent tmp_hostent,*hp; - char buff2[GETHOSTBYNAME_BUFF_SIZE]; - hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), - &tmp_errno); - - /* - Don't attempt to connect to non IPv4 addresses as the client could - end up sending information to a unknown server. For example, a IPv6 - address might be returned from gethostbyname depending on options - set via the RES_OPTIONS environment variable. - */ - if (!hp || (hp->h_addrtype != AF_INET)) + DBUG_PRINT("info", ("Create socket, family: %d type: %d proto: %d", + t_res->ai_family, t_res->ai_socktype, + t_res->ai_protocol)); + sock= socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol); + if (sock == SOCKET_ERROR) { - my_gethostbyname_r_free(); - set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, - ER(CR_UNKNOWN_HOST), host, tmp_errno); - goto error; + saved_error= socket_errno; + continue; } - for (i= 0; status && hp->h_addr_list[i]; i++) + net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); + if (!net->vio) { - IF_DBUG(char ipaddr[18];) - memcpy(&sock_addr.sin_addr, hp->h_addr_list[i], - min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length)); - DBUG_PRINT("info",("Trying %s...", - (my_inet_ntoa(sock_addr.sin_addr, ipaddr), ipaddr))); - status= connect_sync_or_async(mysql, net, sock, - (struct sockaddr *) &sock_addr, - sizeof(sock_addr)); + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + closesocket(sock); + freeaddrinfo(res_lst); + goto error; } - my_gethostbyname_r_free(); + DBUG_PRINT("info", ("Connect socket")); + status= connect_sync_or_async(mysql, net, sock, + t_res->ai_addr, t_res->ai_addrlen); + /* + Here we rely on my_connect() to return success only if the + connect attempt was really successful. Otherwise we would stop + trying another address, believing we were successful. + */ + if (!status) + break; + + /* + Save value as socket errno might be overwritten due to + calling a socket function below. + */ + saved_error= socket_errno; + + DBUG_PRINT("info", ("No success, close socket, try next address.")); + vio_delete(mysql->net.vio); + mysql->net.vio= 0; + } + DBUG_PRINT("info", + ("End of connect attempts, sock: %d status: %d error: %d", + sock, status, saved_error)); + + freeaddrinfo(res_lst); + + if (sock == SOCKET_ERROR) + { + set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, + ER(CR_IPSOCK_ERROR), saved_error); + goto error; } if (status) { - DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno, - host)); + DBUG_PRINT("error",("Got error %d on connect to '%s'", saved_error, host)); set_mysql_extended_error(mysql, CR_CONN_HOST_ERROR, unknown_sqlstate, - ER(CR_CONN_HOST_ERROR), host, socket_errno); + ER(CR_CONN_HOST_ERROR), host, saved_error); goto error; } } + + DBUG_PRINT("info", ("net->vio: %p", net->vio)); if (!net->vio) { DBUG_PRINT("error",("Unknow protocol %d ",mysql->options.protocol)); @@ -2879,6 +3329,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, /* Part 1: Connection established, read and parse first packet */ + DBUG_PRINT("info", ("Read first packet.")); if ((pkt_length=cli_safe_read(mysql)) == packet_error) { @@ -3003,11 +3454,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, if (mysql->client_flag & CLIENT_COMPRESS) /* We will use compression */ net->compress=1; -#ifdef CHECK_LICENSE - if (check_license(mysql)) - goto error; -#endif - if (db && !mysql->db && mysql_select_db(mysql, db)) { if (mysql->net.last_errno == CR_SERVER_LOST) @@ -3055,17 +3501,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } #endif -#ifndef TO_BE_DELETED - if (mysql->options.rpl_probe && mysql_rpl_probe(mysql)) - goto error; -#endif - DBUG_PRINT("exit", ("Mysql handler: 0x%lx", (long) mysql)); - reset_sigpipe(mysql); DBUG_RETURN(mysql); error: - reset_sigpipe(mysql); DBUG_PRINT("error",("message: %u/%s (%s)", net->last_errno, net->sqlstate, @@ -3079,28 +3518,6 @@ error: } -/* needed when we move MYSQL structure to a different address */ - -#ifndef TO_BE_DELETED -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; -} -#endif - - struct my_hook_data { MYSQL *orig_mysql; MYSQL *new_mysql; @@ -3144,8 +3561,7 @@ my_bool mysql_reconnect(MYSQL *mysql) mysql_init(&tmp_mysql); tmp_mysql.options= mysql->options; tmp_mysql.options.my_cnf_file= tmp_mysql.options.my_cnf_group= 0; - tmp_mysql.rpl_pivot= mysql->rpl_pivot; - + /* If we are automatically re-connecting inside a non-blocking API call, we may need to suspend and yield to the user application during the reconnect. @@ -3204,7 +3620,6 @@ 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, 1); mysql->affected_rows= ~(my_ulonglong) 0; DBUG_RETURN(0); @@ -3225,7 +3640,7 @@ mysql_select_db(MYSQL *mysql, const char *db) if ((error=simple_command(mysql,COM_INIT_DB, (const uchar*) db, (ulong) strlen(db),0))) DBUG_RETURN(error); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->db); mysql->db=my_strdup(db,MYF(MY_WME)); DBUG_RETURN(0); } @@ -3240,44 +3655,44 @@ static void mysql_close_free_options(MYSQL *mysql) { DBUG_ENTER("mysql_close_free_options"); - my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); - 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)); - my_free(mysql->options.client_ip,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.user); + my_free(mysql->options.host); + my_free(mysql->options.password); + my_free(mysql->options.unix_socket); + my_free(mysql->options.db); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); + my_free(mysql->options.charset_dir); + my_free(mysql->options.charset_name); + my_free(mysql->options.client_ip); if (mysql->options.init_commands) { DYNAMIC_ARRAY *init_commands= mysql->options.init_commands; char **ptr= (char**)init_commands->buffer; char **end= ptr + init_commands->elements; for (; ptr<end; ptr++) - my_free(*ptr,MYF(MY_WME)); + my_free(*ptr); delete_dynamic(init_commands); - my_free((char*)init_commands,MYF(MY_WME)); + my_free(init_commands); } -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) mysql_ssl_free(mysql); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #ifdef HAVE_SMEM if (mysql->options.shared_memory_base_name != def_shared_memory_base_name) - my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.shared_memory_base_name); #endif /* HAVE_SMEM */ if (mysql->options.extension) { struct mysql_async_context *ctxt= mysql->options.extension->async_context; - my_free(mysql->options.extension->plugin_dir,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.extension->default_auth,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.extension->plugin_dir); + my_free(mysql->options.extension->default_auth); if (ctxt) { my_context_destroy(&ctxt->async_context); - my_free(ctxt, MYF(0)); + my_free(ctxt); } - my_free(mysql->options.extension,MYF(0)); + my_free(mysql->options.extension); } bzero((char*) &mysql->options,sizeof(mysql->options)); DBUG_VOID_RETURN; @@ -3286,12 +3701,12 @@ static void mysql_close_free_options(MYSQL *mysql) static void mysql_close_free(MYSQL *mysql) { - my_free((uchar*) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->host_info); + my_free(mysql->user); + my_free(mysql->passwd); + my_free(mysql->db); #if defined(EMBEDDED_LIBRARY) || MYSQL_VERSION_ID >= 50100 - my_free(mysql->info_buffer,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->info_buffer); mysql->info_buffer= 0; #endif /* Clear pointers for better safety */ @@ -3419,26 +3834,6 @@ void STDCALL mysql_close(MYSQL *mysql) mysql_close_free_options(mysql); mysql_close_free(mysql); mysql_detach_stmt_list(&mysql->stmts, "mysql_close"); -#ifndef TO_BE_DELETED - /* 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; - } -#endif - if (mysql != mysql->master) - { - mysql_close(mysql->master); - mysql->master= 0; - } #ifndef MYSQL_SERVER if (mysql->thd) { @@ -3447,7 +3842,7 @@ void STDCALL mysql_close(MYSQL *mysql) } #endif if (mysql->free_me) - my_free((uchar*) mysql,MYF(0)); + my_free(mysql); } DBUG_VOID_RETURN; } @@ -3461,12 +3856,6 @@ static my_bool cli_read_query_result(MYSQL *mysql) ulong length; DBUG_ENTER("cli_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 = cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); free_old_query(mysql); /* Free old result */ @@ -3541,23 +3930,6 @@ int STDCALL mysql_send_query(MYSQL* mysql, const char* query, ulong length) { DBUG_ENTER("mysql_send_query"); - DBUG_PRINT("enter",("rpl_parse: %d rpl_pivot: %d", - mysql->options.rpl_parse, mysql->rpl_pivot)); -#ifndef TO_BE_DELETED - if (mysql->options.rpl_parse && mysql->rpl_pivot) - { - switch (mysql_rpl_query_type(query, length)) { - case MYSQL_RPL_MASTER: - DBUG_RETURN(mysql_master_send_query(mysql, query, length)); - case MYSQL_RPL_SLAVE: - DBUG_RETURN(mysql_slave_send_query(mysql, query, length)); - case MYSQL_RPL_ADMIN: - break; /* fall through */ - } - } - mysql->last_used_con = mysql; -#endif - DBUG_RETURN(simple_command(mysql, COM_QUERY, (uchar*) query, length, 1)); } @@ -3584,8 +3956,7 @@ MYSQL_RES * STDCALL 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) @@ -3608,7 +3979,7 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql) if (!(result->data= (*mysql->methods->read_rows)(mysql,mysql->fields,mysql->field_count))) { - my_free((uchar*) result,MYF(0)); + my_free(result); DBUG_RETURN(0); } mysql->affected_rows= result->row_count= result->data->rows; @@ -3640,8 +4011,6 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("cli_use_result"); - mysql = mysql->last_used_con; - if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -3658,7 +4027,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) if (!(result->row=(MYSQL_ROW) my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) { /* Ptrs: to one row */ - my_free((uchar*) result,MYF(0)); + my_free(result); DBUG_RETURN(0); } result->fields= mysql->fields; @@ -3755,7 +4124,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) struct mysql_async_context *ctxt; size_t stacksize; - DBUG_ENTER("mysql_option"); + DBUG_ENTER("mysql_options"); DBUG_PRINT("enter",("option: %d",(int) option)); switch (option) { case MYSQL_OPT_CONNECT_TIMEOUT: @@ -3784,19 +4153,19 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) add_init_command(&mysql->options,arg); break; case MYSQL_READ_DEFAULT_FILE: - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_file); mysql->options.my_cnf_file=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_READ_DEFAULT_GROUP: - my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_group); mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_SET_CHARSET_DIR: - my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_dir); mysql->options.charset_dir=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_SET_CHARSET_NAME: - my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_name); mysql->options.charset_name=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_OPT_PROTOCOL: @@ -3805,7 +4174,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) case MYSQL_SHARED_MEMORY_BASE_NAME: #ifdef HAVE_SMEM if (mysql->options.shared_memory_base_name != def_shared_memory_base_name) - my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.shared_memory_base_name); mysql->options.shared_memory_base_name=my_strdup(arg,MYF(MY_WME)); #endif break; @@ -3833,10 +4202,19 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT; break; case MYSQL_PLUGIN_DIR: - extension_set_string(&mysql->options, plugin_dir, arg); + EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg); break; case MYSQL_DEFAULT_AUTH: - extension_set_string(&mysql->options, default_auth, arg); + EXTENSION_SET_STRING(&mysql->options, default_auth, arg); + break; + case MYSQL_PROGRESS_CALLBACK: + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extention *) + my_malloc(sizeof(struct st_mysql_options_extention), + MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension) + mysql->options.extension->report_progress= + (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg; break; case MYSQL_OPT_NONBLOCK: if (mysql->options.extension && @@ -3849,7 +4227,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) if (ctxt->suspended) DBUG_RETURN(1); my_context_destroy(&ctxt->async_context); - my_free(ctxt, MYF(0)); + my_free(ctxt); } if (!(ctxt= (struct mysql_async_context *) my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) @@ -3864,10 +4242,10 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE; if (my_context_init(&ctxt->async_context, stacksize)) { - my_free(ctxt, MYF(0)); + my_free(ctxt); DBUG_RETURN(1); } - extension_set(&(mysql->options), async_context, ctxt) + EXTENSION_SET(&(mysql->options), async_context, ctxt) if (mysql->net.vio) mysql->net.vio->async_context= ctxt; break; @@ -3978,17 +4356,18 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) return mysql->net.last_errno; } - /** client authentication plugin that does native MySQL authentication using a 20-byte (4.1+) scramble */ - static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { int pkt_len; uchar *pkt; + DBUG_ENTER("native_password_auth_client"); + + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* @@ -4002,10 +4381,10 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { /* read the scramble */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); if (pkt_len != SCRAMBLE_LENGTH + 1) - return CR_SERVER_HANDSHAKE_ERR; + DBUG_RETURN(CR_SERVER_HANDSHAKE_ERR); /* save it in MYSQL */ memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH); @@ -4015,28 +4394,32 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) if (mysql->passwd[0]) { char scrambled[SCRAMBLE_LENGTH + 1]; + DBUG_PRINT("info", ("sending scramble")); scramble(scrambled, (char*)pkt, mysql->passwd); if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH)) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); } else + { + DBUG_PRINT("info", ("no password")); if (vio->write_packet(vio, 0, 0)) /* no password */ - return CR_ERROR; + DBUG_RETURN(CR_ERROR); + } - return CR_OK; + DBUG_RETURN(CR_OK); } - /** client authentication plugin that does old MySQL authentication using an 8-byte (4.0-) scramble */ - static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { uchar *pkt; int pkt_len; + DBUG_ENTER("old_password_auth_client"); + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* @@ -4050,14 +4433,14 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { /* read the scramble */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); if (pkt_len != SCRAMBLE_LENGTH_323 + 1 && pkt_len != SCRAMBLE_LENGTH + 1) - return CR_SERVER_HANDSHAKE_ERR; + DBUG_RETURN(CR_SERVER_HANDSHAKE_ERR); /* save it in MYSQL */ - memcpy(mysql->scramble, pkt, pkt_len); + memmove(mysql->scramble, pkt, pkt_len); mysql->scramble[pkt_len] = 0; } @@ -4066,13 +4449,13 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) char scrambled[SCRAMBLE_LENGTH_323 + 1]; scramble_323(scrambled, (char*)pkt, mysql->passwd); if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1)) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); } else if (vio->write_packet(vio, 0, 0)) /* no password */ - return CR_ERROR; + DBUG_RETURN(CR_ERROR); - return CR_OK; + DBUG_RETURN(CR_OK); } diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c index 0b1bdeae0be..f31ddb22a6a 100644 --- a/sql-common/client_plugin.c +++ b/sql-common/client_plugin.c @@ -1,4 +1,5 @@ /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab + Copyright (c) 2010, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file @@ -36,11 +37,7 @@ #include "mysql.h" #include <my_sys.h> #include <m_string.h> -#ifdef THREAD #include <my_pthread.h> -#else -#include <my_no_pthread.h> -#endif #include <sql_common.h> #include "errmsg.h" @@ -55,8 +52,8 @@ struct st_client_plugin_int { static my_bool initialized= 0; static MEM_ROOT mem_root; -static const char *plugin_declarations_sym __attribute__((unused)) = - "_mysql_client_plugin_declaration_"; +#define plugin_declarations_sym "_mysql_client_plugin_declaration_" + static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= { 0, /* these two are taken by Connector/C */ @@ -73,22 +70,21 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= loading the same plugin twice in parallel. */ struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS]; -#ifdef THREAD static pthread_mutex_t LOCK_load_client_plugin; -#endif static int is_not_initialized(MYSQL *mysql, const char *name) { + DBUG_ENTER("is_not_initialized"); + if (initialized) - return 0; + DBUG_RETURN(0); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "not initialized"); - return 1; + DBUG_RETURN(1); } - /** finds a plugin in the list @@ -99,25 +95,25 @@ static int is_not_initialized(MYSQL *mysql, const char *name) @retval a pointer to a found plugin or 0 */ - -static struct st_mysql_client_plugin *find_plugin(const char *name, int type) +static struct st_mysql_client_plugin * +find_plugin(const char *name, int type) { struct st_client_plugin_int *p; + DBUG_ENTER("find_plugin"); DBUG_ASSERT(initialized); DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS); if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) - return 0; + DBUG_RETURN(0); for (p= plugin_list[type]; p; p= p->next) { if (strcmp(p->plugin->name, name) == 0) - return p->plugin; + DBUG_RETURN(p->plugin); } - return NULL; + DBUG_RETURN(NULL); } - /** verifies the plugin and adds it to the list @@ -130,7 +126,6 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) @retval a pointer to an installed plugin or 0 */ - static struct st_mysql_client_plugin * add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, int argc, va_list args) @@ -138,6 +133,7 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, const char *errmsg; struct st_client_plugin_int plugin_int, *p; char errbuf[1024]; + DBUG_ENTER("add_plugin"); DBUG_ASSERT(initialized); @@ -178,22 +174,22 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, p->next= plugin_list[plugin->type]; plugin_list[plugin->type]= p; + net_clear_error(&mysql->net); - return plugin; + DBUG_RETURN(plugin); err2: if (plugin->deinit) plugin->deinit(); err1: - if (dlhandle) - (void)dlclose(dlhandle); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg); - return NULL; + if (dlhandle) + (void)dlclose(dlhandle); + DBUG_RETURN(NULL); } - /** Loads plugins which are specified in the environment variable LIBMYSQL_PLUGINS. @@ -209,14 +205,14 @@ err1: or LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..." */ - static void load_env_plugins(MYSQL *mysql) { char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS"); + DBUG_ENTER("load_env_plugins"); /* no plugins to load */ if (!s) - return; + DBUG_VOID_RETURN; free_env= plugs= my_strdup(s, MYF(MY_WME)); @@ -227,7 +223,8 @@ static void load_env_plugins(MYSQL *mysql) plugs= s + 1; } while (s); - my_free(free_env, MYF(0)); + my_free(free_env); + DBUG_VOID_RETURN; } /********** extern functions to be used by libmysql *********************/ @@ -240,16 +237,16 @@ static void load_env_plugins(MYSQL *mysql) @retval 0 successful @retval != 0 error occured */ - int mysql_client_plugin_init() { MYSQL mysql; struct st_mysql_client_plugin **builtin; va_list unused; + DBUG_ENTER("mysql_client_plugin_init"); LINT_INIT_STRUCT(unused); if (initialized) - return 0; + DBUG_RETURN(0); bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */ @@ -269,23 +266,22 @@ int mysql_client_plugin_init() load_env_plugins(&mysql); - return 0; + DBUG_RETURN(0); } - /** Deinitializes the client plugin layer. Unloades all client plugins and frees any associated resources. */ - void mysql_client_plugin_deinit() { int i; struct st_client_plugin_int *p; + DBUG_ENTER("mysql_client_plugin_deinit"); if (!initialized) - return; + DBUG_VOID_RETURN; for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++) for (p= plugin_list[i]; p; p= p->next) @@ -300,6 +296,7 @@ void mysql_client_plugin_deinit() initialized= 0; free_root(&mem_root, MYF(0)); pthread_mutex_destroy(&LOCK_load_client_plugin); + DBUG_VOID_RETURN; } /************* public facing functions, for client consumption *********/ @@ -310,10 +307,11 @@ mysql_client_register_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin) { va_list unused; + DBUG_ENTER("mysql_client_register_plugin"); LINT_INIT_STRUCT(unused); if (is_not_initialized(mysql, plugin->name)) - return NULL; + DBUG_RETURN(NULL); pthread_mutex_lock(&LOCK_load_client_plugin); @@ -329,10 +327,9 @@ mysql_client_register_plugin(MYSQL *mysql, plugin= add_plugin(mysql, plugin, 0, 0, unused); pthread_mutex_unlock(&LOCK_load_client_plugin); - return plugin; + DBUG_RETURN(plugin); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, @@ -342,9 +339,14 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, char dlpath[FN_REFLEN+1]; void *sym, *dlhandle; struct st_mysql_client_plugin *plugin; + DBUG_ENTER("mysql_load_plugin_v"); + DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc)); if (is_not_initialized(mysql, name)) - return NULL; + { + DBUG_PRINT ("leave", ("mysql not initialized")); + DBUG_RETURN (NULL); + } pthread_mutex_lock(&LOCK_load_client_plugin); @@ -361,9 +363,11 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, mysql->options.extension->plugin_dir : PLUGINDIR, "/", name, SO_EXT, NullS); + DBUG_PRINT ("info", ("dlopeninig %s", dlpath)); /* Open new dll handle */ if (!(dlhandle= dlopen(dlpath, RTLD_NOW))) { + DBUG_PRINT ("info", ("failed to dlopen")); errmsg= dlerror(); goto err; } @@ -399,37 +403,41 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, pthread_mutex_unlock(&LOCK_load_client_plugin); - return plugin; + DBUG_PRINT ("leave", ("plugin loaded ok")); + DBUG_RETURN (plugin); err: pthread_mutex_unlock(&LOCK_load_client_plugin); + DBUG_PRINT ("leave", ("plugin load error : %s", errmsg)); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg); - return NULL; + DBUG_RETURN (NULL); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...) { struct st_mysql_client_plugin *p; va_list args; + DBUG_ENTER("mysql_load_plugin"); + va_start(args, argc); p= mysql_load_plugin_v(mysql, name, type, argc, args); va_end(args); - return p; + DBUG_RETURN(p); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) { struct st_mysql_client_plugin *p; + DBUG_ENTER("mysql_client_find_plugin"); + DBUG_PRINT ("entry", ("name=%s, type=%d", name, type)); if (is_not_initialized(mysql, name)) - return NULL; + DBUG_RETURN (NULL); if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) { @@ -439,9 +447,26 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) } if ((p= find_plugin(name, type))) - return p; + { + DBUG_PRINT ("leave", ("found %p", p)); + DBUG_RETURN (p); + } /* not found, load it */ - return mysql_load_plugin(mysql, name, type, 0); + p= mysql_load_plugin(mysql, name, type, 0); + DBUG_PRINT ("leave", ("loaded %p", p)); + DBUG_RETURN (p); } + +/* see <mysql/client_plugin.h> for a full description */ +int mysql_plugin_options(struct st_mysql_client_plugin *plugin, + const char *option, + const void *value) +{ + DBUG_ENTER("mysql_plugin_options"); + /* does the plugin support options call? */ + if (!plugin || !plugin->options) + DBUG_RETURN(1); + DBUG_RETURN(plugin->options(option, value)); +} diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 03a124a341a..3476401792c 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2004-2006 MySQL AB +/* + Copyright (c) 2004, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,13 +12,14 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <my_time.h> #include <m_string.h> #include <m_ctype.h> /* Windows version of localtime_r() is declared in my_ptrhead.h */ #include <my_pthread.h> +#include <mysqld_error.h> ulonglong log_10_int[20]= { @@ -76,12 +78,12 @@ uint calc_days_in_year(uint year) */ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, - ulong flags, int *was_cut) + ulonglong flags, int *was_cut) { if (not_zero_date) { if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) && - (ltime->month == 0 || ltime->day == 0)) || + (ltime->month == 0 || ltime->day == 0)) || ltime->neg || (!(flags & TIME_INVALID_DATES) && ltime->month && ltime->day > days_in_month[ltime->month-1] && (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 || @@ -158,9 +160,9 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, enum enum_mysql_timestamp_type str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, - uint flags, int *was_cut) + ulonglong flags, int *was_cut) { - uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields; + uint UNINIT_VAR(field_length), UNINIT_VAR(year_length), digits, i, number_of_fields; uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS]; uint add_hours= 0, start_loop; ulong not_zero_date, allow_space; @@ -171,9 +173,14 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, my_bool found_delimitier= 0, found_space= 0; uint frac_pos, frac_len; DBUG_ENTER("str_to_datetime"); - DBUG_PRINT("ENTER",("str: %.*s",length,str)); + DBUG_PRINT("enter",("str: %.*s",length,str)); - LINT_INIT(field_length); + if (flags & TIME_TIME_ONLY) + { + enum enum_mysql_timestamp_type ret; + ret= str_to_time(str, length, l_time, flags, was_cut); + DBUG_RETURN(ret); + } *was_cut= 0; @@ -337,7 +344,7 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, { if (str[0] == 'p' || str[0] == 'P') add_hours= 12; - else if (str[0] != 'a' || str[0] != 'A') + else if (str[0] != 'a' && str[0] != 'A') continue; /* Not AM/PM */ str+= 2; /* Skip AM/PM */ /* Skip space after AM/PM */ @@ -477,12 +484,13 @@ err: work with times where the time arguments are in the above order. RETURN - 0 ok - 1 error + MYSQL_TIMESTAMP_TIME + MYSQL_TIMESTAMP_ERROR */ -my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - int *warning) +enum enum_mysql_timestamp_type +str_to_time(const char *str, uint length, MYSQL_TIME *l_time, + ulonglong fuzzydate, int *warning) { ulong date[5]; ulonglong value; @@ -501,7 +509,7 @@ my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, length--; } if (str == end) - return 1; + return MYSQL_TIMESTAMP_ERROR; /* Check first if this is a full TIMESTAMP */ if (length >= 12) @@ -509,12 +517,13 @@ my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int was_cut; enum enum_mysql_timestamp_type res= str_to_datetime(str, length, l_time, - (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut); + (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY, + &was_cut); if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR) { if (was_cut) *warning|= MYSQL_TIME_WARN_TRUNCATED; - return res == MYSQL_TIMESTAMP_ERROR; + return res; } } @@ -609,7 +618,7 @@ fractional: ((str[1] == '-' || str[1] == '+') && (end - str) > 2 && my_isdigit(&my_charset_latin1, str[2])))) - return 1; + return MYSQL_TIMESTAMP_ERROR; if (internal_format_positions[7] != 255) { @@ -632,7 +641,7 @@ fractional: if (date[0] > UINT_MAX || date[1] > UINT_MAX || date[2] > UINT_MAX || date[3] > UINT_MAX || date[4] > UINT_MAX) - return 1; + return MYSQL_TIMESTAMP_ERROR; l_time->year= 0; /* For protocol::store_time */ l_time->month= 0; @@ -644,8 +653,8 @@ fractional: l_time->time_type= MYSQL_TIMESTAMP_TIME; /* Check if the value is valid and fits into MYSQL_TIME range */ - if (check_time_range(l_time, warning)) - return 1; + if (check_time_range(l_time, 6, warning)) + return MYSQL_TIMESTAMP_ERROR; /* Check if there is garbage at end of the MYSQL_TIME specification */ if (str != end) @@ -659,7 +668,7 @@ fractional: } } while (++str != end); } - return 0; + return MYSQL_TIMESTAMP_TIME; } @@ -669,6 +678,7 @@ fractional: SYNOPSIS: check_time_range() time pointer to MYSQL_TIME value + uint dec warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range DESCRIPTION @@ -681,24 +691,31 @@ fractional: 1 time value is invalid */ -int check_time_range(struct st_mysql_time *my_time, int *warning) +int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning) { longlong hour; + static ulong max_sec_part[TIME_SECOND_PART_DIGITS+1]= {000000, 900000, 990000, + 999000, 999900, 999990, 999999}; if (my_time->minute >= 60 || my_time->second >= 60) return 1; hour= my_time->hour + (24*my_time->day); + + if (dec == AUTO_SEC_PART_DIGITS) + dec= TIME_SECOND_PART_DIGITS; + if (hour <= TIME_MAX_HOUR && (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE || - my_time->second != TIME_MAX_SECOND || !my_time->second_part)) + my_time->second != TIME_MAX_SECOND || + my_time->second_part <= max_sec_part[dec])) return 0; my_time->day= 0; my_time->hour= TIME_MAX_HOUR; my_time->minute= TIME_MAX_MINUTE; my_time->second= TIME_MAX_SECOND; - my_time->second_part= 0; + my_time->second_part= max_sec_part[dec]; *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE; return 0; } @@ -715,7 +732,7 @@ void my_init_time(void) time_t seconds; struct tm *l_time,tm_tmp; MYSQL_TIME my_time; - my_bool not_used; + uint not_used; seconds= (time_t) time((time_t*) 0); localtime_r(&seconds,&tm_tmp); @@ -730,6 +747,7 @@ void my_init_time(void) my_time.neg= 0; my_time.second_part= 0; my_time.time_type= MYSQL_TIMESTAMP_DATETIME; + my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */ } @@ -800,7 +818,11 @@ long calc_daynr(uint year,uint month,uint day) t - time value to be converted my_timezone - pointer to long where offset of system time zone from UTC will be stored for caching - in_dst_time_gap - set to true if time falls into spring time-gap + error_code - 0, if the conversion was successful; + ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value + which is out of TIMESTAMP range; + ER_WARN_INVALID_TIMESTAMP, if t represents value which + doesn't exists (falls into the spring time-gap). NOTES The idea is to cache the time zone offset from UTC (including daylight @@ -814,8 +836,7 @@ long calc_daynr(uint year,uint month,uint day) Time in UTC seconds since Unix Epoch representation. */ my_time_t -my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, - my_bool *in_dst_time_gap) +my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, uint *error_code) { uint loop; time_t tmp= 0; @@ -832,7 +853,11 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME)); if (!validate_timestamp_range(t)) + { + *error_code= ER_WARN_DATA_OUT_OF_RANGE; return 0; + } + *error_code= 0; /* Calculate the gmt time based on current time and timezone @@ -978,7 +1003,7 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, else if (diff == -3600) tmp-=t->minute*60 + t->second; /* Move to previous hour */ - *in_dst_time_gap= 1; + *error_code= ER_WARN_INVALID_TIMESTAMP; } *my_timezone= current_timezone; @@ -997,7 +1022,10 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, larger then TIMESTAMP_MAX_VALUE, so another check will work. */ if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp)) + { tmp= 0; + *error_code= ER_WARN_DATA_OUT_OF_RANGE; + } return (my_time_t) tmp; } /* my_system_gmt_sec */ @@ -1017,41 +1045,50 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) using default format. This functions don't check that given MYSQL_TIME structure members are in valid range. If they are not, return value won't reflect any - valid date either. Additionally, make_time doesn't take into - account time->day member: it's assumed that days have been converted - to hours already. + valid date either. RETURN number of characters written to 'to' */ -int my_time_to_str(const MYSQL_TIME *l_time, char *to) +int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { - uint extra_hours= 0; - return my_sprintf(to, (to, "%s%02u:%02u:%02u", - (l_time->neg ? "-" : ""), - extra_hours+ l_time->hour, - l_time->minute, - l_time->second)); + ulong day= (l_time->year || l_time->month) ? 0 : l_time->day; + + if (digits == AUTO_SEC_PART_DIGITS) + digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; + + DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); + + return sprintf(to, + digits ? "%s%02lu:%02u:%02u.%0*lu" + : "%s%02lu:%02u:%02u", + (l_time->neg ? "-" : ""), + day * 24L + l_time->hour, l_time->minute, l_time->second, + digits, (ulong)sec_part_shift(l_time->second_part, digits)); } int my_date_to_str(const MYSQL_TIME *l_time, char *to) { - return my_sprintf(to, (to, "%04u-%02u-%02u", - l_time->year, - l_time->month, - l_time->day)); + return sprintf(to, "%04u-%02u-%02u", + l_time->year, + l_time->month, + l_time->day); } -int my_datetime_to_str(const MYSQL_TIME *l_time, char *to) +int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { - return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u", - l_time->year, - l_time->month, - l_time->day, - l_time->hour, - l_time->minute, - l_time->second)); + if (digits == AUTO_SEC_PART_DIGITS) + digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; + + DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); + + return sprintf(to, + digits ? "%04u-%02u-%02u %02u:%02u:%02u.%0*lu" + : "%04u-%02u-%02u %02u:%02u:%02u", + l_time->year, l_time->month, l_time->day, + l_time->hour, l_time->minute, l_time->second, + digits, (ulong)sec_part_shift(l_time->second_part, digits)); } @@ -1062,19 +1099,22 @@ int my_datetime_to_str(const MYSQL_TIME *l_time, char *to) SYNOPSIS my_TIME_to_string() + RETURN + length of string + NOTE The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved. */ -int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) +int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { switch (l_time->time_type) { case MYSQL_TIMESTAMP_DATETIME: - return my_datetime_to_str(l_time, to); + return my_datetime_to_str(l_time, to, digits); case MYSQL_TIMESTAMP_DATE: return my_date_to_str(l_time, to); case MYSQL_TIMESTAMP_TIME: - return my_time_to_str(l_time, to); + return my_time_to_str(l_time, to, digits); case MYSQL_TIMESTAMP_NONE: case MYSQL_TIMESTAMP_ERROR: to[0]='\0'; @@ -1112,16 +1152,15 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) Datetime value in YYYYMMDDHHMMSS format. */ -longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, - uint flags, int *was_cut) +longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res, + ulonglong flags, int *was_cut) { long part1,part2; *was_cut= 0; - bzero((char*) time_res, sizeof(*time_res)); time_res->time_type=MYSQL_TIMESTAMP_DATE; - if (nr == LL(0) || nr >= LL(10000101000000)) + if (nr == 0 || nr >= 10000101000000LL || sec_part) { time_res->time_type=MYSQL_TIMESTAMP_DATETIME; goto ok; @@ -1140,7 +1179,12 @@ longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */ goto ok; } - if (nr < 10000101L) + /* + Though officially we support DATE values from 1000-01-01 only, one can + easily insert a value like 1-1-1. So, for consistency reasons such dates + are allowed when TIME_FUZZY_DATE is set. + */ + if (nr < 10000101L && !(flags & TIME_FUZZY_DATE)) goto err; if (nr <= 99991231L) { @@ -1171,22 +1215,87 @@ longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, time_res->hour= (int) (part2/10000L); part2%=10000L; time_res->minute=(int) part2 / 100; time_res->second=(int) part2 % 100; + time_res->second_part= sec_part; + time_res->neg= 0; if (time_res->year <= 9999 && time_res->month <= 12 && time_res->day <= 31 && time_res->hour <= 23 && time_res->minute <= 59 && time_res->second <= 59 && - !check_date(time_res, (nr != 0), flags, was_cut)) + sec_part <= TIME_MAX_SECOND_PART && + !check_date(time_res, nr || sec_part, flags, was_cut)) return nr; /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */ - if (!nr && (flags & TIME_NO_ZERO_DATE)) - return LL(-1); + if (nr || !(flags & TIME_NO_ZERO_DATE)) + *was_cut= 1; + return LL(-1); err: - *was_cut= 1; + { + /* reset everything except time_type */ + enum enum_mysql_timestamp_type save= time_res->time_type; + bzero((char*) time_res, sizeof(*time_res)); + time_res->time_type= save; /* Restore range */ + *was_cut= 1; /* Found invalid date */ + } return LL(-1); } +/* + Convert a pair of integers to a MYSQL_TIME struct. + + @param[in] nr a number to convert + @param[out] ltime Date to check. + @param[out] was_cut MYSQL_TIME_WARN_OUT_OF_RANGE if the value was + modified to fit in the valid range. Otherwise 0. + + @details + Takes a number in the [-]HHHMMSS.uuuuuu, + YYMMDDHHMMSS.uuuuuu, or in the YYYYMMDDHHMMSS.uuuuuu formats. + + @return + 0 time value is valid, but was possibly truncated + -1 time value is invalid +*/ +int number_to_time(my_bool neg, longlong nr, ulong sec_part, + MYSQL_TIME *ltime, int *was_cut) +{ + if (nr > 9999999 && neg == 0) + { + if (number_to_datetime(nr, sec_part, ltime, + TIME_FUZZY_DATE | TIME_INVALID_DATES, was_cut) < 0) + return -1; + + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + *was_cut= MYSQL_TIME_WARN_TRUNCATED; + return 0; + } + + *was_cut= 0; + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + + ltime->neg= neg; + + if (nr > TIME_MAX_VALUE) + { + nr= TIME_MAX_VALUE; + sec_part= TIME_MAX_SECOND_PART; + *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE; + } + ltime->hour = (uint)(nr/100/100); + ltime->minute= nr/100%100; + ltime->second= nr%100; + ltime->second_part= sec_part; + + if (ltime->minute < 60 && ltime->second < 60 && sec_part <= TIME_MAX_SECOND_PART) + return 0; + + *was_cut= MYSQL_TIME_WARN_TRUNCATED; + return -1; +} + /* Convert time value to integer in YYYYMMDDHHMMSS format */ @@ -1235,7 +1344,7 @@ ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time) DESCRIPTION The function is used when we need to convert value of time item to a number if it's used in numeric context, i. e.: - SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0; + SELECT NOW()+1, CURDATE()+0, CURTIME()+0; SELECT ?+1; NOTE @@ -1262,3 +1371,41 @@ ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time) return 0; } +double TIME_to_double(const MYSQL_TIME *my_time) +{ + double d= (double)TIME_to_ulonglong(my_time); + + if (my_time->time_type == MYSQL_TIMESTAMP_DATE) + return d; + + d+= my_time->second_part/(double)TIME_SECOND_PART_FACTOR; + return my_time->neg ? -d : d; +} + +longlong pack_time(MYSQL_TIME *my_time) +{ + return ((((((my_time->year * 13ULL + + my_time->month) * 32ULL + + my_time->day) * 24ULL + + my_time->hour) * 60ULL + + my_time->minute) * 60ULL + + my_time->second) * 1000000ULL + + my_time->second_part) * (my_time->neg ? -1 : 1); +} + +#define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR + +MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time) +{ + if ((my_time->neg= packed < 0)) + packed= -packed; + get_one(my_time->second_part, 1000000ULL); + get_one(my_time->second, 60ULL); + get_one(my_time->minute, 60ULL); + get_one(my_time->hour, 24ULL); + get_one(my_time->day, 32ULL); + get_one(my_time->month, 13ULL); + my_time->year= (uint)packed; + my_time->time_type= MYSQL_TIMESTAMP_DATETIME; + return my_time; +} diff --git a/sql-common/my_user.c b/sql-common/my_user.c index 0b72e002977..8d717ea7131 100644 --- a/sql-common/my_user.c +++ b/sql-common/my_user.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <my_user.h> #include <m_string.h> diff --git a/sql-common/mysql_async.c b/sql-common/mysql_async.c index a2b818b77e2..c130eab5061 100644 --- a/sql-common/mysql_async.c +++ b/sql-common/mysql_async.c @@ -61,6 +61,14 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, int res; size_socket s_err_size; + /* Make the socket non-blocking. */ +#ifdef __WIN__ + ulong arg= 1; + ioctlsocket(fd, FIONBIO, (void *)&arg); +#else + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + b->events_to_wait_for= 0; /* Start to connect asynchronously. @@ -1817,6 +1825,43 @@ MK_ASYNC_CONT_BODY( 1, r_int) } + +/* Structure used to pass parameters from mysql_stmt_next_result_start(). */ +struct mysql_stmt_next_result_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_next_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_next_result, + (parms->stmt), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_next_result_start(int *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_next_result, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_stmt_next_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} #endif |