Skip to content
/ server Public

Commit 24da629

Browse files
committed
squash! 7b54e04
MDEV-36501 EITS data is lost after failed attempt to CREATE OR REPLACE table MDEV-36493 Atomic CREATE OR REPLACE ... SELECT blocks InnoDB purge Other things: - EITS data is preserved if create or replace fails if drop_before_create_or_replace=OFF. If ON, then create or replace will drop EITS before the drop of the original table (as before).
1 parent 9699ada commit 24da629

File tree

5 files changed

+130
-10
lines changed

5 files changed

+130
-10
lines changed

mysql-test/main/create_or_replace.result

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,5 +1173,65 @@ SELECT 1;
11731173
1
11741174
DROP TABLE t1;
11751175
#
1176+
# MDEV-36501 EITS data is lost after failed attempt to
1177+
# CREATE OR REPLACE table
1178+
#
1179+
create table t1 (a int primary key);
1180+
insert into t1 values (1),(2);
1181+
analyze table t1 persistent for all;
1182+
Table Op Msg_type Msg_text
1183+
test.t1 analyze status Engine-independent statistics collected
1184+
test.t1 analyze status OK
1185+
select count(*) from mysql.table_stats where table_name = 't1';
1186+
count(*)
1187+
1
1188+
select count(*) from mysql.index_stats where table_name = 't1';
1189+
count(*)
1190+
1
1191+
select count(*) from mysql.column_stats where table_name = 't1';
1192+
count(*)
1193+
1
1194+
create or replace table t1 (b int primary key) as select 1 as b union all select 1;
1195+
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
1196+
select count(*) from mysql.table_stats where table_name = 't1';
1197+
count(*)
1198+
1
1199+
select count(*) from mysql.index_stats where table_name = 't1';
1200+
count(*)
1201+
1
1202+
select count(*) from mysql.column_stats where table_name = 't1';
1203+
count(*)
1204+
1
1205+
create or replace table t1 (b int primary key);
1206+
select count(*) from mysql.table_stats where table_name = 't1';
1207+
count(*)
1208+
0
1209+
select count(*) from mysql.index_stats where table_name = 't1';
1210+
count(*)
1211+
0
1212+
select count(*) from mysql.column_stats where table_name = 't1';
1213+
count(*)
1214+
0
1215+
analyze table t1 persistent for all;
1216+
Table Op Msg_type Msg_text
1217+
test.t1 analyze status Engine-independent statistics collected
1218+
test.t1 analyze status Table is already up to date
1219+
set @@session.drop_before_create_or_replace='ON';
1220+
create or replace table t1 (c int primary key) as select 1 as c union all select 1;
1221+
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
1222+
select count(*) from mysql.table_stats where table_name = 't1';
1223+
count(*)
1224+
0
1225+
select count(*) from mysql.index_stats where table_name = 't1';
1226+
count(*)
1227+
0
1228+
select count(*) from mysql.column_stats where table_name = 't1';
1229+
count(*)
1230+
0
1231+
drop table if exists t1;
1232+
Warnings:
1233+
Note 1051 Unknown table 'test.t1'
1234+
set @@session.drop_before_create_or_replace=default;
1235+
#
11761236
# End of 12.3 tests
11771237
#

mysql-test/main/create_or_replace.test

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,39 @@ CREATE OR REPLACE TABLE t1 (b INT) ENGINE=aria;
908908
SELECT 1;
909909
DROP TABLE t1;
910910

911+
--echo #
912+
--echo # MDEV-36501 EITS data is lost after failed attempt to
913+
--echo # CREATE OR REPLACE table
914+
--echo #
915+
create table t1 (a int primary key);
916+
insert into t1 values (1),(2);
917+
analyze table t1 persistent for all;
918+
select count(*) from mysql.table_stats where table_name = 't1';
919+
select count(*) from mysql.index_stats where table_name = 't1';
920+
select count(*) from mysql.column_stats where table_name = 't1';
921+
922+
--error ER_DUP_ENTRY
923+
create or replace table t1 (b int primary key) as select 1 as b union all select 1;
924+
select count(*) from mysql.table_stats where table_name = 't1';
925+
select count(*) from mysql.index_stats where table_name = 't1';
926+
select count(*) from mysql.column_stats where table_name = 't1';
927+
928+
create or replace table t1 (b int primary key);
929+
select count(*) from mysql.table_stats where table_name = 't1';
930+
select count(*) from mysql.index_stats where table_name = 't1';
931+
select count(*) from mysql.column_stats where table_name = 't1';
932+
analyze table t1 persistent for all;
933+
934+
set @@session.drop_before_create_or_replace='ON';
935+
936+
--error ER_DUP_ENTRY
937+
create or replace table t1 (c int primary key) as select 1 as c union all select 1;
938+
select count(*) from mysql.table_stats where table_name = 't1';
939+
select count(*) from mysql.index_stats where table_name = 't1';
940+
select count(*) from mysql.column_stats where table_name = 't1';
941+
drop table if exists t1;
942+
set @@session.drop_before_create_or_replace=default;
943+
911944
--echo #
912945
--echo # End of 12.3 tests
913946
--echo #

sql/ddl_log.cc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,9 +1734,14 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
17341734
break;
17351735
}
17361736
}
1737+
if (ddl_log_entry->extra_name.str)
1738+
delete_statistics_for_table(thd, &db, &ddl_log_entry->extra_name);
17371739
break;
17381740
case DDL_DROP_PHASE_RESET:
1739-
/* We have already logged all previous drop's. Clear the query */
1741+
/*
1742+
We only come here if DDL_DROP_PHASE_BINLOG fails.
1743+
We have already logged all previous drop's. Clear the query
1744+
*/
17401745
recovery_state.drop_table.length(recovery_state.drop_table_init_length);
17411746
recovery_state.drop_view.length(recovery_state.drop_view_init_length);
17421747
break;
@@ -3343,6 +3348,7 @@ bool ddl_log_drop_view_init(DDL_LOG_STATE *ddl_state,
33433348
@param path Table filepath without extension
33443349
@param db DB name
33453350
@param table Table name
3351+
@param orig_table Original table name (in case of create or replace)
33463352
@param flags DDL_LOG_FLAG_FROM_IS_TMP or DDL_LOG_FLAG_TO_IS_TMP
33473353
*/
33483354

@@ -3353,6 +3359,7 @@ static bool ddl_log_drop(DDL_LOG_STATE *ddl_state,
33533359
const LEX_CSTRING *path,
33543360
const LEX_CSTRING *db,
33553361
const LEX_CSTRING *table,
3362+
const LEX_CSTRING *orig_table,
33563363
uint16 flags)
33573364
{
33583365
DDL_LOG_ENTRY ddl_log_entry;
@@ -3371,6 +3378,8 @@ static bool ddl_log_drop(DDL_LOG_STATE *ddl_state,
33713378
ddl_log_entry.db= *const_cast<LEX_CSTRING*>(db);
33723379
ddl_log_entry.name= *const_cast<LEX_CSTRING*>(table);
33733380
ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(path);
3381+
if (orig_table)
3382+
ddl_log_entry.extra_name= *const_cast<LEX_CSTRING*>(orig_table);
33743383
ddl_log_entry.phase= (uchar) phase;
33753384
ddl_log_entry.flags= flags;
33763385

@@ -3402,6 +3411,7 @@ static bool ddl_log_drop(DDL_LOG_STATE *ddl_state,
34023411
@param path Table filepath without extension
34033412
@param db DB name
34043413
@param table Table name
3414+
@param orig_table Original table name (in case of create or replace)
34053415
@param flags DDL_LOG_FLAG_FROM_IS_TMP or DDL_LOG_FLAG_TO_IS_TMP
34063416
*/
34073417

@@ -3410,12 +3420,13 @@ bool ddl_log_drop_table(DDL_LOG_STATE *ddl_state,
34103420
const LEX_CSTRING *path,
34113421
const LEX_CSTRING *db,
34123422
const LEX_CSTRING *table,
3423+
const LEX_CSTRING *orig_table,
34133424
uint16 flags)
34143425
{
34153426
DBUG_ENTER("ddl_log_drop_table");
34163427
DBUG_RETURN(ddl_log_drop(ddl_state,
34173428
DDL_LOG_DROP_TABLE_ACTION, DDL_DROP_PHASE_TABLE,
3418-
hton, path, db, table, flags));
3429+
hton, path, db, table, orig_table, flags));
34193430
}
34203431

34213432

@@ -3434,7 +3445,8 @@ bool ddl_log_drop_view(DDL_LOG_STATE *ddl_state,
34343445
DBUG_ENTER("ddl_log_drop_view");
34353446
DBUG_RETURN(ddl_log_drop(ddl_state,
34363447
DDL_LOG_DROP_VIEW_ACTION, 0,
3437-
(handlerton*) 0, path, db, table, 0));
3448+
(handlerton*) 0, path, db, table, (LEX_CSTRING*) 0,
3449+
0));
34383450
}
34393451

34403452

sql/ddl_log.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ bool ddl_log_drop_table(DDL_LOG_STATE *ddl_state,
325325
const LEX_CSTRING *path,
326326
const LEX_CSTRING *db,
327327
const LEX_CSTRING *table,
328+
const LEX_CSTRING *orig_table,
328329
uint16 flags);
329330
bool ddl_log_drop_view(DDL_LOG_STATE *ddl_state,
330331
const LEX_CSTRING *path,

sql/sql_table.cc

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,9 +1351,9 @@ static uint32 get_comment(THD *thd, uint32 comment_pos,
13511351
Used by create_or_replace().
13521352
*/
13531353

1354-
int ddl_log_drop_tmp_table(THD *thd, DDL_LOG_STATE *ddl_log_state,
1355-
TABLE_LIST *table,
1356-
Table_specification_st *create_info)
1354+
static int ddl_log_drop_tmp_table(THD *thd, DDL_LOG_STATE *ddl_log_state,
1355+
TABLE_LIST *table,
1356+
Table_specification_st *create_info)
13571357
{
13581358
bool res= 0;
13591359
LEX_CSTRING comment= {"",0};
@@ -1377,6 +1377,9 @@ int ddl_log_drop_tmp_table(THD *thd, DDL_LOG_STATE *ddl_log_state,
13771377
else
13781378
res= ddl_log_drop_table(ddl_log_state, create_info->org_hton,
13791379
&cpath, db, &create_info->tmp_name,
1380+
(!create_info->tmp_table() ?
1381+
&create_info->alias /* original name */ :
1382+
(LEX_CSTRING*) 0),
13801383
DDL_LOG_FLAG_FROM_IS_TMP);
13811384
return res;
13821385
}
@@ -1777,7 +1780,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables,
17771780
res= ddl_log_drop_view(ddl_log_state, &cpath, &db, &table_name);
17781781
else
17791782
res= ddl_log_drop_table(ddl_log_state, hton, &cpath, &db, &table_name,
1780-
0);
1783+
(LEX_CSTRING*) 0, 0);
17811784
if (res)
17821785
goto err;
17831786

@@ -1865,7 +1868,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables,
18651868
int ferror= 0;
18661869
DBUG_ASSERT(!was_view);
18671870

1868-
if (ddl_log_drop_table(ddl_log_state, 0, &cpath, &db, &table_name, 0))
1871+
if (ddl_log_drop_table(ddl_log_state, 0, &cpath, &db, &table_name,
1872+
(LEX_CSTRING *) 0, 0))
18691873
{
18701874
error= -1;
18711875
goto err;
@@ -4724,6 +4728,7 @@ finalize_create_table(THD *thd, TABLE_LIST *orig_table, const char *query,
47244728
}
47254729
else if (table_was_deleted)
47264730
ddl_log_update_xid(ddl_log_state_rm, thd->binlog_xid);
4731+
47274732
ddl_log_update_xid(ddl_log_state_create, thd->binlog_xid);
47284733

47294734
debug_crash_here("ddl_log_create_before_binlog");
@@ -5132,8 +5137,6 @@ int create_table_impl(THD *thd,
51325137
goto err;
51335138
}
51345139

5135-
(void) delete_statistics_for_table(thd, &db, &table_name);
5136-
51375140
if (use_drop_table)
51385141
{
51395142
/*
@@ -5148,6 +5151,8 @@ int create_table_impl(THD *thd,
51485151
save_unsafe_rollback_flags;
51495152
}
51505153

5154+
(void) delete_statistics_for_table(thd, &db, &table_name);
5155+
51515156
/* Remove normal table without logging. Keep tables locked */
51525157
if (mysql_rm_table_no_locks(thd, &table_list, &thd->db,
51535158
ddl_log_state_rm,
@@ -5178,6 +5183,7 @@ int create_table_impl(THD *thd,
51785183
/* Rename the conflicting table to a temporary table name */
51795184
bool force_if_exists= 0;
51805185
rename_param param;
5186+
MDL_request mdl_request;
51815187

51825188
create_info->org_hton= db_type;
51835189
if (!(create_info->tmp_name.str= thd->alloc(64)))
@@ -5213,6 +5219,14 @@ int create_table_impl(THD *thd,
52135219
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db.str,
52145220
table_name.str,
52155221
MDL_EXCLUSIVE));
5222+
/*
5223+
Create an exclusive lock on the temporary table name.
5224+
Needed by InnoDB to not block purge.
5225+
*/
5226+
MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE,
5227+
db.str, create_info->tmp_name.str,
5228+
MDL_EXCLUSIVE, MDL_TRANSACTION);
5229+
thd->mdl_context.acquire_lock(&mdl_request, 0);
52165230

52175231
/*
52185232
We have to reset partition_info from the CREATE TABLE as

0 commit comments

Comments
 (0)