maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #09712
Re: 56a2835: MDEV-5535: Cannot reopen temporary table
Hi Serg!
On Fri, Jun 3, 2016 at 4:10 PM, Sergei Golubchik <serg@xxxxxxxxxxx> wrote:
> Hi, Nirbhay!
>
> Just a couple of questions/comments, see below.
>
> And please pay attention to Kristian's email about locking and parallel
> replication. I don't think I can confidently review it in a patch, so
> you may want to spend more time testing or analyzing that code.
>
Yes, I did add some related rpl test cases.
>
> Ok to push, when you're ready! Thanks!
>
> On May 26, Nirbhay Choubey wrote:
> > revision-id: 56a2835872c4ac7296ec0ae2ff618822855b0fc0
> (mariadb-10.1.8-82-g56a2835)
> > parent(s): 28c289626f318631d707f85b057a90af99018b06
> > author: Nirbhay Choubey
> > committer: Nirbhay Choubey
> > timestamp: 2016-05-26 20:42:47 -0400
> > message:
> >
> > MDEV-5535: Cannot reopen temporary table
> >
> > mysqld maintains a list of TABLE objects for all temporary
> > tables in THD, where each table is represented by a TABLE
> > object. A query referencing a temporary table for more than
> > once, however, failed with ER_CANT_REOPEN_TABLE error.
>
> "because a TABLE_SHARE was allocate together with the TABLE, so
> temporary tables always had one TABLE per TABLE_SHARE"
>
> > This patch lift this restriction by preserving the TABLE_SHARE
> > object of each created temporary table, so that multiple instances
> > of TABLE objects can be created.
>
> better "by separating TABLE and TABLE_SHARE objects and storing
> TABLE_SHARE's in the list in a THD, and TABLE's in the list in
> the corresponding TABLE_SHARE"
>
That's better indeed. I will update that when I merge the consolidated
patches to the main branch.
>
> > diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
> > index 7bdd9c1..77f458b 100644
> > --- a/sql/rpl_rli.cc
> > +++ b/sql/rpl_rli.cc
> > @@ -1060,24 +1061,43 @@ void
> Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
> >
> > void Relay_log_info::close_temporary_tables()
> > {
> > - TABLE *table,*next;
> > DBUG_ENTER("Relay_log_info::close_temporary_tables");
> >
> > - for (table=save_temporary_tables ; table ; table=next)
> > + TMP_TABLE_SHARE *share;
> > + TABLE *table;
> > +
> > + while ((share= save_temporary_tables.pop_front()))
> > {
> > - next=table->next;
> > + /*
> > + Iterate over the list of tables for this TABLE_SHARE and close
> them.
> > + */
> > + while ((table= share->all_tmp_tables.pop_front()))
> > + {
> > + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
> > + table->s->db.str,
> table->s->table_name.str));
> > +
> > + /* Reset in_use as the table may have been created by another thd
> */
> > + table->in_use= 0;
> > + free_io_cache(table);
> > + /*
> > + Lets not free TABLE_SHARE here as there could be multiple
> TABLEs opened
> > + for the same table (TABLE_SHARE).
> > + */
> > + closefrm(table, false);
> > + my_free(table);
> > + }
> >
> > - /* Reset in_use as the table may have been created by another thd */
> > - table->in_use=0;
> > /*
> > Don't ask for disk deletion. For now, anyway they will be deleted
> when
> > slave restarts, but it is a better intention to not delete them.
> > */
> > - DBUG_PRINT("info", ("table: 0x%lx", (long) table));
> > - close_temporary(table, 1, 0);
> > +
> > + free_table_share(share);
> > + my_free(share);
> > }
> > - save_temporary_tables= 0;
> > - slave_open_temp_tables= 0;
> > +
> > + save_temporary_tables.empty();
>
> Is it needed?
> you've popped all shares from the list, it's empty.
> I'd rather add an assert instead.
>
Right. I realized this later. Fixed.
>
> > +
> > DBUG_VOID_RETURN;
> > }
> >
> > diff --git a/sql/sql_class.h b/sql/sql_class.h
> > index ae240ae..a70c4a9 100644
> > --- a/sql/sql_class.h
> > +++ b/sql/sql_class.h
> > @@ -1247,6 +1247,61 @@ enum enum_locked_tables_mode
> > LTM_PRELOCKED_UNDER_LOCK_TABLES
> > };
> >
> > +/**
> > + The following structure is an extension to TABLE_SHARE and is
> > + exclusively for temporary tables.
> > +
> > + @note:
> > + Although, TDC_element has data members (like next, prev &
> > + all_tables) to store the list of TABLE_SHARE & TABLE objects
> > + related to a particular TABLE_SHARE, they cannot be moved to
> > + TABLE_SHARE in order to be reused for temporary tables. This
> > + is because, as concurrent threads iterating through hash of
> > + TDC_element's may need access to all_tables, but if all_tables
> > + is made part of TABLE_SHARE, then TDC_element->share->all_tables
> > + is not always guaranteed to be valid, as TDC_element can live
> > + longer than TABLE_SHARE.
> > +*/
> > +struct TMP_TABLE_SHARE : public TABLE_SHARE
> > +{
> > +private:
> > + /*
> > + Link to all temporary table shares. Declared as private to
> > + avoid direct manipulation with those objects. One should
> > + use methods of I_P_List template instead.
> > + */
> > + TMP_TABLE_SHARE *tmp_next;
> > + TMP_TABLE_SHARE **tmp_prev;
> > +
> > + friend struct All_tmp_table_shares;
> > +
> > +public:
> > + /*
> > + Doubly-linked (back-linked) lists of used and unused TABLE objects
> > + for this share.
>
> and unused? you don't free them but keep in the list?
>
Yes. We did agree to not free/close but reuse them.
>
> > + */
> > + All_share_tables_list all_tmp_tables;
> > +};
> > +
> > +/**
> > + Helper class which specifies which members of TMP_TABLE_SHARE are
> > + used for participation in the list of temporary tables.
> > +*/
> > +
> > +struct All_tmp_table_shares
> > +{
> > + static inline TMP_TABLE_SHARE **next_ptr(TMP_TABLE_SHARE *l)
> > + {
> > + return &l->tmp_next;
> > + }
> > + static inline TMP_TABLE_SHARE ***prev_ptr(TMP_TABLE_SHARE *l)
> > + {
> > + return &l->tmp_prev;
> > + }
> > +};
> > +
> > +/* Also used in rpl_rli.h. */
> > +typedef I_P_List <TMP_TABLE_SHARE, All_tmp_table_shares>
> All_tmp_tables_list;
> >
> > /**
> > Class that holds information about tables which were opened and locked
> > diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
> > new file mode 100644
> > index 0000000..1514b72
> > --- /dev/null
> > +++ b/sql/temporary_tables.cc
> ...
> > +TABLE *THD::find_temporary_table(const char *db,
> > + const char *table_name)
> > +{
> > + DBUG_ENTER("THD::find_temporary_table");
> > +
> > + TABLE *table;
> > + char key[MAX_DBKEY_LENGTH];
> > + uint key_length;
> > + bool locked;
> > +
> > + if (!(has_temporary_tables() || (rgi_slave &&
> has_slave_temporary_tables())))
>
> hmm, really? you had here is_empty(true) before
> and your is_empty() was working like this:
>
> if (flag_is_true)
> { do rgi_slave }
> else
> { do thd }
>
> your old is_empty was not checking thd if the argument was true,
> your new code does, as if your is_empty() was
>
> do thd;
> if (flag_is_true)
> { do rgi_slave}
>
> > + {
> > + DBUG_RETURN(NULL);
> > + }
> > +
> > + key_length= create_tmp_table_def_key(key, db, table_name);
>
Yea, there was a problem here. It should have checked for !rgi_slave for
thd before
proceeding check slave temporary tables. Its now :
(!rgi_slave && has_temporary_tables()) || (rgi_slave &&
unlikely(has_slave_temporary_tables())))
Best,
Nirbhay
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security@xxxxxxxxxxx
>
References