mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
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.
This commit is contained in:
parent
9c6ba43eb1
commit
38d68559bf
5 changed files with 108 additions and 9 deletions
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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:
|
||||
return TRUE;
|
||||
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));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue