Skip to content

Commit 38d6855

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.
1 parent 9c6ba43 commit 38d6855

5 files changed

Lines changed: 108 additions & 9 deletions

File tree

include/errmsg.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ extern const char *client_errors[]; /* Error messages */
9696
#define CR_NO_DATA 2051
9797
#define CR_NO_STMT_METADATA 2052
9898
#define CR_NO_RESULT_SET 2053
99-
#define CR_ERROR_LAST /*Copy last error nr:*/ 2053
99+
#define CR_NOT_IMPLEMENTED 2054
100+
#define CR_ERROR_LAST /*Copy last error nr:*/ 2054
100101
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
101102

include/mysql.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ typedef struct st_mysql_stmt
663663
unsigned char **row);
664664
unsigned long stmt_id; /* Id for prepared statement */
665665
unsigned long flags; /* i.e. type of cursor to open */
666+
unsigned long prefetch_rows; /* number of rows per one COM_FETCH */
666667
/*
667668
Copied from mysql->server_status after execute/fetch to know
668669
server-side cursor status for this statement.
@@ -701,7 +702,12 @@ enum enum_stmt_attr_type
701702
unsigned long with combination of cursor flags (read only, for update,
702703
etc)
703704
*/
704-
STMT_ATTR_CURSOR_TYPE
705+
STMT_ATTR_CURSOR_TYPE,
706+
/*
707+
Amount of rows to retrieve from server per one fetch if using cursors.
708+
Accepts unsigned long attribute in the range 1 - ulong_max
709+
*/
710+
STMT_ATTR_PREFETCH_ROWS
705711
};
706712

707713

libmysql/errmsg.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const char *client_errors[]=
8181
"Attempt to read column without prior row fetch",
8282
"Prepared statement contains no metadata",
8383
"Attempt to read a row while there is no result set associated with the statement",
84+
"This feature is not implemented yet",
8485
""
8586
};
8687

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

@@ -203,6 +205,7 @@ const char *client_errors[]=
203205
"Attempt to read column without prior row fetch",
204206
"Prepared statement contains no metadata",
205207
"Attempt to read a row while there is no result set associated with the statement",
208+
"This feature is not implemented yet",
206209
""
207210
};
208211
#endif

libmysql/libmysql.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,9 @@ myodbc_remove_escape(MYSQL *mysql,char *name)
17031703

17041704
/******************* Declarations ***********************************/
17051705

1706+
/* Default number of rows fetched per one COM_FETCH command. */
1707+
1708+
#define DEFAULT_PREFETCH_ROWS 1UL
17061709

17071710
/*
17081711
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);
17281731

17291732
#define RESET_SERVER_SIDE 1
17301733
#define RESET_LONG_DATA 2
1734+
#define RESET_STORE_RESULT 4
17311735

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

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

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

2029-
if (reset_stmt_handle(stmt, RESET_LONG_DATA))
2034+
if (reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT))
20302035
DBUG_RETURN(1);
20312036
/*
20322037
These members must be reset for API to
@@ -2681,7 +2686,7 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row)
26812686
result->rows= 0;
26822687
/* Send row request to the server */
26832688
int4store(buff, stmt->stmt_id);
2684-
int4store(buff + 4, 1); /* number of rows to fetch */
2689+
int4store(buff + 4, stmt->prefetch_rows); /* number of rows to fetch */
26852690
if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff),
26862691
NullS, 0, 1))
26872692
{
@@ -2739,12 +2744,29 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
27392744
stmt->update_max_length= value ? *(const my_bool*) value : 0;
27402745
break;
27412746
case STMT_ATTR_CURSOR_TYPE:
2742-
stmt->flags= value ? *(const unsigned long *) value : 0;
2747+
{
2748+
ulong cursor_type;
2749+
cursor_type= value ? *(ulong*) value : 0UL;
2750+
if (cursor_type > (ulong) CURSOR_TYPE_READ_ONLY)
2751+
goto err_not_implemented;
2752+
stmt->flags= cursor_type;
2753+
break;
2754+
}
2755+
case STMT_ATTR_PREFETCH_ROWS:
2756+
{
2757+
ulong prefetch_rows= value ? *(ulong*) value : DEFAULT_PREFETCH_ROWS;
2758+
if (value == 0)
2759+
return TRUE;
2760+
stmt->prefetch_rows= prefetch_rows;
27432761
break;
2762+
}
27442763
default:
2745-
return TRUE;
2764+
goto err_not_implemented;
27462765
}
27472766
return FALSE;
2767+
err_not_implemented:
2768+
set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate);
2769+
return TRUE;
27482770
}
27492771

27502772

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

2824-
if (reset_stmt_handle(stmt, 0))
2846+
if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
28252847
DBUG_RETURN(1);
28262848
/*
28272849
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)
48264848
MYSQL_DATA *result= &stmt->result;
48274849
my_bool has_cursor= stmt->read_row_func == stmt_read_row_from_cursor;
48284850

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

48874913
/* Free the client side and close the server side cursor if there is one */
4888-
DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA));
4914+
DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT));
48894915
}
48904916

48914917
/********************************************************************

tests/mysql_client_test.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13075,6 +13075,68 @@ static void test_bug9478()
1307513075
}
1307613076

1307713077

13078+
/*
13079+
Error message is returned for unsupported features.
13080+
Test also cursors with non-default PREFETCH_ROWS
13081+
*/
13082+
13083+
static void test_bug9643()
13084+
{
13085+
MYSQL_STMT *stmt;
13086+
MYSQL_BIND bind[1];
13087+
int32 a;
13088+
int rc;
13089+
const char *stmt_text;
13090+
int num_rows= 0;
13091+
ulong type;
13092+
ulong prefetch_rows= 5;
13093+
13094+
myheader("test_bug9643");
13095+
13096+
mysql_query(mysql, "drop table if exists t1");
13097+
mysql_query(mysql, "create table t1 (id integer not null primary key)");
13098+
rc= mysql_query(mysql, "insert into t1 (id) values "
13099+
" (1), (2), (3), (4), (5), (6), (7), (8), (9)");
13100+
myquery(rc);
13101+
13102+
stmt= mysql_stmt_init(mysql);
13103+
/* Not implemented in 5.0 */
13104+
type= (ulong) CURSOR_TYPE_SCROLLABLE;
13105+
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
13106+
DIE_UNLESS(rc);
13107+
if (! opt_silent)
13108+
printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
13109+
13110+
type= (ulong) CURSOR_TYPE_READ_ONLY;
13111+
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
13112+
check_execute(stmt, rc);
13113+
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS,
13114+
(void*) &prefetch_rows);
13115+
check_execute(stmt, rc);
13116+
stmt_text= "select * from t1";
13117+
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
13118+
check_execute(stmt, rc);
13119+
13120+
bzero(bind, sizeof(bind));
13121+
bind[0].buffer_type= MYSQL_TYPE_LONG;
13122+
bind[0].buffer= (void*) &a;
13123+
bind[0].buffer_length= sizeof(a);
13124+
mysql_stmt_bind_result(stmt, bind);
13125+
13126+
rc= mysql_stmt_execute(stmt);
13127+
check_execute(stmt, rc);
13128+
13129+
while ((rc= mysql_stmt_fetch(stmt)) == 0)
13130+
++num_rows;
13131+
DIE_UNLESS(num_rows == 9);
13132+
13133+
rc= mysql_stmt_close(stmt);
13134+
DIE_UNLESS(rc == 0);
13135+
13136+
rc= mysql_query(mysql, "drop table t1");
13137+
myquery(rc);
13138+
}
13139+
1307813140
/*
1307913141
Read and parse arguments and MySQL options from my.cnf
1308013142
*/
@@ -13306,6 +13368,7 @@ static struct my_tests_st my_tests[]= {
1330613368
{ "test_bug9159", test_bug9159 },
1330713369
{ "test_bug9520", test_bug9520 },
1330813370
{ "test_bug9478", test_bug9478 },
13371+
{ "test_bug9643", test_bug9643 },
1330913372
{ 0, 0 }
1331013373
};
1331113374

0 commit comments

Comments
 (0)