2768 Konstantin Osipov 2008-12-06
Manual merge with the trunk.
modified:
sql/si_objects.cc
2767 Konstantin Osipov 2008-12-06
WL#4264 "Backup: Stabilize Service Interface", the final part
of review fixes.
Change the way result set data is collected: implement Ed_connection,
execute direct connection.
modified:
sql/backup/backup_test.cc
sql/backup/kernel.cc
sql/protocol.cc
sql/protocol.h
sql/si_objects.cc
sql/sql_class.cc
sql/sql_class.h
sql/sql_error.cc
sql/sql_error.h
sql/sql_prepare.cc
sql/sql_prepare.h
2766 Magne Mahre 2008-12-05
Bug #38661 'all threads hang in "opening tables" or "waiting for table"
and cpu is at 100%'
Concurrent execution of FLUSH TABLES statement and at least two statements
using the same table might have led to live-lock which caused all three
connections to stall and hog 100% of CPU.
tdc_wait_for_old_versions() wrongly assumed that there cannot be a share
with an old version and no used TABLE instances and thus was failing to
perform wait in situation when such old share was cached in MDL subsystem
thanks to a still active metadata lock on the table. So it might have
happened that two or more connections simultaneously executing statements
which involve table being flushed managed to prevent each other from
waiting in this function by keeping shared metadata lock on the table
constantly active (i.e. one of the statements managed to take/hold this
lock while other statements were calling tdc_wait_for_old_versions()).
Thus they were forcing each other to loop infinitely in open_tables() -
close_thread_tables_for_reopen() - tdc_wait_for_old_versions() cycle
causing CPU hogging.
This patch fixes this problem by removing this false assumption from
tdc_wait_for_old_versions().
Note that the problem is specific only for server versions >= 6.0.
No test case is submitted for this test, as the test infrastructure
hasn't got the necessary primitives to test the behaviour. The
manifestation is that throughput will decrease to a low level
(possibly 0) after some time, and stay at that level. Several
transactions will not complete.
Manual testing can be done by running the code submitted by Shane
Bester attached to the bug report. If the bug persists, the
transaction thruput will almost immediately drop to near zero
(shown as the transaction count output from the test program staying
on a close to constant value, instead of increasing rapidly)
modified:
sql/sql_base.cc
=== modified file 'sql/backup/backup_test.cc'
--- a/sql/backup/backup_test.cc 2008-12-04 16:50:07 +0000
+++ b/sql/backup/backup_test.cc 2008-12-05 23:47:51 +0000
@@ -261,7 +261,6 @@ int execute_backup_test_command(THD *thd
}
}
}
- thd->stmt_da->reset_diagnostics_area();
my_eof(thd);
DBUG_RETURN(res);
}
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-12-04 23:14:30 +0000
+++ b/sql/backup/kernel.cc 2008-12-05 23:47:51 +0000
@@ -1263,15 +1263,6 @@ int Backup_restore_ctx::do_restore(bool
DBUG_PRINT("restore",("Restoring table data"));
- /*
- FIXME: this call is here because object services doesn't clean the
- statement execution context properly, which leads to assertion failure.
- It should be fixed inside object services implementation and then the
- following line should be removed.
- */
- close_thread_tables(m_thd); // Never errors
- m_thd->stmt_da->reset_diagnostics_area(); // Never errors
-
if (lock_tables_for_restore()) // logs errors
DBUG_RETURN(m_error);
@@ -1301,15 +1292,6 @@ int Backup_restore_ctx::do_restore(bool
DBUG_RETURN(m_error);
}
- /*
- FIXME: this call is here because object services doesn't clean the
- statement execution context properly, which leads to assertion failure.
- It should be fixed inside object services implementation and then the
- following line should be removed.
- */
- close_thread_tables(m_thd); // Never errors
- m_thd->stmt_da->reset_diagnostics_area(); // Never errors
-
/*
Report validity point time and binlog position stored in the backup image
(in the summary section).
=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc 2008-12-04 16:50:07 +0000
+++ b/sql/protocol.cc 2008-12-05 23:47:51 +0000
@@ -1475,476 +1475,3 @@ bool Protocol_binary::send_out_parameter
return FALSE;
}
-///////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////
-
-Ed_result::Ed_result() :
- m_current_result_set(NULL),
- m_status(Diagnostics_area::DA_EMPTY),
- m_server_status(0),
- m_affected_rows(0),
- m_last_insert_id(0),
- m_sql_errno(0),
- m_warning_info(0),
- m_warning_info_saved(NULL)
-{
- init_sql_alloc(&m_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
-
- m_message[0]= 0;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-Ed_result::~Ed_result()
-{
- free_root(&m_mem_root, MYF(0));
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_result::add_result_set(List<Item> *col_metadata)
-{
- Ed_result_set *rs= Ed_result_set::create(&m_mem_root, col_metadata);
-
- if (!rs)
- return TRUE;
-
- m_current_result_set= rs;
-
- return push_back(rs, &m_mem_root) ? TRUE : FALSE;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void Ed_result::send_ok(THD *thd,
- uint server_status, uint statement_warn_count,
- ha_rows affected_rows, ulonglong last_insert_id,
- const char *message)
-{
- DBUG_ENTER("Ed_result::send_ok()");
- DBUG_ASSERT(m_status == Diagnostics_area::DA_EMPTY);
-
- m_status= thd->stmt_da->status();
- DBUG_ASSERT(m_status == Diagnostics_area::DA_OK);
-
- DBUG_ASSERT(m_warning_info.statement_warn_count() == statement_warn_count);
-
- m_server_status= server_status;
- m_affected_rows= affected_rows;
- m_last_insert_id= last_insert_id;
-
- strmake(m_message, message, sizeof (m_message) - 1);
-
- DBUG_VOID_RETURN;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void Ed_result::send_eof(THD *thd, uint server_status,
- uint statement_warn_count)
-{
- DBUG_ENTER("Ed_result::send_eof");
- DBUG_ASSERT(m_status == Diagnostics_area::DA_EMPTY);
-
- m_status= thd->stmt_da->status();
- DBUG_ASSERT(m_status == Diagnostics_area::DA_EOF);
-
- DBUG_ASSERT(m_warning_info.statement_warn_count() == statement_warn_count);
-
- m_server_status= server_status;
-
- DBUG_VOID_RETURN;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void Ed_result::send_error(THD *thd, uint sql_errno, const char *err_msg)
-{
- DBUG_ENTER("Ed_result::send_error()");
- DBUG_ASSERT(m_status == Diagnostics_area::DA_EMPTY);
-
- m_status= thd->stmt_da->status();
- DBUG_ASSERT(m_status == Diagnostics_area::DA_ERROR);
-
- m_sql_errno= sql_errno;
- strmake(m_message, err_msg, sizeof (m_message) - 1);
-
- DBUG_VOID_RETURN;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void Ed_result::begin_statement(THD *thd)
-{
- DBUG_ASSERT(!m_warning_info_saved);
-
- m_warning_info_saved= thd->warning_info;
- thd->warning_info= &m_warning_info;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void Ed_result::end_statement(THD *thd)
-{
- DBUG_ASSERT(m_warning_info_saved);
- thd->warning_info= m_warning_info_saved;
-}
-
-///////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////
-
-Ed_result_set *
-Ed_result_set::create(MEM_ROOT *mem_root, List<Item> *col_metadata)
-{
- Ed_result_set *rs= new (mem_root) Ed_result_set(mem_root);
-
- if (!rs || rs->init(col_metadata))
- return NULL;
-
- return rs;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_result_set::init(List<Item> *col_metadata)
-{
- m_metadata= Ed_result_set_metadata::create(m_mem_root, col_metadata);
-
- return m_metadata == NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-Ed_row *Ed_result_set::add_row()
-{
- Ed_row *row= Ed_row::create(m_mem_root, m_metadata);
-
- if (!row)
- return NULL;
-
- m_current_row= row;
-
- return m_data.push_back(row, m_mem_root) ? NULL : row;
-}
-
-///////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////
-
-Ed_result_set_metadata *
-Ed_result_set_metadata::create(MEM_ROOT *mem_root,
- List<Item> *col_metadata)
-{
- Ed_result_set_metadata *md= new (mem_root) Ed_result_set_metadata();
-
- if (!md || md->init(mem_root, col_metadata))
- return NULL;
-
- return md;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_result_set_metadata::init(MEM_ROOT *mem_root, List<Item> *col_metadata)
-{
- if (!col_metadata)
- return FALSE;
-
- m_metadata= new (mem_root) Send_field[col_metadata->elements];
-
- if (!m_metadata)
- return TRUE;
-
- m_num_columns= col_metadata->elements;
-
- List_iterator_fast<Item> it(*col_metadata);
-
- int i= 0;
- for (Item *column= it++; column; column= it++)
- column->make_field(&m_metadata[i]);
-
- return FALSE;
-}
-
-///////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////
-
-Ed_row *Ed_row::create(MEM_ROOT *mem_root,
- const Ed_result_set_metadata *metadata)
-{
- DBUG_ASSERT(metadata);
-
- Ed_row *row= new (mem_root) Ed_row(mem_root, metadata);
-
- if (!row || row->init())
- return NULL;
-
- return row;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_row::init()
-{
- m_columns= new (m_mem_root) Ed_column[m_metadata->get_num_columns()];
-
- return m_columns == NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_row::add_null()
-{
- if (m_current_column_index >= m_metadata->get_num_columns())
- return TRUE;
-
- ++m_current_column_index;
-
- return FALSE;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-bool Ed_row::add_column(const void *data_ptr, int data_length)
-{
- if (m_current_column_index >= m_metadata->get_num_columns())
- return TRUE;
-
- m_columns[m_current_column_index++].set_data(m_mem_root,
- data_ptr, data_length);
-
- return FALSE;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-// Protocol_local: a protocol for retrieving result sets from the server
-// locally.
-//
-///////////////////////////////////////////////////////////////////////////
-
-void Protocol_local::prepare_for_resend()
-{
- DBUG_ASSERT(m_result);
-
- Ed_result_set *rs= m_result->get_cur_result_set();
-
- DBUG_ASSERT(rs);
-
- rs->add_row();
-}
-
-bool Protocol_local::write()
-{
- return FALSE;
-}
-
-bool Protocol_local::store_null()
-{
- DBUG_ASSERT(m_result);
-
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
- DBUG_ASSERT(row);
-
- return row->add_null();
-}
-
-bool Protocol_local::store_string(const char *str,
- int length,
- CHARSET_INFO *src_cs,
- CHARSET_INFO *dst_cs)
-{
- DBUG_ASSERT(m_result);
-
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
- DBUG_ASSERT(row);
-
- /* 'dst_cs' is set 0 when client issued SET character_set_results = NULL */
-
- if (!dst_cs ||
- my_charset_same(src_cs, dst_cs) ||
- src_cs == &my_charset_bin ||
- dst_cs == &my_charset_bin)
- {
- return row->add_column(str, length);
- }
-
- /* Store with conversion */
- uint dummy_errors;
-
- if (convert->copy(str, length, src_cs, dst_cs, &dummy_errors))
- return TRUE;
-
- return row->add_column(convert->ptr(), convert->length());
-}
-
-bool Protocol_local::store_tiny(longlong value)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- char v= (char) value;
-
- return row->add_column(&v, 1);
-}
-
-bool Protocol_local::store_short(longlong value)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- int16 v= (int16) value;
-
- return row->add_column(&v, 2);
-}
-
-bool Protocol_local::store_long(longlong value)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- int32 v= (int32) value;
-
- return row->add_column(&v, 4);
-}
-
-bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- int64 v= (int64) value;
-
- return row->add_column(&v, 8);
-}
-
-bool Protocol_local::store_decimal(const my_decimal *value)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- char buf[DECIMAL_MAX_STR_LENGTH];
- String str(buf, sizeof (buf), &my_charset_bin);
- my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str);
-
- return row->add_column(str.ptr(), str.length());
-}
-
-bool Protocol_local::store(const char *str,
- size_t length,
- CHARSET_INFO *src_cs)
-{
- CHARSET_INFO *dst_cs= this->thd->variables.character_set_results;
- return store_string(str, length, src_cs, dst_cs);
-}
-
-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);
-}
-
-bool Protocol_local::store(MYSQL_TIME *time)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- return row->add_column(time, sizeof (MYSQL_TIME));
-}
-
-bool Protocol_local::store_date(MYSQL_TIME *time)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- return row->add_column(time, sizeof (MYSQL_TIME));
-}
-
-bool Protocol_local::store_time(MYSQL_TIME *time)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- return row->add_column(time, sizeof (MYSQL_TIME));
-}
-
-bool Protocol_local::store(float value, uint32 decimals, String *buffer)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- return row->add_column(&value, sizeof (float));
-}
-
-bool Protocol_local::store(double value, uint32 decimals, String *buffer)
-{
- Ed_result_set *rs= m_result->get_cur_result_set();
- Ed_row *row= rs->get_cur_row();
-
- /* TODO: check medata type. */
-
- return row->add_column(&value, sizeof (double));
-}
-
-bool Protocol_local::store(Field *field)
-{
- if (field->is_null())
- return store_null();
- return field->send_binary(this);
-}
-
-bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
-{
- DBUG_ASSERT(m_result);
-
- return m_result->add_result_set(columns);
-}
-
-bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
-{
- return FALSE;
-}
-
-void Protocol_local::send_ok(uint server_status, uint statement_warn_count,
- ha_rows affected_rows, ulonglong last_insert_id,
- const char *message)
-{
- m_result->send_ok(thd, server_status, statement_warn_count,
- affected_rows, last_insert_id, message);
-}
-
-void Protocol_local::send_eof(uint server_status, uint statement_warn_count)
-{
- m_result->send_eof(thd, server_status, statement_warn_count);
-}
-
-void Protocol_local::send_error(uint sql_errno, const char *err_msg)
-{
- m_result->send_error(thd, sql_errno, err_msg);
-}
-
-#ifdef EMBEDDED_LIBRARY
-void Protocol_local::remove_last_row()
-{ }
-#endif
=== modified file 'sql/protocol.h'
--- a/sql/protocol.h 2008-11-27 18:31:59 +0000
+++ b/sql/protocol.h 2008-12-05 23:47:51 +0000
@@ -204,264 +204,3 @@ uchar *net_store_data(uchar *to,longlong
///////////////////////////////////////////////////////////////////////////
-/**
- Protocol_local: a protocol to retrieve and store
- a result set.
-*/
-
-class Ed_result;
-
-class Protocol_local :public Protocol
-{
-public:
- inline Protocol_local(THD *thd, Ed_result *result)
- :Protocol(thd), m_result(result)
- {}
-
-public:
- 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; };
-
-protected:
- virtual void send_ok(uint server_status, uint statement_warn_count,
- ha_rows affected_rows, ulonglong last_insert_id,
- const char *message);
-
- virtual void send_eof(uint server_status, uint statement_warn_count);
-
- virtual void send_error(uint sql_errno, const char *err_msg);
-
-private:
- bool store_string(const char *str, int length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
-
-private:
- Ed_result *m_result;
-};
-
-
-/**
- Ed_column -- a class representing a column data in a row. Used with
- Ed_row and Protocol_local.
-*/
-
-class Ed_column : public LEX_STRING, public Sql_alloc
-{
-public:
- inline Ed_column()
- {
- str= NULL;
- length= 0;
- }
-
- inline void set_data(MEM_ROOT *mem_root, const void *p_str, int p_length)
- {
- str= (char *) memdup_root(mem_root, p_str, p_length);
- length= p_length;
- }
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-/**
- Ed_result_set_metadata -- a class representing result set metadata. Used
- with Ed_result_set and Protocol_local.
-*/
-
-class Ed_result_set_metadata : public Sql_alloc
-{
-public:
- static Ed_result_set_metadata *create(MEM_ROOT *mem_root,
- List<Item> *col_metadata);
-
-public:
- inline int get_num_columns() const { return m_num_columns; }
-
- inline const Send_field *get_column(int idx) const
- { return &m_metadata[idx]; }
-
-private:
- inline Ed_result_set_metadata() :
- m_num_columns(0),
- m_metadata(NULL)
- { }
-
-private:
- bool init(MEM_ROOT *mem_root, List<Item> *col_metadata);
-
-private:
- int m_num_columns;
- Send_field *m_metadata;
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-/**
- Ed_row -- a class representing a row in a result set. Used with
- Ed_result_set and Protocol_local.
-*/
-class Ed_row : public Sql_alloc
-{
-public:
- static Ed_row *create(MEM_ROOT *mem_root,
- const Ed_result_set_metadata *metadata);
-
-public:
- bool add_null();
- bool add_column(const void *data_ptr, int data_length);
-
-public:
- inline const Ed_result_set_metadata *get_metadata() const
- { return m_metadata; }
-
- inline const Ed_column *get_column(int idx) const
- { return &m_columns[idx]; }
-
- inline const Ed_column &operator [](int idx) const
- { return *get_column(idx); }
-
- inline int get_current_column_index() const
- { return m_current_column_index; }
-
-private:
- inline Ed_row(MEM_ROOT *mem_root,
- const Ed_result_set_metadata *metadata) :
- m_mem_root(mem_root),
- m_metadata(metadata),
- m_current_column_index(0)
- { }
-
- bool init();
-
-private:
- MEM_ROOT *m_mem_root;
- const Ed_result_set_metadata *m_metadata;
- Ed_column *m_columns;
- int m_current_column_index;
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-/**
- Ed_result_set -- a class representing one result set. Used with Ed_result
- and Protocol_local.
-*/
-class Ed_result_set : public Sql_alloc
-{
-public:
- static Ed_result_set *create(MEM_ROOT *mem_root,
- List<Item> *col_metadata);
-
-private:
- inline Ed_result_set(MEM_ROOT *mem_root) :
- m_mem_root(mem_root),
- m_metadata(NULL),
- m_current_row(NULL)
- { }
-
-private:
- bool init(List<Item> *col_metadata);
-
-public:
- inline const Ed_result_set_metadata *get_metadata() const
- { return m_metadata; }
-
- inline List<Ed_row> *data()
- { return &m_data; }
-
- inline Ed_row *get_cur_row()
- { return m_current_row; }
-
- Ed_row *add_row();
-
-private:
- MEM_ROOT *m_mem_root;
-
- Ed_result_set_metadata *m_metadata;
- List<Ed_row> m_data;
-
- Ed_row *m_current_row;
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-/*
- Ed_result -- a class representing results for an SQL statement execution.
- Used with Protocol_local.
-*/
-class Ed_result : public List<Ed_result_set>
-{
-public:
- Ed_result();
- ~Ed_result();
-
-public:
- inline Ed_result_set *get_cur_result_set()
- { return m_current_result_set; }
-
- bool add_result_set(List<Item> *col_metadata);
-
-public:
- void send_ok(THD *thd, uint server_status, uint statement_warn_count,
- ha_rows affected_rows, ulonglong last_insert_id,
- const char *message);
-
- void send_eof(THD *thd, uint server_status, uint statement_warn_count);
-
- void send_error(THD *thd, uint sql_errno, const char *err_msg);
-
- void begin_statement(THD *thd);
- void end_statement(THD *thd);
-
-public:
- inline uint get_status() const { return m_status; }
- inline uint get_server_status() const { return m_server_status; }
- inline ha_rows get_affected_rows() const { return m_affected_rows; }
- inline ulonglong get_last_insert_id() const { return m_last_insert_id; }
- inline uint get_sql_errno() const { return m_sql_errno; }
- inline const char *get_message() const { return m_message; }
-
- inline uint get_statement_warn_count() const
- { return m_warning_info.statement_warn_count(); }
-
- inline List<MYSQL_ERROR> &get_warnings()
- { return m_warning_info.warn_list(); }
-
-private:
- MEM_ROOT m_mem_root;
- Ed_result_set *m_current_result_set;
-
-private:
- uint m_status;
- uint m_server_status;
- ha_rows m_affected_rows;
- ulonglong m_last_insert_id;
- uint m_sql_errno;
- char m_message[MYSQL_ERRMSG_SIZE];
-
- Warning_info m_warning_info;
- Warning_info *m_warning_info_saved;
-};
-
=== modified file 'sql/si_objects.cc'
--- a/sql/si_objects.cc 2008-12-04 23:14:30 +0000
+++ b/sql/si_objects.cc 2008-12-06 00:02:44 +0000
@@ -176,8 +176,8 @@ void Si_session_context::restore_si_ctx(
*/
bool
-run_service_interface_sql(THD *thd, const LEX_STRING *query,
- Ed_result *ed_result)
+run_service_interface_sql(THD *thd, Ed_connection *ed_connection,
+ const LEX_STRING *query)
{
Si_session_context session_context;
@@ -189,7 +189,7 @@ run_service_interface_sql(THD *thd, cons
session_context.save_si_ctx(thd);
session_context.reset_si_ctx(thd);
- bool rc= mysql_execute_direct(thd, *query, ed_result);
+ bool rc= ed_connection->execute_direct(*query);
session_context.restore_si_ctx(thd);
@@ -769,12 +769,12 @@ bool Abstract_obj::create(THD *thd)
/* Run queries from the serialization image. */
while ((sql_text= it++))
{
- Ed_result ed_result;
+ Ed_connection ed_connection(thd);
- rc= mysql_execute_direct(thd, *sql_text, &ed_result);
+ rc= ed_connection.execute_direct(*sql_text);
/* Push warnings on the THD error stack. */
- copy_warnings(thd, &ed_result.get_warnings());
+ copy_warnings(thd, ed_connection.get_warn_list());
if (rc)
break;
@@ -804,6 +804,8 @@ bool Abstract_obj::drop(THD *thd)
{
String_stream s_stream;
const LEX_STRING *sql_text;
+ Ed_connection ed_connection(thd);
+ bool rc;
DBUG_ENTER("Abstract_obj::drop");
@@ -821,10 +823,8 @@ bool Abstract_obj::drop(THD *thd)
/* Allow to execute DDL operations. */
::obs::ddl_blocker_exception_on(thd);
- Ed_result ed_result;
-
/* Execute DDL operation. */
- bool rc= mysql_execute_direct(thd, *sql_text, &ed_result);
+ rc= ed_connection.execute_direct(*sql_text);
/* Disable further DDL execution. */
::obs::ddl_blocker_exception_off(thd);
@@ -1281,27 +1281,25 @@ private:
template <typename Iterator>
Iterator *create_row_set_iterator(THD *thd, const LEX_STRING *query)
{
- Ed_result *ed_result= new Ed_result();
-
- if (!ed_result)
- return NULL;
+ Ed_connection ed_connection(thd);
+ Ed_result_set *ed_result_set;
+ Iterator *it;
- if (run_service_interface_sql(thd, query, ed_result) ||
- ed_result->get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, query) ||
+ ed_connection.get_warn_count())
{
/* There should be no warnings. */
- delete ed_result;
return NULL;
}
- /* The result must contain only one result-set. */
- DBUG_ASSERT(ed_result->elements == 1);
+ DBUG_ASSERT(ed_connection.get_field_count());
- Iterator *it= new Iterator(ed_result);
+ /* Use store_result to get ownership of result memory */
+ ed_result_set= ed_connection.store_result_set();
- if (!it)
+ if (! (it= new Iterator(ed_result_set)))
{
- delete ed_result;
+ delete ed_result_set;
return NULL;
}
@@ -1322,14 +1320,14 @@ template <typename Obj_type>
class Ed_result_set_iterator : public Obj_iterator
{
public:
- inline Ed_result_set_iterator(Ed_result *ed_result);
+ inline Ed_result_set_iterator(Ed_result_set *ed_result_set);
inline ~Ed_result_set_iterator();
public:
virtual Obj *next();
private:
- Ed_result *m_ed_result;
+ Ed_result_set *m_ed_result_set;
List_iterator_fast<Ed_row> m_row_it;
};
@@ -1338,9 +1336,9 @@ private:
template <typename Obj_type>
inline
Ed_result_set_iterator<Obj_type>::
-Ed_result_set_iterator(Ed_result *ed_result)
- : m_ed_result(ed_result),
- m_row_it(*ed_result->get_cur_result_set()->data())
+Ed_result_set_iterator(Ed_result_set *ed_result_set)
+ : m_ed_result_set(ed_result_set),
+ m_row_it(*ed_result_set)
{ }
///////////////////////////////////////////////////////////////////////////
@@ -1349,7 +1347,7 @@ template <typename Obj_type>
inline
Ed_result_set_iterator<Obj_type>::~Ed_result_set_iterator()
{
- delete m_ed_result;
+ delete m_ed_result_set;
}
@@ -1620,11 +1618,9 @@ bool View_base_obj_iterator::init(THD *t
{
Find_view_underlying_tables find_tables(this, &m_table_names,
db_name, view_name);
- Ed_result ed_result; /* Just to grab OK or ERROR */
-
- /* The table list is filled with unique underlying table names. */
+ Ed_connection ed_connection(thd); /* Just to grab OK or ERROR */
- return mysql_execute_direct(thd, &find_tables, &ed_result);
+ return ed_connection.execute_direct(&find_tables);
}
///////////////////////////////////////////////////////////////////////////
@@ -1736,6 +1732,9 @@ create<View_base_view_iterator>(THD *thd
bool Database_obj::do_serialize(THD *thd, Out_stream &out_stream)
{
+ Ed_connection ed_connection(thd);
+ Ed_result_set *ed_result_set;
+
DBUG_ENTER("Database_obj::do_serialize");
DBUG_PRINT("Database_obj::do_serialize",
("name: %.*s", STR(*get_name())));
@@ -1750,15 +1749,13 @@ bool Database_obj::do_serialize(THD *thd
/* Run 'SHOW CREATE' query. */
- Ed_result ed_result;
-
{
String_stream s_stream;
s_stream <<
"SHOW CREATE DATABASE `" << get_name() << "`";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/*
There should be no warnings. A warning means that serialization has
@@ -1771,27 +1768,17 @@ bool Database_obj::do_serialize(THD *thd
/* Check result. */
/* The result must contain only one result-set... */
- DBUG_ASSERT(ed_result.elements == 1);
-
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ DBUG_ASSERT(ed_connection.get_field_count());
- /* ... which is not NULL. */
- DBUG_ASSERT(ed_result_set);
+ ed_result_set= ed_connection.use_result_set();
- if (ed_result_set->data()->elements == 0)
+ if (ed_result_set->size() != 1)
DBUG_RETURN(TRUE);
- /* There must be one row. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
-
- List_iterator_fast<Ed_row> row_it(*ed_result_set->data());
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
Ed_row *row= row_it++;
- /* There must be two columns: database name and create statement. */
- DBUG_ASSERT(row->get_metadata()->get_num_columns() == 2);
-
/* Generate image. */
-
out_stream << row->get_column(1);
DBUG_RETURN(FALSE);
@@ -1824,19 +1811,20 @@ void Database_obj::build_drop_statement(
bool Table_obj::do_serialize(THD *thd, Out_stream &out_stream)
{
+ Ed_connection ed_connection(thd);
+ String_stream s_stream;
+ Ed_result_set *ed_result_set;
+
DBUG_ENTER("Table_obj::do_serialize");
DBUG_PRINT("Table_obj::do_serialize",
("name: %.*s.%.*s",
STR(m_db_name), STR(m_id)));
- Ed_result ed_result;
- String_stream s_stream;
-
s_stream <<
"SHOW CREATE TABLE `" << &m_db_name << "`.`" << &m_id << "`";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/*
There should be no warnings. A warning means that serialization has
@@ -1847,25 +1835,16 @@ bool Table_obj::do_serialize(THD *thd, O
/* Check result. */
- /* The result must contain only one result-set... */
- DBUG_ASSERT(ed_result.elements == 1);
-
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
-
- /* ... which is not NULL. */
- DBUG_ASSERT(ed_result_set);
+ ed_result_set= ed_connection.use_result_set();
- if (ed_result_set->data()->elements == 0)
+ if (ed_result_set->size() != 1)
DBUG_RETURN(TRUE);
- /* There must be one row. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
-
- List_iterator_fast<Ed_row> row_it(*ed_result_set->data());
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
Ed_row *row= row_it++;
/* There must be two columns: database name and create statement. */
- DBUG_ASSERT(row->get_metadata()->get_num_columns() == 2);
+ DBUG_ASSERT(row->size() == 2);
/* Generate serialization image. */
@@ -1891,15 +1870,16 @@ get_view_create_stmt(THD *thd,
LEX_STRING *connection_cl_name)
{
/* Get a create statement for a view. */
- Ed_result ed_result;
+ Ed_connection ed_connection(thd);
+ Ed_result_set *ed_result_set;
String_stream s_stream;
s_stream <<
"SHOW CREATE VIEW `" << view->get_db_name() << "`."
"`" << view->get_name() << "`";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/*
There should be no warnings. A warning means that serialization has
@@ -1908,25 +1888,19 @@ get_view_create_stmt(THD *thd,
return TRUE;
}
- /* The result must contain only one result-set... */
- DBUG_ASSERT(ed_result.elements == 1);
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ /* The result must contain only one result-set... */
- /* ... which is not NULL. */
- DBUG_ASSERT(ed_result_set);
+ ed_result_set= ed_connection.use_result_set();
- if (ed_result_set->data()->elements == 0)
+ if (ed_result_set->size() != 1)
return TRUE;
- /* There must be one row. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
-
- List_iterator_fast<Ed_row> row_it(*ed_result_set->data());
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
Ed_row *row= row_it++;
/* There must be four columns. */
- DBUG_ASSERT(row->get_metadata()->get_num_columns() == 4);
+ DBUG_ASSERT(row->size() == 4);
const LEX_STRING *c1= row->get_column(1);
const LEX_STRING *c2= row->get_column(2);
@@ -1999,6 +1973,10 @@ bool View_obj::do_serialize(THD *thd, Ou
bool Stored_program_obj::do_serialize(THD *thd, Out_stream &out_stream)
{
+ String_stream s_stream;
+ Ed_connection ed_connection(thd);
+ Ed_result_set *ed_result_set;
+
DBUG_ENTER("Stored_program_obj::do_serialize");
DBUG_PRINT("Stored_program_obj::do_serialize",
("name: %.*s.%.*s",
@@ -2006,15 +1984,12 @@ bool Stored_program_obj::do_serialize(TH
DBUG_EXECUTE_IF("backup_fail_add_trigger", DBUG_RETURN(TRUE););
- String_stream s_stream;
- Ed_result ed_result;
-
s_stream <<
"SHOW CREATE " << get_type_name() <<
" `" << &m_db_name << "`.`" << &m_id << "`";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/*
There should be no warnings. A warning means that serialization has
@@ -2023,21 +1998,12 @@ bool Stored_program_obj::do_serialize(TH
DBUG_RETURN(TRUE);
}
- /* The result must contain only one result-set... */
- DBUG_ASSERT(ed_result.elements == 1);
-
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
-
- /* ... which is not NULL. */
- DBUG_ASSERT(ed_result_set);
+ ed_result_set= ed_connection.use_result_set();
- if (ed_result_set->data()->elements == 0)
+ if (ed_result_set->size() != 1)
DBUG_RETURN(TRUE);
- /* There must be one row. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
-
- List_iterator_fast<Ed_row> row_it(*ed_result_set->data());
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
Ed_row *row= row_it++;
s_stream.reset();
@@ -2675,8 +2641,8 @@ bool check_db_existence(THD *thd, const
s_stream << "SHOW CREATE DATABASE `" << db_name << "`";
- Ed_result ed_result;
- rc= run_service_interface_sql(thd, s_stream.lex_string(), &ed_result);
+ Ed_connection ed_connection(thd);
+ rc= run_service_interface_sql(thd, &ed_connection, s_stream.lex_string());
/* We're not interested in warnings/errors here. */
@@ -2691,7 +2657,8 @@ bool check_user_existence(THD *thd, cons
return TRUE;
#else
Grant_obj *grant_obj= (Grant_obj *) obj;
- Ed_result ed_result;
+ Ed_connection ed_connection(thd);
+ Ed_result_set *ed_result_set;
String_stream s_stream;
s_stream <<
@@ -2700,19 +2667,16 @@ bool check_user_existence(THD *thd, cons
"WHERE grantee = \"" << grant_obj->get_user_name() << "\"";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/* Should be no warnings. */
return FALSE;
}
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ ed_result_set= ed_connection.use_result_set();
- if (!ed_result_set)
- return FALSE;
-
- return ed_result_set->data()->elements > 0;
+ return ed_result_set->size() > 0;
#endif
}
@@ -2734,8 +2698,9 @@ const String *grant_get_grant_info(const
Obj *find_tablespace(THD *thd, const String *ts_name)
{
- Ed_result ed_result;
+ Ed_connection ed_connection(thd);
String_stream s_stream;
+ Ed_result_set *ed_result_set;
s_stream <<
"SELECT t1.tablespace_comment, t2.file_name, t1.engine "
@@ -2745,25 +2710,22 @@ Obj *find_tablespace(THD *thd, const Str
"t1.tablespace_name = '" << ts_name << "'";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/* Should be no warnings. */
return NULL;
}
- if (!ed_result.elements)
- return NULL;
-
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ ed_result_set= ed_connection.use_result_set();
- /* The result must contain only one result-set. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
+ DBUG_ASSERT(ed_result_set->size() == 1);
- Ed_row *row= ed_result_set->get_cur_row();
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
+ Ed_row *row= row_it++;
/* There must be 3 columns. */
- DBUG_ASSERT(row->get_metadata()->get_num_columns() == 3);
+ DBUG_ASSERT(row->size() == 3);
const LEX_STRING *comment= row->get_column(0);
const LEX_STRING *data_file_name= row->get_column(1);
@@ -2771,6 +2733,7 @@ Obj *find_tablespace(THD *thd, const Str
return new Tablespace_obj(ts_name->lex_string(),
*comment, *data_file_name, *engine);
+ return 0;
}
///////////////////////////////////////////////////////////////////////////
@@ -2795,8 +2758,9 @@ Obj *find_tablespace_for_table(THD *thd,
const String *db_name,
const String *table_name)
{
- Ed_result ed_result;
+ Ed_connection ed_connection(thd);
String_stream s_stream;
+ Ed_result_set *ed_result_set;
s_stream <<
"SELECT t1.tablespace_name, t1.engine, t1.tablespace_comment, t2.file_name "
@@ -2809,28 +2773,24 @@ Obj *find_tablespace_for_table(THD *thd,
"t3.table_name = '" << table_name << "'";
- if (run_service_interface_sql(thd, s_stream.lex_string(), &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string()) ||
+ ed_connection.get_warn_count())
{
/* Should be no warnings. */
return NULL;
}
- if (!ed_result.elements)
- return NULL;
-
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ ed_result_set= ed_connection.use_result_set();
- if (!ed_result_set->data()->elements)
+ /* The result must contain only one row. */
+ if (ed_result_set->size() != 1)
return NULL;
- /* The result must contain only one result-set. */
- DBUG_ASSERT(ed_result_set->data()->elements == 1);
-
- Ed_row *row= ed_result_set->get_cur_row();
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
+ Ed_row *row= row_it++;
/* There must be 4 columns. */
- DBUG_ASSERT(row->get_metadata()->get_num_columns() == 4);
+ DBUG_ASSERT(row->size() == 4);
const LEX_STRING *ts_name= row->get_column(0);
const LEX_STRING *engine= row->get_column(1);
@@ -3125,14 +3085,15 @@ bool is_slave()
int num_slaves_attached()
{
THD *thd= current_thd;
-
- Ed_result ed_result;
+ Ed_row *ed_row;
+ const LEX_STRING *num_slaves_str;
+ Ed_connection ed_connection(thd);
LEX_STRING sql_text= LXS_INIT("SELECT CONCAT(COUNT(1)) "
"FROM INFORMATION_SCHEMA.PROCESSLIST"
"WHERE LCASE(command) = LCASE('Binlog Dump')");
- if (run_service_interface_sql(thd, &sql_text, &ed_result) ||
- ed_result.get_warnings().elements > 0)
+ if (run_service_interface_sql(thd, &ed_connection, &sql_text) ||
+ ed_connection.get_warn_count())
{
/* Should be no warnings. */
@@ -3143,16 +3104,18 @@ int num_slaves_attached()
return 0;
}
- Ed_result_set *ed_result_set= ed_result.get_cur_result_set();
+ Ed_result_set *ed_result_set= ed_connection.use_result_set();
- if (!ed_result_set || ed_result_set->data()->elements < 1)
+ if (ed_result_set->size() != 1)
return 0;
- Ed_row *ed_row= ed_result_set->get_cur_row();
- const LEX_STRING *num_slaves_str= ed_row->get_column(0);
+ List_iterator_fast<Ed_row> row_it(*ed_result_set);
+
+ ed_row= row_it++;
+ num_slaves_str= ed_row->get_column(0);
- char *buffer= thd->strmake(num_slaves_str->str, num_slaves_str->length + 1);
- return atoi(buffer);
+ /* Can safely run atoi, strings are always NUL-terminated in Ed interface */
+ return atoi(num_slaves_str->str);
}
/**
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2008-12-04 16:50:07 +0000
+++ b/sql/sql_class.cc 2008-12-05 23:47:51 +0000
@@ -376,142 +376,6 @@ char *thd_security_context(THD *thd, cha
return thd->strmake(str.ptr(), str.length());
}
-/**
- Clear this diagnostics area.
-
- Normally called at the end of a statement.
-*/
-
-void
-Diagnostics_area::reset_diagnostics_area()
-{
- DBUG_ENTER("reset_diagnostics_area");
-#ifdef DBUG_OFF
- can_overwrite_status= FALSE;
- /** Don't take chances in production */
- m_message[0]= '\0';
- m_sql_errno= 0;
- m_server_status= 0;
- m_affected_rows= 0;
- m_last_insert_id= 0;
- m_statement_warn_count= 0;
-#endif
- is_sent= FALSE;
- /** Tiny reset in debug mode to see garbage right away */
- m_status= DA_EMPTY;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Set OK status -- ends commands that do not return a
- result set, e.g. INSERT/UPDATE/DELETE.
-*/
-
-void
-Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message_arg)
-{
- DBUG_ENTER("set_ok_status");
- DBUG_ASSERT(! is_set());
- /*
- In production, refuse to overwrite an error or a custom response
- with an OK packet.
- */
- if (is_error() || is_disabled())
- return;
-
- m_server_status= thd->server_status;
- m_statement_warn_count= thd->warning_info->statement_warn_count();
- m_affected_rows= affected_rows_arg;
- m_last_insert_id= last_insert_id_arg;
- if (message_arg)
- strmake(m_message, message_arg, sizeof(m_message) - 1);
- else
- m_message[0]= '\0';
- m_status= DA_OK;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Set EOF status.
-*/
-
-void
-Diagnostics_area::set_eof_status(THD *thd)
-{
- DBUG_ENTER("set_eof_status");
- /* Only allowed to report eof if has not yet reported an error */
- DBUG_ASSERT(! is_set());
- /*
- In production, refuse to overwrite an error or a custom response
- with an EOF packet.
- */
- if (is_error() || is_disabled())
- return;
-
- m_server_status= thd->server_status;
- /*
- If inside a stored procedure, do not return the total
- number of warnings, since they are not available to the client
- anyway.
- */
- m_statement_warn_count= (thd->spcont ?
- 0 : thd->warning_info->statement_warn_count());
-
- m_status= DA_EOF;
- DBUG_VOID_RETURN;
-}
-
-/**
- Set ERROR status.
-*/
-
-void
-Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
- const char *message_arg)
-{
- DBUG_ENTER("set_error_status");
- /*
- Only allowed to report error if has not yet reported a success
- The only exception is when we flush the message to the client,
- an error can happen during the flush.
- */
- DBUG_ASSERT(! is_set() || can_overwrite_status);
-#ifdef DBUG_OFF
- /*
- In production, refuse to overwrite a custom response with an
- ERROR packet.
- */
- if (is_disabled())
- return;
-#endif
-
- m_sql_errno= sql_errno_arg;
- strmake(m_message, message_arg, sizeof(m_message)-1);
-
- m_status= DA_ERROR;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Mark the diagnostics area as 'DISABLED'.
-
- This is used in rare cases when the COM_ command at hand sends a response
- in a custom format. One example is the query cache, another is
- COM_STMT_PREPARE.
-*/
-
-void
-Diagnostics_area::disable_status()
-{
- DBUG_ASSERT(! is_set());
- m_status= DA_DISABLED;
-}
-
THD::THD()
:Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION,
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2008-12-04 16:50:07 +0000
+++ b/sql/sql_class.h 2008-12-05 23:47:51 +0000
@@ -1097,128 +1097,6 @@ public:
const char *message) = 0;
};
-
-/**
- Stores status of the currently executed statement.
- Cleared at the beginning of the statement, and then
- can hold either OK, ERROR, or EOF status.
- Can not be assigned twice per statement.
-*/
-
-class Diagnostics_area
-{
-public:
- enum enum_diagnostics_status
- {
- /** The area is cleared at start of a statement. */
- DA_EMPTY= 0,
- /** Set whenever one calls my_ok(). */
- DA_OK,
- /** Set whenever one calls my_eof(). */
- DA_EOF,
- /** Set whenever one calls my_error() or my_message(). */
- DA_ERROR,
- /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
- DA_DISABLED
- };
- /** True if status information is sent to the client. */
- bool is_sent;
- /** Set to make set_error_status after set_{ok,eof}_status possible. */
- bool can_overwrite_status;
-
- void set_ok_status(THD *thd, ha_rows affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message);
- void set_eof_status(THD *thd);
- void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg);
-
- void disable_status();
-
- void reset_diagnostics_area();
-
- bool is_set() const { return m_status != DA_EMPTY; }
- bool is_error() const { return m_status == DA_ERROR; }
- bool is_eof() const { return m_status == DA_EOF; }
- bool is_ok() const { return m_status == DA_OK; }
- bool is_disabled() const { return m_status == DA_DISABLED; }
- enum_diagnostics_status status() const { return m_status; }
-
- const char *message() const
- { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
-
- uint sql_errno() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
-
- uint server_status() const
- {
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
- return m_server_status;
- }
-
- ha_rows affected_rows() const
- { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
-
- ulonglong last_insert_id() const
- { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
-
- uint statement_warn_count() const
- {
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
- return m_statement_warn_count;
- }
-
- Diagnostics_area() { reset_diagnostics_area(); }
-
-private:
- /** Message buffer. Can be used by OK or ERROR status. */
- char m_message[MYSQL_ERRMSG_SIZE];
- /**
- SQL error number. One of ER_ codes from share/errmsg.txt.
- Set by set_error_status.
- */
- uint m_sql_errno;
-
- /**
- Copied from thd->server_status when the diagnostics area is assigned.
- We need this member as some places in the code use the following pattern:
- thd->server_status|= ...
- my_eof(thd);
- thd->server_status&= ~...
- Assigned by OK, EOF or ERROR.
- */
- uint m_server_status;
- /**
- The number of rows affected by the last statement. This is
- semantically close to thd->row_count_func, but has a different
- life cycle. thd->row_count_func stores the value returned by
- function ROW_COUNT() and is cleared only by statements that
- update its value, such as INSERT, UPDATE, DELETE and few others.
- This member is cleared at the beginning of the next statement.
-
- We could possibly merge the two, but life cycle of thd->row_count_func
- can not be changed.
- */
- ha_rows m_affected_rows;
- /**
- Similarly to the previous member, this is a replacement of
- thd->first_successful_insert_id_in_prev_stmt, which is used
- to implement LAST_INSERT_ID().
- */
- ulonglong m_last_insert_id;
- /**
- Number of warnings of this last statement. May differ from
- the number of warnings returned by SHOW WARNINGS e.g. in case
- the statement doesn't clear the warnings, and doesn't generate
- them.
- */
- uint m_statement_warn_count;
- enum_diagnostics_status m_status;
- /**
- @todo: the following THD members belong here:
- - warn_list, warn_count,
- */
-};
-
/**
Tables that were locked with LOCK TABLES statement.
=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc 2008-11-18 22:30:59 +0000
+++ b/sql/sql_error.cc 2008-12-05 23:47:51 +0000
@@ -45,6 +45,143 @@ This file contains the implementation of
#include "mysql_priv.h"
#include "sp_rcontext.h"
+/**
+ Clear this diagnostics area.
+
+ Normally called at the end of a statement.
+*/
+
+void
+Diagnostics_area::reset_diagnostics_area()
+{
+ DBUG_ENTER("reset_diagnostics_area");
+#ifdef DBUG_OFF
+ can_overwrite_status= FALSE;
+ /** Don't take chances in production */
+ m_message[0]= '\0';
+ m_sql_errno= 0;
+ m_server_status= 0;
+ m_affected_rows= 0;
+ m_last_insert_id= 0;
+ m_statement_warn_count= 0;
+#endif
+ is_sent= FALSE;
+ /** Tiny reset in debug mode to see garbage right away */
+ m_status= DA_EMPTY;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set OK status -- ends commands that do not return a
+ result set, e.g. INSERT/UPDATE/DELETE.
+*/
+
+void
+Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg,
+ ulonglong last_insert_id_arg,
+ const char *message_arg)
+{
+ DBUG_ENTER("set_ok_status");
+ DBUG_ASSERT(! is_set());
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an OK packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+
+ m_server_status= thd->server_status;
+ m_statement_warn_count= thd->warning_info->statement_warn_count();
+ m_affected_rows= affected_rows_arg;
+ m_last_insert_id= last_insert_id_arg;
+ if (message_arg)
+ strmake(m_message, message_arg, sizeof(m_message) - 1);
+ else
+ m_message[0]= '\0';
+ m_status= DA_OK;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set EOF status.
+*/
+
+void
+Diagnostics_area::set_eof_status(THD *thd)
+{
+ DBUG_ENTER("set_eof_status");
+ /* Only allowed to report eof if has not yet reported an error */
+ DBUG_ASSERT(! is_set());
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an EOF packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+
+ m_server_status= thd->server_status;
+ /*
+ If inside a stored procedure, do not return the total
+ number of warnings, since they are not available to the client
+ anyway.
+ */
+ m_statement_warn_count= (thd->spcont ?
+ 0 : thd->warning_info->statement_warn_count());
+
+ m_status= DA_EOF;
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Set ERROR status.
+*/
+
+void
+Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
+ const char *message_arg)
+{
+ DBUG_ENTER("set_error_status");
+ /*
+ Only allowed to report error if has not yet reported a success
+ The only exception is when we flush the message to the client,
+ an error can happen during the flush.
+ */
+ DBUG_ASSERT(! is_set() || can_overwrite_status);
+#ifdef DBUG_OFF
+ /*
+ In production, refuse to overwrite a custom response with an
+ ERROR packet.
+ */
+ if (is_disabled())
+ return;
+#endif
+
+ m_sql_errno= sql_errno_arg;
+ strmake(m_message, message_arg, sizeof(m_message)-1);
+
+ m_status= DA_ERROR;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Mark the diagnostics area as 'DISABLED'.
+
+ This is used in rare cases when the COM_ command at hand sends a response
+ in a custom format. One example is the query cache, another is
+ COM_STMT_PREPARE.
+*/
+
+void
+Diagnostics_area::disable_status()
+{
+ DBUG_ASSERT(! is_set());
+ m_status= DA_DISABLED;
+}
+
+
/* Store a new message in an error object. */
void MYSQL_ERROR::set_msg(MEM_ROOT *warn_root, const char *msg_arg)
=== modified file 'sql/sql_error.h'
--- a/sql/sql_error.h 2008-11-19 19:20:47 +0000
+++ b/sql/sql_error.h 2008-12-05 23:47:51 +0000
@@ -18,9 +18,130 @@
#include "sql_list.h" /* Sql_alloc, MEM_ROOT */
#include "m_string.h" /* LEX_STRING */
+#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */
class THD;
+/**
+ Stores status of the currently executed statement.
+ Cleared at the beginning of the statement, and then
+ can hold either OK, ERROR, or EOF status.
+ Can not be assigned twice per statement.
+*/
+
+class Diagnostics_area
+{
+public:
+ enum enum_diagnostics_status
+ {
+ /** The area is cleared at start of a statement. */
+ DA_EMPTY= 0,
+ /** Set whenever one calls my_ok(). */
+ DA_OK,
+ /** Set whenever one calls my_eof(). */
+ DA_EOF,
+ /** Set whenever one calls my_error() or my_message(). */
+ DA_ERROR,
+ /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
+ DA_DISABLED
+ };
+ /** True if status information is sent to the client. */
+ bool is_sent;
+ /** Set to make set_error_status after set_{ok,eof}_status possible. */
+ bool can_overwrite_status;
+
+ void set_ok_status(THD *thd, ulonglong affected_rows_arg,
+ ulonglong last_insert_id_arg,
+ const char *message);
+ void set_eof_status(THD *thd);
+ void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg);
+
+ void disable_status();
+
+ void reset_diagnostics_area();
+
+ bool is_set() const { return m_status != DA_EMPTY; }
+ bool is_error() const { return m_status == DA_ERROR; }
+ bool is_eof() const { return m_status == DA_EOF; }
+ bool is_ok() const { return m_status == DA_OK; }
+ bool is_disabled() const { return m_status == DA_DISABLED; }
+ enum_diagnostics_status status() const { return m_status; }
+
+ const char *message() const
+ { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
+
+ uint sql_errno() const
+ { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
+
+ uint server_status() const
+ {
+ DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
+ return m_server_status;
+ }
+
+ ulonglong affected_rows() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
+
+ ulonglong last_insert_id() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
+
+ uint statement_warn_count() const
+ {
+ DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
+ return m_statement_warn_count;
+ }
+
+ Diagnostics_area() { reset_diagnostics_area(); }
+
+private:
+ /** Message buffer. Can be used by OK or ERROR status. */
+ char m_message[MYSQL_ERRMSG_SIZE];
+ /**
+ SQL error number. One of ER_ codes from share/errmsg.txt.
+ Set by set_error_status.
+ */
+ uint m_sql_errno;
+
+ /**
+ Copied from thd->server_status when the diagnostics area is assigned.
+ We need this member as some places in the code use the following pattern:
+ thd->server_status|= ...
+ my_eof(thd);
+ thd->server_status&= ~...
+ Assigned by OK, EOF or ERROR.
+ */
+ uint m_server_status;
+ /**
+ The number of rows affected by the last statement. This is
+ semantically close to thd->row_count_func, but has a different
+ life cycle. thd->row_count_func stores the value returned by
+ function ROW_COUNT() and is cleared only by statements that
+ update its value, such as INSERT, UPDATE, DELETE and few others.
+ This member is cleared at the beginning of the next statement.
+
+ We could possibly merge the two, but life cycle of thd->row_count_func
+ can not be changed.
+ */
+ ulonglong m_affected_rows;
+ /**
+ Similarly to the previous member, this is a replacement of
+ thd->first_successful_insert_id_in_prev_stmt, which is used
+ to implement LAST_INSERT_ID().
+ */
+ ulonglong m_last_insert_id;
+ /**
+ Number of warnings of this last statement. May differ from
+ the number of warnings returned by SHOW WARNINGS e.g. in case
+ the statement doesn't clear the warnings, and doesn't generate
+ them.
+ */
+ uint m_statement_warn_count;
+ enum_diagnostics_status m_status;
+ /**
+ @todo: the following THD members belong here:
+ - warn_list, warn_count,
+ */
+};
///////////////////////////////////////////////////////////////////////////
class MYSQL_ERROR: public Sql_alloc
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2008-12-04 16:50:07 +0000
+++ b/sql/sql_prepare.cc 2008-12-05 23:47:51 +0000
@@ -197,6 +197,66 @@ 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 void send_ok(uint server_status, uint statement_warn_count,
+ ha_rows affected_rows, ulonglong last_insert_id,
+ const char *message);
+
+ virtual void send_eof(uint server_status, uint statement_warn_count);
+ virtual void send_error(uint sql_errno, const char *err_msg);
+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
******************************************************************************/
@@ -2777,54 +2837,6 @@ void mysql_stmt_get_longdata(THD *thd, c
}
-bool
-mysql_execute_direct(THD *thd, LEX_STRING query, Ed_result *ed_result)
-{
- Execute_sql_statement execute_sql_statement(query);
-
- return mysql_execute_direct(thd, &execute_sql_statement, ed_result);
-}
-
-
-/**
- Execute a fragment of server functionality without an effect on
- thd, and store results in Ed_result.
-
- @param thd Thread handle.
- @param server_runnable A code fragment to execute.
- @param ed_result Result interceptor
-*/
-
-bool
-mysql_execute_direct(THD *thd, Server_runnable *server_runnable,
- Ed_result *ed_result)
-{
- Protocol_local protocol_local(thd, ed_result);
- Prepared_statement stmt(thd);
-
- DBUG_ENTER("mysql_execute_direct");
-
- DBUG_ASSERT(ed_result);
-
- Protocol *protocol_saved= thd->protocol;
-
- thd->protocol= &protocol_local;
-
- ed_result->begin_statement(thd);
- bool rc= stmt.execute_server_runnable(server_runnable);
- ed_result->end_statement(thd);
-
- thd->protocol->end_statement();
-
- thd->protocol= protocol_saved;
-
- thd->stmt_da->reset_diagnostics_area();
-
- DBUG_RETURN(rc);
-}
-
-
-
/***************************************************************************
Select_fetch_protocol_binary
****************************************************************************/
@@ -3811,3 +3823,569 @@ 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);
+
+ 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. */
+
+void
+Protocol_local::send_ok(uint server_status, uint statement_warn_count,
+ ha_rows 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.
+ */
+}
+
+
+/**
+ 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.
+*/
+
+void 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;
+
+ /* 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);
+}
+
+
+/** Called to send an error to the client at the end of a statement. */
+
+void
+Protocol_local::send_error(uint sql_errno, const char *err_msg)
+{
+ /*
+ Just make sure that nothing is sent to the client (default
+ implementation).
+ */
+}
+
+
+#ifdef EMBEDDED_LIBRARY
+void Protocol_local::remove_last_row()
+{ }
+#endif
=== modified file 'sql/sql_prepare.h'
--- a/sql/sql_prepare.h 2008-12-04 20:02:32 +0000
+++ b/sql/sql_prepare.h 2008-12-05 23:47:51 +0000
@@ -15,12 +15,10 @@
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 "m_string.h"
+#include "sql_error.h"
class THD;
struct LEX;
-template <typename T> class List;
/**
An interface that is used to take an action when
@@ -92,12 +90,277 @@ public:
virtual ~Server_runnable();
};
-class Ed_result;
-bool
-mysql_execute_direct(THD *thd, LEX_STRING query, Ed_result *ed_result);
-bool
-mysql_execute_direct(THD *thd, Server_runnable *server_runnable,
- Ed_result *ed_result);
+/**
+ 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(); }
+
+ /**
+ 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
| Thread |
|---|
| • bzr push into mysql-6.0-runtime branch (kostja:2766 to 2768) | Konstantin Osipov | 6 Dec |