From: Date: December 6 2008 12:48am Subject: bzr commit into mysql-6.0-runtime branch (kostja:2767) WL#4264 List-Archive: http://lists.mysql.com/commits/60788 Message-Id: <20081205234822.1D4234A01D@vajra.local> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7BIT #At file:///opt/local/work/mysql-6.0-ed2/ 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 per-file messages: sql/backup/backup_test.cc Remove a call that is no longer needed. si_objects.cc implementation no longer touches the main diagnostics area of the thread. sql/backup/kernel.cc Remove calls that are no longer needed. si_objects.cc no longer touches the diagnostics area of the thread, and closes the tables that it used. sql/protocol.cc Ed_* (execute direct) implementation was moved to sql_prepare.cc. sql/protocol.h Ed_* declarations were moved to sql_prepare.h. The advantage of using sql_prepare.h is that it's a stand-alone header with header guards, unlike protocol.h. sql/si_objects.cc Use the modified interface to work with execute direct results. Coding style fixes (declare variables in the beginning of a function or block). sql/sql_class.cc Move Diagnostics_area implementation to sql_error.cc sql/sql_class.h Move Diagnostics_area declaration to sql_error.h. This groups all error-related classes into one, independent header. sql/sql_error.cc Diagnostics_area implementation added. sql/sql_error.h Diagnostics_area declaration added. sql/sql_prepare.cc Implement Ed_connection - a way to execute queries within the server. Derives heavily on the original implementation, however: - we no longer need to grab messages or warnings from THD, since Warning_info and Diagnostics_area are members of Ed_connection and can be used directly. Add comments. sql/sql_prepare.h Move declarations of Ed_* classes (execute direct interface) to this header. This allows usage of execute direct without having to include mysql_priv.h. === 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 *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 *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 *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 *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 *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 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 *columns, uint) -{ - DBUG_ASSERT(m_result); - - return m_result->add_result_set(columns); -} - -bool Protocol_local::send_out_parameters(List *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 *list, uint flags); - virtual bool send_out_parameters(List *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 *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 *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 *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 *col_metadata); - -public: - inline const Ed_result_set_metadata *get_metadata() const - { return m_metadata; } - - inline List *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 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 -{ -public: - Ed_result(); - ~Ed_result(); - -public: - inline Ed_result_set *get_cur_result_set() - { return m_current_result_set; } - - bool add_result_set(List *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 &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-05 23:47:51 +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 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 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 m_row_it; }; @@ -1338,9 +1336,9 @@ private: template inline Ed_result_set_iterator:: -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 inline Ed_result_set_iterator::~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(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 row_it(*ed_result_set->data()); + List_iterator_fast 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(); + ed_result_set= ed_connection.use_result_set(); - /* ... which is not NULL. */ - DBUG_ASSERT(ed_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 row_it(*ed_result_set->data()); + List_iterator_fast 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 row_it(*ed_result_set->data()); + List_iterator_fast 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 row_it(*ed_result_set->data()); + List_iterator_fast 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 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_connection.use_result_set(); - Ed_result_set *ed_result_set= ed_result.get_cur_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 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); === 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 *list, uint flags); + virtual bool send_out_parameters(List *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 *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 *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 *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)) + 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 *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 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&() { return *m_rows; } + unsigned int size() const { return m_rows->elements; } + + Ed_result_set(List *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 *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 *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