summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorHe Zhenxing <zhenxing.he@sun.com>2009-04-08 16:17:26 +0800
committerHe Zhenxing <zhenxing.he@sun.com>2009-04-08 16:17:26 +0800
commit16641a882048f7bec779d786798771609c9d57dc (patch)
tree690e34ec8eb6f78c27c0bc0a15937b7c14e7d56f /sql
parent51a91166387760546d563ebf72bfa037fcff24a8 (diff)
parent10350e2097393cb7410eedfcf6ba533faba20a56 (diff)
downloadmariadb-git-16641a882048f7bec779d786798771609c9d57dc.tar.gz
Auto merge
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc6
-rw-r--r--sql/handler.cc16
-rw-r--r--sql/item.cc31
-rw-r--r--sql/item.h2
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sql_base.cc64
-rw-r--r--sql/sql_class.cc8
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h16
-rw-r--r--sql/sql_parse.cc82
-rw-r--r--sql/sql_select.cc11
-rw-r--r--sql/sql_yacc.yy7
15 files changed, 213 insertions, 49 deletions
diff --git a/sql/field.cc b/sql/field.cc
index f8ab4b852ec..36cc4681dec 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -8587,16 +8587,16 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
else if (tmp_length > PRECISION_FOR_FLOAT)
{
sql_type= FIELD_TYPE_DOUBLE;
- length= DBL_DIG+7; /* -[digits].E+### */
+ length= MAX_DOUBLE_STR_LENGTH;
}
else
- length= FLT_DIG+6; /* -[digits].E+## */
+ length= MAX_FLOAT_STR_LENGTH;
decimals= NOT_FIXED_DEC;
break;
}
if (!fld_length && !fld_decimals)
{
- length= FLT_DIG+6;
+ length= MAX_FLOAT_STR_LENGTH;
decimals= NOT_FIXED_DEC;
}
if (length < decimals &&
diff --git a/sql/handler.cc b/sql/handler.cc
index d069d56caae..63f652fc2b4 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -730,6 +730,16 @@ end:
if (is_real_trans)
start_waiting_global_read_lock(thd);
}
+ else if (all)
+ {
+ /*
+ A COMMIT of an empty transaction. There may be savepoints.
+ Destroy them. If the transaction is not empty
+ savepoints are cleared in ha_commit_one_phase()
+ or ha_rollback_trans().
+ */
+ thd->transaction.cleanup();
+ }
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
}
@@ -825,11 +835,11 @@ int ha_rollback_trans(THD *thd, bool all)
thd->transaction.xid_state.xid.null();
}
if (all)
- {
thd->variables.tx_isolation=thd->session_tx_isolation;
- thd->transaction.cleanup();
- }
}
+ /* Always cleanup. Even if there nht==0. There may be savepoints. */
+ if (all)
+ thd->transaction.cleanup();
#endif /* USING_TRANSACTIONS */
if (all)
thd->transaction_rollback_request= FALSE;
diff --git a/sql/item.cc b/sql/item.cc
index 86a1b59d662..c284e8b3bf4 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1323,6 +1323,7 @@ public:
else
Item_ident::print(str);
}
+ virtual Ref_Type ref_type() { return AGGREGATE_REF; }
};
@@ -6969,18 +6970,26 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
{
if (decimals != NOT_FIXED_DEC)
{
- int delta1= max_length_orig - decimals_orig;
- int delta2= item->max_length - item->decimals;
- max_length= max(delta1, delta2) + decimals;
- if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2)
- {
- max_length= FLT_DIG + 6;
- decimals= NOT_FIXED_DEC;
- }
- if (fld_type == MYSQL_TYPE_DOUBLE && max_length > DBL_DIG + 2)
+ /*
+ For FLOAT(M,D)/DOUBLE(M,D) do not change precision
+ if both fields have the same M and D
+ */
+ if (item->max_length != max_length_orig ||
+ item->decimals != decimals_orig)
{
- max_length= DBL_DIG + 7;
- decimals= NOT_FIXED_DEC;
+ int delta1= max_length_orig - decimals_orig;
+ int delta2= item->max_length - item->decimals;
+ max_length= max(delta1, delta2) + decimals;
+ if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2)
+ {
+ max_length= MAX_FLOAT_STR_LENGTH;
+ decimals= NOT_FIXED_DEC;
+ }
+ else if (fld_type == MYSQL_TYPE_DOUBLE && max_length > DBL_DIG + 2)
+ {
+ max_length= MAX_DOUBLE_STR_LENGTH;
+ decimals= NOT_FIXED_DEC;
+ }
}
}
else
diff --git a/sql/item.h b/sql/item.h
index 852b0fcc1ba..22eb0c08e2d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1899,7 +1899,7 @@ class Item_ref :public Item_ident
protected:
void set_properties();
public:
- enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF };
+ enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF };
Field *result_field; /* Save result here */
Item **ref;
Item_ref(Name_resolution_context *context_arg,
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 37ba0611ee0..4c5b838eb40 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -251,6 +251,11 @@ MY_LOCALE *my_locale_by_number(uint number);
#define PRECISION_FOR_DOUBLE 53
#define PRECISION_FOR_FLOAT 24
+/* -[digits].E+## */
+#define MAX_FLOAT_STR_LENGTH (FLT_DIG + 6)
+/* -[digits].E+### */
+#define MAX_DOUBLE_STR_LENGTH (DBL_DIG + 7)
+
/*
Default time to wait before aborting a new client connection
that does not respond to "initial server greeting" timely
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 7da52458f26..e20c3b361c0 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -894,6 +894,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
qbuf.length(0);
cur= query_str->str;
prev_pos= res= 0;
+ thd->query_name_consts= 0;
+
for (Item_splocal **splocal= sp_vars_uses.front();
splocal < sp_vars_uses.back(); splocal++)
{
@@ -927,6 +929,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
res|= qbuf.append(')');
if (res)
break;
+
+ thd->query_name_consts++;
}
res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos);
if (res)
@@ -2622,6 +2626,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
*nextp= m_ip+1;
thd->query= query;
thd->query_length= query_length;
+ thd->query_name_consts= 0;
}
DBUG_RETURN(res);
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 8f60c02a08c..2529bc594bd 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1587,27 +1587,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{ // Using table locks
TABLE *best_table= 0;
int best_distance= INT_MIN;
- bool check_if_used= thd->prelocked_mode &&
- ((int) table_list->lock_type >=
- (int) TL_WRITE_ALLOW_WRITE);
for (table=thd->open_tables; table ; table=table->next)
{
if (table->s->key_length == key_length &&
!memcmp(table->s->table_cache_key, key, key_length))
{
- if (check_if_used && table->query_id &&
- table->query_id != thd->query_id)
- {
- /*
- If we are in stored function or trigger we should ensure that
- we won't change table that is already used by calling statement.
- So if we are opening table for writing, we should check that it
- is not already open by some calling stamement.
- */
- my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
- table->s->table_name);
- DBUG_RETURN(0);
- }
if (!my_strcasecmp(system_charset_info, table->alias, alias) &&
table->query_id != thd->query_id && /* skip tables already used */
!(thd->prelocked_mode && table->query_id))
@@ -1631,13 +1615,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{
best_distance= distance;
best_table= table;
- if (best_distance == 0 && !check_if_used)
+ if (best_distance == 0)
{
/*
- If we have found perfect match and we don't need to check that
- table is not used by one of calling statements (assuming that
- we are inside of function or trigger) we can finish iterating
- through open tables list.
+ We have found a perfect match and can finish iterating
+ through open tables list. Check for table use conflict
+ between calling statement and SP/trigger is done in
+ lock_tables().
*/
break;
}
@@ -2936,9 +2920,9 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
lock_type Lock to use for open
NOTE
- This function don't do anything like SP/SF/views/triggers analysis done
- in open_tables(). It is intended for opening of only one concrete table.
- And used only in special contexts.
+ This function doesn't do anything like SP/SF/views/triggers analysis done
+ in open_tables()/lock_tables(). It is intended for opening of only one
+ concrete table. And used only in special contexts.
RETURN VALUES
table Opened table
@@ -3254,8 +3238,36 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
for (table= tables; table != first_not_own; table= table->next_global)
{
- if (!table->placeholder() &&
- check_lock_and_start_stmt(thd, table->table, table->lock_type))
+ if (table->placeholder())
+ continue;
+
+ /*
+ In a stored function or trigger we should ensure that we won't change
+ a table that is already used by the calling statement.
+ */
+ if (thd->prelocked_mode &&
+ table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ for (TABLE* opentab= thd->open_tables; opentab; opentab= opentab->next)
+ {
+ /*
+ issue an error if the tables are the same (by key comparison),
+ but query_id isn't
+ */
+ if (opentab->query_id &&
+ table->table->query_id != opentab->query_id &&
+ table->table->s->key_length == opentab->s->key_length &&
+ !memcmp(table->table->s->table_cache_key,
+ opentab->s->table_cache_key, opentab->s->key_length))
+ {
+ my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
+ table->table->s->table_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+
+ if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
ha_rollback_stmt(thd);
DBUG_RETURN(-1);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 74bc669a049..4389fd5039e 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -219,6 +219,7 @@ THD::THD()
one_shot_set= 0;
file_id = 0;
query_id= 0;
+ query_name_consts= 0;
warn_id= 0;
db_charset= global_system_variables.collation_database;
bzero(ha_data, sizeof(ha_data));
@@ -2144,6 +2145,13 @@ void Security_context::skip_grants()
}
+bool Security_context::user_matches(Security_context *them)
+{
+ return ((user != NULL) && (them->user != NULL) &&
+ !strcmp(user, them->user));
+}
+
+
/****************************************************************************
Handling of open and locked tables states.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 3e3dfcd08fa..d4f054a2b19 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -985,6 +985,7 @@ public:
{
return (*priv_host ? priv_host : (char *)"%");
}
+ bool user_matches(Security_context *);
};
@@ -1556,6 +1557,9 @@ public:
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
+ /** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
+ uint query_name_consts;
+
/*
If we do a purge of binary logs, log index info of the threads
that are currently reading it needs to be adjusted. To do that
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index dcee47f6518..34d955a8ba8 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1609,7 +1609,9 @@ public:
uint query_length;
delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg)
- :record(0), query(0), time_zone(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
+ :record(0), query(0), dup(dup_arg), ignore(ignore_arg),
+ log_query(log_query_arg), time_zone(0)
+ {}
~delayed_row()
{
x_free(record);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 4c53772bc25..da31106648a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -204,6 +204,7 @@ void lex_start(THD *thd)
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
+ lex->protect_against_global_read_lock= FALSE;
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 9f020f4adc5..3e762581e04 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1176,6 +1176,22 @@ typedef struct st_lex : public Query_tables_list
bool escape_used;
+ /*
+ Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
+
+ Protect from a impending GRL as otherwise the thread might deadlock
+ if it starts waiting for the GRL in mysql_lock_tables.
+
+ The protection is needed because there is a race between setting
+ the global read lock and waiting for all open tables to be closed.
+ The problem is a circular wait where a thread holding "old" open
+ tables will wait for the global read lock to be released while the
+ thread holding the global read lock will wait for all "old" open
+ tables to be closed -- the flush part of flush tables with read
+ lock.
+ */
+ bool protect_against_global_read_lock;
+
st_lex();
virtual ~st_lex()
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a946a6afb35..e4618874fb9 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2800,6 +2800,10 @@ mysql_execute_command(THD *thd)
if (res)
goto error;
+ if (!thd->locked_tables && lex->protect_against_global_read_lock &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
+
if (!(res= open_and_lock_tables(thd, all_tables)))
{
if (lex->describe)
@@ -3211,6 +3215,42 @@ mysql_execute_command(THD *thd)
}
if (select_lex->item_list.elements) // With select
{
+ /*
+ If:
+ a) we inside an SP and there was NAME_CONST substitution,
+ b) binlogging is on,
+ c) we log the SP as separate statements
+ raise a warning, as it may cause problems
+ (see 'NAME_CONST issues' in 'Binary Logging of Stored Programs')
+ */
+ if (thd->query_name_consts &&
+ mysql_bin_log.is_open() &&
+ !mysql_bin_log.is_query_in_union(thd, thd->query_id))
+ {
+ List_iterator_fast<Item> it(select_lex->item_list);
+ Item *item;
+ uint splocal_refs= 0;
+ /* Count SP local vars in the top-level SELECT list */
+ while ((item= it++))
+ {
+ if (item->is_splocal())
+ splocal_refs++;
+ }
+ /*
+ If it differs from number of NAME_CONST substitution applied,
+ we may have a SOME_FUNC(NAME_CONST()) in the SELECT list,
+ that may cause a problem with binary log (see BUG#35383),
+ raise a warning.
+ */
+ if (splocal_refs != thd->query_name_consts)
+ push_warning(thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+"Invoked routine ran a statement that may cause problems with "
+"binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' "
+"section of the manual.");
+ }
+
select_result *sel_result;
select_lex->options|= SELECT_NO_UNLOCK;
@@ -3627,6 +3667,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables))
break;
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
res= (up_result= mysql_update(thd, all_tables,
@@ -3653,6 +3696,15 @@ end_with_restore_list:
else
res= 0;
+ /*
+ Protection might have already been risen if its a fall through
+ from the SQLCOM_UPDATE case above.
+ */
+ if (!thd->locked_tables &&
+ lex->sql_command == SQLCOM_UPDATE_MULTI &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
+
res= mysql_multi_update_prepare(thd);
#ifdef HAVE_REPLICATION
@@ -3820,7 +3872,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
-
+ if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
res= mysql_truncate(thd, first_table, 0);
break;
case SQLCOM_DELETE:
@@ -3994,6 +4047,10 @@ end_with_restore_list:
if (check_one_table_access(thd, privilege, all_tables))
goto error;
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
+
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
lex->update_list, lex->value_list, lex->duplicates,
lex->ignore, (bool) lex->local_file);
@@ -4049,6 +4106,9 @@ end_with_restore_list:
goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
goto error;
+ if (lex->protect_against_global_read_lock &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
@@ -7390,8 +7450,26 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (tmp)
{
+
+ /*
+ If we're SUPER, we can KILL anything, including system-threads.
+ No further checks.
+
+ KILLer: thd->security_ctx->user could in theory be NULL while
+ we're still in "unauthenticated" state. This is a theoretical
+ case (the code suggests this could happen, so we play it safe).
+
+ KILLee: tmp->security_ctx->user will be NULL for system threads.
+ We need to check so Jane Random User doesn't crash the server
+ when trying to kill a) system threads or b) unauthenticated users'
+ threads (Bug#43748).
+
+ If user of both killer and killee are non-NULL, proceed with
+ slayage if both are string-equal.
+ */
+
if ((thd->security_ctx->master_access & SUPER_ACL) ||
- !strcmp(thd->security_ctx->user, tmp->security_ctx->user))
+ thd->security_ctx->user_matches(tmp->security_ctx))
{
tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3b4668a3925..777d32ba149 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -14205,6 +14205,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
Item *pos;
List_iterator_fast<Item> li(all_fields);
Copy_field *copy= NULL;
+ IF_DBUG(Copy_field *copy_start);
res_selected_fields.empty();
res_all_fields.empty();
List_iterator_fast<Item> itr(res_all_fields);
@@ -14217,12 +14218,19 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
goto err2;
param->copy_funcs.empty();
+ IF_DBUG(copy_start= copy);
for (i= 0; (pos= li++); i++)
{
Field *field;
char *tmp;
Item *real_pos= pos->real_item();
- if (real_pos->type() == Item::FIELD_ITEM)
+ /*
+ Aggregate functions can be substituted for fields (by e.g. temp tables).
+ We need to filter those substituted fields out.
+ */
+ if (real_pos->type() == Item::FIELD_ITEM &&
+ !(real_pos != pos &&
+ ((Item_ref *)pos)->ref_type() == Item_ref::AGGREGATE_REF))
{
Item_field *item;
if (!(item= new Item_field(thd, ((Item_field*) real_pos))))
@@ -14270,6 +14278,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
goto err;
if (copy)
{
+ DBUG_ASSERT (param->field_count > (uint) (copy - copy_start));
copy->set(tmp, item->result_field);
item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
#ifdef HAVE_purify
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 51fb5dbdfe4..b36c65854d7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -4522,6 +4522,7 @@ select_lock_type:
LEX *lex=Lex;
lex->current_select->set_lock_for_tables(TL_WRITE);
lex->safe_to_cache_query=0;
+ lex->protect_against_global_read_lock= TRUE;
}
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{
@@ -10058,8 +10059,12 @@ table_lock_list:
table_lock:
table_ident opt_table_alias lock_option
{
- if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3))
+ thr_lock_type lock_type= (thr_lock_type) $3;
+ if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type))
MYSQL_YYABORT;
+ /* If table is to be write locked, protect from a impending GRL. */
+ if (lock_type >= TL_WRITE_ALLOW_WRITE)
+ Lex->protect_against_global_read_lock= TRUE;
}
;