mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
Merge with 4.1.3-beta
This commit is contained in:
@@ -92,16 +92,22 @@ public:
|
||||
bool get_longdata_error;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
|
||||
uchar *read_pos);
|
||||
uchar *read_pos, String *expanded_query);
|
||||
#else
|
||||
bool (*set_params_data)(Prepared_statement *st);
|
||||
bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
|
||||
#endif
|
||||
bool (*set_params_from_vars)(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *expanded_query);
|
||||
public:
|
||||
Prepared_statement(THD *thd_arg);
|
||||
virtual ~Prepared_statement();
|
||||
void setup_set_params();
|
||||
virtual Statement::Type type() const;
|
||||
};
|
||||
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
||||
String *expanded_query, bool set_context);
|
||||
|
||||
/******************************************************************************
|
||||
Implementation
|
||||
@@ -130,7 +136,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where,
|
||||
|
||||
if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
|
||||
char llbuf[22];
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where);
|
||||
if (se == SEND_ERROR)
|
||||
send_error(thd);
|
||||
return 0;
|
||||
@@ -211,7 +218,13 @@ static ulong get_param_length(uchar **packet, ulong len)
|
||||
if (len < 5)
|
||||
return 0;
|
||||
(*packet)+=9; // Must be 254 when here
|
||||
/* TODO: why uint4korr here? (should be uint8korr) */
|
||||
/*
|
||||
In our client-server protocol all numbers bigger than 2^24
|
||||
stored as 8 bytes with uint8korr. Here we always know that
|
||||
parameter length is less than 2^4 so don't look at the second
|
||||
4 bytes. But still we need to obey the protocol hence 9 in the
|
||||
assignment above.
|
||||
*/
|
||||
return (ulong) uint4korr(pos+1);
|
||||
}
|
||||
#else
|
||||
@@ -327,20 +340,19 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
uchar *to= *pos;
|
||||
TIME tm;
|
||||
|
||||
/* TODO: why length is compared with 8 here? */
|
||||
tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;
|
||||
|
||||
tm.neg= (bool) to[0];
|
||||
day= (uint) sint4korr(to+1);
|
||||
/*
|
||||
Note, that though ranges of hour, minute and second are not checked
|
||||
here we rely on them being < 256: otherwise
|
||||
we'll get buffer overflow in make_{date,time} functions,
|
||||
which are called when time value is converted to string.
|
||||
*/
|
||||
day= (uint) sint4korr(to+1);
|
||||
tm.hour= (uint) to[5] + day * 24;
|
||||
tm.minute= (uint) to[6];
|
||||
tm.second= (uint) to[7];
|
||||
tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
|
||||
if (tm.hour > 838)
|
||||
{
|
||||
/* TODO: add warning 'Data truncated' here */
|
||||
@@ -349,9 +361,8 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
tm.second= 59;
|
||||
}
|
||||
tm.day= tm.year= tm.month= 0;
|
||||
tm.neg= (bool)to[0];
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_TIME,
|
||||
param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
|
||||
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
*pos+= length;
|
||||
@@ -360,14 +371,16 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
uint length;
|
||||
|
||||
|
||||
if ((length= get_param_length(pos, len)) >= 4)
|
||||
{
|
||||
uchar *to= *pos;
|
||||
TIME tm;
|
||||
|
||||
tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
|
||||
|
||||
|
||||
tm.neg= 0;
|
||||
tm.year= (uint) sint2korr(to);
|
||||
tm.month= (uint) to[2];
|
||||
tm.day= (uint) to[3];
|
||||
/*
|
||||
Note, that though ranges of hour, minute and second are not checked
|
||||
here we rely on them being < 256: otherwise
|
||||
@@ -381,13 +394,10 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
}
|
||||
else
|
||||
tm.hour= tm.minute= tm.second= 0;
|
||||
|
||||
tm.year= (uint) sint2korr(to);
|
||||
tm.month= (uint) to[2];
|
||||
tm.day= (uint) to[3];
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATETIME,
|
||||
tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
|
||||
|
||||
param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
|
||||
MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
*pos+= length;
|
||||
@@ -414,7 +424,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
tm.second_part= 0;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATE,
|
||||
param->set_time(&tm, MYSQL_TIMESTAMP_DATE,
|
||||
MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
*pos+= length;
|
||||
@@ -423,58 +433,25 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
#else/*!EMBEDDED_LIBRARY*/
|
||||
void set_param_time(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
TIME tm;
|
||||
MYSQL_TIME *to= (MYSQL_TIME*)*pos;
|
||||
|
||||
tm.second_part= to->second_part;
|
||||
|
||||
tm.day= to->day;
|
||||
tm.hour= to->hour;
|
||||
tm.minute= to->minute;
|
||||
tm.second= to->second;
|
||||
|
||||
tm.year= tm.month= 0;
|
||||
tm.neg= to->neg;
|
||||
param->set_time(&tm, TIMESTAMP_TIME,
|
||||
param->set_time(to, MYSQL_TIMESTAMP_TIME,
|
||||
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
|
||||
}
|
||||
|
||||
void set_param_datetime(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
TIME tm;
|
||||
MYSQL_TIME *to= (MYSQL_TIME*)*pos;
|
||||
|
||||
tm.second_part= to->second_part;
|
||||
|
||||
tm.day= to->day;
|
||||
tm.hour= to->hour;
|
||||
tm.minute= to->minute;
|
||||
tm.second= to->second;
|
||||
tm.year= to->year;
|
||||
tm.month= to->month;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATETIME,
|
||||
param->set_time(to, MYSQL_TIMESTAMP_DATETIME,
|
||||
MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
|
||||
void set_param_date(Item_param *param, uchar **pos, ulong len)
|
||||
{
|
||||
TIME tm;
|
||||
MYSQL_TIME *to= (MYSQL_TIME*)*pos;
|
||||
|
||||
tm.second_part= to->second_part;
|
||||
|
||||
tm.day= to->day;
|
||||
tm.year= to->year;
|
||||
tm.month= to->month;
|
||||
tm.neg= 0;
|
||||
tm.hour= tm.minute= tm.second= 0;
|
||||
tm.second_part= 0;
|
||||
tm.neg= 0;
|
||||
|
||||
param->set_time(&tm, TIMESTAMP_DATE,
|
||||
param->set_time(to, MYSQL_TIMESTAMP_DATE,
|
||||
MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
|
||||
}
|
||||
#endif /*!EMBEDDED_LIBRARY*/
|
||||
@@ -580,6 +557,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
|
||||
param->item_result_type= STRING_RESULT;
|
||||
}
|
||||
}
|
||||
param->param_type= (enum enum_field_types) param_type;
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
@@ -589,19 +567,20 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
|
||||
*/
|
||||
|
||||
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
|
||||
uchar *read_pos, uchar *data_end)
|
||||
uchar *read_pos, uchar *data_end,
|
||||
String *query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
uint32 length= 0;
|
||||
|
||||
String str, query;
|
||||
String str;
|
||||
const String *res;
|
||||
|
||||
DBUG_ENTER("insert_params_withlog");
|
||||
|
||||
if (query.copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
@@ -622,20 +601,18 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
|
||||
if (query.replace(param->pos_in_query+length, 1, *res))
|
||||
if (query->replace(param->pos_in_query+length, 1, *res))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
length+= res->length()-1;
|
||||
}
|
||||
if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
|
||||
uchar *read_pos, uchar *data_end)
|
||||
uchar *read_pos, uchar *data_end,
|
||||
String *expanded_query)
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
@@ -700,7 +677,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
|
||||
|
||||
#else
|
||||
|
||||
static bool emb_insert_params(Prepared_statement *stmt)
|
||||
static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **it= stmt->param_array;
|
||||
@@ -733,20 +710,20 @@ static bool emb_insert_params(Prepared_statement *stmt)
|
||||
}
|
||||
|
||||
|
||||
static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
||||
static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **it= stmt->param_array;
|
||||
Item_param **end= it + stmt->param_count;
|
||||
MYSQL_BIND *client_param= thd->client_params;
|
||||
|
||||
String str, query;
|
||||
String str;
|
||||
const String *res;
|
||||
uint32 length= 0;
|
||||
|
||||
DBUG_ENTER("emb_insert_params_withlog");
|
||||
|
||||
if (query.copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (; it < end; ++it, ++client_param)
|
||||
@@ -765,24 +742,121 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
||||
*client_param->length :
|
||||
client_param->buffer_length);
|
||||
}
|
||||
res= param->query_val_str(&str);
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
}
|
||||
if (query.replace(param->pos_in_query+length, 1, *res))
|
||||
res= param->query_val_str(&str);
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
|
||||
if (query->replace(param->pos_in_query+length, 1, *res))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
length+= res->length()-1;
|
||||
}
|
||||
|
||||
if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
#endif /*!EMBEDDED_LIBRARY*/
|
||||
|
||||
|
||||
/*
|
||||
Set prepared statement parameters from user variables.
|
||||
SYNOPSIS
|
||||
insert_params_from_vars()
|
||||
stmt Statement
|
||||
varnames List of variables. Caller must ensure that number of variables
|
||||
in the list is equal to number of statement parameters
|
||||
query Ignored
|
||||
*/
|
||||
|
||||
static bool insert_params_from_vars(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *query __attribute__((unused)))
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
user_var_entry *entry;
|
||||
LEX_STRING *varname;
|
||||
List_iterator<LEX_STRING> var_it(varnames);
|
||||
DBUG_ENTER("insert_params_from_vars");
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
varname= var_it++;
|
||||
entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
|
||||
(byte*) varname->str,
|
||||
varname->length);
|
||||
if (param->set_from_user_var(stmt->thd, entry) ||
|
||||
param->convert_str_value(stmt->thd))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Do the same as insert_params_from_vars but also construct query text for
|
||||
binary log.
|
||||
SYNOPSIS
|
||||
insert_params_from_vars()
|
||||
stmt Statement
|
||||
varnames List of variables. Caller must ensure that number of variables
|
||||
in the list is equal to number of statement parameters
|
||||
query The query with parameter markers replaced with their values
|
||||
*/
|
||||
|
||||
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *query)
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
user_var_entry *entry;
|
||||
LEX_STRING *varname;
|
||||
DBUG_ENTER("insert_params_from_vars");
|
||||
|
||||
List_iterator<LEX_STRING> var_it(varnames);
|
||||
String str;
|
||||
uint32 length= 0;
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
varname= var_it++;
|
||||
if (get_var_with_binlog(stmt->thd, *varname, &entry))
|
||||
DBUG_RETURN(1);
|
||||
DBUG_ASSERT(entry);
|
||||
|
||||
if (param->set_from_user_var(stmt->thd, entry))
|
||||
DBUG_RETURN(1);
|
||||
/* Insert @'escaped-varname' instead of parameter in the query */
|
||||
char *buf, *ptr;
|
||||
str.length(0);
|
||||
if (str.reserve(entry->name.length*2+3))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
buf= str.c_ptr_quick();
|
||||
ptr= buf;
|
||||
*ptr++= '@';
|
||||
*ptr++= '\'';
|
||||
ptr+=
|
||||
escape_string_for_mysql(&my_charset_utf8_general_ci,
|
||||
ptr, entry->name.str, entry->name.length);
|
||||
*ptr++= '\'';
|
||||
str.length(ptr - buf);
|
||||
|
||||
if (param->convert_str_value(stmt->thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
|
||||
if (query->replace(param->pos_in_query+length, 1, str))
|
||||
DBUG_RETURN(1);
|
||||
length+= str.length()-1;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Validate INSERT statement:
|
||||
|
||||
@@ -979,7 +1053,7 @@ static int mysql_test_delete(Prepared_statement *stmt,
|
||||
*/
|
||||
|
||||
static int mysql_test_select(Prepared_statement *stmt,
|
||||
TABLE_LIST *tables)
|
||||
TABLE_LIST *tables, bool text_protocol)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
LEX *lex= stmt->lex;
|
||||
@@ -1009,30 +1083,32 @@ static int mysql_test_select(Prepared_statement *stmt,
|
||||
goto err;
|
||||
}
|
||||
|
||||
thd->used_tables= 0; // Updated by setup_fields
|
||||
|
||||
// JOIN::prepare calls
|
||||
if (unit->prepare(thd, 0, 0))
|
||||
{
|
||||
send_error(thd);
|
||||
goto err_prep;
|
||||
}
|
||||
if (lex->describe)
|
||||
{
|
||||
if (send_prep_stmt(stmt, 0))
|
||||
goto err;
|
||||
if (!text_protocol && send_prep_stmt(stmt, 0))
|
||||
goto err_prep;
|
||||
unit->cleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
thd->used_tables= 0; // Updated by setup_fields
|
||||
|
||||
// JOIN::prepare calls
|
||||
if (unit->prepare(thd, 0, 0))
|
||||
if (!text_protocol)
|
||||
{
|
||||
send_error(thd);
|
||||
goto err_prep;
|
||||
}
|
||||
|
||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
||||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
|| net_flush(&thd->net)
|
||||
|| net_flush(&thd->net)
|
||||
#endif
|
||||
)
|
||||
goto err_prep;
|
||||
|
||||
)
|
||||
goto err_prep;
|
||||
}
|
||||
unit->cleanup();
|
||||
}
|
||||
thd->free_temporary_memory_pool_for_ps_preparing();
|
||||
@@ -1199,6 +1275,7 @@ static int mysql_test_create_table(Prepared_statement *stmt,
|
||||
DBUG_ENTER("mysql_test_create_table");
|
||||
THD *thd= stmt->thd;
|
||||
LEX *lex= stmt->lex;
|
||||
SELECT_LEX *select_lex= &lex->select_lex;
|
||||
int res= 0;
|
||||
|
||||
/* Skip first table, which is the table we are creating */
|
||||
@@ -1207,8 +1284,12 @@ static int mysql_test_create_table(Prepared_statement *stmt,
|
||||
&create_table_local);
|
||||
|
||||
if (!(res= create_table_precheck(thd, tables, create_table)) &&
|
||||
lex->select_lex.item_list.elements)
|
||||
select_lex->item_list.elements)
|
||||
{
|
||||
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
|
||||
res= select_like_statement_test(stmt, tables);
|
||||
select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
|
||||
}
|
||||
|
||||
/* put tables back for PS rexecuting */
|
||||
tables= lex->link_first_table_back(tables, create_table,
|
||||
@@ -1292,7 +1373,11 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
|
||||
(TABLE_LIST *)lex->select_lex.table_list.first;
|
||||
/* Skip first table, which is the table we are inserting in */
|
||||
lex->select_lex.table_list.first= (byte*) first_local_table->next;
|
||||
lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE;
|
||||
/*
|
||||
insert/replace from SELECT give its SELECT_LEX for SELECT,
|
||||
and item_list belong to SELECT
|
||||
*/
|
||||
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
|
||||
res= select_like_statement_test(stmt, tables);
|
||||
/* revert changes*/
|
||||
lex->select_lex.table_list.first= (byte*) first_local_table;
|
||||
@@ -1310,7 +1395,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
|
||||
0 success
|
||||
1 error, sent to client
|
||||
*/
|
||||
static int send_prepare_results(Prepared_statement *stmt)
|
||||
static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
LEX *lex= stmt->lex;
|
||||
@@ -1347,7 +1432,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
||||
break;
|
||||
|
||||
case SQLCOM_SELECT:
|
||||
if ((res= mysql_test_select(stmt, tables)))
|
||||
if ((res= mysql_test_select(stmt, tables, text_protocol)))
|
||||
goto error;
|
||||
/* Statement and field info has already been sent */
|
||||
DBUG_RETURN(0);
|
||||
@@ -1405,7 +1490,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
||||
goto error;
|
||||
}
|
||||
if (res == 0)
|
||||
DBUG_RETURN(send_prep_stmt(stmt, 0));
|
||||
DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0));
|
||||
error:
|
||||
if (res < 0)
|
||||
send_error(thd,thd->killed_errno());
|
||||
@@ -1446,20 +1531,36 @@ static bool init_param_array(Prepared_statement *stmt)
|
||||
|
||||
|
||||
/*
|
||||
Parse the query and send the total number of parameters
|
||||
and resultset metadata information back to client (if any),
|
||||
without executing the query i.e. without any log/disk
|
||||
writes. This will allow the queries to be re-executed
|
||||
without re-parsing during execute.
|
||||
Given a query string with parameter markers, create a Prepared Statement
|
||||
from it and send PS info back to the client.
|
||||
|
||||
SYNOPSIS
|
||||
mysql_stmt_prepare()
|
||||
packet query to be prepared
|
||||
packet_length query string length, including ignored trailing NULL or
|
||||
quote char.
|
||||
name NULL or statement name. For unnamed statements binary PS
|
||||
protocol is used, for named statements text protocol is
|
||||
used.
|
||||
RETURN
|
||||
0 OK, statement prepared successfully
|
||||
other Error
|
||||
|
||||
NOTES
|
||||
This function parses the query and sends the total number of parameters
|
||||
and resultset metadata information back to client (if any), without
|
||||
executing the query i.e. without any log/disk writes. This allows the
|
||||
queries to be re-executed without re-parsing during execute.
|
||||
|
||||
If parameter markers are found in the query, then store
|
||||
the information using Item_param along with maintaining a
|
||||
list in lex->param_array, so that a fast and direct
|
||||
retrieval can be made without going through all field
|
||||
items.
|
||||
If parameter markers are found in the query, then store the information
|
||||
using Item_param along with maintaining a list in lex->param_array, so
|
||||
that a fast and direct retrieval can be made without going through all
|
||||
field items.
|
||||
|
||||
*/
|
||||
|
||||
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
|
||||
LEX_STRING *name)
|
||||
{
|
||||
LEX *lex;
|
||||
Prepared_statement *stmt= new Prepared_statement(thd);
|
||||
@@ -1471,14 +1572,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
if (stmt == 0)
|
||||
{
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
stmt->name.length= name->length;
|
||||
if (!(stmt->name.str= memdup_root(&stmt->mem_root, (char*)name->str,
|
||||
name->length)))
|
||||
{
|
||||
delete stmt;
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (thd->stmt_map.insert(stmt))
|
||||
{
|
||||
delete stmt;
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
@@ -1495,7 +1608,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
/* Statement map deletes statement on erase */
|
||||
thd->stmt_map.erase(stmt);
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
mysql_log.write(thd, COM_PREPARE, "%s", packet);
|
||||
@@ -1507,7 +1620,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
|
||||
error= yyparse((void *)thd) || thd->is_fatal_error ||
|
||||
init_param_array(stmt) ||
|
||||
send_prepare_results(stmt);
|
||||
send_prepare_results(stmt, test(name));
|
||||
|
||||
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
@@ -1530,9 +1643,14 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
{
|
||||
/* Statement map deletes statement on erase */
|
||||
thd->stmt_map.erase(stmt);
|
||||
stmt= NULL;
|
||||
/* error is sent inside yyparse/send_prepare_results */
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
else
|
||||
stmt->setup_set_params();
|
||||
|
||||
DBUG_RETURN(!stmt);
|
||||
|
||||
}
|
||||
|
||||
/* Reinit statement before execution */
|
||||
@@ -1545,6 +1663,9 @@ void reset_stmt_for_execute(THD *thd, LEX *lex)
|
||||
{
|
||||
if (!sl->first_execution)
|
||||
{
|
||||
/* remove option which was put by mysql_explain_union() */
|
||||
sl->options&= ~SELECT_DESCRIBE;
|
||||
|
||||
/*
|
||||
Copy WHERE clause pointers to avoid damaging they by optimisation
|
||||
*/
|
||||
@@ -1609,13 +1730,21 @@ static void reset_stmt_params(Prepared_statement *stmt)
|
||||
Executes previously prepared query.
|
||||
If there is any parameters, then replace markers with the data supplied
|
||||
from client, and then execute the query.
|
||||
SYNOPSYS
|
||||
SYNOPSIS
|
||||
mysql_stmt_execute()
|
||||
thd Current thread
|
||||
packet Query string
|
||||
packet_length Query string length, including terminator character.
|
||||
*/
|
||||
|
||||
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
{
|
||||
ulong stmt_id= uint4korr(packet);
|
||||
/*
|
||||
Query text for binary log, or empty string if the query is not put into
|
||||
binary log.
|
||||
*/
|
||||
String expanded_query;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
uchar *packet_end= (uchar *) packet + packet_length - 1;
|
||||
#endif
|
||||
@@ -1623,7 +1752,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
DBUG_ENTER("mysql_stmt_execute");
|
||||
|
||||
packet+= 9; /* stmt_id + 5 bytes of flags */
|
||||
|
||||
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute",
|
||||
SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
@@ -1637,15 +1766,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
reset_stmt_for_execute(thd, stmt->lex);
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (stmt->param_count)
|
||||
{
|
||||
uchar *null_array= (uchar *) packet;
|
||||
if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
|
||||
stmt->set_params(stmt, null_array, (uchar *) packet, packet_end))
|
||||
stmt->set_params(stmt, null_array, (uchar *) packet, packet_end,
|
||||
&expanded_query))
|
||||
goto set_params_data_err;
|
||||
}
|
||||
#else
|
||||
@@ -1654,43 +1781,18 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
we set params, and also we don't need to parse packet.
|
||||
So we do it in one function.
|
||||
*/
|
||||
if (stmt->param_count && stmt->set_params_data(stmt))
|
||||
if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
|
||||
goto set_params_data_err;
|
||||
#endif
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
/*
|
||||
TODO:
|
||||
Also, have checks on basic executions such as mysql_insert(),
|
||||
mysql_delete(), mysql_update() and mysql_select() to not to
|
||||
have re-check on setup_* and other things ..
|
||||
*/
|
||||
thd->current_arena= stmt;
|
||||
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
|
||||
mysql_execute_command(thd);
|
||||
thd->lex->unit.cleanup();
|
||||
execute_stmt(thd, stmt, &expanded_query, true);
|
||||
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
||||
thd->current_arena= 0;
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||
|
||||
cleanup_items(stmt->free_list);
|
||||
reset_stmt_params(stmt);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
/*
|
||||
Free Items that were created during this execution of the PS by query
|
||||
optimizer.
|
||||
*/
|
||||
free_items(thd->free_list);
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
set_params_data_err:
|
||||
reset_stmt_params(stmt);
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
@@ -1698,11 +1800,107 @@ set_params_data_err:
|
||||
|
||||
|
||||
/*
|
||||
Reset a prepared statement in case there was a recoverable error.
|
||||
Execute prepared statement using parameter values from
|
||||
lex->prepared_stmt_params and send result to the client using text protocol.
|
||||
*/
|
||||
|
||||
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
|
||||
{
|
||||
Prepared_statement *stmt;
|
||||
/*
|
||||
Query text for binary log, or empty string if the query is not put into
|
||||
binary log.
|
||||
*/
|
||||
String expanded_query;
|
||||
DBUG_ENTER("mysql_sql_stmt_execute");
|
||||
|
||||
if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length,
|
||||
stmt_name->str, "EXECUTE");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
|
||||
{
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
thd->free_list= NULL;
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
if (stmt->set_params_from_vars(stmt,
|
||||
thd->stmt_backup.lex->prepared_stmt_params,
|
||||
&expanded_query))
|
||||
{
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
|
||||
send_error(thd);
|
||||
}
|
||||
execute_stmt(thd, stmt, &expanded_query, false);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Execute prepared statement.
|
||||
SYNOPSIS
|
||||
execute_stmt()
|
||||
thd Current thread
|
||||
stmt Statement to execute
|
||||
expanded_query If binary log is enabled, query string with parameter
|
||||
placeholders replaced with actual values. Otherwise empty
|
||||
string.
|
||||
NOTES
|
||||
Caller must set parameter values and thd::protocol.
|
||||
thd->free_list is assumed to be garbage.
|
||||
*/
|
||||
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
||||
String *expanded_query, bool set_context)
|
||||
{
|
||||
DBUG_ENTER("execute_stmt");
|
||||
if (set_context)
|
||||
{
|
||||
thd->free_list= NULL;
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
}
|
||||
reset_stmt_for_execute(stmt);
|
||||
|
||||
if (expanded_query->length() &&
|
||||
alloc_query(thd, (char *)expanded_query->ptr(),
|
||||
expanded_query->length()+1))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
mysql_execute_command(thd);
|
||||
thd->lex->unit.cleanup();
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||
|
||||
/* Free Items that were created during this execution of the PS. */
|
||||
free_items(thd->free_list);
|
||||
cleanup_items(stmt->free_list);
|
||||
reset_stmt_params(stmt);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reset a prepared statement in case there was a recoverable error.
|
||||
SYNOPSIS
|
||||
mysql_stmt_reset()
|
||||
thd Thread handle
|
||||
packet Packet with stmt id
|
||||
thd Thread handle
|
||||
packet Packet with stmt id
|
||||
|
||||
DESCRIPTION
|
||||
This function resets statement to the state it was right after prepare.
|
||||
@@ -1842,8 +2040,14 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
||||
get_longdata_error(0)
|
||||
{
|
||||
*last_error= '\0';
|
||||
if (mysql_bin_log.is_open())
|
||||
}
|
||||
|
||||
void Prepared_statement::setup_set_params()
|
||||
{
|
||||
/* Setup binary logging */
|
||||
if (mysql_bin_log.is_open() && is_update_query(lex->sql_command))
|
||||
{
|
||||
set_params_from_vars= insert_params_from_vars_with_log;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
set_params= insert_params_withlog;
|
||||
#else
|
||||
@@ -1851,14 +2055,16 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
set_params_from_vars= insert_params_from_vars;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
set_params= insert_params;
|
||||
#else
|
||||
set_params_data= emb_insert_params;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Prepared_statement::~Prepared_statement()
|
||||
{
|
||||
free_items(free_list);
|
||||
|
||||
Reference in New Issue
Block a user