summaryrefslogtreecommitdiff
path: root/sql/sp_rcontext.cc
diff options
context:
space:
mode:
authorkonstantin@mysql.com <>2005-09-22 02:11:21 +0400
committerkonstantin@mysql.com <>2005-09-22 02:11:21 +0400
commit6f8d3c4844e7f7dd760f09a5ab8ad309d1d690d6 (patch)
treec670884265eea87654bb764c095e254eff48128d /sql/sp_rcontext.cc
parente89c5220b54fe74d1afc1361f91e18b164e90e33 (diff)
downloadmariadb-git-6f8d3c4844e7f7dd760f09a5ab8ad309d1d690d6.tar.gz
A fix and a test case for Bug#6513 "Test Suite: Values inserted by using
cursor is interpreted latin1 character and Bug#9819 "Cursors: Mysql Server Crash while fetching from table with 5 million records." A fix for a possible memory leak when fetching into an SP cursor in a long loop. The patch uses a common implementation of cursors in the binary protocol and in stored procedures and implements materialized cursors. For implementation details, see comments in sql_cursor.cc
Diffstat (limited to 'sql/sp_rcontext.cc')
-rw-r--r--sql/sp_rcontext.cc209
1 files changed, 76 insertions, 133 deletions
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 748c09f56c7..fcb7719aeb1 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -25,6 +25,7 @@
#include "mysql.h"
#include "sp_head.h"
+#include "sql_cursor.h"
#include "sp_rcontext.h"
#include "sp_pcontext.h"
@@ -45,31 +46,18 @@ int
sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type)
{
- extern Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type,
- Item *reuse, bool use_callers_arena);
Item *it;
Item *reuse_it;
- Item *old_item_next;
/* sp_eval_func_item will use callers_arena */
- Item *old_free_list= thd->spcont->callers_arena->free_list;
int res;
- LINT_INIT(old_item_next);
- if ((reuse_it= get_item(idx)))
- old_item_next= reuse_it->next;
+ reuse_it= get_item(idx);
it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it)
res= -1;
else
{
res= 0;
- if (reuse_it && it == reuse_it)
- {
- // A reused item slot, where the constructor put it in the free_list,
- // so we have to restore the list.
- thd->spcont->callers_arena->free_list= old_free_list;
- it->next= old_item_next;
- }
set_item(idx, it);
}
@@ -170,7 +158,8 @@ sp_rcontext::pop_cursors(uint count)
*/
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
- :m_lex_keeper(lex_keeper), m_prot(NULL), m_isopen(0), m_current_row(NULL),
+ :m_lex_keeper(lex_keeper),
+ server_side_cursor(NULL),
m_i(i)
{
/*
@@ -182,59 +171,37 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
/*
- pre_open cursor
+ Open an SP cursor
SYNOPSIS
- pre_open()
- THD Thread handler
+ open()
+ THD Thread handler
- NOTES
- We have to open cursor in two steps to make it easy for sp_instr_copen
- to reuse the sp_instr::exec_stmt() code.
- If this function returns 0, post_open should not be called
RETURN
- 0 ERROR
+ 0 in case of success, -1 otherwise
*/
-sp_lex_keeper*
-sp_cursor::pre_open(THD *thd)
+int
+sp_cursor::open(THD *thd)
{
- if (m_isopen)
+ if (server_side_cursor)
{
my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN),
MYF(0));
- return NULL;
+ return -1;
}
- init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
- if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
- return NULL;
-
- /* Save for execution. Will be restored in post_open */
- m_oprot= thd->protocol;
- m_nseof= thd->net.no_send_eof;
-
- /* Change protocol for execution */
- thd->protocol= m_prot;
- thd->net.no_send_eof= TRUE;
- return m_lex_keeper;
-}
-
-
-void
-sp_cursor::post_open(THD *thd, my_bool was_opened)
-{
- thd->net.no_send_eof= m_nseof; // Restore the originals
- thd->protocol= m_oprot;
- if ((m_isopen= was_opened))
- m_current_row= m_prot->data;
+ if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result,
+ &server_side_cursor))
+ return -1;
+ return 0;
}
int
sp_cursor::close(THD *thd)
{
- if (! m_isopen)
+ if (! server_side_cursor)
{
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
return -1;
@@ -247,106 +214,82 @@ sp_cursor::close(THD *thd)
void
sp_cursor::destroy()
{
- if (m_prot)
- {
- delete m_prot;
- m_prot= NULL;
- free_root(&m_mem_root, MYF(0));
- }
- m_isopen= FALSE;
+ delete server_side_cursor;
+ server_side_cursor= 0;
}
+
int
sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
{
- List_iterator_fast<struct sp_pvar> li(*vars);
- sp_pvar_t *pv;
- MYSQL_ROW row;
- uint fldcount;
-
- if (! m_isopen)
+ if (! server_side_cursor)
{
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
return -1;
}
- if (m_current_row == NULL)
+ if (vars->elements != result.get_field_count())
{
- my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
+ my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
+ ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
return -1;
}
- row= m_current_row->data;
- for (fldcount= 0 ; (pv= li++) ; fldcount++)
- {
- Item *it;
- Item *reuse;
- uint rsize;
- Item *old_item_next;
- Item *old_free_list= thd->free_list;
- const char *s;
- LINT_INIT(old_item_next);
-
- if (fldcount >= m_prot->get_field_count())
- {
- my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
- ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
- return -1;
- }
+ result.set_spvar_list(vars);
- if ((reuse= thd->spcont->get_item(pv->offset)))
- old_item_next= reuse->next;
+ /* Attempt to fetch one row */
+ if (server_side_cursor->is_open())
+ server_side_cursor->fetch(1);
- s= row[fldcount];
- if (!s)
- it= new(reuse, &rsize) Item_null();
- else
- {
- /*
- Length of data can be calculated as:
- pointer_to_next_not_null_object - s -1
- where the last -1 is to remove the end \0
- */
- uint len;
- MYSQL_ROW next= row+fldcount+1;
- while (!*next) // Skip nulls
- next++;
- len= (*next -s)-1;
- switch (sp_map_result_type(pv->type)) {
- case INT_RESULT:
- it= new(reuse, &rsize) Item_int(s);
- break;
- case REAL_RESULT:
- it= new(reuse, &rsize) Item_float(s, len);
- break;
- case DECIMAL_RESULT:
- it= new(reuse, &rsize) Item_decimal(s, len, thd->db_charset);
- break;
- case STRING_RESULT:
- /* TODO: Document why we do an extra copy of the string 's' here */
- it= new(reuse, &rsize) Item_string(thd->strmake(s, len), len,
- thd->db_charset);
- break;
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- }
- }
- it->rsize= rsize;
- if (reuse && it == reuse)
- {
- // A reused item slot, where the constructor put it in the free_list,
- // so we have to restore the list.
- thd->free_list= old_free_list;
- it->next= old_item_next;
- }
- thd->spcont->set_item(pv->offset, it);
- }
- if (fldcount < m_prot->get_field_count())
+ /*
+ If the cursor was pointing after the last row, the fetch will
+ close it instead of sending any rows.
+ */
+ if (! server_side_cursor->is_open())
{
- my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
- ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
+ my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
return -1;
}
- m_current_row= m_current_row->next;
+
return 0;
}
+
+
+/***************************************************************************
+ Select_fetch_into_spvars
+****************************************************************************/
+
+int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
+{
+ /*
+ Cache the number of columns in the result set in order to easily
+ return an error if column count does not match value count.
+ */
+ field_count= fields.elements;
+ return select_result_interceptor::prepare(fields, u);
+}
+
+
+bool Select_fetch_into_spvars::send_data(List<Item> &items)
+{
+ List_iterator_fast<struct sp_pvar> pv_iter(*spvar_list);
+ List_iterator_fast<Item> item_iter(items);
+ sp_pvar_t *pv;
+ Item *item;
+
+ /* Must be ensured by the caller */
+ DBUG_ASSERT(spvar_list->elements == items.elements);
+
+ /*
+ Assign the row fetched from a server side cursor to stored
+ procedure variables.
+ */
+ for (; pv= pv_iter++, item= item_iter++; )
+ {
+ Item *reuse= thd->spcont->get_item(pv->offset);
+ /* Evaluate a new item on the arena of the calling instruction */
+ Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE);
+
+ thd->spcont->set_item(pv->offset, it);
+ }
+ return FALSE;
+}