From 645191aa1338caa397124e5c02002d93a07a628b Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 20 Jun 2019 18:50:20 -0700 Subject: MDEV-19778 Wrong Result on Left Outer Join with Subquery right on true and WHERE filter afterwards This patch complements the patch fixing the bug MDEV-6892. The latter properly handled queries that used mergeable views returning constant columns as inner tables of outer joins and whose where clause contained predicates referring to these columns if the predicates of happened not to be equality predicates. Otherwise the server still could return wrong result sets for such queries. Besides the fix for MDEV-6892 prevented some possible conversions of outer joins to inner joins for such queries. This patch corrected the function check_simple_equality() to handle properly conjunctive equalities of the where clause that refer to the constant columns of mergeable views used as inner tables of an outer join. The patch also changed the code of Item_direct_view_ref::not_null_tables(). This change allowed to take into account predicates containing references to constant columns of mergeable views when converting outer joins into inner joins. --- sql/item.cc | 13 ++++++++----- sql/item.h | 1 + sql/item_func.cc | 1 + sql/sql_select.cc | 9 ++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 719bdcfaf89..ffd899ef820 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9814,11 +9814,14 @@ table_map Item_direct_view_ref::used_tables() const table_map Item_direct_view_ref::not_null_tables() const { - return get_depended_from() ? - 0 : - ((view->is_merged_derived() || view->merged || !view->table) ? - (*ref)->not_null_tables() : - view->table->map); + if (get_depended_from()) + return 0; + if (!( view->merged || !view->table)) + return view->table->map; + TABLE *tab= get_null_ref_table(); + if (tab == NO_NULL_TABLE || (*ref)->used_tables()) + return (*ref)->not_null_tables(); + return get_null_ref_table()->map; } /* diff --git a/sql/item.h b/sql/item.h index f6c3c20516b..6dc99970914 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3382,6 +3382,7 @@ public: void update_used_tables(); table_map not_null_tables() const; bool const_item() const { return used_tables() == 0; } + TABLE *get_null_ref_table() const { return null_ref_table; } bool walk(Item_processor processor, bool walk_subquery, uchar *arg) { return (*ref)->walk(processor, walk_subquery, arg) || diff --git a/sql/item_func.cc b/sql/item_func.cc index 22d7e8c7bb6..dad4b89b3bc 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -226,6 +226,7 @@ Item_func::fix_fields(THD *thd, Item **ref) with_field= with_field || item->with_field; used_tables_cache|= item->used_tables(); const_item_cache&= item->const_item(); + not_null_tables_cache|= item->not_null_tables(); with_subselect|= item->has_subquery(); } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0d94ba2f293..3d78000b3d4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1030,7 +1030,6 @@ JOIN::optimize() DBUG_RETURN(1); /* purecov: inspected */ /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ select_lex->update_used_tables(); - } eval_select_list_used_tables(); @@ -1092,6 +1091,8 @@ JOIN::optimize() sel->where= conds; + select_lex->update_used_tables(); + if (arena) thd->restore_active_arena(arena, &backup); } @@ -11763,6 +11764,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, { if (((Item_ref*)left_item)->get_depended_from()) return FALSE; + if (((Item_direct_view_ref*)left_item)->get_null_ref_table() != + NO_NULL_TABLE && !left_item->real_item()->used_tables()) + return FALSE; left_item= left_item->real_item(); } if (right_item->type() == Item::REF_ITEM && @@ -11770,6 +11774,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, { if (((Item_ref*)right_item)->get_depended_from()) return FALSE; + if (((Item_direct_view_ref*)right_item)->get_null_ref_table() != + NO_NULL_TABLE && !right_item->real_item()->used_tables()) + return FALSE; right_item= right_item->real_item(); } if (left_item->type() == Item::FIELD_ITEM && -- cgit v1.2.1 From 8540fa83bb34df6a3d489a4e85c77692f36e3e26 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 30 Jun 2019 13:16:12 -0700 Subject: MDEV-19421 Basic 3-way join queries are not parsed. The parser returned a syntax error message for the queries with join expressions like this t1 JOIN t2 [LEFT | RIGHT] JOIN t3 ON ... ON ... when the second operand of the outer JOIN operation with ON clause was another join expression with ON clause. In this expression the JOIN operator is right-associative, i.e. expression has to be parsed as the expression t1 JOIN (t2 [LEFT | RIGHT] JOIN t3 ON ... ) ON ... Such join expressions are hard to parse because the outer JOIN is left-associative if there is no ON clause for the first outer JOIN operator. The patch implements the solution when the JOIN operator is always parsed as right-associative and builds first the right-associative tree. If it happens that there is no corresponding ON clause for this operator the tree is converted to left-associative. The idea of the solution was taken from the patch by Martin Hansson "WL#8083: Fixed the join_table rule" from MySQL-8.0 code line. As the grammar rules related to join expressions in MySQL-8.0 and MariaDB-5.5+ are quite different MariaDB solution could not borrow any code from the MySQL-8.0 solution. --- sql/sql_lex.h | 6 +- sql/sql_parse.cc | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/sql_yacc.yy | 49 ++++++------ sql/table.h | 24 ++++++ 4 files changed, 288 insertions(+), 26 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 615b583897b..8f629750198 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -957,6 +957,8 @@ public: TABLE_LIST *end_nested_join(THD *thd); TABLE_LIST *nest_last_join(THD *thd); void add_joined_table(TABLE_LIST *table); + bool add_cross_joined_table(TABLE_LIST *left_op, TABLE_LIST *right_op, + bool straight_fl); TABLE_LIST *convert_right_join(); List* get_item_list(); ulong get_table_join_options(); @@ -2745,9 +2747,9 @@ struct LEX: public Query_tables_list return context_stack.push_front(context); } - void pop_context() + Name_resolution_context *pop_context() { - context_stack.pop(); + return context_stack.pop(); } bool copy_db_to(char **p_db, size_t *p_db_length) const; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1d5733a646b..346f8ad5e8b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6390,6 +6390,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) join_list= ptr->join_list; embedding= ptr->embedding; nested_join= ptr->nested_join; + nested_join->nest_type= 0; if (nested_join->join_list.elements == 1) { TABLE_LIST *embedded= nested_join->join_list.head(); @@ -6399,6 +6400,8 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) join_list->push_front(embedded); ptr= embedded; embedded->lifted= 1; + if (embedded->nested_join) + embedded->nested_join->nest_type= 0; } else if (nested_join->join_list.elements == 0) { @@ -6429,6 +6432,16 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) List *embedded_list; DBUG_ENTER("nest_last_join"); + TABLE_LIST *head= join_list->head(); + if (head->nested_join && head->nested_join->nest_type & REBALANCED_NEST) + { + List_iterator li(*join_list); + li++; + while (li++) + li.remove(); + DBUG_RETURN(head); + } + if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+ sizeof(NESTED_JOIN)))) DBUG_RETURN(0); @@ -6440,6 +6453,7 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) ptr->alias= (char*) "(nest_last_join)"; embedded_list= &nested_join->join_list; embedded_list->empty(); + nested_join->nest_type= JOIN_OP_NEST; for (uint i=0; i < 2; i++) { @@ -6490,6 +6504,227 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) } +/** + @brief + Create a node for JOIN/INNER JOIN/CROSS JOIN/STRAIGHT_JOIN operation + + @param left_op the node for the left operand constructed by the parser + @param right_op the node for the right operand constructed by the parser + @param straight_fl TRUE if STRAIGHT_JOIN is used + + @retval + false on success + true otherwise + + @details + + JOIN operator can be left-associative with other join operators in one + context and right-associative in another context. + + In this query + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a (Q1) + JOIN is left-associative and the query Q1 is interpreted as + SELECT * FROM (t1 JOIN t2) LEFT JOIN t3 ON t2.a=t3.a. + While in this query + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.b=t2.b (Q2) + JOIN is right-associative and the query Q2 is interpreted as + SELECT * FROM t1 JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) ON t1.b=t2.b + + JOIN is right-associative if it is used with ON clause or with USING clause. + Otherwise it is left-associative. + When parsing a join expression with JOIN operator we can't determine + whether this operation left or right associative until either we read the + corresponding ON clause or we reach the end of the expression. This creates + a problem for the parser to build a proper internal representation of the + used join expression. + + For Q1 and Q2 the trees representing the used join expressions look like + + LJ - ON J - ON + / \ / \ + J t3 (TQ1) t1 LJ - ON (TQ2) + / \ / \ + t1 t2 t2 t3 + + To build TQ1 the parser has to reduce the expression for JOIN right after + it has read the reference to t2. To build TQ2 the parser reduces JOIN + when he has read the whole join expression. There is no way to determine + whether an early reduction is needed until the whole join expression is + read. + A solution here is always to do a late reduction. In this case the parser + first builds an incorrect tree TQ1* that has to be rebalanced right after + it has been constructed. + + J LJ - ON + / \ / \ + t1 LJ - ON (TQ1*) => J t3 + / \ / \ + t2 t3 t1 t2 + + Actually the transformation is performed over the nodes t1 and LJ before the + node for J is created in the function st_select_lex::add_cross_joined_table. + The function creates a node for J which replaces the node t2. Then it + attaches the nodes t1 and t2 to this newly created node. The node LJ becomes + the top node of the tree. + + For the query + SELECT * FROM t1 JOIN t2 RIGHT JOIN t3 ON t2.a=t3.a (Q3) + the transformation looks slightly differently because the parser + replaces the RIGHT JOIN tree for an equivalent LEFT JOIN tree. + + J LJ - ON + / \ / \ + t1 LJ - ON (TQ3*) => J t2 + / \ / \ + t3 t2 t1 t3 + + With several left associative JOINs + SELECT * FROM t1 JOIN t2 JOIN t3 LEFT JOIN t4 ON t3.a=t4.a (Q4) + the newly created node for JOIN replaces the left most node of the tree: + + J1 LJ - ON + / \ / \ + t1 LJ - ON J2 t4 + / \ => / \ + J2 t4 J1 t3 + / \ / \ + t2 t3 t1 t2 + + Here's another example: + SELECT * + FROM t1 JOIN t2 LEFT JOIN t3 JOIN t4 ON t3.a=t4.a ON t2.b=t3.b (Q5) + + J LJ - ON + / \ / \ + t1 LJ - ON J J - ON + / \ => / \ / \ + t2 J - ON t1 t2 t3 t4 + / \ + t3 t4 + + If the transformed nested join node node is a natural join node like in + the following query + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 USING(a) (Q6) + the transformation additionally has to take care about setting proper + references in the field natural_join for both operands of the natural + join operation. + The function also has to change the name resolution context for ON + expressions used in the transformed join expression to take into + account the tables of the left_op node. +*/ + +bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op, + TABLE_LIST *right_op, + bool straight_fl) +{ + DBUG_ENTER("add_cross_joined_table"); + THD *thd= parent_lex->thd; + if (!(right_op->nested_join && + (right_op->nested_join->nest_type & JOIN_OP_NEST))) + { + /* + This handles the cases when the right operand is not a nested join. + like in queries + SELECT * FROM t1 JOIN t2; + SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a JOIN t3 + */ + right_op->straight= straight_fl; + DBUG_RETURN(false); + } + + TABLE_LIST *tbl; + List *jl= &right_op->nested_join->join_list; + TABLE_LIST *cj_nest; + + /* + Create the node NJ for a new nested join for the future inclusion + of left_op in it. Initially the nest is empty. + */ + if (unlikely(!(cj_nest= + (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+ + sizeof(NESTED_JOIN))))) + DBUG_RETURN(true); + cj_nest->nested_join= + ((NESTED_JOIN*) ((uchar*) cj_nest + ALIGN_SIZE(sizeof(TABLE_LIST)))); + cj_nest->nested_join->nest_type= JOIN_OP_NEST; + List *cjl= &cj_nest->nested_join->join_list; + cjl->empty(); + + /* Look for the left most node tbl of the right_op tree */ + for ( ; ; ) + { + TABLE_LIST *pair_tbl= 0; /* useful only for operands of natural joins */ + + List_iterator li(*jl); + tbl= li++; + + /* Expand name resolution context */ + Name_resolution_context *on_context; + if ((on_context= tbl->on_context)) + { + on_context->first_name_resolution_table= + left_op->first_leaf_for_name_resolution(); + } + + if (!(tbl->outer_join & JOIN_TYPE_RIGHT)) + { + pair_tbl= tbl; + tbl= li++; + } + if (tbl->nested_join && + tbl->nested_join->nest_type & JOIN_OP_NEST) + { + jl= &tbl->nested_join->join_list; + continue; + } + + /* Replace the tbl node in the tree for the newly created NJ node */ + cj_nest->outer_join= tbl->outer_join; + cj_nest->on_expr= tbl->on_expr; + cj_nest->embedding= tbl->embedding; + cj_nest->join_list= jl; + cj_nest->alias= (char*) "(nest_last_join)"; + li.replace(cj_nest); + + /* + If tbl is an operand of a natural join set properly the references + in the fields natural_join for both operands of the operation. + */ + if(tbl->embedding && tbl->embedding->is_natural_join) + { + if (!pair_tbl) + pair_tbl= li++; + pair_tbl->natural_join= cj_nest; + cj_nest->natural_join= pair_tbl; + } + break; + } + + /* Attach tbl as the right operand of NJ */ + if (unlikely(cjl->push_back(tbl, thd->mem_root))) + DBUG_RETURN(true); + tbl->outer_join= 0; + tbl->on_expr= 0; + tbl->straight= straight_fl; + tbl->natural_join= 0; + tbl->embedding= cj_nest; + tbl->join_list= cjl; + + /* Add left_op as the left operand of NJ */ + if (unlikely(cjl->push_back(left_op, thd->mem_root))) + DBUG_RETURN(true); + left_op->embedding= cj_nest; + left_op->join_list= cjl; + + /* + Mark right_op as a rebalanced nested join in order not to + create a new top level nested join node. + */ + right_op->nested_join->nest_type|= REBALANCED_NEST; + DBUG_RETURN(false); +} + + /** Convert a right join into equivalent left join. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 93ac55592bf..a0e9497f217 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1428,9 +1428,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */ -%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ -%left TABLE_REF_PRIORITY +%left CONDITIONLESS_JOIN +%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT ON_SYM USING %left SET_VAR %left OR_OR_SYM OR_SYM OR2_SYM %left XOR @@ -9607,9 +9607,9 @@ join_table_list: and are ignored. */ esc_table_ref: - table_ref { $$=$1; } - | '{' ident table_ref '}' { $$=$3; } - ; + table_ref { $$=$1; } + | '{' ident table_ref '}' { $$=$3; } + ; /* Equivalent to in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ @@ -9622,23 +9622,24 @@ derived_table_list: ; /* - Notice that JOIN is a left-associative operation, and it must be parsed - as such, that is, the parser must process first the left join operand - then the right one. Such order of processing ensures that the parser - produces correct join trees which is essential for semantic analysis - and subsequent optimization phases. + Notice that JOIN can be a left-associative operator in one context and + a right-associative operator in another context (see the comment for + st_select_lex::add_cross_joined_table). */ join_table: /* INNER JOIN variants */ - /* - Use %prec to evaluate production 'table_ref' before 'normal_join' - so that [INNER | CROSS] JOIN is properly nested as other - left-associative joins. - */ - table_ref normal_join table_ref %prec TABLE_REF_PRIORITY - { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } - | table_ref STRAIGHT_JOIN table_factor - { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } + table_ref normal_join table_ref %prec CONDITIONLESS_JOIN + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + if (unlikely(Select->add_cross_joined_table($1, $3, false))) + MYSQL_YYABORT; + } + | table_ref STRAIGHT_JOIN table_ref %prec CONDITIONLESS_JOIN + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + if (unlikely(Select->add_cross_joined_table($1, $3, true))) + MYSQL_YYABORT; + } | table_ref normal_join table_ref ON { @@ -9651,10 +9652,10 @@ join_table: expr { add_join_on($3,$6); - Lex->pop_context(); + $3->on_context= Lex->pop_context(); Select->parsing_place= NO_MATTER; } - | table_ref STRAIGHT_JOIN table_factor + | table_ref STRAIGHT_JOIN table_ref ON { MYSQL_YYABORT_UNLESS($1 && $3); @@ -9667,7 +9668,7 @@ join_table: { $3->straight=1; add_join_on($3,$6); - Lex->pop_context(); + $3->on_context= Lex->pop_context(); Select->parsing_place= NO_MATTER; } | table_ref normal_join table_ref @@ -9696,7 +9697,7 @@ join_table: expr { add_join_on($5,$8); - Lex->pop_context(); + $5->on_context= Lex->pop_context(); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; Select->parsing_place= NO_MATTER; @@ -9735,7 +9736,7 @@ join_table: if (!($$= lex->current_select->convert_right_join())) MYSQL_YYABORT; add_join_on($$, $8); - Lex->pop_context(); + $1->on_context= Lex->pop_context(); Select->parsing_place= NO_MATTER; } | table_ref RIGHT opt_outer JOIN_SYM table_factor diff --git a/sql/table.h b/sql/table.h index c81ee68b0a6..66a51b9d4f5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -46,6 +46,7 @@ struct TABLE_LIST; class ACL_internal_schema_access; class ACL_internal_table_access; class Field; +struct Name_resolution_context; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -1618,6 +1619,7 @@ struct TABLE_LIST char *db, *alias, *table_name, *schema_table_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ + Name_resolution_context *on_context; /* For ON expressions */ Item *sj_on_expr; /* @@ -2332,9 +2334,31 @@ public: }; +#define JOIN_OP_NEST 1 +#define REBALANCED_NEST 2 + typedef struct st_nested_join { List join_list; /* list of elements in the nested join */ + /* + Currently the valid values for nest type are: + JOIN_OP_NEST - for nest created for JOIN operation used as an operand in + a join expression, contains 2 elements; + JOIN_OP_NEST | REBALANCED_NEST - nest created after tree re-balancing + in st_select_lex::add_cross_joined_table(), contains 1 element; + 0 - for all other nests. + Examples: + 1. SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a; + Here the nest created for LEFT JOIN at first has nest_type==JOIN_OP_NEST. + After re-balancing in st_select_lex::add_cross_joined_table() this nest + has nest_type==JOIN_OP_NEST | REBALANCED_NEST. The nest for JOIN created + in st_select_lex::add_cross_joined_table() has nest_type== JOIN_OP_NEST. + 2. SELECT * FROM t1 JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) + Here the nest created for LEFT JOIN has nest_type==0, because it's not + an operand in a join expression. The nest created for JOIN has nest_type + set to JOIN_OP_NEST. + */ + uint nest_type; /* Bitmap of tables within this nested join (including those embedded within its children), including tables removed by table elimination. -- cgit v1.2.1 From 1a79a29c87eca4dffa393796b7dc4deb62eb0865 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 12 Jul 2019 10:03:33 +0200 Subject: MDEV-17042: prepared statement does not return error with SQL_MODE STRICT_TRANS_TABLES. Use for parameters value conversion functions which issue warnings. --- sql/item.cc | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index ffd899ef820..14a703f4620 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3668,10 +3668,10 @@ double Item_param::val_real() case STRING_VALUE: case LONG_DATA_VALUE: { - int dummy_err; - char *end_not_used; - return my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), &end_not_used, &dummy_err); + return double_from_string_with_check(str_value.charset(), + str_value.ptr(), + str_value.ptr() + + str_value.length()); } case TIME_VALUE: /* @@ -3706,11 +3706,10 @@ longlong Item_param::val_int() } case STRING_VALUE: case LONG_DATA_VALUE: - { - int dummy_err; - return my_strntoll(str_value.charset(), str_value.ptr(), - str_value.length(), 10, (char**) 0, &dummy_err); - } + return longlong_from_string_with_check(str_value.charset(), + str_value.ptr(), + str_value.ptr() + + str_value.length()); case TIME_VALUE: return (longlong) TIME_to_ulonglong(&value.time); case NULL_VALUE: @@ -3735,8 +3734,7 @@ my_decimal *Item_param::val_decimal(my_decimal *dec) return dec; case STRING_VALUE: case LONG_DATA_VALUE: - string2my_decimal(E_DEC_FATAL_ERROR, &str_value, dec); - return dec; + return val_decimal_from_string(dec); case TIME_VALUE: { longlong i= (longlong) TIME_to_ulonglong(&value.time); -- cgit v1.2.1 From cc86a0bd11a436dfe4df0622481c571f83b1680a Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 11 Jul 2019 14:50:50 +0200 Subject: MDEV-15572: view.test, server crash with --big-tables=1 Check that table is really opened before cleanup using handler. --- sql/sql_insert.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8f5ef0b531c..ddf9bd155a9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3743,8 +3743,12 @@ void select_insert::abort_result_set() { example), no table will have been opened and therefore 'table' will be NULL. In that case, we still need to execute the rollback and the end of the function. + + If it fail due to inability to insert in multi-table view for example, + table will be assigned with view table structure, but that table will + not be opened really (it is dummy to check fields types & Co). */ - if (table) + if (table && table->file->get_table()) { bool changed, transactional_table; /* -- cgit v1.2.1 From f90040fd9ccb99cb4541d181a7052c37dc38decb Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Wed, 17 Jul 2019 12:31:45 +0200 Subject: MDEV-19429: Wrong query result with EXISTS and LIMIT 0 Check EXISTS LIMIT before rewriting. --- sql/item_subselect.cc | 21 ++++++++++++++------- sql/sql_lex.cc | 5 ++--- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index afc42dc08d5..9d6fe5ac5ab 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1432,13 +1432,20 @@ void Item_exists_subselect::fix_length_and_dec() { DBUG_ENTER("Item_exists_subselect::fix_length_and_dec"); init_length_and_dec(); - /* - We need only 1 row to determine existence (i.e. any EXISTS that is not - an IN always requires LIMIT 1) - */ - thd->change_item_tree(&unit->global_parameters->select_limit, - new Item_int((int32) 1)); - DBUG_PRINT("info", ("Set limit to 1")); + // If limit is not set or it is constant more than 1 + if (!unit->global_parameters->select_limit || + (unit->global_parameters->select_limit->basic_const_item() && + unit->global_parameters->select_limit->val_int() > 1)) + { + /* + We need only 1 row to determine existence (i.e. any EXISTS that is not + an IN always requires LIMIT 1) + */ + thd->change_item_tree(&unit->global_parameters->select_limit, + new Item_int((int32) 1)); + unit->global_parameters->explicit_limit= 1; // we set the limit + DBUG_PRINT("info", ("Set limit to 1")); + } DBUG_VOID_RETURN; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 28f56282bad..fe4dcfd1524 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2456,14 +2456,13 @@ void st_select_lex::print_limit(THD *thd, if (item && unit->global_parameters == this) { Item_subselect::subs_type subs_type= item->substype(); - if (subs_type == Item_subselect::EXISTS_SUBS || - subs_type == Item_subselect::IN_SUBS || + if (subs_type == Item_subselect::IN_SUBS || subs_type == Item_subselect::ALL_SUBS) { return; } } - if (explicit_limit) + if (explicit_limit && select_limit) { str->append(STRING_WITH_LEN(" limit ")); if (offset_limit) -- cgit v1.2.1 From 82563c5fc0a40d64d8e8e9de2bf6f904fa6c0dc6 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 21 Jul 2019 12:09:17 +0200 Subject: MDEV-20110 don't try to load client plugins with invalid names reported by lixtelnis --- sql/sql_acl.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ef5fed1fc19..8670f5b390d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8256,6 +8256,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin; DBUG_EXECUTE_IF("auth_disconnect", { vio_close(net->vio); DBUG_RETURN(1); }); + DBUG_EXECUTE_IF("auth_invalid_plugin", client_auth_plugin="foo/bar"; ); DBUG_ASSERT(client_auth_plugin); /* -- cgit v1.2.1 From c5e967430059212a06b1e3764030f704ca910393 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 7 Mar 2019 14:08:19 +0100 Subject: Bug #27312862: ASAN: HEAP-USE-AFTER-FREE: UPDATEXML RB#21666 RB#21666 The xpath parsing function was using a local string buffer that was deallocated when going out of scope. However references to it are preserved in the XPATH parse tree. This was causing read-after-free. Fixed by making the xpath buffer a local variable inside the Item class for the relevant xpath function, thus being preserved for the duration of the query. --- sql/item_xmlfunc.cc | 8 ++++---- sql/item_xmlfunc.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 90d1f598b5b..6978218fb90 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2005, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2017, MariaDB +/* Copyright (c) 2005, 2019, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2601,7 +2601,7 @@ my_xpath_parse(MY_XPATH *xpath, const char *str, const char *strend) void Item_xml_str_func::fix_length_and_dec() { - String *xp, tmp; + String *xp; MY_XPATH xpath; int rc; @@ -2628,7 +2628,7 @@ void Item_xml_str_func::fix_length_and_dec() return; } - if (!(xp= args[1]->val_str(&tmp))) + if (!(xp= args[1]->val_str(&xpath_tmp_value))) return; my_xpath_init(&xpath); xpath.cs= collation.collation; diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index c5cc986348d..5cac858ee76 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -1,8 +1,7 @@ #ifndef ITEM_XMLFUNC_INCLUDED #define ITEM_XMLFUNC_INCLUDED -/* Copyright (c) 2000-2007 MySQL AB, 2009 Sun Microsystems, Inc. - Use is subject to license terms. +/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +30,7 @@ class Item_xml_str_func: public Item_str_func protected: String tmp_value, pxml; Item *nodeset_func; + String xpath_tmp_value; public: Item_xml_str_func(Item *a, Item *b): Item_str_func(a,b) -- cgit v1.2.1 From 5e8ab9b7af159cee6e954f62b6304c2c33b6f6e2 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 16 Apr 2019 09:33:02 +0530 Subject: Bug#27302459: EMPTY VALUE IN MYSQL.PLUGIN TABLE CAUSES SERVER TO EXIT ON STARTUP Description:- During server startup, the server exits if the 'mysql.plugin' system table has any rows with empty value for the field 'name' (plugin name). --- sql/sql_plugin.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 46df991639e..91d0a4393c5 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1781,6 +1781,9 @@ static void plugin_load(MEM_ROOT *tmp_root) LEX_STRING name= {(char *)str_name.ptr(), str_name.length()}; LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()}; + if (!name.length || !dl.length) + continue; + /* there're no other threads running yet, so we don't need a mutex. but plugin_add() before is designed to work in multi-threaded -- cgit v1.2.1 From 8ddb7e3eb71010decd5acc99aa98c82bbe0139aa Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 24 Jul 2019 13:14:03 +0200 Subject: Bug#27167197 USING ? IN INSTALL PLUGIN QUERY ABORTS DEBUG, AND HANGS OPTIMIZED SERVER check_valid_path() uses my_strcspn() that cannot handle invalid characters properly. This is fixed by a big refactoring in 10.2 (MDEV-6353). For 5.5, let's simply swap tests, because check_string_char_length() rejects invalid characters just fine. --- sql/sql_plugin.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 91d0a4393c5..a90c7558045 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -736,9 +736,9 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) This is done to ensure that only approved libraries from the plugin directory are used (to make this even remotely secure). */ - if (check_valid_path(dl->str, dl->length) || - check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN, + if (check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN, system_charset_info, 1) || + check_valid_path(dl->str, dl->length) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) { report_error(report, ER_UDF_NO_PATHS); -- cgit v1.2.1 From 7473a71a282c47a1e95359c575089c8ef51caf56 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Mon, 20 May 2019 10:53:00 +0400 Subject: Bug #29419820: MEMORY LEAK IN MY_YYOVERFLOW() Note: this patch is for 5.6. Detected by ASAN. The patch fixes the cleanup of parser stack pointers. Reviewed-by: Guilhem Bichot --- sql/sql_lex.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8f629750198..2410f6edc23 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2010, 2018, MariaDB Corporation +/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. + Copyright (c) 2010, 2019, MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2858,15 +2858,18 @@ public: class Yacc_state { public: - Yacc_state() - { - reset(); - } + Yacc_state() : yacc_yyss(NULL), yacc_yyvs(NULL) { reset(); } void reset() { - yacc_yyss= NULL; - yacc_yyvs= NULL; + if (yacc_yyss != NULL) { + my_free(yacc_yyss); + yacc_yyss = NULL; + } + if (yacc_yyvs != NULL) { + my_free(yacc_yyvs); + yacc_yyvs = NULL; + } m_set_signal_info.clear(); m_lock_type= TL_READ_DEFAULT; m_mdl_type= MDL_SHARED_READ; -- cgit v1.2.1 From 2536c0b1ebf6c5012ae34435d82fb2f5fa54aea5 Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Wed, 17 Apr 2019 15:36:29 +0530 Subject: BUG#28642318: POINT IN TIME RECOVERY USING MYSQLBINLOG BROKEN WITH TEMPORARY TABLE -> ERRORS Analysis ======== Point in time recovery using mysqlbinlog containing queries operating on temporary tables results in an error. While writing the query log event in the binary log, the thread id used for execution of DROP TABLE and DELETE commands were incorrect. The thread variable 'thread_specific_used' is used to determine whether a specific thread id is to used while executing the statements i.e using 'SET @@session.pseudo_thread_id'. This variable was not set correctly for DROP TABLE query and was never set for DELETE query. The thread id is important for temporary tables since the tables are session specific. DROP TABLE and DELETE queries executed using a wrong thread id resulted in errors while applying the queries generated by mysqlbinlog utility. Fix === Set the 'thread_specific_used' THD variable for DROP TABLE and DELETE queries. ReviewBoard: 21833 --- sql/sql_delete.cc | 5 +++-- sql/sql_table.cc | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 27dd4260ea2..df1489fbd93 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,5 +1,5 @@ -/* - Copyright (c) 2000, 2010, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1054,6 +1054,7 @@ bool multi_delete::send_eof() thd->clear_error(); else errcode= query_error_code(thd, killed_status == NOT_KILLED); + thd->thread_specific_used= TRUE; if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_tables, FALSE, FALSE, errcode) && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 99f2ce580c0..9e80350f3ab 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2018, MariaDB + Copyright (c) 2000, 2019, Oracle and/or its affiliates. + Copyright (c) 2010, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2358,8 +2358,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name);); } DEBUG_SYNC(thd, "rm_table_no_locks_before_binlog"); - thd->thread_specific_used|= (trans_tmp_table_deleted || - non_trans_tmp_table_deleted); + thd->thread_specific_used= TRUE; error= 0; err: if (wrong_tables.length()) -- cgit v1.2.1