mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 21:42:35 +01:00
218e00ac68
Lots of small fixes to multi-precision-math path Give Note for '123.4e' Added helper functions type 'val_string_from_real() Don't give warnings for end space for string2decimal() Changed storage of values for SP so that we can detect length of argument without strlen() Changed interface for str2dec() so that we must supple the pointer to the last character in the buffer
286 lines
6.7 KiB
C++
286 lines
6.7 KiB
C++
/* 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 */
|
|
|
|
#ifdef __GNUC__
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#if defined(WIN32) || defined(__WIN__)
|
|
#undef SAFEMALLOC /* Problems with threads */
|
|
#endif
|
|
|
|
#include "mysql_priv.h"
|
|
#include "mysql.h"
|
|
#include "sp_head.h"
|
|
#include "sp_rcontext.h"
|
|
#include "sp_pcontext.h"
|
|
|
|
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)
|
|
{
|
|
in_handler= FALSE;
|
|
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
|
|
m_outs= (int *)sql_alloc(fsize * sizeof(int));
|
|
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
|
|
m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
|
|
m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
|
|
m_saved.empty();
|
|
}
|
|
|
|
int
|
|
sp_rcontext::set_item_eval(uint idx, Item *i, enum_field_types type)
|
|
{
|
|
extern Item *sp_eval_func_item(THD *thd, Item *it, enum_field_types type);
|
|
Item *it= sp_eval_func_item(current_thd, i, type);
|
|
|
|
if (! it)
|
|
return -1;
|
|
else
|
|
{
|
|
set_item(idx, it);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool
|
|
sp_rcontext::find_handler(uint sql_errno,
|
|
MYSQL_ERROR::enum_warning_level level)
|
|
{
|
|
if (in_handler)
|
|
return 0; // Already executing a handler
|
|
if (m_hfound >= 0)
|
|
return 1; // Already got one
|
|
|
|
const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
|
|
int i= m_hcount, found= -1;
|
|
|
|
while (i--)
|
|
{
|
|
sp_cond_type_t *cond= m_handler[i].cond;
|
|
|
|
switch (cond->type)
|
|
{
|
|
case sp_cond_type_t::number:
|
|
if (sql_errno == cond->mysqlerr)
|
|
found= i; // Always the most specific
|
|
break;
|
|
case sp_cond_type_t::state:
|
|
if (strcmp(sqlstate, cond->sqlstate) == 0 &&
|
|
(found < 0 || m_handler[found].cond->type > sp_cond_type_t::number))
|
|
found= i;
|
|
break;
|
|
case sp_cond_type_t::warning:
|
|
if ((sqlstate[0] == '0' && sqlstate[1] == '1' ||
|
|
level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
|
|
(found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
|
|
found= i;
|
|
break;
|
|
case sp_cond_type_t::notfound:
|
|
if (sqlstate[0] == '0' && sqlstate[1] == '2' &&
|
|
(found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
|
|
found= i;
|
|
break;
|
|
case sp_cond_type_t::exception:
|
|
if ((sqlstate[0] != '0' || sqlstate[1] > '2' ||
|
|
level == MYSQL_ERROR::WARN_LEVEL_ERROR) &&
|
|
(found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
|
|
found= i;
|
|
break;
|
|
}
|
|
}
|
|
if (found < 0)
|
|
return FALSE;
|
|
m_hfound= found;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
sp_rcontext::save_variables(uint fp)
|
|
{
|
|
while (fp < m_count)
|
|
m_saved.push_front(m_frame[fp++]);
|
|
}
|
|
|
|
void
|
|
sp_rcontext::restore_variables(uint fp)
|
|
{
|
|
uint i= m_count;
|
|
|
|
while (i-- > fp)
|
|
m_frame[i]= m_saved.pop();
|
|
}
|
|
|
|
void
|
|
sp_rcontext::push_cursor(LEX *lex)
|
|
{
|
|
m_cstack[m_ccount++]= new sp_cursor(lex);
|
|
}
|
|
|
|
void
|
|
sp_rcontext::pop_cursors(uint count)
|
|
{
|
|
while (count--)
|
|
{
|
|
delete m_cstack[--m_ccount];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* sp_cursor
|
|
*
|
|
*/
|
|
|
|
// We have split this in two to make it easy for sp_instr_copen
|
|
// to reuse the sp_instr::exec_stmt() code.
|
|
LEX *
|
|
sp_cursor::pre_open(THD *thd)
|
|
{
|
|
if (m_isopen)
|
|
{
|
|
my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN),
|
|
MYF(0));
|
|
return NULL;
|
|
}
|
|
|
|
bzero((char *)&m_mem_root, sizeof(m_mem_root));
|
|
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;
|
|
|
|
m_oprot= thd->protocol; // Save the original protocol
|
|
thd->protocol= m_prot;
|
|
|
|
m_nseof= thd->net.no_send_eof;
|
|
thd->net.no_send_eof= TRUE;
|
|
return m_lex;
|
|
}
|
|
|
|
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 (was_opened)
|
|
{
|
|
m_isopen= was_opened;
|
|
m_current_row= m_prot->data;
|
|
}
|
|
}
|
|
|
|
int
|
|
sp_cursor::close(THD *thd)
|
|
{
|
|
if (! m_isopen)
|
|
{
|
|
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
|
|
return -1;
|
|
}
|
|
destroy();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sp_cursor::destroy()
|
|
{
|
|
if (m_prot)
|
|
{
|
|
delete m_prot;
|
|
m_prot= NULL;
|
|
free_root(&m_mem_root, MYF(0));
|
|
bzero((char *)&m_mem_root, sizeof(m_mem_root));
|
|
}
|
|
m_isopen= FALSE;
|
|
}
|
|
|
|
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)
|
|
{
|
|
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
|
|
return -1;
|
|
}
|
|
if (m_current_row == NULL)
|
|
{
|
|
my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
row= m_current_row->data;
|
|
for (fldcount= 0 ; (pv= li++) ; fldcount++)
|
|
{
|
|
Item *it;
|
|
const char *s;
|
|
|
|
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;
|
|
}
|
|
s= row[fldcount];
|
|
if (!s)
|
|
it= new 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 Item_int(s);
|
|
break;
|
|
case REAL_RESULT:
|
|
it= new Item_float(s, len);
|
|
break;
|
|
case DECIMAL_RESULT:
|
|
it= new 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 Item_string(thd->strmake(s, len), len, thd->db_charset);
|
|
break;
|
|
case ROW_RESULT:
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
}
|
|
}
|
|
thd->spcont->set_item(pv->offset, it);
|
|
}
|
|
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;
|
|
}
|
|
m_current_row= m_current_row->next;
|
|
return 0;
|
|
}
|