summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/Makefile.am3
-rw-r--r--sql/events.cc2
-rw-r--r--sql/handler.cc4
-rw-r--r--sql/item.cc168
-rw-r--r--sql/item.h30
-rw-r--r--sql/mysql_priv.h17
-rw-r--r--sql/procedure.h2
-rw-r--r--sql/protocol.cc280
-rw-r--r--sql/protocol.h36
-rw-r--r--sql/repl_failsafe.cc4
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/sp_head.cc19
-rw-r--r--sql/sql_acl.cc2
-rw-r--r--sql/sql_base.cc1
-rw-r--r--sql/sql_cache.cc10
-rw-r--r--sql/sql_class.cc43
-rw-r--r--sql/sql_class.h52
-rw-r--r--sql/sql_connect.cc2
-rw-r--r--sql/sql_cursor.cc30
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_handler.cc17
-rw-r--r--sql/sql_help.cc6
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_lex.h7
-rw-r--r--sql/sql_list.cc2
-rw-r--r--sql/sql_list.h5
-rw-r--r--sql/sql_parse.cc11
-rw-r--r--sql/sql_prepare.cc808
-rw-r--r--sql/sql_prepare.h367
-rw-r--r--sql/sql_profile.cc2
-rw-r--r--sql/sql_repl.cc6
-rw-r--r--sql/sql_select.cc20
-rw-r--r--sql/sql_show.cc20
-rw-r--r--sql/sql_string.h5
-rw-r--r--sql/sql_table.cc4
-rw-r--r--sql/sql_update.cc1
-rw-r--r--sql/sql_yacc.yy2
37 files changed, 1733 insertions, 261 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index d4c79851904..cf1b8ecbcc3 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -110,7 +110,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
sql_plugin.h authors.h event_parse_data.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
- contributors.h sql_servers.h sql_signal.h records.h
+ contributors.h sql_servers.h sql_signal.h records.h \
+ sql_prepare.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
diff --git a/sql/events.cc b/sql/events.cc
index 34da0e185b7..af36ede39ec 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -757,7 +757,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
field_list.push_back(
new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/handler.cc b/sql/handler.cc
index 495670e238e..bdf2f862275 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1644,7 +1644,7 @@ bool mysql_xa_recover(THD *thd)
field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS));
field_list.push_back(new Item_empty_string("data",XIDDATASIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -4434,7 +4434,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
field_list.push_back(new Item_empty_string("Status",10));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
return TRUE;
diff --git a/sql/item.cc b/sql/item.cc
index e56364beee3..bc05d95abc1 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -2583,7 +2583,8 @@ Item_param::Item_param(uint pos_in_query_arg) :
param_type(MYSQL_TYPE_VARCHAR),
pos_in_query(pos_in_query_arg),
set_param_func(default_set_param_func),
- limit_clause_param(FALSE)
+ limit_clause_param(FALSE),
+ m_out_param_info(NULL)
{
name= (char*) "?";
/*
@@ -2665,6 +2666,17 @@ void Item_param::set_decimal(const char *str, ulong length)
DBUG_VOID_RETURN;
}
+void Item_param::set_decimal(const my_decimal *dv)
+{
+ state= DECIMAL_VALUE;
+
+ my_decimal2decimal(dv, &decimal_value);
+
+ decimals= (uint8) decimal_value.frac;
+ unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, unsigned_flag);
+}
/**
Set parameter value from MYSQL_TIME value.
@@ -3287,6 +3299,158 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
str_value_ptr.swap(src->str_value_ptr);
}
+
+/**
+ This operation is intended to store some item value in Item_param to be
+ used later.
+
+ @param thd thread context
+ @param ctx stored procedure runtime context
+ @param it a pointer to an item in the tree
+
+ @return Error status
+ @retval TRUE on error
+ @retval FALSE on success
+*/
+
+bool
+Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+ Item *value= *it;
+
+ if (value->is_null())
+ {
+ set_null();
+ return FALSE;
+ }
+
+ null_value= FALSE;
+
+ switch (value->result_type()) {
+ case STRING_RESULT:
+ {
+ char str_buffer[STRING_BUFFER_USUAL_SIZE];
+ String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin);
+ String *sv= value->val_str(&sv_buffer);
+
+ if (!sv)
+ return TRUE;
+
+ set_str(sv->c_ptr_safe(), sv->length());
+ str_value_ptr.set(str_value.ptr(),
+ str_value.length(),
+ str_value.charset());
+ collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+ decimals= 0;
+ param_type= MYSQL_TYPE_STRING;
+
+ break;
+ }
+
+ case REAL_RESULT:
+ set_double(value->val_real());
+ param_type= MYSQL_TYPE_DOUBLE;
+ break;
+
+ case INT_RESULT:
+ set_int(value->val_int(), value->max_length);
+ param_type= MYSQL_TYPE_LONG;
+ break;
+
+ case DECIMAL_RESULT:
+ {
+ my_decimal dv_buf;
+ my_decimal *dv= value->val_decimal(&dv_buf);
+
+ if (!dv)
+ return TRUE;
+
+ set_decimal(dv);
+ param_type= MYSQL_TYPE_NEWDECIMAL;
+
+ break;
+ }
+
+ default:
+ /* That can not happen. */
+
+ DBUG_ASSERT(TRUE); // Abort in debug mode.
+
+ set_null(); // Set to NULL in release mode.
+ return FALSE;
+ }
+
+ item_result_type= value->result_type();
+ item_type= value->type();
+ return FALSE;
+}
+
+
+/**
+ Setter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be set in sp_head::execute() after
+ Item_param::set_value() is called.
+*/
+
+void
+Item_param::set_out_param_info(Send_field *info)
+{
+ m_out_param_info= info;
+}
+
+
+/**
+ Getter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be retrieved in
+ Protocol_binary::send_out_parameters() during creation of OUT-parameter
+ result set.
+*/
+
+const Send_field *
+Item_param::get_out_param_info() const
+{
+ return m_out_param_info;
+}
+
+
+/**
+ Fill meta-data information for the corresponding column in a result set.
+ If this is an OUT-parameter of a stored procedure, preserve meta-data of
+ stored-routine variable.
+
+ @param field container for meta-data to be filled
+*/
+
+void Item_param::make_field(Send_field *field)
+{
+ Item::make_field(field);
+
+ if (!m_out_param_info)
+ return;
+
+ /*
+ This is an OUT-parameter of stored procedure. We should use
+ OUT-parameter info to fill out the names.
+ */
+
+ field->db_name= m_out_param_info->db_name;
+ field->table_name= m_out_param_info->table_name;
+ field->org_table_name= m_out_param_info->org_table_name;
+ field->col_name= m_out_param_info->col_name;
+ field->org_col_name= m_out_param_info->org_col_name;
+ field->length= m_out_param_info->length;
+ field->charsetnr= m_out_param_info->charsetnr;
+ field->flags= m_out_param_info->flags;
+ field->decimals= m_out_param_info->decimals;
+ field->type= m_out_param_info->type;
+}
+
/****************************************************************************
Item_copy
****************************************************************************/
@@ -3518,7 +3682,7 @@ void Item_copy_decimal::copy()
/*
- Functions to convert item to field (for send_fields)
+ Functions to convert item to field (for send_result_set_metadata)
*/
/* ARGSUSED */
diff --git a/sql/item.h b/sql/item.h
index a2cff3ab3a9..b3234ae0120 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -450,6 +450,11 @@ public:
TRUE if error has occured.
*/
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+
+ virtual void set_out_param_info(Send_field *info) {}
+
+ virtual const Send_field *get_out_param_info() const
+ { return NULL; }
};
@@ -1560,7 +1565,8 @@ public:
/* Item represents one placeholder ('?') of prepared statement */
-class Item_param :public Item
+class Item_param :public Item,
+ private Settable_routine_parameter
{
char cnvbuf[MAX_FIELD_WIDTH];
String cnvstr;
@@ -1648,6 +1654,7 @@ public:
void set_int(longlong i, uint32 max_length_arg);
void set_double(double i);
void set_decimal(const char *str, ulong length);
+ void set_decimal(const my_decimal *dv);
bool set_str(const char *str, ulong length);
bool set_longdata(const char *str, ulong length);
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
@@ -1697,6 +1704,25 @@ public:
/** Item is a argument to a limit clause. */
bool limit_clause_param;
void set_param_type_and_swap_value(Item_param *from);
+
+private:
+ virtual inline Settable_routine_parameter *
+ get_settable_routine_parameter()
+ {
+ return this;
+ }
+
+ virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+
+ virtual void set_out_param_info(Send_field *info);
+
+public:
+ virtual const Send_field *get_out_param_info() const;
+
+ virtual void make_field(Send_field *field);
+
+private:
+ Send_field *m_out_param_info;
};
@@ -2054,7 +2080,7 @@ public:
/**
Item_empty_string -- is a utility class to put an item into List<Item>
- which is then used in protocol.send_fields() when sending SHOW output to
+ which is then used in protocol.send_result_set_metadata() when sending SHOW output to
the client.
*/
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index cac647a0d03..ec3779d1ddf 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -274,7 +274,7 @@ protected:
#define TABLE_OPEN_CACHE_DEFAULT 400
#define TABLE_DEF_CACHE_DEFAULT 400
/**
- We must have room for at least 256 table definitions in the table
+ We must have room for at least 400 table definitions in the table
cache, since otherwise there is no chance prepared
statements that use these many tables can work.
Prepared statements use table definition cache ids (table_map_id)
@@ -886,7 +886,7 @@ struct Query_cache_query_flags
{
unsigned int client_long_flag:1;
unsigned int client_protocol_41:1;
- unsigned int result_in_binary_protocol:1;
+ unsigned int protocol_type:2;
unsigned int more_results_exists:1;
unsigned int in_trans:1;
unsigned int autocommit:1;
@@ -1426,19 +1426,6 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
#define is_schema_db(X) \
!my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X))
-/* sql_prepare.cc */
-
-void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
-void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
-void mysqld_stmt_close(THD *thd, char *packet);
-void mysql_sql_stmt_prepare(THD *thd);
-void mysql_sql_stmt_execute(THD *thd);
-void mysql_sql_stmt_close(THD *thd);
-void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
-void mysqld_stmt_reset(THD *thd, char *packet);
-void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
-void reinit_stmt_before_use(THD *thd, LEX *lex);
-
/* sql_handler.cc */
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
diff --git a/sql/procedure.h b/sql/procedure.h
index ceb586766b1..152cd636cb4 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -23,7 +23,7 @@
#define PROC_NO_SORT 1 /**< Bits in flags */
#define PROC_GROUP 2 /**< proc must have group */
-/* Procedure items used by procedures to store values for send_fields */
+/* Procedure items used by procedures to store values for send_result_set_metadata */
class Item_proc :public Item
{
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 86f95755bd9..68e22564952 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -28,6 +28,7 @@
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
+/* Declared non-static only because of the embedded library. */
bool net_send_error_packet(THD *, uint, const char *, const char *);
/* Declared non-static only because of the embedded library. */
bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *);
@@ -141,6 +142,7 @@ bool Protocol::net_store_data(const uchar *from, size_t length,
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate)
{
+ bool error;
DBUG_ENTER("net_send_error");
DBUG_ASSERT(!thd->spcont);
@@ -148,7 +150,6 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
DBUG_ASSERT(err);
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
- bool error;
if (sqlstate == NULL)
sqlstate= mysql_errno_to_sqlstate(sql_errno);
@@ -470,6 +471,12 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
packet is "buffered" in the diagnostics area and sent to the client
in the end of statement.
+ @note This method defines a template, but delegates actual
+ sending of data to virtual Protocol::send_{ok,eof,error}. This
+ allows for implementation of protocols that "intercept" ok/eof/error
+ messages, and store them in memory, etc, instead of sending to
+ the client.
+
@pre The diagnostics area is assigned or disabled. It can not be empty
-- we assume that every SQL statement or COM_* command
generates OK, ERROR, or EOF status.
@@ -484,47 +491,94 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
Diagnostics_area::is_sent is set for debugging purposes only.
*/
-void net_end_statement(THD *thd)
+void Protocol::end_statement()
{
+ DBUG_ENTER("Protocol::end_statement");
DBUG_ASSERT(! thd->stmt_da->is_sent);
+ bool error= FALSE;
/* Can not be true, but do not take chances in production. */
if (thd->stmt_da->is_sent)
- return;
-
- bool error= FALSE;
+ DBUG_VOID_RETURN;
switch (thd->stmt_da->status()) {
case Diagnostics_area::DA_ERROR:
/* The query failed, send error to log and abort bootstrap. */
- error= net_send_error(thd,
- thd->stmt_da->sql_errno(),
- thd->stmt_da->message(),
- thd->stmt_da->get_sqlstate());
+ error= send_error(thd->stmt_da->sql_errno(),
+ thd->stmt_da->message(),
+ thd->stmt_da->get_sqlstate());
break;
case Diagnostics_area::DA_EOF:
- error= net_send_eof(thd,
- thd->stmt_da->server_status(),
- thd->stmt_da->statement_warn_count());
+ error= send_eof(thd->stmt_da->server_status(),
+ thd->stmt_da->statement_warn_count());
break;
case Diagnostics_area::DA_OK:
- error= net_send_ok(thd,
- thd->stmt_da->server_status(),
- thd->stmt_da->statement_warn_count(),
- thd->stmt_da->affected_rows(),
- thd->stmt_da->last_insert_id(),
- thd->stmt_da->message());
+ error= send_ok(thd->stmt_da->server_status(),
+ thd->stmt_da->statement_warn_count(),
+ thd->stmt_da->affected_rows(),
+ thd->stmt_da->last_insert_id(),
+ thd->stmt_da->message());
break;
case Diagnostics_area::DA_DISABLED:
break;
case Diagnostics_area::DA_EMPTY:
default:
DBUG_ASSERT(0);
- error= net_send_ok(thd, thd->server_status, 0, 0, 0, NULL);
+ error= send_ok(thd->server_status, 0, 0, 0, NULL);
break;
}
if (!error)
thd->stmt_da->is_sent= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ A default implementation of "OK" packet response to the client.
+
+ Currently this implementation is re-used by both network-oriented
+ protocols -- the binary and text one. They do not differ
+ in their OK packet format, which allows for a significant simplification
+ on client side.
+*/
+
+bool Protocol::send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message)
+{
+ DBUG_ENTER("Protocol::send_ok");
+
+ DBUG_RETURN(net_send_ok(thd, server_status, statement_warn_count,
+ affected_rows, last_insert_id, message));
+}
+
+
+/**
+ A default implementation of "EOF" packet response to the client.
+
+ Binary and text protocol do not differ in their EOF packet format.
+*/
+
+bool Protocol::send_eof(uint server_status, uint statement_warn_count)
+{
+ DBUG_ENTER("Protocol::send_eof");
+
+ DBUG_RETURN(net_send_eof(thd, server_status, statement_warn_count));
+}
+
+
+/**
+ A default implementation of "ERROR" packet response to the client.
+
+ Binary and text protocol do not differ in ERROR packet format.
+*/
+
+bool Protocol::send_error(uint sql_errno, const char *err_msg,
+ const char *sql_state)
+{
+ DBUG_ENTER("Protocol::send_error");
+
+ DBUG_RETURN(net_send_error_packet(thd, sql_errno, err_msg, sql_state));
}
@@ -581,9 +635,10 @@ void Protocol::init(THD *thd_arg)
for the error.
*/
-void Protocol::end_partial_result_set(THD *thd)
+void Protocol::end_partial_result_set(THD *thd_arg)
{
- net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */);
+ net_send_eof(thd_arg, thd_arg->server_status,
+ 0 /* no warnings, we're inside SP */);
}
@@ -616,16 +671,16 @@ bool Protocol::flush()
1 Error (Note that in this case the error is not sent to the
client)
*/
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- uchar buff[80];
+ uchar buff[MAX_FIELD_WIDTH];
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
- DBUG_ENTER("send_fields");
+ DBUG_ENTER("send_result_set_metadata");
if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
@@ -770,7 +825,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
write_eof_packet(thd, &thd->net, thd->server_status,
thd->warning_info->statement_warn_count());
}
- DBUG_RETURN(prepare_for_send(list));
+ DBUG_RETURN(prepare_for_send(list->elements));
err:
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
@@ -789,6 +844,47 @@ bool Protocol::write()
/**
+ Send one result set row.
+
+ @param row_items a collection of column values for that row
+
+ @return Error status.
+ @retval TRUE Error.
+ @retval FALSE Success.
+*/
+
+bool Protocol::send_result_set_row(List<Item> *row_items)
+{
+ char buffer[MAX_FIELD_WIDTH];
+ String str_buffer(buffer, sizeof (buffer), &my_charset_bin);
+ List_iterator_fast<Item> it(*row_items);
+
+ DBUG_ENTER("Protocol::send_result_set_row");
+
+ for (Item *item= it++; item; item= it++)
+ {
+ if (item->send(this, &str_buffer))
+ {
+ // If we're out of memory, reclaim some, to help us recover.
+ this->free();
+ DBUG_RETURN(TRUE);
+ }
+ /* Item::send() may generate an error. If so, abort the loop. */
+ if (thd->is_error())
+ DBUG_RETURN(TRUE);
+
+ /*
+ Reset str_buffer to its original state, as it may have been altered in
+ Item::send().
+ */
+ str_buffer.set(buffer, sizeof(buffer), &my_charset_bin);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Send \\0 end terminated string.
@param from NullS or \\0 terminated string
@@ -834,7 +930,6 @@ bool Protocol::store(I_List<i_string>* str_list)
return store((char*) tmp.ptr(), len, tmp.charset());
}
-
/****************************************************************************
Functions to handle the simple (default) protocol where everything is
This protocol is the one that is used by default between the MySQL server
@@ -1113,6 +1208,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm)
return net_store_data((uchar*) buff, length);
}
+/**
+ Assign OUT-parameters to user variables.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
+{
+ DBUG_ASSERT(sp_params->elements ==
+ thd->lex->prepared_stmt_params.elements);
+
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+ List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+ LEX_STRING *user_var_name= user_var_name_it++;
+
+ if (!item_param || !user_var_name)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ Item_func_set_user_var *suv=
+ new Item_func_set_user_var(*user_var_name, item_param);
+ /*
+ Item_func_set_user_var is not fixed after construction, call
+ fix_fields().
+ */
+ if (suv->fix_fields(thd, NULL))
+ return TRUE;
+
+ if (suv->check(FALSE))
+ return TRUE;
+
+ if (suv->update())
+ return TRUE;
+ }
+
+ return FALSE;
+}
/****************************************************************************
Functions to handle the binary protocol used with prepared statements
@@ -1133,14 +1275,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm)
[..]..[[length]data] data
****************************************************************************/
-bool Protocol_binary::prepare_for_send(List<Item> *item_list)
+bool Protocol_binary::prepare_for_send(uint num_columns)
{
- Protocol::prepare_for_send(item_list);
+ Protocol::prepare_for_send(num_columns);
bit_fields= (field_count+9)/8;
- if (packet->alloc(bit_fields+1))
- return 1;
+ return packet->alloc(bit_fields+1);
+
/* prepare_for_resend will be called after this one */
- return 0;
}
@@ -1328,3 +1469,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm)
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
}
+
+/**
+ Send a result set with OUT-parameter values by means of PS-protocol.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
+{
+ if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
+ {
+ /* The client does not support OUT-parameters. */
+ return FALSE;
+ }
+
+ List<Item> out_param_lst;
+
+ {
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+
+ if (!item_param)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ if (out_param_lst.push_back(item_param))
+ return TRUE;
+ }
+ }
+
+ if (!out_param_lst.elements)
+ return FALSE;
+
+ /*
+ We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it
+ is used in send_result_set_metadata().
+ */
+
+ thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
+
+ /* Send meta-data. */
+ if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+ return TRUE;
+
+ /* Send data. */
+
+ prepare_for_resend();
+
+ if (send_result_set_row(&out_param_lst))
+ return TRUE;
+
+ if (write())
+ return TRUE;
+
+ /* Restore THD::server_status. */
+ thd->server_status&= ~SERVER_PS_OUT_PARAMS;
+
+ /*
+ Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
+ for sure.
+ */
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+
+ /* Send EOF-packet. */
+ net_send_eof(thd, thd->server_status, 0);
+
+ return FALSE;
+}
diff --git a/sql/protocol.h b/sql/protocol.h
index 3b6a3bdb863..4ba874594c3 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -21,6 +21,7 @@
class i_string;
class THD;
+class Item_param;
typedef struct st_mysql_field MYSQL_FIELD;
typedef struct st_mysql_rows MYSQL_ROWS;
@@ -47,6 +48,16 @@ protected:
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+
+ virtual bool send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message);
+
+ virtual bool send_eof(uint server_status, uint statement_warn_count);
+
+ virtual bool send_error(uint sql_errno, const char *err_msg,
+ const char *sql_state);
+
public:
Protocol() {}
Protocol(THD *thd_arg) { init(thd_arg); }
@@ -54,7 +65,8 @@ public:
void init(THD* thd_arg);
enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
- virtual bool send_fields(List<Item> *list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ bool send_result_set_row(List<Item> *row_items);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
@@ -72,9 +84,9 @@ public:
inline bool store(String *str)
{ return store((char*) str->ptr(), str->length(), str->charset()); }
- virtual bool prepare_for_send(List<Item> *item_list)
+ virtual bool prepare_for_send(uint num_columns)
{
- field_count=item_list->elements;
+ field_count= num_columns;
return 0;
}
virtual bool flush();
@@ -96,6 +108,8 @@ public:
virtual bool store_date(MYSQL_TIME *time)=0;
virtual bool store_time(MYSQL_TIME *time)=0;
virtual bool store(Field *field)=0;
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
#ifdef EMBEDDED_LIBRARY
int begin_dataset();
virtual void remove_last_row() {}
@@ -104,13 +118,15 @@ public:
#endif
enum enum_protocol_type
{
- PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1
/*
- before adding here or change the values, consider that it is cast to a
- bit in sql_cache.cc.
+ Before adding a new type, please make sure
+ there is enough storage for it in Query_cache_query_flags.
*/
+ PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2
};
virtual enum enum_protocol_type type()= 0;
+
+ void end_statement();
};
@@ -137,6 +153,8 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
@@ -151,7 +169,7 @@ private:
public:
Protocol_binary() {}
Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
- virtual bool prepare_for_send(List<Item> *item_list);
+ virtual bool prepare_for_send(uint num_columns);
virtual void prepare_for_resend();
#ifdef EMBEDDED_LIBRARY
virtual bool write();
@@ -172,13 +190,15 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
+
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate);
-void net_end_statement(THD *thd);
bool send_old_password_request(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index aa8618399f8..62e05ea25b7 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -471,7 +471,7 @@ bool show_new_master(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 20));
field_list.push_back(new Item_return_int("Log_pos", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -675,7 +675,7 @@ bool show_slave_hosts(THD* thd)
field_list.push_back(new Item_return_int("Master_id", 10,
MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/slave.cc b/sql/slave.cc
index 7ad282c0f24..a86d42eb934 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1605,7 +1605,7 @@ bool show_master_info(THD* thd, Master_info* mi)
field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Last_SQL_Error", 20));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index c013037eef4..965949223a5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -14,6 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
+#include "sql_prepare.h"
#include "probes_mysql.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
@@ -2043,6 +2044,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
err_status= TRUE;
break;
}
+
+ Send_field *out_param_info= new Send_field();
+ nctx->get_item(i)->make_field(out_param_info);
+ out_param_info->db_name= m_db.str;
+ out_param_info->table_name= m_name.str;
+ out_param_info->org_table_name= m_name.str;
+ out_param_info->col_name= spvar->name.str;
+ out_param_info->org_col_name= spvar->name.str;
+
+ srp->set_out_param_info(out_param_info);
}
}
@@ -2450,7 +2461,7 @@ sp_head::show_create_routine(THD *thd, int type)
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (protocol->send_fields(&fields,
+ if (protocol->send_result_set_metadata(&fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_RETURN(TRUE);
@@ -2636,8 +2647,8 @@ sp_head::show_routine_code(THD *thd)
field_list.push_back(new Item_uint("Pos", 9));
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Instruction",
- max(buffer.length(), 1024)));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ max(buffer.length(), 1024)));
+ if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -2869,7 +2880,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
if (thd->stmt_da->is_eof())
- net_end_statement(thd);
+ thd->protocol->end_statement();
query_cache_end_of_result(thd);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 040c985560e..d3fb8e3fe5c 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -4532,7 +4532,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0c6ea53d39c..b5d713ebe62 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -21,6 +21,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
+#include "sql_prepare.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index fc853af65a8..0d7e45705f4 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1155,7 +1155,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
protocol (COM_EXECUTE) cannot be served to statements asking for results
in the text protocol (COM_QUERY) and vice-versa.
*/
- flags.result_in_binary_protocol= (unsigned int) thd->protocol->type();
+ flags.protocol_type= (unsigned int) thd->protocol->type();
+ /* PROTOCOL_LOCAL results are not cached. */
+ DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
@@ -1184,7 +1186,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
- (int)flags.result_in_binary_protocol,
+ (int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@@ -1439,7 +1441,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
- flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
+ flags.protocol_type= (unsigned int) thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
@@ -1466,7 +1468,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
- (int)flags.result_in_binary_protocol,
+ (int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 456ff0cb3c5..275969f0026 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1493,8 +1493,8 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_fields(field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
+ return (result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
@@ -1618,10 +1618,10 @@ bool sql_exchange::escaped_given(void)
}
-bool select_send::send_fields(List<Item> &list, uint flags)
+bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
{
bool res;
- if (!(res= thd->protocol->send_fields(&list, flags)))
+ if (!(res= thd->protocol->send_result_set_metadata(&list, flags)))
is_result_set_started= 1;
return res;
}
@@ -1662,10 +1662,13 @@ void select_send::cleanup()
bool select_send::send_data(List<Item> &items)
{
+ Protocol *protocol= thd->protocol;
+ DBUG_ENTER("select_send::send_data");
+
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
- return 0;
+ DBUG_RETURN(FALSE);
}
/*
@@ -1675,36 +1678,18 @@ bool select_send::send_data(List<Item> &items)
*/
ha_release_temporary_latches(thd);
- List_iterator_fast<Item> li(items);
- Protocol *protocol= thd->protocol;
- char buff[MAX_FIELD_WIDTH];
- String buffer(buff, sizeof(buff), &my_charset_bin);
- DBUG_ENTER("select_send::send_data");
-
protocol->prepare_for_resend();
- Item *item;
- while ((item=li++))
- {
- if (item->send(protocol, &buffer))
- {
- protocol->free(); // Free used buffer
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
- /*
- Reset buffer to its original state, as it may have been altered in
- Item::send().
- */
- buffer.set(buff, sizeof(buff), &my_charset_bin);
- }
- thd->sent_row_count++;
- if (thd->is_error())
+ if (protocol->send_result_set_row(&items))
{
protocol->remove_last_row();
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
+
+ thd->sent_row_count++;
+
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
+
DBUG_RETURN(0);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 558de19b7af..5b367bec55a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -23,52 +23,8 @@
#include "log.h"
#include "rpl_tblmap.h"
-/**
- An interface that is used to take an action when
- the locking module notices that a table version has changed
- since the last execution. "Table" here may refer to any kind of
- table -- a base table, a temporary table, a view or an
- information schema table.
-
- When we open and lock tables for execution of a prepared
- statement, we must verify that they did not change
- since statement prepare. If some table did change, the statement
- parse tree *may* be no longer valid, e.g. in case it contains
- optimizations that depend on table metadata.
-
- This class provides an interface (a method) that is
- invoked when such a situation takes place.
- The implementation of the method simply reports an error, but
- the exact details depend on the nature of the SQL statement.
-
- At most 1 instance of this class is active at a time, in which
- case THD::m_reprepare_observer is not NULL.
-
- @sa check_and_update_table_version() for details of the
- version tracking algorithm
-
- @sa Open_tables_state::m_reprepare_observer for the life cycle
- of metadata observers.
-*/
-
-class Reprepare_observer
-{
-public:
- Reprepare_observer() {}
- /**
- Check if a change of metadata is OK. In future
- the signature of this method may be extended to accept the old
- and the new versions, but since currently the check is very
- simple, we only need the THD to report an error.
- */
- bool report_error(THD *thd);
- bool is_invalidated() const { return m_invalidated; }
- void reset_reprepare_observer() { m_invalidated= FALSE; }
-private:
- bool m_invalidated;
-};
-
+class Reprepare_observer;
class Relay_log_info;
class Query_log_event;
@@ -2441,7 +2397,7 @@ public:
*/
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
- virtual bool send_fields(List<Item> &list, uint flags)=0;
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
virtual bool send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
@@ -2486,7 +2442,7 @@ class select_result_interceptor: public select_result
public:
select_result_interceptor() {} /* Remove gcc warning */
uint field_count(List<Item> &fields) const { return 0; }
- bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
+ bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE; }
};
@@ -2499,7 +2455,7 @@ class select_send :public select_result {
bool is_result_set_started;
public:
select_send() :is_result_set_started(FALSE) {}
- bool send_fields(List<Item> &list, uint flags);
+ bool send_result_set_metadata(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
virtual bool check_simple_select() const { return FALSE; }
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 6ff5e8666d7..53b68961c02 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -957,7 +957,7 @@ static bool login_connection(THD *thd)
my_net_set_write_timeout(net, connect_timeout);
error= check_connection(thd);
- net_end_statement(thd);
+ thd->protocol->end_statement();
if (error)
{ // Wrong permissions
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 553342f155b..6d07f753086 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -90,7 +90,7 @@ class Materialized_cursor: public Server_side_cursor
public:
Materialized_cursor(select_result *result, TABLE *table);
- int fill_item_list(THD *thd, List<Item> &send_fields);
+ int fill_item_list(THD *thd, List<Item> &send_result_set_metadata);
virtual bool is_open() const { return table != 0; }
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
@@ -115,7 +115,7 @@ public:
Materialized_cursor *materialized_cursor;
Select_materialize(select_result *result_arg)
:result(result_arg), materialized_cursor(0) {}
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
};
@@ -376,12 +376,12 @@ Sensitive_cursor::open(JOIN *join_arg)
join->change_result(result);
/*
Send fields description to the client; server_status is sent
- in 'EOF' packet, which follows send_fields().
- We don't simply use SEND_EOF flag of send_fields because we also
+ in 'EOF' packet, which follows send_result_set_metadata().
+ We don't simply use SEND_EOF flag of send_result_set_metadata because we also
want to flush the network buffer, which is done only in a standalone
send_eof().
*/
- result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+ result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -566,14 +566,14 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg,
Preserve the original metadata that would be sent to the client.
@param thd Thread identifier.
- @param send_fields List of fields that would be sent.
+ @param send_result_set_metadata List of fields that would be sent.
*/
-int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
+int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_result_set_metadata)
{
Query_arena backup_arena;
int rc;
- List_iterator_fast<Item> it_org(send_fields);
+ List_iterator_fast<Item> it_org(send_result_set_metadata);
List_iterator_fast<Item> it_dst(item_list);
Item *item_org;
Item *item_dst;
@@ -583,7 +583,7 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
if ((rc= table->fill_item_list(&item_list)))
goto end;
- DBUG_ASSERT(send_fields.elements == item_list.elements);
+ DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements);
/*
Unless we preserve the original metadata, it will be lost,
@@ -623,17 +623,17 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
{
/*
Now send the result set metadata to the client. We need to
- do it here, as in Select_materialize::send_fields the items
- for column types are not yet created (send_fields requires
+ do it here, as in Select_materialize::send_result_set_metadata the items
+ for column types are not yet created (send_result_set_metadata requires
a list of items). The new types may differ from the original
ones sent at prepare if some of them were altered by MySQL
HEAP tables mechanism -- used when create_tmp_field_from_item
may alter the original column type.
- We can't simply supply SEND_EOF flag to send_fields, because
- send_fields doesn't flush the network buffer.
+ We can't simply supply SEND_EOF flag to send_result_set_metadata, because
+ send_result_set_metadata doesn't flush the network buffer.
*/
- rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS);
+ rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -717,7 +717,7 @@ Materialized_cursor::~Materialized_cursor()
Select_materialize
****************************************************************************/
-bool Select_materialize::send_fields(List<Item> &list, uint flags)
+bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
{
DBUG_ASSERT(table == 0);
if (create_result_table(unit->thd, unit->get_unit_column_types(),
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 2837e45fb47..c8cf03d9ef0 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -672,7 +672,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index ae07e648921..b44edbd373d 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -548,7 +548,7 @@ retry:
tables->db, tables->alias, &it, 0))
goto err;
- protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -670,18 +670,11 @@ retry:
continue;
if (num_rows >= offset_limit_cnt)
{
- Item *item;
protocol->prepare_for_resend();
- it.rewind();
- while ((item=it++))
- {
- if (item->send(thd->protocol, &buffer))
- {
- protocol->free(); // Free used
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- goto err;
- }
- }
+
+ if (protocol->send_result_set_row(&list))
+ goto err;
+
protocol->write();
}
num_rows++;
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 2818aa5082c..003741a7ddc 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
field_list.push_back(new Item_empty_string("description",1000));
field_list.push_back(new Item_empty_string("example",1000));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -463,7 +463,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
+- -+
RETURN VALUES
- result of protocol->send_fields
+ result of protocol->send_result_set_metadata
*/
int send_header_2(Protocol *protocol, bool for_category)
@@ -474,7 +474,7 @@ int send_header_2(Protocol *protocol, bool for_category)
field_list.push_back(new Item_empty_string("source_category_name",64));
field_list.push_back(new Item_empty_string("name",64));
field_list.push_back(new Item_empty_string("is_it_category",1));
- DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ DBUG_RETURN(protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF));
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 61c8e8d123a..97aea49014a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -35,6 +35,7 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
LEX_STRING constant for null-string to be used in parser and other places.
*/
const LEX_STRING null_lex_str= {NULL, 0};
+const LEX_STRING empty_lex_str= { (char*) "", 0 };
/* Longest standard keyword name */
@@ -140,6 +141,7 @@ Lex_input_stream::Lex_input_stream(THD *thd,
found_semicolon(NULL),
ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
stmt_prepare_mode(FALSE),
+ multi_statements(TRUE),
in_comment(NO_COMMENT),
m_underscore_cs(NULL)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 1fc7cb2a948..32b95318469 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -952,6 +952,7 @@ enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
XA_SUSPEND, XA_FOR_MIGRATE};
extern const LEX_STRING null_lex_str;
+extern const LEX_STRING empty_lex_str;
/*
@@ -1489,9 +1490,13 @@ public:
/**
TRUE if we're parsing a prepared statement: in this mode
- we should allow placeholders and disallow multi-statements.
+ we should allow placeholders.
*/
bool stmt_prepare_mode;
+ /**
+ TRUE if we should allow multi-statements.
+ */
+ bool multi_statements;
/** State of the lexical analyser for comments. */
enum_comment_state in_comment;
diff --git a/sql/sql_list.cc b/sql/sql_list.cc
index 49b649133d0..a256212471d 100644
--- a/sql/sql_list.cc
+++ b/sql/sql_list.cc
@@ -18,7 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_list.h"
list_node end_of_list;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 22df77afeb3..64ca12c4ec2 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -15,11 +15,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include "my_global.h"
+#include "my_sys.h"
+
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+void *sql_alloc(size_t);
+
/* mysql standard class memory allocator */
class Sql_alloc
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 43660d525e3..a9a6367d877 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -27,6 +27,7 @@
#include "sp_cache.h"
#include "events.h"
#include "sql_trigger.h"
+#include "sql_prepare.h"
#include "probes_mysql.h"
/**
@@ -453,7 +454,7 @@ static void handle_bootstrap_impl(THD *thd)
/* purecov: begin tested */
if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
{
- net_end_statement(thd);
+ thd->protocol->end_statement();
bootstrap_error= 1;
break;
}
@@ -494,7 +495,7 @@ static void handle_bootstrap_impl(THD *thd)
close_thread_tables(thd); // Free tables
bootstrap_error= thd->is_error();
- net_end_statement(thd);
+ thd->protocol->end_statement();
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
@@ -819,7 +820,7 @@ bool do_command(THD *thd)
/* The error must be set. */
DBUG_ASSERT(thd->is_error());
- net_end_statement(thd);
+ thd->protocol->end_statement();
if (net->error != 3)
{
@@ -1236,7 +1237,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *beginning_of_next_stmt= (char*) end_of_stmt;
- net_end_statement(thd);
+ thd->protocol->end_statement();
query_cache_end_of_result(thd);
/*
Multiple queries exits, execute them individually
@@ -1609,7 +1610,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->transaction.stmt.reset();
- net_end_statement(thd);
+ thd->protocol->end_statement();
query_cache_end_of_result(thd);
thd->proc_info= "closing tables";
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 1558b34a3b9..5feaa02cf7e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -84,6 +84,7 @@ When one supplies long data for a placeholder:
*/
#include "mysql_priv.h"
+#include "sql_prepare.h"
#include "sql_select.h" // for JOIN
#include "sql_cursor.h"
#include "sp_head.h"
@@ -106,7 +107,7 @@ class Select_fetch_protocol_binary: public select_send
Protocol_binary protocol;
public:
Select_fetch_protocol_binary(THD *thd);
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
virtual bool send_data(List<Item> &items);
virtual bool send_eof();
#ifdef EMBEDDED_LIBRARY
@@ -163,6 +164,7 @@ public:
bool execute_loop(String *expanded_query,
bool open_cursor,
uchar *packet_arg, uchar *packet_end_arg);
+ bool execute_server_runnable(Server_runnable *server_runnable);
/* Destroy this statement */
void deallocate();
private:
@@ -183,6 +185,78 @@ private:
void swap_prepared_statement(Prepared_statement *copy);
};
+/**
+ Execute one SQL statement in an isolated context.
+*/
+
+class Execute_sql_statement: public Server_runnable
+{
+public:
+ Execute_sql_statement(LEX_STRING sql_text);
+ virtual bool execute_server_code(THD *thd);
+private:
+ LEX_STRING m_sql_text;
+};
+
+
+class Ed_connection;
+
+/**
+ Protocol_local: a helper class to intercept the result
+ of the data written to the network.
+*/
+
+class Protocol_local :public Protocol
+{
+public:
+ Protocol_local(THD *thd, Ed_connection *ed_connection);
+ ~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
+protected:
+ virtual void prepare_for_resend();
+ virtual bool write();
+ virtual bool store_null();
+ virtual bool store_tiny(longlong from);
+ virtual bool store_short(longlong from);
+ virtual bool store_long(longlong from);
+ virtual bool store_longlong(longlong from, bool unsigned_flag);
+ virtual bool store_decimal(const my_decimal *);
+ virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
+ virtual bool store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ virtual bool store(MYSQL_TIME *time);
+ virtual bool store_date(MYSQL_TIME *time);
+ virtual bool store_time(MYSQL_TIME *time);
+ virtual bool store(float value, uint32 decimals, String *buffer);
+ virtual bool store(double value, uint32 decimals, String *buffer);
+ virtual bool store(Field *field);
+
+ virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
+#ifdef EMBEDDED_LIBRARY
+ void remove_last_row();
+#endif
+ virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
+
+ virtual bool send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message);
+
+ virtual bool send_eof(uint server_status, uint statement_warn_count);
+ virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
+private:
+ bool store_string(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
+
+ bool store_column(const void *data, size_t length);
+ void opt_add_row_to_rset();
+private:
+ Ed_connection *m_connection;
+ MEM_ROOT m_rset_root;
+ List<Ed_row> *m_rset;
+ size_t m_column_count;
+ Ed_column *m_current_row;
+ Ed_column *m_current_column;
+};
/******************************************************************************
Implementation
@@ -260,7 +334,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
error= my_net_write(net, buff, sizeof(buff));
if (stmt->param_count && ! error)
{
- error= thd->protocol_text.send_fields((List<Item> *)
+ error= thd->protocol_text.send_result_set_metadata((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF);
}
@@ -1374,7 +1448,7 @@ static int mysql_test_select(Prepared_statement *stmt,
unit->prepare call above.
*/
if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
- lex->result->send_fields(fields, Protocol::SEND_EOF) ||
+ lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) ||
thd->protocol->flush())
goto error;
DBUG_RETURN(2);
@@ -1920,30 +1994,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
- case SQLCOM_SHOW_PROCESSLIST:
- case SQLCOM_SHOW_STORAGE_ENGINES:
- case SQLCOM_SHOW_PRIVILEGES:
- case SQLCOM_SHOW_COLUMN_TYPES:
- case SQLCOM_SHOW_ENGINE_LOGS:
- case SQLCOM_SHOW_ENGINE_STATUS:
- case SQLCOM_SHOW_ENGINE_MUTEX:
- case SQLCOM_SHOW_CREATE_DB:
- case SQLCOM_SHOW_GRANTS:
- case SQLCOM_SHOW_BINLOG_EVENTS:
- case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_SLAVE_STAT:
- case SQLCOM_SHOW_CREATE_PROC:
- case SQLCOM_SHOW_CREATE_FUNC:
- case SQLCOM_SHOW_CREATE_EVENT:
- case SQLCOM_SHOW_CREATE_TRIGGER:
- case SQLCOM_SHOW_CREATE:
- case SQLCOM_SHOW_PROC_CODE:
- case SQLCOM_SHOW_FUNC_CODE:
- case SQLCOM_SHOW_AUTHORS:
- case SQLCOM_SHOW_CONTRIBUTORS:
- case SQLCOM_SHOW_WARNS:
- case SQLCOM_SHOW_ERRORS:
- case SQLCOM_SHOW_BINLOGS:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@@ -2066,7 +2116,6 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{
Protocol *save_protocol= thd->protocol;
Prepared_statement *stmt;
- bool error;
DBUG_ENTER("mysqld_stmt_prepare");
DBUG_PRINT("prep_query", ("%s", packet));
@@ -2091,15 +2140,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= &thd->protocol_binary;
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
- error= stmt->prepare(packet, packet_length);
-
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),WAIT_PRIOR);
-
- if (error)
+ if (stmt->prepare(packet, packet_length))
{
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
@@ -2478,7 +2519,6 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
DBUG_VOID_RETURN;
-
}
@@ -2787,19 +2827,19 @@ Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg)
:protocol(thd_arg)
{}
-bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
+bool Select_fetch_protocol_binary::send_result_set_metadata(List<Item> &list, uint flags)
{
bool rc;
Protocol *save_protocol= thd->protocol;
/*
- Protocol::send_fields caches the information about column types:
+ Protocol::send_result_set_metadata caches the information about column types:
this information is later used to send data. Therefore, the same
dedicated Protocol object must be used for all operations with
a cursor.
*/
thd->protocol= &protocol;
- rc= select_send::send_fields(list, flags);
+ rc= select_send::send_result_set_metadata(list, flags);
thd->protocol= save_protocol;
return rc;
@@ -2848,6 +2888,70 @@ Reprepare_observer::report_error(THD *thd)
}
+/*******************************************************************
+* Server_runnable
+*******************************************************************/
+
+Server_runnable::~Server_runnable()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Execute_sql_statement::
+Execute_sql_statement(LEX_STRING sql_text)
+ :m_sql_text(sql_text)
+{}
+
+
+/**
+ Parse and execute a statement. Does not prepare the query.
+
+ Allows to execute a statement from within another statement.
+ The main property of the implementation is that it does not
+ affect the environment -- i.e. you can run many
+ executions without having to cleanup/reset THD in between.
+*/
+
+bool
+Execute_sql_statement::execute_server_code(THD *thd)
+{
+ bool error;
+
+ if (alloc_query(thd, m_sql_text.str, m_sql_text.length))
+ return TRUE;
+
+ Parser_state parser_state(thd, thd->query, thd->query_length);
+
+ parser_state.m_lip.multi_statements= FALSE;
+ lex_start(thd);
+
+ error= parse_sql(thd, &parser_state, NULL) || thd->is_error();
+
+ if (error)
+ goto end;
+
+ thd->lex->set_trg_event_type_for_tables();
+
+ error= mysql_execute_command(thd);
+
+ if (thd->killed_errno())
+ {
+ if (! thd->stmt_da->is_set())
+ thd->send_kill_message();
+ }
+
+ /* report error issued during command execution */
+ if (error == 0 && thd->spcont == NULL)
+ general_log_write(thd, COM_STMT_EXECUTE,
+ thd->query, thd->query_length);
+
+end:
+ lex_end(thd->lex);
+
+ return error;
+}
+
/***************************************************************************
Prepared_statement
****************************************************************************/
@@ -2945,7 +3049,8 @@ void Prepared_statement::cleanup_stmt()
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
- DBUG_ASSERT(lex->sphead == 0);
+ delete lex->sphead;
+ lex->sphead= 0;
/* The order is important */
lex->unit.cleanup();
cleanup_items(free_list);
@@ -3058,6 +3163,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Parser_state parser_state(thd, thd->query, thd->query_length);
parser_state.m_lip.stmt_prepare_mode= TRUE;
+ parser_state.m_lip.multi_statements= FALSE;
lex_start(thd);
error= parse_sql(thd, & parser_state, NULL) ||
@@ -3302,6 +3408,45 @@ reexecute:
}
+bool
+Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
+{
+ Statement stmt_backup;
+ bool error;
+ Query_arena *save_stmt_arena= thd->stmt_arena;
+ Item_change_list save_change_list;
+ thd->change_list= save_change_list;
+
+ state= CONVENTIONAL_EXECUTION;
+
+ if (!(lex= new (mem_root) st_lex_local))
+ return TRUE;
+
+ thd->set_n_backup_statement(this, &stmt_backup);
+ thd->set_n_backup_active_arena(this, &stmt_backup);
+ thd->stmt_arena= this;
+
+ error= server_runnable->execute_server_code(thd);
+
+ delete lex->sphead;
+ lex->sphead= 0;
+ /* The order is important */
+ lex->unit.cleanup();
+ close_thread_tables(thd);
+ thd->cleanup_after_query();
+
+ thd->restore_active_arena(this, &stmt_backup);
+ thd->restore_backup_statement(this, &stmt_backup);
+ thd->stmt_arena= save_stmt_arena;
+
+ save_change_list= thd->change_list;
+
+ /* Items and memory will freed in destructor */
+
+ return error;
+}
+
+
/**
Reprepare this prepared statement.
@@ -3637,6 +3782,15 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (state == Query_arena::PREPARED)
state= Query_arena::EXECUTED;
+ if (this->lex->sql_command == SQLCOM_CALL)
+ {
+ if (is_sql_prepare())
+ thd->protocol_text.send_out_parameters(&this->lex->param_list);
+ else
+ thd->protocol->send_out_parameters(&this->lex->param_list);
+ }
+
+
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
prepared statements this causes two records to be output:
@@ -3670,3 +3824,573 @@ void Prepared_statement::deallocate()
/* Statement map calls delete stmt on erase */
thd->stmt_map.erase(this);
}
+
+
+/***************************************************************************
+* Ed_result_set
+***************************************************************************/
+/**
+ Use operator delete to free memory of Ed_result_set.
+ Accessing members of a class after the class has been destroyed
+ is a violation of the C++ standard but is commonly used in the
+ server code.
+*/
+
+void Ed_result_set::operator delete(void *ptr, size_t size) throw ()
+{
+ if (ptr)
+ {
+ /*
+ Make a stack copy, otherwise free_root() will attempt to
+ write to freed memory.
+ */
+ MEM_ROOT own_root= ((Ed_result_set*) ptr)->m_mem_root;
+ free_root(&own_root, MYF(0));
+ }
+}
+
+
+/**
+ Initialize an instance of Ed_result_set.
+
+ Instances of the class, as well as all result set rows, are
+ always allocated in the memory root passed over as the second
+ argument. In the constructor, we take over ownership of the
+ memory root. It will be freed when the class is destroyed.
+
+ sic: Ed_result_est is not designed to be allocated on stack.
+*/
+
+Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg,
+ size_t column_count_arg,
+ MEM_ROOT *mem_root_arg)
+ :m_mem_root(*mem_root_arg),
+ m_column_count(column_count_arg),
+ m_rows(rows_arg),
+ m_next_rset(NULL)
+{
+ /* Take over responsibility for the memory */
+ clear_alloc_root(mem_root_arg);
+}
+
+/***************************************************************************
+* Ed_result_set
+***************************************************************************/
+
+/**
+ Create a new "execute direct" connection.
+*/
+
+Ed_connection::Ed_connection(THD *thd)
+ :m_warning_info(thd->query_id),
+ m_thd(thd),
+ m_rsets(0),
+ m_current_rset(0)
+{
+}
+
+
+/**
+ Free all result sets of the previous statement, if any,
+ and reset warnings and errors.
+
+ Called before execution of the next query.
+*/
+
+void
+Ed_connection::free_old_result()
+{
+ while (m_rsets)
+ {
+ Ed_result_set *rset= m_rsets->m_next_rset;
+ delete m_rsets;
+ m_rsets= rset;
+ }
+ m_current_rset= m_rsets;
+ m_diagnostics_area.reset_diagnostics_area();
+ m_warning_info.clear_warning_info(m_thd->query_id);
+}
+
+
+/**
+ A simple wrapper that uses a helper class to execute SQL statements.
+*/
+
+bool
+Ed_connection::execute_direct(LEX_STRING sql_text)
+{
+ Execute_sql_statement execute_sql_statement(sql_text);
+ DBUG_PRINT("ed_query", ("%s", sql_text.str));
+
+ return execute_direct(&execute_sql_statement);
+}
+
+
+/**
+ Execute a fragment of server functionality without an effect on
+ thd, and store results in memory.
+
+ Conventions:
+ - the code fragment must finish with OK, EOF or ERROR.
+ - the code fragment doesn't have to close thread tables,
+ free memory, commit statement transaction or do any other
+ cleanup that is normally done in the end of dispatch_command().
+
+ @param server_runnable A code fragment to execute.
+*/
+
+bool Ed_connection::execute_direct(Server_runnable *server_runnable)
+{
+ bool rc= FALSE;
+ Protocol_local protocol_local(m_thd, this);
+ Prepared_statement stmt(m_thd);
+ Protocol *save_protocol= m_thd->protocol;
+ Diagnostics_area *save_diagnostics_area= m_thd->stmt_da;
+ Warning_info *save_warning_info= m_thd->warning_info;
+
+ DBUG_ENTER("Ed_connection::execute_direct");
+
+ free_old_result(); /* Delete all data from previous execution, if any */
+
+ m_thd->protocol= &protocol_local;
+ m_thd->stmt_da= &m_diagnostics_area;
+ m_thd->warning_info= &m_warning_info;
+
+ rc= stmt.execute_server_runnable(server_runnable);
+ m_thd->protocol->end_statement();
+
+ m_thd->protocol= save_protocol;
+ m_thd->stmt_da= save_diagnostics_area;
+ m_thd->warning_info= save_warning_info;
+ /*
+ Protocol_local makes use of m_current_rset to keep
+ track of the last result set, while adding result sets to the end.
+ Reset it to point to the first result set instead.
+ */
+ m_current_rset= m_rsets;
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ A helper method that is called only during execution.
+
+ Although Ed_connection doesn't support multi-statements,
+ a statement may generate many result sets. All subsequent
+ result sets are appended to the end.
+
+ @pre This is called only by Protocol_local.
+*/
+
+void
+Ed_connection::add_result_set(Ed_result_set *ed_result_set)
+{
+ if (m_rsets)
+ {
+ m_current_rset->m_next_rset= ed_result_set;
+ /* While appending, use m_current_rset as a pointer to the tail. */
+ m_current_rset= ed_result_set;
+ }
+ else
+ m_current_rset= m_rsets= ed_result_set;
+}
+
+
+/**
+ Release ownership of the current result set to the client.
+
+ Since we use a simple linked list for result sets,
+ this method uses a linear search of the previous result
+ set to exclude the released instance from the list.
+
+ @todo Use double-linked list, when this is really used.
+
+ XXX: This has never been tested with more than one result set!
+
+ @pre There must be a result set.
+*/
+
+Ed_result_set *
+Ed_connection::store_result_set()
+{
+ Ed_result_set *ed_result_set;
+
+ DBUG_ASSERT(m_current_rset);
+
+ if (m_current_rset == m_rsets)
+ {
+ /* Assign the return value */
+ ed_result_set= m_current_rset;
+ /* Exclude the return value from the list. */
+ m_current_rset= m_rsets= m_rsets->m_next_rset;
+ }
+ else
+ {
+ Ed_result_set *prev_rset= m_rsets;
+ /* Assign the return value. */
+ ed_result_set= m_current_rset;
+
+ /* Exclude the return value from the list */
+ while (prev_rset->m_next_rset != m_current_rset)
+ prev_rset= ed_result_set->m_next_rset;
+ m_current_rset= prev_rset->m_next_rset= m_current_rset->m_next_rset;
+ }
+ ed_result_set->m_next_rset= NULL; /* safety */
+
+ return ed_result_set;
+}
+
+/*************************************************************************
+* Protocol_local
+**************************************************************************/
+
+Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
+ :Protocol(thd),
+ m_connection(ed_connection),
+ m_rset(NULL),
+ m_column_count(0),
+ m_current_row(NULL),
+ m_current_column(NULL)
+{
+ clear_alloc_root(&m_rset_root);
+}
+
+/**
+ Called between two result set rows.
+
+ Prepare structures to fill result set rows.
+ Unfortunately, we can't return an error here. If memory allocation
+ fails, we'll have to return an error later. And so is done
+ in methods such as @sa store_column().
+*/
+
+void Protocol_local::prepare_for_resend()
+{
+ DBUG_ASSERT(alloc_root_inited(&m_rset_root));
+
+ opt_add_row_to_rset();
+ /* Start a new row. */
+ m_current_row= (Ed_column *) alloc_root(&m_rset_root,
+ sizeof(Ed_column) * m_column_count);
+ m_current_column= m_current_row;
+}
+
+
+/**
+ In "real" protocols this is called to finish a result set row.
+ Unused in the local implementation.
+*/
+
+bool Protocol_local::write()
+{
+ return FALSE;
+}
+
+/**
+ A helper function to add the current row to the current result
+ set. Called in @sa prepare_for_resend(), when a new row is started,
+ and in send_eof(), when the result set is finished.
+*/
+
+void Protocol_local::opt_add_row_to_rset()
+{
+ if (m_current_row)
+ {
+ /* Add the old row to the result set */
+ Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
+ if (ed_row)
+ m_rset->push_back(ed_row, &m_rset_root);
+ }
+}
+
+
+/**
+ Add a NULL column to the current row.
+*/
+
+bool Protocol_local::store_null()
+{
+ if (m_current_column == NULL)
+ return TRUE; /* prepare_for_resend() failed to allocate memory. */
+
+ bzero(m_current_column, sizeof(*m_current_column));
+ ++m_current_column;
+ return FALSE;
+}
+
+
+/**
+ A helper method to add any column to the current row
+ in its binary form.
+
+ Allocates memory for the data in the result set memory root.
+*/
+
+bool Protocol_local::store_column(const void *data, size_t length)
+{
+ if (m_current_column == NULL)
+ return TRUE; /* prepare_for_resend() failed to allocate memory. */
+ /*
+ alloc_root() automatically aligns memory, so we don't need to
+ do any extra alignment if we're pointing to, say, an integer.
+ */
+ m_current_column->str= (char*) memdup_root(&m_rset_root,
+ data,
+ length + 1 /* Safety */);
+ if (! m_current_column->str)
+ return TRUE;
+ m_current_column->str[length]= '\0'; /* Safety */
+ m_current_column->length= length;
+ ++m_current_column;
+ return FALSE;
+}
+
+
+/**
+ Store a string value in a result set column, optionally
+ having converted it to character_set_results.
+*/
+
+bool
+Protocol_local::store_string(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+{
+ /* Store with conversion */
+ uint error_unused;
+
+ if (dst_cs && !my_charset_same(src_cs, dst_cs) &&
+ src_cs != &my_charset_bin &&
+ dst_cs != &my_charset_bin)
+ {
+ if (convert->copy(str, length, src_cs, dst_cs, &error_unused))
+ return TRUE;
+ str= convert->ptr();
+ length= convert->length();
+ }
+ return store_column(str, length);
+}
+
+
+/** Store a tiny int as is (1 byte) in a result set column. */
+
+bool Protocol_local::store_tiny(longlong value)
+{
+ char v= (char) value;
+ return store_column(&v, 1);
+}
+
+
+/** Store a short as is (2 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_short(longlong value)
+{
+ int16 v= (int16) value;
+ return store_column(&v, 2);
+}
+
+
+/** Store a "long" as is (4 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_long(longlong value)
+{
+ int32 v= (int32) value;
+ return store_column(&v, 4);
+}
+
+
+/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
+{
+ int64 v= (int64) value;
+ return store_column(&v, 8);
+}
+
+
+/** Store a decimal in string format in a result set column */
+
+bool Protocol_local::store_decimal(const my_decimal *value)
+{
+ char buf[DECIMAL_MAX_STR_LENGTH];
+ String str(buf, sizeof (buf), &my_charset_bin);
+ int rc;
+
+ rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str);
+
+ if (rc)
+ return TRUE;
+
+ return store_column(str.ptr(), str.length());
+}
+
+
+/** Convert to cs_results and store a string. */
+
+bool Protocol_local::store(const char *str, size_t length,
+ CHARSET_INFO *src_cs)
+{
+ CHARSET_INFO *dst_cs;
+
+ dst_cs= m_connection->m_thd->variables.character_set_results;
+ return store_string(str, length, src_cs, dst_cs);
+}
+
+
+/** Store a string. */
+
+bool Protocol_local::store(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+{
+ return store_string(str, length, src_cs, dst_cs);
+}
+
+
+/* Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store(MYSQL_TIME *time)
+{
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/** Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store_date(MYSQL_TIME *time)
+{
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/** Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store_time(MYSQL_TIME *time)
+{
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/* Store a floating point number, as is. */
+
+bool Protocol_local::store(float value, uint32 decimals, String *buffer)
+{
+ return store_column(&value, sizeof(float));
+}
+
+
+/* Store a double precision number, as is. */
+
+bool Protocol_local::store(double value, uint32 decimals, String *buffer)
+{
+ return store_column(&value, sizeof (double));
+}
+
+
+/* Store a Field. */
+
+bool Protocol_local::store(Field *field)
+{
+ if (field->is_null())
+ return store_null();
+ return field->send_binary(this);
+}
+
+
+/** Called to start a new result set. */
+
+bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
+{
+ DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
+
+ init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0);
+
+ if (! (m_rset= new (&m_rset_root) List<Ed_row>))
+ return TRUE;
+
+ m_column_count= columns->elements;
+
+ return FALSE;
+}
+
+
+/**
+ Normally this is a separate result set with OUT parameters
+ of stored procedures. Currently unsupported for the local
+ version.
+*/
+
+bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
+{
+ return FALSE;
+}
+
+
+/** Called for statements that don't have a result set, at statement end. */
+
+bool
+Protocol_local::send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message)
+{
+ /*
+ Just make sure nothing is sent to the client, we have grabbed
+ the status information in the connection diagnostics area.
+ */
+ return FALSE;
+}
+
+
+/**
+ Called at the end of a result set. Append a complete
+ result set to the list in Ed_connection.
+
+ Don't send anything to the client, but instead finish
+ building of the result set at hand.
+*/
+
+bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
+{
+ Ed_result_set *ed_result_set;
+
+ DBUG_ASSERT(m_rset);
+
+ opt_add_row_to_rset();
+ m_current_row= 0;
+
+ ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
+ &m_rset_root);
+
+ m_rset= NULL;
+
+ if (! ed_result_set)
+ return TRUE;
+
+ /* In case of successful allocation memory ownership was transferred. */
+ DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
+
+ /*
+ Link the created Ed_result_set instance into the list of connection
+ result sets. Never fails.
+ */
+ m_connection->add_result_set(ed_result_set);
+ return FALSE;
+}
+
+
+/** Called to send an error to the client at the end of a statement. */
+
+bool
+Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
+{
+ /*
+ Just make sure that nothing is sent to the client (default
+ implementation).
+ */
+ return FALSE;
+}
+
+
+#ifdef EMBEDDED_LIBRARY
+void Protocol_local::remove_last_row()
+{ }
+#endif
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
new file mode 100644
index 00000000000..11017b127b1
--- /dev/null
+++ b/sql/sql_prepare.h
@@ -0,0 +1,367 @@
+#ifndef SQL_PREPARE_H
+#define SQL_PREPARE_H
+/* Copyright (C) 1995-2008 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; version 2 of the License.
+
+ 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 */
+
+#include "sql_error.h"
+
+class THD;
+struct LEX;
+
+/**
+ An interface that is used to take an action when
+ the locking module notices that a table version has changed
+ since the last execution. "Table" here may refer to any kind of
+ table -- a base table, a temporary table, a view or an
+ information schema table.
+
+ When we open and lock tables for execution of a prepared
+ statement, we must verify that they did not change
+ since statement prepare. If some table did change, the statement
+ parse tree *may* be no longer valid, e.g. in case it contains
+ optimizations that depend on table metadata.
+
+ This class provides an interface (a method) that is
+ invoked when such a situation takes place.
+ The implementation of the method simply reports an error, but
+ the exact details depend on the nature of the SQL statement.
+
+ At most 1 instance of this class is active at a time, in which
+ case THD::m_reprepare_observer is not NULL.
+
+ @sa check_and_update_table_version() for details of the
+ version tracking algorithm
+
+ @sa Open_tables_state::m_reprepare_observer for the life cycle
+ of metadata observers.
+*/
+
+class Reprepare_observer
+{
+public:
+ /**
+ Check if a change of metadata is OK. In future
+ the signature of this method may be extended to accept the old
+ and the new versions, but since currently the check is very
+ simple, we only need the THD to report an error.
+ */
+ bool report_error(THD *thd);
+ bool is_invalidated() const { return m_invalidated; }
+ void reset_reprepare_observer() { m_invalidated= FALSE; }
+private:
+ bool m_invalidated;
+};
+
+
+void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
+void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
+void mysqld_stmt_close(THD *thd, char *packet);
+void mysql_sql_stmt_prepare(THD *thd);
+void mysql_sql_stmt_execute(THD *thd);
+void mysql_sql_stmt_close(THD *thd);
+void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
+void mysqld_stmt_reset(THD *thd, char *packet);
+void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
+void reinit_stmt_before_use(THD *thd, LEX *lex);
+
+/**
+ Execute a fragment of server code in an isolated context, so that
+ it doesn't leave any effect on THD. THD must have no open tables.
+ The code must not leave any open tables around.
+ The result of execution (if any) is stored in Ed_result.
+*/
+
+class Server_runnable
+{
+public:
+ virtual bool execute_server_code(THD *thd)= 0;
+ virtual ~Server_runnable();
+};
+
+
+/**
+ Execute direct interface.
+
+ @todo Implement support for prelocked mode.
+*/
+
+class Ed_row;
+
+/**
+ Ed_result_set -- a container with result set rows.
+ @todo Implement support for result set metadata and
+ automatic type conversion.
+*/
+
+class Ed_result_set: public Sql_alloc
+{
+public:
+ operator List<Ed_row>&() { return *m_rows; }
+ unsigned int size() const { return m_rows->elements; }
+
+ Ed_result_set(List<Ed_row> *rows_arg, size_t column_count,
+ MEM_ROOT *mem_root_arg);
+
+ /** We don't call member destructors, they all are POD types. */
+ ~Ed_result_set() {}
+
+ size_t get_field_count() const { return m_column_count; }
+
+ static void operator delete(void *ptr, size_t size) throw ();
+private:
+ Ed_result_set(const Ed_result_set &); /* not implemented */
+ Ed_result_set &operator=(Ed_result_set &); /* not implemented */
+private:
+ MEM_ROOT m_mem_root;
+ size_t m_column_count;
+ List<Ed_row> *m_rows;
+ Ed_result_set *m_next_rset;
+ friend class Ed_connection;
+};
+
+
+class Ed_connection
+{
+public:
+ /**
+ Construct a new "execute direct" connection.
+
+ The connection can be used to execute SQL statements.
+ If the connection failed to initialize, the error
+ will be returned on the attempt to execute a statement.
+
+ @pre thd must have no open tables
+ while the connection is used. However,
+ Ed_connection works okay in LOCK TABLES mode.
+ Other properties of THD, such as the current warning
+ information, errors, etc. do not matter and are
+ preserved by Ed_connection. One thread may have many
+ Ed_connections created for it.
+ */
+ Ed_connection(THD *thd);
+
+ /**
+ Execute one SQL statement.
+
+ Until this method is executed, no other methods of
+ Ed_connection can be used. Life cycle of Ed_connection is:
+
+ Initialized -> a statement has been executed ->
+ look at result, move to next result ->
+ look at result, move to next result ->
+ ...
+ moved beyond the last result == Initialized.
+
+ This method can be called repeatedly. Once it's invoked,
+ results of the previous execution are lost.
+
+ A result of execute_direct() can be either:
+
+ - success, no result set rows. In this case get_field_count()
+ returns 0. This happens after execution of INSERT, UPDATE,
+ DELETE, DROP and similar statements. Some other methods, such
+ as get_affected_rows() can be used to retrieve additional
+ result information.
+
+ - success, there are some result set rows (maybe 0). E.g.
+ happens after SELECT. In this case get_field_count() returns
+ the number of columns in a result set and store_result()
+ can be used to retrieve a result set..
+
+ - an error, methods to retrieve error information can
+ be used.
+
+ @return execution status
+ @retval FALSE success, use get_field_count()
+ to determine what to do next.
+ @retval TRUE error, use get_last_error()
+ to see the error number.
+ */
+ bool execute_direct(LEX_STRING sql_text);
+
+ /**
+ Same as the previous, but takes an instance of Server_runnable
+ instead of SQL statement text.
+
+ @return execution status
+
+ @retval FALSE success, use get_field_count()
+ if your code fragment is supposed to
+ return a result set
+ @retval TRUE failure
+ */
+ bool execute_direct(Server_runnable *server_runnable);
+
+ /**
+ Get the number of result set fields.
+
+ This method is valid only if we have a result:
+ execute_direct() has been called. Otherwise
+ the returned value is undefined.
+
+ @sa Documentation for C API function
+ mysql_field_count()
+ */
+ ulong get_field_count() const
+ {
+ return m_current_rset ? m_current_rset->get_field_count() : 0;
+ }
+
+ /**
+ Get the number of affected (deleted, updated)
+ rows for the current statement. Can be
+ used for statements with get_field_count() == 0.
+
+ @sa Documentation for C API function
+ mysql_affected_rows().
+ */
+ ulonglong get_affected_rows() const
+ {
+ return m_diagnostics_area.affected_rows();
+ }
+
+ /**
+ Get the last insert id, if any.
+
+ @sa Documentation for mysql_insert_id().
+ */
+ ulonglong get_last_insert_id() const
+ {
+ return m_diagnostics_area.last_insert_id();
+ }
+
+ /**
+ Get the total number of warnings for the last executed
+ statement. Note, that there is only one warning list even
+ if a statement returns multiple results.
+
+ @sa Documentation for C API function
+ mysql_num_warnings().
+ */
+ ulong get_warn_count() const
+ {
+ return m_warning_info.warn_count();
+ }
+ /**
+ Get the server warnings as a result set.
+ The result set has fixed metadata:
+ The first column is the level.
+ The second is a numeric code.
+ The third is warning text.
+ */
+ List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); }
+ /**
+ The following members are only valid if execute_direct()
+ or move_to_next_result() returned an error.
+ They never fail, but if they are called when there is no
+ result, or no error, the result is not defined.
+ */
+ const char *get_last_error() const { return m_diagnostics_area.message(); }
+ unsigned int get_last_errno() const { return m_diagnostics_area.sql_errno(); }
+ const char *get_last_sqlstate() const { return m_diagnostics_area.get_sqlstate(); }
+
+ /**
+ Provided get_field_count() is not 0, this never fails. You don't
+ need to free the result set, this is done automatically when
+ you advance to the next result set or destroy the connection.
+ Not returning const because of List iterator not accepting
+ Should be used when you would like Ed_connection to manage
+ result set memory for you.
+ */
+ Ed_result_set *use_result_set() { return m_current_rset; }
+ /**
+ Provided get_field_count() is not 0, this never fails. You
+ must free the returned result set. This can be called only
+ once after execute_direct().
+ Should be used when you would like to get the results
+ and destroy the connection.
+ */
+ Ed_result_set *store_result_set();
+
+ /**
+ If the query returns multiple results, this method
+ can be checked if there is another result beyond the next
+ one.
+ Never fails.
+ */
+ bool has_next_result() const { return test(m_current_rset->m_next_rset); }
+ /**
+ Only valid to call if has_next_result() returned true.
+ Otherwise the result is undefined.
+ */
+ bool move_to_next_result()
+ {
+ m_current_rset= m_current_rset->m_next_rset;
+ return test(m_current_rset);
+ }
+
+ ~Ed_connection() { free_old_result(); }
+private:
+ Diagnostics_area m_diagnostics_area;
+ Warning_info m_warning_info;
+ /**
+ Execute direct interface does not support multi-statements, only
+ multi-results. So we never have a situation when we have
+ a mix of result sets and OK or error packets. We either
+ have a single result set, a single error, or a single OK,
+ or we have a series of result sets, followed by an OK or error.
+ */
+ THD *m_thd;
+ Ed_result_set *m_rsets;
+ Ed_result_set *m_current_rset;
+ friend class Protocol_local;
+private:
+ void free_old_result();
+ void add_result_set(Ed_result_set *ed_result_set);
+private:
+ Ed_connection(const Ed_connection &); /* not implemented */
+ Ed_connection &operator=(Ed_connection &); /* not implemented */
+};
+
+
+/** One result set column. */
+
+struct Ed_column: public LEX_STRING
+{
+ /** Implementation note: destructor for this class is never called. */
+};
+
+
+/** One result set record. */
+
+class Ed_row: public Sql_alloc
+{
+public:
+ const Ed_column &operator[](const unsigned int column_index) const
+ {
+ return *get_column(column_index);
+ }
+ const Ed_column *get_column(const unsigned int column_index) const
+ {
+ DBUG_ASSERT(column_index < size());
+ return m_column_array + column_index;
+ }
+ size_t size() const { return m_column_count; }
+
+ Ed_row(Ed_column *column_array_arg, size_t column_count_arg)
+ :m_column_array(column_array_arg),
+ m_column_count(column_count_arg)
+ {}
+private:
+ Ed_column *m_column_array;
+ size_t m_column_count; /* TODO: change to point to metadata */
+};
+
+#endif // SQL_PREPARE_H
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 3801d9a3f8d..69e5bc3cbb4 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -415,7 +415,7 @@ bool PROFILING::show_profiles()
MYSQL_TYPE_DOUBLE));
field_list.push_back(new Item_empty_string("Query", 40));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index b6ae8860cf5..496fa290428 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1404,7 +1404,7 @@ bool mysql_show_binlog_events(THD* thd)
DBUG_ENTER("mysql_show_binlog_events");
Log_event::init_show_field_list(&field_list);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -1563,7 +1563,7 @@ bool show_binlog_info(THD* thd)
field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -1615,7 +1615,7 @@ bool show_binlogs(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 255));
field_list.push_back(new Item_return_int("File_size", 20,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 9e94bd59bae..ef2ff8ff02a 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1680,8 +1680,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- if (result->send_fields(*columns_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (result->send_result_set_metadata(*columns_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_VOID_RETURN;
}
@@ -2081,7 +2081,7 @@ JOIN::exec()
}
if (curr_join->group_list || curr_join->order)
{
- DBUG_PRINT("info",("Sorting for send_fields"));
+ DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
thd_proc_info(thd, "Sorting result");
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
@@ -2221,7 +2221,7 @@ JOIN::exec()
{
thd_proc_info(thd, "Sending data");
DBUG_PRINT("info", ("%s", thd->proc_info));
- result->send_fields((procedure ? curr_join->procedure_fields_list :
+ result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list :
*curr_fields_list),
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
@@ -7214,7 +7214,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,
+ if (!(result->send_result_set_metadata(fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
bool send_error= FALSE;
@@ -9736,7 +9736,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
Create a temp table according to a field list.
Given field pointers are changed to point at tmp_table for
- send_fields. The table object is self contained: it's
+ send_result_set_metadata. The table object is self contained: it's
allocated in its own memory root, as well as Field objects
created for table columns.
This function will replace Item_sum items in 'fields' list with
@@ -14988,7 +14988,7 @@ test_if_group_changed(List<Cached_item> &list)
Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
Change old item_field to use a new field with points at saved fieldvalue
- This function is only called before use of send_fields.
+ This function is only called before use of send_result_set_metadata.
@param thd THD pointer
@param param temporary table parameters
@@ -15228,7 +15228,7 @@ bool JOIN::alloc_func_list()
Initialize 'sum_funcs' array with all Item_sum objects.
@param field_list All items
- @param send_fields Items in select list
+ @param send_result_set_metadata Items in select list
@param before_group_by Set to 1 if this is called before GROUP BY handling
@param recompute Set to TRUE if sum_funcs must be recomputed
@@ -15238,7 +15238,7 @@ bool JOIN::alloc_func_list()
1 error
*/
-bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
+bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_result_set_metadata,
bool before_group_by, bool recompute)
{
List_iterator_fast<Item> it(field_list);
@@ -15260,7 +15260,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
{
rollup.state= ROLLUP::STATE_READY;
- if (rollup_make_fields(field_list, send_fields, &func))
+ if (rollup_make_fields(field_list, send_result_set_metadata, &func))
DBUG_RETURN(TRUE); // Should never happen
}
else if (rollup.state == ROLLUP::STATE_NONE)
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 9a2774735ad..79079007099 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -216,7 +216,7 @@ bool mysqld_show_authors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -250,7 +250,7 @@ bool mysqld_show_contributors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -326,7 +326,7 @@ bool mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Context",15));
field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -403,8 +403,8 @@ bool mysqld_show_column_types(THD *thd)
field_list.push_back(new Item_empty_string("Default",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
/* TODO: Change the loop to not use 'i' */
@@ -655,7 +655,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
max(buffer.length(),1024)));
}
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -740,7 +740,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Create Database",1024));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -808,7 +808,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
}
restore_record(table, s->default_values); // Get empty record
table->use_all_columns();
- if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
+ if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_DEFAULTS))
DBUG_VOID_RETURN;
my_eof(thd);
DBUG_VOID_RETURN;
@@ -1698,7 +1698,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
@@ -6905,7 +6905,7 @@ static bool show_create_trigger_impl(THD *thd,
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
return TRUE;
/* Send data. */
diff --git a/sql/sql_string.h b/sql/sql_string.h
index d62908e5d66..4bd6a9cf398 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -120,6 +120,11 @@ public:
(void) realloc(str_length);
return Ptr;
}
+ LEX_STRING lex_string() const
+ {
+ LEX_STRING lex_string = { (char*) ptr(), length() };
+ return lex_string;
+ }
void set(String &str,uint32 offset,uint32 arg_length)
{
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index e6f9bb9e5e3..f40c5acedab 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4524,7 +4524,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_text", 255));
item->maybe_null = 1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -7819,7 +7819,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
MY_INT64_NUM_DECIMAL_DIGITS));
item->maybe_null= 1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 01a66a3a98b..b0d81d6b480 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -922,7 +922,6 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
{
update_non_unique_table_error(table_list, "UPDATE", duplicate);
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
DBUG_RETURN(TRUE);
}
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6fd03b416a6..821b7164523 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1385,7 +1385,7 @@ query:
Lex_input_stream *lip = YYLIP;
if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) &&
- ! lip->stmt_prepare_mode &&
+ lip->multi_statements &&
! lip->eof())
{
/*