[CONC-194] mysql_stmt_fetch_column does not respect 'offset' parameter for blob fields Created: 2016-07-25  Updated: 2016-08-03  Resolved: 2016-08-03

Status: Closed
Project: MariaDB Connector/C
Component/s: None
Affects Version/s: 2.2.3
Fix Version/s: 3.0.1, 2.3.1

Type: Bug Priority: Major
Reporter: Lawrin Novitsky Assignee: Georg Richter
Resolution: Fixed Votes: 0
Labels: None

Attachments: File blob_issue_v2.diff    
Issue Links:
Problem/Incident
causes CONC-195 Inconsistent data if blob field fetch... Closed
causes ODBC-47 Inconsistent data if blob field fetch... Closed

 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*/;
 }
 /* }}} */



 Comments   
Comment by Georg Richter [ 2016-08-03 ]

Fixed - revisions:
C/C 3.0.1: 4f2c9da859be26330c0c919dce25b65508bf48ac
C/C 2.3.1: d3b82898f6ef8d3d01416cc82e52e6eb09831f7e

Generated at Thu Feb 08 03:03:33 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.