Commit 1bb1bc69 authored by unknown's avatar unknown
Browse files

A fix and a test case for Bug#9643 " CURSOR_TYPE_SCROLLABLE dos not work"

- check on the client the unsupported feature and return 
an error message if it's been requested.
Additionally added API support for STMT_ATTR_PREFETCH_ROWS.
Post-review fixes.


include/errmsg.h:
  Add a new error code for "Not implemented" client-side error message.
include/mysql.h:
  Add a statement attribute STMT_ATTR_PREFETCH_ROWS - unsigned long
  number of rows to fetch per one COM_FETCH command, used when there
  is a read-only cursor.
  Note, that we don't break compatibility by adding this new member
  because MYSQL_STMT is always allocated inside the client library by
  mysql_stmt_init.
libmysql/errmsg.c:
  Text for the error message CR_NOT_IMPLEMENTED
libmysql/libmysql.c:
  Implement support for STMT_ATTR_PREFETCH_ROWS
  Return an error message on attempt to set an attribute of a prepared
  statement which is not implemented yet. We probably should be doing
  it in the server: currently the server just ignores unknown attributes.
tests/mysql_client_test.c:
  A test case for Bug#9643 "CURSOR_TYPE_SCROLLABLE dos not work"
  - check that an error message is returned for CURSOR_TYPE_SCROLLABLE.
  Additionally, check setting of STMT_ATTR_PREFETCH_ROWS.
parent 6f4c2486
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ extern const char *client_errors[]; /* Error messages */
#define CR_NO_DATA                              2051
#define CR_NO_STMT_METADATA                     2052
#define CR_NO_RESULT_SET                        2053
#define CR_ERROR_LAST  /*Copy last error nr:*/  2053
#define CR_NOT_IMPLEMENTED                      2054
#define CR_ERROR_LAST  /*Copy last error nr:*/  2054
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
+7 −1
Original line number Diff line number Diff line
@@ -663,6 +663,7 @@ typedef struct st_mysql_stmt
                                  unsigned char **row);
  unsigned long	 stmt_id;	       /* Id for prepared statement */
  unsigned long  flags;                /* i.e. type of cursor to open */
  unsigned long  prefetch_rows;        /* number of rows per one COM_FETCH */
  /*
    Copied from mysql->server_status after execute/fetch to know
    server-side cursor status for this statement.
@@ -701,7 +702,12 @@ enum enum_stmt_attr_type
    unsigned long with combination of cursor flags (read only, for update,
    etc)
  */
  STMT_ATTR_CURSOR_TYPE
  STMT_ATTR_CURSOR_TYPE,
  /*
    Amount of rows to retrieve from server per one fetch if using cursors.
    Accepts unsigned long attribute in the range 1 - ulong_max
  */
  STMT_ATTR_PREFETCH_ROWS
};


+3 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ const char *client_errors[]=
  "Attempt to read column without prior row fetch",
  "Prepared statement contains no metadata",
  "Attempt to read a row while there is no result set associated with the statement",
  "This feature is not implemented yet",
  ""
};

@@ -143,6 +144,7 @@ const char *client_errors[]=
  "Attempt to read column without prior row fetch",
  "Prepared statement contains no metadata",
  "Attempt to read a row while there is no result set associated with the statement",
  "This feature is not implemented yet",
  ""
};

@@ -203,6 +205,7 @@ const char *client_errors[]=
  "Attempt to read column without prior row fetch",
  "Prepared statement contains no metadata",
  "Attempt to read a row while there is no result set associated with the statement",
  "This feature is not implemented yet",
  ""
};
#endif
+33 −7
Original line number Diff line number Diff line
@@ -1703,6 +1703,9 @@ myodbc_remove_escape(MYSQL *mysql,char *name)

/******************* Declarations ***********************************/

/* Default number of rows fetched per one COM_FETCH command. */

#define DEFAULT_PREFETCH_ROWS 1UL

/*
  These functions are called by function pointer MYSQL_STMT::read_row_func.
@@ -1728,6 +1731,7 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field);

#define RESET_SERVER_SIDE 1
#define RESET_LONG_DATA 2
#define RESET_STORE_RESULT 4

static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags);

@@ -1968,6 +1972,7 @@ mysql_stmt_init(MYSQL *mysql)
  stmt->state= MYSQL_STMT_INIT_DONE;
  stmt->mysql= mysql;
  stmt->read_row_func= stmt_read_row_no_data;
  stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS;
  /* The rest of statement members was bzeroed inside malloc */

  DBUG_RETURN(stmt);
@@ -2026,7 +2031,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
    /* This is second prepare with another statement */
    char buff[MYSQL_STMT_HEADER];               /* 4 bytes - stmt id */

    if (reset_stmt_handle(stmt, RESET_LONG_DATA))
    if (reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT))
      DBUG_RETURN(1);
    /*
      These members must be reset for API to
@@ -2681,7 +2686,7 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row)
    result->rows= 0;
    /* Send row request to the server */
    int4store(buff, stmt->stmt_id);
    int4store(buff + 4, 1); /* number of rows to fetch */
    int4store(buff + 4, stmt->prefetch_rows); /* number of rows to fetch */
    if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff),
                             NullS, 0, 1))
    {
@@ -2739,12 +2744,29 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
    stmt->update_max_length= value ? *(const my_bool*) value : 0;
    break;
  case STMT_ATTR_CURSOR_TYPE:
    stmt->flags= value ? *(const unsigned long *) value : 0;
  {
    ulong cursor_type;
    cursor_type= value ? *(ulong*) value : 0UL;
    if (cursor_type > (ulong) CURSOR_TYPE_READ_ONLY)
      goto err_not_implemented;
    stmt->flags= cursor_type;
    break;
  default:
  }
  case STMT_ATTR_PREFETCH_ROWS:
  {
    ulong prefetch_rows= value ? *(ulong*) value : DEFAULT_PREFETCH_ROWS;
    if (value == 0)
      return TRUE;
    stmt->prefetch_rows= prefetch_rows;
    break;
  }
  default:
    goto err_not_implemented;
  }
  return FALSE;
err_not_implemented:
  set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate);
  return TRUE;
}


@@ -2821,7 +2843,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
    DBUG_RETURN(1);
  }

  if (reset_stmt_handle(stmt, 0))
  if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
    DBUG_RETURN(1);
  /*
    No need to check for stmt->state: if the statement wasn't
@@ -4826,7 +4848,11 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags)
    MYSQL_DATA *result= &stmt->result;
    my_bool has_cursor= stmt->read_row_func == stmt_read_row_from_cursor;

    if (result->data)
    /*
      Reset stored result set if so was requested or it's a part
      of cursor fetch.
    */
    if (result->data && (has_cursor || (flags & RESET_STORE_RESULT)))
    {
      /* Result buffered */
      free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
@@ -4885,7 +4911,7 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
  DBUG_ENTER("mysql_stmt_free_result");

  /* Free the client side and close the server side cursor if there is one */
  DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA));
  DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT));
}

/********************************************************************
+63 −0
Original line number Diff line number Diff line
@@ -13075,6 +13075,68 @@ static void test_bug9478()
}


/*
  Error message is returned for unsupported features.
  Test also cursors with non-default PREFETCH_ROWS
*/

static void test_bug9643()
{
  MYSQL_STMT *stmt;
  MYSQL_BIND bind[1];
  int32 a;
  int rc;
  const char *stmt_text;
  int num_rows= 0;
  ulong type;
  ulong prefetch_rows= 5;

  myheader("test_bug9643");

  mysql_query(mysql, "drop table if exists t1");
  mysql_query(mysql, "create table t1 (id integer not null primary key)");
  rc= mysql_query(mysql, "insert into t1 (id) values "
                         " (1), (2), (3), (4), (5), (6), (7), (8), (9)");
  myquery(rc);

  stmt= mysql_stmt_init(mysql);
  /* Not implemented in 5.0 */
  type= (ulong) CURSOR_TYPE_SCROLLABLE;
  rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
  DIE_UNLESS(rc);
  if (! opt_silent)
    printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));

  type= (ulong) CURSOR_TYPE_READ_ONLY;
  rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
  check_execute(stmt, rc);
  rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS,
                          (void*) &prefetch_rows);
  check_execute(stmt, rc);
  stmt_text= "select * from t1";
  rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
  check_execute(stmt, rc);

  bzero(bind, sizeof(bind));
  bind[0].buffer_type= MYSQL_TYPE_LONG;
  bind[0].buffer= (void*) &a;
  bind[0].buffer_length= sizeof(a);
  mysql_stmt_bind_result(stmt, bind);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  while ((rc= mysql_stmt_fetch(stmt)) == 0)
    ++num_rows;
  DIE_UNLESS(num_rows == 9);

  rc= mysql_stmt_close(stmt);
  DIE_UNLESS(rc == 0);

  rc= mysql_query(mysql, "drop table t1");
  myquery(rc);
}

/*
  Read and parse arguments and MySQL options from my.cnf
*/
@@ -13306,6 +13368,7 @@ static struct my_tests_st my_tests[]= {
  { "test_bug9159", test_bug9159 },
  { "test_bug9520", test_bug9520 },
  { "test_bug9478", test_bug9478 },
  { "test_bug9643", test_bug9643 },
  { 0, 0 }
};