diff options
-rw-r--r-- | include/errmsg.h | 3 | ||||
-rw-r--r-- | libmysql/errmsg.c | 3 | ||||
-rw-r--r-- | libmysql/libmysql.c | 120 | ||||
-rw-r--r-- | sql-common/client.c | 2 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 12 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 27 | ||||
-rw-r--r-- | sql/sql_select.cc | 18 | ||||
-rw-r--r-- | sql/sql_select.h | 4 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 197 |
9 files changed, 293 insertions, 93 deletions
diff --git a/include/errmsg.h b/include/errmsg.h index ae3b04b4f3a..fd3da392df4 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -95,6 +95,7 @@ extern const char *client_errors[]; /* Error messages */ #define CR_FETCH_CANCELED 2050 #define CR_NO_DATA 2051 #define CR_NO_STMT_METADATA 2052 -#define CR_ERROR_LAST /*Copy last error nr:*/ 2052 +#define CR_NO_RESULT_SET 2053 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2053 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */ diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 90ad3aefaca..48e44327d0f 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -80,6 +80,7 @@ const char *client_errors[]= "Row retrieval was canceled by mysql_stmt_close() call", "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" "" }; @@ -141,6 +142,7 @@ const char *client_errors[]= "Row retrieval was canceled by mysql_stmt_close() call", "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" "" }; @@ -200,6 +202,7 @@ const char *client_errors[]= "Row retrieval was canceled by mysql_stmt_close() call", "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" "" }; #endif diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index bf797dfd580..3e6b29b7602 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1724,6 +1724,13 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field); +/* Auxilary function used to reset statement handle. */ + +#define RESET_SERVER_SIDE 1 +#define RESET_LONG_DATA 2 + +static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags); + /* Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME values stored in network buffer. @@ -2019,7 +2026,8 @@ 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 */ - mysql_stmt_free_result(stmt); + if (reset_stmt_handle(stmt, RESET_LONG_DATA)) + DBUG_RETURN(1); /* These members must be reset for API to function in case of error or misuse. @@ -2702,12 +2710,8 @@ static int stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)), unsigned char **row __attribute__((unused))) { - if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) - { - set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate); - return 1; - } - return MYSQL_NO_DATA; + set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate); + return 1; } @@ -2817,7 +2821,8 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) DBUG_RETURN(1); } - mysql_stmt_free_result(stmt); + if (reset_stmt_handle(stmt, 0)) + DBUG_RETURN(1); /* No need to check for stmt->state: if the statement wasn't prepared we'll get 'unknown statement handler' error from server. @@ -4805,16 +4810,21 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) DBUG_RETURN(stmt->result.rows); } -my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) -{ - MYSQL_DATA *result= &stmt->result; - DBUG_ENTER("mysql_stmt_free_result"); - DBUG_ASSERT(stmt != 0); +/* + Free the client side memory buffers, reset long data state + on client if necessary, and reset the server side statement if + this has been requested. +*/ +static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags) +{ + /* If statement hasn't been prepared there is nothing to reset */ if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) { MYSQL *mysql= stmt->mysql; + MYSQL_DATA *result= &stmt->result; + my_bool has_cursor= stmt->read_row_func == stmt_read_row_from_cursor; if (result->data) { @@ -4824,23 +4834,58 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) result->rows= 0; stmt->data_cursor= NULL; } - - if (mysql && stmt->field_count && - (int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE) + if (flags & RESET_LONG_DATA) { - if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) - mysql->unbuffered_fetch_owner= 0; - if (mysql->status != MYSQL_STATUS_READY) + MYSQL_BIND *param= stmt->params, *param_end= param + stmt->param_count; + /* Clear long_data_used flags */ + for (; param < param_end; param++) + param->long_data_used= 0; + } + stmt->read_row_func= stmt_read_row_no_data; + if (mysql) + { + if ((int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE) { - /* There is a result set and it belongs to this statement */ - (*mysql->methods->flush_use_result)(mysql); - mysql->status= MYSQL_STATUS_READY; + if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + if (stmt->field_count && mysql->status != MYSQL_STATUS_READY) + { + /* There is a result set and it belongs to this statement */ + (*mysql->methods->flush_use_result)(mysql); + if (mysql->unbuffered_fetch_owner) + *mysql->unbuffered_fetch_owner= TRUE; + mysql->status= MYSQL_STATUS_READY; + } + } + if (has_cursor || (flags & RESET_SERVER_SIDE)) + { + /* + Reset the server side statement and close the server side + cursor if it exists. + */ + char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */ + int4store(buff, stmt->stmt_id); + if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff, + sizeof(buff), 0, 0, 0)) + { + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + mysql->net.sqlstate); + stmt->state= MYSQL_STMT_INIT_DONE; + return 1; + } } } stmt->state= MYSQL_STMT_PREPARE_DONE; - stmt->read_row_func= stmt_read_row_no_data; } - DBUG_RETURN(0); + return 0; +} + +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)); } /******************************************************************** @@ -4913,33 +4958,10 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) { - char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */ - MYSQL *mysql; - MYSQL_BIND *param, *param_end; DBUG_ENTER("mysql_stmt_reset"); DBUG_ASSERT(stmt != 0); - - /* If statement hasnt been prepared there is nothing to reset */ - if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) - DBUG_RETURN(0); - - mysql= stmt->mysql->last_used_con; - int4store(buff, stmt->stmt_id); /* Send stmt id to server */ - if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff, - sizeof(buff), 0, 0, 0)) - { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); - DBUG_RETURN(1); - } - - /* Clear long_data_used for next call (as we do in mysql_stmt_execute() */ - for (param= stmt->params, param_end= param + stmt->param_count; - param < param_end; - param++) - param->long_data_used= 0; - - DBUG_RETURN(0); + /* Reset the client and server sides of the prepared statement */ + DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA)); } /* diff --git a/sql-common/client.c b/sql-common/client.c index 59e27e4090a..4cbdb4b8e0d 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -864,6 +864,8 @@ mysql_free_result(MYSQL_RES *result) { (*mysql->methods->flush_use_result)(mysql); mysql->status=MYSQL_STATUS_READY; + if (mysql->unbuffered_fetch_owner) + *mysql->unbuffered_fetch_owner= TRUE; } } free_rows(result->data); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 388f47bf96f..848cc422dc2 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -4766,13 +4766,13 @@ ER_SUBQUERY_NO_1_ROW 21000 swe "Subquery returnerade mer än 1 rad" ukr "ð¦ÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ¦ÌØÛ ÎiÖ 1 ÚÁÐÉÓ" ER_UNKNOWN_STMT_HANDLER - dan "Unknown prepared statement handler (%ld) given to %s" + dan "Unknown prepared statement handler (%.*s) given to %s" eng "Unknown prepared statement handler (%.*s) given to %s" ger "Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben" por "Desconhecido manipulador de declaração preparado (%.*s) determinado para %s" - spa "Desconocido preparado comando handler (%ld) dado para %s" - swe "Okänd PREPARED STATEMENT id (%ld) var given till %s" - ukr "Unknown prepared statement handler (%ld) given to %s" + spa "Desconocido preparado comando handler (%.*s) dado para %s" + swe "Okänd PREPARED STATEMENT id (%.*s) var given till %s" + ukr "Unknown prepared statement handler (%.*s) given to %s" ER_CORRUPT_HELP_DB eng "Help database is corrupt or does not exist" ger "Die Hilfe-Datenbank ist beschädigt oder existiert nicht" @@ -5352,3 +5352,7 @@ ER_BINLOG_UNSAFE_ROUTINE eng "This routine is declared to be non-deterministic and to modify data and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)" ER_BINLOG_CREATE_ROUTINE_NEED_SUPER eng "You do not have SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable)" +ER_EXEC_STMT_WITH_OPEN_CURSOR + eng "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it." +ER_STMT_HAS_NO_OPEN_CURSOR + eng "The statement (%d) has no open cursor." diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 17c5f51f1e1..03b3df1bc22 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -135,7 +135,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where) if (stmt == 0 || stmt->type() != Item_arena::PREPARED_STATEMENT) { char llbuf[22]; - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where); + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf), llstr(id, llbuf), + where); return 0; } return (Prepared_statement *) stmt; @@ -1969,7 +1970,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); ulong flags= (ulong) ((uchar) packet[4]); - Cursor *cursor= 0; + Cursor *cursor; /* Query text for binary log, or empty string if the query is not put into binary log. @@ -1995,6 +1996,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } + cursor= stmt->cursor; + if (cursor && cursor->is_open()) + { + my_error(ER_EXEC_STMT_WITH_OPEN_CURSOR, MYF(0)); + DBUG_VOID_RETURN; + } + DBUG_ASSERT(thd->free_list == NULL); mysql_reset_thd_for_next_command(thd); if (flags & (ulong) CURSOR_TYPE_READ_ONLY) @@ -2013,7 +2021,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) else { DBUG_PRINT("info",("Using READ_ONLY cursor")); - if (!stmt->cursor && + if (!cursor && !(cursor= stmt->cursor= new (&stmt->main_mem_root) Cursor())) DBUG_VOID_RETURN; /* If lex->result is set, mysql_execute_command will use it */ @@ -2208,13 +2216,15 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) Statement *stmt; DBUG_ENTER("mysql_stmt_fetch"); - if (!(stmt= thd->stmt_map.find(stmt_id)) || - !stmt->cursor || - !stmt->cursor->is_open()) + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch"))) + DBUG_VOID_RETURN; + + if (!stmt->cursor || !stmt->cursor->is_open()) { - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch"); + my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0)); DBUG_VOID_RETURN; } + thd->current_arena= stmt; thd->set_n_backup_statement(stmt, &thd->stmt_backup); stmt->cursor->init_thd(thd); @@ -2266,6 +2276,9 @@ void mysql_stmt_reset(THD *thd, char *packet) if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset"))) DBUG_VOID_RETURN; + if (stmt->cursor && stmt->cursor->is_open()) + stmt->cursor->close(); + stmt->state= Item_arena::PREPARED; /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47c7de6eba7..84f3bb92a7d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1742,6 +1742,7 @@ Cursor::init_from_thd(THD *thd) /* XXX: thd->locked_tables is not changed. What problems can we have with it if cursor is open? + TODO: must be fixed because of the prelocked mode. */ /* TODO: grab thd->free_list here? @@ -1871,10 +1872,6 @@ Cursor::fetch(ulong num_rows) } else if (error != NESTED_LOOP_KILLED) my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - /* free cursor memory */ - free_items(free_list); - free_list= 0; - free_root(&main_mem_root, MYF(0)); } } @@ -1914,6 +1911,13 @@ Cursor::close() } join= 0; unit= 0; + free_items(free_list); + free_list= 0; + /* + Must be last, as some memory might be allocated for free purposes, + like in free_tmp_table() (TODO: fix this issue) + */ + free_root(mem_root, MYF(0)); DBUG_VOID_RETURN; } @@ -1922,12 +1926,6 @@ Cursor::~Cursor() { if (is_open()) close(); - free_items(free_list); - /* - Must be last, as some memory might be allocated for free purposes, - like in free_tmp_table() (TODO: fix this issue) - */ - free_root(&main_mem_root, MYF(0)); } /*********************************************************************/ diff --git a/sql/sql_select.h b/sql/sql_select.h index a27fbc60856..49ac58e913b 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -364,6 +364,10 @@ class JOIN :public Sql_alloc /* Server-side cursor (now stands only for basic read-only cursor) See class implementation in sql_select.cc + A cursor has its own runtime state - list of used items and memory root of + used memory - which is different from Prepared statement runtime: it must + be different at least for the purpose of reusing the same prepared + statement for many cursors. */ class Cursor: public Sql_alloc, public Item_arena diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 6ab5843803e..da57fbabaf2 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -2269,7 +2269,7 @@ static void test_ps_conj_select() check_execute(stmt, rc); int_data= 1; - strcpy(str_data, "hh"); + strmov(str_data, "hh"); str_length= strlen(str_data); rc= mysql_stmt_execute(stmt); @@ -7288,7 +7288,7 @@ static void test_decimal_bug() rc= mysql_stmt_bind_param(stmt, bind); check_execute(stmt, rc); - strcpy(data, "8.0"); + strmov(data, "8.0"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); @@ -7306,7 +7306,7 @@ static void test_decimal_bug() rc= mysql_stmt_fetch(stmt); DIE_UNLESS(rc == MYSQL_NO_DATA); - strcpy(data, "5.61"); + strmov(data, "5.61"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); @@ -7331,7 +7331,7 @@ static void test_decimal_bug() rc= mysql_stmt_fetch(stmt); DIE_UNLESS(rc == MYSQL_NO_DATA); - strcpy(data, "10.22"); is_null= 0; + strmov(data, "10.22"); is_null= 0; rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); @@ -8510,13 +8510,13 @@ static void test_sqlmode() myquery(rc); /* PIPES_AS_CONCAT */ - strcpy(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\""); + strmov(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\""); if (!opt_silent) fprintf(stdout, "\n With %s", query); rc= mysql_query(mysql, query); myquery(rc); - strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); + strmov(query, "INSERT INTO test_piping VALUES(?||?)"); if (!opt_silent) fprintf(stdout, "\n query: %s", query); stmt= mysql_simple_prepare(mysql, query); @@ -8542,7 +8542,7 @@ static void test_sqlmode() rc= mysql_stmt_bind_param(stmt, bind); check_execute(stmt, rc); - strcpy(c1, "My"); strcpy(c2, "SQL"); + strmov(c1, "My"); strmov(c2, "SQL"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); @@ -8552,20 +8552,20 @@ static void test_sqlmode() rc= mysql_query(mysql, "DELETE FROM test_piping"); myquery(rc); - strcpy(query, "SELECT connection_id ()"); + strmov(query, "SELECT connection_id ()"); if (!opt_silent) fprintf(stdout, "\n query: %s", query); stmt= mysql_simple_prepare(mysql, query); check_stmt_r(stmt); /* ANSI */ - strcpy(query, "SET SQL_MODE= \"ANSI\""); + strmov(query, "SET SQL_MODE= \"ANSI\""); if (!opt_silent) fprintf(stdout, "\n With %s", query); rc= mysql_query(mysql, query); myquery(rc); - strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); + strmov(query, "INSERT INTO test_piping VALUES(?||?)"); if (!opt_silent) fprintf(stdout, "\n query: %s", query); stmt= mysql_simple_prepare(mysql, query); @@ -8576,7 +8576,7 @@ static void test_sqlmode() rc= mysql_stmt_bind_param(stmt, bind); check_execute(stmt, rc); - strcpy(c1, "My"); strcpy(c2, "SQL"); + strmov(c1, "My"); strmov(c2, "SQL"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); @@ -8584,7 +8584,7 @@ static void test_sqlmode() verify_col_data("test_piping", "name", "MySQL"); /* ANSI mode spaces ... */ - strcpy(query, "SELECT connection_id ()"); + strmov(query, "SELECT connection_id ()"); if (!opt_silent) fprintf(stdout, "\n query: %s", query); stmt= mysql_simple_prepare(mysql, query); @@ -8604,13 +8604,13 @@ static void test_sqlmode() mysql_stmt_close(stmt); /* IGNORE SPACE MODE */ - strcpy(query, "SET SQL_MODE= \"IGNORE_SPACE\""); + strmov(query, "SET SQL_MODE= \"IGNORE_SPACE\""); if (!opt_silent) fprintf(stdout, "\n With %s", query); rc= mysql_query(mysql, query); myquery(rc); - strcpy(query, "SELECT connection_id ()"); + strmov(query, "SELECT connection_id ()"); if (!opt_silent) fprintf(stdout, "\n query: %s", query); stmt= mysql_simple_prepare(mysql, query); @@ -9115,7 +9115,7 @@ static void test_bug2248() /* This too should not hang but should return proper error */ rc= mysql_stmt_fetch(stmt); - DIE_UNLESS(rc == MYSQL_NO_DATA); + DIE_UNLESS(rc == 1); /* This too should not hang but should not bark */ rc= mysql_stmt_store_result(stmt); @@ -9124,7 +9124,7 @@ static void test_bug2248() /* This should return proper error */ rc= mysql_stmt_fetch(stmt); check_execute_r(stmt, rc); - DIE_UNLESS(rc == MYSQL_NO_DATA); + DIE_UNLESS(rc == 1); mysql_stmt_close(stmt); @@ -10266,7 +10266,7 @@ static void test_union_param() my_bool my_null= FALSE; myheader("test_union_param"); - strcpy(my_val, "abc"); + strmov(my_val, "abc"); query= (char*)"select ? as my_col union distinct select ?"; stmt= mysql_simple_prepare(mysql, query); @@ -10548,14 +10548,14 @@ static void test_bug3796() if (!opt_silent) printf("Concat result: '%s'\n", out_buff); check_execute(stmt, rc); - strcpy(canonical_buff, concat_arg0); + strmov(canonical_buff, concat_arg0); strcat(canonical_buff, "ONE"); DIE_UNLESS(strlen(canonical_buff) == out_length && strncmp(out_buff, canonical_buff, out_length) == 0); rc= mysql_stmt_fetch(stmt); check_execute(stmt, rc); - strcpy(canonical_buff + strlen(concat_arg0), "TWO"); + strmov(canonical_buff + strlen(concat_arg0), "TWO"); DIE_UNLESS(strlen(canonical_buff) == out_length && strncmp(out_buff, canonical_buff, out_length) == 0); if (!opt_silent) @@ -10852,7 +10852,8 @@ static void test_view() rc= mysql_stmt_prepare(stmt, query, strlen(query)); check_execute(stmt, rc); - strcpy(str_data, "TEST"); + strmov(str_data, "TEST"); + bzero(bind, sizeof(bind)); bind[0].buffer_type= FIELD_TYPE_STRING; bind[0].buffer= (char *)&str_data; bind[0].buffer_length= 50; @@ -10971,8 +10972,9 @@ static void test_view_2where() " AENAME,T0001.DEPENDVARS AS DEPENDVARS,T0001.INACTIVE AS " " INACTIVE from LTDX T0001 where (T0001.SRTF2 = 0)"); myquery(rc); + bzero(bind, sizeof(bind)); for (i=0; i < 8; i++) { - strcpy(parms[i], "1"); + strmov(parms[i], "1"); bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; bind[i].buffer = (char *)&parms[i]; bind[i].buffer_length = 100; @@ -11019,6 +11021,7 @@ static void test_view_star() myquery(rc); rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1"); myquery(rc); + bzero(bind, sizeof(bind)); for (i= 0; i < 2; i++) { sprintf((char *)&parms[i], "%d", i); bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; @@ -11084,6 +11087,7 @@ static void test_view_insert() rc= mysql_stmt_prepare(select_stmt, query, strlen(query)); check_execute(select_stmt, rc); + bzero(bind, sizeof(bind)); bind[0].buffer_type = FIELD_TYPE_LONG; bind[0].buffer = (char *)&my_val; bind[0].length = &my_length; @@ -11187,6 +11191,7 @@ static void test_view_insert_fields() " F7F8 AS F7F8, F6N4 AS F6N4, F5C8 AS F5C8, F9D8 AS F9D8" " from t1 T0001"); + bzero(bind, sizeof(bind)); for (i= 0; i < 11; i++) { l[i]= 20; @@ -11194,7 +11199,7 @@ static void test_view_insert_fields() bind[i].is_null= 0; bind[i].buffer= (char *)&parm[i]; - strcpy(parm[i], "1"); + strmov(parm[i], "1"); bind[i].buffer_length= 2; bind[i].length= &l[i]; } @@ -12707,6 +12712,7 @@ from t2);"); rc= mysql_query(mysql, "DROP VIEW v1"); myquery(rc); rc= mysql_query(mysql, "DROP TABLE t1, t2"); + mysql_free_result(res); myquery(rc); } @@ -12924,6 +12930,152 @@ static void test_bug9520() /* + We can't have more than one cursor open for a prepared statement. + Test re-executions of a PS with cursor; mysql_stmt_reset must close + the cursor attached to the statement, if there is one. +*/ + +static void test_bug9478() +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + char a[6]; + ulong a_len; + int rc, i; + + myheader("test_bug9478"); + + mysql_query(mysql, "drop table if exists t1"); + mysql_query(mysql, "create table t1 (id integer not null primary key, " + " name varchar(20) not null)"); + rc= mysql_query(mysql, "insert into t1 (id, name) values " + " (1, 'aaa'), (2, 'bbb'), (3, 'ccc')"); + myquery(rc); + + stmt= open_cursor("select name from t1 where id=2"); + + bzero(bind, sizeof(bind)); + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer= (char*) a; + bind[0].buffer_length= sizeof(a); + bind[0].length= &a_len; + mysql_stmt_bind_result(stmt, bind); + + for (i= 0; i < 5; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + + /* + The query above is a one-row result set. Therefore, there is no + cursor associated with it, as the server won't bother with opening + a cursor for a one-row result set. The first row was read from the + server in the fetch above. But there is eof packet pending in the + network. mysql_stmt_execute will flush the packet and successfully + execute the statement. + */ + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc == MYSQL_NO_DATA); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + + rc= mysql_stmt_reset(stmt); + check_execute(stmt, rc); + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc && mysql_stmt_errno(stmt)); + if (!opt_silent && i == 0) + printf("Got error (as expected): %s\n", mysql_stmt_error(stmt)); + } + rc= mysql_stmt_close(stmt); + DIE_UNLESS(rc == 0); + + /* Test the case with a server side cursor */ + stmt= open_cursor("select name from t1"); + + mysql_stmt_bind_result(stmt, bind); + + for (i= 0; i < 5; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + /* + Although protocol-wise an attempt to execute a statement which + already has an open cursor associated with it will yield an error, + the client library behavior tested here is consistent with + the non-cursor execution scenario: mysql_stmt_execute will + silently close the cursor if necessary. + */ + { + char buff[9]; + bzero(buff, sizeof(buff)); + /* Fill in the execute packet */ + int4store(buff, stmt->stmt_id); + int4store(buff+5, 1); + rc= ((*mysql->methods->advanced_command)(mysql, COM_EXECUTE, buff, + sizeof(buff), 0,0,1) || + (*mysql->methods->read_query_result)(mysql)); + DIE_UNLESS(rc); + if (!opt_silent && i == 0) + printf("Got error (as expected): %s\n", mysql_error(mysql)); + } + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + while (! (rc= mysql_stmt_fetch(stmt))) + { + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + } + DIE_UNLESS(rc == MYSQL_NO_DATA); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + if (!opt_silent && i == 0) + printf("Fetched row: %s\n", a); + + rc= mysql_stmt_reset(stmt); + check_execute(stmt, rc); + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc && mysql_stmt_errno(stmt)); + if (!opt_silent && i == 0) + printf("Got error (as expected): %s\n", mysql_stmt_error(stmt)); + } + + 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 */ @@ -13153,6 +13305,7 @@ static struct my_tests_st my_tests[]= { { "test_bug8880", test_bug8880 }, { "test_bug9159", test_bug9159 }, { "test_bug9520", test_bug9520 }, + { "test_bug9478", test_bug9478 }, { 0, 0 } }; |