diff --git a/include/my_stmt.h b/include/my_stmt.h index 0dfb1ba1..e58e3524 100644 --- a/include/my_stmt.h +++ b/include/my_stmt.h @@ -253,3 +253,4 @@ my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt); unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt); int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt); my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt); +int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, const char *stmt_str, size_t length); diff --git a/include/mysql.h b/include/mysql.h index c74653d0..78e7911e 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -165,6 +165,12 @@ extern unsigned int mariadb_deinitialize_ssl; MEM_ROOT alloc; } MYSQL_DATA; + enum mariadb_com_multi { + MARIADB_COM_MULTI_END, + MARIADB_COM_MULTI_BEGIN, + MARIADB_COM_MULTI_CANCEL + }; + enum mysql_option { MYSQL_OPT_CONNECT_TIMEOUT, @@ -637,7 +643,7 @@ int STDCALL mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, /* API function calls (used by dynmic plugins) */ struct st_mariadb_api { - my_ulonglong (STDCALL *mysql_num_rows)(MYSQL_RES *res); + my_ulonglong (STDCALL *mysql_num_rows)(MYSQL_RES *res); unsigned int (STDCALL *mysql_num_fields)(MYSQL_RES *res); my_bool (STDCALL *mysql_eof)(MYSQL_RES *res); MYSQL_FIELD *(STDCALL *mysql_fetch_field_direct)(MYSQL_RES *res, unsigned int fieldnr); @@ -752,6 +758,7 @@ struct st_mariadb_api { unsigned int (STDCALL *mysql_stmt_field_count)(MYSQL_STMT *stmt); int (STDCALL *mysql_stmt_next_result)(MYSQL_STMT *stmt); my_bool (STDCALL *mysql_stmt_more_results)(MYSQL_STMT *stmt); + int (STDCALL *mariadb_stmt_execute_direct)(MYSQL_STMT *stmt, const char *stmtstr, size_t length); }; /* these methods can be overwritten by db plugins */ diff --git a/include/mysql_com.h b/include/mysql_com.h index 5e71809f..cfd520d5 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -129,7 +129,7 @@ enum enum_server_command #define REFRESH_READ_LOCK 16384 /* Lock tables for read */ #define REFRESH_FAST 32768 /* Intern flag */ -#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ +#define CLIENT_MYSQL 1 #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ #define CLIENT_LONG_FLAG 4 /* Get all column flags */ #define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */ @@ -157,14 +157,14 @@ 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_FLAGS (1ULL << 63) +#define MARIADB_CLIENT_COM_MULTI (1ULL << 33) +//#define MARIADB_CLIENT_EXTENDED_FLAGS (1ULL << 63) #define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\ - MARIADB_CLIENT_EXTENDED_FLAGS) + MARIADB_CLIENT_COM_MULTI) -#define CLIENT_SUPPORTED_FLAGS (CLIENT_LONG_PASSWORD |\ +#define CLIENT_SUPPORTED_FLAGS (CLIENT_MYSQL |\ CLIENT_FOUND_ROWS |\ CLIENT_LONG_FLAG |\ CLIENT_CONNECT_WITH_DB |\ @@ -188,7 +188,7 @@ enum enum_server_command CLIENT_PLUGIN_AUTH |\ CLIENT_CONNECT_ATTRS) -#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \ +#define CLIENT_CAPABILITIES (CLIENT_MYSQL | \ CLIENT_LONG_FLAG |\ CLIENT_TRANSACTIONS |\ CLIENT_SECURE_CONNECTION |\ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 26767358..c8da08eb 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -35,6 +35,7 @@ SET(EXPORT_SYMBOLS mariadb_dyncol_val_long mariadb_dyncol_val_str mariadb_get_charset_by_name + mariadb_stmt_execute_direct mariadb_get_charset_by_nr mariadb_get_info mariadb_get_infov diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index f2cff1b8..2ace684d 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -361,16 +361,16 @@ 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; + enum mariadb_com_multi multi= MARIADB_COM_MULTI_END; if (OPT_HAS_EXT_VAL(mysql, multi_command)) - is_multi= mysql->options.extension->multi_command; + 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) + if (multi == MARIADB_COM_MULTI_BEGIN) { /* todo: error handling */ DBUG_RETURN(net_add_multi_command(&mysql->net, command, arg, length)); @@ -1546,7 +1546,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, if (strncmp(end, MA_RPL_VERSION_HACK, sizeof(MA_RPL_VERSION_HACK) - 1) == 0) { - if (!(mysql->server_version= my_strdup(end + sizeof(MA_RPL_VERSION_HACK), 0))) + if (!(mysql->server_version= my_strdup(end + sizeof(MA_RPL_VERSION_HACK) - 1, 0))) { SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); goto error; @@ -1586,11 +1586,11 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, { mysql->server_language= uint1korr(end + 2); mysql->server_status= uint2korr(end + 3); - mysql->server_capabilities|= uint2korr(end + 5) << 16; + 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_LONG_PASSWORD)) + if (is_maria && !(mysql->server_capabilities & CLIENT_MYSQL)) { mysql->server_capabilities|= (ulonglong) uint4korr(end + 14) << 32; } @@ -2630,6 +2630,24 @@ void ma_hash_free(void *p) my_free(p); } +int mariadb_flush_multi_command(MYSQL *mysql) +{ + int rc; + size_t length= mysql->net.mbuff_pos - mysql->net.mbuff; + + rc= simple_command(mysql, COM_MULTI, mysql->net.mbuff, + length, 1, 0); + /* reset multi_buff */ + mysql->net.mbuff_pos= mysql->net.mbuff; + + if (!rc) + if (mysql->net.mbuff && length > 3 && + (mysql->net.mbuff[3] == COM_STMT_PREPARE || mysql->net.mbuff[3] == COM_STMT_EXECUTE)) + return rc; + else + return mysql->methods->db_read_query_result(mysql); + return rc; +} int STDCALL mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) @@ -2975,8 +2993,33 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) break; case MARIADB_OPT_COM_MULTI: if (&mysql->net.pvio && - (mysql->server_capabilities & MARIADB_CLIENT_EXTENDED_FLAGS)) + !(mysql->server_capabilities & CLIENT_MYSQL)) { + enum mariadb_com_multi type= *(enum mariadb_com_multi *)arg1; + switch (type) + { + case MARIADB_COM_MULTI_BEGIN: + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, multi_command, type); + break; + case MARIADB_COM_MULTI_CANCEL: + if (!mysql->options.extension || + mysql->options.extension->multi_command != MARIADB_COM_MULTI_BEGIN) + DBUG_RETURN(-1); + /* reset multi_buff */ + mysql->net.mbuff_pos= mysql->net.mbuff; + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, multi_command, MARIADB_COM_MULTI_END); + break; + case MARIADB_COM_MULTI_END: + if (!mysql->options.extension || + mysql->options.extension->multi_command != MARIADB_COM_MULTI_BEGIN) + DBUG_RETURN(-1); + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, multi_command, MARIADB_COM_MULTI_END); + if (mariadb_flush_multi_command(mysql)) + DBUG_RETURN(-1); + break; + default: + DBUG_RETURN(-1); + } OPT_SET_EXTENDED_VALUE_INT(&mysql->options, multi_command, *(my_bool *)arg1); } else @@ -3071,6 +3114,15 @@ mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) case MYSQL_OPT_NONBLOCK: *((my_bool *)arg)= test(mysql->options.extension && mysql->options.extension->async_context); break; + case MARIADB_OPT_COM_MULTI: + if (!(mysql->server_capabilities & CLIENT_MYSQL)) + { + *((enum mariadb_com_multi *)arg)= mysql->options.extension ? + mysql->options.extension->multi_command : 0; + } + else + DBUG_RETURN(-1); + break; case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: *((my_bool *)arg)= test(mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT); break; @@ -3575,28 +3627,6 @@ static my_socket mariadb_get_socket(MYSQL *mysql) return sock; } -int 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, - 1, 0); - /* reset multi_buff */ - mysql->net.mbuff_pos= mysql->net.mbuff; - - if (!rc) - rc= mysql->methods->db_read_query_result(mysql); - - return rc; -} - my_socket STDCALL mysql_get_socket(MYSQL *mysql) { @@ -3951,7 +3981,8 @@ struct st_mariadb_api MARIADB_API= mysql_stmt_insert_id, mysql_stmt_field_count, mysql_stmt_next_result, - mysql_stmt_more_results + mysql_stmt_more_results, + mariadb_stmt_execute_direct }; /* diff --git a/libmariadb/my_stmt.c b/libmariadb/my_stmt.c index 4894ceb2..4b97469d 100644 --- a/libmariadb/my_stmt.c +++ b/libmariadb/my_stmt.c @@ -113,7 +113,7 @@ my_bool mthd_supported_buffer_type(enum enum_field_types type) } static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags); - +static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close); static int stmt_unbuffered_eof(MYSQL_STMT *stmt, uchar **row) { return MYSQL_NO_DATA; @@ -281,7 +281,7 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) int4store(buf, stmt->stmt_id); int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows); - if (simple_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt)) + if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt)) DBUG_RETURN(1); /* free previously allocated buffer */ @@ -774,9 +774,40 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type a my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) { + MYSQL *mysql= stmt->mysql; DBUG_ENTER("mysql_stmt_bind_param"); - if (stmt->state < MYSQL_STMT_PREPARED) { + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* for mariadb_stmt_execute_direct we need to bind parameters in advance: + client has to pass a bind array, where last parameter needs to be set + to buffer type MAX_NO_FIELD_TYPES */ + if (stmt->state < MYSQL_STMT_PREPARED && + !(mysql->server_capabilities & CLIENT_MYSQL)) + { + if (!stmt->params) + { + int param_count; + for(param_count= 0; + bind[param_count].buffer_type != MAX_NO_FIELD_TYPES; + param_count++); + stmt->param_count= param_count; + if (stmt->param_count) + { + if (!(stmt->params= (MYSQL_BIND *)alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + } + memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND)); + } + } + else if (stmt->state < MYSQL_STMT_PREPARED) { SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); DBUG_RETURN(1); } @@ -947,7 +978,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) DBUG_RETURN(0); } -my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) +static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) { char stmt_id[STMT_ID_LENGTH]; MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; @@ -974,7 +1005,8 @@ my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) if (stmt->state > MYSQL_STMT_INITTED) { int4store(stmt_id, stmt->stmt_id); - if (simple_command(stmt->mysql,COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + if (stmt->mysql->methods->db_command(stmt->mysql,COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) { SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error); return 1; @@ -989,7 +1021,8 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) DBUG_ENTER("mysql_stmt_close"); if (stmt && stmt->mysql && stmt->mysql->net.pvio) - mysql_stmt_reset(stmt); + mysql_stmt_internal_reset(stmt, 1); + net_stmt_close(stmt, 1); my_free(stmt->extension); @@ -1142,6 +1175,7 @@ MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql) /* fill mysql's stmt list */ stmt->list.data= stmt; stmt->mysql= mysql; + stmt->stmt_id= -1; mysql->stmts= list_add(mysql->stmts, &stmt->list); @@ -1226,9 +1260,7 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, size_t lengt MYSQL *mysql= stmt->mysql; int rc= 1; DBUG_ENTER("mysql_stmt_prepare"); - - if (length == -1) - length= strlen(query); + enum mariadb_com_multi multi= MARIADB_COM_MULTI_END; if (!stmt->mysql) { @@ -1236,6 +1268,11 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, size_t lengt DBUG_RETURN(1); } + if (length == -1) + length= strlen(query); + + mysql_get_optionv(mysql, MARIADB_OPT_COM_MULTI, &multi); + /* clear flags */ CLEAR_CLIENT_STMT_ERROR(stmt); CLEAR_CLIENT_ERROR(stmt->mysql); @@ -1259,14 +1296,18 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, size_t lengt stmt->field_count= 0; int4store(stmt_id, stmt->stmt_id); - if (simple_command(mysql, COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) goto fail; } - if (simple_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt)) + if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt)) goto fail; - if (stmt->mysql->methods->db_read_prepare_response && - stmt->mysql->methods->db_read_prepare_response(stmt)) + if (multi == MARIADB_COM_MULTI_BEGIN) + return 0; + + if (mysql->methods->db_read_prepare_response && + mysql->methods->db_read_prepare_response(stmt)) goto fail; /* metadata not supported yet */ @@ -1346,7 +1387,8 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) int4store(buff, stmt->stmt_id); int4store(buff + STMT_ID_LENGTH, (int)~0); - if (simple_command(stmt->mysql, COM_STMT_FETCH, buff, sizeof(buff), 1, stmt)) + if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, + buff, sizeof(buff), 1, stmt)) DBUG_RETURN(1); /* todo: cursor */ } @@ -1442,63 +1484,17 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) DBUG_RETURN(0); } - -int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +int stmt_read_execute_response(MYSQL_STMT *stmt) { MYSQL *mysql= stmt->mysql; - char *request; - int ret; - size_t request_len= 0; + DBUG_ENTER("stmt_read_execute_response"); - DBUG_ENTER("mysql_stmt_execute"); - - if (!stmt->mysql) - { - SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + if (!mysql) DBUG_RETURN(1); - } - - if (stmt->state < MYSQL_STMT_PREPARED) - { - SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); - SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); - DBUG_RETURN(1); - } - - if (stmt->param_count && !stmt->bind_param_done) - { - SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); - DBUG_RETURN(1); - } - - if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) - { - stmt->default_rset_handler = _mysql_stmt_use_result; - stmt->default_rset_handler(stmt); - } - if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data) - { - mysql->methods->db_stmt_flush_unbuffered(stmt); - stmt->state= MYSQL_STMT_PREPARED; - stmt->mysql->status= MYSQL_STATUS_READY; - } - - /* clear data, in case mysql_stmt_store_result was called */ - if (stmt->result.data) - { - free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); - stmt->result_cursor= stmt->result.data= 0; - stmt->result.rows= 0; - } - request= (char *)mysql_stmt_execute_generate_request(stmt, &request_len); - DBUG_PRINT("info",("request_len=%ld", request_len)); - - ret= test(simple_command(mysql, COM_STMT_EXECUTE, request, request_len, 1, stmt) || - (mysql && mysql->methods->db_read_stmt_result && mysql->methods->db_read_stmt_result(mysql))); - if (request) - my_free(request); + int ret= test((mysql->methods->db_read_stmt_result && + mysql->methods->db_read_stmt_result(mysql))); /* if a reconnect occured, our connection handle is invalid */ if (!stmt->mysql) DBUG_RETURN(1); @@ -1615,6 +1611,78 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) DBUG_RETURN(0); } +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + char *request; + int ret; + size_t request_len= 0; + enum mariadb_com_multi multi= MARIADB_COM_MULTI_END; + + DBUG_ENTER("mysql_stmt_execute"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + mysql_get_optionv(mysql, MARIADB_OPT_COM_MULTI, &multi); + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->param_count && !stmt->bind_param_done) + { + SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler = _mysql_stmt_use_result; + stmt->default_rset_handler(stmt); + } + if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data) + { + mysql->methods->db_stmt_flush_unbuffered(stmt); + stmt->state= MYSQL_STMT_PREPARED; + stmt->mysql->status= MYSQL_STATUS_READY; + } + + /* clear data, in case mysql_stmt_store_result was called */ + if (stmt->result.data) + { + free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result_cursor= stmt->result.data= 0; + stmt->result.rows= 0; + } + request= (char *)mysql_stmt_execute_generate_request(stmt, &request_len); + DBUG_PRINT("info",("request_len=%ld", request_len)); + + ret= stmt->mysql->methods->db_command(mysql, COM_STMT_EXECUTE, request, + request_len, 1, stmt); + + if (request) + my_free(request); + + if (ret) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + DBUG_RETURN(1); + } + + if (multi == MARIADB_COM_MULTI_BEGIN) + DBUG_RETURN(0); + + DBUG_RETURN(stmt_read_execute_response(stmt)); +} + static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) { MYSQL *mysql= stmt->mysql; @@ -1670,11 +1738,13 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) if (flags & MADB_RESET_SERVER) { /* reset statement on server side */ - if (stmt->mysql && stmt->mysql->status == MYSQL_STATUS_READY) + if (stmt->mysql && stmt->mysql->status == MYSQL_STATUS_READY && + stmt->mysql->net.pvio) { unsigned char cmd_buf[STMT_ID_LENGTH]; int4store(cmd_buf, stmt->stmt_id); - if ((ret= simple_command(mysql,COM_STMT_RESET, (char *)cmd_buf, sizeof(cmd_buf), 0, stmt))) + if ((ret= stmt->mysql->methods->db_command(mysql,COM_STMT_RESET, (char *)cmd_buf, + sizeof(cmd_buf), 0, stmt))) { SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, mysql->net.last_error); @@ -1698,13 +1768,13 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) DBUG_RETURN(ret); } -my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close) { MYSQL *mysql= stmt->mysql; my_bool ret= 1; unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR; - DBUG_ENTER("mysql_stmt_reset"); + DBUG_ENTER("mysql_stmt_internal_reset"); if (!mysql) { @@ -1739,7 +1809,9 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) stmt->mysql->status= MYSQL_STATUS_READY; } } - ret= madb_reset_stmt(stmt, MADB_RESET_SERVER); + if (!stmt->execute_count) + if (!is_close) + ret= madb_reset_stmt(stmt, MADB_RESET_SERVER); } stmt->state= MYSQL_STMT_PREPARED; stmt->upsert_status.affected_rows= mysql->affected_rows; @@ -1770,7 +1842,13 @@ MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt) res->eof= 1; res->fields= stmt->fields; res->field_count= stmt->field_count; - DBUG_RETURN(res);} + DBUG_RETURN(res); +} + +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +{ + return mysql_stmt_internal_reset(stmt, 0); +} const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt) { @@ -1828,7 +1906,8 @@ my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, int2store(cmd_buff + STMT_ID_LENGTH, param_number); memcpy(cmd_buff + STMT_ID_LENGTH + 2, data, length); stmt->params[param_number].long_data_used= 1; - ret= simple_command(stmt->mysql, COM_STMT_SEND_LONG_DATA, (char *)cmd_buff, packet_len, 1, stmt); + ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA, + (char *)cmd_buff, packet_len, 1, stmt); my_free(cmd_buff); DBUG_RETURN(ret); } @@ -1915,3 +1994,75 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) DBUG_RETURN(rc); } + +int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, + const char *stmt_str, + size_t length) +{ + enum mariadb_com_multi multi= MARIADB_COM_MULTI_BEGIN; + MYSQL *mysql= stmt->mysql; + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + goto fail; + } + + if (mysql_optionsv(mysql, MARIADB_OPT_COM_MULTI, &multi)) + goto fail; + + if (length == -1) + length= strlen(stmt_str); + + if (mysql_stmt_prepare(stmt, stmt_str, length)) + goto fail; + + stmt->state= MYSQL_STMT_PREPARED; + + if (mysql_stmt_execute(stmt)) + goto fail; + + multi= MARIADB_COM_MULTI_END; + if (mysql_optionsv(mysql, MARIADB_OPT_COM_MULTI, &multi)) + goto fail; + + /* read prepare response */ + if (mysql->methods->db_read_prepare_response && + mysql->methods->db_read_prepare_response(stmt)) + goto fail; + + /* metadata not supported yet */ + + if (stmt->param_count && + stmt->mysql->methods->db_stmt_get_param_metadata(stmt)) + { + goto fail; + } + + /* allocated bind buffer for parameters */ + if (stmt->field_count && + stmt->mysql->methods->db_stmt_get_result_metadata(stmt)) + { + goto fail; + } + + /* allocated bind buffer for result */ + if (stmt->field_count) + { + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + } + stmt->state = MYSQL_STMT_PREPARED; + + /* read execute response packet */ + return stmt_read_execute_response(stmt); +fail: + stmt->state= MYSQL_STMT_INITTED; + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + return 1; +} diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index 039d5eb6..ab797f85 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -169,12 +169,6 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mysql->client_flag & CLIENT_MULTI_STATEMENTS) mysql->client_flag|= CLIENT_MULTI_RESULTS; - /* 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_FLAGS) - mysql->client_flag &= ~CLIENT_LONG_PASSWORD; - #if defined(HAVE_SSL) && !defined(EMBEDDED_LIBRARY) if (mysql->options.ssl_key || mysql->options.ssl_cert || mysql->options.ssl_ca || mysql->options.ssl_capath || @@ -218,7 +212,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_FLAGS) + if (!(mysql->server_capabilities & CLIENT_MYSQL)) { 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 90e5a1b7..0d3703b1 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -22,7 +22,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ADD_DEFINITIONS(-DLIBMARIADB) SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" - "sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol" "features-10_2") + "sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol" "features-10_2" ) #exclude following tests from ctests, since we need to run them maually with different credentials SET(MANUAL_TESTS "t_aurora") diff --git a/unittest/libmariadb/features-10_2.c b/unittest/libmariadb/features-10_2.c index f0708686..39007346 100644 --- a/unittest/libmariadb/features-10_2.c +++ b/unittest/libmariadb/features-10_2.c @@ -3,26 +3,30 @@ #include "my_test.h" +my_bool have_com_multi= 1; + static int com_multi_1(MYSQL *mysql) { int rc; MYSQL_RES *res; - my_bool is_multi= 1; + enum mariadb_com_multi status; /* TEST a simple query before COM_MULTI */ rc= mysql_query(mysql, "select 1"); check_mysql_rc(rc, mysql); - FAIL_UNLESS(res, "1 simple query no result"); res= mysql_store_result(mysql); + FAIL_UNLESS(res, "1 simple query no result"); mysql_free_result(res); /* TEST COM_MULTI */ - if (mysql_options(mysql, MARIADB_OPT_COM_MULTI, &is_multi)) + status= MARIADB_COM_MULTI_BEGIN; + if (mysql_options(mysql, MARIADB_OPT_COM_MULTI, &status)) { diag("COM_MULT not supported"); + have_com_multi= 0; return SKIP; } @@ -30,7 +34,8 @@ static int com_multi_1(MYSQL *mysql) rc= mysql_query(mysql, "select 2"); - rc= mariadb_flush_multi_command(mysql); + status= MARIADB_COM_MULTI_END; + rc= mysql_options(mysql, MARIADB_OPT_COM_MULTI, &status); check_mysql_rc(rc, mysql); /* 1 SELECT result */ res= mysql_store_result(mysql); @@ -48,10 +53,6 @@ static int com_multi_1(MYSQL *mysql) strcmp(res->fields[0].name, "2") == 0, "1 of 2 simple query in batch wrong result"); mysql_free_result(res); - /* WHOLE batch result (OK) */ - rc= mysql_next_result(mysql); - res= mysql_store_result(mysql); - FAIL_UNLESS(res == NULL, "rows instead of batch OK"); rc= mysql_next_result(mysql); FAIL_UNLESS(rc == -1, "more then 2 results"); @@ -69,8 +70,86 @@ static int com_multi_1(MYSQL *mysql) return OK; } +static int com_multi_ps1(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + if (!have_com_multi) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int, b varchar(20))"); + + rc= mysql_stmt_prepare(stmt, "INSERT INTO t1 values (2, 'execute_direct')", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + diag("affected_rows: %d", mysql_stmt_affected_rows(stmt)); + diag("stmt_id: %d", stmt->stmt_id); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + rc= mariadb_stmt_execute_direct(stmt, "INSERT INTO t1 values (2, 'execute_direct')", -1); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "expected affected_rows= 1"); + FAIL_IF(stmt->stmt_id < 1, "expected statement id > 0"); + + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int com_multi_ps2(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[3]; + int intval= 3, rc; + int i; + char *varval= "com_multi_ps2"; + + + if (!have_com_multi) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int, b varchar(20))"); + + memset(&bind, 0, sizeof(MYSQL_BIND) * 3); + bind[0].buffer_type= MYSQL_TYPE_SHORT; + bind[0].buffer= &intval; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= varval; + bind[1].buffer_length= strlen(varval); + bind[2].buffer_type= MAX_NO_FIELD_TYPES; + + for (i=0; i < 2; i++) + { + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "INSERT INTO t1 VALUES (1,'foo')", -1); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "expected affected_rows= 1"); + FAIL_IF(stmt->stmt_id < 1, "expected statement id > 0"); + + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + } + + return OK; +} + struct my_tests_st my_tests[] = { {"com_multi_1", com_multi_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"com_multi_ps1", com_multi_ps1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"com_multi_ps2", com_multi_ps2, TEST_CONNECTION_NEW, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} }; diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 2897b3a7..2c7d744b 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -189,6 +189,8 @@ int do_verify_prepare_field(MYSQL_RES *result, FAIL_UNLESS(strcmp(field->table, table) == 0, "field->table differs"); if (org_table) FAIL_UNLESS(strcmp(field->org_table, org_table) == 0, "field->org_table differs"); + if (strcmp(field->db,db)) + diag("%s / %s", field->db, db); FAIL_UNLESS(strcmp(field->db, db) == 0, "field->db differs"); /* Character set should be taken into account for multibyte encodings, such diff --git a/unittest/libmariadb/ps.c b/unittest/libmariadb/ps.c index 3eb74bb9..6385fe35 100644 --- a/unittest/libmariadb/ps.c +++ b/unittest/libmariadb/ps.c @@ -830,6 +830,7 @@ static int test_prepare_alter(MYSQL *mysql) FAIL_IF(!(mysql_real_connect(mysql_new, hostname, username, password, schema, port, socketname, 0)), "mysql_real_connect failed"); rc= mysql_query(mysql_new, "ALTER TABLE test_prep_alter change id id_new varchar(20)"); + diag("Error: %d %s", mysql_errno(mysql_new), mysql_error(mysql_new)); check_mysql_rc(rc, mysql_new); mysql_close(mysql_new);