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

initial implementation for MDEV-12471

This commit is contained in:
Georg Richter
2017-04-24 15:45:58 +02:00
parent 44a740c348
commit 08450df078
7 changed files with 302 additions and 97 deletions

View File

@@ -93,6 +93,7 @@ enum enum_server_command
COM_DAEMON= 29, COM_DAEMON= 29,
COM_UNSUPPORTED= 30, COM_UNSUPPORTED= 30,
COM_RESET_CONNECTION = 31, COM_RESET_CONNECTION = 31,
COM_STMT_BULK_EXECUTE = 250,
COM_MULTI = 254, COM_MULTI = 254,
COM_END COM_END
}; };

View File

@@ -29,8 +29,7 @@
#define MADB_BIND_DUMMY 1 #define MADB_BIND_DUMMY 1
#define MARIADB_STMT_BULK_SUPPORTED(stmt)\ #define MARIADB_STMT_BULK_SUPPORTED(stmt)\
((stmt)->array_size > 0 && \ ((stmt)->mysql && \
(stmt)->mysql && \
(!((stmt)->mysql->server_capabilities & CLIENT_MYSQL) &&\ (!((stmt)->mysql->server_capabilities & CLIENT_MYSQL) &&\
((stmt)->mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))) ((stmt)->mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32)))

View File

@@ -207,7 +207,7 @@ restart:
if (last_errno== 65535 && if (last_errno== 65535 &&
((mariadb_connection(mysql) && (mysql->server_capabilities & CLIENT_PROGRESS)) || ((mariadb_connection(mysql) && (mysql->server_capabilities & CLIENT_PROGRESS)) ||
(!(mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_PROGRESS >> 32)))) (!(mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_PROGRESS << 32))))
{ {
if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1))) if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1)))
{ {
@@ -1483,7 +1483,8 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
net->compress= 1; net->compress= 1;
/* last part: select default db */ /* last part: select default db */
if (db && !mysql->db) if (!(mysql->server_capabilities & CLIENT_CONNECT_WITH_DB) &&
(db && !mysql->db))
{ {
if (mysql_select_db(mysql, db)) if (mysql_select_db(mysql, db))
{ {

View File

@@ -628,8 +628,8 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long r
return 0; return 0;
} }
/* {{{ mysqlnd_stmt_execute_generate_request */ /* {{{ mysqlnd_stmt_execute_generate_simple_request */
unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *request_len) unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size_t *request_len)
{ {
/* execute packet has the following format: /* execute packet has the following format:
Offset Length Description Offset Length Description
@@ -648,30 +648,17 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req
unsigned flag (32768) unsigned flag (32768)
indicator variable exists (16384) indicator variable exists (16384)
------------------------------------------ ------------------------------------------
Pre 10.2 protocol
n data from bind_buffer n data from bind_buffer
10.2 protocol
if indicator variable exists
1st byte: indicator variable
2nd-n: data
*/ */
size_t length= 1024; size_t length= 1024;
size_t free_bytes= 0; size_t free_bytes= 0;
size_t null_byte_offset= 0; size_t null_byte_offset= 0;
uint i, j, num_rows= 1; uint i;
uchar *start= NULL, *p; uchar *start= NULL, *p;
if (!MARIADB_STMT_BULK_SUPPORTED(stmt) && stmt->array_size > 0)
{
stmt_set_error(stmt, CR_FUNCTION_NOT_SUPPORTED, SQLSTATE_UNKNOWN,
CER(CR_FUNCTION_NOT_SUPPORTED), "Bulk operation");
return NULL;
}
/* preallocate length bytes */ /* preallocate length bytes */
/* check: gr */ /* check: gr */
if (!(start= p= (uchar *)malloc(length))) if (!(start= p= (uchar *)malloc(length)))
@@ -683,17 +670,11 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req
/* flags is 4 bytes, we store just 1 */ /* flags is 4 bytes, we store just 1 */
int1store(p, (unsigned char) stmt->flags); int1store(p, (unsigned char) stmt->flags);
p++; p++;
if (MARIADB_STMT_BULK_SUPPORTED(stmt) && stmt->array_size)
num_rows= stmt->array_size; int4store(p, 1);
int4store(p, num_rows);
p+= 4; p+= 4;
if (!stmt->param_count && stmt->prebind_params)
stmt->param_count= stmt->prebind_params;
if (stmt->param_count) if (stmt->param_count)
{
if (!stmt->array_size)
{ {
size_t null_count= (stmt->param_count + 7) / 8; size_t null_count= (stmt->param_count + 7) / 8;
@@ -710,7 +691,7 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req
null_byte_offset= p - start; null_byte_offset= p - start;
memset(p, 0, null_count); memset(p, 0, null_count);
p += null_count; p += null_count;
}
int1store(p, stmt->send_types_to_server); int1store(p, stmt->send_types_to_server);
p++; p++;
@@ -734,49 +715,200 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req
/* this differs from mysqlnd, c api supports unsinged !! */ /* this differs from mysqlnd, c api supports unsinged !! */
uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0); uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
/* check if parameter requires indicator variable */ /* check if parameter requires indicator variable */
if (MARIADB_STMT_BULK_SUPPORTED(stmt) &&
(stmt->params[i].u.indicator || stmt->params[i].buffer_type == MYSQL_TYPE_NULL))
buffer_type|= 16384;
int2store(p, buffer_type); int2store(p, buffer_type);
p+= 2; p+= 2;
} }
} }
/* calculate data size */ /* calculate data size */
for (j=0; j < num_rows; j++)
{
for (i=0; i < stmt->param_count; i++) for (i=0; i < stmt->param_count; i++)
{ {
size_t size= 0; size_t size= 0;
my_bool has_data= TRUE; my_bool has_data= TRUE;
char indicator= 0;
if (MARIADB_STMT_BULK_SUPPORTED(stmt) &&
(stmt->params[i].u.indicator || stmt->params[i].buffer_type == MYSQL_TYPE_NULL))
{
if (stmt->params[i].buffer_type == MYSQL_TYPE_NULL)
{
indicator= STMT_INDICATOR_NULL;
}
else
indicator= ma_get_indicator(stmt, i, j);
/* check if we need to send data */
if (indicator > 0)
has_data= FALSE;
size= 1;
}
if (stmt->params[i].long_data_used) if (stmt->params[i].long_data_used)
{ {
has_data= FALSE; has_data= FALSE;
stmt->params[i].long_data_used= 0; stmt->params[i].long_data_used= 0;
} }
if (has_data)
{
switch (stmt->params[i].buffer_type) {
case MYSQL_TYPE_NULL:
has_data= FALSE;
break;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_JSON:
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_GEOMETRY:
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_ENUM:
case MYSQL_TYPE_BIT:
case MYSQL_TYPE_SET:
size+= 5; /* max 8 bytes for size */
size+= (size_t)ma_get_length(stmt, i, 0);
break;
default:
size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len;
break;
}
}
free_bytes= length - (p - start);
if (free_bytes < size + 20)
{
size_t offset= p - start;
length= MAX(2 * length, offset + size + 20);
if (!(start= (uchar *)realloc(start, length)))
goto mem_error;
p= start + offset;
}
if (((stmt->params[i].is_null && *stmt->params[i].is_null) ||
stmt->params[i].buffer_type == MYSQL_TYPE_NULL ||
!stmt->params[i].buffer))
{
has_data= FALSE;
(start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7));
}
if (has_data)
{
store_param(stmt, i, &p, 0);
}
}
}
stmt->send_types_to_server= 0;
*request_len = (size_t)(p - start);
return start;
mem_error:
SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
free(start);
*request_len= 0;
return NULL;
}
/* }}} */
/* {{{ mysqlnd_stmt_execute_generate_bulk_request */
unsigned char* mysql_stmt_execute_generate_bulk_request(MYSQL_STMT *stmt, size_t *request_len)
{
/* execute packet has the following format:
Offset Length Description
-----------------------------------------
0 4 Statement id
4 2 Flags (cursor type):
STMT_BULK_SEND_TYPES = 64
STMT_BULK_RETURN_AUTO_ID = 128
6 4 array size
-----------------------------------------
if (stmt->send_types_to_server):
for (i=0; i < param_count; i++)
1st byte: parameter type
2nd byte flag:
unsigned flag (32768)
------------------------------------------
for (i=0; i < param_count; i++)
1 indicator variable
STMT_INDICATOR_NONE 0
STMT_INDICATOR_NULL 1
STMT_INDICATOR_DEFAULT 2
STMT_INDICATOR_IGNORE 3
n data from bind buffer
*/
size_t length= 1024;
size_t free_bytes= 0;
ushort flags= 0;
uint i, j;
uchar *start= NULL, *p;
if (!MARIADB_STMT_BULK_SUPPORTED(stmt))
{
stmt_set_error(stmt, CR_FUNCTION_NOT_SUPPORTED, SQLSTATE_UNKNOWN,
CER(CR_FUNCTION_NOT_SUPPORTED), "Bulk operation");
return NULL;
}
/* preallocate length bytes */
if (!(start= p= (uchar *)malloc(length)))
goto mem_error;
int4store(p, stmt->stmt_id);
p += STMT_ID_LENGTH;
/* todo: request to return auto generated ids */
if (stmt->send_types_to_server)
flags|= 0x40;
int2store(p, flags);
p++;
int4store(p, stmt->array_size);
p+= 4;
/* When using mariadb_stmt_execute_direct stmt->paran_count is
not knowm, so we need to assign prebind_params, which was previously
set by mysql_stmt_attr_set
*/
if (!stmt->param_count && stmt->prebind_params)
stmt->param_count= stmt->prebind_params;
if (stmt->param_count)
{
int1store(p, stmt->send_types_to_server);
p++;
free_bytes= length - (p - start);
/* Store type information:
2 bytes per type
*/
if (stmt->send_types_to_server)
{
if (free_bytes < stmt->param_count * 2 + 20)
{
size_t offset= p - start;
length= offset + stmt->param_count * 2 + 20;
if (!(start= (uchar *)realloc(start, length)))
goto mem_error;
p= start + offset;
}
for (i = 0; i < stmt->param_count; i++)
{
/* this differs from mysqlnd, c api supports unsinged !! */
uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
int2store(p, buffer_type);
p+= 2;
}
}
/* calculate data size */
for (j=0; j < stmt->array_size; j++)
{
for (i=0; i < stmt->param_count; i++)
{
size_t size= 0;
my_bool has_data= TRUE;
char indicator= ma_get_indicator(stmt, i, j);
/* check if we need to send data */
if (indicator > 0)
has_data= FALSE;
size= 1;
/* Please note that mysql_stmt_send_long_data is not supported
current when performing bulk execute */
if (has_data) if (has_data)
{ {
switch (stmt->params[i].buffer_type) { switch (stmt->params[i].buffer_type) {
case MYSQL_TYPE_NULL: case MYSQL_TYPE_NULL:
if (MARIADB_STMT_BULK_SUPPORTED(stmt))
indicator= STMT_INDICATOR_NULL;
has_data= FALSE; has_data= FALSE;
break; break;
case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_TINY_BLOB:
@@ -818,29 +950,12 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req
p= start + offset; p= start + offset;
} }
if ((indicator != STMT_INDICATOR_DEFAULT && indicator != STMT_INDICATOR_IGNORE) &&
((stmt->params[i].is_null && *stmt->params[i].is_null) ||
stmt->params[i].buffer_type == MYSQL_TYPE_NULL ||
!stmt->params[i].buffer))
{
has_data= FALSE;
if (!stmt->array_size)
(start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7));
else
indicator= STMT_INDICATOR_NULL;
}
if (MARIADB_STMT_BULK_SUPPORTED(stmt) &&
(indicator || stmt->params[i].u.indicator))
{
int1store(p, indicator > 0 ? indicator : 0); int1store(p, indicator > 0 ? indicator : 0);
p++; p++;
}
if (has_data) if (has_data)
{
store_param(stmt, i, &p, j); store_param(stmt, i, &p, j);
} }
} }
}
} }
stmt->send_types_to_server= 0; stmt->send_types_to_server= 0;
@@ -853,7 +968,6 @@ mem_error:
return NULL; return NULL;
} }
/* }}} */ /* }}} */
/*! /*!
******************************************************************************* *******************************************************************************
@@ -1820,13 +1934,17 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
stmt->result_cursor= stmt->result.data= 0; stmt->result_cursor= stmt->result.data= 0;
stmt->result.rows= 0; stmt->result.rows= 0;
} }
request= (char *)mysql_stmt_execute_generate_request(stmt, &request_len); if (stmt->array_size > 0)
request= (char *)mysql_stmt_execute_generate_bulk_request(stmt, &request_len);
else
request= (char *)mysql_stmt_execute_generate_simple_request(stmt, &request_len);
if (!request) if (!request)
return 1; return 1;
ret= stmt->mysql->methods->db_command(mysql, COM_STMT_EXECUTE, request, ret= stmt->mysql->methods->db_command(mysql,
request_len, 1, stmt); stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE,
request, request_len, 1, stmt);
if (request) if (request)
free(request); free(request);

View File

@@ -212,6 +212,8 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
if (mysql->client_flag & CLIENT_PROTOCOL_41) if (mysql->client_flag & CLIENT_PROTOCOL_41)
{ {
/* 4.1 server and 4.1 client has a 32 byte option flag */ /* 4.1 server and 4.1 client has a 32 byte option flag */
if (!(mysql->server_capabilities & CLIENT_MYSQL))
mysql->client_flag&= ~CLIENT_MYSQL;
int4store(buff,mysql->client_flag); int4store(buff,mysql->client_flag);
int4store(buff+4, net->max_packet_size); int4store(buff+4, net->max_packet_size);
buff[8]= (char) mysql->charset->nr; buff[8]= (char) mysql->charset->nr;

View File

@@ -512,8 +512,93 @@ static int bulk6(MYSQL *mysql)
return OK; return OK;
} }
static int test_conc243(MYSQL *mysql)
{
MYSQL_STMT *stmt;
MYSQL_BIND bind[3];
MYSQL_RES *result;
MYSQL_ROW row;
struct st_data {
unsigned long id;
char id_ind;
char forename[30];
char forename_ind;
char surname[30];
char surname_ind;
};
struct st_data data[]= {
{0, STMT_INDICATOR_NULL, "Monty", STMT_INDICATOR_NTS, "Widenius", STMT_INDICATOR_NTS},
{0, STMT_INDICATOR_NULL, "David", STMT_INDICATOR_NTS, "Axmark", STMT_INDICATOR_NTS},
{0, STMT_INDICATOR_NULL, "default", STMT_INDICATOR_DEFAULT, "N.N.", STMT_INDICATOR_NTS},
};
unsigned int array_size= 1;
size_t row_size= sizeof(struct st_data);
int rc;
rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk_example2");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "CREATE TABLE bulk_example2 (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"\
"forename CHAR(30) NOT NULL DEFAULT 'unknown', surname CHAR(30))");
check_mysql_rc(rc, mysql);
stmt= mysql_stmt_init(mysql);
rc= mysql_stmt_prepare(stmt, "INSERT INTO bulk_example2 VALUES (?,?,?)", -1);
check_stmt_rc(rc, stmt);
memset(bind, 0, sizeof(MYSQL_BIND) * 3);
/* We autogenerate id's, so all indicators are STMT_INDICATOR_NULL */
bind[0].u.indicator= &data[0].id_ind;
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= &data[0].forename;
bind[1].buffer_type= MYSQL_TYPE_STRING;
bind[1].u.indicator= &data[0].forename_ind;
bind[2].buffer_type= MYSQL_TYPE_STRING;
bind[2].buffer= &data[0].surname;
bind[2].u.indicator= &data[0].surname_ind;
/* set array size */
mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size);
/* set row size */
mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size);
/* bind parameter */
mysql_stmt_bind_param(stmt, bind);
/* execute */
rc= mysql_stmt_execute(stmt);
check_stmt_rc(rc, stmt);
mysql_stmt_close(stmt);
rc= mysql_query(mysql, "SELECT forename, surname FROM bulk_example2");
check_mysql_rc(rc, mysql);
result= mysql_store_result(mysql);
FAIL_IF(!result || !mysql_num_rows(result), "Invalid resultset");
row = mysql_fetch_row(result);
if (strcmp(row[0], "Monty") || strcmp(row[1], "Widenius"))
{
mysql_free_result(result);
diag("Wrong walues");
return FAIL;
}
mysql_free_result(result);
rc= mysql_query(mysql, "DROP TABLE bulk_example2");
check_mysql_rc(rc, mysql);
return OK;
}
struct my_tests_st my_tests[] = { struct my_tests_st my_tests[] = {
{"check_bulk", check_bulk, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"check_bulk", check_bulk, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_conc243", test_conc243, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"bulk5", bulk5, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"bulk5", bulk5, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"bulk6", bulk6, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"bulk6", bulk6, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"bulk1", bulk1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"bulk1", bulk1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},

View File

@@ -832,7 +832,6 @@ static int test_prepare_alter(MYSQL *mysql)
rc= mysql_stmt_execute(stmt); rc= mysql_stmt_execute(stmt);
check_stmt_rc(rc, stmt); check_stmt_rc(rc, stmt);
mysql_new= mysql_init(NULL); mysql_new= mysql_init(NULL);
FAIL_IF(!mysql_new, "mysql_init failed"); FAIL_IF(!mysql_new, "mysql_init failed");
FAIL_IF(!(my_test_connect(mysql_new, hostname, username, password, FAIL_IF(!(my_test_connect(mysql_new, hostname, username, password,