Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Fixed
-
2.2.3
-
None
-
None
Description
mysql_stmt_fetch_column always copy field data with offset 0.
Code to repeat the bug (extended for blob field test_fetch_offset test from fetch.c suite)
static int test_fetch_offset(MYSQL *mysql)
|
{
|
MYSQL_STMT *stmt;
|
MYSQL_BIND my_bind[2];
|
char data[11], chunk[5];
|
ulong length[2];
|
int rc;
|
my_bool is_null[2];
|
char *query = "SELECT * FROM t1";
|
|
|
rc= mysql_query(mysql, "drop table if exists t1");
|
check_mysql_rc(rc, mysql);
|
rc= mysql_query(mysql, "create table t1(a char(10), b mediumblob)");
|
check_mysql_rc(rc, mysql);
|
rc= mysql_query(mysql, "insert into t1 values('abcdefghij', 'klmnopqrstzy'), (null, null)");
|
check_mysql_rc(rc, mysql);
|
|
stmt= mysql_stmt_init(mysql);
|
FAIL_IF(!stmt, mysql_error(mysql));
|
|
rc= mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query));
|
check_stmt_rc(rc,stmt);
|
|
memset(my_bind, '\0', sizeof(my_bind));
|
my_bind[0].buffer_type= MYSQL_TYPE_STRING;
|
my_bind[0].buffer= (void *)data;
|
my_bind[0].buffer_length= 11;
|
my_bind[0].is_null= &is_null[0];
|
my_bind[0].length= &length[0];
|
|
my_bind[1].buffer_type= MYSQL_TYPE_STRING;
|
my_bind[1].buffer= NULL;
|
my_bind[1].buffer_length= 0;
|
my_bind[1].is_null= &is_null[1];
|
my_bind[1].length= &length[1];
|
|
rc= mysql_stmt_execute(stmt);
|
check_stmt_rc(rc,stmt);
|
|
rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
|
FAIL_IF(!rc, "Error expected");
|
|
rc= mysql_stmt_execute(stmt);
|
check_stmt_rc(rc,stmt);
|
|
rc= mysql_stmt_bind_result(stmt, my_bind);
|
check_stmt_rc(rc,stmt);
|
|
rc= mysql_stmt_store_result(stmt);
|
check_stmt_rc(rc,stmt);
|
|
rc= mysql_stmt_fetch(stmt);
|
FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED, "rc != MYSQL_DATA_TRUNCATED");
|
|
data[0]= '\0';
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 0);
|
check_stmt_rc(rc,stmt);
|
|
|
FAIL_IF(!(strncmp(data, "abcd", 4) == 0 && length[0] == 10), "Wrong value");
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 5);
|
check_stmt_rc(rc,stmt);
|
FAIL_IF(!(strncmp(data, "fg", 2) == 0 && length[0] == 10), "Wrong value");
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 9);
|
check_stmt_rc(rc,stmt);
|
FAIL_IF(!(strncmp(data, "j", 1) == 0 && length[0] == 10), "Wrong value");
|
|
/* Now blob field */
|
my_bind[1].buffer= chunk;
|
my_bind[1].buffer_length= sizeof(chunk);
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 0);
|
check_stmt_rc(rc,stmt);
|
|
FAIL_IF(!(strncmp(chunk, "klmno", 5) == 0 && length[1] == 12), "Wrong value");
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 5);
|
check_stmt_rc(rc,stmt);
|
FAIL_IF(!(strncmp(chunk, "pqrst", 5) == 0 && length[1] == 12), "Wrong value");
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 10);
|
check_stmt_rc(rc,stmt);
|
FAIL_IF(!(strncmp(chunk, "zy", 2) == 0 && length[1] == 12), "Wrong value");
|
|
rc= mysql_stmt_fetch(stmt);
|
check_stmt_rc(rc,stmt);
|
|
memset(is_null, 0, sizeof(is_null));
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 0);
|
check_stmt_rc(rc,stmt);
|
|
FAIL_IF(is_null[0] != 1, "Null flag not set");
|
|
rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 0);
|
check_stmt_rc(rc,stmt);
|
|
FAIL_IF(is_null[1] != 1, "Null flag not set");
|
|
rc= mysql_stmt_fetch(stmt);
|
FAIL_IF(rc != MYSQL_NO_DATA, "Expected MYSQL_NO_DATA");
|
|
rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
|
FAIL_IF(!rc, "Error expected");
|
|
mysql_stmt_close(stmt);
|
|
rc= mysql_query(mysql, "drop table t1");
|
check_mysql_rc(rc, mysql);
|
|
return OK;
|
}
|
Proposed fix:
diff --git a/libmariadb/my_stmt_codec.c b/libmariadb/my_stmt_codec.c
|
index 7d90151..6e9dee2 100644
|
--- a/libmariadb/my_stmt_codec.c
|
+++ b/libmariadb/my_stmt_codec.c
|
@@ -925,8 +925,9 @@ void ps_fetch_bin(MYSQL_BIND *r_param, const MYSQL_FIELD *field,
|
if (!(field->flags & BINARY_FLAG) && r_param->buffer_type == MYSQL_TYPE_STRING)
|
field_length++;
|
|
- copylen= MIN(field_length, r_param->buffer_length);
|
- memcpy(r_param->buffer, *row, copylen);
|
+ /* Making sure that we do not copy past the field. e.g. if offset >= field_length, copylen is 0 */
|
+ copylen= MIN(field_length - MIN(r_param->offset, field_length), r_param->buffer_length);
|
+ memcpy(r_param->buffer, *row + MIN(r_param->offset, field_length), copylen);
|
*r_param->error= copylen < field_length;
|
|
/* don't count trailing zero if we fetch into string */
|
@@ -934,9 +935,9 @@ void ps_fetch_bin(MYSQL_BIND *r_param, const MYSQL_FIELD *field,
|
!*r_param->error)
|
field_length--;
|
|
- *r_param->length= field_length;
|
+ *r_param->length= field_length; /*copylen;*/
|
|
- (*row) += field_length;
|
+ (*row) += copylen/*field_length*/;
|
}
|
/* }}} */
|