From 43d743e75b7b747341b9a5c36a933b490548bebb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 4 Nov 2017 01:17:16 +0300 Subject: Add implementation --- mysql/libmariadb/mariadb_lib.c | 4141 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4141 insertions(+) create mode 100644 mysql/libmariadb/mariadb_lib.c (limited to 'mysql/libmariadb/mariadb_lib.c') diff --git a/mysql/libmariadb/mariadb_lib.c b/mysql/libmariadb/mariadb_lib.c new file mode 100644 index 0000000..d8f3ada --- /dev/null +++ b/mysql/libmariadb/mariadb_lib.c @@ -0,0 +1,4141 @@ +/************************************************************************************ + 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 "ma_context.h" +#include "mysql.h" +#include "mariadb_version.h" +#include "ma_server_error.h" +#include +#include "errmsg.h" +#include +#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 +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif +#include +#ifndef _WIN32 +#include +#endif +#include +#ifdef HAVE_TLS +#include +#endif +#include +#ifdef _WIN32 +#include "Shlwapi.h" +#endif + +#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) +#define MA_RPL_VERSION_HACK "5.5.5-" + +#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 void release_configuration_dirs(); +extern char **get_default_configuration_dirs(); +extern my_bool ma_init_done; +extern my_bool mysql_ps_subsystem_initialized; +extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename); +extern const MARIADB_CHARSET_INFO * mysql_find_charset_nr(uint charsetnr); +extern const MARIADB_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); +extern int net_add_multi_command(NET *net, uchar command, const uchar *packet, + size_t length); + +extern LIST *pvio_callback; + +/* 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 my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file, + char *group); +extern unsigned char *mysql_net_store_length(unsigned char *packet, size_t length); + +extern void +my_context_install_suspend_resume_hook(struct mysql_async_context *b, + void (*hook)(my_bool, void *), + void *user_data); + +uint mysql_port=0; +my_string mysql_unix_port=0; + +#define CONNECT_TIMEOUT 0 + +struct st_mariadb_methods MARIADB_DEFAULT_METHODS; + +#if defined(MSDOS) || defined(_WIN32) +// socket_errno is defined in ma_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" + +#define IS_CONNHDLR_ACTIVE(mysql)\ + ((mysql)->extension && (mysql)->extension->conn_hdlr) + +static void end_server(MYSQL *mysql); +static void mysql_close_memory(MYSQL *mysql); +void read_user_name(char *name); +my_bool STDCALL mariadb_reconnect(MYSQL *mysql); +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); + +extern int mysql_client_plugin_init(); +extern void mysql_client_plugin_deinit(); + +/* 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, SQLSTATE_UNKNOWN, SQLSTATE_LENGTH); + } +} + +/***************************************************************************** +** read a packet from server. Give error message if socket was down +** or packet is an error message +*****************************************************************************/ + +ulong +ma_net_safe_read(MYSQL *mysql) +{ + NET *net= &mysql->net; + ulong len=0; + +restart: + if (net->pvio != 0) + len=ma_net_read(net); + + 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, errno); + 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 && + ((mariadb_connection(mysql) && (mysql->server_capabilities & CLIENT_PROGRESS)) || + (!(mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_PROGRESS << 32)))) + { + if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1))) + { + /* Wrong packet */ + my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + return (packet_error); + } + goto restart; + } + net->last_errno= last_errno; + if (pos[0]== '#') + { + ma_strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH); + pos+= SQLSTATE_LENGTH + 1; + } + else + { + strcpy(net->sqlstate, SQLSTATE_UNKNOWN); + } + ma_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; + + 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 unsigned long long +net_field_length_ll(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (unsigned long long) *pos; + } + if (*pos == 251) + { + (*packet)++; + return (unsigned long long) NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (unsigned long long) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (unsigned long long) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG + return (unsigned long long) uint4korr(pos+1); +#else + return (unsigned long long) uint8korr(pos+1); +#endif +} + + +void free_rows(MYSQL_DATA *cur) +{ + if (cur) + { + ma_free_root(&cur->alloc,MYF(0)); + 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; + if (mysql->net.pvio == 0) + { + /* Do reconnect if possible */ + if (mariadb_reconnect(mysql)) + return(1); + } + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + goto end; + } + + if (IS_CONNHDLR_ACTIVE(mysql)) + { + result= mysql->extension->conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg); + if (result== -1) + return(result); + } + + CLEAR_CLIENT_ERROR(mysql); + + mysql->info=0; + mysql->affected_rows= ~(unsigned long long) 0; + ma_net_clear(net); /* Clear receive buffer */ + if (!arg) + arg=""; + + if (net->extension->multi_status== COM_MULTI_ENABLED) + { + return net_add_multi_command(net, command, (const uchar *)arg, length); + } + + if (ma_net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg), 0)) + { + 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 (mariadb_reconnect(mysql)) + goto end; + if (ma_net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg), 0)) + { + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + result=0; + + if (net->extension->multi_status > COM_MULTI_OFF) + skipp_check= 1; + + if (!skipp_check) + { + result= ((mysql->packet_length=ma_net_safe_read(mysql)) == packet_error ? + 1 : 0); + } + end: + return(result); +} + +int +ma_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); +} + +int ma_multi_command(MYSQL *mysql, enum enum_multi_status status) +{ + NET *net= &mysql->net; + + switch (status) { + case COM_MULTI_OFF: + ma_net_clear(net); + net->extension->multi_status= status; + return 0; + case COM_MULTI_ENABLED: + if (net->extension->multi_status > COM_MULTI_DISABLED) + return 1; + ma_net_clear(net); + net->extension->multi_status= status; + return 0; + case COM_MULTI_DISABLED: + /* Opposite to COM_MULTI_OFF we don't clear net buffer, + next command or com_nulti_end will flush entire buffer */ + net->extension->multi_status= status; + return 0; + case COM_MULTI_END: + { + size_t len= net->write_pos - net->buff - NET_HEADER_SIZE; + + if (len < NET_HEADER_SIZE) /* don't send empty COM_MULTI */ + { + ma_net_clear(net); + return 1; + } + net->extension->multi_status= COM_MULTI_OFF; + return ma_net_flush(net); + } + case COM_MULTI_CANCEL: + ma_net_clear(net); + net->extension->multi_status= COM_MULTI_OFF; + return 0; + default: + return 1; + } +} + +static void free_old_query(MYSQL *mysql) +{ + if (mysql->fields) + ma_free_root(&mysql->field_alloc,MYF(0)); + ma_init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */ + mysql->fields=0; + mysql->field_count=0; /* For API */ + mysql->info= 0; + 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) +{ + if (geteuid() == 0) + strcpy(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"; + } + ma_strmake(name,str,USERNAME_LENGTH); +#elif HAVE_CUSERID + (void) cuserid(name); +#else + ma_strmake(name,"UNKNOWN_USER", USERNAME_LENGTH); +#endif + } + return; +} + +#else /* If MSDOS || VMS */ + +void read_user_name(char *name) +{ + char *str=getenv("USERNAME"); /* ODBC will send user variable */ + ma_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 + +/************************************************************************** +** Shut down connection +**************************************************************************/ + +static void +end_server(MYSQL *mysql) +{ + /* if net->error 2 and reconnect is activated, we need to inforn + connection handler */ + if (mysql->net.pvio != 0) + { + ma_pvio_close(mysql->net.pvio); + mysql->net.pvio= 0; /* Marker */ + } + ma_net_end(&mysql->net); + free_old_query(mysql); + return; +} + +void mthd_my_skip_result(MYSQL *mysql) +{ + ulong pkt_len; + + do { + pkt_len= ma_net_safe_read(mysql); + if (pkt_len == packet_error) + break; + } while (pkt_len > 8 || mysql->net.read_pos[0] != 254); + return; +} + +void STDCALL +mysql_free_result(MYSQL_RES *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) + ma_free_root(&result->field_alloc,MYF(0)); + if (result->row) + free(result->row); + free(result); + } + return; +} + + +/**************************************************************************** +** Get options from my.cnf +****************************************************************************/ +enum enum_option_type { + MARIADB_OPTION_NONE, + MARIADB_OPTION_BOOL, + MARIADB_OPTION_INT, + MARIADB_OPTION_SIZET, + MARIADB_OPTION_STR, +}; + +struct st_default_options { + enum mysql_option option; + enum enum_option_type type; + const char *conf_key; +}; + +struct st_default_options mariadb_defaults[] = +{ + {MARIADB_OPT_PORT, MARIADB_OPTION_INT,"port"}, + {MARIADB_OPT_UNIXSOCKET, MARIADB_OPTION_STR, "socket"}, + {MYSQL_OPT_COMPRESS, MARIADB_OPTION_BOOL, "compress"}, + {MARIADB_OPT_PASSWORD, MARIADB_OPTION_STR, "password"}, + {MYSQL_OPT_NAMED_PIPE, MARIADB_OPTION_BOOL, "pipe"}, + {MYSQL_OPT_CONNECT_TIMEOUT, MARIADB_OPTION_INT, "timeout"}, + {MARIADB_OPT_USER, MARIADB_OPTION_STR, "user"}, + {MYSQL_INIT_COMMAND, MARIADB_OPTION_STR, "init-command"}, + {MARIADB_OPT_HOST, MARIADB_OPTION_STR, "host"}, + {MARIADB_OPT_SCHEMA, MARIADB_OPTION_STR, "database"}, + {MARIADB_OPT_DEBUG, MARIADB_OPTION_STR, "debug"}, + {MARIADB_OPT_FOUND_ROWS, MARIADB_OPTION_NONE, "return-found-rows"}, + {MYSQL_OPT_SSL_KEY, MARIADB_OPTION_STR, "ssl-key"}, + {MYSQL_OPT_SSL_CERT, MARIADB_OPTION_STR,"ssl-cert"}, + {MYSQL_OPT_SSL_CA, MARIADB_OPTION_STR,"ssl-ca"}, + {MYSQL_OPT_SSL_CAPATH, MARIADB_OPTION_STR,"ssl-capath"}, + {MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MARIADB_OPTION_BOOL,"ssl-verify-server-cert"}, + {MYSQL_SET_CHARSET_DIR, MARIADB_OPTION_STR, "character-sets-dir"}, + {MYSQL_SET_CHARSET_NAME, MARIADB_OPTION_STR, "default-character-set"}, + {MARIADB_OPT_INTERACTIVE, MARIADB_OPTION_NONE, "interactive-timeout"}, + {MYSQL_OPT_CONNECT_TIMEOUT, MARIADB_OPTION_INT, "connect-timeout"}, + {MYSQL_OPT_LOCAL_INFILE, MARIADB_OPTION_BOOL, "local-infile"}, + {0, 0 ,"disable-local-infile",}, + {MYSQL_OPT_SSL_CIPHER, MARIADB_OPTION_STR, "ssl-cipher"}, + {MYSQL_OPT_MAX_ALLOWED_PACKET, MARIADB_OPTION_SIZET, "max-allowed-packet"}, + {MYSQL_OPT_NET_BUFFER_LENGTH, MARIADB_OPTION_SIZET, "net-buffer-length"}, + {MYSQL_OPT_PROTOCOL, MARIADB_OPTION_INT, "protocol"}, + {MYSQL_SHARED_MEMORY_BASE_NAME, MARIADB_OPTION_STR,"shared-memory-base-name"}, + {MARIADB_OPT_MULTI_RESULTS, MARIADB_OPTION_NONE, "multi-results"}, + {MARIADB_OPT_MULTI_STATEMENTS, MARIADB_OPTION_STR, "multi-statements"}, + {MARIADB_OPT_MULTI_STATEMENTS, MARIADB_OPTION_STR, "multi-queries"}, + {MYSQL_SECURE_AUTH, MARIADB_OPTION_BOOL, "secure-auth"}, + {MYSQL_REPORT_DATA_TRUNCATION, MARIADB_OPTION_BOOL, "report-data-truncation"}, + {MYSQL_OPT_RECONNECT, MARIADB_OPTION_BOOL, "reconnect"}, + {MYSQL_PLUGIN_DIR, MARIADB_OPTION_STR, "plugin-dir"}, + {MYSQL_DEFAULT_AUTH, MARIADB_OPTION_STR, "default-auth"}, + {MARIADB_OPT_SSL_FP, MARIADB_OPTION_STR, "ssl-fp"}, + {MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fp-list"}, + {MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fplist"}, + {MARIADB_OPT_TLS_PASSPHRASE, MARIADB_OPTION_STR, "ssl-passphrase"}, + {MARIADB_OPT_TLS_VERSION, MARIADB_OPTION_STR, "tls_version"}, + {MYSQL_OPT_BIND, MARIADB_OPTION_STR, "bind-address"}, + {0, 0, NULL} +}; + +#define CHECK_OPT_EXTENSION_SET(OPTS)\ + if (!(OPTS)->extension) \ + (OPTS)->extension= (struct st_mysql_options_extension *) \ + calloc(1, sizeof(struct st_mysql_options_extension)); + +#define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ + free((gptr)(OPTS)->extension->KEY); \ + if((VAL)) \ + (OPTS)->extension->KEY= strdup((char *)(VAL)); \ + else \ + (OPTS)->extension->KEY= NULL + +#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ + (OPTS)->extension->KEY= (VAL) + +#define OPT_SET_EXTENDED_VALUE_INT(A,B,C) OPT_SET_EXTENDED_VALUE(A,B,C) + +#define OPT_SET_VALUE_STR(OPTS, KEY, VAL) \ + free((OPTS)->KEY); \ + if((VAL)) \ + (OPTS)->KEY= strdup((char *)(VAL)); \ + else \ + (OPTS)->KEY= NULL + +#define OPT_SET_VALUE_INT(OPTS, KEY, VAL) \ + (OPTS)->KEY= (VAL) + +static void options_add_initcommand(struct st_mysql_options *options, + const char *init_cmd) +{ + char *insert= strdup(init_cmd); + if (!options->init_command) + { + options->init_command= (DYNAMIC_ARRAY*)malloc(sizeof(DYNAMIC_ARRAY)); + ma_init_dynamic_array(options->init_command, sizeof(char*), 5, 5); + } + + if (ma_insert_dynamic(options->init_command, (gptr)&insert)) + free(insert); +} +my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value) +{ + if (config_option) + { + int i; + + for (i=0; mariadb_defaults[i].conf_key; i++) + { + if (!strcmp(mariadb_defaults[i].conf_key, config_option)) + { + my_bool val_bool; + int val_int; + size_t val_sizet; + int rc; + void *option_val= NULL; + switch (mariadb_defaults[i].type) { + case MARIADB_OPTION_BOOL: + val_bool= 0; + if (config_value) + val_bool= atoi(config_value); + option_val= &val_bool; + break; + case MARIADB_OPTION_INT: + val_int= 0; + if (config_value) + val_int= atoi(config_value); + option_val= &val_int; + break; + case MARIADB_OPTION_SIZET: + val_sizet= 0; + if (config_value) + val_sizet= strtol(config_value, NULL, 10); + option_val= &val_sizet; + break; + case MARIADB_OPTION_STR: + option_val= (void*)config_value; + break; + case MARIADB_OPTION_NONE: + break; + } + rc= mysql_optionsv(mysql, mariadb_defaults[i].option, option_val); + return(test(rc)); + } + } + } + /* unknown key */ + return 1; +} + +/*************************************************************************** +** 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,MA_MEM_ROOT *alloc,uint fields, + my_bool default_value, my_bool long_flag_protocol __attribute__((unused))) +{ + MYSQL_ROWS *row; + MYSQL_FIELD *field,*result; + char *p; + unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2; + + field=result=(MYSQL_FIELD*) ma_alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); + if (!result) + return(0); + + for (row=data->data; row ; row = row->next,field++) + { + for (i=0; i < field_count; i++) + { + switch(row->data[i][0]) { + case 0: + *(char **)(((char *)field) + rset_field_offsets[i*2])= ma_strdup_root(alloc, ""); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= 0; + break; + default: + *(char **)(((char *)field) + rset_field_offsets[i*2])= + ma_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=ma_strdup_root(alloc,(char*) row->data[7]); + } + else + field->def=0; + field->max_length= 0; + } + free_rows(data); /* Free old data */ + return(result); +} + + +/* 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; + + if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) + return(0); + if (!(result=(MYSQL_DATA*) calloc(1, sizeof(MYSQL_DATA)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(0); + } + ma_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*) ma_alloc_root(&result->alloc, + sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) + ma_alloc_root(&result->alloc, + (fields+1)*sizeof(char *)+fields+pkt_len)))) + { + free_rows(result); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + 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, SQLSTATE_UNKNOWN, 0); + 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=ma_net_safe_read(mysql)) == packet_error) + { + free_rows(result); + 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); + } + 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) ma_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; + strcpy(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*) calloc(1, sizeof(MYSQL)))) + return 0; + mysql->free_me=1; + mysql->net.pvio= 0; + mysql->net.extension= 0; + } + else + { + memset((char*) (mysql), 0, sizeof(*(mysql))); + mysql->net.pvio= 0; + mysql->net.extension= 0; + } + + if (!(mysql->net.extension= (struct st_mariadb_net_extension *) + calloc(1, sizeof(struct st_mariadb_net_extension))) || + !(mysql->extension= (struct st_mariadb_extension *) + calloc(1, sizeof(struct st_mariadb_extension)))) + goto error; + mysql->options.report_data_truncation= 1; + mysql->options.connect_timeout=CONNECT_TIMEOUT; + mysql->charset= ma_default_charset_info; + mysql->methods= &MARIADB_DEFAULT_METHODS; + strcpy(mysql->net.sqlstate, "00000"); + mysql->net.last_error[0]= mysql->net.last_errno= 0; + +/* + 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->options.reconnect= 0; + return mysql; +error: + if (mysql->free_me) + free(mysql); + return 0; +} + +int STDCALL +mysql_ssl_set(MYSQL *mysql __attribute__((unused)), + const char *key __attribute__((unused)), + const char *cert __attribute__((unused)), + const char *ca __attribute__((unused)), + const char *capath __attribute__((unused)), + const char *cipher __attribute__((unused))) +{ +#ifdef HAVE_TLS + char enable= 1; + return (mysql_optionsv(mysql, MYSQL_OPT_SSL_ENFORCE, &enable) | + 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 __attribute__((unused))) +{ +#ifdef HAVE_TLS + if (mysql->net.pvio && mysql->net.pvio->ctls) + { + return ma_pvio_tls_cipher(mysql->net.pvio->ctls); + } +#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. +**************************************************************************/ + +char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer) +{ + if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS) + { + buffer= (unsigned char *)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= strlen((char *)p); + buffer= mysql_net_store_length(buffer, len); + memcpy(buffer, p, len); + buffer+= (len); + p+= (len + 1); + len= strlen((char *)p); + buffer= mysql_net_store_length(buffer, len); + memcpy(buffer, p, len); + buffer+= len; + } + } + } + return (char *)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"); + + 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); + 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) +{ + char *end= NULL; + char *connection_handler= (mysql->options.extension) ? + mysql->options.extension->connection_handler : 0; + + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + + if (connection_handler || + (host && (end= strstr(host, "://")))) + { + MARIADB_CONNECTION_PLUGIN *plugin; + char plugin_name[64]; + + if (!connection_handler || !connection_handler[0]) + { + memset(plugin_name, 0, 64); + ma_strmake(plugin_name, host, MIN(end - host, 63)); + end+= 3; + } + else + ma_strmake(plugin_name, connection_handler, MIN(63, strlen(connection_handler))); + + if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN))) + return NULL; + + if (!(mysql->extension->conn_hdlr= (MA_CONNECTION_HANDLER *)calloc(1, sizeof(MA_CONNECTION_HANDLER)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return NULL; + } + + /* save URL for reconnect */ + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, url, host); + + mysql->extension->conn_hdlr->plugin= plugin; + + if (plugin && plugin->connect) + { + MYSQL *my= plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag); + if (!my) + { + free(mysql->extension->conn_hdlr); + mysql->extension->conn_hdlr= NULL; + } + return my; + } + } + + 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; + MA_PVIO_CINFO cinfo= {NULL, NULL, 0, -1, NULL}; + MARIADB_PVIO *pvio= NULL; + char *scramble_data; + my_bool is_maria= 0; + const char *scramble_plugin; + uint pkt_length, scramble_len, pkt_scramble_len= 0; + NET *net= &mysql->net; + + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + + ma_set_connect_attrs(mysql); + + if (net->pvio) /* check if we are already connected */ + { + SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0); + return(NULL); + } + + /* use default options */ + if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) + { + _mariadb_read_options(mysql, + (mysql->options.my_cnf_file ? + mysql->options.my_cnf_file : NULL), + mysql->options.my_cnf_group); + free(mysql->options.my_cnf_file); + free(mysql->options.my_cnf_group); + mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; + } + +#ifndef WIN32 + if (mysql->options.protocol > MYSQL_PROTOCOL_SOCKET) + { + SET_CLIENT_ERROR(mysql, CR_CONN_UNKNOWN_PROTOCOL, SQLSTATE_UNKNOWN, 0); + return(NULL); + } +#endif + + /* 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) */ + if (!passwd) + passwd= ""; +#endif + } + if (!db || !db[0]) + db=mysql->options.db; + if (!port) + port=mysql->options.port; + if (!unix_socket) + unix_socket=mysql->options.unix_socket; + + mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + + /* try to connect via pvio_init */ + cinfo.host= host; + cinfo.unix_socket= unix_socket; + cinfo.port= port; + cinfo.mysql= mysql; + + /* + ** Grab a socket and connect it to the server + */ +#ifndef _WIN32 +#if defined(HAVE_SYS_UN_H) + if ((!host || strcmp(host,LOCAL_HOST) == 0) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP && + (unix_socket || mysql_unix_port)) + { + cinfo.host= LOCAL_HOST; + cinfo.unix_socket= (unix_socket) ? unix_socket : mysql_unix_port; + cinfo.type= PVIO_TYPE_UNIXSOCKET; + sprintf(host_info=buff,ER(CR_LOCALHOST_CONNECTION),cinfo.host); + } + else +#endif +#else + if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) + { + cinfo.host= mysql->options.shared_memory_base_name; + cinfo.type= PVIO_TYPE_SHAREDMEM; + sprintf(host_info=buff,ER(CR_SHARED_MEMORY_CONNECTION), cinfo.host ? cinfo.host : SHM_DEFAULT_NAME); + } + /* named pipe */ + else if (mysql->options.protocol == MYSQL_PROTOCOL_PIPE || + (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0)) + { + cinfo.type= PVIO_TYPE_NAMEDPIPE; + sprintf(host_info=buff,ER(CR_NAMEDPIPE_CONNECTION),cinfo.host); + } + else +#endif + { + cinfo.unix_socket=0; /* This is not used */ + if (!port) + port=mysql_port; + if (!host) + host=LOCAL_HOST; + cinfo.host= host; + cinfo.port= port; + cinfo.type= PVIO_TYPE_SOCKET; + sprintf(host_info=buff,ER(CR_TCP_CONNECTION), cinfo.host); + } + /* Initialize and load pvio plugin */ + if (!(pvio= ma_pvio_init(&cinfo))) + goto error; + + /* try to connect */ + if (ma_pvio_connect(pvio, &cinfo) != 0) + { + ma_pvio_close(pvio); + goto error; + } + + if (mysql->options.extension && mysql->options.extension->proxy_header) + { + char *hdr = mysql->options.extension->proxy_header; + size_t len = mysql->options.extension->proxy_header_len; + if (ma_pvio_write(pvio, (unsigned char *)hdr, len) <= 0) + { + ma_pvio_close(pvio); + goto error; + } + } + + if (ma_net_init(net, pvio)) + goto error; + + if (mysql->options.max_allowed_packet) + net->max_packet_size= mysql->options.max_allowed_packet; + + ma_pvio_keepalive(net->pvio); + strcpy(mysql->net.sqlstate, "00000"); + + /* Get version info */ + mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ +/* + if (ma_pvio_wait_io_or_timeout(net->pvio, FALSE, 0) < 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=ma_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 protocol 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; + } + + 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 (!(mysql->host_info= strdup(host_info ? host_info : "")) || + !(mysql->host= strdup(cinfo.host ? cinfo.host : "")) || + !(mysql->user=strdup(user)) || + !(mysql->passwd=strdup(passwd))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto error; + } + if (cinfo.unix_socket) + mysql->unix_socket= strdup(cinfo.unix_socket); + else + mysql->unix_socket=0; + mysql->port=port; + client_flag|=mysql->options.client_flag; + + if (strncmp(end, MA_RPL_VERSION_HACK, sizeof(MA_RPL_VERSION_HACK) - 1) == 0) + { + mysql->server_version= strdup(end + sizeof(MA_RPL_VERSION_HACK) - 1); + is_maria= 1; + } + else + { + if (!(mysql->server_version= strdup(end))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 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|= (unsigned int)(uint2korr(end + 5) << 16); + pkt_scramble_len= uint1korr(end + 7); + + /* check if MariaD2B specific capabilities are available */ + if (is_maria && !(mysql->server_capabilities & CLIENT_MYSQL)) + { + mysql->extension->mariadb_server_capabilities= (ulonglong) uint4korr(end + 14); + } + } + + /* 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, SQLSTATE_UNKNOWN, 0); + goto error; + } + } + + /* Set character set */ + if (mysql->options.charset_name) + mysql->charset= mysql_find_charset_name(mysql->options.charset_name); + else if (mysql->server_language) + mysql->charset= mysql_find_charset_nr(mysql->server_language); + else + mysql->charset=ma_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 (!(mysql->server_capabilities & CLIENT_CONNECT_WITH_DB) && + (db && !mysql->db)) + { + if (mysql_select_db(mysql, db)) + { + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "Setting intital database", + errno); + goto error; + } + } + + 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->options.reconnect; + mysql->options.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->options.reconnect= save_reconnect; + } + + strcpy(mysql->net.sqlstate, "00000"); + + /* connection established, apply timeouts */ + ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout); + ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout); + return(mysql); + +error: + /* Free alloced memory */ + end_server(mysql); + /* only free the allocated memory, user needs to call mysql_close */ + mysql_close_memory(mysql); + if (!(client_flag & CLIENT_REMEMBER_OPTIONS) && + !mysql->options.extension->async_context) + mysql_close_options(mysql); + return(0); +} + +struct my_hook_data { + MYSQL *orig_mysql; + MYSQL *new_mysql; + /* This is always NULL currently, but restoring does not hurt just in case. */ + MARIADB_PVIO *orig_pvio; +}; +/* + Callback hook to make the new VIO accessible via the old MYSQL to calling + application when suspending a non-blocking call during automatic reconnect. +*/ +static void +my_suspend_hook(my_bool suspend, void *data) +{ + struct my_hook_data *hook_data= (struct my_hook_data *)data; + if (suspend) + { + hook_data->orig_pvio= hook_data->orig_mysql->net.pvio; + hook_data->orig_mysql->net.pvio= hook_data->new_mysql->net.pvio; + } + else + hook_data->orig_mysql->net.pvio= hook_data->orig_pvio; +} + +my_bool STDCALL mariadb_reconnect(MYSQL *mysql) +{ + MYSQL tmp_mysql; + struct my_hook_data hook_data; + struct mysql_async_context *ctxt= NULL; + LIST *li_stmt= mysql->stmts; + + /* check if connection handler is active */ + if (IS_CONNHDLR_ACTIVE(mysql)) + { + if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reconnect) + return(mysql->extension->conn_hdlr->plugin->reconnect(mysql)); + } + + if (!mysql->options.reconnect || + (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info) + { + /* Allow reconnect next time */ + mysql->server_status&= ~SERVER_STATUS_IN_TRANS; + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + return(1); + } + + mysql_init(&tmp_mysql); + tmp_mysql.options=mysql->options; + if (mysql->extension->conn_hdlr) + { + tmp_mysql.extension->conn_hdlr= mysql->extension->conn_hdlr; + mysql->extension->conn_hdlr= 0; + } + + /* don't reread options from configuration files */ + tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL; + if (IS_MYSQL_ASYNC_ACTIVE(mysql)) + { + ctxt= mysql->options.extension->async_context; + hook_data.orig_mysql= mysql; + hook_data.new_mysql= &tmp_mysql; + hook_data.orig_pvio= mysql->net.pvio; + my_context_install_suspend_resume_hook(ctxt, my_suspend_hook, &hook_data); + } + + 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) || + mysql_set_character_set(&tmp_mysql, mysql->charset->csname)) + { + if (ctxt) + my_context_install_suspend_resume_hook(ctxt, NULL, NULL); + /* 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); + return(1); + } + + 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; + + if (ctxt) + my_context_install_suspend_resume_hook(ctxt, NULL, NULL); + /* Don't free options, we moved them to tmp_mysql */ + memset(&mysql->options, 0, sizeof(mysql->options)); + mysql->free_me=0; + mysql_close(mysql); + *mysql=tmp_mysql; + mysql->net.pvio->mysql= mysql; + ma_net_clear(&mysql->net); + mysql->affected_rows= ~(unsigned long long) 0; + mysql->info= 0; + return(0); +} + +void ma_invalidate_stmts(MYSQL *mysql, const char *function_name) +{ + if (mysql->stmts) + { + LIST *li_stmt= mysql->stmts; + + 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_STMT_CLOSED, SQLSTATE_UNKNOWN, function_name); + } + mysql->stmts= NULL; + } +} + +/* + Legacy support of the MariaDB 5.5 version, where timeouts where only in + seconds resolution. Applications that use this will be asked to set a timeout + at the nearest higher whole-seconds value. +*/ +unsigned int STDCALL +mysql_get_timeout_value(const MYSQL *mysql) +{ + unsigned int timeout= mysql->options.extension->async_context->timeout_value; + /* Avoid overflow. */ + if (timeout > UINT_MAX - 999) + return (timeout - 1)/1000 + 1; + else + return (timeout+999)/1000; +} + + +unsigned int STDCALL +mysql_get_timeout_value_ms(const MYSQL *mysql) +{ + return mysql->options.extension->async_context->timeout_value; +} + +/************************************************************************** +** Change user and database +**************************************************************************/ + +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db) +{ + const MARIADB_CHARSET_INFO *s_cs= mysql->charset; + char *s_user= mysql->user, + *s_passwd= mysql->passwd, + *s_db= mysql->db; + int rc; + + 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 if (mysql->server_language) + mysql->charset=mysql_find_charset_nr(mysql->server_language); + else + mysql->charset=ma_default_charset_info; + + mysql->user= strdup(user ? user : ""); + mysql->passwd= strdup(passwd ? passwd : ""); + + /* db will be set in run_plugin_auth */ + mysql->db= 0; + rc= run_plugin_auth(mysql, 0, 0, 0, db); + + /* COM_CHANGE_USER always releases prepared statements, so we need to invalidate them */ + ma_invalidate_stmts(mysql, "mysql_change_user()"); + + if (rc==0) + { + free(s_user); + free(s_passwd); + free(s_db); + + if (db && !(mysql->db= strdup(db))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + rc= 1; + } + } else + { + free(mysql->user); + free(mysql->passwd); + free(mysql->db); + + mysql->user= s_user; + mysql->passwd= s_passwd; + mysql->db= s_db; + mysql->charset= s_cs; + } + return(rc); +} + + +/************************************************************************** +** Set current database +**************************************************************************/ + +int STDCALL +mysql_select_db(MYSQL *mysql, const char *db) +{ + int error; + + if ((error=ma_simple_command(mysql, COM_INIT_DB,db,(uint) strlen(db),0,0))) + return(error); + free(mysql->db); + mysql->db=strdup(db); + 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++) + free(*begin); + ma_delete_dynamic(mysql->options.init_command); + free(mysql->options.init_command); + } + free(mysql->options.user); + free(mysql->options.host); + free(mysql->options.password); + free(mysql->options.unix_socket); + free(mysql->options.db); + free(mysql->options.my_cnf_file); + free(mysql->options.my_cnf_group); + free(mysql->options.charset_dir); + free(mysql->options.charset_name); + free(mysql->options.bind_address); + free(mysql->options.ssl_key); + free(mysql->options.ssl_cert); + free(mysql->options.ssl_ca); + free(mysql->options.ssl_capath); + free(mysql->options.ssl_cipher); + + if (mysql->options.extension) + { + struct mysql_async_context *ctxt; + if ((ctxt = mysql->options.extension->async_context)) + { + my_context_destroy(&ctxt->async_context); + free(ctxt); + mysql->options.extension->async_context= 0; + } + free(mysql->options.extension->plugin_dir); + free(mysql->options.extension->default_auth); + free(mysql->options.extension->db_driver); + free(mysql->options.extension->ssl_crl); + free(mysql->options.extension->ssl_crlpath); + free(mysql->options.extension->tls_fp); + free(mysql->options.extension->tls_fp_list); + free(mysql->options.extension->tls_pw); + free(mysql->options.extension->tls_version); + free(mysql->options.extension->url); + free(mysql->options.extension->connection_handler); + if(hash_inited(&mysql->options.extension->connect_attrs)) + hash_free(&mysql->options.extension->connect_attrs); + if (hash_inited(&mysql->options.extension->userdata)) + hash_free(&mysql->options.extension->userdata); + + } + free(mysql->options.extension); + /* clear all pointer */ + memset(&mysql->options, 0, sizeof(mysql->options)); +} + +static void mysql_close_memory(MYSQL *mysql) +{ + free(mysql->host_info); + free(mysql->host); + free(mysql->user); + free(mysql->passwd); + free(mysql->db); + free(mysql->unix_socket); + free(mysql->server_version); + mysql->host_info= mysql->host= mysql->unix_socket= + 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; + + mysql->net.last_errno= error_nr; + ma_strmake(mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH); + va_start(ap, format); + vsnprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE, + format ? format : ER(error_nr), ap); + va_end(ap); + return; +} + +void mysql_close_slow_part(MYSQL *mysql) +{ + if (mysql->net.pvio) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + mysql->options.reconnect=0; + if (mysql->net.pvio && mysql->net.buff) + ma_simple_command(mysql, COM_QUIT,NullS,0,1,0); + end_server(mysql); + } +} + +void ma_clear_session_state(MYSQL *mysql) +{ + uint i; + + if (!mysql || !mysql->extension) + return; + + for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++) + { + /* we acquired memory via ma_multi_alloc, so we don't need to free data */ + list_free(mysql->extension->session_state[i].list, 0); + } + memset(mysql->extension->session_state, 0, sizeof(struct st_mariadb_session_state) * SESSION_TRACK_TYPES); +} + +void STDCALL +mysql_close(MYSQL *mysql) +{ + if (mysql) /* Some simple safety */ + { + if (mysql->extension && mysql->extension->conn_hdlr) + { + MA_CONNECTION_HANDLER *p= mysql->extension->conn_hdlr; + p->plugin->close(mysql); + free(p); + } + + if (mysql->methods) + mysql->methods->db_close(mysql); + + /* reset the connection in all active statements */ + ma_invalidate_stmts(mysql, "mysql_close()"); + + mysql_close_memory(mysql); + mysql_close_options(mysql); + ma_clear_session_state(mysql); + + if (mysql->net.extension) + free(mysql->net.extension); + + mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + + /* Clear pointers for better safety */ + memset((char*) &mysql->options, 0, sizeof(mysql->options)); + + if (mysql->extension) + free(mysql->extension); + + mysql->net.pvio= 0; + if (mysql->free_me) + free(mysql); + } + return; +} + + +/************************************************************************** +** Do a query. If query returned rows, free old rows. +** Read data by mysql_store_result or by repeating calls to mysql_fetch_row +**************************************************************************/ + +int STDCALL +mysql_query(MYSQL *mysql, const char *query) +{ + return mysql_real_query(mysql,query, (unsigned long) 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 ma_simple_command(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; + + if (!mysql || (length = ma_net_safe_read(mysql)) == packet_error) + { + 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) + { + size_t item_len; + 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) + { + if ((item_len= net_field_length(&pos))) + mysql->info=(char*) pos; + + /* check if server supports session tracking */ + if (mysql->server_capabilities & CLIENT_SESSION_TRACKING) + { + ma_clear_session_state(mysql); + pos+= item_len; + + if (mysql->server_status & SERVER_SESSION_STATE_CHANGED) + { + int i; + if (pos < mysql->net.read_pos + length) + { + LIST *session_item; + MYSQL_LEX_STRING *str= NULL; + enum enum_session_state_type si_type; + uchar *old_pos= pos; + size_t item_len= net_field_length(&pos); /* length for all items */ + + /* length was already set, so make sure that info will be zero terminated */ + if (mysql->info) + *old_pos= 0; + + while (item_len > 0) + { + size_t plen; + char *data; + old_pos= pos; + si_type= (enum enum_session_state_type)net_field_length(&pos); + switch(si_type) { + case SESSION_TRACK_SCHEMA: + case SESSION_TRACK_STATE_CHANGE: + case SESSION_TRACK_TRANSACTION_CHARACTERISTICS: + case SESSION_TRACK_SYSTEM_VARIABLES: + net_field_length(&pos); /* ignore total length, item length will follow next */ + plen= net_field_length(&pos); + if (!ma_multi_malloc(0, + &session_item, sizeof(LIST), + &str, sizeof(MYSQL_LEX_STRING), + &data, plen, + NULL)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return -1; + } + str->length= plen; + str->str= data; + memcpy(str->str, (char *)pos, plen); + pos+= plen; + session_item->data= str; + mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item); + + /* in case schema has changed, we have to update mysql->db */ + if (si_type == SESSION_TRACK_SCHEMA) + { + free(mysql->db); + mysql->db= malloc(plen + 1); + memcpy(mysql->db, str->str, plen); + mysql->db[plen]= 0; + } + else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES) + { + my_bool set_charset= 0; + /* make sure that we update charset in case it has changed */ + if (!strncmp(str->str, "character_set_client", str->length)) + set_charset= 1; + plen= net_field_length(&pos); + if (!ma_multi_malloc(0, + &session_item, sizeof(LIST), + &str, sizeof(MYSQL_LEX_STRING), + &data, plen, + NULL)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return -1; + } + str->length= plen; + str->str= data; + memcpy(str->str, (char *)pos, plen); + pos+= plen; + session_item->data= str; + mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item); + if (set_charset && + strncmp(mysql->charset->csname, str->str, str->length) != 0) + { + char cs_name[64]; + MARIADB_CHARSET_INFO *cs_info; + memcpy(cs_name, str->str, str->length); + cs_name[str->length]= 0; + if ((cs_info = (MARIADB_CHARSET_INFO *)mysql_find_charset_name(cs_name))) + mysql->charset= cs_info; + } + } + break; + default: + /* not supported yet */ + plen= net_field_length(&pos); + pos+= plen; + break; + } + item_len-= (pos - old_pos); + } + } + for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++) + { + mysql->extension->session_state[i].list= list_reverse(mysql->extension->session_state[i].list); + mysql->extension->session_state[i].current= mysql->extension->session_state[i].list; + } + } + } + } + return(0); + } + if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ + { + int error=mysql_handle_local_infile(mysql, (char *)pos); + + if ((length=ma_net_safe_read(mysql)) == packet_error || error) + 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))) + return(-1); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, + (uint) field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + return(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + return(0); +} + +int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type, + const char **data, size_t *length) +{ + MYSQL_LEX_STRING *str; + if (!mysql->extension->session_state[type].current) + return 1; + + str= (MYSQL_LEX_STRING *)mysql->extension->session_state[type].current->data; + mysql->extension->session_state[type].current= mysql->extension->session_state[type].current->next; + + *data= str->str ? str->str : NULL; + *length= str->str ? str->length : 0; + return 0; +} + +int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type, + const char **data, size_t *length) +{ + mysql->extension->session_state[type].current= mysql->extension->session_state[type].list; + return mysql_session_track_get_next(mysql, type, data, length); +} + +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) +{ + my_bool skip_result= OPT_EXT_VAL(mysql, multi_command); + + if (length == (unsigned long)-1) + length= strlen(query); + + free_old_query(mysql); + + if (ma_simple_command(mysql, COM_QUERY,query,length,1,0)) + return(-1); + if (!skip_result) + return(mysql->methods->db_read_query_result(mysql)); + return(0); +} + +/************************************************************************** +** 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; + + if (!mysql->fields) + return(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(0); + } + mysql->status=MYSQL_STATUS_READY; /* server is ready */ + if (!(result=(MYSQL_RES*) calloc(1, sizeof(MYSQL_RES)+ + sizeof(ulong)*mysql->field_count))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + 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))) + { + free(result); + 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 */ + 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; + + if (!mysql->fields) + return(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(0); + } + if (!(result=(MYSQL_RES*) calloc(1, sizeof(*result)+ + sizeof(ulong)*mysql->field_count))) + return(0); + result->lengths=(ulong*) (result+1); + if (!(result->row=(MYSQL_ROW) + malloc(sizeof(result->row[0])*(mysql->field_count+1)))) + { /* Ptrs: to one row */ + free(result); + 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; + 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) +{ + if (!res) + return 0; + if (res->handle) + if (res->handle->status != MYSQL_STATUS_USE_RESULT && + res->handle->status != MYSQL_STATUS_GET_RESULT) + 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++; + return(res->current_row=res->row); + } + res->eof=1; + res->handle->status=MYSQL_STATUS_READY; + /* Don't clear handle in mysql_free_results */ + res->handle=0; + } + return((MYSQL_ROW) NULL); + } + { + MYSQL_ROW tmp; + if (!res->data_cursor) + { + return(res->current_row=(MYSQL_ROW) NULL); + } + tmp = res->data_cursor->data; + res->data_cursor = res->data_cursor->next; + 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, unsigned long long row) +{ + MYSQL_ROWS *tmp=0; + 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]; + snprintf(buff, 255, "SHOW DATABASES LIKE '%s'", wild ? wild : "%"); + if (mysql_query(mysql,buff)) + return(0); + return (mysql_store_result(mysql)); +} + + +/***************************************************************************** +** List all tables in a database +** If wild is given then only the tables matching wild are returned +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_tables(MYSQL *mysql, const char *wild) +{ + char buff[255]; + + snprintf(buff, 255, "SHOW TABLES LIKE '%s'", wild ? wild : "%"); + if (mysql_query(mysql,buff)) + return(0); + return (mysql_store_result(mysql)); +} + + +/************************************************************************** +** List all fields in a table +** If wild is given then only the fields matching wild are 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[255]; + int length= 0; + + LINT_INIT(query); + + length= snprintf(buff, 128, "%s%c%s", table, '\0', wild ? wild : ""); + + if (ma_simple_command(mysql, COM_FIELD_LIST,buff,length,1,0) || + !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + return(NULL); + + free_old_query(mysql); + if (!(result = (MYSQL_RES *) calloc(1, sizeof(MYSQL_RES)))) + { + free_rows(query); + 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; + 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; + + LINT_INIT(fields); + if (ma_simple_command(mysql, COM_PROCESS_INFO,0,0,0,0)) + 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))) + return(NULL); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + return(0); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + return(mysql_store_result(mysql)); +} + +/* 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]; + s_level[0]= (uchar)shutdown_level; + return(ma_simple_command(mysql, COM_SHUTDOWN, (char *)s_level, 1, 0, 0)); +} + +int STDCALL +mysql_refresh(MYSQL *mysql,uint options) +{ + uchar bits[1]; + bits[0]= (uchar) options; + return(ma_simple_command(mysql, COM_REFRESH,(char*) bits,1,0,0)); +} + +int STDCALL +mysql_kill(MYSQL *mysql,ulong pid) +{ + char buff[12]; + int4store(buff,pid); + /* if we kill our own thread, reading the response packet will fail */ + return(ma_simple_command(mysql, COM_PROCESS_KILL,buff,4,0,0)); +} + + +int STDCALL +mysql_dump_debug_info(MYSQL *mysql) +{ + return(ma_simple_command(mysql, COM_DEBUG,0,0,0,0)); +} + +char * STDCALL +mysql_stat(MYSQL *mysql) +{ + if (ma_simple_command(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 , SQLSTATE_UNKNOWN, 0); + return mysql->net.last_error; + } + return((char*) mysql->net.read_pos); +} + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + int rc; + rc= ma_simple_command(mysql, COM_PING,0,0,0,0); + + /* if connection was terminated and reconnect is true, try again */ + if (rc!=0 && mysql->options.reconnect) + rc= ma_simple_command(mysql, COM_PING,0,0,0,0); + return rc; +} + +char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + +static size_t mariadb_server_version_id(MYSQL *mysql) +{ + size_t 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 (major * 10000L + (unsigned long)(minor * 100L + patch)); +} + +unsigned long STDCALL mysql_get_server_version(MYSQL *mysql) +{ + return (unsigned long)mariadb_server_version_id(mysql); +} + +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*) MARIADB_CLIENT_VERSION_STR; +} + +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_keyval(const uchar *hash_entry, + unsigned int *length, + my_bool not_used __attribute__((unused))) +{ + /* Hash entry has the following format: + Offset: 0 key (\0 terminated) + key_length + 1 value (\0 terminated) + */ + uchar *p= (uchar *)hash_entry; + size_t len= strlen((char *)p); + *length= (unsigned int)len; + return p; +} + +void ma_hash_free(void *p) +{ + free(p); +} + +int STDCALL +mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) +{ + va_list ap; + void *arg1; + size_t stacksize; + struct mysql_async_context *ctxt; + + 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_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/ + if (!arg1 || test(*(my_bool*) 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: + OPT_SET_VALUE_STR(&mysql->options, my_cnf_file, (char *)arg1); + break; + case MYSQL_READ_DEFAULT_GROUP: + if (!arg1 || !((char *)arg1)[0]) + { +#if defined(__APPLE__) || defined(__FreeBSD__) + const char * appname = getprogname(); +#elif defined(_GNU_SOURCE) + const char * appname = program_invocation_short_name; +#elif defined(WIN32) + char appname[FN_REFLEN]= ""; + + if (GetModuleFileName(NULL, appname, FN_REFLEN)) + { + PathStripPath(appname); + PathRemoveExtension(appname); + } +#else + const char * appname = ""; +#endif + OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, appname); + break; + } + OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, (char *)arg1); + break; + case MYSQL_SET_CHARSET_DIR: + OPT_SET_VALUE_STR(&mysql->options, charset_dir, arg1); + break; + case MYSQL_SET_CHARSET_NAME: + OPT_SET_VALUE_STR(&mysql->options, charset_name, arg1); + break; + case MYSQL_OPT_RECONNECT: + mysql->options.reconnect= *(my_bool *)arg1; + break; + case MYSQL_OPT_PROTOCOL: + mysql->options.protocol= *((uint *)arg1); + break; +#ifdef _WIN32 + case MYSQL_SHARED_MEMORY_BASE_NAME: + OPT_SET_VALUE_STR(&mysql->options, shared_memory_base_name, arg1); + break; +#endif + 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= *(my_bool *)arg1; + break; + case MYSQL_PROGRESS_CALLBACK: + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (mysql->options.extension) + mysql->options.extension->report_progress= + (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1; + break; + case MYSQL_SERVER_PUBLIC_KEY: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, server_public_key, (char *)arg1); + break; + case MYSQL_PLUGIN_DIR: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, plugin_dir, (char *)arg1); + break; + case MYSQL_DEFAULT_AUTH: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, default_auth, (char *)arg1); + 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) + goto end; + my_context_destroy(&ctxt->async_context); + free(ctxt); + } + if (!(ctxt= (struct mysql_async_context *) + calloc(1, sizeof(*ctxt)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + 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)) + { + free(ctxt); + goto end; + } + if (!mysql->options.extension) + if(!(mysql->options.extension= (struct st_mysql_options_extension *) + calloc(1, sizeof(struct st_mysql_options_extension)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + mysql->options.extension->async_context= ctxt; + break; + case MYSQL_OPT_MAX_ALLOWED_PACKET: + if (mysql) + mysql->options.max_allowed_packet= (unsigned long)(*(size_t *)arg1); + else + max_allowed_packet= (unsigned long)(*(size_t *)arg1); + break; + case MYSQL_OPT_NET_BUFFER_LENGTH: + net_buffer_length= (unsigned long)(*(size_t *)arg1); + break; + case MYSQL_OPT_SSL_ENFORCE: + mysql->options.use_ssl= (*(my_bool *)arg1); + 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: + OPT_SET_VALUE_STR(&mysql->options, ssl_key, (char *)arg1); + break; + case MYSQL_OPT_SSL_CERT: + OPT_SET_VALUE_STR(&mysql->options, ssl_cert, (char *)arg1); + break; + case MYSQL_OPT_SSL_CA: + OPT_SET_VALUE_STR(&mysql->options, ssl_ca, (char *)arg1); + break; + case MYSQL_OPT_SSL_CAPATH: + OPT_SET_VALUE_STR(&mysql->options, ssl_capath, (char *)arg1); + break; + case MYSQL_OPT_SSL_CIPHER: + OPT_SET_VALUE_STR(&mysql->options, ssl_cipher, (char *)arg1); + break; + case MYSQL_OPT_SSL_CRL: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); + break; + case MYSQL_OPT_SSL_CRLPATH: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1); + break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + { + uchar *h; + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs) && + (h= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, + arg1 ? (uint)strlen((char *)arg1) : 0))) + { + uchar *p= h; + size_t key_len= strlen((char *)p); + mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len); + p+= key_len + 1; + key_len= strlen((char *)p); + mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len); + hash_delete(&mysql->options.extension->connect_attrs, h); + } + + } + 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 MARIADB_OPT_CONNECTION_HANDLER: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, connection_handler, (char *)arg1); + break; + case MARIADB_OPT_PORT: + OPT_SET_VALUE_INT(&mysql->options, port, *((uint *)arg1)); + break; + case MARIADB_OPT_UNIXSOCKET: + OPT_SET_VALUE_STR(&mysql->options, unix_socket, arg1); + break; + case MARIADB_OPT_USER: + OPT_SET_VALUE_STR(&mysql->options, user, arg1); + break; + case MARIADB_OPT_HOST: + OPT_SET_VALUE_STR(&mysql->options, host, arg1); + break; + case MARIADB_OPT_SCHEMA: + OPT_SET_VALUE_STR(&mysql->options, db, arg1); + break; + case MARIADB_OPT_DEBUG: + break; + case MARIADB_OPT_FOUND_ROWS: + mysql->options.client_flag|= CLIENT_FOUND_ROWS; + break; + case MARIADB_OPT_INTERACTIVE: + mysql->options.client_flag|= CLIENT_INTERACTIVE; + break; + case MARIADB_OPT_MULTI_RESULTS: + mysql->options.client_flag|= CLIENT_MULTI_RESULTS; + break; + case MARIADB_OPT_MULTI_STATEMENTS: + mysql->options.client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS; + break; + case MARIADB_OPT_PASSWORD: + OPT_SET_VALUE_STR(&mysql->options, password, arg1); + break; + case MARIADB_OPT_USERDATA: + { + void *data= va_arg(ap, void *); + uchar *buffer, *p; + char *key= (char *)arg1; + + if (!key || !data) + { + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto end; + } + + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (!hash_inited(&mysql->options.extension->userdata)) + { + if (_hash_init(&mysql->options.extension->userdata, + 0, 0, 0, ma_get_hash_keyval, ma_hash_free, 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + /* check if key is already in buffer */ + p= (uchar *)hash_search(&mysql->options.extension->userdata, + (uchar *)key, + (uint)strlen(key)); + if (p) + { + p+= strlen(key) + 1; + memcpy(p, &data, sizeof(void *)); + break; + } + + if (!(buffer= (uchar *)malloc(strlen(key) + 1 + sizeof(void *)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + + p= buffer; + strcpy((char *)p, key); + p+= strlen(key) + 1; + memcpy(p, &data, sizeof(void *)); + + if (hash_insert(&mysql->options.extension->userdata, buffer)) + { + free(buffer); + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + 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= key_len + value_len + + get_store_length(key_len) + + get_store_length(value_len); + + /* since we store terminating zero character in hash, we need + * to increase lengths */ + key_len++; + value_len++; + + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (!key_len || + storage_len + mysql->options.extension->connect_attrs_len > 0xFFFF) + { + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto end; + } + + if (!hash_inited(&mysql->options.extension->connect_attrs)) + { + if (_hash_init(&mysql->options.extension->connect_attrs, + 0, 0, 0, ma_get_hash_keyval, ma_hash_free, 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + if ((buffer= (uchar *)malloc(key_len + value_len))) + { + uchar *p= buffer; + strcpy((char *)p, arg1); + p+= (strlen(arg1) + 1); + if (arg2) + strcpy((char *)p, arg2); + + if (hash_insert(&mysql->options.extension->connect_attrs, buffer)) + { + free(buffer); + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto end; + } + mysql->options.extension->connect_attrs_len+= storage_len; + } + else + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + break; + case MYSQL_ENABLE_CLEARTEXT_PLUGIN: + break; + case MYSQL_SECURE_AUTH: + mysql->options.secure_auth= *(my_bool *)arg1; + break; + case MYSQL_OPT_BIND: + OPT_SET_VALUE_STR(&mysql->options, bind_address, arg1); + break; + case MARIADB_OPT_TLS_CIPHER_STRENGTH: + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, tls_cipher_strength, *((unsigned int *)arg1)); + break; + case MARIADB_OPT_SSL_FP: + case MARIADB_OPT_TLS_PEER_FP: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp, (char *)arg1); + mysql->options.use_ssl= 1; + break; + case MARIADB_OPT_SSL_FP_LIST: + case MARIADB_OPT_TLS_PEER_FP_LIST: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp_list, (char *)arg1); + mysql->options.use_ssl= 1; + break; + case MARIADB_OPT_TLS_PASSPHRASE: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_pw, (char *)arg1); + break; + case MARIADB_OPT_CONNECTION_READ_ONLY: + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, read_only, *(my_bool *)arg1); + break; + case MARIADB_OPT_PROXY_HEADER: + { + size_t arg2 = va_arg(ap, size_t); + OPT_SET_EXTENDED_VALUE(&mysql->options, proxy_header, (char *)arg1); + OPT_SET_EXTENDED_VALUE(&mysql->options, proxy_header_len, arg2); + } + break; + case MARIADB_OPT_TLS_VERSION: + case MYSQL_OPT_TLS_VERSION: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_version, (char *)arg1); + break; + default: + va_end(ap); + return(-1); + } + va_end(ap); + return(0); +end: + va_end(ap); + return(1); +} + +int STDCALL +mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) +{ + va_list ap; + + va_start(ap, arg); + + switch(option) { + case MYSQL_OPT_CONNECT_TIMEOUT: + *((uint *)arg)= mysql->options.connect_timeout; + break; + case MYSQL_OPT_COMPRESS: + *((my_bool *)arg)= mysql->options.compress; + break; + case MYSQL_OPT_NAMED_PIPE: + *((my_bool *)arg)= mysql->options.named_pipe; + break; + case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/ + *((uint *)arg)= test(mysql->options.client_flag & CLIENT_LOCAL_FILES); + break; + case MYSQL_INIT_COMMAND: + /* mysql_get_optionsv(mysql, MYSQL_INIT_COMMAND, commands, elements) */ + { + unsigned int *elements; + if (arg) + *((char **)arg)= mysql->options.init_command ? mysql->options.init_command->buffer : NULL; + if ((elements= va_arg(ap, unsigned int *))) + *elements= mysql->options.init_command ? mysql->options.init_command->elements : 0; + } + break; + case MYSQL_READ_DEFAULT_FILE: + *((char **)arg)= mysql->options.my_cnf_file; + break; + case MYSQL_READ_DEFAULT_GROUP: + *((char **)arg)= mysql->options.my_cnf_group; + break; + case MYSQL_SET_CHARSET_DIR: + /* not supported in this version. Since all character sets + are internally available, we don't throw an error */ + *((char **)arg)= NULL; + break; + case MYSQL_SET_CHARSET_NAME: + if (mysql->charset) + *((const char **)arg)= mysql->charset->csname; + else + *((char **)arg)= mysql->options.charset_name; + break; + case MYSQL_OPT_RECONNECT: + *((my_bool *)arg)= mysql->options.reconnect; + break; + case MYSQL_OPT_PROTOCOL: + *((uint *)arg)= mysql->options.protocol; + break; + case MYSQL_OPT_READ_TIMEOUT: + *((uint *)arg)= mysql->options.read_timeout; + break; + case MYSQL_OPT_WRITE_TIMEOUT: + *((uint *)arg)= mysql->options.write_timeout; + break; + case MYSQL_REPORT_DATA_TRUNCATION: + *((my_bool *)arg)= mysql->options.report_data_truncation; + break; + case MYSQL_PROGRESS_CALLBACK: + *((void (**)(const MYSQL *, uint, uint, double, const char *, uint))arg)= + mysql->options.extension ? mysql->options.extension->report_progress : NULL; + break; + case MYSQL_SERVER_PUBLIC_KEY: + *((char **)arg)= mysql->options.extension ? + mysql->options.extension->server_public_key : NULL; + break; + case MYSQL_PLUGIN_DIR: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->plugin_dir : NULL; + break; + case MYSQL_DEFAULT_AUTH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->default_auth : NULL; + break; + case MYSQL_OPT_NONBLOCK: + *((my_bool *)arg)= test(mysql->options.extension && mysql->options.extension->async_context); + break; + case MYSQL_OPT_SSL_ENFORCE: + *((my_bool *)arg)= mysql->options.use_ssl; + break; + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + *((my_bool *)arg)= test(mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT); + break; + case MYSQL_OPT_SSL_KEY: + *((char **)arg)= mysql->options.ssl_key; + break; + case MYSQL_OPT_SSL_CERT: + *((char **)arg)= mysql->options.ssl_cert; + break; + case MYSQL_OPT_SSL_CA: + *((char **)arg)= mysql->options.ssl_ca; + break; + case MYSQL_OPT_SSL_CAPATH: + *((char **)arg)= mysql->options.ssl_capath; + break; + case MYSQL_OPT_SSL_CIPHER: + *((char **)arg)= mysql->options.ssl_cipher; + break; + case MYSQL_OPT_SSL_CRL: + *((char **)arg)= mysql->options.extension ? mysql->options.ssl_cipher : NULL; + break; + case MYSQL_OPT_SSL_CRLPATH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL; + break; + case MYSQL_OPT_CONNECT_ATTRS: + /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */ + { + unsigned int i, *elements; + char **key= NULL; + void *arg1; + char **val= NULL; + + if (arg) + key= *(char ***)arg; + + arg1= va_arg(ap, char **); + if (arg1) + val= *(char ***)arg1; + + if (!(elements= va_arg(ap, unsigned int *))) + goto error; + + if (!elements) + goto error; + + *elements= 0; + + if (!mysql->options.extension || + !hash_inited(&mysql->options.extension->connect_attrs)) + break; + + *elements= mysql->options.extension->connect_attrs.records; + + if (val || key) + { + for (i=0; i < *elements; i++) + { + uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); + if (key) + key[i]= (char *)p; + p+= strlen((char *)p) + 1; + if (val) + val[i]= (char *)p; + } + } + } + break; + case MYSQL_OPT_MAX_ALLOWED_PACKET: + *((unsigned long *)arg)= (mysql) ? mysql->options.max_allowed_packet : + max_allowed_packet; + break; + case MYSQL_OPT_NET_BUFFER_LENGTH: + *((unsigned long *)arg)= net_buffer_length; + break; + case MYSQL_SECURE_AUTH: + *((my_bool *)arg)= mysql->options.secure_auth; + break; + case MYSQL_OPT_BIND: + *((char **)arg)= mysql->options.bind_address; + break; + case MARIADB_OPT_TLS_CIPHER_STRENGTH: + *((unsigned int *)arg) = mysql->options.extension ? mysql->options.extension->tls_cipher_strength : 0; + break; + case MARIADB_OPT_SSL_FP: + case MARIADB_OPT_TLS_PEER_FP: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp : NULL; + break; + case MARIADB_OPT_SSL_FP_LIST: + case MARIADB_OPT_TLS_PEER_FP_LIST: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp_list : NULL; + break; + case MARIADB_OPT_TLS_PASSPHRASE: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_pw : NULL; + break; + case MARIADB_OPT_CONNECTION_READ_ONLY: + *((my_bool *)arg)= mysql->options.extension ? mysql->options.extension->read_only : 0; + break; + case MARIADB_OPT_USERDATA: + /* nysql_get_optionv(mysql, MARIADB_OPT_USERDATA, key, value) */ + { + uchar *p; + void *data= va_arg(ap, void *); + char *key= (char *)arg; + if (key && data && mysql->options.extension && hash_inited(&mysql->options.extension->userdata) && + (p= (uchar *)hash_search(&mysql->options.extension->userdata, (uchar *)key, + (uint)strlen((char *)key)))) + { + p+= strlen(key) + 1; + *((void **)data)= *((void **)p); + break; + } + if (data) + *((void **)data)= NULL; + } + break; + case MARIADB_OPT_CONNECTION_HANDLER: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->connection_handler : NULL; + break; + default: + va_end(ap); + return(-1); + } + va_end(ap); + return(0); +error: + va_end(ap); + return(-1); +} + +int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option, void *arg) +{ + return mysql_get_optionv(mysql, option, arg); +} + +int STDCALL +mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) +{ + return mysql_optionsv(mysql, option, arg); +} + +int STDCALL +mysql_options4(MYSQL *mysql,enum mysql_option option, const void *arg1, const void *arg2) +{ + return mysql_optionsv(mysql, option, arg1, arg2); +} +/**************************************************************************** +** 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) +{ + return((my_bool) mysql_real_query(mysql, (mode) ? "SET autocommit=1" : + "SET autocommit=0", 16)); +} + +my_bool STDCALL mysql_commit(MYSQL *mysql) +{ + return((my_bool)mysql_real_query(mysql, "COMMIT", (unsigned long) sizeof("COMMIT"))); +} + +my_bool STDCALL mysql_rollback(MYSQL *mysql) +{ + return((my_bool)mysql_real_query(mysql, "ROLLBACK", (unsigned long)sizeof("ROLLBACK"))); +} + +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) +{ + return (mysql)->insert_id; +} + +uint STDCALL mysql_errno(MYSQL *mysql) +{ + return mysql ? mysql->net.last_errno : 0; +} + +const char * STDCALL mysql_error(MYSQL *mysql) +{ + return mysql ? (mysql)->net.last_error : (char *)""; +} + +const char *STDCALL mysql_info(MYSQL *mysql) +{ + return (mysql)->info; +} + +my_bool STDCALL mysql_more_results(MYSQL *mysql) +{ + return(test(mysql->server_status & SERVER_MORE_RESULTS_EXIST)); +} + +int STDCALL mysql_next_result(MYSQL *mysql) +{ + + /* make sure communication is not blocking */ + if (mysql->status != MYSQL_STATUS_READY) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + 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) + { + return(mysql->methods->db_read_query_result(mysql)); + } + + 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(ma_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); +} + +static void mariadb_get_charset_info(MYSQL *mysql, MY_CHARSET_INFO *cs) +{ + if (!cs) + 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; + + return; +} + +void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs) +{ + mariadb_get_charset_info(mysql, cs); +} + +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) +{ + const MARIADB_CHARSET_INFO *cs; + + if (!csname) + goto error; + + if ((cs= mysql_find_charset_name(csname))) + { + char buff[64]; + + snprintf(buff, 63, "SET NAMES %s", cs->csname); + if (!mysql_real_query(mysql, buff, (unsigned long)strlen(buff))) + { + mysql->charset= cs; + return(0); + } + } + +error: + my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, + 0, csname, "compiled_in"); + 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; +} + +#ifndef _WIN32 +#include +static void ignore_sigpipe() +{ + signal(SIGPIPE, SIG_IGN); +} +#else +#define ignore_sigpipe() +#endif + +#ifdef _WIN32 +static int mysql_once_init() +#else +static void mysql_once_init() +#endif +{ + ma_init(); /* Will init threads */ + init_client_errs(); + get_default_configuration_dirs(); + if (mysql_client_plugin_init()) + { +#ifdef _WIN32 + return 1; +#else + return; +#endif + } + if (!mysql_port) + { + struct servent *serv_ptr; + char *env; + + mysql_port = MARIADB_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*)MARIADB_NAMEDPIPE; +#else + mysql_unix_port = (char*)MARIADB_UNIX_ADDR; +#endif + if ((env = getenv("MYSQL_UNIX_PORT")) || + (env = getenv("MARIADB_UNIX_PORT"))) + mysql_unix_port = env; + } + if (!mysql_ps_subsystem_initialized) + mysql_init_ps_subsystem(); +#ifdef HAVE_TLS + ma_tls_start(0, 0); +#endif + ignore_sigpipe(); + mysql_client_init = 1; +#ifdef _WIN32 + return 0; +#endif +} + +#ifdef _WIN32 +BOOL CALLBACK win_init_once( + PINIT_ONCE InitOnce, + PVOID Parameter, + PVOID *lpContext) +{ + return !mysql_once_init(); + return TRUE; +} +#endif + +int STDCALL mysql_server_init(int argc __attribute__((unused)), + char **argv __attribute__((unused)), + char **groups __attribute__((unused))) +{ +#ifdef _WIN32 + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + BOOL ret = InitOnceExecuteOnce(&init_once, win_init_once, NULL, NULL); + return ret? 0: 1; +#else + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + return pthread_once(&init_once, mysql_once_init); +#endif +} + +void STDCALL mysql_server_end(void) +{ + if (!mysql_client_init) + return; + + release_configuration_dirs(); + mysql_client_plugin_deinit(); + + list_free(pvio_callback, 0); + if (ma_init_done) + ma_end(0); +#ifdef HAVE_TLS + ma_pvio_tls_end(); +#endif + mysql_client_init= 0; + ma_init_done= 0; +} + +my_bool STDCALL mysql_thread_init(void) +{ + return 0; +} + +void STDCALL mysql_thread_end(void) +{ +} + +int STDCALL mysql_set_server_option(MYSQL *mysql, + enum enum_mysql_set_option option) +{ + char buffer[2]; + int2store(buffer, (uint)option); + return(ma_simple_command(mysql, COM_SET_OPTION, buffer, sizeof(buffer), 0, 0)); +} + +ulong STDCALL mysql_get_client_version(void) +{ + return MARIADB_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"; +} + +static my_socket mariadb_get_socket(MYSQL *mysql) +{ + my_socket sock= INVALID_SOCKET; + if (mysql->net.pvio) + { + ma_pvio_get_handle(mysql->net.pvio, &sock); + + } + /* if an asynchronous connect is in progress, we need to obtain + pvio handle from async_context until the connection was + successfully established. + */ + else if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->pvio) + { + ma_pvio_get_handle(mysql->options.extension->async_context->pvio, &sock); + } + return sock; +} + +my_socket STDCALL +mysql_get_socket(MYSQL *mysql) +{ + return mariadb_get_socket(mysql); +} + +MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_name(const char *csname) +{ + return (MARIADB_CHARSET_INFO *)mysql_find_charset_name(csname); +} + +MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr) +{ + return (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(csnr); +} + +my_bool STDCALL mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...) +{ + va_list ap; + + va_start(ap, arg); + + switch(value) { + case MARIADB_MAX_ALLOWED_PACKET: + *((size_t *)arg)= (size_t)max_allowed_packet; + break; + case MARIADB_NET_BUFFER_LENGTH: + *((size_t *)arg)= (size_t)net_buffer_length; + break; + case MARIADB_CONNECTION_ERROR_ID: + if (!mysql) + goto error; + *((unsigned int *)arg)= mysql->net.last_errno; + break; + case MARIADB_CONNECTION_ERROR: + if (!mysql) + goto error; + *((char **)arg)= mysql->net.last_error; + break; + case MARIADB_CONNECTION_SQLSTATE: + if (!mysql) + goto error; + *((char **)arg)= mysql->net.sqlstate; + break; + case MARIADB_CONNECTION_TLS_VERSION: + #ifdef HAVE_TLS + if (mysql && mysql->net.pvio && mysql->net.pvio->ctls) + *((char **)arg)= (char *)ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls); + else + #endif + goto error; + break; + case MARIADB_CONNECTION_TLS_VERSION_ID: + #ifdef HAVE_TLS + if (mysql && mysql->net.pvio && mysql->net.pvio->ctls) + *((unsigned int *)arg)= ma_pvio_tls_get_protocol_version_id(mysql->net.pvio->ctls); + else + #endif + goto error; + break; + case MARIADB_TLS_LIBRARY: +#ifdef HAVE_TLS + *((const char **)arg)= tls_library_version; +#else + *((char **)arg)= "Off"; +#endif + break; + case MARIADB_CLIENT_VERSION: + *((const char **)arg)= MARIADB_CLIENT_VERSION_STR; + break; + case MARIADB_CLIENT_VERSION_ID: + *((size_t *)arg)= MARIADB_VERSION_ID; + break; + case MARIADB_CONNECTION_SERVER_VERSION: + if (mysql) + *((char **)arg)= mysql->server_version; + else + goto error; + break; + case MARIADB_CONNECTION_SERVER_TYPE: + if (mysql) + *((const char **)arg)= mariadb_connection(mysql) ? "MariaDB" : "MySQL"; + else + goto error; + break; + case MARIADB_CONNECTION_SERVER_VERSION_ID: + if (mysql) + *((size_t *)arg)= mariadb_server_version_id(mysql); + else + goto error; + break; + case MARIADB_CONNECTION_PROTOCOL_VERSION_ID: + if (mysql) + *((unsigned int *)arg)= mysql->protocol_version; + else + goto error; + break; + case MARIADB_CONNECTION_MARIADB_CHARSET_INFO: + if (mysql) + mariadb_get_charset_info(mysql, (MY_CHARSET_INFO *)arg); + else + goto error; + break; + case MARIADB_CONNECTION_SOCKET: + if (mysql) + *((my_socket *)arg)= mariadb_get_socket(mysql); + else + goto error; + break; + case MARIADB_CONNECTION_TYPE: + if (mysql && mysql->net.pvio) + *((int *)arg)= (int)mysql->net.pvio->type; + else + goto error; + break; + case MARIADB_CONNECTION_ASYNC_TIMEOUT_MS: + if (mysql && mysql->options.extension && mysql->options.extension->async_context) + *((unsigned int *)arg)= mysql->options.extension->async_context->timeout_value; + break; + case MARIADB_CONNECTION_ASYNC_TIMEOUT: + if (mysql && mysql->options.extension && mysql->options.extension->async_context) + { + unsigned int timeout= mysql->options.extension->async_context->timeout_value; + if (timeout > UINT_MAX - 999) + *((unsigned int *)arg)= (timeout - 1)/1000 + 1; + else + *((unsigned int *)arg)= (timeout+999)/1000; + } + break; + case MARIADB_CHARSET_NAME: + { + char *name; + name= va_arg(ap, char *); + if (name) + *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_name(name); + else + goto error; + } + break; + case MARIADB_CHARSET_ID: + { + unsigned int nr; + nr= va_arg(ap, unsigned int); + *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(nr); + } + break; + case MARIADB_CONNECTION_SSL_CIPHER: + #ifdef HAVE_TLS + if (mysql && mysql->net.pvio && mysql->net.pvio->ctls) + *((char **)arg)= (char *)ma_pvio_tls_cipher(mysql->net.pvio->ctls); + else + #endif + goto error; + break; + case MARIADB_CLIENT_ERRORS: + *((char ***)arg)= (char **)client_errors; + break; + case MARIADB_CONNECTION_INFO: + if (mysql) + *((char **)arg)= (char *)mysql->info; + else + goto error; + break; + case MARIADB_CONNECTION_PVIO_TYPE: + if (mysql && mysql->net.pvio) + *((unsigned int *)arg)= (unsigned int)mysql->net.pvio->type; + else + goto error; + break; + case MARIADB_CONNECTION_SCHEMA: + if (mysql) + *((char **)arg)= mysql->db; + else + goto error; + break; + case MARIADB_CONNECTION_USER: + if (mysql) + *((char **)arg)= mysql->user; + else + goto error; + break; + case MARIADB_CONNECTION_PORT: + if (mysql) + *((unsigned int *)arg)= mysql->port; + else + goto error; + break; + case MARIADB_CONNECTION_UNIX_SOCKET: + if (mysql) + *((char **)arg)= mysql->unix_socket; + else + goto error; + break; + case MARIADB_CONNECTION_HOST: + if (mysql) + *((char **)arg)= mysql->host; + else + goto error; + break; + case MARIADB_CONNECTION_SERVER_STATUS: + if (mysql) + *((unsigned int *)arg)= mysql->server_status; + else + goto error; + break; + case MARIADB_CONNECTION_SERVER_CAPABILITIES: + if (mysql) + *((unsigned long *)arg)= mysql->server_capabilities; + else + goto error; + break; + case MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES: + if (mysql) + *((unsigned long *)arg)= mysql->extension->mariadb_server_capabilities; + else + goto error; + break; + case MARIADB_CONNECTION_CLIENT_CAPABILITIES: + if (mysql) + *((unsigned long *)arg)= mysql->client_flag; + else + goto error; + break; + default: + va_end(ap); + return(-1); + } + va_end(ap); + return(0); +error: + va_end(ap); + return(-1); +} + +my_bool STDCALL mariadb_get_info(MYSQL *mysql, enum mariadb_value value, void *arg) +{ + return mariadb_get_infov(mysql, value, arg); +} + +/* + Immediately aborts connection, making all subsequent read/write operations fail. + Does not invalidate memory used for mysql structure, nor closes any communication + channels - mysql_close is still needed. + Useful to break long query, in situation sending KILL is not possible. +*/ +int STDCALL mariadb_cancel(MYSQL *mysql) +{ + if (!mysql || !mysql->net.pvio || !mysql->net.pvio->methods || !mysql->net.pvio->methods->shutdown) + { + return 1; + } + else + { + MARIADB_PVIO *pvio = mysql->net.pvio; + return pvio->methods->shutdown(pvio); + } +} + +/* compatibility functions for MariaDB */ +void STDCALL +mysql_debug(const char *debug __attribute__((unused))) +{ + return; +} + +/******************************************************************** + mysql_net_ functions - low-level API to MySQL protocol +*********************************************************************/ +ulong STDCALL mysql_net_read_packet(MYSQL *mysql) +{ + return ma_net_safe_read(mysql); +} + +ulong STDCALL mysql_net_field_length(uchar **packet) +{ + return net_field_length(packet); +} + +my_bool STDCALL mysql_embedded(void) +{ +#ifdef EMBEDDED_LIBRARY + return 1; +#else + return 0; +#endif +} + +MYSQL_PARAMETERS *STDCALL +mysql_get_parameters(void) +{ + return &mariadb_internal_parameters; +} + +int STDCALL mysql_reset_connection(MYSQL *mysql) +{ + int rc; + + /* check if connection handler is active */ + if (IS_CONNHDLR_ACTIVE(mysql)) + { + if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reset) + return(mysql->extension->conn_hdlr->plugin->reset(mysql)); + } + + /* skip result sets */ + if (mysql->status == MYSQL_STATUS_USE_RESULT || + mysql->status == MYSQL_STATUS_GET_RESULT || + mysql->status & SERVER_MORE_RESULTS_EXIST) + { + mthd_my_skip_result(mysql); + mysql->status= MYSQL_STATUS_READY; + } + + rc= ma_simple_command(mysql, COM_RESET_CONNECTION, 0, 0, 0, 0); + if (rc && mysql->options.reconnect) + { + /* There is no big sense in resetting but we need reconnect */ + rc= ma_simple_command(mysql, COM_RESET_CONNECTION,0,0,0,0); + } + if (rc) + return 1; + + /* reset the connection in all active statements */ + ma_invalidate_stmts(mysql, "mysql_reset_connection()"); + free_old_query(mysql); + mysql->status= MYSQL_STATUS_READY; + mysql->affected_rows= ~(my_ulonglong)0; + mysql->insert_id= 0; + return 0; +} + +#undef STDCALL +/* API functions for usage in dynamic plugins */ +struct st_mariadb_api MARIADB_API= +{ + mysql_num_rows, + mysql_num_fields, + mysql_eof, + mysql_fetch_field_direct, + mysql_fetch_fields, + mysql_row_tell, + mysql_field_tell, + mysql_field_count, + mysql_more_results, + mysql_next_result, + mysql_affected_rows, + mysql_autocommit, + mysql_commit, + mysql_rollback, + mysql_insert_id, + mysql_errno, + mysql_error, + mysql_info, + mysql_thread_id, + mysql_character_set_name, + mysql_get_character_set_info, + mysql_set_character_set, + mariadb_get_infov, + mariadb_get_info, + mysql_init, + mysql_ssl_set, + mysql_get_ssl_cipher, + mysql_change_user, + mysql_real_connect, + mysql_close, + mysql_select_db, + mysql_query, + mysql_send_query, + mysql_read_query_result, + mysql_real_query, + mysql_shutdown, + mysql_dump_debug_info, + mysql_refresh, + mysql_kill, + mysql_ping, + mysql_stat, + mysql_get_server_info, + mysql_get_server_version, + mysql_get_host_info, + mysql_get_proto_info, + mysql_list_dbs, + mysql_list_tables, + mysql_list_fields, + mysql_list_processes, + mysql_store_result, + mysql_use_result, + mysql_options, + mysql_free_result, + mysql_data_seek, + mysql_row_seek, + mysql_field_seek, + mysql_fetch_row, + mysql_fetch_lengths, + mysql_fetch_field, + mysql_escape_string, + mysql_real_escape_string, + mysql_thread_safe, + mysql_warning_count, + mysql_sqlstate, + mysql_server_init, + mysql_server_end, + mysql_thread_end, + mysql_thread_init, + mysql_set_server_option, + mysql_get_client_info, + mysql_get_client_version, + mariadb_connection, + mysql_get_server_name, + mariadb_get_charset_by_name, + mariadb_get_charset_by_nr, + mariadb_convert_string, + mysql_optionsv, + mysql_get_optionv, + mysql_get_option, + mysql_hex_string, + mysql_get_socket, + mysql_get_timeout_value, + mysql_get_timeout_value_ms, + mariadb_reconnect, + mysql_stmt_init, + mysql_stmt_prepare, + mysql_stmt_execute, + mysql_stmt_fetch, + mysql_stmt_fetch_column, + mysql_stmt_store_result, + mysql_stmt_param_count, + mysql_stmt_attr_set, + mysql_stmt_attr_get, + mysql_stmt_bind_param, + mysql_stmt_bind_result, + mysql_stmt_close, + mysql_stmt_reset, + mysql_stmt_free_result, + mysql_stmt_send_long_data, + mysql_stmt_result_metadata, + mysql_stmt_param_metadata, + mysql_stmt_errno, + mysql_stmt_error, + mysql_stmt_sqlstate, + mysql_stmt_row_seek, + mysql_stmt_row_tell, + mysql_stmt_data_seek, + mysql_stmt_num_rows, + mysql_stmt_affected_rows, + mysql_stmt_insert_id, + mysql_stmt_field_count, + mysql_stmt_next_result, + mysql_stmt_more_results, + mariadb_stmt_execute_direct, + mysql_reset_connection +}; + +/* + * 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_mariadb_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, + /* set error */ + my_set_error, + /* invalidate statements */ + ma_invalidate_stmts, + &MARIADB_API +}; -- cgit v1.1