summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergey Glukhov <sergey.glukhov@oracle.com>2010-12-14 12:33:03 +0300
committerSergey Glukhov <sergey.glukhov@oracle.com>2010-12-14 12:33:03 +0300
commitfcb83cbf15653bbed15936d8eafb4fc7de3e743b (patch)
tree20285d8b9efab14b3f4eaa8bbdc05166562520eb /sql
parentfda629004112411c0bda78d1bb0ab9fe6d894a26 (diff)
downloadmariadb-git-fcb83cbf15653bbed15936d8eafb4fc7de3e743b.tar.gz
Fixed following problems:
--Bug#52157 various crashes and assertions with multi-table update, stored function --Bug#54475 improper error handling causes cascading crashing failures in innodb/ndb --Bug#57703 create view cause Assertion failed: 0, file .\item_subselect.cc, line 846 --Bug#57352 valgrind warnings when creating view --Recently discovered problem when a nested materialized derived table is used before being populated and it leads to incorrect result We have several modes when we should disable subquery evaluation. The reasons for disabling are different. It could be uselessness of the evaluation as in case of 'CREATE VIEW' or 'PREPARE stmt', or we should disable subquery evaluation if tables are not locked yet as it happens in bug#54475, or too early evaluation of subqueries can lead to wrong result as it happened in Bug#19077. Main problem is that if subquery items are treated as const they are evaluated in ::fix_fields(), ::fix_length_and_dec() of the parental items as a lot of these methods have Item::val_...() calls inside. We have to make subqueries non-const to prevent unnecessary subquery evaluation. At the moment we have different methods for this. Here is a list of these modes: 1. PREPARE stmt; We use UNCACHEABLE_PREPARE flag. It is set during parsing in sql_parse.cc, mysql_new_select() for each SELECT_LEX object and cleared at the end of PREPARE in sql_prepare.cc, init_stmt_after_parse(). If this flag is set subquery becomes non-const and evaluation does not happen. 2. CREATE|ALTER VIEW, SHOW CREATE VIEW, I_S tables which process FRM files We use LEX::view_prepare_mode field. We set it before view preparation and check this flag in ::fix_fields(), ::fix_length_and_dec(). Some bugs are fixed using this approach, some are not(Bug#57352, Bug#57703). The problem here is that we have a lot of ::fix_fields(), ::fix_length_and_dec() where we use Item::val_...() calls for const items. 3. Derived tables with subquery = wrong result(Bug19077) The reason of this bug is too early subquery evaluation. It was fixed by adding Item::with_subselect field The check of this field in appropriate places prevents const item evaluation if the item have subquery. The fix for Bug19077 fixes only the problem with convert_constant_item() function and does not cover other places(::fix_fields(), ::fix_length_and_dec() again) where subqueries could be evaluated. Example: CREATE TABLE t1 (i INT, j BIGINT); INSERT INTO t1 VALUES (1, 2), (2, 2), (3, 2); SELECT * FROM (SELECT MIN(i) FROM t1 WHERE j = SUBSTRING('12', (SELECT * FROM (SELECT MIN(j) FROM t1) t2))) t3; DROP TABLE t1; 4. Derived tables with subquery where subquery is evaluated before table locking(Bug#54475, Bug#52157) Suggested solution is following: -Introduce new field LEX::context_analysis_only with the following possible flags: #define CONTEXT_ANALYSIS_ONLY_PREPARE 1 #define CONTEXT_ANALYSIS_ONLY_VIEW 2 #define CONTEXT_ANALYSIS_ONLY_DERIVED 4 -Set/clean these flags when we perform context analysis operation -Item_subselect::const_item() returns result depending on LEX::context_analysis_only. If context_analysis_only is set then we return FALSE that means that subquery is non-const. As all subquery types are wrapped by Item_subselect it allow as to make subquery non-const when it's necessary. mysql-test/r/derived.result: test case mysql-test/r/multi_update.result: test case mysql-test/r/view.result: test case mysql-test/suite/innodb/r/innodb_multi_update.result: test case mysql-test/suite/innodb/t/innodb_multi_update.test: test case mysql-test/suite/innodb_plugin/r/innodb_multi_update.result: test case mysql-test/suite/innodb_plugin/t/innodb_multi_update.test: test case mysql-test/t/derived.test: test case mysql-test/t/multi_update.test: test case mysql-test/t/view.test: test case sql/item.cc: --removed unnecessary code sql/item_cmpfunc.cc: --removed unnecessary checks --THD::is_context_analysis_only() is replaced with LEX::is_ps_or_view_context_analysis() sql/item_func.cc: --refactored context analysis checks sql/item_row.cc: --removed unnecessary checks sql/item_subselect.cc: --removed unnecessary code --added DBUG_ASSERT into Item_subselect::exec() which asserts that subquery execution can not happen if LEX::context_analysis_only is set, i.e. at context analysis stage. --Item_subselect::const_item() Return FALSE if LEX::context_analysis_only is set. It prevents subquery evaluation in ::fix_fields & ::fix_length_and_dec at context analysis stage. sql/item_subselect.h: --removed unnecessary code sql/mysql_priv.h: --Added new set of flags. sql/sql_class.h: --removed unnecessary code sql/sql_derived.cc: --added LEX::context_analysis_only analysis intialization/cleanup sql/sql_lex.cc: --init LEX::context_analysis_only field sql/sql_lex.h: --New LEX::context_analysis_only field sql/sql_parse.cc: --removed unnecessary code sql/sql_prepare.cc: --removed unnecessary code --added LEX::context_analysis_only analysis intialization/cleanup sql/sql_select.cc: --refactored context analysis checks sql/sql_show.cc: --added LEX::context_analysis_only analysis intialization/cleanup sql/sql_view.cc: --added LEX::context_analysis_only analysis intialization/cleanup
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc11
-rw-r--r--sql/item_cmpfunc.cc10
-rw-r--r--sql/item_func.cc2
-rw-r--r--sql/item_row.cc8
-rw-r--r--sql/item_subselect.cc20
-rw-r--r--sql/item_subselect.h1
-rw-r--r--sql/mysql_priv.h33
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_derived.cc3
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_lex.h17
-rw-r--r--sql/sql_parse.cc7
-rw-r--r--sql/sql_prepare.cc18
-rw-r--r--sql/sql_select.cc3
-rw-r--r--sql/sql_show.cc8
-rw-r--r--sql/sql_view.cc2
16 files changed, 64 insertions, 83 deletions
diff --git a/sql/item.cc b/sql/item.cc
index d88a6e80bfe..c75db2dc15b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1712,16 +1712,7 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
- {
- /*
- We should disable const subselect item evaluation because
- subselect transformation does not happen in view_prepare_mode
- and thus val_...() methods can not be called for const items.
- */
- bool resolve_const= ((*arg)->type() == Item::SUBSELECT_ITEM &&
- thd->lex->view_prepare_mode) ? FALSE : TRUE;
- conv= new Item_func_conv_charset(*arg, coll.collation, resolve_const);
- }
+ conv= new Item_func_conv_charset(*arg, coll.collation, 1);
if (!conv)
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 6987dd9e053..e2faa51479c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -401,7 +401,7 @@ static bool convert_constant_item(THD *thd, Item_field *field_item,
Field *field= field_item->field;
int result= 0;
- if (!(*item)->with_subselect && (*item)->const_item())
+ if ((*item)->const_item())
{
TABLE *table= field->table;
ulong orig_sql_mode= thd->variables.sql_mode;
@@ -497,7 +497,7 @@ void Item_bool_func2::fix_length_and_dec()
}
thd= current_thd;
- if (!thd->is_context_analysis_only())
+ if (!thd->lex->is_ps_or_view_context_analysis())
{
if (args[0]->real_item()->type() == FIELD_ITEM)
{
@@ -801,7 +801,7 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value)
confuse storage engines since in context analysis mode tables
aren't locked.
*/
- if (!thd->is_context_analysis_only() &&
+ if (!thd->lex->is_ps_or_view_context_analysis() &&
cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() &&
(str_arg->type() != Item::FUNC_ITEM ||
((Item_func*)str_arg)->functype() != Item_func::GUSERVAR_FUNC))
@@ -1027,7 +1027,7 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
Item_result type)
{
/* Don't need cache if doing context analysis only. */
- if (!thd_arg->is_context_analysis_only() &&
+ if (!thd->lex->is_ps_or_view_context_analysis() &&
(*value)->const_item() && type != (*value)->result_type())
{
Item_cache *cache= Item_cache::get_cache(*value, type);
@@ -4686,7 +4686,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
return TRUE;
}
- if (escape_item->const_item() && !thd->lex->view_prepare_mode)
+ if (escape_item->const_item())
{
/* If we are on execution stage */
String *escape_str= escape_item->val_str(&cmp.value1);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index b542969cfb0..2ba4415dbac 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -6066,7 +6066,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (res)
DBUG_RETURN(res);
- if (thd->lex->view_prepare_mode)
+ if (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
{
/*
Here we check privileges of the stored routine only during view
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 7535c1fa80b..408bc11eb9b 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -73,12 +73,8 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
used_tables_cache |= item->used_tables();
const_item_cache&= item->const_item() && !with_null;
not_null_tables_cache|= item->not_null_tables();
- /*
- Some subqueries transformations aren't done in the view_prepare_mode thus
- is_null() will fail. So we skip is_null() calculation for CREATE VIEW as
- not necessary.
- */
- if (const_item_cache && !thd->lex->view_prepare_mode)
+
+ if (const_item_cache)
{
if (item->cols() > 1)
with_null|= item->null_inside();
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index d521ad0b4e8..6f2286561b9 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -123,20 +123,6 @@ void Item_subselect::cleanup()
}
-/*
- We cannot use generic Item::safe_charset_converter() because
- Subselect transformation does not happen in view_prepare_mode
- and thus we can not evaluate val_...() for const items.
-*/
-
-Item *Item_subselect::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_func_conv_charset *conv=
- new Item_func_conv_charset(this, tocs, thd->lex->view_prepare_mode ? 0 : 1);
- return conv->safe ? conv : NULL;
-}
-
-
void Item_singlerow_subselect::cleanup()
{
DBUG_ENTER("Item_singlerow_subselect::cleanup");
@@ -271,6 +257,7 @@ bool Item_subselect::exec()
if (thd->is_error() || thd->killed)
return 1;
+ DBUG_ASSERT(!thd->lex->context_analysis_only);
/*
Simulate a failure in sub-query execution. Used to test e.g.
out of memory or query being killed conditions.
@@ -307,7 +294,7 @@ table_map Item_subselect::used_tables() const
bool Item_subselect::const_item() const
{
- return const_item_cache;
+ return thd->lex->context_analysis_only ? FALSE : const_item_cache;
}
Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
@@ -1638,7 +1625,8 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
bool result = 0;
- if (thd_arg->lex->view_prepare_mode && left_expr && !left_expr->fixed)
+ if ((thd_arg->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) &&
+ left_expr && !left_expr->fixed)
result = left_expr->fix_fields(thd_arg, &left_expr);
return result || Item_subselect::fix_fields(thd_arg, ref);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 3806e68e377..467e9b22637 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -126,7 +126,6 @@ public:
virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
- Item *safe_charset_converter(CHARSET_INFO *tocs);
/**
Get the SELECT_LEX structure associated with this Item.
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 709a49b2036..04feb217d66 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -566,17 +566,42 @@ protected:
#define MY_CHARSET_BIN_MB_MAXLEN 1
+/*
+ Flags below are set when we perform
+ context analysis of the statement and make
+ subqueries non-const. It prevents subquery
+ evaluation at context analysis stage.
+*/
+
+/*
+ Don't evaluate this subquery during statement prepare even if
+ it's a constant one. The flag is switched off in the end of
+ mysqld_stmt_prepare.
+*/
+#define CONTEXT_ANALYSIS_ONLY_PREPARE 1
+/*
+ Special JOIN::prepare mode: changing of query is prohibited.
+ When creating a view, we need to just check its syntax omitting
+ any optimizations: afterwards definition of the view will be
+ reconstructed by means of ::print() methods and written to
+ to an .frm file. We need this definition to stay untouched.
+*/
+#define CONTEXT_ANALYSIS_ONLY_VIEW 2
+/*
+ Don't evaluate this subquery during derived table prepare even if
+ it's a constant one.
+*/
+#define CONTEXT_ANALYSIS_ONLY_DERIVED 4
+
// uncachable cause
#define UNCACHEABLE_DEPENDENT 1
#define UNCACHEABLE_RAND 2
#define UNCACHEABLE_SIDEEFFECT 4
/// forcing to save JOIN for explain
#define UNCACHEABLE_EXPLAIN 8
-/** Don't evaluate subqueries in prepare even if they're not correlated */
-#define UNCACHEABLE_PREPARE 16
/* For uncorrelated SELECT in an UNION with some correlated SELECTs */
-#define UNCACHEABLE_UNITED 32
-#define UNCACHEABLE_CHECKOPTION 64
+#define UNCACHEABLE_UNITED 16
+#define UNCACHEABLE_CHECKOPTION 32
/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
#define UNDEF_POS (-1)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 774ae4abac4..cfb43356f2a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2180,8 +2180,6 @@ public:
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
}
void set_status_var_init();
- bool is_context_analysis_only()
- { return stmt_arena->is_stmt_prepare() || lex->view_prepare_mode; }
void reset_n_backup_open_tables_state(Open_tables_state *backup);
void restore_backup_open_tables_state(Open_tables_state *backup);
void reset_sub_statement_state(Sub_statement_state *backup, uint new_state);
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 782589f7d0f..3214c756bc7 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -147,10 +147,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
if (!(derived_result= new select_union))
DBUG_RETURN(TRUE); // out of memory
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
// st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived_result, 0)))
goto exit;
-
+ lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
if ((res= check_duplicate_names(unit->types, 0)))
goto exit;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 24c51be2512..9ea144df9bc 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -305,7 +305,7 @@ void lex_start(THD *thd)
lex->select_lex.group_list.empty();
lex->describe= 0;
lex->subqueries= FALSE;
- lex->view_prepare_mode= FALSE;
+ lex->context_analysis_only= 0;
lex->derived_tables= 0;
lex->lock_option= TL_READ;
lex->safe_to_cache_query= 1;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 9131cec9d04..b1f30b07824 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1715,14 +1715,8 @@ typedef struct st_lex : public Query_tables_list
bool verbose, no_write_to_binlog;
bool tx_chain, tx_release;
- /*
- Special JOIN::prepare mode: changing of query is prohibited.
- When creating a view, we need to just check its syntax omitting
- any optimizations: afterwards definition of the view will be
- reconstructed by means of ::print() methods and written to
- to an .frm file. We need this definition to stay untouched.
- */
- bool view_prepare_mode;
+
+ uint8 context_analysis_only;
bool safe_to_cache_query;
bool subqueries, ignore;
st_parsing_options parsing_options;
@@ -1843,6 +1837,13 @@ typedef struct st_lex : public Query_tables_list
delete_dynamic(&plugins);
}
+ inline bool is_ps_or_view_context_analysis()
+ {
+ return (context_analysis_only &
+ (CONTEXT_ANALYSIS_ONLY_PREPARE |
+ CONTEXT_ANALYSIS_ONLY_VIEW));
+ }
+
inline void uncacheable(uint8 cause)
{
safe_to_cache_query= 0;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7cf64134d70..abfc2ec26b3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5865,13 +5865,6 @@ mysql_new_select(LEX *lex, bool move_down)
DBUG_RETURN(1);
}
select_lex->nest_level= lex->nest_level;
- /*
- Don't evaluate this subquery during statement prepare even if
- it's a constant one. The flag is switched off in the end of
- mysqld_stmt_prepare.
- */
- if (thd->stmt_arena->is_stmt_prepare())
- select_lex->uncacheable|= UNCACHEABLE_PREPARE;
if (move_down)
{
SELECT_LEX_UNIT *unit;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 5ba375f9710..aadfb831087 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1688,7 +1688,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
if (open_normal_and_derived_tables(thd, tables, 0))
goto err;
- lex->view_prepare_mode= 1;
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
res= select_like_stmt_test(stmt, 0, 0);
err:
@@ -2234,19 +2234,6 @@ end:
}
-/** Init PS/SP specific parse tree members. */
-
-static void init_stmt_after_parse(LEX *lex)
-{
- SELECT_LEX *sl= lex->all_selects_list;
- /*
- Switch off a temporary flag that prevents evaluation of
- subqueries in statement prepare.
- */
- for (; sl; sl= sl->next_select_in_list())
- sl->uncacheable&= ~UNCACHEABLE_PREPARE;
-}
-
/**
SQLCOM_PREPARE implementation.
@@ -3080,6 +3067,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
parser_state.m_lip.stmt_prepare_mode= TRUE;
lex_start(thd);
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_PREPARE;
error= parse_sql(thd, & parser_state, NULL) ||
thd->is_error() ||
@@ -3132,7 +3120,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (error == 0)
{
setup_set_params();
- init_stmt_after_parse(lex);
+ lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE;
state= Query_arena::PREPARED;
flags&= ~ (uint) IS_IN_USE;
/*
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index c17cb946fa3..5bb5650fae7 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -538,7 +538,8 @@ JOIN::prepare(Item ***rref_pointer_array,
thd->lex->allow_sum_func= save_allow_sum_func;
}
- if (!thd->lex->view_prepare_mode && !(select_options & SELECT_DESCRIBE))
+ if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) &&
+ !(select_options & SELECT_DESCRIBE))
{
Item_subselect *subselect;
/* Is it subselect? */
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index ee0e2bf5ce7..ed7a8d32eb8 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -718,7 +718,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
table_list->table_name));
/* We want to preserve the tree for views. */
- thd->lex->view_prepare_mode= TRUE;
+ thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
{
Show_create_error_handler view_error_suppressor(thd, table_list);
@@ -3315,7 +3315,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
uint derived_tables= lex->derived_tables;
int error= 1;
Open_tables_state open_tables_state_backup;
- bool save_view_prepare_mode= lex->view_prepare_mode;
+ uint8 save_context_analysis_only= lex->context_analysis_only;
Query_tables_list query_tables_list_backup;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
@@ -3323,7 +3323,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
uint table_open_method;
DBUG_ENTER("get_all_tables");
- lex->view_prepare_mode= TRUE;
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
/*
@@ -3540,7 +3540,7 @@ err:
lex->restore_backup_query_tables_list(&query_tables_list_backup);
lex->derived_tables= derived_tables;
lex->all_selects_list= old_all_select_lex;
- lex->view_prepare_mode= save_view_prepare_mode;
+ lex->context_analysis_only= save_context_analysis_only;
lex->sql_command= save_sql_command;
DBUG_RETURN(error);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 133574089aa..6cb4f590ae0 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -545,7 +545,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
/* prepare select to resolve all fields */
- lex->view_prepare_mode= 1;
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
if (unit->prepare(thd, 0, 0))
{
/*