midenok / mariadb

MariaDB server is a community developed fork of MySQL server. Started by core members of the original MySQL team, MariaDB actively works with outside developers to deliver the most featureful, stable, and sanely licensed open SQL server in the industry.
GNU General Public License v2.0
0 stars 0 forks source link

MDEV-23639 Auto-create does not work under LOCK TABLES or inside triggers #92

Open midenok opened 3 years ago

midenok commented 3 years ago

Reproduce

--source include/have_partition.inc

set timestamp= unix_timestamp('2000-01-01 00:00:00');
create or replace table t1 (x int) with system versioning
partition by system_time interval 1 hour auto (
    partition p1 history,
    partition p3 history,
    partition pn current);

insert into t1 values (1);

set timestamp= unix_timestamp('2000-01-01 04:00:00');
lock tables t1 write;
update t1 set x= x + 5;
unlock tables;

show create table t1;
drop tables t1;

Result

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:49
#1  0x00007f6b5a213864 in __GI_abort () at abort.c:79
#2  0x00007f6b5a213749 in __assert_fail_base (fmt=0x7f6b5a39f458 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x17ea513 "!table->pos_in_locked_tables", file=0x17ea3fc "/home/midenok/src/mariadb/10.6b/src/sql/table_cache.cc", line=441, function=<optimized out>) at assert.c:92
#3  0x00007f6b5a225a96 in __GI___assert_fail (assertion=0x17ea513 "!table->pos_in_locked_tables", file=0x17ea3fc "/home/midenok/src/mariadb/10.6b/src/sql/table_cache.cc", line=441, function=0x17ea4f4 "void tc_release_table(TABLE *)") at assert.c:101
#4  0x0000000000bc29b6 in tc_release_table (table=0x7f6b38018188) at /home/midenok/src/mariadb/10.6b/src/sql/table_cache.cc:441
#5  0x0000000000822f6a in TABLE::vers_switch_partition (this=0x7f6b38018188, thd=0x7f6b38000d48, table_list=0x7f6b3802d030, ot_ctx=0x7f6b5405c8a8) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:1735
#6  0x0000000000823503 in open_table (thd=0x7f6b38000d48, table_list=0x7f6b3802d030, ot_ctx=0x7f6b5405c8a8) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:1897
#7  0x0000000000829f39 in open_and_process_table (thd=0x7f6b38000d48, tables=0x7f6b3802d030, counter=0x7f6b5405d41c, flags=0, prelocking_strategy=0x7f6b5405c968, has_prelocking_list=false, ot_ctx=0x7f6b5405c8a8) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:3981
#8  0x000000000082875a in open_tables (thd=0x7f6b38000d48, options=..., start=0x7f6b5405d468, counter=0x7f6b5405d41c, flags=0, prelocking_strategy=0x7f6b5405c968) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:4464
#9  0x000000000081a8e6 in open_tables (thd=0x7f6b38000d48, tables=0x7f6b5405d468, counter=0x7f6b5405d41c, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.h:479
#10 0x0000000000a36ab3 in mysql_update (thd=0x7f6b38000d48, table_list=0x7f6b3802d030, fields=..., values=..., conds=0x0, order_num=0, order=0x0, limit=18446744073709551615, ignore=false, found_return=0x7f6b5405e0a0, updated_return=0x7f6b5405e098) at /home/midenok/src/mariadb/10.6b/src/sql/sql_update.cc:409
#11 0x00000000008f16cc in mysql_execute_command (thd=0x7f6b38000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:4403
#12 0x00000000008e8696 in mysql_parse (thd=0x7f6b38000d48, rawbuf=0x7f6b38013e70 "update t1 set x= x + 5", length=22, parser_state=0x7f6b5405f340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

Possible fix

  1. acquire MDL_EXCLUSIVE;
  2. remove table from locked tables list and close;
  3. do auto-creation;
  4. open new table and add it to locked tables list.
midenok commented 3 years ago

1. MDL_SHARED_NO_READ_WRITE acquired

#0  0x0000000000ad6cd4 in MDL_context::try_acquire_lock_impl (this=0x7f6b38000e80, mdl_request=0x7f6b38028060, out_ticket=0x7f6b5405c900) at /home/midenok/src/mariadb/10.6b/src/sql/mdl.cc:2050
#1  0x0000000000ad74db in MDL_context::acquire_lock (this=0x7f6b38000e80, mdl_request=0x7f6b38028060, lock_wait_timeout=86400) at /home/midenok/src/mariadb/10.6b/src/sql/mdl.cc:2248
#2  0x0000000000824bb8 in open_table_get_mdl_lock (thd=0x7f6b38000d48, ot_ctx=0x7f6b5405d2c8, mdl_request=0x7f6b38028060, flags=0, mdl_ticket=0x7f6b5405cf40) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:1546
#3  0x00000000008236ac in open_table (thd=0x7f6b38000d48, table_list=0x7f6b38027c00, ot_ctx=0x7f6b5405d2c8) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:1934
#4  0x0000000000829f39 in open_and_process_table (thd=0x7f6b38000d48, tables=0x7f6b38027c00, counter=0x7f6b5405d434, flags=0, prelocking_strategy=0x7f6b5405d470, has_prelocking_list=false, ot_ctx=0x7f6b5405d2c8) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:3981
#5  0x000000000082875a in open_tables (thd=0x7f6b38000d48, options=..., start=0x7f6b5405d478, counter=0x7f6b5405d434, flags=0, prelocking_strategy=0x7f6b5405d470) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:4464
#6  0x00000000009047a5 in open_tables (thd=0x7f6b38000d48, tables=0x7f6b5405d478, counter=0x7f6b5405d434, flags=0, prelocking_strategy=0x7f6b5405d470) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.h:265
#7  0x00000000008fb6a7 in lock_tables_open_and_lock_tables (thd=0x7f6b38000d48, tables=0x7f6b38027c00) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:2856
#8  0x00000000008f4706 in mysql_execute_command (thd=0x7f6b38000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:5121
#9  0x00000000008e8696 in mysql_parse (thd=0x7f6b38000d48, rawbuf=0x7f6b38013e70 "lock tables t1 write", length=20, parser_state=0x7f6b5405f340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

2. Table locked by F_WRLCK

#0  ha_myisam::external_lock (this=0x7f6b3801e660, thd=0x7f6b38000d48, lock_type=1) at /home/midenok/src/mariadb/10.6b/src/storage/myisam/ha_myisam.cc:2187
#1  0x0000000000cc989e in handler::ha_external_lock (this=0x7f6b3801e660, thd=0x7f6b38000d48, lock_type=1) at /home/midenok/src/mariadb/10.6b/src/sql/handler.cc:6690
#2  0x000000000107af9f in ha_partition::external_lock (this=0x7f6b380b7a40, thd=0x7f6b38000d48, lock_type=1) at /home/midenok/src/mariadb/10.6b/src/sql/ha_partition.cc:4061
#3  0x0000000000cc983c in handler::ha_external_lock (this=0x7f6b380b7a40, thd=0x7f6b38000d48, lock_type=1) at /home/midenok/src/mariadb/10.6b/src/sql/handler.cc:6690
#4  0x0000000000e524a1 in lock_external (thd=0x7f6b38000d48, tables=0x7f6b380b5078, count=1) at /home/midenok/src/mariadb/10.6b/src/sql/lock.cc:393
#5  0x0000000000e51fd3 in mysql_lock_tables (thd=0x7f6b38000d48, sql_lock=0x7f6b380b5008, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/lock.cc:338
#6  0x0000000000e510a2 in mysql_lock_tables (thd=0x7f6b38000d48, tables=0x7f6b38028ed0, count=1, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/lock.cc:301
#7  0x000000000082b944 in lock_tables (thd=0x7f6b38000d48, tables=0x7f6b38027c00, count=1, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5725
#8  0x00000000008fbae4 in lock_tables_open_and_lock_tables (thd=0x7f6b38000d48, tables=0x7f6b38027c00) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:2952
#9  0x00000000008f4706 in mysql_execute_command (thd=0x7f6b38000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:5121
#10 0x00000000008e8696 in mysql_parse (thd=0x7f6b38000d48, rawbuf=0x7f6b38013e70 "lock tables t1 write", length=20, parser_state=0x7f6b5405f340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

frame 7

5725        if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
5726                                            flags)))
5727          DBUG_RETURN(TRUE);

3. pos_in_locked_tables set

#0  0x00000000008255ec in Locked_tables_list::init_locked_tables (this=0x7f6b38004ee8, thd=0x7f6b38000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:2435
#1  0x00000000008fbb1b in lock_tables_open_and_lock_tables (thd=0x7f6b38000d48, tables=0x7f6b38027c00) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:2953
#2  0x00000000008f4706 in mysql_execute_command (thd=0x7f6b38000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:5121
#3  0x00000000008e8696 in mysql_parse (thd=0x7f6b38000d48, rawbuf=0x7f6b38013e70 "lock tables t1 write", length=20, parser_state=0x7f6b5405f340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

frame 1

2952      if (lock_tables(thd, tables, counter, 0) ||
2953          thd->locked_tables_list.init_locked_tables(thd))
2954        goto err;

Note

While we closing the table thd->lock must be kept intact

midenok commented 3 years ago

Info

/**
  Class that holds information about tables which were opened and locked
  by the thread. It is also used to save/restore this information in
  push_open_tables_state()/pop_open_tables_state().
*/

class Open_tables_state
{
...
  /*
    During a MySQL session, one can lock tables in two modes: automatic
    or manual. In automatic mode all necessary tables are locked just before
    statement execution, and all acquired locks are stored in 'lock'
    member. Unlocking takes place automatically as well, when the
    statement ends.
    Manual mode comes into play when a user issues a 'LOCK TABLES'
    statement. In this mode the user can only use the locked tables.
    Trying to use any other tables will give an error.
    The locked tables are also stored in this member, however,
    thd->locked_tables_mode is turned on.  Manual locking is described in
    the 'LOCK_TABLES' chapter of the MySQL manual.
    See also lock_tables() for details.
  */
  MYSQL_LOCK *lock;

THD is Open_tables_state!

/****************************************************************************
  Handling of open and locked tables states.

  This is used when we want to open/lock (and then close) some tables when
  we already have a set of tables open and locked. We use these methods for
  access to mysql.proc table to find definitions of stored routines.
****************************************************************************/

void THD::reset_n_backup_open_tables_state(Open_tables_backup *backup)
{
  DBUG_ENTER("reset_n_backup_open_tables_state");
  backup->set_open_tables_state(this);
  backup->mdl_system_tables_svp= mdl_context.mdl_savepoint();
  reset_open_tables_state(this);
  state_flags|= Open_tables_state::BACKUPS_AVAIL;
  DBUG_VOID_RETURN;
}

void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
{
  DBUG_ENTER("restore_backup_open_tables_state");
  mdl_context.rollback_to_savepoint(backup->mdl_system_tables_svp);
  /*
    Before we will throw away current open tables state we want
    to be sure that it was properly cleaned up.
  */
  DBUG_ASSERT(open_tables == 0 &&
              temporary_tables == 0 &&
              derived_tables == 0 &&
              lock == 0 &&
              locked_tables_mode == LTM_NONE &&
              m_reprepare_observer == NULL);

  set_open_tables_state(backup);
  DBUG_VOID_RETURN;
}
void
close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
                          ha_extra_function extra,
                          TABLE *skip_table)
{
...
  for (TABLE **prev= &thd->open_tables; *prev; )
  {
...
      /*
        Does nothing if the table is not locked.
        This allows one to use this function after a table
        has been unlocked, e.g. in partition management.
      */
      mysql_lock_remove(thd, thd->lock, table);
int close_thread_tables(THD *thd)
{
...
  if (thd->lock)
  {
...
    error= mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
bool
Locked_tables_list::reopen_tables(THD *thd, bool need_reopen)
{
...
  for (TABLE_LIST *table_list= m_locked_tables;
       table_list; table_list= table_list->next_global)
  {
    if (need_reopen)
    {
...
      for (TABLE **prev= &thd->open_tables; *prev; prev= &(*prev)->next)
      {
        if (*prev == table_list->table)
        {
          thd->locked_tables_list.unlink_from_list(thd, table_list, false);
          mysql_lock_remove(thd, thd->lock, *prev);
          (*prev)->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
          close_thread_table(thd, prev);
          break;
        }
      }
/**
  Add back a locked table to the locked list that we just removed from it.
  This is needed in CREATE OR REPLACE TABLE where we are dropping, creating
  and re-opening a locked table.
*/
bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list,
                                      TABLE *table, MYSQL_LOCK *lock)
{
...
  if (!(merged_lock= mysql_lock_merge(thd->lock, lock)))
    DBUG_RETURN(1);
  thd->lock= merged_lock;
midenok commented 3 years ago

Bug: share not released

#8  0x0000000000bc5b0e in tdc_remove_table (thd=0x7fffe0000d48, db=0x7fffe0028ed0 "test", table_name=0x7fffe002bbc0 "t1") at ../src/sql/table_cache.cc:1061
#9  0x00000000008262fb in Open_table_context::recover_from_failed_open (this=0x7ffff1f47850) at ../src/sql/sql_base.cc:3341
#10 0x000000000082889d in open_tables (thd=0x7fffe0000d48, options=..., start=0x7ffff1f48468, counter=0x7ffff1f4841c, flags=0, prelocking_strategy=0x7ffff1f47968) at ../src/sql/sql_base.cc:4503
#11 0x000000000081a8e6 in open_tables (thd=0x7fffe0000d48, tables=0x7ffff1f48468, counter=0x7ffff1f4841c, flags=0) at ../src/sql/sql_base.h:479
#12 0x0000000000a36b63 in mysql_update (thd=0x7fffe0000d48, table_list=0x7fffe002d030, fields=..., values=..., conds=0x0, order_num=0, order=0x0, limit=18446744073709551615, ignore=false, found_return=0x7ffff1f490a0, updated_return=0x7ffff1f49098) at ../src/sql/sql_update.cc:409
#13 0x00000000008f175c in mysql_execute_command (thd=0x7fffe0000d48) at ../src/sql/sql_parse.cc:4403
#14 0x00000000008e8726 in mysql_parse (thd=0x7fffe0000d48, rawbuf=0x7fffe0013e70 "update t1 set x= x + 5"

frame 9

3341          tdc_remove_table(m_thd, m_failed_table->db.str,
3342                           m_failed_table->table_name.str);
3343
3344          switch (m_action)

Cause

We didn't release locked table and hence share.

Fix?

Keep file lock but release table. Then somehow attach lock to new table? Is it possible to do fast alter while holding the file lock? Should be yes, because we are the thread owning the lock.

midenok commented 3 years ago

handler closed

/*
  Free information allocated by openfrm

  SYNOPSIS
    closefrm()
    table       TABLE object to free
*/

int closefrm(TABLE *table)
{
...
  if (table->db_stat)
    error=table->file->ha_close();

Info

/**
  Try to find the table in the list of locked tables.
  In case of success, unlock the table and remove it from this list.
  If a table has more than one lock instance, removes them all.

  @param  thd             thread context
  @param  locked          list of locked tables
  @param  table           the table to unlock
*/

int mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
  int error= 0;
  if (locked)
  {
    uint i;
    for (i=0; i < locked->table_count; i++)
    {
      if (locked->table[i] == table)
      {
...
        if ((tmp_error= mysql_unlock_some_tables(thd, &table,
                                                 /* table count */ 1, 0)))

... then it splices corresponding arrays (table, locks) inside locked structure

The below function allocates MYSQL_LOCK structure specific to given tables.

/**
  Unlock some of the tables locked by mysql_lock_tables.

  This will work even if get_lock_data fails (next unlock will free all)
*/

int mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag)
{
  int error;
  MYSQL_LOCK *sql_lock;
  if (!(sql_lock= get_lock_data(thd, table, count,
                                GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag)))
    error= ER_OUTOFMEMORY;
  else
    error= mysql_unlock_tables(thd, sql_lock, 0);
  return error;
}

Fix?

Clone handler, save old handler, assign clone to table before close. When reopened do the reverse: close current handler and assign saved one. This may require flushing from cache here:

void tc_release_table(TABLE *table)
{
  uint32 i= table->instance;
  DBUG_ENTER("tc_release_table");
  DBUG_ASSERT(table->in_use);
  DBUG_ASSERT(table->file);
  DBUG_ASSERT(!table->pos_in_locked_tables);

  mysql_mutex_lock(&tc[i].LOCK_table_cache);
  if (table->needs_reopen() || table->s->tdc->flushed ||
      tc[i].records > tc_size)
  {
    tc[i].records--;
    mysql_mutex_unlock(&tc[i].LOCK_table_cache);
    tc_remove_table(table);
  }
  else
  {
    table->in_use= 0;
    table->s->tdc->free_tables[i].list.push_front(table);
    tc[i].free_tables.push_back(table);
    mysql_mutex_unlock(&tc[i].LOCK_table_cache);
  }
  DBUG_VOID_RETURN;
}
midenok commented 3 years ago
/**
   Clone a handler

   @param name     name of new table instance
   @param mem_root Where 'this->ref' should be allocated. It can't be
                   in this->table->mem_root as otherwise we will not be
                   able to reclaim that memory when the clone handler
                   object is destroyed.
*/

handler *handler::clone(const char *name, MEM_ROOT *mem_root)
{
  handler *new_handler= get_new_handler(table->s, mem_root, ht);

  if (!new_handler)
    return NULL;
  if (new_handler->set_ha_share_ref(ha_share))
    goto err;

  /*
    TODO: Implement a more efficient way to have more than one index open for
    the same table instance. The ha_open call is not cacheable for clone.

    This is not critical as the engines already have the table open
    and should be able to use the original instance of the table.
  */
  if (new_handler->ha_open(table, name, table->db_stat,
                           HA_OPEN_IGNORE_IF_LOCKED, mem_root))
    goto err;

  return new_handler;

err:
  delete new_handler;
  return NULL;
}

HA_OPEN_IGNORE_IF_LOCKED: don't fail open() on lock error.

MI_INFO *mi_open(const char *name, int mode, uint open_flags)
{
...
    if (!(open_flags & HA_OPEN_TMP_TABLE))
    {
      if ((lock_error=my_lock(kfile,F_RDLCK,0L,F_TO_EOF,
                  MYF(open_flags & HA_OPEN_WAIT_IF_LOCKED ?
                  0 : MY_SHORT_WAIT))) &&
      !(open_flags & HA_OPEN_IGNORE_IF_LOCKED))
    goto err;
    }
midenok commented 3 years ago

Info: search of locked table

1871      if (thd->locked_tables_mode &&
1872          ! (flags & MYSQL_OPEN_GET_NEW_TABLE))
1873      {                                             // Using table locks
1874        TABLE *best_table= 0;
1875        int best_distance= INT_MIN;
1876        for (table=thd->open_tables; table ; table=table->next)
1877        {
1878          if (table->s->table_cache_key.length == key_length &&
1879              !memcmp(table->s->table_cache_key.str, key, key_length))
1880          {
1881            if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
1882                table->query_id != thd->query_id && /* skip tables already used */
1883                (thd->locked_tables_mode == LTM_LOCK_TABLES ||
1884                 table->query_id == 0))
1885            {
1886              int distance= ((int) table->reginfo.lock_type -
1887                             (int) table_list->lock_type);
1888
1889              /*
1890                Find a table that either has the exact lock type requested,
1891                or has the best suitable lock. In case there is no locked
1892                table that has an equal or higher lock than requested,
1893                we us the closest matching lock to be able to produce an error
1894                message about wrong lock mode on the table. The best_table
1895                is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.
1896
1897                distance <  0 - No suitable lock found
1898                distance >  0 - we have lock mode higher then we require
1899                distance == 0 - we have lock mode exactly which we need
1900              */
1901              if ((best_distance < 0 && distance > best_distance) ||
1902                  (distance >= 0 && distance < best_distance))
1903              {
1904                best_distance= distance;
1905                best_table= table;
1906                if (best_distance == 0)
1907                {
1908                  /*
1909                    We have found a perfect match and can finish iterating
1910                    through open tables list. Check for table use conflict
1911                    between calling statement and SP/trigger is done in
1912                    lock_tables().
1913                  */
1914                  break;
1915                }
1916              }
1917            }
1918          }
1919        }

Bug: table->file access segfault

==1769920==ERROR: AddressSanitizer: heap-use-after-free on address 0x61900003f798 at pc 0x000000aac313 bp 0x7f759fe3a980 sp 0x7f759fe3a978
READ of size 8 at 0x61900003f798 thread T5
    #0 0xaac312 in Locked_tables_list::mark_table_for_reopen(THD*, TABLE*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:2731:28
    #1 0x117421b in TABLE::mark_table_for_reopen() /home/midenok/src/mariadb/10.6b/build/../src/sql/table.cc:10070:27
    #2 0x1e3550f in prep_alter_part_table(THD*, TABLE*, Alter_info*, HA_CREATE_INFO*, bool*, bool*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_partition.cc:4993:14
    #3 0x11e3974 in vers_create_partitions(THD*, TABLE_LIST*, unsigned int) /home/midenok/src/mariadb/10.6b/build/../src/sql/partition_info.cc:1001:9
    #4 0xaae33d in Open_table_context::recover_from_failed_open() /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:3411:19
    #5 0xab460b in open_tables(THD*, DDL_options_st const&, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:4554:22
    #6 0xa8b810 in open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.h:479:10
    #7 0x10b9897 in mysql_update(THD*, TABLE_LIST*, List<Item>&, List<Item>&, Item*, unsigned int, st_order*, unsigned long long, bool, unsigned long long*, unsigned long long*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_update.cc:409:7
    #8 0xcdbcd9 in mysql_execute_command(THD*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:4403:21
    #9 0xcc4580 in mysql_parse(THD*, char*, unsigned int, Parser_state*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:8021:18
    #10 0xcbdd8d in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1898:7
    #11 0xcc642d in do_command(THD*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1407:17
    #12 0x126832a in do_handle_one_connection(CONNECT*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1410:11
    #13 0x1267a51 in handle_one_connection /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1312:5
    #14 0x234ae35 in pfs_spawn_thread /home/midenok/src/mariadb/10.6b/build/../src/storage/perfschema/pfs.cc:2201:3
    #15 0x7f75a91f758f in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x958f)
    #16 0x7f75a90f5222 in clone misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:95

0x61900003f798 is located 24 bytes inside of 1008-byte region [0x61900003f780,0x61900003fb70)
freed by thread T5 here:
    #0 0x86cc92 in __interceptor_free (/home/midenok/src/mariadb/10.6b/build/sql/mariadbd+0x86cc92)
    #1 0x3310b31 in my_free /home/midenok/src/mariadb/10.6b/build/../src/mysys/my_malloc.c:211:3
    #2 0x1516283 in intern_close_table(TABLE*) /home/midenok/src/mariadb/10.6b/build/../src/sql/table_cache.cc:222:3
    #3 0x151f382 in TDC_element::flush_unused(bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/table_cache.cc:1292:5
    #4 0x151ef2b in tdc_remove_referenced_share(THD*, TABLE_SHARE*) /home/midenok/src/mariadb/10.6b/build/../src/sql/table_cache.cc:1004:15
    #5 0x151fb4c in tdc_remove_table(THD*, char const*, char const*) /home/midenok/src/mariadb/10.6b/build/../src/sql/table_cache.cc:1061:3
    #6 0xaadace in Open_table_context::recover_from_failed_open() /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:3369:7
    #7 0xab460b in open_tables(THD*, DDL_options_st const&, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:4554:22
    #8 0xa8b810 in open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.h:479:10
    #9 0x10b9897 in mysql_update(THD*, TABLE_LIST*, List<Item>&, List<Item>&, Item*, unsigned int, st_order*, unsigned long long, bool, unsigned long long*, unsigned long long*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_update.cc:409:7
    #10 0xcdbcd9 in mysql_execute_command(THD*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:4403:21
    #11 0xcc4580 in mysql_parse(THD*, char*, unsigned int, Parser_state*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:8021:18
    #12 0xcbdd8d in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1898:7
    #13 0xcc642d in do_command(THD*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1407:17
    #14 0x126832a in do_handle_one_connection(CONNECT*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1410:11
    #15 0x1267a51 in handle_one_connection /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1312:5
    #16 0x234ae35 in pfs_spawn_thread /home/midenok/src/mariadb/10.6b/build/../src/storage/perfschema/pfs.cc:2201:3
    #17 0x7f75a91f758f in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x958f)

previously allocated by thread T5 here:
    #0 0x86d013 in __interceptor_malloc (/home/midenok/src/mariadb/10.6b/build/sql/mariadbd+0x86d013)
    #1 0x330edfe in my_malloc /home/midenok/src/mariadb/10.6b/build/../src/mysys/my_malloc.c:90:29
    #2 0xaa6837 in open_table(THD*, TABLE_LIST*, Open_table_context*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:2148:26
    #3 0xab8599 in open_and_process_table(THD*, TABLE_LIST*, unsigned int*, unsigned int, Prelocking_strategy*, bool, Open_table_context*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:4041:14
    #4 0xab454e in open_tables(THD*, DDL_options_st const&, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.cc:4524:14
    #5 0xd0cfb4 in open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_base.h:265:10
    #6 0xcf3118 in lock_tables_open_and_lock_tables(THD*, TABLE_LIST*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:2856:7
    #7 0xce298d in mysql_execute_command(THD*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:5121:10
    #8 0xcc4580 in mysql_parse(THD*, char*, unsigned int, Parser_state*) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:8021:18
    #9 0xcbdd8d in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1898:7
    #10 0xcc642d in do_command(THD*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_parse.cc:1407:17
    #11 0x126832a in do_handle_one_connection(CONNECT*, bool) /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1410:11
    #12 0x1267a51 in handle_one_connection /home/midenok/src/mariadb/10.6b/build/../src/sql/sql_connect.cc:1312:5
    #13 0x234ae35 in pfs_spawn_thread /home/midenok/src/mariadb/10.6b/build/../src/storage/perfschema/pfs.cc:2201:3
    #14 0x7f75a91f758f in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x958f)
216     static void intern_close_table(TABLE *table)
217     {
218       delete table->triggers;
219       DBUG_ASSERT(table->file);
220       closefrm(table);
221       tdc_release_share(table->s);
222       my_free(table);
223     }

Cause

Locked_tables_list structure was not updated.

Note

table->file is allocated from TABLE::mem_root.

midenok commented 3 years ago

Info: fast_alter_partition can work under LOCK TABLES

6800    static bool alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
6801    {
6802      THD *thd= lpt->thd;
6803
6804      if (lpt->table)
6805      {
6806        /*
6807          Remove all instances of the table and its locks and other resources.
6808        */
6809        close_all_tables_for_name(thd, lpt->table->s, HA_EXTRA_NOT_USED, NULL);
6810      }
6811      lpt->table= 0;
6812      lpt->table_list->table= 0;
6813      if (thd->locked_tables_mode)
6814        return thd->locked_tables_list.reopen_tables(thd, false);
6815
6816      return false;
6817    }

But in LTM_PRELOCKED mode locked_tables_list is empty.

midenok commented 3 years ago

Info: prelocking environment set

1. Table added for prelocking

#0  0x00000000007d3226 in TABLE_LIST::init_one_table_for_prelocking (this=0x7f372813c240, db_arg=0x7f37399905f8, table_name_arg=0x7f37399905d8, alias_arg=0x7f37399905c8, lock_type_arg=TL_WRITE_DEFAULT, prelocking_type=TABLE_LIST::PRELOCK_ROUTINE, belong_to_view_arg=0x0, trg_event_map_arg=4 '\004', last_ptr=0x7f3728005000, insert_data=0 '\000') at /home/midenok/src/mariadb/10.6b/src/sql/table.h:2201
#1  0x00000000007ce8f2 in sp_head::add_used_tables_to_table_list (this=0x7f37280c5f00, thd=0x7f3728000d48, query_tables_last_ptr=0x7f3728005000, belong_to_view=0x0) at /home/midenok/src/mariadb/10.6b/src/sql/sp_head.cc:5100
#2  0x0000000000a25bb2 in Table_triggers_list::add_tables_and_routines_for_triggers (this=0x7f3728018540, thd=0x7f3728000d48, prelocking_ctx=0x7f3728004ff0, table_list=0x7f3728027c00) at /home/midenok/src/mariadb/10.6b/src/sql/sql_trigger.cc:2296
#3  0x000000000082ae4a in DML_prelocking_strategy::handle_table (this=0x7f3739990eb0, thd=0x7f3728000d48, prelocking_ctx=0x7f3728004ff0, table_list=0x7f3728027c00, need_prelocking=0x7f37399909d6) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:4941
#4  0x0000000000827f41 in extend_table_list (thd=0x7f3728000d48, tables=0x7f3728027c00, prelocking_strategy=0x7f3739990eb0, has_prelocking_list=false) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:3736
#5  0x000000000082a090 in open_and_process_table (thd=0x7f3728000d48, tables=0x7f3728027c00, counter=0x7f3739990e3c, flags=0, prelocking_strategy=0x7f3739990eb0, has_prelocking_list=false, ot_ctx=0x7f3739990d38) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:4061
#6  0x000000000082870a in open_tables (thd=0x7f3728000d48, options=..., start=0x7f3739990e50, counter=0x7f3739990e3c, flags=0, prelocking_strategy=0x7f3739990eb0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:4490
#7  0x000000000082c9a5 in open_and_lock_tables (thd=0x7f3728000d48, options=..., tables=0x7f3728027c00, derived=true, flags=0, prelocking_strategy=0x7f3739990eb0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5453
#8  0x00000000007d29ac in open_and_lock_tables (thd=0x7f3728000d48, tables=0x7f3728027c00, derived=true, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.h:509
#9  0x000000000088f83a in mysql_insert (thd=0x7f3728000d48, table_list=0x7f3728027c00, fields=..., values_list=..., update_fields=..., update_values=..., duplic=DUP_ERROR, ignore=false, result=0x0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_insert.cc:756
#10 0x00000000008f2207 in mysql_execute_command (thd=0x7f3728000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:4560
#11 0x00000000008e8866 in mysql_parse (thd=0x7f3728000d48, rawbuf=0x7f3728013e70 "insert into t2 values (2)", length=25, parser_state=0x7f3739993340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

2. Tables locked

#0  mysql_lock_tables (thd=0x7f3728000d48, tables=0x7f372813cd00, count=2, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/lock.cc:288
#1  0x000000000082bb24 in lock_tables (thd=0x7f3728000d48, tables=0x7f3728027c00, count=2, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5751
#2  0x000000000082ca59 in open_and_lock_tables (thd=0x7f3728000d48, options=..., tables=0x7f3728027c00, derived=true, flags=0, prelocking_strategy=0x7f3739990eb0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5462
#3  0x00000000007d29ac in open_and_lock_tables (thd=0x7f3728000d48, tables=0x7f3728027c00, derived=true, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.h:509
#4  0x000000000088f83a in mysql_insert (thd=0x7f3728000d48, table_list=0x7f3728027c00, fields=..., values_list=..., update_fields=..., update_values=..., duplic=DUP_ERROR, ignore=false, result=0x0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_insert.cc:756

3. LTM_PRELOCKED set

#0  THD::enter_locked_tables_mode (this=0x7f3728000d48, mode_arg=LTM_PRELOCKED) at /home/midenok/src/mariadb/10.6b/src/sql/sql_class.h:4962
#1  0x000000000082bda6 in lock_tables (thd=0x7f3728000d48, tables=0x7f3728027c00, count=2, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5791
#2  0x000000000082ca59 in open_and_lock_tables (thd=0x7f3728000d48, options=..., tables=0x7f3728027c00, derived=true, flags=0, prelocking_strategy=0x7f3739990eb0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.cc:5462
#3  0x00000000007d29ac in open_and_lock_tables (thd=0x7f3728000d48, tables=0x7f3728027c00, derived=true, flags=0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_base.h:509
#4  0x000000000088f83a in mysql_insert (thd=0x7f3728000d48, table_list=0x7f3728027c00, fields=..., values_list=..., update_fields=..., update_values=..., duplic=DUP_ERROR, ignore=false, result=0x0) at /home/midenok/src/mariadb/10.6b/src/sql/sql_insert.cc:756
#5  0x00000000008f2207 in mysql_execute_command (thd=0x7f3728000d48) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:4560
#6  0x00000000008e8866 in mysql_parse (thd=0x7f3728000d48, rawbuf=0x7f3728013e70 "insert into t2 values (2)", length=25, parser_state=0x7f3739993340) at /home/midenok/src/mariadb/10.6b/src/sql/sql_parse.cc:8021

frame 2

5757        if (thd->lex->requires_prelocking() &&
5758            thd->lex->sql_command != SQLCOM_LOCK_TABLES)
5759        {
5760          /*
5761            We just have done implicit LOCK TABLES, and now we have
5762            to emulate first open_and_lock_tables() after it.
5763
5764            When open_and_lock_tables() is called for a single table out of
5765            a table list, the 'next_global' chain is temporarily broken. We
5766            may not find 'first_not_own' before the end of the "list".
5767            Look for example at those places where open_n_lock_single_table()
5768            is called. That function implements the temporary breaking of
5769            a table list for opening a single table.
5770          */
5771          for (table= tables;
5772               table && table != first_not_own;
5773               table= table->next_global)
5774          {
5775            if (!table->placeholder())
5776            {
5777              if (check_lock_and_start_stmt(thd, thd->lex, table))
5778              {
5779                mysql_unlock_tables(thd, thd->lock);
5780                thd->lock= 0;
5781                DBUG_RETURN(TRUE);
5782              }
5783            }
5784          }
5785          /*
5786            Let us mark all tables which don't belong to the statement itself,
5787            and was marked as occupied during open_tables() as free for reuse.
5788          */
5789          mark_real_tables_as_free_for_reuse(first_not_own);
5790          DBUG_PRINT("info",("locked_tables_mode= LTM_PRELOCKED"));
5791          thd->enter_locked_tables_mode(LTM_PRELOCKED);
5792        }
5793      }