mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-17070 Table corruption or Assertion table->file->stats.records > 0 || error' or Assertion
!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' failed upon actions on temporary table
This was caused by a combination of factors: * MyISAM/Aria temporary tables historically never saved the state to disk (MYI/MAI), because the state never needed to persist * certain ALTER TABLE operations modify the original TABLE structure and if they fail, the original table has to be reopened to revert all changes (m_needs_reopen=1) as a result, when ALTER fails and MyISAM/Aria temp table gets reopened, it reads the stale state from the disk. As a fix, MyISAM/Aria tables now *always* write the state to disk on close, *unless* HA_EXTRA_PREPARE_FOR_DROP was done first. And the server now always does HA_EXTRA_PREPARE_FOR_DROP before dropping a temporary table.
This commit is contained in:
@ -190,3 +190,56 @@ NULL NULL
|
|||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
# Cleanup
|
# Cleanup
|
||||||
DROP DATABASE temp_db;
|
DROP DATABASE temp_db;
|
||||||
|
USE test;
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=MyISAM;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
select * from t1;
|
||||||
|
f b
|
||||||
|
NULL 1
|
||||||
|
NULL 2
|
||||||
|
drop table t1;
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=1;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
select * from t1;
|
||||||
|
f b
|
||||||
|
NULL 1
|
||||||
|
NULL 2
|
||||||
|
drop table t1;
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=0 row_format=page;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
select * from t1;
|
||||||
|
f b
|
||||||
|
NULL 1
|
||||||
|
NULL 2
|
||||||
|
drop table t1;
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=0 row_format=dynamic;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
|
||||||
|
check table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 check status OK
|
||||||
|
select * from t1;
|
||||||
|
f b
|
||||||
|
NULL 1
|
||||||
|
NULL 2
|
||||||
|
drop table t1;
|
||||||
|
@ -182,3 +182,43 @@ DROP TABLE t;
|
|||||||
|
|
||||||
--echo # Cleanup
|
--echo # Cleanup
|
||||||
DROP DATABASE temp_db;
|
DROP DATABASE temp_db;
|
||||||
|
USE test;
|
||||||
|
|
||||||
|
#
|
||||||
|
# MDEV-17070 Table corruption or Assertion `table->file->stats.records > 0 || error' or Assertion `!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' failed upon actions on temporary table
|
||||||
|
#
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=MyISAM;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
--error ER_ALTER_OPERATION_NOT_SUPPORTED
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
check table t1;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=1;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
--error ER_ALTER_OPERATION_NOT_SUPPORTED
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
check table t1;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=0 row_format=page;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
--error ER_ALTER_OPERATION_NOT_SUPPORTED
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
check table t1;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
create temporary table t1 (f char(255), b int, index(b)) engine=aria transactional=0 row_format=dynamic;
|
||||||
|
replace into t1 values (null,1),(null,2);
|
||||||
|
alter table t1 add fulltext key(f);
|
||||||
|
--error ER_ALTER_OPERATION_NOT_SUPPORTED
|
||||||
|
alter table t1 change if exists a b int, algorithm=inplace;
|
||||||
|
check table t1;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1;
|
||||||
|
@ -491,6 +491,7 @@ bool THD::close_temporary_tables()
|
|||||||
/* Traverse the table list. */
|
/* Traverse the table list. */
|
||||||
while ((table= share->all_tmp_tables.pop_front()))
|
while ((table= share->all_tmp_tables.pop_front()))
|
||||||
{
|
{
|
||||||
|
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
|
||||||
free_temporary_table(table);
|
free_temporary_table(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,9 +580,7 @@ bool THD::rename_temporary_table(TABLE *table,
|
|||||||
@return false Table was dropped
|
@return false Table was dropped
|
||||||
true Error
|
true Error
|
||||||
*/
|
*/
|
||||||
bool THD::drop_temporary_table(TABLE *table,
|
bool THD::drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table)
|
||||||
bool *is_trans,
|
|
||||||
bool delete_table)
|
|
||||||
{
|
{
|
||||||
DBUG_ENTER("THD::drop_temporary_table");
|
DBUG_ENTER("THD::drop_temporary_table");
|
||||||
|
|
||||||
@ -624,7 +623,8 @@ bool THD::drop_temporary_table(TABLE *table,
|
|||||||
parallel replication
|
parallel replication
|
||||||
*/
|
*/
|
||||||
tab->in_use= this;
|
tab->in_use= this;
|
||||||
|
if (delete_table)
|
||||||
|
tab->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
|
||||||
free_temporary_table(tab);
|
free_temporary_table(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1419,8 +1419,7 @@ bool THD::log_events_and_free_tmp_shares()
|
|||||||
|
|
||||||
@return void
|
@return void
|
||||||
*/
|
*/
|
||||||
void THD::free_tmp_table_share(TMP_TABLE_SHARE *share,
|
void THD::free_tmp_table_share(TMP_TABLE_SHARE *share, bool delete_table)
|
||||||
bool delete_table)
|
|
||||||
{
|
{
|
||||||
DBUG_ENTER("THD::free_tmp_table_share");
|
DBUG_ENTER("THD::free_tmp_table_share");
|
||||||
|
|
||||||
|
@ -449,9 +449,7 @@ my_bool _ma_once_end_block_record(MARIA_SHARE *share)
|
|||||||
if (share->bitmap.file.file >= 0)
|
if (share->bitmap.file.file >= 0)
|
||||||
{
|
{
|
||||||
if (flush_pagecache_blocks(share->pagecache, &share->bitmap.file,
|
if (flush_pagecache_blocks(share->pagecache, &share->bitmap.file,
|
||||||
((share->temporary || share->deleting) ?
|
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
||||||
FLUSH_IGNORE_CHANGED :
|
|
||||||
FLUSH_RELEASE)))
|
|
||||||
res= 1;
|
res= 1;
|
||||||
/*
|
/*
|
||||||
File must be synced as it is going out of the maria_open_list and so
|
File must be synced as it is going out of the maria_open_list and so
|
||||||
|
@ -47,9 +47,7 @@ int maria_close(register MARIA_HA *info)
|
|||||||
a global mutex
|
a global mutex
|
||||||
*/
|
*/
|
||||||
if (flush_pagecache_blocks(share->pagecache, &share->kfile,
|
if (flush_pagecache_blocks(share->pagecache, &share->kfile,
|
||||||
((share->temporary || share->deleting) ?
|
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
||||||
FLUSH_IGNORE_CHANGED :
|
|
||||||
FLUSH_RELEASE)))
|
|
||||||
error= my_errno;
|
error= my_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,23 +111,14 @@ int maria_close(register MARIA_HA *info)
|
|||||||
since the start of the function (very unlikely)
|
since the start of the function (very unlikely)
|
||||||
*/
|
*/
|
||||||
if (flush_pagecache_blocks(share->pagecache, &share->kfile,
|
if (flush_pagecache_blocks(share->pagecache, &share->kfile,
|
||||||
((share->temporary || share->deleting) ?
|
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
||||||
FLUSH_IGNORE_CHANGED :
|
|
||||||
FLUSH_RELEASE)))
|
|
||||||
error= my_errno;
|
error= my_errno;
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
if (share->file_map)
|
if (share->file_map)
|
||||||
_ma_unmap_file(info);
|
_ma_unmap_file(info);
|
||||||
#endif
|
#endif
|
||||||
/*
|
|
||||||
If we are crashed, we can safely flush the current state as it will
|
|
||||||
not change the crashed state.
|
|
||||||
We can NOT write the state in other cases as other threads
|
|
||||||
may be using the file at this point
|
|
||||||
IF using --external-locking, which does not apply to Maria.
|
|
||||||
*/
|
|
||||||
if (((share->changed && share->base.born_transactional) ||
|
if (((share->changed && share->base.born_transactional) ||
|
||||||
maria_is_crashed(info)))
|
maria_is_crashed(info) || (share->temporary && !share->deleting)))
|
||||||
{
|
{
|
||||||
if (save_global_changed)
|
if (save_global_changed)
|
||||||
{
|
{
|
||||||
|
@ -314,6 +314,8 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
|
|||||||
share->state.open_count= 1;
|
share->state.open_count= 1;
|
||||||
share->changed= 1;
|
share->changed= 1;
|
||||||
_ma_mark_file_changed_now(share);
|
_ma_mark_file_changed_now(share);
|
||||||
|
if (share->temporary)
|
||||||
|
break;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case HA_EXTRA_PREPARE_FOR_RENAME:
|
case HA_EXTRA_PREPARE_FOR_RENAME:
|
||||||
{
|
{
|
||||||
|
@ -1374,7 +1374,7 @@ uint _ma_state_info_write(MARIA_SHARE *share, uint pWrite)
|
|||||||
|
|
||||||
if (pWrite & MA_STATE_INFO_WRITE_LOCK)
|
if (pWrite & MA_STATE_INFO_WRITE_LOCK)
|
||||||
mysql_mutex_lock(&share->intern_lock);
|
mysql_mutex_lock(&share->intern_lock);
|
||||||
else if (maria_multi_threaded)
|
else if (maria_multi_threaded && !share->temporary)
|
||||||
mysql_mutex_assert_owner(&share->intern_lock);
|
mysql_mutex_assert_owner(&share->intern_lock);
|
||||||
if (share->base.born_transactional && translog_status == TRANSLOG_OK &&
|
if (share->base.born_transactional && translog_status == TRANSLOG_OK &&
|
||||||
!maria_in_recovery)
|
!maria_in_recovery)
|
||||||
|
@ -65,11 +65,8 @@ int mi_close(register MI_INFO *info)
|
|||||||
DBUG_EXECUTE_IF("crash_before_flush_keys",
|
DBUG_EXECUTE_IF("crash_before_flush_keys",
|
||||||
if (share->kfile >= 0) DBUG_ABORT(););
|
if (share->kfile >= 0) DBUG_ABORT(););
|
||||||
if (share->kfile >= 0 &&
|
if (share->kfile >= 0 &&
|
||||||
flush_key_blocks(share->key_cache, share->kfile,
|
flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map,
|
||||||
&share->dirty_part_map,
|
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
||||||
((share->temporary || share->deleting) ?
|
|
||||||
FLUSH_IGNORE_CHANGED :
|
|
||||||
FLUSH_RELEASE)))
|
|
||||||
error=my_errno;
|
error=my_errno;
|
||||||
if (share->kfile >= 0)
|
if (share->kfile >= 0)
|
||||||
{
|
{
|
||||||
@ -77,10 +74,14 @@ int mi_close(register MI_INFO *info)
|
|||||||
If we are crashed, we can safely flush the current state as it will
|
If we are crashed, we can safely flush the current state as it will
|
||||||
not change the crashed state.
|
not change the crashed state.
|
||||||
We can NOT write the state in other cases as other threads
|
We can NOT write the state in other cases as other threads
|
||||||
may be using the file at this point
|
may be using the file at this point IF using --external-locking.
|
||||||
IF using --external-locking.
|
|
||||||
|
Also, write the state if a temporary table is not being dropped
|
||||||
|
(the server might want to reopen it, and mi_lock_database() only
|
||||||
|
writes the state for non-temp ones)
|
||||||
*/
|
*/
|
||||||
if (share->mode != O_RDONLY && mi_is_crashed(info))
|
if (share->mode != O_RDONLY &&
|
||||||
|
(mi_is_crashed(info) || (share->temporary && !share->deleting)))
|
||||||
mi_state_info_write(share->kfile, &share->state, 1);
|
mi_state_info_write(share->kfile, &share->state, 1);
|
||||||
/* Decrement open count must be last I/O on this file. */
|
/* Decrement open count must be last I/O on this file. */
|
||||||
_mi_decrement_open_count(info);
|
_mi_decrement_open_count(info);
|
||||||
|
@ -260,9 +260,11 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
|
|||||||
break;
|
break;
|
||||||
case HA_EXTRA_PREPARE_FOR_DROP:
|
case HA_EXTRA_PREPARE_FOR_DROP:
|
||||||
/* Signals about intent to delete this table */
|
/* Signals about intent to delete this table */
|
||||||
//share->deleting= TRUE;
|
share->deleting= TRUE;
|
||||||
share->global_changed= FALSE; /* force writing changed flag */
|
share->global_changed= FALSE; /* force writing changed flag */
|
||||||
_mi_mark_file_changed(info);
|
_mi_mark_file_changed(info);
|
||||||
|
if (share->temporary)
|
||||||
|
break;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case HA_EXTRA_PREPARE_FOR_RENAME:
|
case HA_EXTRA_PREPARE_FOR_RENAME:
|
||||||
DBUG_ASSERT(!share->temporary);
|
DBUG_ASSERT(!share->temporary);
|
||||||
|
Reference in New Issue
Block a user