summaryrefslogtreecommitdiff
path: root/sql/sql_class.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r--sql/sql_class.cc821
1 files changed, 597 insertions, 224 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 11b45b848c8..2917626ff35 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -35,6 +35,9 @@
#endif
#include <mysys_err.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
+
/*
The following is used to initialise Table_ident with a internal
table name
@@ -46,7 +49,7 @@ char internal_table_name[2]= "*";
** Instansiate templates
*****************************************************************************/
-#ifdef __GNUC__
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
/* Used templates */
template class List<Key>;
template class List_iterator<Key>;
@@ -154,26 +157,43 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions
****************************************************************************/
-THD::THD()
- :user_time(0), global_read_lock(0), is_fatal_error(0),
- last_insert_id_used(0),
- insert_id_used(0), rand_used(0), time_zone_used(0),
- in_lock_tables(0), bootstrap(0)
+Open_tables_state::Open_tables_state(ulong version_arg)
+ :version(version_arg)
{
- current_arena= this;
- host= user= priv_user= db= ip=0;
- host_or_ip= "connecting host";
+ reset_open_tables_state();
+}
+
+
+/*
+ Pass nominal parameters to Statement constructor only to ensure that
+ the destructor works OK in case of error. The main_mem_root will be
+ re-initialized in init().
+*/
+
+THD::THD()
+ :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
+ Open_tables_state(refresh_version),
+ lock_id(&main_lock_id),
+ user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
+ rand_used(0), time_zone_used(0),
+ last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
+ in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
+ spcont(NULL)
+{
+ stmt_arena= this;
+ db= 0;
+ catalog= (char*)"std"; // the only catalog we have for now
+ main_security_ctx.init();
+ security_ctx= &main_security_ctx;
locked=some_tables_deleted=no_errors=password= 0;
- killed=0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
- db_length= col_access= 0;
+ killed= NOT_KILLED;
+ db_length= col_access=0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
- open_tables= temporary_tables= handler_tables= derived_tables= 0;
hash_clear(&handler_tables_hash);
tmp_table=0;
- lock=locked_tables=0;
used_tables=0;
cuted_fields= sent_row_count= 0L;
limit_found_rows= 0;
@@ -186,26 +206,29 @@ THD::THD()
variables.pseudo_thread_id= 0;
one_shot_set= 0;
file_id = 0;
+ query_id= 0;
warn_id= 0;
db_charset= global_system_variables.collation_database;
+ bzero(ha_data, sizeof(ha_data));
mysys_var=0;
+ binlog_evt_union.do_union= FALSE;
#ifndef DBUG_OFF
dbug_sentry=THD_SENTRY_MAGIC;
#endif
-#ifndef EMBEDDED_LIBRARY
+#ifndef EMBEDDED_LIBRARY
net.vio=0;
#endif
- net.last_error[0]=0; // If error on boot
+ net.last_error[0]=0; // If error on boot
+ net.query_cache_query=0; // If error on boot
ull=0;
- system_thread=cleanup_done=0;
+ system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0;
peer_port= 0; // For SHOW PROCESSLIST
- transaction.changed_tables = 0;
#ifdef __WIN__
real_id = 0;
#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
-#endif
+#endif
pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST);
/* Variables with default values */
@@ -214,26 +237,23 @@ THD::THD()
server_id = ::server_id;
slave_net = 0;
command=COM_CONNECT;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- db_access=NO_ACCESS;
-#endif
- version=refresh_version; // For boot
*scramble= '\0';
init();
/* Initialize sub structures */
init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
- (hash_free_key) free_user_var,0);
+ (hash_free_key) free_user_var, 0);
+
+ sp_proc_cache= NULL;
+ sp_func_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
my_init_dynamic_array(&user_var_events,
- sizeof(BINLOG_USER_VAR_EVENT *),
- 16,
- 16);
+ sizeof(BINLOG_USER_VAR_EVENT *), 16, 16);
else
bzero((char*) &user_var_events, sizeof(user_var_events));
@@ -243,26 +263,10 @@ THD::THD()
protocol_prep.init(this);
tablespace_op=FALSE;
-#ifdef USING_TRANSACTIONS
- bzero((char*) &transaction,sizeof(transaction));
- /*
- Binlog is always open (if needed) before a THD is created (including
- bootstrap).
- */
- if (opt_using_transactions && mysql_bin_log.is_open())
- {
- if (open_cached_file(&transaction.trans_log,
- mysql_tmpdir, LOG_PREFIX, binlog_cache_size,
- MYF(MY_WME)))
- killed=1;
- transaction.trans_log.end_of_file= max_binlog_cache_size;
- }
-#endif
- init_sql_alloc(&transaction.mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
- {
- ulong tmp=sql_rnd_with_mutex();
- randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
- }
+ ulong tmp=sql_rnd_with_mutex();
+ randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
+ thr_lock_info_init(&lock_info); /* safety: will be reset after start */
+ thr_lock_owner_init(&main_lock_id, &lock_info);
}
@@ -285,6 +289,8 @@ void THD::init(void)
#endif
pthread_mutex_unlock(&LOCK_global_system_variables);
server_status= SERVER_STATUS_AUTOCOMMIT;
+ if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
+ server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
options= thd_startup_options;
open_options=ha_open_options;
update_lock_default= (variables.low_priority_updates ?
@@ -295,6 +301,7 @@ void THD::init(void)
bzero((char*) warn_count, sizeof(warn_count));
total_warn_count= 0;
update_charset();
+ bzero((char *) &status_var, sizeof(status_var));
}
@@ -310,9 +317,13 @@ void THD::init_for_queries()
reset_root_defaults(mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
+#ifdef USING_TRANSACTIONS
reset_root_defaults(&transaction.mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
+#endif
+ transaction.xid_state.xid.null();
+ transaction.xid_state.in_thd=1;
}
@@ -333,9 +344,11 @@ void THD::change_user(void)
cleanup_done= 0;
init();
stmt_map.reset();
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
(hash_free_key) free_user_var, 0);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
}
@@ -344,7 +357,16 @@ void THD::change_user(void)
void THD::cleanup(void)
{
DBUG_ENTER("THD::cleanup");
- ha_rollback(this);
+#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
+ if (transaction.xid_state.xa_state == XA_PREPARED)
+ {
+#error xid_state in the cache should be replaced by the allocated value
+ }
+#endif
+ {
+ ha_rollback(this);
+ xid_cache_delete(&transaction.xid_state);
+ }
if (locked_tables)
{
lock=locked_tables; locked_tables=0;
@@ -359,6 +381,10 @@ void THD::cleanup(void)
my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR));
my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR));
my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
+
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
+
if (global_read_lock)
unlock_global_read_lock(this);
if (ull)
@@ -368,6 +394,7 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
+
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -380,53 +407,69 @@ THD::~THD()
/* Ensure that no one is using THD */
pthread_mutex_lock(&LOCK_delete);
pthread_mutex_unlock(&LOCK_delete);
+ add_to_status(&global_status_var, &status_var);
/* Close connection */
-#ifndef EMBEDDED_LIBRARY
+#ifndef EMBEDDED_LIBRARY
if (net.vio)
{
vio_delete(net.vio);
- net_end(&net);
+ net_end(&net);
}
#endif
+ stmt_map.destroy(); /* close all prepared statements */
+ DBUG_ASSERT(lock_info.n_cursors == 0);
if (!cleanup_done)
cleanup();
-#ifdef USING_TRANSACTIONS
- if (opt_using_transactions)
- {
- close_cached_file(&transaction.trans_log);
- ha_close_connection(this);
- }
-#endif
- DBUG_PRINT("info", ("freeing host"));
- if (host != my_localhost) // If not pointer to constant
- safeFree(host);
- if (user != delayed_user)
- safeFree(user);
- safeFree(ip);
+ ha_close_connection(this);
+
+ DBUG_PRINT("info", ("freeing security context"));
+ main_security_ctx.destroy();
safeFree(db);
free_root(&warn_root,MYF(0));
+#ifdef USING_TRANSACTIONS
free_root(&transaction.mem_root,MYF(0));
+#endif
mysys_var=0; // Safety (shouldn't be needed)
pthread_mutex_destroy(&LOCK_delete);
#ifndef DBUG_OFF
- dbug_sentry = THD_SENTRY_GONE;
+ dbug_sentry= THD_SENTRY_GONE;
#endif
- /* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */
- clear_alloc_root(&stmt_backup.main_mem_root);
DBUG_VOID_RETURN;
}
-void THD::awake(bool prepare_to_die)
+/*
+ Add to one status variable another status variable
+
+ NOTES
+ This function assumes that all variables are long/ulong.
+ If this assumption will change, then we have to explictely add
+ the other variables after the while loop
+*/
+
+void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
+{
+ ulong *end= (ulong*) ((byte*) to_var + offsetof(STATUS_VAR,
+ last_system_status_var) +
+ sizeof(ulong));
+ ulong *to= (ulong*) to_var, *from= (ulong*) from_var;
+
+ while (to != end)
+ *(to++)+= *(from++);
+ /* it doesn't make sense to add last_query_cost values */
+}
+
+
+void THD::awake(THD::killed_state state_to_set)
{
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
- if (prepare_to_die)
- killed = 1;
- thr_alarm_kill(real_id);
+ killed= state_to_set;
+ if (state_to_set != THD::KILL_QUERY)
+ thr_alarm_kill(real_id);
#ifdef SIGNAL_WITH_VIO_CLOSE
close_active_vio();
#endif
@@ -481,10 +524,28 @@ bool THD::store_globals()
if this is the slave SQL thread.
*/
variables.pseudo_thread_id= thread_id;
+ /*
+ We have to call thr_lock_info_init() again here as THD may have been
+ created in another thread
+ */
+ thr_lock_info_init(&lock_info);
return 0;
}
+/* Cleanup after a query */
+
+void THD::cleanup_after_query()
+{
+ if (clear_next_insert_id)
+ {
+ clear_next_insert_id= 0;
+ next_insert_id= 0;
+ }
+ /* Free Items that were created during this execution */
+ free_items();
+}
+
/*
Convert a string to another character set
@@ -592,7 +653,7 @@ void THD::add_changed_table(TABLE *table)
DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
table->file->has_transactions());
- add_changed_table(table->table_cache_key, table->key_length);
+ add_changed_table(table->s->table_cache_key, table->s->key_length);
DBUG_VOID_RETURN;
}
@@ -646,8 +707,8 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
if (!new_table)
{
my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
- ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
- killed= 1;
+ ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
+ killed= KILL_CONNECTION;
return 0;
}
@@ -676,8 +737,8 @@ int THD::send_explain_fields(select_result *result)
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("key", NAME_LEN, cs));
item->maybe_null=1;
- field_list.push_back(item=new Item_return_int("key_len",3,
- MYSQL_TYPE_LONGLONG));
+ field_list.push_back(item=new Item_empty_string("key_len",
+ NAME_LEN*MAX_KEY));
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("ref",
NAME_LEN*MAX_REF_PARTS, cs));
@@ -686,7 +747,8 @@ int THD::send_explain_fields(select_result *result)
MYSQL_TYPE_LONGLONG));
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_fields(field_list,1));
+ return (result->send_fields(field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
@@ -720,7 +782,7 @@ struct Item_change_record: public ilink
/*
Register an item tree tree transformation, performed by the query
optimizer. We need a pointer to runtime_memroot because it may be !=
- thd->mem_root (due to possible set_n_backup_item_arena called for thd).
+ thd->mem_root (due to possible set_n_backup_active_arena called for thd).
*/
void THD::nocheck_register_item_tree_change(Item **place, Item *old_value,
@@ -735,7 +797,10 @@ void THD::nocheck_register_item_tree_change(Item **place, Item *old_value,
void *change_mem= alloc_root(runtime_memroot, sizeof(*change));
if (change_mem == 0)
{
- fatal_error();
+ /*
+ OOM, thd->fatal_error() is called by the error handler of the
+ memroot. Just return.
+ */
return;
}
change= new (change_mem) Item_change_record;
@@ -749,10 +814,13 @@ void THD::rollback_item_tree_changes()
{
I_List_iterator<Item_change_record> it(change_list);
Item_change_record *change;
+ DBUG_ENTER("rollback_item_tree_changes");
+
while ((change= it++))
*change->place= change->old_value;
/* We can forget about changes memory: it's allocated in runtime memroot */
change_list.empty();
+ DBUG_VOID_RETURN;
}
@@ -767,7 +835,7 @@ select_result::select_result()
void select_result::send_error(uint errcode,const char *err)
{
- ::send_error(thd, errcode, err);
+ my_message(errcode, err, MYF(0));
}
@@ -789,11 +857,36 @@ sql_exchange::sql_exchange(char *name,bool flag)
escaped= &default_escaped;
}
-bool select_send::send_fields(List<Item> &list,uint flag)
+bool select_send::send_fields(List<Item> &list, uint flags)
{
- return thd->protocol->send_fields(&list,flag);
+ bool res;
+ if (!(res= thd->protocol->send_fields(&list, flags)))
+ status= 1;
+ return res;
}
+void select_send::abort()
+{
+ DBUG_ENTER("select_send::abort");
+ if (status && thd->spcont &&
+ thd->spcont->find_handler(thd->net.last_errno,
+ MYSQL_ERROR::WARN_LEVEL_ERROR))
+ {
+ /*
+ Executing stored procedure without a handler.
+ Here we should actually send an error to the client,
+ but as an error will break a multiple result set, the only thing we
+ can do for now is to nicely end the current data set and remembering
+ the error so that the calling routine will abort
+ */
+ thd->net.report_error= 0;
+ send_eof();
+ thd->net.report_error= 1; // Abort SP
+ }
+ DBUG_VOID_RETURN;
+}
+
+
/* Send data to client. Returns 0 if ok */
bool select_send::send_data(List<Item> &items)
@@ -810,7 +903,6 @@ bool select_send::send_data(List<Item> &items)
InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
by thd
*/
- if (thd->transaction.all.innobase_tid)
ha_release_temporary_latches(thd);
#endif
@@ -845,7 +937,6 @@ bool select_send::send_eof()
/* We may be passing the control from mysqld to the client: release the
InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
by thd */
- if (thd->transaction.all.innobase_tid)
ha_release_temporary_latches(thd);
#endif
@@ -858,6 +949,7 @@ bool select_send::send_eof()
if (!thd->net.report_error)
{
::send_eof(thd);
+ status= 0;
return 0;
}
else
@@ -871,7 +963,7 @@ bool select_send::send_eof()
void select_to_file::send_error(uint errcode,const char *err)
{
- ::send_error(thd,errcode,err);
+ my_message(errcode, err, MYF(0));
if (file > 0)
{
(void) end_io_cache(&cache);
@@ -1195,7 +1287,7 @@ bool select_dump::send_data(List<Item> &items)
}
if (row_count++ > 1)
{
- my_error(ER_TOO_MANY_ROWS, MYF(0));
+ my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
goto err;
}
while ((item=li++))
@@ -1208,7 +1300,7 @@ bool select_dump::send_data(List<Item> &items)
}
else if (my_b_write(&cache,(byte*) res->ptr(),res->length()))
{
- my_error(ER_ERROR_ON_WRITE,MYF(0), path, my_errno);
+ my_error(ER_ERROR_ON_WRITE, MYF(0), path, my_errno);
goto err;
}
}
@@ -1284,6 +1376,9 @@ bool select_max_min_finder_subselect::send_data(List<Item> &items)
case STRING_RESULT:
op= &select_max_min_finder_subselect::cmp_str;
break;
+ case DECIMAL_RESULT:
+ op= &select_max_min_finder_subselect::cmp_decimal;
+ break;
case ROW_RESULT:
// This case should never be choosen
DBUG_ASSERT(0);
@@ -1300,15 +1395,14 @@ bool select_max_min_finder_subselect::send_data(List<Item> &items)
bool select_max_min_finder_subselect::cmp_real()
{
Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
- double val1= cache->val(), val2= maxmin->val();
+ double val1= cache->val_real(), val2= maxmin->val_real();
if (fmax)
return (cache->null_value && !maxmin->null_value) ||
(!cache->null_value && !maxmin->null_value &&
val1 > val2);
- else
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- val1 < val2);
+ return (maxmin->null_value && !cache->null_value) ||
+ (!cache->null_value && !maxmin->null_value &&
+ val1 < val2);
}
bool select_max_min_finder_subselect::cmp_int()
@@ -1319,10 +1413,23 @@ bool select_max_min_finder_subselect::cmp_int()
return (cache->null_value && !maxmin->null_value) ||
(!cache->null_value && !maxmin->null_value &&
val1 > val2);
- else
- return (maxmin->null_value && !cache->null_value) ||
+ return (maxmin->null_value && !cache->null_value) ||
+ (!cache->null_value && !maxmin->null_value &&
+ val1 < val2);
+}
+
+bool select_max_min_finder_subselect::cmp_decimal()
+{
+ Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
+ my_decimal cval, *cvalue= cache->val_decimal(&cval);
+ my_decimal mval, *mvalue= maxmin->val_decimal(&mval);
+ if (fmax)
+ return (cache->null_value && !maxmin->null_value) ||
(!cache->null_value && !maxmin->null_value &&
- val1 < val2);
+ my_decimal_cmp(cvalue, mvalue) > 0) ;
+ return (maxmin->null_value && !cache->null_value) ||
+ (!cache->null_value && !maxmin->null_value &&
+ my_decimal_cmp(cvalue,mvalue) < 0);
}
bool select_max_min_finder_subselect::cmp_str()
@@ -1339,10 +1446,9 @@ bool select_max_min_finder_subselect::cmp_str()
return (cache->null_value && !maxmin->null_value) ||
(!cache->null_value && !maxmin->null_value &&
sortcmp(val1, val2, cache->collation.collation) > 0) ;
- else
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- sortcmp(val1, val2, cache->collation.collation) < 0);
+ return (maxmin->null_value && !cache->null_value) ||
+ (!cache->null_value && !maxmin->null_value &&
+ sortcmp(val1, val2, cache->collation.collation) < 0);
}
bool select_exists_subselect::send_data(List<Item> &items)
@@ -1367,27 +1473,37 @@ bool select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
List_iterator_fast<Item> li(list);
- List_iterator_fast<LEX_STRING> gl(var_list);
+ List_iterator_fast<my_var> gl(var_list);
Item *item;
- LEX_STRING *ls;
+
+ local_vars.empty(); // Clear list if SP
+ unit= u;
+ row_count= 0;
+
if (var_list.elements != list.elements)
{
- my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
+ my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+ ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
return 1;
}
- unit=u;
while ((item=li++))
{
- ls= gl++;
- Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
- /*
- Item_func_set_user_var can't substitute something else on its place =>
- 0 can be passed as last argument (reference on item)
- */
- xx->fix_fields(thd,(TABLE_LIST*) thd->lex->select_lex.table_list.first,
- 0);
- xx->fix_length_and_dec();
- vars.push_back(xx);
+ my_var *mv= gl++;
+ if (mv->local)
+ (void)local_vars.push_back(new Item_splocal(mv->s, mv->offset));
+ else
+ {
+ Item_func_set_user_var *var= new Item_func_set_user_var(mv->s, item);
+ /*
+ Item_func_set_user_var can't substitute something else on its place =>
+ 0 can be passed as last argument (reference on item)
+ Item_func_set_user_var can't be fixed after creation, so we do not
+ check var->fixed
+ */
+ var->fix_fields(thd, 0);
+ var->fix_length_and_dec();
+ vars.push_back(var);
+ }
}
return 0;
}
@@ -1400,94 +1516,62 @@ void select_dumpvar::cleanup()
}
-/*
- Create arena for already constructed THD.
-
- SYNOPSYS
- Item_arena()
- thd - thread for which arena is created
-
- DESCRIPTION
- Create arena for already existing THD using its variables as parameters
- for memory root initialization.
-*/
-Item_arena::Item_arena(THD* thd)
- :free_list(0), mem_root(&main_mem_root),
- state(INITIALIZED)
+Query_arena::Type Query_arena::type() const
{
- init_sql_alloc(&main_mem_root,
- thd->variables.query_alloc_block_size,
- thd->variables.query_prealloc_size);
+ DBUG_ASSERT(0); /* Should never be called */
+ return STATEMENT;
}
-/*
- Create arena and optionally initialize memory root.
-
- SYNOPSYS
- Item_arena()
- init_mem_root - whenever we need to initialize memory root
+void Query_arena::free_items()
+{
+ Item *next;
+ DBUG_ENTER("Query_arena::free_items");
+ /* This works because items are allocated with sql_alloc() */
+ for (; free_list; free_list= next)
+ {
+ next= free_list->next;
+ free_list->delete_self();
+ }
+ /* Postcondition: free_list is 0 */
+ DBUG_VOID_RETURN;
+}
- DESCRIPTION
- Create arena and optionally initialize memory root with minimal
- possible parameters.
- NOTE
- We use this constructor when arena is part of THD, but reinitialize
- its memory root in THD::init_for_queries() before execution of real
- statements.
-*/
-Item_arena::Item_arena(bool init_mem_root)
- :free_list(0), mem_root(&main_mem_root),
- state(CONVENTIONAL_EXECUTION)
+void Query_arena::set_query_arena(Query_arena *set)
{
- if (init_mem_root)
- init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+ mem_root= set->mem_root;
+ free_list= set->free_list;
+ state= set->state;
}
-Item_arena::Type Item_arena::type() const
+void Query_arena::cleanup_stmt()
{
- DBUG_ASSERT("Item_arena::type()" == "abstract");
- return STATEMENT;
+ DBUG_ASSERT("Query_arena::cleanup_stmt()" == "not implemented");
}
-
/*
Statement functions
*/
-Statement::Statement(THD *thd)
- :Item_arena(thd),
- id(++thd->statement_id_counter),
+Statement::Statement(enum enum_state state_arg, ulong id_arg,
+ ulong alloc_block_size, ulong prealloc_size)
+ :Query_arena(&main_mem_root, state_arg),
+ id(id_arg),
set_query_id(1),
allow_sum_func(0),
lex(&main_lex),
query(0),
- query_length(0)
+ query_length(0),
+ cursor(0)
{
name.str= NULL;
-}
-
-/*
- This constructor is called when statement is a subobject of THD:
- Some variables are initialized in THD::init due to locking problems
- This statement object will be used to
-*/
-
-Statement::Statement()
- :Item_arena((bool)TRUE),
- id(0),
- set_query_id(1),
- allow_sum_func(0), /* initialized later */
- lex(&main_lex),
- query(0), /* these two are set */
- query_length(0) /* in alloc_query() */
-{
+ init_sql_alloc(&main_mem_root, alloc_block_size, prealloc_size);
}
-Item_arena::Type Statement::type() const
+Query_arena::Type Statement::type() const
{
return STATEMENT;
}
@@ -1501,21 +1585,26 @@ void Statement::set_statement(Statement *stmt)
lex= stmt->lex;
query= stmt->query;
query_length= stmt->query_length;
+ cursor= stmt->cursor;
}
void
Statement::set_n_backup_statement(Statement *stmt, Statement *backup)
{
+ DBUG_ENTER("Statement::set_n_backup_statement");
backup->set_statement(this);
set_statement(stmt);
+ DBUG_VOID_RETURN;
}
void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
{
+ DBUG_ENTER("Statement::restore_backup_statement");
stmt->set_statement(this);
set_statement(backup);
+ DBUG_VOID_RETURN;
}
@@ -1525,8 +1614,8 @@ void THD::end_statement()
lex_end(lex);
delete lex->result;
lex->result= 0;
- free_items(free_list);
- free_list= 0;
+ /* Note that free_list is freed in cleanup_after_query() */
+
/*
Don't free mem_root, as mem_root is freed in the end of dispatch_command
(once for any command).
@@ -1534,42 +1623,39 @@ void THD::end_statement()
}
-void Item_arena::set_n_backup_item_arena(Item_arena *set, Item_arena *backup)
+void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
{
- DBUG_ENTER("Item_arena::set_n_backup_item_arena");
- backup->set_item_arena(this);
- set_item_arena(set);
+ DBUG_ENTER("THD::set_n_backup_active_arena");
+ DBUG_ASSERT(backup->is_backup_arena == FALSE);
+
+ backup->set_query_arena(this);
+ set_query_arena(set);
+#ifndef DBUG_OFF
+ backup->is_backup_arena= TRUE;
+#endif
DBUG_VOID_RETURN;
}
-void Item_arena::restore_backup_item_arena(Item_arena *set, Item_arena *backup)
+void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
{
- DBUG_ENTER("Item_arena::restore_backup_item_arena");
- set->set_item_arena(this);
- set_item_arena(backup);
-#ifdef NOT_NEEDED_NOW
- /*
- Reset backup mem_root to avoid its freeing.
- Since Item_arena's mem_root is freed only when it is part of Statement
- we need this only if we use some Statement's arena as backup storage.
- But we do this only with THD::stmt_backup and this Statement is specially
- handled in this respect. So this code is not really needed now.
- */
- clear_alloc_root(&backup->mem_root);
+ DBUG_ENTER("THD::restore_active_arena");
+ DBUG_ASSERT(backup->is_backup_arena);
+ set->set_query_arena(this);
+ set_query_arena(backup);
+#ifndef DBUG_OFF
+ backup->is_backup_arena= FALSE;
#endif
DBUG_VOID_RETURN;
}
-void Item_arena::set_item_arena(Item_arena *set)
-{
- mem_root= set->mem_root;
- free_list= set->free_list;
- state= set->state;
-}
-
Statement::~Statement()
{
+ /*
+ We must free `main_mem_root', not `mem_root' (pointer), to work
+ correctly if this statement is used as a backup statement,
+ for which `mem_root' may point to some other statement.
+ */
free_root(&main_mem_root, MYF(0));
}
@@ -1614,32 +1700,46 @@ Statement_map::Statement_map() :
NULL,MYF(0));
}
+
int Statement_map::insert(Statement *statement)
{
int rc= my_hash_insert(&st_hash, (byte *) statement);
- if (rc == 0)
- last_found_statement= statement;
if (statement->name.str)
{
- /*
- If there is a statement with the same name, remove it. It is ok to
- remove old and fail to insert new one at the same time.
- */
- Statement *old_stmt;
- if ((old_stmt= find_by_name(&statement->name)))
- erase(old_stmt);
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
hash_delete(&st_hash, (byte*)statement);
}
+ if (rc == 0)
+ last_found_statement= statement;
return rc;
}
+void Statement_map::close_transient_cursors()
+{
+#ifdef TO_BE_IMPLEMENTED
+ Statement *stmt;
+ while ((stmt= transient_cursor_list.head()))
+ stmt->close_cursor(); /* deletes itself from the list */
+#endif
+}
+
+
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
+ List_iterator_fast<Item_splocal> var_li(local_vars);
+ List_iterator_fast<my_var> my_li(var_list);
+ List_iterator<Item> it(items);
Item_func_set_user_var *xx;
+ Item_splocal *yy;
+ my_var *zz;
DBUG_ENTER("send_data");
+ if (unit->offset_limit_cnt)
+ { // using limit offset,count
+ unit->offset_limit_cnt--;
+ DBUG_RETURN(0);
+ }
if (unit->offset_limit_cnt)
{ // Using limit offset,count
@@ -1648,29 +1748,39 @@ bool select_dumpvar::send_data(List<Item> &items)
}
if (row_count++)
{
- my_error(ER_TOO_MANY_ROWS, MYF(0));
+ my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
}
- while ((xx=li++))
+ while ((zz=my_li++) && (it++))
{
- xx->check();
- xx->update();
+ if (zz->local)
+ {
+ if ((yy=var_li++))
+ {
+ if (thd->spcont->set_item_eval(current_thd,
+ yy->get_offset(), it.ref(), zz->type))
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ if ((xx=li++))
+ {
+ xx->check();
+ xx->update();
+ }
+ }
}
DBUG_RETURN(0);
}
bool select_dumpvar::send_eof()
{
- if (row_count)
- {
- ::send_ok(thd,row_count);
- return 0;
- }
- else
- {
- my_error(ER_EMPTY_QUERY,MYF(0));
- return 1;
- }
+ if (! row_count)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
+ ::send_ok(thd,row_count);
+ return 0;
}
/****************************************************************************
@@ -1682,4 +1792,267 @@ void TMP_TABLE_PARAM::init()
field_count= sum_func_count= func_count= hidden_field_count= 0;
group_parts= group_length= group_null_parts= 0;
quick_group= 1;
+ table_charset= 0;
+}
+
+
+void thd_increment_bytes_sent(ulong length)
+{
+ THD *thd=current_thd;
+ if (likely(thd != 0))
+ { /* current_thd==0 when close_connection() calls net_send_error() */
+ thd->status_var.bytes_sent+= length;
+ }
+}
+
+
+void thd_increment_bytes_received(ulong length)
+{
+ current_thd->status_var.bytes_received+= length;
+}
+
+
+void thd_increment_net_big_packet_count(ulong length)
+{
+ current_thd->status_var.net_big_packet_count+= length;
+}
+
+
+void THD::set_status_var_init()
+{
+ bzero((char*) &status_var, sizeof(status_var));
+}
+
+
+void Security_context::init()
+{
+ host= user= priv_user= ip= 0;
+ host_or_ip= "connecting host";
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ db_access= NO_ACCESS;
+#endif
+}
+
+
+void Security_context::destroy()
+{
+ // If not pointer to constant
+ if (host != my_localhost)
+ safeFree(host);
+ if (user != delayed_user)
+ safeFree(user);
+ safeFree(ip);
+}
+
+
+void Security_context::skip_grants()
+{
+ /* privileges for the user are unknown everything is allowed */
+ host_or_ip= (char *)"";
+ master_access= ~NO_ACCESS;
+ priv_user= (char *)"";
+ *priv_host= '\0';
+}
+
+
+/****************************************************************************
+ Handling of open and locked tables states.
+
+ This is used when we want to open/lock (and then close) some tables when
+ we already have a set of tables open and locked. We use these methods for
+ access to mysql.proc table to find definitions of stored routines.
+****************************************************************************/
+
+void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
+{
+ DBUG_ENTER("reset_n_backup_open_tables_state");
+ backup->set_open_tables_state(this);
+ reset_open_tables_state();
+ DBUG_VOID_RETURN;
+}
+
+
+void THD::restore_backup_open_tables_state(Open_tables_state *backup)
+{
+ DBUG_ENTER("restore_backup_open_tables_state");
+ /*
+ Before we will throw away current open tables state we want
+ to be sure that it was properly cleaned up.
+ */
+ DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
+ handler_tables == 0 && derived_tables == 0 &&
+ lock == 0 && locked_tables == 0 &&
+ prelocked_mode == NON_PRELOCKED);
+ set_open_tables_state(backup);
+ DBUG_VOID_RETURN;
+}
+
+
+
+/****************************************************************************
+ Handling of statement states in functions and triggers.
+
+ This is used to ensure that the function/trigger gets a clean state
+ to work with and does not cause any side effects of the calling statement.
+
+ It also allows most stored functions and triggers to replicate even
+ if they are used items that would normally be stored in the binary
+ replication (like last_insert_id() etc...)
+
+ The following things is done
+ - Disable binary logging for the duration of the statement
+ - Disable multi-result-sets for the duration of the statement
+ - Value of last_insert_id() is reset and restored
+ - Value set by 'SET INSERT_ID=#' is reset and restored
+ - Value for found_rows() is reset and restored
+ - examined_row_count is added to the total
+ - cuted_fields is added to the total
+
+ NOTES:
+ Seed for random() is saved for the first! usage of RAND()
+ We reset examined_row_count and cuted_fields and add these to the
+ result to ensure that if we have a bug that would reset these within
+ a function, we are not loosing any rows from the main statement.
+****************************************************************************/
+
+void THD::reset_sub_statement_state(Sub_statement_state *backup,
+ uint new_state)
+{
+ backup->options= options;
+ backup->in_sub_stmt= in_sub_stmt;
+ backup->no_send_ok= net.no_send_ok;
+ backup->enable_slow_log= enable_slow_log;
+ backup->last_insert_id= last_insert_id;
+ backup->next_insert_id= next_insert_id;
+ backup->insert_id_used= insert_id_used;
+ backup->limit_found_rows= limit_found_rows;
+ backup->examined_row_count= examined_row_count;
+ backup->sent_row_count= sent_row_count;
+ backup->cuted_fields= cuted_fields;
+ backup->client_capabilities= client_capabilities;
+
+ if (!lex->requires_prelocking() || is_update_query(lex->sql_command))
+ options&= ~OPTION_BIN_LOG;
+ /* Disable result sets */
+ client_capabilities &= ~CLIENT_MULTI_RESULTS;
+ in_sub_stmt|= new_state;
+ last_insert_id= 0;
+ next_insert_id= 0;
+ insert_id_used= 0;
+ examined_row_count= 0;
+ sent_row_count= 0;
+ cuted_fields= 0;
+
+#ifndef EMBEDDED_LIBRARY
+ /* Surpress OK packets in case if we will execute statements */
+ net.no_send_ok= TRUE;
+#endif
+}
+
+
+void THD::restore_sub_statement_state(Sub_statement_state *backup)
+{
+ options= backup->options;
+ in_sub_stmt= backup->in_sub_stmt;
+ net.no_send_ok= backup->no_send_ok;
+ enable_slow_log= backup->enable_slow_log;
+ last_insert_id= backup->last_insert_id;
+ next_insert_id= backup->next_insert_id;
+ insert_id_used= backup->insert_id_used;
+ limit_found_rows= backup->limit_found_rows;
+ sent_row_count= backup->sent_row_count;
+ client_capabilities= backup->client_capabilities;
+
+ /*
+ The following is added to the old values as we are interested in the
+ total complexity of the query
+ */
+ examined_row_count+= backup->examined_row_count;
+ cuted_fields+= backup->cuted_fields;
+}
+
+
+/***************************************************************************
+ Handling of XA id cacheing
+***************************************************************************/
+
+pthread_mutex_t LOCK_xid_cache;
+HASH xid_cache;
+
+static byte *xid_get_hash_key(const byte *ptr,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=((XID_STATE*)ptr)->xid.length();
+ return (byte *)&((XID_STATE*)ptr)->xid;
+}
+
+static void xid_free_hash (void *ptr)
+{
+ if (!((XID_STATE*)ptr)->in_thd)
+ my_free((gptr)ptr, MYF(0));
+}
+
+bool xid_cache_init()
+{
+ pthread_mutex_init(&LOCK_xid_cache, MY_MUTEX_INIT_FAST);
+ return hash_init(&xid_cache, &my_charset_bin, 100, 0, 0,
+ xid_get_hash_key, xid_free_hash, 0) != 0;
+}
+
+void xid_cache_free()
+{
+ if (hash_inited(&xid_cache))
+ {
+ hash_free(&xid_cache);
+ pthread_mutex_destroy(&LOCK_xid_cache);
+ }
+}
+
+XID_STATE *xid_cache_search(XID *xid)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, (byte *)xid, xid->length());
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
+}
+
+
+bool xid_cache_insert(XID *xid, enum xa_states xa_state)
+{
+ XID_STATE *xs;
+ my_bool res;
+ pthread_mutex_lock(&LOCK_xid_cache);
+ if (hash_search(&xid_cache, (byte *)xid, xid->length()))
+ res=0;
+ else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME))))
+ res=1;
+ else
+ {
+ xs->xa_state=xa_state;
+ xs->xid.set(xid);
+ xs->in_thd=0;
+ res=my_hash_insert(&xid_cache, (byte*)xs);
+ }
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
}
+
+
+bool xid_cache_insert(XID_STATE *xid_state)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ DBUG_ASSERT(hash_search(&xid_cache, (byte *)&xid_state->xid,
+ xid_state->xid.length())==0);
+ my_bool res=my_hash_insert(&xid_cache, (byte*)xid_state);
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
+}
+
+
+void xid_cache_delete(XID_STATE *xid_state)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ hash_delete(&xid_cache, (byte *)xid_state);
+ pthread_mutex_unlock(&LOCK_xid_cache);
+}
+