diff --git a/CMakeLists.txt b/CMakeLists.txt index 636de8940..8d21bd6ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,13 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) ENDIF() +#Allow access to non existing targets +IF(CMAKE_VERSION VERSION_GREATER "2.9.9") + CMAKE_POLICY(SET CMP0026 OLD) + CMAKE_POLICY(SET CMP0042 OLD) + CMAKE_POLICY(SET CMP0045 OLD) +ENDIF() + SET(MARIADB_CONNECTOR_C_COPYRIGHT "2013-2015 MariaDB Corporation Ab") ### Options ### diff --git a/include/errmsg.h b/include/errmsg.h index ff795a7b8..8a91519a9 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -84,6 +84,7 @@ extern const char *mariadb_client_errors[]; /* Error messages */ #define CR_EVENT_CREATE_FAILED 5000 #define CR_BIND_ADDR_FAILED 5001 +#define CR_FUNCTION_NOT_SUPPORTED 5002 #define SQLSTATE_UNKNOWN "HY000" diff --git a/include/ma_common.h b/include/ma_common.h index 6a6a80a05..6d61f1f0e 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -52,6 +52,8 @@ struct st_mysql_options_extension { char *ssl_fp; /* finger print of server certificate */ char *ssl_fp_list; /* white list of finger prints */ char *ssl_pw; /* password for encrypted certificates */ + my_bool multi_command; /* indicates if client wants to send multiple + commands in one packet */ }; #define OPT_HAS_EXT_VAL(a,key) \ diff --git a/include/mysql.h b/include/mysql.h index d2a5c7e38..b262af85a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -214,6 +214,7 @@ extern unsigned int mariadb_deinitialize_ssl; MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */ MARIADB_OPT_SSL_FP_LIST, /* finger print white list for server certificate verification */ MARIADB_OPT_SSL_PASSWORD, /* password for encrypted certificates */ + MARIADB_OPT_COM_MULTI, MARIADB_OPT_CONNECTION_READ_ONLY }; diff --git a/include/mysql_com.h b/include/mysql_com.h index eba227fb5..045e4d72e 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -87,6 +87,7 @@ enum enum_server_command COM_SET_OPTION = 27, COM_STMT_FETCH = 28, COM_DAEMON, + COM_MULTI = 255, COM_END }; @@ -156,11 +157,12 @@ enum enum_server_command /* MariaDB specific capabilities */ #define MARIADB_CLIENT_FLAGS 0xFFFFFFFF00000000ULL +#define MARIADB_CLIENT_COM_MULTI 1 #define MARIADB_CLIENT_PROGRESS (1ULL << 32) -#define MARIADB_CLIENT_EXTENDED_PROTOCOL (1ULL << 63) +#define MARIADB_CLIENT_EXTENDED_FLAGS (1ULL << 63) #define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\ - MARIADB_CLIENT_EXTENDED_PROTOCOL) + MARIADB_CLIENT_EXTENDED_FLAGS) #define CLIENT_SUPPORTED_FLAGS (CLIENT_LONG_PASSWORD |\ CLIENT_FOUND_ROWS |\ @@ -245,6 +247,7 @@ typedef struct st_net { MARIADB_PVIO *pvio; unsigned char *buff; unsigned char *buff_end,*write_pos,*read_pos; + unsigned char *mbuff, *mbuff_end, *mbuff_pos; my_socket fd; /* For Perl DBI/dbd */ unsigned long remain_in_buf,length; unsigned long buf_length, where_b; diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index ed0c041e9..9de5f7d01 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -34,6 +34,7 @@ SET(EXPORT_SYMBOLS mariadb_dyncol_val_double mariadb_dyncol_val_long mariadb_dyncol_val_str + mariadb_flush_multi_command myodbc_remove_escape mysql_affected_rows mysql_autocommit diff --git a/libmariadb/errmsg.c b/libmariadb/errmsg.c index 8ae0fa04f..d228d6b3e 100644 --- a/libmariadb/errmsg.c +++ b/libmariadb/errmsg.c @@ -149,6 +149,7 @@ const char *mariadb_client_errors[] = { /* 5000 */ "Creating an event failed (Errorcode: %d)", /* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)", + /* 5002 */ "Server doesn't support function '%s'", "" }; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 9ab13e461..d2409b35e 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -88,6 +88,8 @@ extern const CHARSET_INFO * mysql_find_charset_nr(uint charsetnr); extern const CHARSET_INFO * mysql_find_charset_name(const char * const name); extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *data_plugin, const char *db); +extern int net_add_multi_command(NET *net, uchar command, const uchar *packet, + size_t length); extern LIST *pvio_callback; @@ -350,11 +352,21 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, { NET *net= &mysql->net; int result= -1; + my_bool is_multi= 0; - DBUG_ENTER("mthd_my_send_command"); + if (OPT_HAS_EXT_VAL(mysql, multi_command)) + is_multi= mysql->options.extension->multi_command; + + DBUG_ENTER("mthd_my_send_cmd"); DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); + if (is_multi) + { + /* todo: error handling */ + DBUG_RETURN(net_add_multi_command(&mysql->net, command, arg, length)); + } + if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data) { result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg); @@ -661,7 +673,7 @@ enum option_val #define OPT_SET_EXTENDED_VALUE_INT(OPTS, KEY, VAL) \ CHECK_OPT_EXTENSION_SET(OPTS) \ -+ (OPTS)->extension->KEY= (VAL) + (OPTS)->extension->KEY= (VAL) static TYPELIB option_types={array_elements(default_options)-1, @@ -2106,15 +2118,22 @@ mysql_read_query_result(MYSQL *mysql) int STDCALL mysql_real_query(MYSQL *mysql, const char *query, size_t length) { + my_bool is_multi= 0; + DBUG_ENTER("mysql_real_query"); DBUG_PRINT("enter",("handle: %lx",mysql)); DBUG_PRINT("query",("Query = \"%.255s\" length=%u",query, length)); + if (OPT_HAS_EXT_VAL(mysql, multi_command)) + is_multi= mysql->options.extension->multi_command; + free_old_query(mysql); if (simple_command(mysql, COM_QUERY,query,length,1,0)) DBUG_RETURN(-1); - DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); + if (!is_multi) + DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); + DBUG_RETURN(0); } /************************************************************************** @@ -2891,6 +2910,15 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1)); else return -1; + case MARIADB_OPT_COM_MULTI: + if (&mysql->net.pvio && + (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_FLAGS)) + { + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, multi_command, *(my_bool *)arg1); + } + else + DBUG_RETURN(-1); + break; default: va_end(ap); DBUG_RETURN(-1); @@ -3304,6 +3332,24 @@ mysql_get_socket(const MYSQL *mysql) return sock; } +int STDCALL mariadb_flush_multi_command(MYSQL *mysql) +{ + int is_multi= 0; + int rc; + + /* turn off multi_command option, so simple_command will + * stop to add commands to the queue and send packet + * to the server */ + mysql_options(mysql, MARIADB_OPT_COM_MULTI, &is_multi); + + rc= simple_command(mysql, COM_MULTI, mysql->net.mbuff, + mysql->net.mbuff_pos - mysql->net.mbuff, + 0, 0); + /* reset multi_buff */ + mysql->net.mbuff_pos= mysql->net.mbuff; + return rc; +} + /* * Default methods for a connection. These methods are * stored in mysql->methods and can be overwritten by diff --git a/libmariadb/net.c b/libmariadb/net.c index 8c657d2a1..df75c8c01 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -107,7 +107,6 @@ extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; ** can't normally do this the client should have a bigger max-buffer. */ -#define TEST_BLOCKING 8 static int net_write_buff(NET *net,const char *packet, size_t len); @@ -117,6 +116,10 @@ int my_net_init(NET *net, MARIADB_PVIO* pvio) { if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL)))) return 1; + + /* We don't allocate memory for multi buffer, since we don't know in advance if the server + * supports COM_MULTI comand. It will be allocated on demand in net_add_multi_command */ + max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet); net->buff_end=net->buff+(net->max_packet=net_buffer_length); net->pvio = pvio; @@ -141,13 +144,15 @@ int my_net_init(NET *net, MARIADB_PVIO* pvio) void net_end(NET *net) { - my_free((gptr) net->buff); + my_free(net->buff); + my_free(net->mbuff); net->buff=0; + net->mbuff= 0; } /* Realloc the packet buffer */ -static my_bool net_realloc(NET *net, size_t length) +static my_bool net_realloc(NET *net, my_bool is_multi, size_t length) { uchar *buff; size_t pkt_length; @@ -166,7 +171,7 @@ static my_bool net_realloc(NET *net, size_t length) pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); /* reallocate buffer: size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */ - if (!(buff=(uchar*) my_realloc((char*) net->buff, + if (!(buff=(uchar*) my_realloc(is_multi ? net->mbuff : net->buff, pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE, MYF(MY_WME)))) { @@ -174,8 +179,16 @@ static my_bool net_realloc(NET *net, size_t length) net->error=1; DBUG_RETURN(1); } - net->buff=net->write_pos=buff; - net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length); + if (!is_multi) + { + net->buff=net->write_pos=buff; + net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length); + } + else + { + net->mbuff=net->mbuff_pos=buff; + net->mbuff_end=buff+(net->max_packet=(unsigned long)pkt_length); + } DBUG_RETURN(0); } @@ -186,11 +199,13 @@ void net_clear(NET *net) DBUG_ENTER("net_clear"); net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; + if (net->mbuff) + net->mbuff_pos= net->mbuff; DBUG_VOID_RETURN; } - /* Flush write_buffer if not empty. */ +/* Flush write_buffer if not empty. */ int net_flush(NET *net) { @@ -327,6 +342,55 @@ net_write_buff(NET *net,const char *packet, size_t len) return 0; } +int net_add_multi_command(NET *net, uchar command, const uchar *packet, + size_t length) +{ + size_t left_length; + size_t required_length, current_length; + required_length= length + 1 + NET_HEADER_SIZE; + + /* We didn't allocate memory in my_net_init since it was to early to + * detect if the server supports COM_MULTI command */ + if (!net->mbuff) + { + size_t alloc_size= (required_length + IO_SIZE - 1) & ~(IO_SIZE - 1); + if (!(net->mbuff= (char *)my_malloc(alloc_size, MYF(MY_WME)))) + { + net->last_errno=ER_OUT_OF_RESOURCES; + net->error=2; + net->reading_or_writing=0; + return(1); + } + net->mbuff_pos= net->mbuff; + net->mbuff_end= net->mbuff + alloc_size; + } + + left_length= net->mbuff_end - net->mbuff_pos; + + /* check if our buffer is large enough */ + if (left_length < required_length) + { + current_length= net->mbuff_pos - net->mbuff; + if (net_realloc(net, 1, current_length + required_length)) + goto error; + } + int3store(net->mbuff_pos, length + 1); + net->mbuff_pos+= 3; + *net->mbuff_pos= command; + net->mbuff_pos++; + memcpy(net->mbuff_pos, packet, length); + net->mbuff_pos+= length; + return 0; + +error: + if (net->mbuff) + { + my_free(net->mbuff); + net->mbuff= net->mbuff_pos= net->mbuff_end= 0; + } + return 1; +} + /* Read and write using timeouts */ int @@ -391,7 +455,6 @@ net_real_write(NET *net,const char *packet,size_t len) DBUG_RETURN(((int) (pos != end))); } - /***************************************************************************** ** Read something from server/clinet *****************************************************************************/ @@ -460,7 +523,7 @@ my_real_read(NET *net, size_t *complen) /* The necessary size of net->buff */ if (helping >= net->max_packet) { - if (net_realloc(net,helping)) + if (net_realloc(net, 0, helping)) { len= packet_error; /* Return error */ goto end; diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index bcb957ccd..039d5eb66 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -172,7 +172,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, /* if server supports extended MariaDB extended protocol, we will unset CLIENT_LONG_PASSWORD and send extended client capabilities in last four of 23 unused bytes */ - if (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_PROTOCOL) + if (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_FLAGS) mysql->client_flag &= ~CLIENT_LONG_PASSWORD; #if defined(HAVE_SSL) && !defined(EMBEDDED_LIBRARY) @@ -218,7 +218,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, int4store(buff+4, net->max_packet_size); buff[8]= (char) mysql->charset->nr; bzero(buff + 9, 32-9); - if (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_PROTOCOL) + if (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_FLAGS) { mysql->client_flag |= MARIADB_CLIENT_SUPPORTED_FLAGS; int4store(buff + 28, mysql->client_flag >> 32); diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 4e8d2125a..d8a8e6954 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -21,7 +21,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/unittest/mytap) ADD_DEFINITIONS(-DLIBMARIADB) -SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" +SET(API_TESTS "features-10_2" "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" "sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol") # Get finger print from server certificate diff --git a/unittest/libmariadb/features-10_2.c b/unittest/libmariadb/features-10_2.c new file mode 100644 index 000000000..758b59fe1 --- /dev/null +++ b/unittest/libmariadb/features-10_2.c @@ -0,0 +1,55 @@ +/* +*/ + +#include "my_test.h" + +static int com_multi_1(MYSQL *mysql) +{ + int rc; + my_bool is_multi= 1; + + if (mysql_options(mysql, MARIADB_OPT_COM_MULTI, &is_multi)) + { + diag("COM_MULT not supported"); + return SKIP; + } + + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @b:=2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "select @a,@b"); + check_mysql_rc(rc, mysql); + + rc= mariadb_flush_multi_command(mysql); + check_mysql_rc(rc, mysql); + + /* question: how will result sets look like ? */ + diag("error: %s", mysql_error(mysql)); + + return OK; +} + +struct my_tests_st my_tests[] = { + {"com_multi_1", com_multi_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + + mysql_library_init(0,0,NULL); + + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + mysql_server_end(); + return(exit_status()); +}