From 9552507348fac98861d9bb44535c3986bdb39022 Mon Sep 17 00:00:00 2001 From: "holzboote@googlemail.com" <> Date: Sat, 26 Oct 2013 18:55:24 +0200 Subject: [PATCH] Minor prepared statement fixes for time/date/datetime/timestamp types Added flag MADB_BIND_DUMMY which allows binding empty buffers --- include/my_stmt.h | 5 ++- libmariadb/my_alloc.c | 3 +- libmariadb/my_stmt.c | 66 ++++++++++++++++++++++++++++---------- libmariadb/my_stmt_codec.c | 2 +- libmariadb/net.c | 7 ++-- unittest/libmariadb/misc.c | 26 +++++++++++++++ unittest/libmariadb/ps.c | 39 +++++++++++++--------- 7 files changed, 110 insertions(+), 38 deletions(-) diff --git a/include/my_stmt.h b/include/my_stmt.h index 7601e25c..d9d6850d 100644 --- a/include/my_stmt.h +++ b/include/my_stmt.h @@ -25,6 +25,9 @@ #define MYSQL_DATA_TRUNCATED 101 #define MYSQL_DEFAULT_PREFETCH_ROWS (unsigned long) 1 +/* Bind flags */ +#define MADB_BIND_DUMMY 1 + #define SET_CLIENT_STMT_ERROR(a, b, c, d) \ { \ @@ -94,7 +97,7 @@ typedef struct st_mysql_bind unsigned long buffer_length; unsigned long offset; /* offset position for char/binary fetch */ unsigned long length_value; /* Used if length is 0 */ - unsigned int param_number; /* For null count and error messages */ + unsigned int flags; /* special flags, e.g. for dummy bind */ unsigned int pack_length; /* Internal length for packed data */ enum enum_field_types buffer_type; /* buffer type */ my_bool error_value; /* used if error is 0 */ diff --git a/libmariadb/my_alloc.c b/libmariadb/my_alloc.c index f1a2c3dd..ce8e2de3 100644 --- a/libmariadb/my_alloc.c +++ b/libmariadb/my_alloc.c @@ -78,7 +78,7 @@ gptr alloc_root(MEM_ROOT *mem_root, size_t Size) if (max_left*4 < mem_root->block_size && get_size < mem_root->block_size) get_size=mem_root->block_size; /* Normal alloc */ - if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME)))) + if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME | MY_ZEROFILL)))) { if (mem_root->error_handler) (*mem_root->error_handler)(); @@ -141,6 +141,7 @@ char *strdup_root(MEM_ROOT *root,const char *str) char *pos; if ((pos=alloc_root(root,len))) memcpy(pos,str,len); + pos[len]= 0; return pos; } diff --git a/libmariadb/my_stmt.c b/libmariadb/my_stmt.c index 92447782..46f1a02e 100644 --- a/libmariadb/my_stmt.c +++ b/libmariadb/my_stmt.c @@ -65,6 +65,10 @@ #define MADB_RESET_BUFFER 8 #define MADB_RESET_STORED 16 +#define MAX_TIME_STR_LEN 13 +#define MAX_DATE_STR_LEN 5 +#define MAX_DATETIME_STR_LEN 12 + typedef struct { MEM_ROOT fields_alloc_root; @@ -311,15 +315,28 @@ int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row) stmt->bind[i].row_ptr= NULL; } else { - if (!stmt->bind[i].length) - stmt->bind[i].length= &stmt->bind[i].length_value; - if (!stmt->bind[i].is_null) - stmt->bind[i].is_null= &stmt->bind[i].is_null_value; - *stmt->bind[i].is_null= 0; stmt->bind[i].row_ptr= row; - mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row); - if (stmt->mysql->options.report_data_truncation) - truncations+= *stmt->bind[i].error; + if (stmt->bind[i].flags & MADB_BIND_DUMMY) + { + unsigned long length; + + if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len >= 0) + length= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len; + else + length= net_field_length(&row); + row+= length; + } + else + { + if (!stmt->bind[i].length) + stmt->bind[i].length= &stmt->bind[i].length_value; + if (!stmt->bind[i].is_null) + stmt->bind[i].is_null= &stmt->bind[i].is_null_value; + *stmt->bind[i].is_null= 0; + mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row); + if (stmt->mysql->options.report_data_truncation) + truncations+= *stmt->bind[i].error; + } } if (!((bit_offset <<=1) & 255)) { @@ -423,16 +440,22 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p) 9-13 4 second_part */ MYSQL_TIME *t= (MYSQL_TIME *)stmt->params[column].buffer; - char t_buffer[14]; - uint len= *stmt->params[column].length; + char t_buffer[MAX_TIME_STR_LEN]; + uint len= 0; - t_buffer[0]= len; t_buffer[1]= t->neg ? 1 : 0; int4store(t_buffer + 2, t->day); t_buffer[6]= (uchar) t->hour; t_buffer[7]= (uchar) t->minute; - t_buffer[8]= (uchar) t->second; - int4store(t_buffer + 9, t->second_part); + t_buffer[8]= (uchar) t->second; + if (t->second_part) + { + int4store(t_buffer + 9, t->second_part); + len= 12; + } + else if (t->day || t->hour || t->minute || t->second) + len= 8; + t_buffer[0]= len; memcpy(*p, t_buffer, len); len++; (*p)+= len; @@ -454,17 +477,25 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p) 8-11 4 secondpart */ MYSQL_TIME *t= (MYSQL_TIME *)stmt->params[column].buffer; - char t_buffer[12]; + char t_buffer[MAX_DATETIME_STR_LEN]; uint len= *stmt->params[column].length; - t_buffer[0]= len; int2store(t_buffer + 1, t->year); t_buffer[3]= (char) t->month; t_buffer[4]= (char) t->day; t_buffer[5]= (char) t->hour; t_buffer[6]= (char) t->minute; t_buffer[7]= (char) t->second; - int4store(t_buffer + 8, t->second_part); + if (t->second_part) + { + int4store(t_buffer + 8, t->second_part); + len= 12; + } + else if (t->hour || t->minute || t->second) + len= 7; + else if (t->year || t->month || t->day) + len= 4; + t_buffer[0]= len; memcpy(*p, t_buffer, len); len++; (*p)+= len; @@ -586,6 +617,7 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req p+= 2; } } + /* calculate data size */ for (i = 0; i < stmt->param_count; i++) { if (stmt->params[i].buffer && !stmt->params[i].is_null) @@ -648,7 +680,7 @@ unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *req } } stmt->send_types_to_server= 0; - *request_len = (p - start); + *request_len = (size_t)(p - start); DBUG_RETURN(start); diff --git a/libmariadb/my_stmt_codec.c b/libmariadb/my_stmt_codec.c index 457ab7db..cef9a8be 100644 --- a/libmariadb/my_stmt_codec.c +++ b/libmariadb/my_stmt_codec.c @@ -860,7 +860,7 @@ void mysql_init_ps_subsystem(void) mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double; mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8; - mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len = MAX_DOUBLE_STRING_REP_LENGTH; + mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len = MAX_DOUBLE_STRING_REP_LENGTH; mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_datetime; mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = 13; diff --git a/libmariadb/net.c b/libmariadb/net.c index 54ae355b..0cae5054 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -111,7 +111,7 @@ static int net_write_buff(NET *net,const char *packet, size_t len); int my_net_init(NET *net, Vio* vio) { - if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME)))) + if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL)))) return 1; if (net_buffer_length > max_allowed_packet) max_allowed_packet=net_buffer_length; @@ -220,7 +220,7 @@ int net_flush(NET *net) if (net->buff != net->write_pos) { error=net_real_write(net,(char*) net->buff, - (uint) (net->write_pos - net->buff)); + (size_t) (net->write_pos - net->buff)); net->write_pos=net->buff; } if (net->compress) @@ -274,6 +274,7 @@ net_write_command(NET *net, uchar command, size_t length= 1 + len; /* 1 extra byte for command */ int rc; + buff[NET_HEADER_SIZE]= 0; buff[4]=command; if (length >= MAX_PACKET_LENGTH) @@ -401,7 +402,7 @@ net_real_write(NET *net,const char *packet,size_t len) pos=(char*) packet; end=pos+len; while (pos != end) { - if ((long) (length=vio_write(net->vio,pos,(int) (end-pos))) <= 0) + if ((long) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0) { my_bool interrupted = vio_should_retry(net->vio); #if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index 408c5be3..a3be4900 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -24,6 +24,31 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "my_test.h" #include "ma_common.h" + +static int test_conc60(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + char *stmtstr= "SELECT * FROM agendas"; + int rc; + + rc= mysql_stmt_prepare(stmt, stmtstr, strlen(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + diag("rows: %u", mysql_stmt_num_rows(stmt)); + + while (mysql_stmt_fetch(stmt)); + + mysql_stmt_close(stmt); + + return(OK); +} + /* Bug#28075 "COM_DEBUG crashes mysqld" */ @@ -949,6 +974,7 @@ static int test_connect_attrs(MYSQL *my) struct my_tests_st my_tests[] = { {"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc60", test_conc60, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, diff --git a/unittest/libmariadb/ps.c b/unittest/libmariadb/ps.c index 5f8173a5..251af6da 100644 --- a/unittest/libmariadb/ps.c +++ b/unittest/libmariadb/ps.c @@ -99,7 +99,7 @@ static int test_bind_date_conv(MYSQL *mysql, uint row_count) stmt= mysql_stmt_init(mysql); FAIL_IF(!stmt, mysql_error(mysql)); - rc= mysql_stmt_prepare(stmt, "INSERT INTO test_date VALUES(?, ?, ?, ?)", strlen("INSERT INTO test_date VALUES(?, ?, ?, ?)")); + rc= mysql_stmt_prepare(stmt, "INSERT INTO test_date VALUES(?, ?, ?, ?)", strlen("INSERT INTO test_date VALUES(?, ?, ?, ?)") + 1); check_stmt_rc(rc, stmt); FAIL_IF(mysql_stmt_param_count(stmt) != 4, "param_count != 4"); @@ -4734,13 +4734,13 @@ int test_notrunc(MYSQL *mysql) { MYSQL_STMT *stmt; my_bool trunc= 1; - MYSQL_BIND bind[1]; - char buffer[5]; + MYSQL_BIND bind[2]; + char buffer[5], buffer2[5]; int rc; my_bool error= 0; - unsigned long len= 1; + unsigned long len= 1, len2; - char *query= "SELECT '1234567890' FROM DUAL"; + char *query= "SELECT '1234567890', 'foo' FROM DUAL"; mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &trunc); @@ -4752,21 +4752,30 @@ int test_notrunc(MYSQL *mysql) rc= mysql_stmt_execute(stmt); check_stmt_rc(rc, stmt); - memset(bind, 0, sizeof(MYSQL_BIND)); - bind[0].buffer_type= MYSQL_TYPE_LONG; - bind[0].buffer= buffer; - bind[0].buffer_length= 3; - bind[0].length= &len; - bind[0].error= &error; + strcpy(buffer, "bar"); -// rc= mysql_stmt_bind_result(stmt, bind); -// check_stmt_rc(rc, stmt); + memset(bind, 0, sizeof(MYSQL_BIND) * 2); + bind[0].buffer_type= MYSQL_TYPE_NULL; + bind[0].buffer= buffer; + bind[0].buffer_length= 1; + bind[0].length= &len; + bind[0].flags|= MADB_BIND_DUMMY; + bind[0].error= &error; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= buffer2; + bind[1].buffer_length= 5; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); mysql_stmt_store_result(stmt); rc= mysql_stmt_fetch(stmt); - diag("rc= %d len=%lu", rc, len); - mysql_stmt_close(stmt); + + FAIL_IF(rc!= 0, "expected rc= 0"); + FAIL_IF(strcmp(buffer, "bar"), "Bind dummy failed"); + FAIL_IF(strcmp(buffer2, "foo"), "Invalid second buffer"); + return OK; }