diff options
Diffstat (limited to 'mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c')
-rw-r--r-- | mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c | 3728 |
1 files changed, 3728 insertions, 0 deletions
diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c b/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c new file mode 100644 index 0000000..2f0953f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c @@ -0,0 +1,3728 @@ +/************************************************************************************ + Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB, + Monty Program AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see <http://www.gnu.org/licenses> + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + +#include <my_global.h> + +#include <my_sys.h> +#include <mysys_err.h> +#include <m_string.h> +#include <m_ctype.h> +#include <ma_common.h> +#include "my_context.h" +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "errmsg.h" +#include <sys/stat.h> +#include <signal.h> +#include <time.h> +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#if !defined(MSDOS) && !defined(_WIN32) +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#ifdef HAVE_SELECT_H +# include <select.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#endif +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif +#if defined(THREAD) && !defined(_WIN32) +#include <my_pthread.h> /* because of signal() */ +#endif +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif +#include <sha1.h> +#include <violite.h> +#ifdef HAVE_OPENSSL +#include <ma_secure.h> +#endif +#ifndef _WIN32 +#include <poll.h> +#endif +#include <ma_dyncol.h> +#include <mysql_async.h> + +#define DNS_TIMEOUT 30 +#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) + +#undef max_allowed_packet +#undef net_buffer_length +extern ulong max_allowed_packet; /* net.c */ +extern ulong net_buffer_length; /* net.c */ +static MYSQL_PARAMETERS mariadb_internal_parameters= +{&max_allowed_packet, &net_buffer_length, 0}; + +static my_bool mysql_client_init=0; +static void mysql_close_options(MYSQL *mysql); +extern my_bool my_init_done; +extern my_bool mysql_ps_subsystem_initialized; +extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename); +extern const CHARSET_INFO * mysql_find_charset_nr(uint charsetnr); +extern const CHARSET_INFO * mysql_find_charset_name(const char * const name); +extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + const char *data_plugin, const char *db); + +/* prepare statement methods from my_stmt.c */ +extern my_bool mthd_supported_buffer_type(enum enum_field_types type); +extern my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt); +extern my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt); +extern my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt); +extern int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row); +extern int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row); +extern int mthd_stmt_read_all_rows(MYSQL_STMT *stmt); +extern void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt); +extern unsigned char *mysql_net_store_length(unsigned char *packet, size_t length); + +uint mysql_port=0; +my_string mysql_unix_port=0; + +#ifdef _WIN32 +#define CONNECT_TIMEOUT 20 +#else +#define CONNECT_TIMEOUT 0 +#endif + +struct st_mysql_methods MARIADB_DEFAULT_METHODS; + +#if defined(MSDOS) || defined(_WIN32) +// socket_errno is defined in my_global.h for all platforms +#define perror(A) +#else +#include <errno.h> +#define SOCKET_ERROR -1 +#endif /* _WIN32 */ + +#include <mysql/client_plugin.h> + +#define native_password_plugin_name "mysql_native_password" + +static void end_server(MYSQL *mysql); +static void mysql_close_memory(MYSQL *mysql); +void read_user_name(char *name); +static void append_wild(char *to,char *end,const char *wild); +static my_bool mysql_reconnect(MYSQL *mysql); +static sig_handler pipe_sig_handler(int sig); +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); + +extern int mysql_client_plugin_init(); +extern void mysql_client_plugin_deinit(); + +/* + Let the user specify that we don't want SIGPIPE; This doesn't however work + with threaded applications as we can have multiple read in progress. +*/ + +#if !defined(_WIN32) && defined(SIGPIPE) && !defined(THREAD) +#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; +#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler) +#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); +#else +#define init_sigpipe_variables +#define set_sigpipe(mysql) +#define reset_sigpipe(mysql) +#endif + + + +/**************************************************************************** +* A modified version of connect(). connect2() allows you to specify +* a timeout value, in seconds, that we should wait until we +* derermine we can't connect to a particular host. If timeout is 0, +* connect2() will behave exactly like connect(). +* +* Base version coded by Steve Bernacki, Jr. <steve@navinet.net> +*****************************************************************************/ + +int socket_block(my_socket s,my_bool blocked) +{ +#ifdef _WIN32 + unsigned long socket_blocked= blocked ? 0 : 1; + return ioctlsocket(s, FIONBIO, &socket_blocked); +#else + int flags= fcntl(s, F_GETFL, 0); + if (blocked) + flags&= ~O_NONBLOCK; + else + flags|= O_NONBLOCK; + return fcntl(s, F_SETFL, flags); +#endif +} + +static int connect2(my_socket s, const struct sockaddr *name, size_t namelen, + uint timeout) +{ + int res, s_err; + socklen_t s_err_size = sizeof(uint); +#ifndef _WIN32 + struct pollfd poll_fd; +#else + FD_SET sfds, efds; + struct timeval tv; +#endif + + if (!timeout) + return connect(s, (struct sockaddr*) name, (int)namelen); + + /* set socket to non blocking */ + if (socket_block(s, 0) == SOCKET_ERROR) + return -1; + + res= connect(s, (struct sockaddr*) name, (int)namelen); + if (res == 0) + return res; + +#ifdef _WIN32 + if (GetLastError() != WSAEWOULDBLOCK && + GetLastError() != WSAEINPROGRESS) +#else + if (errno != EINPROGRESS) +#endif + return -1; +#ifndef _WIN32 + memset(&poll_fd, 0, sizeof(struct pollfd)); + poll_fd.events= POLLOUT | POLLERR; + poll_fd.fd= s; + + /* connection timeout in milliseconds */ + res= poll(&poll_fd, 1, timeout); + + switch(res) + { + /* Error= - 1, timeout = 0 */ + case -1: + break; + case 0: + errno= ETIMEDOUT; + break; + } + s_err=0; + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) + return(-1); + + if (s_err) + { /* getsockopt could succeed */ + errno = s_err; + return(-1); /* but return an error... */ + } +#else + FD_ZERO(&sfds); + FD_ZERO(&efds); + FD_SET(s, &sfds); + FD_SET(s, &efds); + + memset(&tv, 0, sizeof(struct timeval)); + tv.tv_sec= timeout; + + res= select((int)s+1, NULL, &sfds, &efds, &tv); + if (res== -1) + { + errno= WSAGetLastError(); + } + else if (res == 0) + { + errno= ETIMEDOUT; + res= SOCKET_ERROR; + } + else if (FD_ISSET(s, &efds)) + { + int err; + int len = sizeof(int); + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &len) != SOCKET_ERROR) + { + errno= err; + } + res= SOCKET_ERROR; + } + if (res < 1) + return -1; +#endif + return (0); /* ok */ +} + +static int +connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, + const struct sockaddr *name, uint namelen) +{ + int vio_timeout= (mysql->options.connect_timeout > 0) ? + mysql->options.connect_timeout * 1000 : -1; + + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + { + my_bool old_mode; + vio_blocking(net->vio, FALSE, &old_mode); + return my_connect_async(mysql->options.extension->async_context, fd, + name, namelen, vio_timeout); + } + + return connect2(fd, name, namelen, vio_timeout); +} +/* +** Create a named pipe connection +*/ + +#ifdef _WIN32 + +HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, + char **arg_unix_socket) +{ + HANDLE hPipe=INVALID_HANDLE_VALUE; + char szPipeName [ 257 ]; + DWORD dwMode; + int i; + my_bool testing_named_pipes=0; + char *host= *arg_host, *unix_socket= *arg_unix_socket; + + if ( ! unix_socket || (unix_socket)[0] == 0x00) + unix_socket = mysql_unix_port; + if (!host || !strcmp(host,LOCAL_HOST)) + host=LOCAL_HOST_NAMEDPIPE; + + sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket); + DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s", + host, unix_socket)); + + for (i=0 ; i < 100 ; i++) /* Don't retry forever */ + { + if ((hPipe = CreateFile(szPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL )) != INVALID_HANDLE_VALUE) + break; + if (GetLastError() != ERROR_PIPE_BUSY) + { + net->last_errno=CR_NAMEDPIPEOPEN_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + /* wait for for an other instance */ + if (! WaitNamedPipe(szPipeName, connect_timeout) ) + { + net->last_errno=CR_NAMEDPIPEWAIT_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + } + if (hPipe == INVALID_HANDLE_VALUE) + { + net->last_errno=CR_NAMEDPIPEOPEN_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) ) + { + CloseHandle( hPipe ); + net->last_errno=CR_NAMEDPIPESETSTATE_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + *arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */ + return (hPipe); +} +#endif + +/* net_get_error */ +void net_get_error(char *buf, size_t buf_len, + char *error, size_t error_len, + unsigned int *error_no, + char *sqlstate) +{ + char *p= buf; + size_t error_msg_len= 0; + + if (buf_len > 2) + { + *error_no= uint2korr(p); + p+= 2; + + /* since 4.1 sqlstate is following */ + if (*p == '#') + { + memcpy(sqlstate, ++p, SQLSTATE_LENGTH); + p+= SQLSTATE_LENGTH; + } + error_msg_len= buf_len - (p - buf); + error_msg_len= MIN(error_msg_len, error_len - 1); + memcpy(error, p, error_msg_len); + } + else + { + *error_no= CR_UNKNOWN_ERROR; + memcpy(sqlstate, unknown_sqlstate, SQLSTATE_LENGTH); + } +} + + +/***************************************************************************** +** read a packet from server. Give error message if socket was down +** or packet is an error message +*****************************************************************************/ + +ulong +net_safe_read(MYSQL *mysql) +{ + NET *net= &mysql->net; + ulong len=0; + init_sigpipe_variables + +restart: + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + if (net->vio != 0) + len=my_net_read(net); + reset_sigpipe(mysql); + + if (len == packet_error || len == 0) + { + end_server(mysql); + my_set_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ? + CR_NET_PACKET_TOO_LARGE: + CR_SERVER_LOST, + SQLSTATE_UNKNOWN, 0); + return(packet_error); + } + if (net->read_pos[0] == 255) + { + if (len > 3) + { + char *pos=(char*) net->read_pos+1; + uint last_errno=uint2korr(pos); + pos+=2; + len-=2; + + if (last_errno== 65535 && + (mysql->server_capabilities & CLIENT_PROGRESS)) + { + if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1))) + { + /* Wrong packet */ + my_set_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate, 0); + return (packet_error); + } + goto restart; + } + net->last_errno= last_errno; + if (pos[0]== '#') + { + strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH); + pos+= SQLSTATE_LENGTH + 1; + } + else + { + strmov(net->sqlstate, SQLSTATE_UNKNOWN); + } + (void) strmake(net->last_error,(char*) pos, + min(len,sizeof(net->last_error)-1)); + } + else + { + my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + } + + mysql->server_status&= ~SERVER_MORE_RESULTS_EXIST; + + DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno, + net->last_error)); + return(packet_error); + } + return len; +} + +/* + 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; +} + +/* Get the length of next field. Change parameter to point at fieldstart */ +ulong +net_field_length(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (ulong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (ulong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (ulong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ + return (ulong) uint4korr(pos+1); +} + +/* Same as above, but returns ulonglong values */ + +static my_ulonglong +net_field_length_ll(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (my_ulonglong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return (my_ulonglong) NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (my_ulonglong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (my_ulonglong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG + return (my_ulonglong) uint4korr(pos+1); +#else + return (my_ulonglong) uint8korr(pos+1); +#endif +} + + +void free_rows(MYSQL_DATA *cur) +{ + if (cur) + { + free_root(&cur->alloc,MYF(0)); + my_free(cur); + } +} + + +int +mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg) +{ + NET *net= &mysql->net; + int result= -1; + init_sigpipe_variables + + DBUG_ENTER("mthd_my_send_command"); + + DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); + + /* 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)) + { + DBUG_PRINT("info", ("reconnect failed")); + DBUG_RETURN(1); + } + } + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + goto end; + } + + CLEAR_CLIENT_ERROR(mysql); + + mysql->info=0; + mysql->affected_rows= ~(my_ulonglong) 0; + net_clear(net); /* Clear receive buffer */ + if (!arg) + arg=""; + + if (net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg))) + { + DBUG_PRINT("error",("Can't send command to server. Error: %d",socket_errno)); + if (net->last_errno == ER_NET_PACKET_TOO_LARGE) + { + my_set_error(mysql, CR_NET_PACKET_TOO_LARGE, SQLSTATE_UNKNOWN, 0); + goto end; + } + end_server(mysql); + if (mysql_reconnect(mysql)) + goto end; + if (net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg))) + { + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + result=0; + if (!skipp_check) { + result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? + 1 : 0); + DBUG_PRINT("info", ("packet_length=%llu", mysql->packet_length)); + } + end: + reset_sigpipe(mysql); + DBUG_RETURN(result); +} + +int +simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg) +{ + return mysql->methods->db_command(mysql, command, arg, length, skipp_check, opt_arg); +} + +static void free_old_query(MYSQL *mysql) +{ + DBUG_ENTER("free_old_query"); + if (mysql->fields) + free_root(&mysql->field_alloc,MYF(0)); + init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */ + mysql->fields=0; + mysql->field_count=0; /* For API */ + mysql->info= 0; + DBUG_VOID_RETURN; +} + +#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL) +struct passwd *getpwuid(uid_t); +char* getlogin(void); +#endif + +#if !defined(MSDOS) && ! defined(VMS) && !defined(_WIN32) && !defined(OS2) +void read_user_name(char *name) +{ + DBUG_ENTER("read_user_name"); + if (geteuid() == 0) + (void) strmov(name,"root"); /* allow use of surun */ + else + { +#ifdef HAVE_GETPWUID + struct passwd *skr; + const char *str; + if ((str=getlogin()) == NULL) + { + if ((skr=getpwuid(geteuid())) != NULL) + str=skr->pw_name; + else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) && + !(str=getenv("LOGIN"))) + str="UNKNOWN_USER"; + } + (void) strmake(name,str,USERNAME_LENGTH); +#elif HAVE_CUSERID + (void) cuserid(name); +#else + strmov(name,"UNKNOWN_USER"); +#endif + } + DBUG_VOID_RETURN; +} + +#else /* If MSDOS || VMS */ + +void read_user_name(char *name) +{ + char *str=getenv("USERNAME"); /* ODBC will send user variable */ + strmake(name,str ? str : "ODBC", USERNAME_LENGTH); +} + +#endif + +#ifdef _WIN32 +static my_bool is_NT(void) +{ + char *os=getenv("OS"); + return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; +} +#endif + +/* +** Expand wildcard to a sql string +*/ + +static void +append_wild(char *to, char *end, const char *wild) +{ + end-=5; /* Some extra */ + if (wild && wild[0]) + { + to=strmov(to," like '"); + while (*wild && to < end) + { + if (*wild == '\\' || *wild == '\'') + *to++='\\'; + *to++= *wild++; + } + if (*wild) /* Too small buffer */ + *to++='%'; /* Nicer this way */ + to[0]='\''; + to[1]=0; + } +} + + + +/************************************************************************** +** Init debugging if MYSQL_DEBUG environment variable is found +**************************************************************************/ +void STDCALL mysql_debug_end() +{ +#ifndef DBUG_OFF + DEBUGGER_OFF; + DBUG_POP(); +#endif +} + +void STDCALL +mysql_debug(const char *debug __attribute__((unused))) +{ +#ifndef DBUG_OFF + char *env; + if (debug) + { + DEBUGGER_ON; + DBUG_PUSH(debug); + } + else if ((env = getenv("MYSQL_DEBUG"))) + { + DEBUGGER_ON; + DBUG_PUSH(env); +#if !defined(_WINVER) && !defined(WINVER) + puts("\n-------------------------------------------------------"); + puts("MYSQL_DEBUG found. libmariadb started with the following:"); + puts(env); + puts("-------------------------------------------------------\n"); +#else + { + char buff[80]; + strmov(strmov(buff,"libmariadb: "),env); + MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK); + } +#endif + } +#endif +} + + +/************************************************************************** +** Close the server connection if we get a SIGPIPE + ARGSUSED +**************************************************************************/ + +static sig_handler +pipe_sig_handler(int sig __attribute__((unused))) +{ + DBUG_PRINT("info",("Hit by signal %d",sig)); +#ifdef DONT_REMEMBER_SIGNAL + (void) signal(SIGPIPE,pipe_sig_handler); +#endif +} + + +/************************************************************************** +** Shut down connection +**************************************************************************/ + +static void +end_server(MYSQL *mysql) +{ + DBUG_ENTER("end_server"); + if (mysql->net.vio != 0) + { + init_sigpipe_variables + set_sigpipe(mysql); + vio_delete(mysql->net.vio); + reset_sigpipe(mysql); + mysql->net.vio= 0; /* Marker */ + } + net_end(&mysql->net); + free_old_query(mysql); + DBUG_VOID_RETURN; +} + +void mthd_my_skip_result(MYSQL *mysql) +{ + ulong pkt_len; + DBUG_ENTER("madb_skip_result"); + + do { + pkt_len= net_safe_read(mysql); + if (pkt_len == packet_error) + break; + } while (pkt_len > 8 || mysql->net.read_pos[0] != 254); + DBUG_VOID_RETURN; +} + +void STDCALL +mysql_free_result(MYSQL_RES *result) +{ + DBUG_ENTER("mysql_free_result"); + DBUG_PRINT("enter",("mysql_res: %lx",result)); + if (result) + { + if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) + { + result->handle->methods->db_skip_result(result->handle); + result->handle->status=MYSQL_STATUS_READY; + } + free_rows(result->data); + if (result->fields) + free_root(&result->field_alloc,MYF(0)); + if (result->row) + my_free(result->row); + my_free(result); + } + DBUG_VOID_RETURN; +} + + +/**************************************************************************** +** Get options from my.cnf +****************************************************************************/ + +static const char *default_options[]= +{ + "port","socket","compress","password","pipe", "timeout", "user", + "init-command", "host", "database", "debug", "return-found-rows", + "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", + "character-sets-dir", "default-character-set", "interactive-timeout", + "connect-timeout", "local-infile", "disable-local-infile", + "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", "database-type", + "ssl-fp", "ssl-fp-list", "bind-address", + NULL +}; + +enum option_val +{ + OPT_port=1, OPT_socket, OPT_compress, OPT_password, OPT_pipe, + OPT_timeout, OPT_user, OPT_init_command, OPT_host, OPT_database, + OPT_debug, OPT_return_found_rows, OPT_ssl_key, OPT_ssl_cert, + OPT_ssl_ca, OPT_ssl_capath, OPT_charset_dir, + OPT_charset_name, OPT_interactive_timeout, + OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile, + 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_db_type, + OPT_ssl_fp, OPT_ssl_fp_list, OPT_bind_address +}; + +#define CHECK_OPT_EXTENSION_SET(OPTS)\ + if (!(OPTS)->extension) \ + (OPTS)->extension= (struct st_mysql_options_extension *) \ + my_malloc(sizeof(struct st_mysql_options_extension), \ + MYF(MY_WME | MY_ZEROFILL)); \ + +#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ + if (IS_STRING) { \ + my_free((OPTS)->extension->KEY); \ + (OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \ + } else \ + (OPTS)->extension->KEY= (VAL); + +static TYPELIB option_types={array_elements(default_options)-1, + "options",default_options}; + +const char *protocol_names[]= {"TCP", "SOCKED", "PIPE", "MEMORY", NULL}; +static TYPELIB protocol_types= {array_elements(protocol_names)-1, + "protocol names", + protocol_names}; + +static void options_add_initcommand(struct st_mysql_options *options, + const char *init_cmd) +{ + char *insert= my_strdup(init_cmd, MYF(MY_WME)); + if (!options->init_command) + { + options->init_command= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY), + MYF(MY_WME)); + my_init_dynamic_array(options->init_command, sizeof(char*), 5, 5); + } + + if (insert_dynamic(options->init_command, (gptr)&insert)) + my_free(insert); +} + + +static void mysql_read_default_options(struct st_mysql_options *options, + const char *filename,const char *group) +{ + int argc; + char *argv_buff[1],**argv; + const char *groups[3]; + DBUG_ENTER("mysql_read_default_options"); + DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); + + argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; + groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0; + + load_defaults(filename, groups, &argc, &argv); + if (argc != 1) /* If some default option */ + { + char **option=argv; + while (*++option) + { + /* DBUG_PRINT("info",("option: %s",option[0])); */ + if (option[0][0] == '-' && option[0][1] == '-') + { + char *end=strcend(*option,'='); + char *opt_arg=0; + if (*end) + { + opt_arg=end+1; + *end=0; /* Remove '=' */ + } + /* Change all '_' in variable name to '-' */ + for (end= *option ; *(end= strcend(end,'_')) ; ) + *end= '-'; + switch (find_type(*option+2,&option_types,2)) { + case OPT_port: + if (opt_arg) + options->port=atoi(opt_arg); + break; + case OPT_socket: + if (opt_arg) + { + my_free(options->unix_socket); + options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_compress: + options->compress=1; + break; + case OPT_password: + if (opt_arg) + { + my_free(options->password); + options->password=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_pipe: + options->named_pipe=1; /* Force named pipe */ + break; + case OPT_connect_timeout: + case OPT_timeout: + if (opt_arg) + options->connect_timeout=atoi(opt_arg); + break; + case OPT_user: + if (opt_arg) + { + my_free(options->user); + options->user=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_init_command: + if (opt_arg) + options_add_initcommand(options, opt_arg); + break; + case OPT_host: + if (opt_arg) + { + my_free(options->host); + options->host=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_database: + if (opt_arg) + { + my_free(options->db); + options->db=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_debug: + mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace"); + break; + case OPT_return_found_rows: + options->client_flag|=CLIENT_FOUND_ROWS; + break; +#ifdef HAVE_OPENSSL + case OPT_ssl_key: + 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); + options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_ca: + 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); + options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_cipher: + break; + case OPT_ssl_fp: + OPT_SET_EXTENDED_VALUE(options, ssl_fp, opt_arg, 1); + break; + case OPT_ssl_fp_list: + OPT_SET_EXTENDED_VALUE(options, ssl_fp_list, opt_arg, 1); + break; +#else + case OPT_ssl_key: + case OPT_ssl_cert: + case OPT_ssl_ca: + case OPT_ssl_capath: + case OPT_ssl_cipher: + case OPT_ssl_fp: + case OPT_ssl_fp_list: + break; +#endif /* HAVE_OPENSSL */ + case OPT_charset_dir: + my_free(options->charset_dir); + options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_charset_name: + my_free(options->charset_name); + options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_interactive_timeout: + options->client_flag|= CLIENT_INTERACTIVE; + break; + case OPT_local_infile: + if (!opt_arg || atoi(opt_arg) != 0) + options->client_flag|= CLIENT_LOCAL_FILES; + else + options->client_flag&= ~CLIENT_LOCAL_FILES; + break; + case OPT_disable_local_infile: + options->client_flag&= CLIENT_LOCAL_FILES; + break; + case OPT_max_allowed_packet: + if(opt_arg) + options->max_allowed_packet= atoi(opt_arg); + break; + case OPT_protocol: + options->protocol= find_type(opt_arg, &protocol_types, 0); +#ifndef _WIN32 + if (options->protocol < 0 || options->protocol > 1) +#else + if (options->protocol < 0) +#endif + { + fprintf(stderr, "Unknown or unsupported protocol %s", opt_arg); + } + break; + case OPT_shared_memory_base_name: + /* todo */ + break; + case OPT_multi_results: + options->client_flag|= CLIENT_MULTI_RESULTS; + break; + case OPT_multi_statements: + case OPT_multi_queries: + options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS; + break; + case OPT_report_data_truncation: + if (opt_arg) + options->report_data_truncation= atoi(opt_arg); + else + options->report_data_truncation= 1; + break; + case OPT_secure_auth: + options->secure_auth= 1; + break; + case OPT_plugin_dir: + { + char directory[FN_REFLEN]; + if (strlen(opt_arg) >= FN_REFLEN) + opt_arg[FN_REFLEN]= 0; + if (!my_realpath(directory, opt_arg, 0)) + OPT_SET_EXTENDED_VALUE(options, plugin_dir, convert_dirname(directory), 1); + } + break; + case OPT_default_auth: + OPT_SET_EXTENDED_VALUE(options, default_auth, opt_arg, 1); + break; + case OPT_bind_address: + my_free(options->bind_address); + options->bind_address= my_strdup(opt_arg, MYF(MY_WME)); + break; + default: + DBUG_PRINT("warning",("unknown option: %s",option[0])); + } + } + } + } + free_defaults(argv); + DBUG_VOID_RETURN; +} + + +/*************************************************************************** +** Change field rows to field structs +***************************************************************************/ + +static size_t rset_field_offsets[]= { + OFFSET(MYSQL_FIELD, catalog), + OFFSET(MYSQL_FIELD, catalog_length), + OFFSET(MYSQL_FIELD, db), + OFFSET(MYSQL_FIELD, db_length), + OFFSET(MYSQL_FIELD, table), + OFFSET(MYSQL_FIELD, table_length), + OFFSET(MYSQL_FIELD, org_table), + OFFSET(MYSQL_FIELD, org_table_length), + OFFSET(MYSQL_FIELD, name), + OFFSET(MYSQL_FIELD, name_length), + OFFSET(MYSQL_FIELD, org_name), + OFFSET(MYSQL_FIELD, org_name_length) +}; + +MYSQL_FIELD * +unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, + my_bool default_value, my_bool long_flag_protocol) +{ + MYSQL_ROWS *row; + MYSQL_FIELD *field,*result; + char *p; + unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2; + + DBUG_ENTER("unpack_fields"); + field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); + if (!result) + DBUG_RETURN(0); + + for (row=data->data; row ; row = row->next,field++) + { + if (field >= result + fields) + goto error; + for (i=0; i < field_count; i++) + { + switch(row->data[i][0]) { + case 0: + *(char **)(((char *)field) + rset_field_offsets[i*2])= strdup_root(alloc, ""); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= 0; + break; + default: + *(char **)(((char *)field) + rset_field_offsets[i*2])= + strdup_root(alloc, (char *)row->data[i]); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= + (uint)(row->data[i+1] - row->data[i] - 1); + break; + } + } + + p= (char *)row->data[6]; + /* filler */ + field->charsetnr= uint2korr(p); + p+= 2; + field->length= (uint) uint4korr(p); + p+= 4; + field->type= (enum enum_field_types)uint1korr(p); + p++; + field->flags= uint2korr(p); + p+= 2; + field->decimals= (uint) p[0]; + p++; + + /* filler */ + p+= 2; + + if (INTERNAL_NUM_FIELD(field)) + field->flags|= NUM_FLAG; + + if (default_value && row->data[7]) + { + field->def=strdup_root(alloc,(char*) row->data[7]); + } + else + field->def=0; + field->max_length= 0; + } + if (field < result + fields) + goto error; + free_rows(data); /* Free old data */ + DBUG_RETURN(result); +error: + free_rows(data); + free_root(alloc, MYF(0)); + return(0); +} + + +/* Read all rows (fields or data) from server */ + +MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + uint fields) +{ + uint field; + ulong pkt_len; + ulong len; + uchar *cp; + char *to, *end_to; + MYSQL_DATA *result; + MYSQL_ROWS **prev_ptr,*cur; + NET *net = &mysql->net; + DBUG_ENTER("madb_read_rows"); + + if ((pkt_len= net_safe_read(mysql)) == packet_error) + DBUG_RETURN(0); + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */ + result->alloc.min_malloc=sizeof(MYSQL_ROWS); + prev_ptr= &result->data; + result->rows=0; + result->fields=fields; + + while (*(cp=net->read_pos) != 254 || pkt_len >= 8) + { + result->rows++; + if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc, + sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) + alloc_root(&result->alloc, + (fields+1)*sizeof(char *)+fields+pkt_len)))) + { + free_rows(result); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + *prev_ptr=cur; + prev_ptr= &cur->next; + to= (char*) (cur->data+fields+1); + end_to=to+fields+pkt_len-1; + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH) + { /* null field */ + cur->data[field] = 0; + } + else + { + cur->data[field] = to; + if (len > (ulong) (end_to - to)) + { + free_rows(result); + SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + memcpy(to,(char*) cp,len); to[len]=0; + to+=len+1; + cp+=len; + if (mysql_fields) + { + if (mysql_fields[field].max_length < len) + mysql_fields[field].max_length=len; + } + } + } + cur->data[field]=to; /* End of last field */ + if ((pkt_len=net_safe_read(mysql)) == packet_error) + { + free_rows(result); + DBUG_RETURN(0); + } + } + *prev_ptr=0; /* last pointer is null */ + /* save status */ + if (pkt_len > 1) + { + cp++; + mysql->warning_count= uint2korr(cp); + cp+= 2; + mysql->server_status= uint2korr(cp); + } + DBUG_PRINT("exit",("Got %d rows",result->rows)); + DBUG_RETURN(result); +} + + +/* +** Read one row. Uses packet buffer as storage for fields. +** When next packet is read, the previous field values are destroyed +*/ + + +int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) +{ + uint field; + ulong pkt_len,len; + uchar *pos,*prev_pos, *end_pos; + + if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error) + return -1; + + if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) + { + mysql->warning_count= uint2korr(mysql->net.read_pos + 1); + mysql->server_status= uint2korr(mysql->net.read_pos + 3); + return 1; /* End of data */ + } + prev_pos= 0; /* allowed to write at packet[-1] */ + pos=mysql->net.read_pos; + end_pos=pos+pkt_len; + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH) + { /* null field */ + row[field] = 0; + *lengths++=0; + } + else + { + if (len > (ulong) (end_pos - pos)) + { + mysql->net.last_errno=CR_UNKNOWN_ERROR; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + return -1; + } + row[field] = (char*) pos; + pos+=len; + *lengths++=len; + } + if (prev_pos) + *prev_pos=0; /* Terminate prev field */ + prev_pos=pos; + } + row[field]=(char*) prev_pos+1; /* End of last field */ + *prev_pos=0; /* Terminate last field */ + return 0; +} + +/**************************************************************************** +** Init MySQL structure or allocate one +****************************************************************************/ + +MYSQL * STDCALL +mysql_init(MYSQL *mysql) +{ + if (mysql_server_init(0, NULL, NULL)) + return NULL; + if (!mysql) + { + if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL)))) + return 0; + mysql->free_me=1; + mysql->net.vio= 0; + } + else + bzero((char*) (mysql),sizeof(*(mysql))); + mysql->options.connect_timeout=CONNECT_TIMEOUT; + mysql->charset= default_charset_info; + strmov(mysql->net.sqlstate, "00000"); + mysql->net.last_error[0]= mysql->net.last_errno= 0; +#if defined(SIGPIPE) && defined(THREAD) && !defined(_WIN32) + if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) + (void) signal(SIGPIPE,pipe_sig_handler); +#endif + +/* + Only enable LOAD DATA INFILE by default if configured with + --enable-local-infile +*/ +#ifdef ENABLED_LOCAL_INFILE + mysql->options.client_flag|= CLIENT_LOCAL_FILES; +#endif + mysql->reconnect= 0; + return mysql; +} + + + +int STDCALL +mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, + const char *ca, const char *capath, const char *cipher) +{ + #ifdef HAVE_OPENSSL + mysql->options.use_ssl= 1; + return (mysql_optionsv(mysql, MYSQL_OPT_SSL_KEY, key) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CERT, cert) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CA, ca) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CAPATH, capath) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CIPHER, cipher)) ? 1 : 0; +#else + return 0; +#endif + +} + +/************************************************************************** +**************************************************************************/ + +const char * STDCALL +mysql_get_ssl_cipher(MYSQL *mysql) +{ +#ifdef HAVE_OPENSSL + if (mysql->net.vio && mysql->net.vio->ssl) + { + return SSL_get_cipher_name(mysql->net.vio->ssl); + } +#endif + return(NULL); +} + +/************************************************************************** +** Free strings in the SSL structure and clear 'use_ssl' flag. +** NB! Errors are not reported until you do mysql_real_connect. +**************************************************************************/ + +/************************************************************************** +** Connect to sql server +** If host == 0 then use localhost +**************************************************************************/ + +MYSQL * STDCALL +mysql_connect(MYSQL *mysql,const char *host, + const char *user, const char *passwd) +{ + MYSQL *res; + mysql=mysql_init(mysql); /* Make it thread safe */ + { + DBUG_ENTER("mysql_connect"); + if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) + { + if (mysql->free_me) + my_free(mysql); + } + DBUG_RETURN(res); + } +} + +uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer) +{ + if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS) + { + buffer= mysql_net_store_length((unsigned char *)buffer, (mysql->options.extension) ? + mysql->options.extension->connect_attrs_len : 0); + if (mysql->options.extension && + hash_inited(&mysql->options.extension->connect_attrs)) + { + uint i; + for (i=0; i < mysql->options.extension->connect_attrs.records; i++) + { + size_t len; + uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); + + len= *(size_t *)p; + buffer= mysql_net_store_length(buffer, len); + p+= sizeof(size_t); + memcpy(buffer, p, len); + buffer+= len; + p+= len; + len= *(size_t *)p; + buffer= mysql_net_store_length(buffer, len); + p+= sizeof(size_t); + memcpy(buffer, p, len); + buffer+= len; + } + } + } + return buffer; +} + +/** set some default attributes */ +static my_bool +ma_set_connect_attrs(MYSQL *mysql) +{ + char buffer[255]; + int rc= 0; + + rc= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os") + +#ifdef _WIN32 + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread") + +#endif + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_server_host"); + + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "libmariadb") + + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_version", MARIADB_PACKAGE_VERSION) + + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_os", MARIADB_SYSTEM_TYPE); + +#ifdef _WIN32 + snprintf(buffer, 255, "%lu", (ulong) GetCurrentThreadId()); + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buffer); + snprintf(buffer, 255, "%lu", (ulong) GetCurrentProcessId()); +#else + snprintf(buffer, 255, "%lu", (ulong) getpid()); +#endif + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buffer); + + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_platform", MARIADB_MACHINE_TYPE); + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", mysql->host); + + return(test(rc>0)); +} + +/* +** Note that the mysql argument must be initialized with mysql_init() +** before calling mysql_real_connect ! +*/ + +MYSQL * STDCALL +mysql_real_connect(MYSQL *mysql, const char *host, const char *user, + const char *passwd, const char *db, + uint port, const char *unix_socket,unsigned long client_flag) +{ + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + + return mysql->methods->db_connect(mysql, host, user, passwd, + db, port, unix_socket, client_flag); +} + +MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, + const char *passwd, const char *db, + uint port, const char *unix_socket, unsigned long client_flag) +{ + char buff[NAME_LEN+USERNAME_LENGTH+100]; + char *end, *end_pkt, *host_info, + *charset_name= NULL; + my_socket sock; + char *scramble_data; + const char *scramble_plugin; + uint pkt_length, scramble_len, pkt_scramble_len= 0; + NET *net= &mysql->net; +#ifdef _WIN32 + HANDLE hPipe=INVALID_HANDLE_VALUE; +#endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un UNIXaddr; +#endif + time_t start_t= time(NULL); + init_sigpipe_variables + DBUG_ENTER("mysql_real_connect"); + + DBUG_PRINT("enter",("host: %s db: %s user: %s", + host ? host : "(Null)", + db ? db : "(Null)", + user ? user : "(Null)")); + + ma_set_connect_attrs(mysql); + + if (net->vio) /* check if we are already connected */ + { + SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + + /* use default options */ + if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) + { + mysql_read_default_options(&mysql->options, + (mysql->options.my_cnf_file ? + mysql->options.my_cnf_file : "my"), + mysql->options.my_cnf_group); + 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; + } + + /* Some empty-string-tests are done because of ODBC */ + if (!host || !host[0]) + host=mysql->options.host; + if (!user || !user[0]) + user=mysql->options.user; + if (!passwd) + { + passwd=mysql->options.password; +#ifndef DONT_USE_MYSQL_PWD + if (!passwd) + passwd=getenv("MYSQL_PWD"); /* get it from environment (haneke) */ +#endif + } + if (!db || !db[0]) + db=mysql->options.db; + if (!port) + port=mysql->options.port; + if (!unix_socket) + unix_socket=mysql->options.unix_socket; + + + /* Since 5.0.3 reconnect is not enabled by default!! + mysql->reconnect=1; */ + mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + + /* + ** Grab a socket and connect it to the server + */ + +#if defined(HAVE_SYS_UN_H) + if ((!host || strcmp(host,LOCAL_HOST) == 0) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP && + (unix_socket || mysql_unix_port)) + { + 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) + { + net->last_errno=CR_SOCKET_CREATE_ERROR; + sprintf(net->last_error,ER(net->last_errno),socket_errno); + goto error; + } + net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); + bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); + UNIXaddr.sun_family = AF_UNIX; + strmov(UNIXaddr.sun_path, unix_socket); + if (connect_sync_or_async(mysql, net, sock, + (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr))) + { + DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno)); + my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR), + unix_socket, socket_errno); + goto error; + } + if (socket_block(sock, 1) == SOCKET_ERROR) + goto error; + } + else +#elif defined(_WIN32) + { + if ((unix_socket || + (!host && is_NT()) || + (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0) || + mysql->options.named_pipe || + !have_tcpip) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP) + { + sock=0; + if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout, + (char**) &host, (char**) &unix_socket)) == + INVALID_HANDLE_VALUE) + { + DBUG_PRINT("error", + ("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d", + host ? host : "<null>", + unix_socket ? unix_socket : "<null>", + (int) mysql->options.named_pipe, + (int) have_tcpip)); + if (mysql->options.named_pipe || + (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || + (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE))) + goto error; /* User only requested named pipes */ + /* Try also with TCP/IP */ + } + else + { + net->vio=vio_new_win32pipe(hPipe); + sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host, + unix_socket); + } + } + } + if (hPipe == INVALID_HANDLE_VALUE) +#endif + { + struct addrinfo hints, *save_res= 0, *res= 0, + *bind_res= 0, *bres= 0; + char server_port[NI_MAXSERV]; + int gai_rc, bind_gai_rc; + int rc; +#ifdef _WIN32 + DWORD wait_gai; +#else + unsigned int wait_gai; +#endif + + unix_socket=0; /* This is not used */ + if (!port) + port=mysql_port; + if (!host) + host=LOCAL_HOST; + sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host); + bzero(&server_port, NI_MAXSERV); + + DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); + + my_snprintf(server_port, NI_MAXSERV, "%d", port); + + /* set hints for getaddrinfo */ + bzero(&hints, sizeof(hints)); + hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */ + hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */ + hints.ai_socktype= SOCK_STREAM; + + /* if client has multiple interfaces, we will bind socket to given + * bind_address */ + if (mysql->options.bind_address) + { + wait_gai= 1; + while ((bind_gai_rc= getaddrinfo(mysql->options.bind_address, 0, &hints, &bind_res)) == EAI_AGAIN) + { + unsigned int timeout= (mysql->options.connect_timeout) ? mysql->options.connect_timeout : DNS_TIMEOUT; + if (time(NULL) - start_t > timeout) + break; +#ifndef _WIN32 + usleep(wait_gai); +#else + Sleep(wait_gai); +#endif + wait_gai*= 2; + } + if (bind_gai_rc != 0 || !bind_res) + { +#ifndef _WIN32 + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), mysql->options.bind_address, + bind_gai_rc == EAI_SYSTEM ? errno : bind_gai_rc); +#else + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), mysql->options.bind_address, + WSAGetLastError()); +#endif + goto error; + } + } + + + /* Get the address information for the server using getaddrinfo() */ + wait_gai= 1; + while ((gai_rc= getaddrinfo(host, server_port, &hints, &res)) == EAI_AGAIN) + { + unsigned int timeout= (mysql->options.connect_timeout) ? mysql->options.connect_timeout : DNS_TIMEOUT; + if (time(NULL) - start_t > timeout) + break; +#ifndef _WIN32 + usleep(wait_gai); +#else + Sleep(wait_gai); +#endif + wait_gai*= 2; + } + if (gai_rc != 0) + { + if (bind_res) + freeaddrinfo(bind_res); +#ifndef _WIN32 + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), host, + gai_rc == EAI_SYSTEM ? errno : gai_rc); +#else + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), host, + WSAGetLastError()); +#endif + goto error; + } + + /* res is a linked list of addresses. If connect to an address fails we will not return + an error, instead we will try the next address */ + for (save_res= res; save_res; save_res= save_res->ai_next) + { + sock= socket(save_res->ai_family, save_res->ai_socktype, + save_res->ai_protocol); + if (sock == SOCKET_ERROR) + /* we do error handling after for loop only for last call */ + continue; + if (bind_res) + { + for (bres= bind_res; bres; bres= bres->ai_next) + { + if (!(rc= bind(sock, bres->ai_addr, (int)bres->ai_addrlen))) + break; + } + if (rc) + { + closesocket(sock); + continue; + } + } + if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, FALSE))) + { + my_set_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + closesocket(sock); + freeaddrinfo(res); + if (bind_res) + freeaddrinfo(bind_res); + goto error; + } + rc= connect_sync_or_async(mysql, net, sock, + save_res->ai_addr, save_res->ai_addrlen); + if (!rc) + { + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + break; + else if (socket_block(sock, 0) == SOCKET_ERROR) + { + closesocket(sock); + continue; + } + break; /* success! */ + } + vio_delete(mysql->net.vio); + mysql->net.vio= NULL; + } + + freeaddrinfo(res); + if (bind_res) + freeaddrinfo(bind_res); + + if (sock == SOCKET_ERROR) + { + my_set_error(mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR), + socket_errno); + goto error; + } + + /* last call to connect 2 failed */ + if (rc) + { + my_set_error(mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR), + host, socket_errno); + goto error; + } + if (socket_block(sock, 1) == SOCKET_ERROR) + goto error; + } + + if (mysql->options.extension && mysql->options.extension->async_context) + net->vio->async_context= mysql->options.extension->async_context; + + if (!net->vio || my_net_init(net, net->vio)) + { + vio_delete(net->vio); + net->vio = 0; + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + vio_keepalive(net->vio,TRUE); + strmov(mysql->net.sqlstate, "00000"); + + + /* set read timeout */ + vio_read_timeout(net->vio, mysql->options.read_timeout); + + /* set write timeout */ + vio_write_timeout(net->vio, mysql->options.write_timeout); + + /* Get version info */ + mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ + if (mysql->options.connect_timeout > 0 && + vio_wait_or_timeout(net->vio, TRUE, mysql->options.connect_timeout * 1000) < 1) + { + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "handshake: waiting for inital communication packet", + errno); + goto error; + } + if ((pkt_length=net_safe_read(mysql)) == packet_error) + { + if (mysql->net.last_errno == CR_SERVER_LOST) + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "handshake: reading inital communication packet", + errno); + + goto error; + } + end= (char *)net->read_pos; + end_pkt= (char *)net->read_pos + pkt_length; + + /* Check if version of protocoll matches current one */ + + mysql->protocol_version= end[0]; + end++; + + /* Check if server sends an error */ + if (mysql->protocol_version == 0XFF) + { + net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error), + &net->last_errno, net->sqlstate); + /* fix for bug #26426 */ + if (net->last_errno == 1040) + memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH); + goto error; + } + + DBUG_DUMP("packet",net->read_pos,10); + DBUG_PRINT("info",("mysql protocol version %d, server=%d", + PROTOCOL_VERSION, mysql->protocol_version)); + if (mysql->protocol_version < PROTOCOL_VERSION) + { + net->last_errno= CR_VERSION_ERROR; + sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version, + PROTOCOL_VERSION); + goto error; + } + /* Save connection information */ + if (!user) user=""; + if (!passwd) passwd=""; + + if (!my_multi_malloc(MYF(0), + &mysql->host_info, (uint) strlen(host_info)+1, + &mysql->host, (uint) strlen(host)+1, + &mysql->unix_socket,unix_socket ? + (uint) strlen(unix_socket)+1 : (uint) 1, + &mysql->server_version, (uint) (end - (char*) net->read_pos), + NullS) || + !(mysql->user=my_strdup(user,MYF(0))) || + !(mysql->passwd=my_strdup(passwd,MYF(0)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + strmov(mysql->host_info,host_info); + strmov(mysql->host,host); + if (unix_socket) + strmov(mysql->unix_socket,unix_socket); + else + mysql->unix_socket=0; + mysql->port=port; + client_flag|=mysql->options.client_flag; + + if (strncmp(end, "5.5.5-", 6) == 0) + { + if (!(mysql->server_version= my_strdup(end + 6, 0))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + } + else + { + if (!(mysql->server_version= my_strdup(end, MYF(0)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + } + end+= strlen(end) + 1; + + mysql->thread_id=uint4korr(end); + end+=4; + + /* This is the first part of scramble packet. In 4.1 and later + a second package will follow later */ + scramble_data= end; + scramble_len= SCRAMBLE_LENGTH_323 + 1; + scramble_plugin= old_password_plugin_name; + end+= SCRAMBLE_LENGTH_323; + + /* 1st pad */ + end++; + + if (end + 1<= end_pkt) + { + mysql->server_capabilities=uint2korr(end); + } + + /* mysql 5.5 protocol */ + if (end + 18 <= end_pkt) + { + mysql->server_language= uint1korr(end + 2); + mysql->server_status= uint2korr(end + 3); + mysql->server_capabilities|= uint2korr(end + 5) << 16; + pkt_scramble_len= uint1korr(end + 7); + } + /* pad 2 */ + end+= 18; + + /* second scramble package */ + if (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 <= end_pkt) + { + memcpy(end - SCRAMBLE_LENGTH_323, scramble_data, SCRAMBLE_LENGTH_323); + scramble_data= end - SCRAMBLE_LENGTH_323; + if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + { + scramble_len= pkt_scramble_len; + scramble_plugin= scramble_data + scramble_len; + if (scramble_data + scramble_len > end_pkt) + scramble_len= (uint)(end_pkt - scramble_data); + } else + { + scramble_len= (uint)(end_pkt - scramble_data); + scramble_plugin= native_password_plugin_name; + } + } else + { + mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION; + if (mysql->options.secure_auth) + { + SET_CLIENT_ERROR(mysql, CR_SECURE_AUTH, unknown_sqlstate, 0); + goto error; + } + } + + + /* Set character set */ + if (mysql->options.charset_name) + mysql->charset= mysql_find_charset_name(mysql->options.charset_name); + else + mysql->charset=default_charset_info; + + if (!mysql->charset) + { + net->last_errno=CR_CANT_READ_CHARSET; + sprintf(net->last_error,ER(net->last_errno), + charset_name ? charset_name : "unknown", + "compiled_in"); + goto error; + } + + mysql->client_flag= client_flag; + + if (run_plugin_auth(mysql, scramble_data, scramble_len, + scramble_plugin, db)) + goto error; + + if (mysql->client_flag & CLIENT_COMPRESS) + net->compress= 1; + + /* last part: select default db */ + if (db && !mysql->db) + { + if (mysql_select_db(mysql, db)) + { + my_set_error(mysql, CR_SERVER_LOST, unknown_sqlstate, + ER(CR_SERVER_LOST_EXTENDED), + "Setting intital database", + errno); + goto error; + } + } + + DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d client_flag: %d", + mysql->server_version,mysql->server_capabilities, + mysql->server_status, client_flag)); + + if (mysql->options.init_command) + { + char **begin= (char **)mysql->options.init_command->buffer; + char **end= begin + mysql->options.init_command->elements; + + /* Avoid reconnect in mysql_real_connect */ + my_bool save_reconnect= mysql->reconnect; + mysql->reconnect= 0; + + for (;begin < end; begin++) + { + if (mysql_real_query(mysql, *begin, (unsigned long)strlen(*begin))) + goto error; + + /* check if query produced a result set */ + do { + MYSQL_RES *res; + if ((res= mysql_use_result(mysql))) + mysql_free_result(res); + } while (!mysql_next_result(mysql)); + } + mysql->reconnect= save_reconnect; + } + + strmov(mysql->net.sqlstate, "00000"); + + DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); + reset_sigpipe(mysql); + DBUG_RETURN(mysql); + +error: + reset_sigpipe(mysql); + DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error)); + { + /* Free alloced memory */ + end_server(mysql); + /* only free the allocated memory, user needs to call mysql_close */ + mysql_close_memory(mysql); + if (!(((ulong) client_flag) & CLIENT_REMEMBER_OPTIONS)) + mysql_close_options(mysql); + } + DBUG_RETURN(0); +} + + +static my_bool mysql_reconnect(MYSQL *mysql) +{ + MYSQL tmp_mysql; + LIST *li_stmt= mysql->stmts; + DBUG_ENTER("mysql_reconnect"); + + if (!mysql->reconnect || + (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info) + { + /* Allov reconnect next time */ + mysql->server_status&= ~SERVER_STATUS_IN_TRANS; + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + mysql_init(&tmp_mysql); + tmp_mysql.options=mysql->options; + + /* don't reread options from configuration files */ + tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL; + + /* make sure that we reconnect with the same character set */ + if (!tmp_mysql.options.charset_name || + strcmp(tmp_mysql.options.charset_name, mysql->charset->csname)) + { + my_free(tmp_mysql.options.charset_name); + tmp_mysql.options.charset_name= my_strdup(mysql->charset->csname, MYF(MY_WME)); + } + + tmp_mysql.reconnect= mysql->reconnect; + if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, + mysql->db, mysql->port, mysql->unix_socket, + mysql->client_flag | CLIENT_REMEMBER_OPTIONS)) + { + /* don't free options (CONC-118) */ + memset(&tmp_mysql.options, 0, sizeof(struct st_mysql_options)); + my_set_error(mysql, tmp_mysql.net.last_errno, + tmp_mysql.net.sqlstate, + tmp_mysql.net.last_error); + mysql_close(&tmp_mysql); + DBUG_RETURN(1); + } + + /* reset the connection in all active statements + todo: check stmt->mysql in mysql_stmt* functions ! */ + for (;li_stmt;li_stmt= li_stmt->next) + { + MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data; + + if (stmt->state != MYSQL_STMT_INITTED) + { + stmt->state= MYSQL_STMT_INITTED; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + } + } + + tmp_mysql.free_me= mysql->free_me; + tmp_mysql.stmts= mysql->stmts; + mysql->stmts= NULL; + + /* Don't free options, we moved them to tmp_mysql */ + memset(&mysql->options, 0, sizeof(mysql->options)); + mysql->free_me=0; + mysql->stmts= NULL; + mysql_close(mysql); + *mysql=tmp_mysql; + mysql->reconnect= 1; + net_clear(&mysql->net); + mysql->affected_rows= ~(my_ulonglong) 0; + DBUG_RETURN(0); +} + + +/************************************************************************** +** Change user and database +**************************************************************************/ + +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db) +{ + const CHARSET_INFO *s_cs= mysql->charset; + char *s_user= mysql->user, + *s_passwd= mysql->passwd, + *s_db= mysql->db; + int rc; + + DBUG_ENTER("mysql_change_user"); + + if (!user) + user=""; + if (!passwd) + passwd=""; + if (!db) + db=""; + + if (mysql->options.charset_name) + mysql->charset =mysql_find_charset_name(mysql->options.charset_name); + else + mysql->charset=default_charset_info; + + mysql->user= (char *)user; + mysql->passwd= (char *)passwd; + mysql->db= (char *)db; + rc= run_plugin_auth(mysql, 0, 0, 0, db); + + if (rc==0) + { + LIST *li_stmt= mysql->stmts; + my_free(s_user); + my_free(s_passwd); + my_free(s_db); + + if (!(mysql->user= my_strdup(user,MYF(MY_WME))) || + !(mysql->passwd=my_strdup(passwd,MYF(MY_WME))) || + !(mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + rc= 1; + } + + for (;li_stmt;li_stmt= li_stmt->next) + { + MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data; + stmt->mysql= NULL; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + }/* detach stmts */ + mysql->stmts= NULL; + + } else + { + mysql->user= s_user; + mysql->passwd= s_passwd; + mysql->db= s_db; + mysql->charset= s_cs; + } + DBUG_RETURN(rc); +} + + +/************************************************************************** +** Set current database +**************************************************************************/ + +int STDCALL +mysql_select_db(MYSQL *mysql, const char *db) +{ + int error; + DBUG_ENTER("mysql_select_db"); + DBUG_PRINT("enter",("db: '%s'",db)); + + if ((error=simple_command(mysql,MYSQL_COM_INIT_DB,db,(uint) strlen(db),0,0))) + DBUG_RETURN(error); + my_free(mysql->db); + mysql->db=my_strdup(db,MYF(MY_WME)); + DBUG_RETURN(0); +} + + +/************************************************************************* +** Send a QUIT to the server and close the connection +** If handle is alloced by mysql connect free it. +*************************************************************************/ + +static void mysql_close_options(MYSQL *mysql) +{ + if (mysql->options.init_command) + { + char **begin= (char **)mysql->options.init_command->buffer; + char **end= begin + mysql->options.init_command->elements; + + for (;begin < end; begin++) + my_free(*begin); + delete_dynamic(mysql->options.init_command); + my_free(mysql->options.init_command); + } + 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.bind_address); + 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 (mysql->options.extension) + { + struct mysql_async_context *ctxt; + my_free(mysql->options.extension->plugin_dir); + my_free(mysql->options.extension->default_auth); + my_free(mysql->options.extension->db_driver); + my_free(mysql->options.extension->ssl_crl); + my_free(mysql->options.extension->ssl_crlpath); + my_free(mysql->options.extension->ssl_fp); + my_free(mysql->options.extension->ssl_fp_list); + if(hash_inited(&mysql->options.extension->connect_attrs)) + hash_free(&mysql->options.extension->connect_attrs); + if (mysql->options.extension && (ctxt = mysql->options.extension->async_context) != 0) + { + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + mysql->options.extension->async_context= 0; + } + + } + my_free(mysql->options.extension); + mysql->options.extension= 0; + /* clear all pointer */ + memset(&mysql->options, 0, sizeof(mysql->options)); +} + +static void mysql_close_memory(MYSQL *mysql) +{ + my_free(mysql->host_info); + my_free(mysql->user); + my_free(mysql->passwd); + my_free(mysql->db); + my_free(mysql->server_version); + mysql->host_info= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; +} + + + +void my_set_error(MYSQL *mysql, + unsigned int error_nr, + const char *sqlstate, + const char *format, + ...) +{ + va_list ap; + + DBUG_ENTER("my_set_error"); + + mysql->net.last_errno= error_nr; + strncpy(mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH); + va_start(ap, format); + my_vsnprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE, + format ? format : ER(error_nr), ap); + DBUG_PRINT("info", ("error(%d) %s", error_nr, mysql->net.last_error)); + va_end(ap); + DBUG_VOID_RETURN; +} + +void mysql_close_slow_part(MYSQL *mysql) +{ + if (mysql->net.vio) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + mysql->reconnect=0; + simple_command(mysql,MYSQL_COM_QUIT,NullS,0,1,0); + end_server(mysql); + } +} + +void STDCALL +mysql_close(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + DBUG_ENTER("mysql_close"); + if (mysql) /* Some simple safety */ + { + LIST *li_stmt= mysql->stmts; + + if (mysql->methods) + mysql->methods->db_close(mysql); + + /* reset the connection in all active statements + todo: check stmt->mysql in mysql_stmt* functions ! */ + for (;li_stmt;li_stmt= li_stmt->next) + { + stmt= (MYSQL_STMT *)li_stmt->data; + stmt->mysql= NULL; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + } + mysql_close_memory(mysql); + mysql_close_options(mysql); + mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + + /* Clear pointers for better safety */ + bzero((char*) &mysql->options,sizeof(mysql->options)); + mysql->net.vio= 0; + if (mysql->free_me) + my_free(mysql); + } + DBUG_VOID_RETURN; +} + + +/************************************************************************** +** Do a query. If query returned rows, free old rows. +** Read data by mysql_store_result or by repeat call of mysql_fetch_row +**************************************************************************/ + +int STDCALL +mysql_query(MYSQL *mysql, const char *query) +{ + return mysql_real_query(mysql,query, (uint) strlen(query)); +} + +/* + Send the query and return so we can do something else. + Needs to be followed by mysql_read_query_result() when we want to + finish processing it. +*/ + +int STDCALL +mysql_send_query(MYSQL* mysql, const char* query, unsigned long length) +{ + return simple_command(mysql, MYSQL_COM_QUERY, query, length, 1,0); +} + +int mthd_my_read_query_result(MYSQL *mysql) +{ + uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + ulong length; + DBUG_ENTER("mthd_my_read_query_result"); + + if (!mysql || (length = net_safe_read(mysql)) == packet_error) + DBUG_RETURN(1); + free_old_query(mysql); /* Free old result */ +get_info: + pos=(uchar*) mysql->net.read_pos; + if ((field_count= net_field_length(&pos)) == 0) + { + mysql->affected_rows= net_field_length_ll(&pos); + mysql->insert_id= net_field_length_ll(&pos); + mysql->server_status=uint2korr(pos); + pos+=2; + mysql->warning_count=uint2korr(pos); + pos+=2; + if (pos < mysql->net.read_pos+length && net_field_length(&pos)) + mysql->info=(char*) pos; + DBUG_RETURN(0); + } + if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ + { + int error= 0; + /* check if a callback was specified for load local infile */ + if (mysql->options.extension && mysql->options.extension->verify_local_infile) + { + if (mysql->options.extension->verify_local_infile(mysql->options.local_infile_userdata[1], (const char *)pos)) + { + my_set_error(mysql, EE_READ, SQLSTATE_UNKNOWN, "filename could not be verified"); + DBUG_RETURN(-1); + } + } + error=mysql_handle_local_infile(mysql, (char *)pos); + + if ((length=net_safe_read(mysql)) == packet_error || error) + DBUG_RETURN(-1); + goto get_info; /* Get info packet */ + } + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) + mysql->server_status|= SERVER_STATUS_IN_TRANS; + + mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ + if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + DBUG_RETURN(-1); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, + (uint) field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(0); +} + +my_bool STDCALL +mysql_read_query_result(MYSQL *mysql) +{ + return test(mysql->methods->db_read_query_result(mysql)) ? 1 : 0; +} + +int STDCALL +mysql_real_query(MYSQL *mysql, const char *query, unsigned long length) +{ + DBUG_ENTER("mysql_real_query"); + DBUG_PRINT("enter",("handle: %lx",mysql)); + DBUG_PRINT("query",("Query = \"%.255s\" length=%u",query, length)); + + free_old_query(mysql); + + if (simple_command(mysql,MYSQL_COM_QUERY,query,length,1,0)) + DBUG_RETURN(-1); + DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); +} + +/************************************************************************** +** Alloc result struct for buffered results. All rows are read to buffer. +** mysql_data_seek may be used. +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_store_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("mysql_store_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + mysql->status=MYSQL_STATUS_READY; /* server is ready */ + if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + result->eof=1; /* Marker for buffered */ + result->lengths=(ulong*) (result+1); + if (!(result->data=mysql->methods->db_read_rows(mysql,mysql->fields,mysql->field_count))) + { + my_free(result); + DBUG_RETURN(0); + } + mysql->affected_rows= result->row_count= result->data->rows; + result->data_cursor= result->data->data; + result->fields= mysql->fields; + result->field_alloc= mysql->field_alloc; + result->field_count= mysql->field_count; + result->current_field=0; + result->current_row=0; /* Must do a fetch first */ + mysql->fields=0; /* fields is now in result */ + DBUG_RETURN(result); /* Data fetched */ +} + + +/************************************************************************** +** Alloc struct for use with unbuffered reads. Data is fetched by domand +** when calling to mysql_fetch_row. +** mysql_data_seek is a noop. +** +** No other queries may be specified with the same MYSQL handle. +** There shouldn't be much processing per row because mysql server shouldn't +** have to wait for the client (and will not wait more than 30 sec/packet). +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_use_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("mysql_use_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(0); + result->lengths=(ulong*) (result+1); + if (!(result->row=(MYSQL_ROW) + my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) + { /* Ptrs: to one row */ + my_free(result); + DBUG_RETURN(0); + } + result->fields= mysql->fields; + result->field_alloc= mysql->field_alloc; + result->field_count= mysql->field_count; + result->current_field=0; + result->handle= mysql; + result->current_row= 0; + mysql->fields=0; /* fields is now in result */ + mysql->status=MYSQL_STATUS_USE_RESULT; + DBUG_RETURN(result); /* Data is read to be fetched */ +} + +/************************************************************************** +** Return next field of the query results +**************************************************************************/ +MYSQL_FIELD * STDCALL +mysql_fetch_field(MYSQL_RES *result) +{ + if (result->current_field >= result->field_count) + return(NULL); + return &result->fields[result->current_field++]; +} + +/************************************************************************** +** Return next row of the query results +**************************************************************************/ +MYSQL_ROW STDCALL +mysql_fetch_row(MYSQL_RES *res) +{ + DBUG_ENTER("mysql_fetch_row"); + if (!res) + return 0; + if (!res->data) + { /* Unbufferred fetch */ + if (!res->eof) + { + if (!(res->handle->methods->db_read_one_row(res->handle,res->field_count,res->row, res->lengths))) + { + res->row_count++; + DBUG_RETURN(res->current_row=res->row); + } + DBUG_PRINT("info",("end of data")); + res->eof=1; + res->handle->status=MYSQL_STATUS_READY; + /* Don't clear handle in mysql_free_results */ + res->handle=0; + } + DBUG_RETURN((MYSQL_ROW) NULL); + } + { + MYSQL_ROW tmp; + if (!res->data_cursor) + { + DBUG_PRINT("info",("end of data")); + DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL); + } + tmp = res->data_cursor->data; + res->data_cursor = res->data_cursor->next; + DBUG_RETURN(res->current_row=tmp); + } +} + +/************************************************************************** +** Get column lengths of the current row +** If one uses mysql_use_result, res->lengths contains the length information, +** else the lengths are calculated from the offset between pointers. +**************************************************************************/ + +ulong * STDCALL +mysql_fetch_lengths(MYSQL_RES *res) +{ + ulong *lengths,*prev_length; + char *start; + MYSQL_ROW column,end; + + if (!(column=res->current_row)) + return 0; /* Something is wrong */ + if (res->data) + { + start=0; + prev_length=0; /* Keep gcc happy */ + lengths=res->lengths; + for (end=column+res->field_count+1 ; column != end ; column++,lengths++) + { + if (!*column) + { + *lengths=0; /* Null */ + continue; + } + if (start) /* Found end of prev string */ + *prev_length= (uint) (*column-start-1); + start= *column; + prev_length=lengths; + } + } + return res->lengths; +} + +/************************************************************************** +** Move to a specific row and column +**************************************************************************/ + +void STDCALL +mysql_data_seek(MYSQL_RES *result, my_ulonglong row) +{ + MYSQL_ROWS *tmp=0; + DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + if (result->data) + for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ; + result->current_row=0; + result->data_cursor = tmp; +} + +/************************************************************************* +** put the row or field cursor one a position one got from mysql_row_tell() +** This doesn't restore any data. The next mysql_fetch_row or +** mysql_fetch_field will return the next row or field after the last used +*************************************************************************/ + +MYSQL_ROW_OFFSET STDCALL +mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row) +{ + MYSQL_ROW_OFFSET return_value=result->data_cursor; + result->current_row= 0; + result->data_cursor= row; + return return_value; +} + + +MYSQL_FIELD_OFFSET STDCALL +mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset) +{ + MYSQL_FIELD_OFFSET return_value=result->current_field; + result->current_field=field_offset; + return return_value; +} + +/***************************************************************************** +** List all databases +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_dbs(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_dbs"); + + append_wild(strmov(buff,"show databases"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/***************************************************************************** +** List all tables in a database +** If wild is given then only the tables matching wild is returned +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_tables(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_tables"); + + append_wild(strmov(buff,"show tables"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/************************************************************************** +** List all fields in a table +** If wild is given then only the fields matching wild is returned +** Instead of this use query: +** show fields in 'table' like "wild" +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) +{ + MYSQL_RES *result; + MYSQL_DATA *query; + char buff[257],*end; + DBUG_ENTER("mysql_list_fields"); + DBUG_PRINT("enter",("table: '%s' wild: '%s'",table,wild ? wild : "")); + + LINT_INIT(query); + + end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); + if (simple_command(mysql,MYSQL_COM_FIELD_LIST,buff,(uint) (end-buff),1,0) || + !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + DBUG_RETURN(NULL); + + free_old_query(mysql); + if (!(result = (MYSQL_RES *) my_malloc(sizeof(MYSQL_RES), + MYF(MY_WME | MY_ZEROFILL)))) + { + free_rows(query); + DBUG_RETURN(NULL); + } + result->field_alloc=mysql->field_alloc; + mysql->fields=0; + result->field_count = (uint) query->rows; + result->fields= unpack_fields(query,&result->field_alloc, + result->field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)); + result->eof=1; + DBUG_RETURN(result); +} + +/* List all running processes (threads) in server */ + +MYSQL_RES * STDCALL +mysql_list_processes(MYSQL *mysql) +{ + MYSQL_DATA *fields; + uint field_count; + uchar *pos; + DBUG_ENTER("mysql_list_processes"); + + LINT_INIT(fields); + if (simple_command(mysql,MYSQL_COM_PROCESS_INFO,0,0,0,0)) + DBUG_RETURN(0); + free_old_query(mysql); + pos=(uchar*) mysql->net.read_pos; + field_count=(uint) net_field_length(&pos); + if (!(fields = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,5))) + DBUG_RETURN(NULL); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(0); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(mysql_store_result(mysql)); +} + + +int STDCALL +mysql_create_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_createdb"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_CREATE_DB,db, (uint) strlen(db),0,0)); +} + + +int STDCALL +mysql_drop_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_drop_db"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_DROP_DB,db,(uint) strlen(db),0,0)); +} + +/* In 5.0 this version became an additional parameter shutdown_level */ +int STDCALL +mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) +{ + uchar s_level[2]; + DBUG_ENTER("mysql_shutdown"); + s_level[0]= (uchar)shutdown_level; + DBUG_RETURN(simple_command(mysql, MYSQL_COM_SHUTDOWN, (char *)s_level, 1, 0, 0)); +} + +int STDCALL +mysql_refresh(MYSQL *mysql,uint options) +{ + uchar bits[1]; + DBUG_ENTER("mysql_refresh"); + bits[0]= (uchar) options; + DBUG_RETURN(simple_command(mysql,MYSQL_COM_REFRESH,(char*) bits,1,0,0)); +} + +int STDCALL +mysql_kill(MYSQL *mysql,ulong pid) +{ + char buff[12]; + DBUG_ENTER("mysql_kill"); + int4store(buff,pid); + /* if we kill our own thread, reading the response packet will fail */ + DBUG_RETURN(simple_command(mysql,MYSQL_COM_PROCESS_KILL,buff,4,0,0)); +} + + +int STDCALL +mysql_dump_debug_info(MYSQL *mysql) +{ + DBUG_ENTER("mysql_dump_debug_info"); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_DEBUG,0,0,0,0)); +} + +char * STDCALL +mysql_stat(MYSQL *mysql) +{ + DBUG_ENTER("mysql_stat"); + if (simple_command(mysql,MYSQL_COM_STATISTICS,0,0,0,0)) + return mysql->net.last_error; + mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ + if (!mysql->net.read_pos[0]) + { + SET_CLIENT_ERROR(mysql, CR_WRONG_HOST_INFO , unknown_sqlstate, 0); + return mysql->net.last_error; + } + DBUG_RETURN((char*) mysql->net.read_pos); +} + + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + int rc; + DBUG_ENTER("mysql_ping"); + rc= simple_command(mysql,MYSQL_COM_PING,0,0,0,0); + + DBUG_RETURN(rc); +} + + +char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + +unsigned long STDCALL mysql_get_server_version(MYSQL *mysql) +{ + long major, minor, patch; + char *p; + + if (!(p = mysql->server_version)) { + return 0; + } + + major = strtol(p, &p, 10); + p += 1; /* consume the dot */ + minor = strtol(p, &p, 10); + p += 1; /* consume the dot */ + patch = strtol(p, &p, 10); + + return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch)); +} + + + +char * STDCALL +mysql_get_host_info(MYSQL *mysql) +{ + return(mysql->host_info); +} + + +uint STDCALL +mysql_get_proto_info(MYSQL *mysql) +{ + return (mysql->protocol_version); +} + +const char * STDCALL +mysql_get_client_info(void) +{ + return (char*) MYSQL_CLIENT_VERSION; +} + +static size_t get_store_length(size_t length) +{ + if (length < (size_t) L64(251)) + return 1; + if (length < (size_t) L64(65536)) + return 2; + if (length < (size_t) L64(16777216)) + return 3; + return 9; +} + +uchar *ma_get_hash_key(const uchar *hash_entry, + unsigned int *length, + my_bool not_used __attribute__((unused))) +{ + /* Hash entry has the following format: + Offset: 0 key length + sizeof(size_t) key + key_length + + sizeof(size_t) value length + value + */ + uchar *p= (uchar *)hash_entry; + size_t len=*((size_t*)p); + p+= sizeof(size_t); + *length= (uint)len; + return p; +} + +void ma_hash_free(void *p) +{ + my_free(p); +} + + +int STDCALL +mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) +{ + va_list ap; + void *arg1; + struct mysql_async_context *ctxt; + size_t stacksize; + + DBUG_ENTER("mysql_option"); + DBUG_PRINT("enter",("option: %d",(int) option)); + + va_start(ap, option); + + arg1= va_arg(ap, void *); + + switch (option) { + case MYSQL_OPT_CONNECT_TIMEOUT: + mysql->options.connect_timeout= *(uint*) arg1; + break; + case MYSQL_OPT_COMPRESS: + mysql->options.compress= 1; /* Remember for connect */ + mysql->options.client_flag|= CLIENT_COMPRESS; + break; + case MYSQL_OPT_NAMED_PIPE: + mysql->options.named_pipe=1; /* Force named pipe */ + break; + case MYSQL_OPT_BIND: + my_free(mysql->options.bind_address); + mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); + break; + case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/ + if (!arg1 || test(*(uint*) arg1)) + mysql->options.client_flag|= CLIENT_LOCAL_FILES; + else + mysql->options.client_flag&= ~CLIENT_LOCAL_FILES; + break; + case MYSQL_INIT_COMMAND: + options_add_initcommand(&mysql->options, (char *)arg1); + break; + case MYSQL_READ_DEFAULT_FILE: + my_free(mysql->options.my_cnf_file); + mysql->options.my_cnf_file=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_READ_DEFAULT_GROUP: + my_free(mysql->options.my_cnf_group); + mysql->options.my_cnf_group=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_SET_CHARSET_DIR: + /* not supported in this version. Since all character sets + are internally available, we don't throw an error */ + break; + case MYSQL_SET_CHARSET_NAME: + my_free(mysql->options.charset_name); + mysql->options.charset_name=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_OPT_RECONNECT: + mysql->reconnect= *(my_bool *)arg1; + break; + case MYSQL_OPT_PROTOCOL: +#ifdef _WIN32 + if (*(uint *)arg1 > MYSQL_PROTOCOL_PIPE) +#else + if (*(uint *)arg1 > MYSQL_PROTOCOL_SOCKET) +#endif + DBUG_RETURN(-1); + mysql->options.protocol= *(uint *)arg1; + break; + case MYSQL_OPT_READ_TIMEOUT: + mysql->options.read_timeout= *(uint *)arg1; + break; + case MYSQL_OPT_WRITE_TIMEOUT: + mysql->options.write_timeout= *(uint *)arg1; + break; + case MYSQL_REPORT_DATA_TRUNCATION: + mysql->options.report_data_truncation= *(uint *)arg1; + break; + case MYSQL_PROGRESS_CALLBACK: + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension) + mysql->options.extension->report_progress= + (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1; + break; + case MYSQL_PLUGIN_DIR: + OPT_SET_EXTENDED_VALUE(&mysql->options, plugin_dir, (char *)arg1, 1); + break; + case MYSQL_DEFAULT_AUTH: + OPT_SET_EXTENDED_VALUE(&mysql->options, default_auth, (char *)arg1, 1); + break; + case MYSQL_DATABASE_DRIVER: + { + MARIADB_DB_PLUGIN *db_plugin; + if (!(db_plugin= (MARIADB_DB_PLUGIN *)mysql_client_find_plugin(mysql, (char *)arg1, + MYSQL_CLIENT_DB_PLUGIN))) + break; + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)); + if (!mysql->options.extension->db_driver) + mysql->options.extension->db_driver= (MARIADB_DB_DRIVER *) + my_malloc(sizeof(MARIADB_DB_DRIVER), MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension && + mysql->options.extension->db_driver) + { + mysql->options.extension->db_driver->plugin = db_plugin; + mysql->options.extension->db_driver->buffer= NULL; + mysql->options.extension->db_driver->name= (char *)db_plugin->name; + mysql->methods= db_plugin->methods; + } + } + break; + case MYSQL_OPT_NONBLOCK: + if (mysql->options.extension && + (ctxt = mysql->options.extension->async_context) != 0) + { + /* + We must not allow changing the stack size while a non-blocking call is + suspended (as the stack is then in use). + */ + if (ctxt->suspended) + DBUG_RETURN(1); + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + } + if (!(ctxt= (struct mysql_async_context *) + my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + stacksize= 0; + if (arg1) + stacksize= *(const size_t *)arg1; + if (!stacksize) + stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE; + if (my_context_init(&ctxt->async_context, stacksize)) + { + my_free(ctxt); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + if (!mysql->options.extension) + if(!(mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + mysql->options.extension->async_context= ctxt; + if (mysql->net.vio) + mysql->net.vio->async_context= ctxt; + break; + + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + if (*(my_bool *)arg1) + mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT; + else + mysql->options.client_flag &= ~CLIENT_SSL_VERIFY_SERVER_CERT; + break; + case MYSQL_OPT_SSL_KEY: + my_free(mysql->options.ssl_key); + mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CERT: + my_free(mysql->options.ssl_cert); + mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CA: + my_free(mysql->options.ssl_ca); + mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CAPATH: + my_free(mysql->options.ssl_capath); + mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CIPHER: + my_free(mysql->options.ssl_cipher); + mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CRL: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crl, (char *)arg1, 1); + break; + case MYSQL_OPT_SSL_CRLPATH: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg1, 1); + break; + case MARIADB_OPT_SSL_FP: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp, (char *)arg1, 1); + break; + case MARIADB_OPT_SSL_FP_LIST: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp_list, (char *)arg1, 1); + break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + { + uchar *p; + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs) && + (p= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, + arg1 ? (uint)strlen((char *)arg1) : 0))) + { + size_t key_len= *(size_t *)p; + mysql->options.extension->connect_attrs_len-= key_len; + mysql->options.extension->connect_attrs_len-= get_store_length(key_len); + key_len= *(size_t *)(p + sizeof(size_t) + key_len); + mysql->options.extension->connect_attrs_len-= key_len; + mysql->options.extension->connect_attrs_len-= get_store_length(key_len); + hash_delete(&mysql->options.extension->connect_attrs, p); + } + } + break; + case MYSQL_OPT_CONNECT_ATTR_RESET: + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs)) + { + hash_free(&mysql->options.extension->connect_attrs); + mysql->options.extension->connect_attrs_len= 0; + } + break; + case MYSQL_OPT_CONNECT_ATTR_ADD: + { + uchar *buffer; + void *arg2= va_arg(ap, void *); + size_t key_len= arg1 ? strlen((char *)arg1) : 0, + value_len= arg2 ? strlen((char *)arg2) : 0; + size_t storage_len; + + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (!key_len || !value_len) + { + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0); + goto end; + } + storage_len= key_len + value_len + + get_store_length(key_len) + + get_store_length(value_len); + + if (!hash_inited(&mysql->options.extension->connect_attrs)) + { + if (_hash_init(&mysql->options.extension->connect_attrs, + 0, 0, 0, ma_get_hash_key, ma_hash_free, 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto end; + } + } + if ((buffer= (uchar *)my_malloc(2 * sizeof(size_t) + key_len + value_len, + MYF(MY_WME | MY_ZEROFILL)))) + { + uchar *p= buffer; + *((size_t *)p)= key_len; + p+= sizeof(size_t); + memcpy(p, arg1, key_len); + p+= key_len; + *((size_t *)p)= value_len; + p+= sizeof(size_t); + if (arg2) + memcpy(p, arg2, value_len); + + if (hash_insert(&mysql->options.extension->connect_attrs, buffer)) + { + my_free(buffer); + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0); + goto end; + } + mysql->options.extension->connect_attrs_len+= storage_len; + } + else + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto end; + } + } + break; + case MYSQL_ENABLE_CLEARTEXT_PLUGIN: + break; + case MYSQL_SECURE_AUTH: + mysql->options.secure_auth= *(my_bool *)arg1; + break; + case MARIADB_OPT_VERIFY_LOCAL_INFILE_CALLBACK: + { + void *arg2= va_arg(ap, void *); + CHECK_OPT_EXTENSION_SET(&mysql->options); + mysql->options.extension->verify_local_infile= (int (*)(void *, const char *))arg1; + if (arg2) + mysql->options.local_infile_userdata[1]= arg2; + } + break; + default: + va_end(ap); + DBUG_RETURN(-1); + } + va_end(ap); + DBUG_RETURN(0); +end: + va_end(ap); + DBUG_RETURN(1); +} + + +int STDCALL +mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) +{ + return mysql_optionsv(mysql, option, arg); +} + + +/**************************************************************************** +** Functions to get information from the MySQL structure +** These are functions to make shared libraries more usable. +****************************************************************************/ + +/* MYSQL_RES */ +my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res) +{ + return res->row_count; +} + +unsigned int STDCALL mysql_num_fields(MYSQL_RES *res) +{ + return res->field_count; +} + +/* deprecated */ +my_bool STDCALL mysql_eof(MYSQL_RES *res) +{ + return res->eof; +} + +MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr) +{ + return &(res)->fields[fieldnr]; +} + +MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res) +{ + return (res)->fields; +} + +MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res) +{ + return res->data_cursor; +} + +uint STDCALL mysql_field_tell(MYSQL_RES *res) +{ + return (res)->current_field; +} + +/* MYSQL */ + +unsigned int STDCALL mysql_field_count(MYSQL *mysql) +{ + return mysql->field_count; +} + +my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) +{ + return (mysql)->affected_rows; +} + +my_bool STDCALL mysql_autocommit(MYSQL *mysql, my_bool mode) +{ + DBUG_ENTER("mysql_autocommit"); + DBUG_RETURN((my_bool) mysql_real_query(mysql, (mode) ? "SET autocommit=1" : + "SET autocommit=0", 16)); +} + +my_bool STDCALL mysql_commit(MYSQL *mysql) +{ + DBUG_ENTER("mysql_commit"); + DBUG_RETURN((my_bool)mysql_real_query(mysql, "COMMIT", sizeof("COMMIT"))); +} + +my_bool STDCALL mysql_rollback(MYSQL *mysql) +{ + DBUG_ENTER("mysql_rollback"); + DBUG_RETURN((my_bool)mysql_real_query(mysql, "ROLLBACK", sizeof("ROLLBACK"))); +} + +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) +{ + return (mysql)->insert_id; +} + +uint STDCALL mysql_errno(MYSQL *mysql) +{ + return (mysql)->net.last_errno; +} + +const char * STDCALL mysql_error(MYSQL *mysql) +{ + return (mysql)->net.last_error; +} + +const char *STDCALL mysql_info(MYSQL *mysql) +{ + return (mysql)->info; +} + +my_bool STDCALL mysql_more_results(MYSQL *mysql) +{ + DBUG_ENTER("mysql_more_results"); + DBUG_RETURN(test(mysql->server_status & SERVER_MORE_RESULTS_EXIST)); +} + +int STDCALL mysql_next_result(MYSQL *mysql) +{ + DBUG_ENTER("mysql_next_result"); + + /* make sure communication is not blocking */ + if (mysql->status != MYSQL_STATUS_READY) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + + /* clear error, and mysql status variables */ + CLEAR_CLIENT_ERROR(mysql); + mysql->affected_rows = (ulonglong) ~0; + + if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); + } + + DBUG_RETURN(-1); +} + +ulong STDCALL mysql_thread_id(MYSQL *mysql) +{ + return (mysql)->thread_id; +} + +const char * STDCALL mysql_character_set_name(MYSQL *mysql) +{ + return mysql->charset->csname; +} + + +uint STDCALL mysql_thread_safe(void) +{ +#ifdef THREAD + return 1; +#else + return 0; +#endif +} + +/**************************************************************************** +** Some support functions +****************************************************************************/ + +/* +** Add escape characters to a string (blob?) to make it suitable for a insert +** to should at least have place for length*2+1 chars +** Returns the length of the to string +*/ + +ulong STDCALL +mysql_escape_string(char *to,const char *from, ulong length) +{ + return (ulong)mysql_cset_escape_slashes(default_charset_info, to, from, length); +} + +ulong STDCALL +mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, + ulong length) +{ + if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) + return (ulong)mysql_cset_escape_quotes(mysql->charset, to, from, length); + else + return (ulong)mysql_cset_escape_slashes(mysql->charset, to, from, length); +} + +void STDCALL +myodbc_remove_escape(MYSQL *mysql,char *name) +{ + char *to; + my_bool use_mb_flag= (mysql->charset->char_maxlen > 1); + char *end= 0; + if (use_mb_flag) + for (end=name; *end ; end++) ; + + for (to=name ; *name ; name++) + { + int l; + if (use_mb_flag && (l = mysql->charset->mb_valid(name , end))) + { + while (l--) + *to++ = *name++; + name--; + continue; + } + if (*name == '\\' && name[1]) + name++; + *to++= *name; + } + *to=0; +} + +void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs) +{ + DBUG_ENTER("mysql_get_character_set_info"); + + if (!cs) + DBUG_VOID_RETURN; + + cs->number= mysql->charset->nr; + cs->csname= mysql->charset->csname; + cs->name= mysql->charset->name; + cs->state= 0; + cs->comment= NULL; + cs->dir= NULL; + cs->mbminlen= mysql->charset->char_minlen; + cs->mbmaxlen= mysql->charset->char_maxlen; + + DBUG_VOID_RETURN; +} + +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) +{ + const CHARSET_INFO *cs; + DBUG_ENTER("mysql_set_character_set"); + + if (!csname) + goto error; + + if ((cs= mysql_find_charset_name(csname))) + { + char buff[64]; + + my_snprintf(buff, 63, "SET NAMES %s", cs->csname); + if (!mysql_real_query(mysql, buff, (uint)strlen(buff))) + { + mysql->charset= cs; + DBUG_RETURN(0); + } + } + +error: + my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, + 0, csname, "compiled_in"); + DBUG_RETURN(mysql->net.last_errno); +} + +unsigned int STDCALL mysql_warning_count(MYSQL *mysql) +{ + return mysql->warning_count; +} + +const char * STDCALL mysql_sqlstate(MYSQL *mysql) +{ + return mysql->net.sqlstate; +} + +int STDCALL mysql_server_init(int argc __attribute__((unused)), + char **argv __attribute__((unused)), + char **groups __attribute__((unused))) +{ + int rc= 0; + + if (!mysql_client_init) + { + mysql_client_init=1; + my_init(); /* Will init threads */ + init_client_errs(); + if (mysql_client_plugin_init()) + return 1; + if (!mysql_port) + { + struct servent *serv_ptr; + char *env; + + mysql_port = MYSQL_PORT; + if ((serv_ptr = getservbyname("mysql", "tcp"))) + mysql_port = (uint) ntohs((ushort) serv_ptr->s_port); + if ((env = getenv("MYSQL_TCP_PORT"))) + mysql_port =(uint) atoi(env); + } + if (!mysql_unix_port) + { + char *env; +#ifdef _WIN32 + mysql_unix_port = (char*) MYSQL_NAMEDPIPE; +#else + mysql_unix_port = (char*) MYSQL_UNIX_ADDR; +#endif + if ((env = getenv("MYSQL_UNIX_PORT"))) + mysql_unix_port = env; + } + mysql_debug(NullS); +#if defined(SIGPIPE) && !defined(THREAD) && !defined(_WIN32) + (void) signal(SIGPIPE,SIG_IGN); +#endif + } +#ifdef THREAD + else + rc= mysql_thread_init(); +#endif + if (!mysql_ps_subsystem_initialized) + mysql_init_ps_subsystem(); + return(rc); +} + +void STDCALL mysql_server_end() +{ + if (!mysql_client_init) + return; +#ifdef HAVE_OPENSSL + my_ssl_end(); +#endif + + mysql_client_plugin_deinit(); + + if (my_init_done) + my_end(0); + mysql_client_init= 0; + my_init_done= 0; +} + +my_bool STDCALL mysql_thread_init() +{ +#ifdef THREAD + return my_thread_init(); +#endif + return 0; +} + +void STDCALL mysql_thread_end() +{ + #ifdef THREAD + my_thread_end(); + #endif +} + +int STDCALL mysql_set_server_option(MYSQL *mysql, + enum enum_mysql_set_option option) +{ + char buffer[2]; + DBUG_ENTER("mysql_set_server_option"); + int2store(buffer, (uint)option); + DBUG_RETURN(simple_command(mysql, MYSQL_COM_SET_OPTION, buffer, sizeof(buffer), 0, 0)); +} + +ulong STDCALL mysql_get_client_version(void) +{ + return MYSQL_VERSION_ID; +} + +ulong STDCALL mysql_hex_string(char *to, const char *from, + unsigned long len) +{ + char *start= to; + char hexdigits[]= "0123456789ABCDEF"; + + while (len--) + { + *to++= hexdigits[((unsigned char)*from) >> 4]; + *to++= hexdigits[((unsigned char)*from) & 0x0F]; + from++; + } + *to= 0; + return (ulong)(to - start); +} + +my_bool STDCALL mariadb_connection(MYSQL *mysql) +{ + return (strstr(mysql->server_version, "MariaDB") || + strstr(mysql->server_version, "-maria-")); +} + +const char * STDCALL +mysql_get_server_name(MYSQL *mysql) +{ + if (mysql->options.extension && + mysql->options.extension->db_driver != NULL) + return mysql->options.extension->db_driver->name; + return mariadb_connection(mysql) ? "MariaDB" : "MySQL"; +} + +MYSQL_PARAMETERS *STDCALL +mysql_get_parameters(void) +{ + return &mariadb_internal_parameters; +} + +my_socket STDCALL +mysql_get_socket(const MYSQL *mysql) +{ + if (mysql->net.vio) + return vio_fd(mysql->net.vio); + return MARIADB_INVALID_SOCKET; +} + +/* + * Default methods for a connection. These methods are + * stored in mysql->methods and can be overwritten by + * a plugin, e.g. for using another database + */ +struct st_mysql_methods MARIADB_DEFAULT_METHODS = { + /* open a connection */ + mthd_my_real_connect, + /* close connection */ + mysql_close_slow_part, + /* send command to server */ + mthd_my_send_cmd, + /* skip result set */ + mthd_my_skip_result, + /* read response packet */ + mthd_my_read_query_result, + /* read all rows from a result set */ + mthd_my_read_rows, + /* read one/next row */ + mthd_my_read_one_row, + /* check if datatype is supported */ + mthd_supported_buffer_type, + /* read response packet from prepare */ + mthd_stmt_read_prepare_response, + /* read response from stmt execute */ + mthd_my_read_query_result, + /* get result set metadata for a prepared statement */ + mthd_stmt_get_result_metadata, + /* get param metadata for a prepared statement */ + mthd_stmt_get_param_metadata, + /* read all rows (buffered) */ + mthd_stmt_read_all_rows, + /* fetch one row (unbuffered) */ + mthd_stmt_fetch_row, + /* store values in bind buffer */ + mthd_stmt_fetch_to_bind, + /* skip unbuffered stmt result */ + mthd_stmt_flush_unbuffered +}; |