sp_rcontext.cc 6.53 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* Copyright (C) 2002 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

17
#include "mysql_priv.h"
18
#ifdef USE_PRAGMA_IMPLEMENTATION
19 20 21 22 23 24 25
#pragma implementation
#endif

#if defined(WIN32) || defined(__WIN__)
#undef SAFEMALLOC				/* Problems with threads */
#endif

26 27
#include "mysql.h"
#include "sp_head.h"
28
#include "sql_cursor.h"
29 30 31
#include "sp_rcontext.h"
#include "sp_pcontext.h"

32 33 34
sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
  : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
    m_hfound(-1), m_ccount(0)
35
{
36
  in_handler= FALSE;
37 38 39
  m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
  m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
  m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
40
  m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
41 42 43
  m_saved.empty();
}

44

45
int
46 47
sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
			   enum_field_types type)
48
{
49 50
  Item *it;
  Item *reuse_it;
51
  /* sp_eval_func_item will use callers_arena */
52
  int res;
53

54
  reuse_it= get_item(idx);
55
  it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
56
  if (! it)
57
    res= -1;
58 59
  else
  {
60
    res= 0;
61 62
    set_item(idx, it);
  }
63 64

  return res;
65 66
}

67
bool
unknown's avatar
unknown committed
68 69
sp_rcontext::find_handler(uint sql_errno,
                          MYSQL_ERROR::enum_warning_level level)
70
{
71 72
  if (in_handler)
    return 0;			// Already executing a handler
73 74 75 76
  if (m_hfound >= 0)
    return 1;			// Already got one

  const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
77
  int i= m_hcount, found= -1;
78

79
  while (i--)
80 81 82 83 84 85 86
  {
    sp_cond_type_t *cond= m_handler[i].cond;

    switch (cond->type)
    {
    case sp_cond_type_t::number:
      if (sql_errno == cond->mysqlerr)
87
	found= i;		// Always the most specific
88 89
      break;
    case sp_cond_type_t::state:
90
      if (strcmp(sqlstate, cond->sqlstate) == 0 &&
91
	  (found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
92
	found= i;
93 94
      break;
    case sp_cond_type_t::warning:
95 96
      if ((sqlstate[0] == '0' && sqlstate[1] == '1' ||
	   level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
97
	  found < 0)
98
	found= i;
99 100
      break;
    case sp_cond_type_t::notfound:
101
      if (sqlstate[0] == '0' && sqlstate[1] == '2' &&
102
	  found < 0)
103
	found= i;
104 105
      break;
    case sp_cond_type_t::exception:
106 107
      if ((sqlstate[0] != '0' || sqlstate[1] > '2') &&
	  level == MYSQL_ERROR::WARN_LEVEL_ERROR &&
108
	  found < 0)
109
	found= i;
110 111 112
      break;
    }
  }
113 114 115 116
  if (found < 0)
    return FALSE;
  m_hfound= found;
  return TRUE;
117 118 119 120 121 122
}

void
sp_rcontext::save_variables(uint fp)
{
  while (fp < m_count)
123 124 125 126
  {
    m_saved.push_front(m_frame[fp]);
    m_frame[fp++]= NULL;	// Prevent reuse
  }
127 128 129 130 131 132 133 134 135 136
}

void
sp_rcontext::restore_variables(uint fp)
{
  uint i= m_count;

  while (i-- > fp)
    m_frame[i]= m_saved.pop();
}
137 138

void
139
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
140
{
141
  m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
}

void
sp_rcontext::pop_cursors(uint count)
{
  while (count--)
  {
    delete m_cstack[--m_ccount];
  }
}


/*
 *
 *  sp_cursor
 *
 */

160
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
161 162
  :m_lex_keeper(lex_keeper),
   server_side_cursor(NULL),
163
   m_i(i)
164 165 166 167 168 169 170 171 172
{
  /*
    currsor can't be stored in QC, so we should prevent opening QC for
    try to write results which are absent.
  */
  lex_keeper->disable_query_cache();
}


unknown's avatar
unknown committed
173
/*
174
  Open an SP cursor
unknown's avatar
unknown committed
175 176

  SYNOPSIS
177 178
    open()
    THD		         Thread handler
unknown's avatar
unknown committed
179 180 181


  RETURN
182
   0 in case of success, -1 otherwise
unknown's avatar
unknown committed
183 184
*/

185 186
int
sp_cursor::open(THD *thd)
187
{
188
  if (server_side_cursor)
189
  {
unknown's avatar
unknown committed
190 191
    my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN),
               MYF(0));
192
    return -1;
193
  }
194 195 196 197
  if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result,
                        &server_side_cursor))
    return -1;
  return 0;
198 199
}

unknown's avatar
unknown committed
200

201 202 203
int
sp_cursor::close(THD *thd)
{
204
  if (! server_side_cursor)
205
  {
unknown's avatar
unknown committed
206
    my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
207 208 209 210 211 212
    return -1;
  }
  destroy();
  return 0;
}

unknown's avatar
unknown committed
213

214 215 216
void
sp_cursor::destroy()
{
217 218
  delete server_side_cursor;
  server_side_cursor= 0;
219 220
}

221

222 223 224
int
sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
{
225
  if (! server_side_cursor)
226
  {
unknown's avatar
unknown committed
227
    my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
228 229
    return -1;
  }
230
  if (vars->elements != result.get_field_count())
231
  {
232 233
    my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
               ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
234 235 236
    return -1;
  }

237
  result.set_spvar_list(vars);
238

239 240 241
  /* Attempt to fetch one row */
  if (server_side_cursor->is_open())
    server_side_cursor->fetch(1);
242

243 244 245 246 247
  /*
    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())
248
  {
249
    my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
250 251
    return -1;
  }
252

253 254
  return 0;
}
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295


/***************************************************************************
 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;
}