1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +03:00

CONC-533: Support for asynchronous statements (binary protocol)

Added a new option MARIADB_OPT_SKIP_READ_RESPONSE which skips automatic
reading of server response after sending a command to the server.

Server packets have to be retrieved by calling the corresponding methods,
e.g:

Send command                         Read method
mysql_real_query/mysql_send_query    db_read_query_result
mysql_stmt_prepare                   db_read_prepare_response
mysql_stmt_execute,
mariadb_stmt_execute_direct          db_read_execute_response
This commit is contained in:
Georg Richter
2021-05-13 21:21:43 +02:00
parent 304353c3a4
commit e4e5b28452
7 changed files with 141 additions and 92 deletions

View File

@@ -79,6 +79,7 @@ struct st_mysql_options_extension {
char *proxy_header;
size_t proxy_header_len;
int (*io_wait)(my_socket handle, my_bool is_read, int timeout);
my_bool skip_read_response;
};
typedef struct st_connection_handler

View File

@@ -246,7 +246,8 @@ extern const char *SQLSTATE_UNKNOWN;
MARIADB_OPT_MULTI_STATEMENTS,
MARIADB_OPT_INTERACTIVE,
MARIADB_OPT_PROXY_HEADER,
MARIADB_OPT_IO_WAIT
MARIADB_OPT_IO_WAIT,
MARIADB_OPT_SKIP_READ_RESPONSE
};
enum mariadb_value {
@@ -872,6 +873,7 @@ struct st_mariadb_methods {
void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...);
void (*invalidate_stmts)(MYSQL *mysql, const char *function_name);
struct st_mariadb_api *api;
int (*db_read_execute_response)(MYSQL_STMT *stmt);
};
/* synonyms/aliases functions */

View File

@@ -112,6 +112,7 @@ 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_read_execute_response(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);
@@ -2489,7 +2490,7 @@ mysql_real_query(MYSQL *mysql, const char *query, unsigned long length)
if (ma_simple_command(mysql, COM_QUERY,query,length,1,0))
return(-1);
if (!skip_result)
if (!skip_result && !mysql->options.extension->skip_read_response)
return(mysql->methods->db_read_query_result(mysql));
return(0);
}
@@ -3362,6 +3363,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
CHECK_OPT_EXTENSION_SET(&mysql->options);
mysql->options.extension->io_wait = (int(*)(my_socket, my_bool, int))arg1;
break;
case MARIADB_OPT_SKIP_READ_RESPONSE:
OPT_SET_EXTENDED_VALUE_INT(&mysql->options, skip_read_response, *(my_bool *)arg1);
break;
default:
va_end(ap);
SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
@@ -3577,6 +3581,9 @@ mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...)
case MARIADB_OPT_IO_WAIT:
*((int(**)(my_socket, my_bool, int))arg) = mysql->options.extension ? mysql->options.extension->io_wait : NULL;
break;
case MARIADB_OPT_SKIP_READ_RESPONSE:
*((my_bool*)arg)= mysql->options.extension ? mysql->options.extension->skip_read_response : 0;
break;
default:
va_end(ap);
SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
@@ -4509,5 +4516,7 @@ struct st_mariadb_methods MARIADB_DEFAULT_METHODS = {
/* invalidate statements */
ma_invalidate_stmts,
/* API functions */
&MARIADB_API
&MARIADB_API,
/* read execute response */
mthd_stmt_read_execute_response
};

View File

@@ -322,11 +322,14 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row)
result->data= 0;
result->rows= 0;
if (!stmt->mysql->options.extension->skip_read_response)
{
if (stmt->mysql->methods->db_stmt_read_all_rows(stmt))
return(1);
return(stmt_buffered_fetch(stmt, row));
}
}
/* no more cursor data available */
*row= NULL;
return(MYSQL_NO_DATA);
@@ -1575,6 +1578,52 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt)
p++;
/* for backward compatibility we also update mysql->warning_count */
stmt->mysql->warning_count= stmt->upsert_status.warning_count= uint2korr(p);
/* metadata not supported yet */
if (stmt->param_count &&
stmt->mysql->methods->db_stmt_get_param_metadata(stmt))
{
return 1;
}
/* allocated bind buffer for parameters */
if (stmt->field_count &&
stmt->mysql->methods->db_stmt_get_result_metadata(stmt))
{
return 1;
}
if (stmt->param_count)
{
if (stmt->prebind_params)
{
if (stmt->prebind_params != stmt->param_count)
{
SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
return 1;
}
} else {
if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND))))
{
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return 1;
}
memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND));
}
}
/* allocated bind buffer for result */
if (stmt->field_count)
{
MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
{
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return 1;
}
memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
}
stmt->state = MYSQL_STMT_PREPARED;
return(0);
}
@@ -1663,57 +1712,14 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon
if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED)
ma_multi_command(mysql, COM_MULTI_END);
if (mysql->net.extension->multi_status > COM_MULTI_OFF)
if (mysql->net.extension->multi_status > COM_MULTI_OFF ||
mysql->options.extension->skip_read_response)
return 0;
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;
}
if (stmt->param_count)
{
if (stmt->prebind_params)
{
if (stmt->prebind_params != stmt->param_count)
{
SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
goto fail;
}
} else {
if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND))))
{
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
goto fail;
}
memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND));
}
}
/* allocated bind buffer for result */
if (stmt->field_count)
{
MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
{
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
goto fail;
}
memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
}
stmt->state = MYSQL_STMT_PREPARED;
return(0);
fail:
@@ -1834,7 +1840,7 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt)
return(0);
}
int stmt_read_execute_response(MYSQL_STMT *stmt)
int mthd_stmt_read_execute_response(MYSQL_STMT *stmt)
{
MYSQL *mysql= stmt->mysql;
int ret;
@@ -2070,10 +2076,11 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
return(1);
}
if (mysql->net.extension->multi_status > COM_MULTI_OFF)
if (mysql->net.extension->multi_status > COM_MULTI_OFF ||
mysql->options.extension->skip_read_response)
return(0);
return(stmt_read_execute_response(stmt));
return(mthd_stmt_read_execute_response(stmt));
}
static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags)
@@ -2475,6 +2482,8 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt,
if (ma_multi_command(mysql, COM_MULTI_END))
goto fail;
if (!mysql->options.extension->skip_read_response)
{
/* read prepare response */
if (mysql->methods->db_read_prepare_response &&
mysql->methods->db_read_prepare_response(stmt))
@@ -2482,36 +2491,9 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt,
clear_result= 0;
/* 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)
{
MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
{
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
goto fail;
}
memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
}
stmt->state = MYSQL_STMT_PREPARED;
/* read execute response packet */
return stmt_read_execute_response(stmt);
return mthd_stmt_read_execute_response(stmt);
}
fail:
/* check if we need to set error message */
if (!mysql_stmt_errno(stmt))

View File

@@ -1019,8 +1019,6 @@ static size_t ma_gnutls_get_protocol_version(const char *tls_version_option,
goto end;
if (strstr(tls_version_option, "TLSv1.0"))
strcat(tls_versions, ":+VERS-TLS1.0");
if (strstr(tls_version_option, "TLSv1.1"))
strcat(tls_versions, ":+VERS-TLS1.1");
if (strstr(tls_version_option, "TLSv1.2"))
@@ -1033,7 +1031,7 @@ end:
if (tls_versions[0])
snprintf(priority_string, prio_len - 1, "-VERS-TLS-ALL%s:NORMAL", tls_versions);
else
strncpy(priority_string, "NORMAL:+VERS-ALL", prio_len - 1);
strncpy(priority_string, "NORMAL:+VERS-ALL:-VERS-TLSv1.0", prio_len - 1);
return strlen(priority_string);
}

View File

@@ -103,8 +103,6 @@ static long ma_tls_version_options(const char *version)
if (!version)
return 0;
if (strstr(version, "TLSv1.0"))
protocol_options&= ~SSL_OP_NO_TLSv1;
if (strstr(version, "TLSv1.1"))
protocol_options&= ~SSL_OP_NO_TLSv1_1;
if (strstr(version, "TLSv1.2"))
@@ -445,7 +443,8 @@ void *ma_tls_init(MYSQL *mysql)
SSL_CTX *ctx= NULL;
long options= SSL_OP_ALL |
SSL_OP_NO_SSLv2 |
SSL_OP_NO_SSLv3;
SSL_OP_NO_SSLv3 |
SSL_OP_NO_TLSv1;
pthread_mutex_lock(&LOCK_openssl_config);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L

View File

@@ -1463,7 +1463,65 @@ static int test_conc458(MYSQL *my __attribute__((unused)))
}
static int test_conc533(MYSQL *mysql)
{
my_bool skip= 1;
int rc;
MYSQL_RES *result;
MYSQL_ROW row;
MYSQL_STMT *stmt;
MYSQL_BIND bind[1];
char buffer[10];
rc= mysql_options(mysql, MARIADB_OPT_SKIP_READ_RESPONSE, &skip);
rc= mysql_real_query(mysql, SL("SELECT 1"));
check_mysql_rc(rc, mysql);
rc= mysql->methods->db_read_query_result(mysql);
check_mysql_rc(rc, mysql);
result= mysql_store_result(mysql);
row= mysql_fetch_row(result);
FAIL_IF(strcmp(row[0], "1"), "Expected value \"1\"");
mysql_free_result(result);
stmt= mysql_stmt_init(mysql);
rc= mysql_stmt_prepare(stmt, SL("SELECT 1"));
check_stmt_rc(rc, stmt);
rc= mysql->methods->db_read_prepare_response(stmt);
check_stmt_rc(rc, stmt);
FAIL_IF(mysql_stmt_field_count(stmt) != 1, "Expected field_count= 1");
rc= mysql_stmt_execute(stmt);
check_stmt_rc(rc, stmt);
rc= mysql->methods->db_read_execute_response(stmt);
check_stmt_rc(rc, stmt);
memset(bind, 0, sizeof(MYSQL_BIND));
bind[0].buffer= buffer;
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer_length= 10;
rc= mysql_stmt_bind_result(stmt, bind);
check_stmt_rc(rc, stmt);
rc= mysql_stmt_fetch(stmt);
check_stmt_rc(rc, stmt);
FAIL_IF(strcmp(buffer, "1"), "Expected value \"1\"");
mysql_stmt_close(stmt);
return OK;
}
struct my_tests_st my_tests[] = {
{"test_conc533", test_conc533, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc458", test_conc458, TEST_CONNECTION_NONE, 0, NULL, NULL},
{"test_conc457", test_conc457, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_conc384", test_conc384, TEST_CONNECTION_NONE, 0, NULL, NULL},