/************************************************************************************ 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 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 #include #include #include #include #include #include "my_context.h" #include "mysql.h" #include "mysql_version.h" #include "mysqld_error.h" #include "errmsg.h" #include #include #include #ifdef HAVE_PWD_H #include #endif #if !defined(MSDOS) && !defined(_WIN32) #include #include #include #include #ifdef HAVE_SELECT_H # include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #endif #ifdef HAVE_SYS_UN_H # include #endif #if defined(THREAD) && !defined(_WIN32) #include /* because of signal() */ #endif #ifndef INADDR_NONE #define INADDR_NONE -1 #endif #include #include #ifdef HAVE_OPENSSL #include #endif #ifndef _WIN32 #include #endif #include #include #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 #define SOCKET_ERROR -1 #endif /* _WIN32 */ #include #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. *****************************************************************************/ 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 : "", unix_socket ? unix_socket : "", (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 };