From 21d61c2be79681e12ad31162885a43db70021ea5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 May 2006 18:31:20 -0700 Subject: [PATCH 1/5] Fixed bug #14292: performance degradation for a benchmark query. This performance degradation was due to the fact that some cost evaluation code added into 4.1 in the function find_best was not merged into the code of the function best_access_path added together with other code for greedy optimizer. Added a parameter to the function print_plan. The parameter contains accumulated cost for a given partial join. The patch does not include a special test case since this performance degradation is hard to reproduse with a simple example. TODO: make the function find_best use the function best_access_path in order to remove duplication of code which might result in incomplete merges in the future. mysql-test/r/delete.result: Fixed bug #14292: performance degradation for a benchmark query. Adjusted test results. mysql-test/r/subselect.result: Fixed bug #14292: performance degradation for a benchmark query. Adjusted test results. sql/mysql_priv.h: Fixed bug #14292: performance degradation for a benchmark query. Added a parameter to the function print_plan. The parameter contains accumulated cost for a given partial join. sql/sql_select.cc: Fixed bug #14292: performance degradation for a benchmark query. This performance degradation was due to the fact that some cost evaluation code added into 4.1 in the function find_best was not merged into the code of the function best_access_path added together with other code for greedy optimizer. sql/sql_test.cc: Fixed bug #14292: performance degradation for a benchmark query. Added a parameter to the function print_plan. The parameter contains accumulated cost for a given partial join. --- mysql-test/r/delete.result | 4 +- mysql-test/r/subselect.result | 6 +-- sql/mysql_priv.h | 4 +- sql/sql_select.cc | 94 +++++++++++++++++++++++------------ sql/sql_test.cc | 14 +++--- 5 files changed, 76 insertions(+), 46 deletions(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index ddfeeac77b5..05f1c967e77 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -186,8 +186,8 @@ a b a b a b explain select * from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 -1 SIMPLE t2 index PRIMARY PRIMARY 8 NULL 3 Using where; Using index -1 SIMPLE t3 index PRIMARY PRIMARY 8 NULL 3 Using where; Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1 Using index +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 8 test.t2.b,test.t1.b 1 Using index delete t2.*,t3.* from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b; select * from t3; a b diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index bd0fa5ae661..8768b22a70d 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1354,10 +1354,10 @@ a explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index -2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using index -2 DEPENDENT SUBQUERY t1 ref a a 10 func,test.t3.a 1167 Using where; Using index +2 DEPENDENT SUBQUERY t1 ref a a 5 func 1001 Using where; Using index +2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using where; Using index Warnings: -Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and ((`test`.`t2`.`a`) = `test`.`t1`.`a`)))) +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t1`.`b`) and ((`test`.`t2`.`a`) = `test`.`t1`.`a`)))) insert into t1 values (3,31); select * from t2 where t2.a in (select a from t1 where t1.b <> 30); a diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4eb444e9854..665f6f2ca5a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1066,8 +1066,8 @@ pthread_handler_t handle_manager(void *arg); void print_where(COND *cond,const char *info); void print_cached_tables(void); void TEST_filesort(SORT_FIELD *sortorder,uint s_length); -void print_plan(JOIN* join, double read_time, double record_count, - uint idx, const char *info); +void print_plan(JOIN* join,uint idx, double record_count, double read_time, + double current_read_time, const char *info); #endif void mysql_print_status(); /* key.cc */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47c5281b258..ff6e5cfb984 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3318,33 +3318,46 @@ best_access_path(JOIN *join, for (keyuse=s->keyuse ; keyuse->table == table ;) { key_part_map found_part= 0; - table_map found_ref= 0; - uint found_ref_or_null= 0; - uint key= keyuse->key; + table_map found_ref= 0; + uint key= keyuse->key; KEY *keyinfo= table->key_info+key; bool ft_key= (keyuse->keypart == FT_KEYPART); + uint found_ref_or_null= 0; /* Calculate how many key segments of the current key we can use */ start_key= keyuse; do { /* for each keypart */ uint keypart= keyuse->keypart; - uint found_part_ref_or_null= KEY_OPTIMIZE_REF_OR_NULL; + table_map best_part_found_ref= 0; + double best_prev_record_reads= DBL_MAX; do { if (!(remaining_tables & keyuse->used_tables) && !(found_ref_or_null & keyuse->optimize)) { found_part|= keyuse->keypart_map; - found_ref|= keyuse->used_tables; + double tmp= prev_record_reads(join, + (found_ref | + keyuse->used_tables)); + if (tmp < best_prev_record_reads) + { + best_part_found_ref= keyuse->used_tables; + best_prev_record_reads= tmp; + } if (rec > keyuse->ref_table_rows) rec= keyuse->ref_table_rows; - found_part_ref_or_null&= keyuse->optimize; + /* + If there is one 'key_column IS NULL' expression, we can + use this ref_or_null optimisation of this field + */ + found_ref_or_null|= (keyuse->optimize & + KEY_OPTIMIZE_REF_OR_NULL); } keyuse++; - found_ref_or_null|= found_part_ref_or_null; } while (keyuse->table == table && keyuse->key == key && keyuse->keypart == keypart); + found_ref|= best_part_found_ref; } while (keyuse->table == table && keyuse->key == key); /* @@ -3409,17 +3422,17 @@ best_access_path(JOIN *join, } } /* Limit the number of matched rows */ - tmp = records; + tmp= records; set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); if (table->used_keys.is_set(key)) { /* we can use only index tree */ uint keys_per_block= table->file->block_size/2/ (keyinfo->key_length+table->file->ref_length)+1; - tmp = record_count*(tmp+keys_per_block-1)/keys_per_block; + tmp= record_count*(tmp+keys_per_block-1)/keys_per_block; } else - tmp = record_count*min(tmp,s->worst_seeks); + tmp= record_count*min(tmp,s->worst_seeks); } } else @@ -3433,7 +3446,7 @@ best_access_path(JOIN *join, (!(table->file->index_flags(key, 0, 0) & HA_ONLY_WHOLE_INDEX) || found_part == PREV_BITS(uint,keyinfo->key_parts))) { - max_key_part=max_part_bit(found_part); + max_key_part= max_part_bit(found_part); /* Check if quick_range could determinate how many rows we will match @@ -3444,8 +3457,8 @@ best_access_path(JOIN *join, else { /* Check if we have statistic about the distribution */ - if ((records = keyinfo->rec_per_key[max_key_part-1])) - tmp = records; + if ((records= keyinfo->rec_per_key[max_key_part-1])) + tmp= records; else { /* @@ -3506,13 +3519,13 @@ best_access_path(JOIN *join, /* we can use only index tree */ uint keys_per_block= table->file->block_size/2/ (keyinfo->key_length+table->file->ref_length)+1; - tmp = record_count*(tmp+keys_per_block-1)/keys_per_block; + tmp= record_count*(tmp+keys_per_block-1)/keys_per_block; } else - tmp = record_count*min(tmp,s->worst_seeks); + tmp= record_count*min(tmp,s->worst_seeks); } else - tmp = best_time; // Do nothing + tmp= best_time; // Do nothing } } /* not ft_key */ if (tmp < best_time - records/(double) TIME_FOR_COMPARE) @@ -3864,7 +3877,8 @@ optimize_straight_join(JOIN *join, table_map join_tables) for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++) { /* Find the best access method from 's' to the current partial plan */ - best_access_path(join, s, join->thd, join_tables, idx, record_count, read_time); + best_access_path(join, s, join->thd, join_tables, idx, + record_count, read_time); /* compute the cost of the new plan extended with 's' */ record_count*= join->positions[idx].records_read; read_time+= join->positions[idx].read_time; @@ -3987,8 +4001,9 @@ greedy_search(JOIN *join, 'join->best_positions' contains a complete optimal extension of the current partial QEP. */ - DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, - join->tables, "optimal");); + DBUG_EXECUTE("opt", print_plan(join, join->tables, + record_count, read_time, read_time, + "optimal");); DBUG_VOID_RETURN; } @@ -4019,8 +4034,9 @@ greedy_search(JOIN *join, --rem_size; ++idx; - DBUG_EXECUTE("opt", - print_plan(join, read_time, record_count, idx, "extended");); + DBUG_EXECUTE("opt", print_plan(join, join->tables, + record_count, read_time, read_time, + "extended");); } while (TRUE); } @@ -4043,13 +4059,14 @@ greedy_search(JOIN *join, read_time the cost of the best partial plan search_depth maximum depth of the recursion and thus size of the found optimal plan (0 < search_depth <= join->tables+1). - prune_level pruning heuristics that should be applied during optimization + prune_level pruning heuristics that should be applied during + optimization (values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS) DESCRIPTION The procedure searches for the optimal ordering of the query tables in set - 'remaining_tables' of size N, and the corresponding optimal access paths to each - table. The choice of a table order and an access path for each table + 'remaining_tables' of size N, and the corresponding optimal access paths to + each table. The choice of a table order and an access path for each table constitutes a query execution plan (QEP) that fully specifies how to execute the query. @@ -4159,8 +4176,8 @@ best_extension_by_limited_search(JOIN *join, double best_record_count= DBL_MAX; double best_read_time= DBL_MAX; - DBUG_EXECUTE("opt", - print_plan(join, read_time, record_count, idx, "part_plan");); + DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time, + "part_plan");); for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++) { @@ -4172,7 +4189,8 @@ best_extension_by_limited_search(JOIN *join, double current_record_count, current_read_time; /* Find the best access method from 's' to the current partial plan */ - best_access_path(join, s, thd, remaining_tables, idx, record_count, read_time); + best_access_path(join, s, thd, remaining_tables, idx, + record_count, read_time); /* Compute the cost of extending the plan with 's' */ current_record_count= record_count * join->positions[idx].records_read; current_read_time= read_time + join->positions[idx].read_time; @@ -4181,7 +4199,12 @@ best_extension_by_limited_search(JOIN *join, if ((current_read_time + current_record_count / (double) TIME_FOR_COMPARE) >= join->best_read) { - DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx, + DBUG_EXECUTE("opt", print_plan(join, idx+1, + current_record_count, + read_time, + (current_read_time + + current_record_count / + (double) TIME_FOR_COMPARE), "prune_by_cost");); restore_prev_nj_state(s); continue; @@ -4210,7 +4233,10 @@ best_extension_by_limited_search(JOIN *join, } else { - DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx, + DBUG_EXECUTE("opt", print_plan(join, idx+1, + current_record_count, + read_time, + current_read_time, "pruned_by_heuristic");); restore_prev_nj_state(s); continue; @@ -4238,7 +4264,8 @@ best_extension_by_limited_search(JOIN *join, */ current_read_time+= current_record_count / (double) TIME_FOR_COMPARE; if (join->sort_by_table && - join->sort_by_table != join->positions[join->const_tables].table->table) + join->sort_by_table != + join->positions[join->const_tables].table->table) /* We have to make a temp table */ current_read_time+= current_record_count; if ((search_depth == 1) || (current_read_time < join->best_read)) @@ -4247,8 +4274,10 @@ best_extension_by_limited_search(JOIN *join, sizeof(POSITION) * (idx + 1)); join->best_read= current_read_time - 0.001; } - DBUG_EXECUTE("opt", print_plan(join, current_read_time, - current_record_count, idx, + DBUG_EXECUTE("opt", print_plan(join, idx+1, + current_record_count, + read_time, + current_read_time, "full_plan");); } restore_prev_nj_state(s); @@ -4269,7 +4298,6 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ha_rows rec; double tmp; THD *thd= join->thd; - if (!rest_tables) { DBUG_PRINT("best",("read_time: %g record_count: %g",read_time, diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 975cc19ea3f..0784685432a 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -230,8 +230,8 @@ TEST_join(JOIN *join) */ void -print_plan(JOIN* join, double read_time, double record_count, - uint idx, const char *info) +print_plan(JOIN* join, uint idx, double record_count, double read_time, + double current_read_time, const char *info) { uint i; POSITION pos; @@ -245,13 +245,15 @@ print_plan(JOIN* join, double read_time, double record_count, DBUG_LOCK_FILE; if (join->best_read == DBL_MAX) { - fprintf(DBUG_FILE,"%s; idx:%u, best: DBL_MAX, current:%g\n", - info, idx, read_time); + fprintf(DBUG_FILE, + "%s; idx:%u, best: DBL_MAX, atime: %g, itime: %g, count: %g\n", + info, idx, current_read_time, read_time, record_count); } else { - fprintf(DBUG_FILE,"%s; idx: %u, best: %g, current: %g\n", - info, idx, join->best_read, read_time); + fprintf(DBUG_FILE, + "%s; idx:%u, best: %g, atime: %g, itime: %g, count: %g\n", + info, idx, join->best_read, current_read_time, read_time, record_count); } /* Print the tables in JOIN->positions */ From 0a78d0aacdb5667202889b5aa7dece1ba13f265e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2006 21:35:27 -0700 Subject: [PATCH 2/5] Post-review changes. --- sql/sql_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 0784685432a..3e745ad8b47 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -252,7 +252,7 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, else { fprintf(DBUG_FILE, - "%s; idx:%u, best: %g, atime: %g, itime: %g, count: %g\n", + "%s; idx:%u, best: %g, accumulated: %g, increment: %g, count: %g\n", info, idx, join->best_read, current_read_time, read_time, record_count); } @@ -272,9 +272,9 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, Print the tables in JOIN->best_positions only if at least one complete plan has been found. An indicator for this is the value of 'join->best_read'. */ - fputs("BEST_POSITIONS: ", DBUG_FILE); if (join->best_read < DBL_MAX) { + fputs("BEST_POSITIONS: ", DBUG_FILE); for (i= 0; i < idx ; i++) { pos= join->best_positions[i]; From 231f4964ad6b19f9c83cac2065f0318cee4b0e74 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2006 17:05:21 +0300 Subject: [PATCH 3/5] Added test case for Bug#18712: Truncation problem. The test is only to make sure that this will not be fixed, as it is intended behaviour. Documentation will be improved accordingly. --- mysql-test/r/select.result | 21 +++++++++++++++++++++ mysql-test/t/select.test | 15 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 9b9f67efeb5..ea85025a37b 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3390,3 +3390,24 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 const PRIMARY PRIMARY 4 const 1 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where DROP TABLE t1,t2; +CREATE TABLE t1 (i TINYINT UNSIGNED NOT NULL); +INSERT t1 SET i = 0; +UPDATE t1 SET i = -1; +Warnings: +Warning 1264 Out of range value adjusted for column 'i' at row 1 +SELECT * FROM t1; +i +0 +UPDATE t1 SET i = CAST(i - 1 AS SIGNED); +Warnings: +Warning 1264 Out of range value adjusted for column 'i' at row 1 +SELECT * FROM t1; +i +0 +UPDATE t1 SET i = i - 1; +Warnings: +Warning 1264 Out of range value adjusted for column 'i' at row 1 +SELECT * FROM t1; +i +255 +DROP TABLE t1; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index c4fe1906cbc..8e3c5847846 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2871,3 +2871,18 @@ SELECT t2.sku, t2.sppr, t2.name, t1.sku, t1.pr DROP TABLE t1,t2; + +# +# Bug#18712: Truncation problem (needs just documenting and test +# cases to prevent fixing this accidently. It is intended behaviour) +# + +CREATE TABLE t1 (i TINYINT UNSIGNED NOT NULL); +INSERT t1 SET i = 0; +UPDATE t1 SET i = -1; +SELECT * FROM t1; +UPDATE t1 SET i = CAST(i - 1 AS SIGNED); +SELECT * FROM t1; +UPDATE t1 SET i = i - 1; +SELECT * FROM t1; +DROP TABLE t1; From d43731e2008a35efe04a342c20b8dcf90bc5435a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2006 17:11:40 +0300 Subject: [PATCH 4/5] Fixed an (unlikely) memory leak --- sql/sql_view.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0fb043430a4..640881cb3aa 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1195,6 +1195,7 @@ ok2: end: if (arena) thd->restore_active_arena(arena, &backup); + lex_end(thd->lex); thd->lex= old_lex; DBUG_RETURN(result); From afe4715242576a8575abcec955baa4bfd78af85e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2006 22:19:31 +0300 Subject: [PATCH 5/5] Fixed wrong free in sql_view.cc mysql-test-run now fails in case of warnings mysql-test/lib/mtr_report.pl: Fail if find warnings mysql-test/mysql-test-run.sh: Fail if find warnings sql/sql_lex.cc: Initalize st_lex properly sql/sql_view.cc: Fixed problem with unaligned memory (wrong free) --- mysql-test/lib/mtr_report.pl | 6 ++++-- mysql-test/mysql-test-run.sh | 12 +++++++----- sql/sql_lex.cc | 6 +++++- sql/sql_view.cc | 5 +++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index 4587c8bc385..69d19709d16 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -157,6 +157,7 @@ sub mtr_report_stats ($) { my $tot_passed= 0; my $tot_failed= 0; my $tot_tests= 0; + my $found_problems= 0; # Some warnings are errors... foreach my $tinfo (@$tests) { @@ -214,8 +215,6 @@ sub mtr_report_stats ($) { } else { - my $found_problems= 0; # Some warnings are errors... - # We report different types of problems in order foreach my $pattern ( "^Warning:", "^Error:", "^==.* at 0x" ) { @@ -266,6 +265,9 @@ sub mtr_report_stats ($) { } } print "\n"; + } + if ( $tot_failed != 0 || $found_problems) + { mtr_error("there where failing test cases"); } } diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index c41344548f5..bcdd8294eee 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -199,6 +199,7 @@ TOT_SKIP=0 TOT_PASS=0 TOT_FAIL=0 TOT_TEST=0 +GOT_WARNINGS=0 USERT=0 SYST=0 REALT=0 @@ -1023,17 +1024,16 @@ report_stats () { | $SED -e 's!Warning: Table:.* on rename!!g' \ > $MY_LOG_DIR/warnings.tmp - found_error=0 # Find errors for i in "^Warning:" "^Error:" "^==.* at 0x" "InnoDB: Warning" do if $GREP "$i" $MY_LOG_DIR/warnings.tmp >> $MY_LOG_DIR/warnings then - found_error=1 + GOT_WARNINGS=1 fi done $RM -f $MY_LOG_DIR/warnings.tmp - if [ $found_error = "1" ] + if [ $GOT_WARNINGS = "1" ] then echo "WARNING: Got errors/warnings while running tests. Please examine" echo "$MY_LOG_DIR/warnings for details." @@ -2217,6 +2217,8 @@ if [ $TOT_FAIL -ne 0 ]; then $ECHO "mysql-test-run in $TEST_MODE mode: *** Failing the test(s):$FAILED_CASES" $ECHO exit 1 -else - exit 0 fi +if [ $GOT_WARNINGS -ne 0 ]; then + exit 1 +fi +exit 0 diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2b31abd6a50..c4c72910265 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -191,8 +191,11 @@ void lex_start(THD *thd, uchar *buf,uint length) void lex_end(LEX *lex) { + DBUG_ENTER("lex_end"); + DBUG_PRINT("enter", ("lex: 0x%lx", (long) lex)); x_free(lex->yacc_yyss); x_free(lex->yacc_yyvs); + DBUG_VOID_RETURN; } @@ -1626,7 +1629,8 @@ void st_select_lex::print_limit(THD *thd, String *str) */ st_lex::st_lex() - :result(0), sql_command(SQLCOM_END), query_tables_own_last(0) + :result(0), yacc_yyss(0), yacc_yyvs(0), + sql_command(SQLCOM_END), query_tables_own_last(0) { hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); sroutines_list.empty(); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 640881cb3aa..0f836bd58ff 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -941,7 +941,6 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) goto err; } - if (!(table->view_tables= (List*) new(thd->mem_root) List)) goto err; @@ -1192,14 +1191,16 @@ ok2: old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used; result= !table->prelocking_placeholder && table->prepare_security(thd); + lex_end(thd->lex); end: if (arena) thd->restore_active_arena(arena, &backup); - lex_end(thd->lex); thd->lex= old_lex; DBUG_RETURN(result); err: + DBUG_ASSERT(thd->lex == table->view); + lex_end(thd->lex); delete table->view; table->view= 0; // now it is not VIEW placeholder result= 1;