summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc18
-rw-r--r--sql/field.h17
-rw-r--r--sql/ha_innodb.cc26
-rw-r--r--sql/item.cc18
-rw-r--r--sql/item.h1
-rw-r--r--sql/item_cmpfunc.cc22
-rw-r--r--sql/item_cmpfunc.h2
-rw-r--r--sql/item_func.cc91
-rw-r--r--sql/item_func.h10
-rw-r--r--sql/item_strfunc.cc17
-rw-r--r--sql/item_strfunc.h6
-rw-r--r--sql/item_subselect.cc6
-rw-r--r--sql/item_subselect.h9
-rw-r--r--sql/item_sum.cc8
-rw-r--r--sql/mysql_priv.h9
-rw-r--r--sql/protocol.cc2
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/sp.cc17
-rw-r--r--sql/sp_head.cc248
-rw-r--r--sql/sp_head.h14
-rw-r--r--sql/sql_base.cc47
-rw-r--r--sql/sql_db.cc94
-rw-r--r--sql/sql_lex.cc25
-rw-r--r--sql/sql_lex.h4
-rw-r--r--sql/sql_parse.cc20
-rw-r--r--sql/sql_prepare.cc36
-rw-r--r--sql/sql_select.cc85
-rw-r--r--sql/sql_show.cc138
-rw-r--r--sql/sql_table.cc7
-rw-r--r--sql/sql_trigger.cc40
-rw-r--r--sql/sql_view.cc113
-rw-r--r--sql/sql_yacc.yy46
-rw-r--r--sql/table.cc16
-rw-r--r--sql/table.h8
-rw-r--r--sql/udf_example.c2
35 files changed, 902 insertions, 322 deletions
diff --git a/sql/field.cc b/sql/field.cc
index 510e3bd259f..2c484bb0979 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4387,6 +4387,24 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg,
}
+Field_timestamp::Field_timestamp(bool maybe_null_arg,
+ const char *field_name_arg,
+ struct st_table *table_arg, CHARSET_INFO *cs)
+ :Field_str((char*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, table_arg, cs)
+{
+ /* For 4.0 MYD and 4.0 InnoDB compatibility */
+ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
+ if (table && !table->timestamp_field &&
+ unireg_check != NONE)
+ {
+ /* This timestamp has auto-update */
+ table->timestamp_field= this;
+ flags|=TIMESTAMP_FLAG;
+ }
+}
+
+
/*
Get auto-set type for TIMESTAMP field.
diff --git a/sql/field.h b/sql/field.h
index 09638b9a979..3306c4123db 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -780,6 +780,8 @@ public:
enum utype unireg_check_arg, const char *field_name_arg,
struct st_table *table_arg,
CHARSET_INFO *cs);
+ Field_timestamp(bool maybe_null_arg, const char *field_name_arg,
+ struct st_table *table_arg, CHARSET_INFO *cs);
enum_field_types type() const { return FIELD_TYPE_TIMESTAMP;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
enum Item_result cmp_type () const { return INT_RESULT; }
@@ -1128,6 +1130,21 @@ public:
{
flags|= BLOB_FLAG;
}
+ Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+ struct st_table *table_arg, CHARSET_INFO *cs, bool set_packlength)
+ :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, table_arg, cs)
+ {
+ flags|= BLOB_FLAG;
+ packlength= 4;
+ if (set_packlength)
+ {
+ uint32 char_length= len_arg/cs->mbmaxlen;
+ packlength= char_length <= 255 ? 1 :
+ char_length <= 65535 ? 2 :
+ char_length <= 16777215 ? 3 : 4;
+ }
+ }
enum_field_types type() const { return FIELD_TYPE_BLOB;}
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 6aadce0191a..472506f9903 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -5943,14 +5943,6 @@ ha_innobase::start_stmt(
innobase_release_stat_resources(trx);
- if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && trx->global_read_view) {
- /* At low transaction isolation levels we let
- each consistent read set its own snapshot */
-
- read_view_close_for_mysql(trx);
- }
-
prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0;
prebuilt->read_just_key = 0;
@@ -6684,17 +6676,17 @@ ha_innobase::store_lock(
&& !thd->tablespace_op
&& thd->lex->sql_command != SQLCOM_TRUNCATE
&& thd->lex->sql_command != SQLCOM_OPTIMIZE
+
#ifdef __WIN__
- /*
- for alter table on win32 for succesfull operation
- completion it is used TL_WRITE(=10) lock instead of
- TL_WRITE_ALLOW_READ(=6), however here in innodb handler
- TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes
- race condition when several clients do alter table
- simultaneously (bug #17264). This fix avoids the problem.
- */
- && thd->lex->sql_command != SQLCOM_ALTER_TABLE
+ /* For alter table on win32 for succesful operation
+ completion it is used TL_WRITE(=10) lock instead of
+ TL_WRITE_ALLOW_READ(=6), however here in innodb handler
+ TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes
+ race condition when several clients do alter table
+ simultaneously (bug #17264). This fix avoids the problem. */
+ && thd->lex->sql_command != SQLCOM_ALTER_TABLE
#endif
+
&& thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
lock_type = TL_WRITE_ALLOW_WRITE;
diff --git a/sql/item.cc b/sql/item.cc
index 7d6ce031e15..27cb6d49fd4 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1424,7 +1424,8 @@ bool agg_item_charsets(DTCollation &coll, const char *fname,
In case we're in statement prepare, create conversion item
in its memory: it will be reused on each execute.
*/
- arena= thd->activate_stmt_arena_if_needed(&backup);
+ arena= thd->is_stmt_prepare() ? thd->activate_stmt_arena_if_needed(&backup)
+ : NULL;
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
{
@@ -1459,7 +1460,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname,
been created in prepare. In this case register the change for
rollback.
*/
- if (arena && arena->is_conventional())
+ if (arena)
*arg= conv;
else
thd->change_item_tree(arg, conv);
@@ -3903,7 +3904,9 @@ Field *Item::make_string_field(TABLE *table)
if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB)
return new Field_blob(max_length, maybe_null, name, table,
collation.collation);
- if (max_length > 0)
+ /* Item_type_holder holds the exact type, do not change it */
+ if (max_length > 0 &&
+ (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING))
return new Field_varstring(max_length, maybe_null, name, table,
collation.collation);
return new Field_string(max_length, maybe_null, name, table,
@@ -3967,6 +3970,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table)
case MYSQL_TYPE_TIME:
return new Field_time(maybe_null, name, table, &my_charset_bin);
case MYSQL_TYPE_TIMESTAMP:
+ return new Field_timestamp(maybe_null, name, table, &my_charset_bin);
case MYSQL_TYPE_DATETIME:
return new Field_datetime(maybe_null, name, table, &my_charset_bin);
case MYSQL_TYPE_YEAR:
@@ -3990,7 +3994,11 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table)
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_GEOMETRY:
- return new Field_blob(max_length, maybe_null, name, table,
+ if (this->type() == Item::TYPE_HOLDER)
+ return new Field_blob(max_length, maybe_null, name, table,
+ collation.collation, 1);
+ else
+ return new Field_blob(max_length, maybe_null, name, table,
collation.collation);
break; // Blob handled outside of case
}
@@ -6159,7 +6167,7 @@ uint32 Item_type_holder::display_length(Item *item)
case MYSQL_TYPE_DOUBLE:
return 53;
case MYSQL_TYPE_NULL:
- return 4;
+ return 0;
case MYSQL_TYPE_LONGLONG:
return 20;
case MYSQL_TYPE_INT24:
diff --git a/sql/item.h b/sql/item.h
index 0f49145082f..514c31c2d74 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -752,6 +752,7 @@ public:
virtual bool find_item_in_field_list_processor(byte *arg) { return 0; }
virtual bool change_context_processor(byte *context) { return 0; }
virtual bool reset_query_id_processor(byte *query_id) { return 0; }
+ virtual bool is_expensive_processor(byte *arg) { return 0; }
virtual Item *equal_fields_propagator(byte * arg) { return this; }
virtual Item *set_no_const_sub(byte *arg) { return this; }
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 98453899375..34170124cd7 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3656,6 +3656,28 @@ Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */
}
+Item *Item_func_nop_all::neg_transformer(THD *thd)
+{
+ /* "NOT (e $cmp$ ANY (SELECT ...)) -> e $rev_cmp$" ALL (SELECT ...) */
+ Item_func_not_all *new_item= new Item_func_not_all(args[0]);
+ Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
+ allany->func= allany->func_creator(FALSE);
+ allany->all= !allany->all;
+ allany->upper_item= new_item;
+ return new_item;
+}
+
+Item *Item_func_not_all::neg_transformer(THD *thd)
+{
+ /* "NOT (e $cmp$ ALL (SELECT ...)) -> e $rev_cmp$" ANY (SELECT ...) */
+ Item_func_nop_all *new_item= new Item_func_nop_all(args[0]);
+ Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
+ allany->all= !allany->all;
+ allany->func= allany->func_creator(TRUE);
+ allany->upper_item= new_item;
+ return new_item;
+}
+
Item *Item_func_eq::negated_item() /* a = b -> a != b */
{
return new Item_func_ne(args[0], args[1]);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index a2b10eacc79..47f9f2aa98f 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -311,6 +311,7 @@ public:
void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; };
void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; };
bool empty_underlying_subquery();
+ Item *neg_transformer(THD *thd);
};
@@ -321,6 +322,7 @@ public:
Item_func_nop_all(Item *a) :Item_func_not_all(a) {}
longlong val_int();
const char *func_name() const { return "<nop>"; }
+ Item *neg_transformer(THD *thd);
};
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 6df72fdf8c2..a5786a2c87f 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -398,6 +398,13 @@ Field *Item_func::tmp_table_field(TABLE *t_arg)
return res;
}
+
+bool Item_func::is_expensive_processor(byte *arg)
+{
+ return is_expensive();
+}
+
+
my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed);
@@ -4840,7 +4847,9 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld)
{
bool err_status= TRUE;
Sub_statement_state statement_state;
- Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx= thd->security_ctx;
+#endif
DBUG_ENTER("Item_func_sp::execute_impl");
@@ -4851,7 +4860,7 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld)
thd->security_ctx= context->security_ctx;
}
#endif
- if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func))
+ if (find_and_check_access(thd))
goto error;
/*
@@ -4863,13 +4872,11 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld)
err_status= m_sp->execute_function(thd, args, arg_count, return_value_fld);
thd->restore_sub_statement_state(&statement_state);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_ctx_func);
error:
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
thd->security_ctx= save_security_ctx;
-#else
-error:
#endif
+
DBUG_RETURN(err_status);
}
@@ -4986,70 +4993,38 @@ Item_func_sp::tmp_table_field(TABLE *t_arg)
SYNOPSIS
find_and_check_access()
thd thread handler
- want_access requested access
- save backup of security context
RETURN
FALSE Access granted
TRUE Requested access can't be granted or function doesn't exists
- In this case security context is not changed and *save = 0
NOTES
Checks if requested access to function can be granted to user.
If function isn't found yet, it searches function first.
If function can't be found or user don't have requested access
error is raised.
- If security context sp_ctx is provided and access can be granted then
- switch back to previous context isn't performed.
- In case of access error or if context is not provided then
- find_and_check_access() switches back to previous security context.
*/
bool
-Item_func_sp::find_and_check_access(THD *thd, ulong want_access,
- Security_context **save)
+Item_func_sp::find_and_check_access(THD *thd)
{
- bool res= TRUE;
-
- *save= 0; // Safety if error
if (! m_sp && ! (m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
&thd->sp_func_cache, TRUE)))
{
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
- goto error;
+ return TRUE;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_routine_access(thd, want_access,
+ if (check_routine_access(thd, EXECUTE_ACL,
m_sp->m_db.str, m_sp->m_name.str, 0, FALSE))
- goto error;
-
- sp_change_security_context(thd, m_sp, save);
- /*
- If we changed context to run as another user, we need to check the
- access right for the new context again as someone may have deleted
- this person the right to use the procedure
-
- TODO:
- Cache if the definer has the right to use the object on the first
- usage and only reset the cache if someone does a GRANT statement
- that 'may' affect this.
- */
- if (*save &&
- check_routine_access(thd, want_access,
- m_sp->m_db.str, m_sp->m_name.str, 0, FALSE))
- {
- sp_restore_security_context(thd, *save);
- *save= 0; // Safety
- goto error;
- }
+ return TRUE;
#endif
- res= FALSE; // no error
-error:
- return res;
+ return FALSE;
}
+
bool
Item_func_sp::fix_fields(THD *thd, Item **ref)
{
@@ -5060,19 +5035,23 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
{
/*
Here we check privileges of the stored routine only during view
- creation, in order to validate the view. A runtime check is perfomed
- in Item_func_sp::execute(), and this method is not called during
- context analysis. We do not need to restore the security context
- changed in find_and_check_access because all view structures created
- in CREATE VIEW are not used for execution. Notice, that during view
- creation we do not infer into stored routine bodies and do not check
- privileges of its statements, which would probably be a good idea
- especially if the view has SQL SECURITY DEFINER and the used stored
- procedure has SQL SECURITY DEFINER
+ creation, in order to validate the view. A runtime check is
+ perfomed in Item_func_sp::execute(), and this method is not
+ called during context analysis. Notice, that during view
+ creation we do not infer into stored routine bodies and do not
+ check privileges of its statements, which would probably be a
+ good idea especially if the view has SQL SECURITY DEFINER and
+ the used stored procedure has SQL SECURITY DEFINER.
*/
- Security_context *save_ctx;
- if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx)))
- sp_restore_security_context(thd, save_ctx);
+ res= find_and_check_access(thd);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_secutiry_ctx;
+ if (!res && !(res= set_routine_security_ctx(thd, m_sp, false,
+ &save_secutiry_ctx)))
+ {
+ sp_restore_security_context(thd, save_secutiry_ctx);
+ }
+#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */
}
return res;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index af1bdad199e..30b62f3642d 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -55,7 +55,7 @@ public:
NOT_FUNC, NOT_ALL_FUNC,
NOW_FUNC, TRIG_COND_FUNC,
GUSERVAR_FUNC, COLLATE_FUNC,
- EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP };
+ EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; }
@@ -189,6 +189,8 @@ public:
Item *transform(Item_transformer transformer, byte *arg);
void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order);
+ bool is_expensive_processor(byte *arg);
+ virtual bool is_expensive() { return 0; }
};
@@ -933,6 +935,7 @@ public:
Item_udf_func(udf_func *udf_arg, List<Item> &list)
:Item_func(list), udf(udf_arg) {}
const char *func_name() const { return udf.name(); }
+ enum Functype functype() const { return UDF_FUNC; }
bool fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
@@ -945,6 +948,7 @@ public:
void cleanup();
Item_result result_type () const { return udf.result_type(); }
table_map not_null_tables() const { return 0; }
+ bool is_expensive() { return 1; }
};
@@ -1466,11 +1470,11 @@ public:
{ context= (Name_resolution_context *)cntx; return FALSE; }
void fix_length_and_dec();
- bool find_and_check_access(THD * thd, ulong want_access,
- Security_context **backup);
+ bool find_and_check_access(THD * thd);
virtual enum Functype functype() const { return FUNC_SP; }
bool fix_fields(THD *thd, Item **ref);
+ bool is_expensive() { return 1; }
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index c63a89351c6..0cd0ffcc38b 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1503,6 +1503,23 @@ void Item_func_trim::fix_length_and_dec()
}
}
+void Item_func_trim::print(String *str)
+{
+ if (arg_count == 1)
+ {
+ Item_func::print(str);
+ return;
+ }
+ str->append(Item_func_trim::func_name());
+ str->append('(');
+ str->append(mode_name());
+ str->append(' ');
+ args[1]->print(str);
+ str->append(STRING_WITH_LEN(" from "));
+ args[0]->print(str);
+ str->append(')');
+}
+
/* Item_func_password */
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 7abbe232249..eae7272835d 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -233,6 +233,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "trim"; }
+ void print(String *str);
+ virtual const char *mode_name() const { return "both"; }
};
@@ -243,6 +245,7 @@ public:
Item_func_ltrim(Item *a) :Item_func_trim(a) {}
String *val_str(String *);
const char *func_name() const { return "ltrim"; }
+ const char *mode_name() const { return "leading"; }
};
@@ -253,6 +256,7 @@ public:
Item_func_rtrim(Item *a) :Item_func_trim(a) {}
String *val_str(String *);
const char *func_name() const { return "rtrim"; }
+ const char *mode_name() const { return "trailing"; }
};
@@ -724,7 +728,7 @@ public:
void fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
const char *func_name() const { return "collate"; }
- enum Functype func_type() const { return COLLATE_FUNC; }
+ enum Functype functype() const { return COLLATE_FUNC; }
void print(String *str);
Item_field *filed_for_view_update()
{
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 5ca2b602c25..0f2ae71bf05 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -572,14 +572,14 @@ Item_in_subselect::Item_in_subselect(Item * left_exp,
}
Item_allany_subselect::Item_allany_subselect(Item * left_exp,
- Comp_creator *fn,
+ chooser_compare_func_creator fc,
st_select_lex *select_lex,
bool all_arg)
- :Item_in_subselect(), all(all_arg)
+ :Item_in_subselect(), func_creator(fc), all(all_arg)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
- func= fn;
+ func= func_creator(all_arg);
init(select_lex, new select_exists_subselect(this));
max_columns= 1;
abort_on_null= 0;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 293408dc09e..45df4f3880d 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -269,14 +269,13 @@ public:
/* ALL/ANY/SOME subselect */
class Item_allany_subselect :public Item_in_subselect
{
-protected:
- Comp_creator *func;
-
public:
+ chooser_compare_func_creator func_creator;
+ Comp_creator *func;
bool all;
- Item_allany_subselect(Item * left_expr, Comp_creator *f,
- st_select_lex *select_lex, bool all);
+ Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc,
+ st_select_lex *select_lex, bool all);
// only ALL subquery has upper not
subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 898ea28985b..0d2a5b3b080 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -377,7 +377,13 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table,
case INT_RESULT:
return new Field_longlong(max_length,maybe_null,name,table,unsigned_flag);
case STRING_RESULT:
- if (max_length/collation.collation->mbmaxlen > 255 && convert_blob_length)
+ /*
+ Make sure that the blob fits into a Field_varstring which has
+ 2-byte lenght.
+ */
+ if (max_length/collation.collation->mbmaxlen > 255 &&
+ max_length/collation.collation->mbmaxlen < UINT_MAX16 &&
+ convert_blob_length)
return new Field_varstring(convert_blob_length, maybe_null,
name, table,
collation.collation);
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 089bc965c8c..c776fc72b16 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -520,9 +520,11 @@ enum enum_var_type
OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
};
class sys_var;
+class Comp_creator;
+typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
#include "item.h"
extern my_decimal decimal_zero;
-typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
+
/* sql_parse.cc */
void free_items(Item *item);
void cleanup_items(Item *item);
@@ -865,8 +867,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
void mysqld_list_processes(THD *thd,const char *user,bool verbose);
int mysqld_show_status(THD *thd);
int mysqld_show_variables(THD *thd,const char *wild);
-int mysql_find_files(THD *thd,List<char> *files, const char *db,
- const char *path, const char *wild, bool dir);
bool mysqld_show_storage_engines(THD *thd);
bool mysqld_show_privileges(THD *thd);
bool mysqld_show_column_types(THD *thd);
@@ -1138,7 +1138,10 @@ uint check_word(TYPELIB *lib, const char *val, const char *end,
bool is_keyword(const char *name, uint len);
#define MY_DB_OPT_FILE "db.opt"
+bool check_db_dir_existence(const char *db_name);
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
+bool load_db_opt_by_name(THD *thd, const char *db_name,
+ HA_CREATE_INFO *db_create_info);
bool my_dbopt_init(void);
void my_dbopt_cleanup(void);
void my_dbopt_free(void);
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 650bd8fc58f..f4efb8004ee 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -322,7 +322,7 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
254 Marker (1 byte)
warning_count Stored in 2 bytes; New in 4.1 protocol
status_flag Stored in 2 bytes;
- For flags like SERVER_STATUS_MORE_RESULTS
+ For flags like SERVER_MORE_RESULTS_EXISTS
Note that the warning count will not be sent if 'no_flush' is set as
we don't want to report the warning count until all data is sent to the
diff --git a/sql/slave.cc b/sql/slave.cc
index 90e95e812bd..8bcaa6be102 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -3051,7 +3051,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings)
return packet_error;
#endif
- len = net_safe_read(mysql);
+ len = cli_safe_read(mysql);
if (len == packet_error || (long) len < 1)
{
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
diff --git a/sql/sp.cc b/sql/sp.cc
index a7078da2f50..b7bf049cb1d 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -495,6 +495,13 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
table.s = &table.share_not_to_be_used;
field= sp->create_result_field(0, 0, &table);
field->sql_type(result);
+
+ if (field->has_charset())
+ {
+ result.append(STRING_WITH_LEN(" CHARSET "));
+ result.append(field->charset()->csname);
+ }
+
delete field;
}
@@ -626,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp)
log_query.append(STRING_WITH_LEN("CREATE "));
append_definer(thd, &log_query, &thd->lex->definer->user,
&thd->lex->definer->host);
- log_query.append(thd->lex->stmt_definition_begin);
+ log_query.append(thd->lex->stmt_definition_begin,
+ (char *)sp->m_body_begin -
+ thd->lex->stmt_definition_begin +
+ sp->m_body.length);
/* Such a statement can always go directly to binlog, no trans cache */
Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0,
@@ -974,6 +984,11 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
sp_head *new_sp;
const char *returns= "";
char definer[USER_HOST_BUFF_SIZE];
+
+ /*
+ String buffer for RETURNS data type must have system charset;
+ 64 -- size of "returns" column of mysql.proc.
+ */
String retstr(64);
DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp));
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 9965c48935a..eec6e0fc3cd 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -470,7 +470,7 @@ sp_head::init(LEX *lex)
lex->trg_table_fields.empty();
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
m_param_begin= m_param_end= m_body_begin= 0;
- m_qname.str= m_db.str= m_name.str= m_params.str=
+ m_qname.str= m_db.str= m_name.str= m_params.str=
m_body.str= m_defstr.str= 0;
m_qname.length= m_db.length= m_name.length= m_params.length=
m_body.length= m_defstr.length= 0;
@@ -478,29 +478,42 @@ sp_head::init(LEX *lex)
DBUG_VOID_RETURN;
}
+
+void
+sp_head::init_sp_name(THD *thd, sp_name *spname)
+{
+ DBUG_ENTER("sp_head::init_sp_name");
+
+ /* Must be initialized in the parser. */
+
+ DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length);
+
+ /* We have to copy strings to get them into the right memroot. */
+
+ m_db.length= spname->m_db.length;
+ m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length);
+
+ m_name.length= spname->m_name.length;
+ m_name.str= strmake_root(thd->mem_root, spname->m_name.str,
+ spname->m_name.length);
+
+ if (spname->m_qname.length == 0)
+ spname->init_qname(thd);
+
+ m_qname.length= spname->m_qname.length;
+ m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str,
+ m_qname.length);
+}
+
+
void
-sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
+sp_head::init_strings(THD *thd, LEX *lex)
{
DBUG_ENTER("sp_head::init_strings");
uchar *endp; /* Used to trim the end */
/* During parsing, we must use thd->mem_root */
MEM_ROOT *root= thd->mem_root;
- DBUG_ASSERT(name);
- /* Must be initialized in the parser */
- DBUG_ASSERT(name->m_db.str && name->m_db.length);
-
- /* We have to copy strings to get them into the right memroot */
- m_db.length= name->m_db.length;
- m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
- m_name.length= name->m_name.length;
- m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
-
- if (name->m_qname.length == 0)
- name->init_qname(thd);
- m_qname.length= name->m_qname.length;
- m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
-
if (m_param_begin && m_param_end)
{
m_params.length= m_param_end - m_param_begin;
@@ -514,10 +527,7 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
Trim "garbage" at the end. This is sometimes needed with the
"/ * ! VERSION... * /" wrapper in dump files.
*/
- while (m_body_begin < endp &&
- (endp[-1] <= ' ' || endp[-1] == '*' ||
- endp[-1] == '/' || endp[-1] == ';'))
- endp-= 1;
+ endp= skip_rear_comments(m_body_begin, endp);
m_body.length= endp - m_body_begin;
m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
@@ -1097,6 +1107,7 @@ sp_head::execute(THD *thd)
thd->restore_active_arena(&execute_arena, &backup_arena);
+ thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
/* Restore all saved */
old_packet.swap(thd->packet);
@@ -1158,6 +1169,161 @@ sp_head::execute(THD *thd)
m_first_instance->m_first_free_instance->m_recursion_level ==
m_recursion_level + 1));
m_first_instance->m_first_free_instance= this;
+
+ DBUG_RETURN(err_status);
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ set_routine_security_ctx() changes routine security context, and
+ checks if there is an EXECUTE privilege in new context. If there is
+ no EXECUTE privilege, it changes the context back and returns a
+ error.
+
+ SYNOPSIS
+ set_routine_security_ctx()
+ thd thread handle
+ sp stored routine to change the context for
+ is_proc TRUE is procedure, FALSE if function
+ save_ctx pointer to an old security context
+
+ RETURN
+ TRUE if there was a error, and the context wasn't changed.
+ FALSE if the context was changed.
+*/
+
+bool
+set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
+ Security_context **save_ctx)
+{
+ *save_ctx= 0;
+ if (sp_change_security_context(thd, sp, save_ctx))
+ return TRUE;
+
+ /*
+ If we changed context to run as another user, we need to check the
+ access right for the new context again as someone may have revoked
+ the right to use the procedure from this user.
+
+ TODO:
+ Cache if the definer has the right to use the object on the
+ first usage and only reset the cache if someone does a GRANT
+ statement that 'may' affect this.
+ */
+ if (*save_ctx &&
+ check_routine_access(thd, EXECUTE_ACL,
+ sp->m_db.str, sp->m_name.str, is_proc, FALSE))
+ {
+ sp_restore_security_context(thd, *save_ctx);
+ *save_ctx= 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif // ! NO_EMBEDDED_ACCESS_CHECKS
+
+
+/*
+ Execute a trigger:
+ - changes security context for triggers
+ - switch to new memroot
+ - call sp_head::execute
+ - restore old memroot
+ - restores security context
+
+ SYNOPSIS
+ sp_head::execute_trigger()
+ thd Thread handle
+ db database name
+ table table name
+ grant_info GRANT_INFO structure to be filled with
+ information about definer's privileges
+ on subject table
+
+ RETURN
+ FALSE on success
+ TRUE on error
+*/
+
+bool
+sp_head::execute_trigger(THD *thd, const char *db, const char *table,
+ GRANT_INFO *grant_info)
+{
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ bool err_status= FALSE;
+ MEM_ROOT call_mem_root;
+ Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
+ Query_arena backup_arena;
+
+ DBUG_ENTER("sp_head::execute_trigger");
+ DBUG_PRINT("info", ("trigger %s", m_name.str));
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_ctx;
+ if (sp_change_security_context(thd, this, &save_ctx))
+ DBUG_RETURN(TRUE);
+
+ /*
+ NOTE: TRIGGER_ACL should be used here.
+ */
+ if (check_global_access(thd, SUPER_ACL))
+ {
+ sp_restore_security_context(thd, save_ctx);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Fetch information about table-level privileges to GRANT_INFO
+ structure for subject table. Check of privileges that will use it
+ and information about column-level privileges will happen in
+ Item_trigger_field::fix_fields().
+ */
+ fill_effective_table_privileges(thd, grant_info, db, table);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+
+ /*
+ Prepare arena and memroot for objects which lifetime is whole
+ duration of trigger call (sp_rcontext, it's tables and items,
+ sp_cursor and Item_cache holders for case expressions). We can't
+ use caller's arena/memroot for those objects because in this case
+ some fixed amount of memory will be consumed for each trigger
+ invocation and so statements which involve lot of them will hog
+ memory.
+
+ TODO: we should create sp_rcontext once per command and reuse it
+ on subsequent executions of a trigger.
+ */
+ init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ thd->set_n_backup_active_arena(&call_arena, &backup_arena);
+
+ if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) ||
+ nctx->init(thd))
+ {
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
+
+#ifndef DBUG_OFF
+ nctx->sp= this;
+#endif
+
+ thd->spcont= nctx;
+
+ err_status= execute(thd);
+
+err_with_cleanup:
+ thd->restore_active_arena(&call_arena, &backup_arena);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, save_ctx);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+ delete nctx;
+ call_arena.free_items();
+ free_root(&call_mem_root, MYF(0));
+ thd->spcont= octx;
+
DBUG_RETURN(err_status);
}
@@ -1165,8 +1331,12 @@ sp_head::execute(THD *thd)
/*
Execute a function:
- evaluate parameters
+ - changes security context for SUID routines
+ - switch to new memroot
- call sp_head::execute
+ - restore old memroot
- evaluate the return value
+ - restores security context
SYNOPSIS
sp_head::execute_function()
@@ -1293,6 +1463,15 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
thd->spcont= nctx;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx;
+ if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx))
+ {
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
+#endif
+
binlog_save_options= thd->options;
if (need_binlog_call)
{
@@ -1333,7 +1512,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
reset_dynamic(&thd->user_var_events);
}
- if (m_type == TYPE_ENUM_FUNCTION && !err_status)
+ if (!err_status)
{
/* We need result only in function but not in trigger */
@@ -1344,8 +1523,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
}
-
- nctx->pop_all_cursors(); // To avoid memory leaks after an error
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, save_security_ctx);
+#endif
err_with_cleanup:
delete nctx;
@@ -1368,8 +1548,10 @@ err_with_cleanup:
The function does the following steps:
- Set all parameters
+ - changes security context for SUID routines
- call sp_head::execute
- copy back values of INOUT and OUT parameters
+ - restores security context
RETURN
FALSE on success
@@ -1490,6 +1672,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont= nctx;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx= 0;
+ if (!err_status)
+ err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx);
+#endif
+
if (!err_status)
err_status= execute(thd);
@@ -1534,10 +1722,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (save_security_ctx)
+ sp_restore_security_context(thd, save_security_ctx);
+#endif
+
if (!save_spcont)
delete octx;
- nctx->pop_all_cursors(); // To avoid memory leaks after an error
delete nctx;
thd->spcont= save_spcont;
@@ -1674,14 +1866,18 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
create_field *field_def)
{
+ HA_CREATE_INFO sp_db_info;
LEX_STRING cmt = { 0, 0 };
uint unused1= 0;
int unused2= 0;
+ load_db_opt_by_name(thd, m_db.str, &sp_db_info);
+
if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
&lex->interval_list,
- (lex->charset ? lex->charset : default_charset_info),
+ (lex->charset ? lex->charset :
+ sp_db_info.default_table_charset),
lex->uint_geom_type))
return TRUE;
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 073cca2cd12..4cd34bc9e20 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -193,9 +193,13 @@ public:
void
init(LEX *lex);
+ /* Copy sp name from parser. */
+ void
+ init_sp_name(THD *thd, sp_name *spname);
+
// Initialize strings after parsing header
void
- init_strings(THD *thd, LEX *lex, sp_name *name);
+ init_strings(THD *thd, LEX *lex);
int
create(THD *thd);
@@ -207,6 +211,10 @@ public:
destroy();
bool
+ execute_trigger(THD *thd, const char *db, const char *table,
+ GRANT_INFO *grant_onfo);
+
+ bool
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
bool
@@ -1149,6 +1157,10 @@ sp_change_security_context(THD *thd, sp_head *sp,
Security_context **backup);
void
sp_restore_security_context(THD *thd, Security_context *backup);
+
+bool
+set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
+ Security_context **save_ctx);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
TABLE_LIST *
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 5383bb52aaa..28edee5c729 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -4041,36 +4041,48 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref,
if (table_ref->nested_join)
{
List_iterator_fast<TABLE_LIST> nested_it(table_ref->nested_join->join_list);
- TABLE_LIST *cur_left_neighbor= nested_it++;
- TABLE_LIST *cur_right_neighbor= NULL;
+ TABLE_LIST *same_level_left_neighbor= nested_it++;
+ TABLE_LIST *same_level_right_neighbor= NULL;
+ /* Left/right-most neighbors, possibly at higher levels in the join tree. */
+ TABLE_LIST *real_left_neighbor, *real_right_neighbor;
- while (cur_left_neighbor)
+ while (same_level_left_neighbor)
{
- TABLE_LIST *cur_table_ref= cur_left_neighbor;
- cur_left_neighbor= nested_it++;
+ TABLE_LIST *cur_table_ref= same_level_left_neighbor;
+ same_level_left_neighbor= nested_it++;
/*
The order of RIGHT JOIN operands is reversed in 'join list' to
transform it into a LEFT JOIN. However, in this procedure we need
the join operands in their lexical order, so below we reverse the
- join operands. Notice that this happens only in the first loop, and
- not in the second one, as in the second loop cur_left_neighbor == NULL.
- This is the correct behavior, because the second loop
- sets cur_table_ref reference correctly after the join operands are
+ join operands. Notice that this happens only in the first loop,
+ and not in the second one, as in the second loop
+ same_level_left_neighbor == NULL.
+ This is the correct behavior, because the second loop sets
+ cur_table_ref reference correctly after the join operands are
swapped in the first loop.
*/
- if (cur_left_neighbor &&
+ if (same_level_left_neighbor &&
cur_table_ref->outer_join & JOIN_TYPE_RIGHT)
{
/* This can happen only for JOIN ... ON. */
DBUG_ASSERT(table_ref->nested_join->join_list.elements == 2);
- swap_variables(TABLE_LIST*, cur_left_neighbor, cur_table_ref);
+ swap_variables(TABLE_LIST*, same_level_left_neighbor, cur_table_ref);
}
+ /*
+ Pick the parent's left and right neighbors if there are no immediate
+ neighbors at the same level.
+ */
+ real_left_neighbor= (same_level_left_neighbor) ?
+ same_level_left_neighbor : left_neighbor;
+ real_right_neighbor= (same_level_right_neighbor) ?
+ same_level_right_neighbor : right_neighbor;
+
if (cur_table_ref->nested_join &&
store_top_level_join_columns(thd, cur_table_ref,
- cur_left_neighbor, cur_right_neighbor))
+ real_left_neighbor, real_right_neighbor))
goto err;
- cur_right_neighbor= cur_table_ref;
+ same_level_right_neighbor= cur_table_ref;
}
}
@@ -4947,12 +4959,17 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
bool ignore_errors)
{
List_iterator_fast<Item> f(fields),v(values);
- Item *value;
+ Item *value, *fld;
Item_field *field;
DBUG_ENTER("fill_record");
- while ((field=(Item_field*) f++))
+ while ((fld= f++))
{
+ if (!(field= fld->filed_for_view_update()))
+ {
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
+ DBUG_RETURN(TRUE);
+ }
value=v++;
Field *rfield= field->field;
TABLE *table= rfield->table;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 902539dfdec..81508e4e9be 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -295,7 +295,6 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
create Where to store the read options
DESCRIPTION
- For now, only default-character-set is read.
RETURN VALUES
0 File found
@@ -384,6 +383,50 @@ err1:
/*
+ Retrieve database options by name. Load database options file or fetch from
+ cache.
+
+ SYNOPSIS
+ load_db_opt_by_name()
+ db_name Database name
+ db_create_info Where to store the database options
+
+ DESCRIPTION
+ load_db_opt_by_name() is a shortcut for load_db_opt().
+
+ NOTE
+ Although load_db_opt_by_name() (and load_db_opt()) returns status of
+ the operation, it is useless usually and should be ignored. The problem
+ is that there are 1) system databases ("mysql") and 2) virtual
+ databases ("information_schema"), which do not contain options file.
+ So, load_db_opt[_by_name]() returns FALSE for these databases, but this
+ is not an error.
+
+ load_db_opt[_by_name]() clears db_create_info structure in any case, so
+ even on failure it contains valid data. So, common use case is just
+ call load_db_opt[_by_name]() without checking return value and use
+ db_create_info right after that.
+
+ RETURN VALUES (read NOTE!)
+ FALSE Success
+ TRUE Failed to retrieve options
+*/
+
+bool load_db_opt_by_name(THD *thd, const char *db_name,
+ HA_CREATE_INFO *db_create_info)
+{
+ char db_opt_path[FN_REFLEN];
+
+ strxnmov(db_opt_path, sizeof (db_opt_path) - 1, mysql_data_home, "/",
+ db_name, "/", MY_DB_OPT_FILE, NullS);
+
+ unpack_filename(db_opt_path, db_opt_path);
+
+ return load_db_opt(thd, db_opt_path, db_create_info);
+}
+
+
+/*
Create a database
SYNOPSIS
@@ -1126,8 +1169,6 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check)
{
int path_length, db_length;
char *db_name;
- char path[FN_REFLEN];
- HA_CREATE_INFO create;
bool system_db= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
@@ -1196,16 +1237,14 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check)
}
}
#endif
- (void) sprintf(path,"%s/%s", mysql_data_home, db_name);
- path_length= unpack_dirname(path, path); // Convert if not UNIX
- if (path_length && path[path_length-1] == FN_LIBCHAR)
- path[path_length-1]= '\0'; // remove ending '\'
- if (my_access(path,F_OK))
+
+ if (check_db_dir_existence(db_name))
{
my_error(ER_BAD_DB_ERROR, MYF(0), db_name);
my_free(db_name, MYF(0));
DBUG_RETURN(1);
}
+
end:
x_free(thd->db);
DBUG_ASSERT(db_name == NULL || db_name[0] != '\0');
@@ -1221,8 +1260,10 @@ end:
}
else
{
- strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
- load_db_opt(thd, path, &create);
+ HA_CREATE_INFO create;
+
+ load_db_opt_by_name(thd, db_name, &create);
+
thd->db_charset= create.default_table_charset ?
create.default_table_charset :
thd->variables.collation_server;
@@ -1230,3 +1271,36 @@ end:
}
DBUG_RETURN(0);
}
+
+
+/*
+ Check if there is directory for the database name.
+
+ SYNOPSIS
+ check_db_dir_existence()
+ db_name database name
+
+ RETURN VALUES
+ FALSE There is directory for the specified database name.
+ TRUE The directory does not exist.
+*/
+
+bool check_db_dir_existence(const char *db_name)
+{
+ char db_dir_path[FN_REFLEN];
+ uint db_dir_path_len;
+
+ strxnmov(db_dir_path, sizeof (db_dir_path) - 1, mysql_data_home, "/",
+ db_name, NullS);
+
+ db_dir_path_len= unpack_dirname(db_dir_path, db_dir_path);
+
+ /* Remove trailing '/' or '\' if exists. */
+
+ if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR)
+ db_dir_path[db_dir_path_len - 1]= 0;
+
+ /* Check access. */
+
+ return my_access(db_dir_path, F_OK);
+}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 7d4dca15608..bb66fde79fe 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -126,6 +126,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->param_list.empty();
lex->view_list.empty();
lex->prepared_stmt_params.empty();
+ lex->auxiliary_table_list.empty();
lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to= 0;
lex->unit.prev= lex->unit.link_prev= 0;
@@ -1053,6 +1054,30 @@ int MYSQLlex(void *arg, void *yythd)
}
}
+
+/*
+ Skip comment in the end of statement.
+
+ SYNOPSIS
+ skip_rear_comments()
+ begin pointer to the beginning of statement
+ end pointer to the end of statement
+
+ DESCRIPTION
+ The function is intended to trim comments at the end of the statement.
+
+ RETURN
+ Pointer to the last non-comment symbol of the statement.
+*/
+
+uchar *skip_rear_comments(uchar *begin, uchar *end)
+{
+ while (begin < end && (end[-1] <= ' ' || end[-1] == '*' ||
+ end[-1] == '/' || end[-1] == ';'))
+ end-= 1;
+ return end;
+}
+
/*
st_select_lex structures initialisations
*/
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e5b087fc72a..17b95014c16 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -978,7 +978,7 @@ typedef struct st_lex : public Query_tables_list
/*
view created to be run from definer (standard behaviour)
*/
- bool create_view_suid;
+ uint8 create_view_suid;
/* Characterstics of trigger being created */
st_trg_chistics trg_chistics;
/*
@@ -1115,4 +1115,4 @@ extern void lex_free(void);
extern void lex_start(THD *thd, uchar *buf,uint length);
extern void lex_end(LEX *lex);
extern int MYSQLlex(void *arg, void *yythd);
-
+extern uchar *skip_rear_comments(uchar *begin, uchar *end);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 28ed3e25d57..72098fb83e4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4413,9 +4413,6 @@ end_with_restore_list:
}
else
{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- Security_context *save_ctx;
-#endif
ha_rows select_limit;
/* bits that should be cleared in thd->server_status */
uint bits_to_be_cleared= 0;
@@ -4457,21 +4454,11 @@ end_with_restore_list:
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, TRUE, 0) ||
- sp_change_security_context(thd, sp, &save_ctx))
+ sp->m_db.str, sp->m_name.str, TRUE, FALSE))
{
thd->net.no_send_ok= nsok;
goto error;
}
- if (save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, TRUE, 0))
- {
- thd->net.no_send_ok= nsok;
- sp_restore_security_context(thd, save_ctx);
- goto error;
- }
-
#endif
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -4495,9 +4482,6 @@ end_with_restore_list:
thd->total_warn_count= 0;
thd->variables.select_limit= select_limit;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_ctx);
-#endif
thd->net.no_send_ok= nsok;
thd->server_status&= ~bits_to_be_cleared;
@@ -7017,7 +7001,7 @@ Item * all_any_subquery_creator(Item *left_expr,
return new Item_func_not(new Item_in_subselect(left_expr, select_lex));
Item_allany_subselect *it=
- new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
+ new Item_allany_subselect(left_expr, cmp, select_lex, all);
if (all)
return it->upper_item= new Item_func_not_all(it); /* ALL */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index ffe54310843..6d35c441368 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2128,29 +2128,21 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
they have their own table list).
*/
for (TABLE_LIST *tables= lex->query_tables;
- tables;
- tables= tables->next_global)
+ tables;
+ tables= tables->next_global)
+ {
+ tables->reinit_before_use(thd);
+ }
+ /*
+ Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ...
+ (multi-delete). We do a full clean up, although at the moment all we
+ need to clean in the tables of MULTI-DELETE list is 'table' member.
+ */
+ for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxiliary_table_list.first;
+ tables;
+ tables= tables->next_global)
{
- /*
- Reset old pointers to TABLEs: they are not valid since the tables
- were closed in the end of previous prepare or execute call.
- */
tables->reinit_before_use(thd);
-
- /* Reset is_schema_table_processed value(needed for I_S tables */
- tables->is_schema_table_processed= FALSE;
-
- TABLE_LIST *embedded; /* The table at the current level of nesting. */
- TABLE_LIST *embedding= tables; /* The parent nested table reference. */
- do
- {
- embedded= embedding;
- if (embedded->prep_on_expr)
- embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd);
- embedding= embedded->embedding;
- }
- while (embedding &&
- embedding->nested_join->join_list.head() == embedded);
}
lex->current_select= &lex->select_lex;
@@ -2165,7 +2157,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
}
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
- DBUG_VOID_RETURN;
+ DBUG_VOID_RETURN;
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 20512563f37..2f16b350d04 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -679,6 +679,25 @@ JOIN::optimize()
DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away";
tables_list= 0; // All tables resolved
+ /*
+ Extract all table-independent conditions and replace the WHERE
+ clause with them. All other conditions were computed by opt_sum_query
+ and the MIN/MAX/COUNT function(s) have been replaced by constants,
+ so there is no need to compute the whole WHERE clause again.
+ Notice that make_cond_for_table() will always succeed to remove all
+ computed conditions, because opt_sum_query() is applicable only to
+ conjunctions.
+ Preserve conditions for EXPLAIN.
+ */
+ if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
+ {
+ COND *table_independent_conds=
+ make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0);
+ DBUG_EXECUTE("where",
+ print_where(table_independent_conds,
+ "where after opt_sum_query()"););
+ conds= table_independent_conds;
+ }
}
}
if (!tables_list)
@@ -1064,6 +1083,23 @@ JOIN::optimize()
{
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
}
+ if (order)
+ {
+ /*
+ Force using of tmp table if sorting by a SP or UDF function due to
+ their expensive and probably non-deterministic nature.
+ */
+ for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
+ {
+ Item *item= *tmp_order->item;
+ if (item->walk(&Item::is_expensive_processor,(byte*)0))
+ {
+ /* Force tmp table without sort */
+ need_tmp=1; simple_order=simple_group=0;
+ break;
+ }
+ }
+ }
}
tmp_having= having;
@@ -2470,8 +2506,11 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
/* field = expression OR field IS NULL */
old->level= and_level;
old->optimize= KEY_OPTIMIZE_REF_OR_NULL;
- /* Remember the NOT NULL value */
- if (old->val->is_null())
+ /*
+ Remember the NOT NULL value unless the value does not depend
+ on other tables.
+ */
+ if (!old->val->used_tables() && old->val->is_null())
old->val= new_fields->val;
/* The referred expression can be NULL: */
old->null_rejecting= 0;
@@ -6174,10 +6213,16 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
DBUG_RETURN(0);
}
-
+/*
+ used only in JOIN::clear
+*/
static void clear_tables(JOIN *join)
{
- for (uint i=0 ; i < join->tables ; i++)
+ /*
+ must clear only the non-const tables, as const tables
+ are not re-calculated.
+ */
+ for (uint i=join->const_tables ; i < join->tables ; i++)
mark_as_null_row(join->table[i]); // All fields are NULL
}
@@ -8034,7 +8079,12 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field,
{
Field *new_field;
- if (convert_blob_length && (org_field->flags & BLOB_FLAG))
+ /*
+ Make sure that the blob fits into a Field_varstring which has
+ 2-byte lenght.
+ */
+ if (convert_blob_length && convert_blob_length < UINT_MAX16 &&
+ (org_field->flags & BLOB_FLAG))
new_field= new Field_varstring(convert_blob_length,
org_field->maybe_null(),
org_field->field_name, table,
@@ -8116,8 +8166,13 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
if ((type= item->field_type()) == MYSQL_TYPE_DATETIME ||
type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE)
new_field= item->tmp_table_field_from_field_type(table);
+ /*
+ Make sure that the blob fits into a Field_varstring which has
+ 2-byte lenght.
+ */
else if (item->max_length/item->collation.collation->mbmaxlen > 255 &&
- convert_blob_length)
+ item->max_length/item->collation.collation->mbmaxlen < UINT_MAX16
+ && convert_blob_length)
new_field= new Field_varstring(convert_blob_length, maybe_null,
item->name, table,
item->collation.collation);
@@ -12810,7 +12865,7 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
{
if (! field->const_item())
{
- Item_sum *sum_item=(Item_sum*) field;
+ Item_sum *sum_item=(Item_sum*) field->real_item();
if (!sum_item->quick_group)
param->quick_group=0; // UDF SUM function
param->sum_func_count++;
@@ -13070,10 +13125,11 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
param->copy_funcs.empty();
for (i= 0; (pos= li++); i++)
{
- if (pos->real_item()->type() == Item::FIELD_ITEM)
+ Item *real_pos= pos->real_item();
+ if (real_pos->type() == Item::FIELD_ITEM)
{
Item_field *item;
- pos= pos->real_item();
+ pos= real_pos;
if (!(item= new Item_field(thd, ((Item_field*) pos))))
goto err;
pos= item;
@@ -13112,12 +13168,13 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
}
}
}
- else if ((pos->type() == Item::FUNC_ITEM ||
- pos->type() == Item::SUBSELECT_ITEM ||
- pos->type() == Item::CACHE_ITEM ||
- pos->type() == Item::COND_ITEM) &&
- !pos->with_sum_func)
+ else if ((real_pos->type() == Item::FUNC_ITEM ||
+ real_pos->type() == Item::SUBSELECT_ITEM ||
+ real_pos->type() == Item::CACHE_ITEM ||
+ real_pos->type() == Item::COND_ITEM) &&
+ !real_pos->with_sum_func)
{ // Save for send fields
+ pos= real_pos;
/* TODO:
In most cases this result will be sent to the user.
This should be changed to use copy_int or copy_real depending
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index cabb04c5f16..a4f7062810f 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd)
}
-int
-mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
- const char *wild, bool dir)
+/*
+ find_files() - find files in a given directory.
+
+ SYNOPSIS
+ find_files()
+ thd thread handler
+ files put found files in this list
+ db database name to set in TABLE_LIST structure
+ path path to database
+ wild filter for found files
+ dir read databases in path if TRUE, read .frm files in
+ database otherwise
+
+ RETURN
+ FIND_FILES_OK success
+ FIND_FILES_OOM out of memory error
+ FIND_FILES_DIR no such directory, or directory can't be read
+*/
+
+enum find_files_result {
+ FIND_FILES_OK,
+ FIND_FILES_OOM,
+ FIND_FILES_DIR
+};
+
+static
+find_files_result
+find_files(THD *thd, List<char> *files, const char *db,
+ const char *path, const char *wild, bool dir)
{
uint i;
char *ext;
@@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
uint col_access=thd->col_access;
#endif
TABLE_LIST table_list;
- DBUG_ENTER("mysql_find_files");
+ DBUG_ENTER("find_files");
if (wild && !wild[0])
wild=0;
@@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
else
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
- DBUG_RETURN(-1);
+ DBUG_RETURN(FIND_FILES_DIR);
}
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
@@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
if (files->push_back(thd->strdup(file->name)))
{
my_dirend(dirp);
- DBUG_RETURN(-1);
+ DBUG_RETURN(FIND_FILES_OOM);
}
}
DBUG_PRINT("info",("found: %d files", files->elements));
@@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
VOID(ha_find_files(thd,db,path,wild,dir,files));
- DBUG_RETURN(0);
+ DBUG_RETURN(FIND_FILES_OK);
}
@@ -439,13 +465,11 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
{
Security_context *sctx= thd->security_ctx;
int length;
- char path[FN_REFLEN];
char buff[2048];
String buffer(buff, sizeof(buff), system_charset_info);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint db_access;
#endif
- bool found_libchar;
HA_CREATE_INFO create;
uint create_options = create_info ? create_info->options : 0;
Protocol *protocol=thd->protocol;
@@ -480,23 +504,13 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
}
else
{
- (void) sprintf(path,"%s/%s",mysql_data_home, dbname);
- length=unpack_dirname(path,path); // Convert if not unix
- found_libchar= 0;
- if (length && path[length-1] == FN_LIBCHAR)
- {
- found_libchar= 1;
- path[length-1]=0; // remove ending '\'
- }
- if (access(path,F_OK))
+ if (check_db_dir_existence(dbname))
{
my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
DBUG_RETURN(TRUE);
}
- if (found_libchar)
- path[length-1]= FN_LIBCHAR;
- strmov(path+length, MY_DB_OPT_FILE);
- load_db_opt(thd, path, &create);
+
+ load_db_opt_by_name(thd, dbname, &create);
}
List<Item> field_list;
field_list.push_back(new Item_empty_string("Database",NAME_LEN));
@@ -2000,8 +2014,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
wild string otherwise it's db name;
RETURN
- 1 error
- 0 success
+ zero success
+ non-zero error
*/
int make_db_list(THD *thd, List<char> *files,
@@ -2027,8 +2041,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str)))
return 1;
}
- return mysql_find_files(thd, files, NullS, mysql_data_home,
- idx_field_vals->db_value, 1);
+ return (find_files(thd, files, NullS, mysql_data_home,
+ idx_field_vals->db_value, 1) != FIND_FILES_OK);
}
/*
@@ -2055,7 +2069,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str)))
return 1;
*with_i_schema= 1;
- return mysql_find_files(thd, files, NullS, mysql_data_home, NullS, 1);
+ return (find_files(thd, files, NullS,
+ mysql_data_home, NullS, 1) != FIND_FILES_OK);
}
@@ -2116,12 +2131,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
LINT_INIT(end);
LINT_INIT(len);
- /*
- Let us set fake sql_command so views won't try to merge
- themselves into main statement.
- */
- lex->sql_command= SQLCOM_SHOW_FIELDS;
-
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
/*
@@ -2144,8 +2153,16 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
I_S tables will be done.
*/
thd->temporary_tables= open_tables_state_backup.temporary_tables;
+ /*
+ Let us set fake sql_command so views won't try to merge
+ themselves into main statement. If we don't do this,
+ SELECT * from information_schema.xxxx will cause problems.
+ SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()'
+ */
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
res= open_normal_and_derived_tables(thd, show_table_list,
MYSQL_LOCK_IGNORE_FLUSH);
+ lex->sql_command= save_sql_command;
/*
get_all_tables() returns 1 on failure and 0 on success thus
return only these and not the result code of ::process_table()
@@ -2204,9 +2221,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
strxmov(path, mysql_data_home, "/", base_name, NullS);
end= path + (len= unpack_dirname(path,path));
len= FN_LEN - len;
- if (mysql_find_files(thd, &files, base_name,
- path, idx_field_vals.table_value, 0))
- goto err;
+ find_files_result res= find_files(thd, &files, base_name,
+ path, idx_field_vals.table_value, 0);
+ if (res != FIND_FILES_OK)
+ {
+ /*
+ Downgrade errors about problems with database directory to
+ warnings if this is not a 'SHOW' command. Another thread
+ may have dropped database, and we may still have a name
+ for that directory.
+ */
+ if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END)
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->net.last_errno, thd->net.last_error);
+ thd->clear_error();
+ continue;
+ }
+ else
+ {
+ goto err;
+ }
+ }
if (lower_case_table_names)
orig_base_name= thd->strdup(base_name);
}
@@ -2267,8 +2303,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
lex->all_selects_list= &sel;
lex->derived_tables= 0;
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
res= open_normal_and_derived_tables(thd, show_table_list,
MYSQL_LOCK_IGNORE_FLUSH);
+ lex->sql_command= save_sql_command;
/*
We should use show_table_list->alias instead of
show_table_list->table_name because table_name
@@ -2319,8 +2357,11 @@ bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name,
int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
{
- char path[FN_REFLEN];
- bool found_libchar;
+ /*
+ TODO: fill_schema_shemata() is called when new client is connected.
+ Returning error status in this case leads to client hangup.
+ */
+
INDEX_FIELD_VALUES idx_field_vals;
List<char> files;
char *file_name;
@@ -2352,20 +2393,9 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
(grant_option && !check_grant_db(thd, file_name)))
#endif
{
- strxmov(path, mysql_data_home, "/", file_name, NullS);
- length=unpack_dirname(path,path); // Convert if not unix
- found_libchar= 0;
- if (length && path[length-1] == FN_LIBCHAR)
- {
- found_libchar= 1;
- path[length-1]=0; // remove ending '\'
- }
+ load_db_opt_by_name(thd, file_name, &create);
- if (found_libchar)
- path[length-1]= FN_LIBCHAR;
- strmov(path+length, MY_DB_OPT_FILE);
- load_db_opt(thd, path, &create);
- if (store_schema_shemata(thd, table, file_name,
+ if (store_schema_shemata(thd, table, file_name,
create.default_table_charset))
DBUG_RETURN(1);
}
@@ -3963,13 +3993,19 @@ bool get_schema_tables_result(JOIN *join)
table_list->table->file->delete_all_rows();
free_io_cache(table_list->table);
filesort_free_buffers(table_list->table);
+ table_list->table->null_row= 0;
}
else
table_list->table->file->records= 0;
if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond))
+ {
result= 1;
+ join->error= 1;
+ table_list->is_schema_table_processed= TRUE;
+ break;
+ }
table_list->is_schema_table_processed= TRUE;
}
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 4772d64ad0a..e065a32c2e8 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1631,10 +1631,9 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
if (!create_info->default_table_charset)
{
HA_CREATE_INFO db_info;
- char path[FN_REFLEN];
- /* Abuse build_table_path() to build the path to the db.opt file */
- build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
- load_db_opt(thd, path, &db_info);
+
+ load_db_opt_by_name(thd, db, &db_info);
+
create_info->default_table_charset= db_info.default_table_charset;
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 66a16f16d8c..e806dd4a3f3 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -295,7 +295,10 @@ end:
append_definer(thd, &log_query, &definer_user, &definer_host);
}
- log_query.append(thd->lex->stmt_definition_begin);
+ log_query.append(thd->lex->stmt_definition_begin,
+ (char *)thd->lex->sphead->m_body_begin -
+ thd->lex->stmt_definition_begin +
+ thd->lex->sphead->m_body.length);
}
/* Such a statement can always go directly to binlog, no trans cache. */
@@ -1503,40 +1506,11 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
old_field= table->field;
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- Security_context *save_ctx;
-
- if (sp_change_security_context(thd, sp_trigger, &save_ctx))
- return TRUE;
-
- /*
- NOTE: TRIGGER_ACL should be used below.
- */
-
- if (check_global_access(thd, SUPER_ACL))
- {
- sp_restore_security_context(thd, save_ctx);
- return TRUE;
- }
-
- /*
- Fetch information about table-level privileges to GRANT_INFO structure for
- subject table. Check of privileges that will use it and information about
- column-level privileges will happen in Item_trigger_field::fix_fields().
- */
-
- fill_effective_table_privileges(thd,
- &subject_table_grants[event][time_type],
- table->s->db, table->s->table_name);
-#endif // NO_EMBEDDED_ACCESS_CHECKS
-
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
- err_status= sp_trigger->execute_function(thd, 0, 0, 0);
+ err_status= sp_trigger->execute_trigger
+ (thd, table->s->db, table->s->table_name,
+ &subject_table_grants[event][time_type]);
thd->restore_sub_statement_state(&statement_state);
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_ctx);
-#endif // NO_EMBEDDED_ACCESS_CHECKS
}
return err_status;
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 1561ade78af..637d2cc3684 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -155,6 +155,54 @@ err:
DBUG_RETURN(TRUE);
}
+/*
+ Fill defined view parts
+
+ SYNOPSIS
+ fill_defined_view_parts()
+ thd current thread.
+ view view to operate on
+
+ DESCRIPTION
+ This function will initialize the parts of the view
+ definition that are not specified in ALTER VIEW
+ to their values from CREATE VIEW.
+ The view must be opened to get its definition.
+ We use a copy of the view when opening because we want
+ to preserve the original view instance.
+
+ RETURN VALUE
+ TRUE can't open table
+ FALSE success
+*/
+static bool
+fill_defined_view_parts (THD *thd, TABLE_LIST *view)
+{
+ LEX *lex= thd->lex;
+ bool not_used;
+ TABLE_LIST decoy;
+
+ memcpy (&decoy, view, sizeof (TABLE_LIST));
+ if (!open_table(thd, &decoy, thd->mem_root, &not_used, 0) &&
+ !decoy.view)
+ {
+ return TRUE;
+ }
+ if (!lex->definer)
+ {
+ view->definer.host= decoy.definer.host;
+ view->definer.user= decoy.definer.user;
+ lex->definer= &view->definer;
+ }
+ if (lex->create_view_algorithm == VIEW_ALGORITHM_UNDEFINED)
+ lex->create_view_algorithm= decoy.algorithm;
+ if (lex->create_view_suid == VIEW_SUID_DEFAULT)
+ lex->create_view_suid= decoy.view_suid ?
+ VIEW_SUID_DEFINER : VIEW_SUID_INVOKER;
+
+ return FALSE;
+}
+
/*
Creating/altering VIEW procedure
@@ -207,7 +255,15 @@ bool mysql_create_view(THD *thd,
}
if (mode != VIEW_CREATE_NEW)
+ {
+ if (mode == VIEW_ALTER &&
+ fill_defined_view_parts(thd, view))
+ {
+ res= TRUE;
+ goto err;
+ }
sp_cache_invalidate();
+ }
if (!lex->definer)
{
@@ -671,8 +727,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->query.str= (char*)str.ptr();
view->query.length= str.length()-1; // we do not need last \0
view->source.str= thd->query + thd->lex->create_view_select_start;
- view->source.length= (thd->query_length -
- thd->lex->create_view_select_start);
+ view->source.length= (char *)skip_rear_comments((uchar *)view->source.str,
+ (uchar *)thd->query +
+ thd->query_length) -
+ view->source.str;
view->file_version= 1;
view->calc_md5(md5);
view->md5.str= md5;
@@ -1226,8 +1284,11 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
DBUG_ENTER("mysql_drop_view");
char path[FN_REFLEN];
TABLE_LIST *view;
- bool type= 0;
+ frm_type_enum type;
db_type not_used;
+ String non_existant_views;
+ char *wrong_object_db= NULL, *wrong_object_name= NULL;
+ bool error= FALSE;
for (view= views; view; view= view->next_local)
{
@@ -1235,8 +1296,9 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
view->table_name, reg_ext, NullS);
(void) unpack_filename(path, path);
VOID(pthread_mutex_lock(&LOCK_open));
- if (access(path, F_OK) ||
- (type= (mysql_frm_type(thd, path, &not_used) != FRMTYPE_VIEW)))
+ type= FRMTYPE_ERROR;
+ if (access(path, F_OK) ||
+ FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
{
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
@@ -1248,25 +1310,46 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
VOID(pthread_mutex_unlock(&LOCK_open));
continue;
}
- if (type)
- my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
+ if (type == FRMTYPE_TABLE)
+ {
+ if (!wrong_object_name)
+ {
+ wrong_object_db= view->db;
+ wrong_object_name= view->table_name;
+ }
+ }
else
- my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
- goto err;
+ {
+ if (non_existant_views.length())
+ non_existant_views.append(',');
+ non_existant_views.append(String(view->table_name,system_charset_info));
+ }
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ continue;
}
if (my_delete(path, MYF(MY_WME)))
- goto err;
+ error= TRUE;
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
VOID(pthread_mutex_unlock(&LOCK_open));
}
+ if (error)
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (wrong_object_name)
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name,
+ "VIEW");
+ DBUG_RETURN(TRUE);
+ }
+ if (non_existant_views.length())
+ {
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr());
+ DBUG_RETURN(TRUE);
+ }
send_ok(thd);
DBUG_RETURN(FALSE);
-
-err:
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(TRUE);
-
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6d560d3d250..5d0a583f7d1 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1256,6 +1256,17 @@ create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
+ if (lex->definer != NULL)
+ {
+ /*
+ DEFINER is a concept meaningful when interpreting SQL code.
+ UDF functions are compiled.
+ Using DEFINER with UDF has therefore no semantic,
+ and is considered a parsing error.
+ */
+ my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER");
+ YYABORT;
+ }
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = lex->spname->m_name;
lex->udf.returns=(Item_result) $2;
@@ -1285,6 +1296,7 @@ create_function_tail:
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
+ sp->init_sp_name(YYTHD, lex->spname);
sp->m_type= TYPE_ENUM_FUNCTION;
lex->sphead= sp;
@@ -1339,7 +1351,7 @@ create_function_tail:
YYABORT;
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
- sp->init_strings(YYTHD, lex, lex->spname);
+ sp->init_strings(YYTHD, lex);
if (!(sp->m_flags & sp_head::HAS_RETURN))
{
my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str);
@@ -1911,9 +1923,12 @@ sp_proc_stmt:
sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(),
lex->spcont, lex);
- /* Extract the query statement from the tokenizer:
- The end is either lex->tok_end or tok->ptr. */
- if (lex->ptr - lex->tok_end > 1)
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lex->ptr, if there was no lookahead,
+ lex->tok_end otherwise.
+ */
+ if (yychar == YYEMPTY)
i->m_query.length= lex->ptr - sp->m_tmp_query;
else
i->m_query.length= lex->tok_end - sp->m_tmp_query;
@@ -7822,7 +7837,12 @@ option_type_value:
lex)))
YYABORT;
- if (lex->ptr - lex->tok_end > 1)
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lex->ptr, if there was no lookahead,
+ lex->tok_end otherwise.
+ */
+ if (yychar == YYEMPTY)
qbuff.length= lex->ptr - sp->m_tmp_query;
else
qbuff.length= lex->tok_end - sp->m_tmp_query;
@@ -8997,11 +9017,11 @@ view_algorithm_opt:
view_suid:
/* empty */
- { Lex->create_view_suid= TRUE; }
+ { Lex->create_view_suid= VIEW_SUID_DEFAULT; }
| SQL_SYM SECURITY_SYM DEFINER_SYM
- { Lex->create_view_suid= TRUE; }
+ { Lex->create_view_suid= VIEW_SUID_DEFINER; }
| SQL_SYM SECURITY_SYM INVOKER_SYM
- { Lex->create_view_suid= FALSE; }
+ { Lex->create_view_suid= VIEW_SUID_INVOKER; }
;
view_tail:
@@ -9092,6 +9112,7 @@ trigger_tail:
YYABORT;
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
+ sp->init_sp_name(YYTHD, $3);
lex->stmt_definition_begin= $2;
lex->ident.str= $7;
@@ -9120,7 +9141,7 @@ trigger_tail:
sp_head *sp= lex->sphead;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
- sp->init_strings(YYTHD, lex, $3);
+ sp->init_strings(YYTHD, lex);
/* Restore flag if it was cleared above */
if (sp->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
@@ -9168,13 +9189,14 @@ sp_tail:
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE");
YYABORT;
}
-
+
lex->stmt_definition_begin= $2;
-
+
/* Order is important here: new - reset - init */
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
+ sp->init_sp_name(YYTHD, $3);
sp->m_type= TYPE_ENUM_PROCEDURE;
lex->sphead= sp;
@@ -9212,7 +9234,7 @@ sp_tail:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
- sp->init_strings(YYTHD, lex, $3);
+ sp->init_strings(YYTHD, lex);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
/* Restore flag if it was cleared above */
if (sp->m_old_cmq)
diff --git a/sql/table.cc b/sql/table.cc
index e50b401df5f..b4c08729a79 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2992,13 +2992,27 @@ Field_iterator_table_ref::get_natural_column_ref()
st_table_list::reinit_before_use()
*/
-void st_table_list::reinit_before_use(THD * /* thd */)
+void st_table_list::reinit_before_use(THD *thd)
{
/*
Reset old pointers to TABLEs: they are not valid since the tables
were closed in the end of previous prepare or execute call.
*/
table= 0;
+ /* Reset is_schema_table_processed value(needed for I_S tables */
+ is_schema_table_processed= FALSE;
+
+ TABLE_LIST *embedded; /* The table at the current level of nesting. */
+ TABLE_LIST *embedding= this; /* The parent nested table reference. */
+ do
+ {
+ embedded= embedding;
+ if (embedded->prep_on_expr)
+ embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd);
+ embedding= embedded->embedding;
+ }
+ while (embedding &&
+ embedding->nested_join->join_list.head() == embedded);
}
diff --git a/sql/table.h b/sql/table.h
index eb34867c390..8c2ad8d8fdf 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -360,6 +360,10 @@ typedef struct st_schema_table
#define VIEW_ALGORITHM_TMPTABLE 1
#define VIEW_ALGORITHM_MERGE 2
+#define VIEW_SUID_INVOKER 0
+#define VIEW_SUID_DEFINER 1
+#define VIEW_SUID_DEFAULT 2
+
/* view WITH CHECK OPTION parameter options */
#define VIEW_CHECK_NONE 0
#define VIEW_CHECK_LOCAL 1
@@ -677,6 +681,10 @@ typedef struct st_table_list
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);
+ /*
+ Cleanup for re-execution in a prepared statement or a stored
+ procedure.
+ */
} TABLE_LIST;
class Item;
diff --git a/sql/udf_example.c b/sql/udf_example.c
index 62995085599..a80fce81278 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -806,6 +806,7 @@ char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
char name_buff[256];
struct hostent tmp_hostent;
+ int tmp_errno;
#endif
struct hostent *hp;
unsigned long taddr;
@@ -845,7 +846,6 @@ char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
return 0;
}
#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
- int tmp_errno;
if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET,
&tmp_hostent, name_buff,sizeof(name_buff),
&tmp_errno)))