List:Commits« Previous MessageNext Message »
From:Roy Lyseng Date:February 9 2011 11:51am
Subject:Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852
View as plain text  
Hi Guilhem,

thank you for this nice refactoring of the optimizer data structs.

But I think that more work is needed before this patch can be approved.

On 31.01.11 22.51, Guilhem Bichot wrote:
> #At file:///home/mysql_src/bzrrepos_new/59852/ based on
> revid:roy.lyseng@stripped
>
>   3335 Guilhem Bichot	2011-01-31
>        Fix for BUG#59852 "EXPLAIN EXTENDED + UNION + 'ORDER BY x IN (subquery)' does
> not show IN->EXISTS transformation":
>        make EXPLAIN show it, and fix the resulting crash by getting rid of a
> redundant, bad THD pointer.
>       @ mysql-test/r/union.result
>          result. Without the change to sql_select.cc, the last EXPLAIN would not
> show
>          the IN->EXISTS transformation (would just show the original query).
>       @ mysql-test/t/union.test
>          test for bug
>       @ sql/item_subselect.cc
>          Item_subselect::thd and subselect_engine::thd are deleted.
>          The first is replaced with the existing Item_subselect::unit::thd.
>          The second one is replaced with the existing
> subselect_engine::item::unit::thd
>          (same object as for the first one, actually: item points to the containing
>          Item_subselect); for subselect_union_engine it's additionally possible to
> use
>          subselect_union_engine::unit::thd.

This is a good change in the code base. You may also consider removing the thd 
pointers from the st_select_lex_unit object. AFAIK, an st_select_lex_unit object 
cannot exist without belonging to a LEX object, hence you may replace the thd 
pointer with a LEX pointer and access the LEX's thd pointer through this one 
instead. It will also mean that data in the LEX object can be accessed without 
going through the THD pointer.

This change will cause far fewer updates to THD pointers to occur.
>
>          Before this change, Item_subselect::thd and subselect_engine::thd
>          were set to NULL in constructors, and Item_subselect::fix_fields()
>          corrected them. As Item_subselect::thd is used in subquery
>          transformations (in Item_singlerow_subselect::select_transformer()
>          called by JOIN::prepare()), it was necessary that
>          Item_subselect::fix_fields() was called before JOIN::prepare()
>          for the subquery's JOIN. And often it works indeed this way,
>          as Item_subselect::fix_fields() calls JOIN::prepare().
>
>          But in
>            "EXPLAIN select1 UNION select2 ORDER BY select3"
>          (case of BUG 49734), we have four SELECT_LEX:
>          select1-2-3 and the "fake UNION SELECT".
>          EXPLAIN EXTENDED (mysql_explain_union()) calls JOIN::prepare()
>          for select1, and select2.
>          select3 is not in the ORDER BY clause of select2, so
>          JOIN::prepare(select2) does not call Item_subselect::fix_fields().
>          Then mysql_explain_union() calls JOIN::exec(select2) which itself
>          calls select_describe(select2). That function calls
>          mysql_explain_union() on "inner units" of select2, and select3 is
>          among them. That is weird, by the way: select3 is not in ORDER BY of
>          select2 (ok) but it's a inner unit of select2 (weird); is this a
>          parsing artifact? Shouldn't it be a inner unit of the fake SELECT?
>          So we go into JOIN::prepare(select3), which calls resolve_subquery()
>          to transform, and crashes on a still-NULL Item_subselect::thd pointer.
>          If the crash is "leaped over" in gdb, then finally
>          JOIN::prepare() is called for the fake UNION SELECT, which owns the ORDER
> BY
>          clause, so it's only at this late moment, precisely in setup_order(), that
>          Item_subselect::fix_fields() is called which sets Item_subselect::thd
>          correctly. Too late.
>          See also the comment at start of st_select_lex::print()
>          about thd being NULL.
>          The advantage of using SELECT_LEX_UNIT::thd pointers is that they are
>          always kept correct by reinit_stmt_before_use(). The NULL thd pointer
>          is eliminated, we now use a correct one, which avoids the crash.
>          One may wonder whether, even with no crash thanks to this patch, it
>          is still a logical problem to have:
>          - JOIN::prepare() called before Item_subselect::fix_fields().
>          - select3 attached to select2 as inner unit but attached to fake
>            SELECT as ORDER BY clause.
>          Those oddities are a specificity of EXPLAIN UNION; without a UNION, in
>            "EXPLAIN select 1 ORDER BY select3",
>          select1 owns the ORDER BY clause, so setup_order() is called
>          from JOIN::prepare(select1), which calls Item_subselect::fix_fields(),
>          which calls JOIN::prepare() (JOIN of select3).
>          It would be interesting to discuss this with reviewers.

I think that you have a good point here. This looks like another problem that is 
worth looking into. It means that the "refactoring" part of your patch (which I 
think is good) will actually hide the original problem.
>
>          We also add assertions that various pointers-to-THD are equal.
>       @ sql/sql_select.cc
>          don't skip subquery transformations in EXPLAIN, so that the user
>          is informed about them. This was skipped by the second part of
>          the patch for BUG 49734, but it's not needed anymore, because we
>          here fix the bad-THD cause in item_subselect.cc.
>
>      modified:
>        mysql-test/r/union.result
>        mysql-test/t/union.test
>        sql/item_subselect.cc
>        sql/item_subselect.h
>        sql/sql_select.cc
> === modified file 'mysql-test/r/union.result'
> --- a/mysql-test/r/union.result	2011-01-10 12:45:53 +0000
> +++ b/mysql-test/r/union.result	2011-01-31 21:51:42 +0000
> @@ -1769,3 +1769,28 @@ dev
>   SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev;
>   dev
>   1
> +#
> +# Bug #59852: EXPLAIN EXTENDED UNION ... ORDER BY ... IN (subquery)
> +# does not show IN->EXISTS transformation
> +#
> +CREATE TABLE t1 (a VARCHAR(10), FULLTEXT KEY a (a));
> +INSERT INTO t1 VALUES (1),(2);
> +CREATE TABLE t2 (b INT);
> +INSERT INTO t2 VALUES (1),(2);
> +EXPLAIN EXTENDED SELECT * FROM t1 ORDER BY a in (SELECT a FROM t2 WHERE b = 1);
> +id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
> +1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	Using filesort
> +2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
> +Warnings:
> +Note	1276	Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
> +Note	1003	select `test`.`t1`.`a` AS `a` from `test`.`t1` order
> by<in_optimizer>(`test`.`t1`.`a`,<exists>(select 1 from `test`.`t2` where
> ((`test`.`t2`.`b` = 1) and trigcond(((<cache>(`test`.`t1`.`a`) = `test`.`t1`.`a`) or
> isnull(`test`.`t1`.`a`)))) having trigcond(<is_not_null_test>(`test`.`t1`.`a`))))
> +EXPLAIN EXTENDED SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a in (SELECT a
> FROM t2 WHERE b = 1);
> +id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
> +1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
> +2	UNION	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
> +3	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
> +NULL	UNION RESULT	<union1,2>	ALL	NULL	NULL	NULL	NULL	NULL	NULL	Using filesort
> +Warnings:
> +Note	1276	Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #2
> +Note	1003	select `test`.`t1`.`a` AS `a` from `test`.`t1` union select
> `test`.`t1`.`a` AS `a` from `test`.`t1` order
> by<in_optimizer>(`a`,<exists>(select 1 from `test`.`t2` where
> ((`test`.`t2`.`b` = 1) and trigcond(((<cache>(`a`) = `test`.`t1`.`a`) or
> isnull(`test`.`t1`.`a`)))) having (trigcond(<is_not_null_test>(`test`.`t1`.`a`)) and
> trigcond((<cache>(`a`) =<ref_null_helper>(1))))))
> +DROP TABLE t1,t2;
>
> === modified file 'mysql-test/t/union.test'
> --- a/mysql-test/t/union.test	2011-01-10 12:45:53 +0000
> +++ b/mysql-test/t/union.test	2011-01-31 21:51:42 +0000
> @@ -1185,3 +1185,16 @@ SELECT(SELECT 0 AS a FROM dual UNION SEL
>   SELECT(SELECT 1 AS a ORDER BY a) AS dev;
>   SELECT(SELECT 1 AS a LIMIT 1) AS dev;
>   SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev;
> +
> +--echo #
> +--echo # Bug #59852: EXPLAIN EXTENDED UNION ... ORDER BY ... IN (subquery)
> +--echo # does not show IN->EXISTS transformation
> +--echo #
> +
> +CREATE TABLE t1 (a VARCHAR(10), FULLTEXT KEY a (a));
> +INSERT INTO t1 VALUES (1),(2);
> +CREATE TABLE t2 (b INT);
> +INSERT INTO t2 VALUES (1),(2);
> +EXPLAIN EXTENDED SELECT * FROM t1 ORDER BY a in (SELECT a FROM t2 WHERE b = 1);
> +EXPLAIN EXTENDED SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a in (SELECT a
> FROM t2 WHERE b = 1);
> +DROP TABLE t1,t2;
>
> === modified file 'sql/item_subselect.cc'
> --- a/sql/item_subselect.cc	2011-01-18 11:42:09 +0000
> +++ b/sql/item_subselect.cc	2011-01-31 21:51:42 +0000
> @@ -45,7 +45,7 @@ inline Item * and_items(Item* cond, Item
>   }
>
>   Item_subselect::Item_subselect():
> -  Item_result_field(), value_assigned(0), thd(0), substitution(0),
> +  Item_result_field(), value_assigned(0), substitution(0),
>     engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0),
>     const_item_cache(1), engine_changed(0), changed(0),
>     is_correlated(FALSE)
> @@ -169,14 +169,25 @@ Item_subselect::select_transformer(JOIN
>   }
>
>
> -bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
> +bool Item_subselect::fix_fields(THD *thd, Item **ref)
>   {
> -  char const *save_where= thd_param->where;
> +  char const *save_where= thd->where;
>     uint8 uncacheable;
>     bool res;
>
>     DBUG_ASSERT(fixed == 0);
> -  engine->set_thd((thd= thd_param));
> +  /*
> +    Pointers to THD must match. unit::thd may vary over the lifetime of the
> +    item (for example triggers, and thus their Item-s, are in a cache shared
> +    by all connections), but reinit_stmt_before_use() keeps it up-to-date,
> +    which we check here. subselect_union_engine functions also do sanity
> +    checks.
> +  */
> +  DBUG_ASSERT(thd == unit->thd);

Not needed if unit->thd goes away :)
> +  // Engine accesses THD via its 'item' pointer, check it:
> +  engine->assert_item(this);

I am not sure that this is needed. Once set up, the optimizer structures should 
be fairly rigid.
> +
> +  engine->set_thd_for_result();
>
>     if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
>       return TRUE;
> @@ -285,6 +296,7 @@ bool Item_subselect::exec()
>       Do not execute subselect in case of a fatal error
>       or if the query has been killed.
>     */
> +  THD * const thd= unit->thd;
>     if (thd->is_error() || thd->killed)
>       return 1;
>
> @@ -436,7 +448,7 @@ table_map Item_subselect::used_tables()
>
>   bool Item_subselect::const_item() const
>   {
> -  return thd->lex->context_analysis_only ? FALSE : const_item_cache;
> +  return unit->thd->lex->context_analysis_only ? FALSE : const_item_cache;
>   }

Deleting unit->thd and adding unit->lex would actually simplify this sentence 
(and others).
>
>   Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
> @@ -520,12 +532,6 @@ Item_maxmin_subselect::Item_maxmin_subse
>     used_tables_cache= parent->get_used_tables_cache();
>     const_item_cache= parent->get_const_item_cache();
>
> -  /*
> -    this subquery always creates during preparation, so we can assign
> -    thd here
> -  */
> -  thd= thd_param;
> -
>     DBUG_VOID_RETURN;
>   }
>
> @@ -580,6 +586,7 @@ Item_singlerow_subselect::select_transfo
>       DBUG_RETURN(RES_OK);
>
>     SELECT_LEX *select_lex= join->select_lex;
> +  THD * const thd= unit->thd;
>     Query_arena *arena= thd->stmt_arena;
>
>     if (!select_lex->master_unit()->is_union()&&
> @@ -1090,6 +1097,8 @@ Item_in_subselect::single_value_transfor
>       DBUG_RETURN(RES_ERROR);
>     }
>
> +  THD * const thd= unit->thd;
> +
>     /*
>       If this is an ALL/ANY single-value subselect, try to rewrite it with
>       a MIN/MAX subselect. We can do that if a possible NULL result of the
> @@ -1263,6 +1272,7 @@ Item_subselect::trans_res
>   Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creator
> *func)
>   {
>     SELECT_LEX *select_lex= join->select_lex;
> +  THD * const thd= unit->thd;
>     DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer");
>
>     select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
> @@ -1466,6 +1476,7 @@ Item_in_subselect::row_value_transformer
>       SELECT_LEX_UNIT *master_unit= select_lex->master_unit();
>       substitution= optimizer;
>
> +    THD * const thd= unit->thd;
>       SELECT_LEX *current= thd->lex->current_select;
>       thd->lex->current_select= current->outer_select();
>       //optimizer never use Item **ref =>  we can pass 0 as parameter
> @@ -1525,6 +1536,7 @@ Item_subselect::trans_res
>   Item_in_subselect::row_value_in_to_exists_transformer(JOIN * join)
>   {
>     SELECT_LEX *select_lex= join->select_lex;
> +  THD * const thd= unit->thd;
>     Item *having_item= 0;
>     uint cols_num= left_expr->cols();
>     bool is_having_used= (join->having || select_lex->with_sum_func ||
> @@ -1755,6 +1767,7 @@ Item_subselect::trans_res
>   Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func)
>   {
>     Query_arena *arena, backup;
> +  THD * const thd= unit->thd;
>     SELECT_LEX *current= thd->lex->current_select;
>     const char *save_where= thd->where;
>     Item_subselect::trans_res res= RES_ERROR;
> @@ -1912,6 +1925,7 @@ bool Item_in_subselect::setup_engine()
>     if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
>     {
>       /* Create/initialize objects in permanent memory. */
> +    THD * const thd= unit->thd;
>       Query_arena *arena= thd->stmt_arena;
>       Query_arena backup;
>       if (arena->is_conventional())
> @@ -2008,7 +2022,7 @@ bool Item_in_subselect::init_left_expr_c
>
>     for (uint i= 0; i<  left_expr->cols(); i++)
>     {
> -    Cached_item *cur_item_cache= new_Cached_item(thd,
> +    Cached_item *cur_item_cache= new_Cached_item(unit->thd,
>                                                    left_expr->element_index(i),
>                                                    use_result_field);
>       if (!cur_item_cache || left_expr_cache->push_front(cur_item_cache))
> @@ -2064,11 +2078,14 @@ void Item_allany_subselect::print(String
>   }
>
>
> -void subselect_engine::set_thd(THD *thd_arg)
> +void subselect_engine::set_thd_for_result()
>   {
> -  thd= thd_arg;
> +  /*
> +    select_result's constructor sets neither select_result::thd nor
> +    select_result::unit.
> +  */
>     if (result)
> -    result->set_thd(thd_arg);
> +    result->set_thd(item->unit->thd);
>   }
>
>
> @@ -2180,6 +2197,7 @@ int subselect_single_select_engine::prep
>   {
>     if (prepared)
>       return 0;
> +  THD * const thd= item->unit->thd;
>     join= new JOIN(thd, select_lex->item_list,
>   		 select_lex->options | SELECT_NO_UNLOCK, result);
>     if (!join || !result)
> @@ -2205,6 +2223,9 @@ int subselect_single_select_engine::prep
>
>   int subselect_union_engine::prepare()
>   {
> +  THD * const thd= unit->thd;
> +  // We can access THD as above, or via 'item', verify equality:
> +  DBUG_ASSERT(thd == item->unit->thd);

Perhaps you could assert against current_thd as well?
>     return unit->prepare(thd, result, SELECT_NO_UNLOCK);
>   }
>
> @@ -2305,6 +2326,7 @@ int subselect_single_select_engine::exec
>   {
>     DBUG_ENTER("subselect_single_select_engine::exec");
>     int rc= 0;
> +  THD * const thd= item->unit->thd;
>     char const *save_where= thd->where;
>     SELECT_LEX *save_select= thd->lex->current_select;
>     thd->lex->current_select= select_lex;
> @@ -2406,6 +2428,8 @@ exit:
>
>   int subselect_union_engine::exec()
>   {
> +  THD * const thd= unit->thd;
> +  DBUG_ASSERT(thd == item->unit->thd);
>     char const *save_where= thd->where;
>     int res= unit->exec();
>     thd->where= save_where;
> @@ -2864,7 +2888,7 @@ subselect_single_select_engine::save_joi
>           plan to save will not be replaced anyway.
>        4) The Item_subselect is cacheable
>     */
> -  if (thd->lex->describe&&                               // 1
> +  if (item->unit->thd->lex->describe&&                   // 1
>         !select_lex->uncacheable&&                         // 2
>         !(join->select_options&  SELECT_DESCRIBE))         // 3
>     {
> @@ -2907,7 +2931,7 @@ table_map subselect_union_engine::upper_
>   void subselect_single_select_engine::print(String *str,
>                                              enum_query_type query_type)
>   {
> -  select_lex->print(thd, str, query_type);
> +  select_lex->print(item->unit->thd, str, query_type);
>   }
>
>
> @@ -3169,6 +3193,7 @@ bool subselect_hash_sj_engine::init_perm
>     */
>     if (!(tmp_result_sink= new select_union))
>       DBUG_RETURN(TRUE);
> +  THD * const thd= item->unit->thd;
>     if (tmp_result_sink->create_result_table(
>                            thd, tmp_columns, TRUE,
>                            thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
> @@ -3323,7 +3348,7 @@ bool subselect_hash_sj_engine::init_runt
>       Repeat name resolution for 'cond' since cond is not part of any
>       clause of the query, and it is not 'fixed' during JOIN::prepare.
>     */
> -  if (cond&&  !cond->fixed&& 
> cond->fix_fields(thd,&cond))
> +  if (cond&&  !cond->fixed&& 
> cond->fix_fields(item->unit->thd,&cond))
>       return TRUE;
>     /* Let our engine reuse this query plan for materialization. */
>     materialize_join= materialize_engine->join;
> @@ -3336,7 +3361,7 @@ subselect_hash_sj_engine::~subselect_has
>   {
>     delete result;
>     if (tab)
> -    free_tmp_table(thd, tab->table);
> +    free_tmp_table(item->unit->thd, tab->table);
>   }
>
>
> @@ -3380,6 +3405,7 @@ int subselect_hash_sj_engine::exec()
>     if (!is_materialized)
>     {
>       int res= 0;
> +    THD * const thd= item->unit->thd;
>       SELECT_LEX *save_select= thd->lex->current_select;
>       thd->lex->current_select= materialize_engine->select_lex;
>       if ((res= materialize_join->optimize()))
>
> === modified file 'sql/item_subselect.h'
> --- a/sql/item_subselect.h	2011-01-24 14:17:03 +0000
> +++ b/sql/item_subselect.h	2011-01-31 21:51:42 +0000
> @@ -46,8 +46,6 @@ class Item_subselect :public Item_result
>   {
>     my_bool value_assigned; /* value already assigned to subselect */
>   public:
> -  /* thread handler, will be assigned in fix_fields only */
> -  THD *thd;
>     /*
>       Used inside Item_subselect::fix_fields() according to this scenario:
>         >  Item_subselect::fix_fields
> @@ -443,7 +441,6 @@ class subselect_engine: public Sql_alloc
>   {
>   protected:
>     select_result_interceptor *result; /* results storage class */
> -  THD *thd; /* pointer to current THD */
>     Item_subselect *item; /* item, that use this engine */
>     enum Item_result res_type; /* type of results */
>     enum_field_types res_field_type; /* column type of the results */
> @@ -455,7 +452,6 @@ public:
>                            INDEXSUBQUERY_ENGINE, HASH_SJ_ENGINE};
>
>     subselect_engine(Item_subselect *si, select_result_interceptor *res)
> -    :thd(0)
>     {
>       result= res;
>       item= si;
> @@ -466,12 +462,8 @@ public:
>     virtual ~subselect_engine() {}; // to satisfy compiler
>     virtual void cleanup()= 0;
>
> -  /*
> -    Also sets "thd" for subselect_engine::result.
> -    Should be called before prepare().
> -  */
> -  void set_thd(THD *thd_arg);
> -  THD * get_thd() { return thd; }
> +  /// Sets "thd" for 'result'. Should be called before prepare()
> +  void set_thd_for_result();
>     virtual int prepare()= 0;
>     virtual void fix_length_and_dec(Item_cache** row)= 0;
>     /*
> @@ -511,6 +503,8 @@ public:
>     /* Check if subquery produced any rows during last query execution */
>     virtual bool no_rows() = 0;
>     virtual enum_engine_type engine_type() { return ABSTRACT_ENGINE; }
> +  /// Verifies that the internal item matches the argument
> +  void assert_item(const Item_subselect *i) const { DBUG_ASSERT(i == item); }
>
>   protected:
>     void set_row(List<Item>  &item_list, Item_cache **row);
> @@ -610,10 +604,7 @@ public:
>     // constructor can assign THD because it will be called after JOIN::prepare
>     subselect_uniquesubquery_engine(THD *thd_arg, st_join_table *tab_arg,
>   				  Item_subselect *subs, Item *where)
> -    :subselect_engine(subs, 0), tab(tab_arg), cond(where)
> -  {
> -    set_thd(thd_arg);
> -  }
> +    :subselect_engine(subs, 0), tab(tab_arg), cond(where) {}
>     void cleanup();
>     int prepare();
>     void fix_length_and_dec(Item_cache** row);
>
> === modified file 'sql/sql_select.cc'
> --- a/sql/sql_select.cc	2011-01-27 11:38:22 +0000
> +++ b/sql/sql_select.cc	2011-01-31 21:51:42 +0000
> @@ -596,8 +596,7 @@ JOIN::prepare(Item ***rref_pointer_array
>
>     if (select_lex->master_unit()->item&&     // This is a subquery
>                                               // Not normalizing a view
> -      !(thd->lex->context_analysis_only& 
> CONTEXT_ANALYSIS_ONLY_VIEW)&&
> -      !(select_options&  SELECT_DESCRIBE))  // Not within a describe
> +      !(thd->lex->context_analysis_only&  CONTEXT_ANALYSIS_ONLY_VIEW))
>     {
>       /* Join object is a subquery within an IN/ANY/ALL/EXISTS predicate */
>       if (resolve_subquery(thd, this))
Thanks,
Roy
Thread
bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Guilhem Bichot31 Jan
  • Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Roy Lyseng9 Feb
    • Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Guilhem Bichot19 Mar
      • Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Roy Lyseng21 Mar
        • Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Guilhem Bichot21 Mar
          • Re: bzr commit into mysql-trunk branch (guilhem.bichot:3335) Bug#59852Roy Lyseng22 Mar