Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions mysql-test/r/bug112704.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# CTE clone handler mismatch when TempTable falls back to InnoDB
SET @@internal_tmp_mem_storage_engine = 'TempTable';
# Case 1: Non-recursive CTE — handler mismatch fix + status counter check
FLUSH STATUS;
SET DEBUG='+d,temptable_create_return_full';
WITH v AS (VALUES ROW(1), ROW(2)) SELECT * FROM v AS v1 JOIN v AS v2 ORDER BY v1.column_0, v2.column_0;
column_0 column_0
1 1
1 2
2 1
2 2
SHOW STATUS LIKE 'Created_tmp_disk_tables';
Variable_name Value
Created_tmp_disk_tables 1
# Case 2: Recursive CTE — recursive ref clone also uses the shortcut path
FLUSH STATUS;
WITH RECURSIVE r AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM r WHERE n < 5
)
SELECT * FROM r ORDER BY n;
n
1
2
3
4
5
SET DEBUG='-d,temptable_create_return_full';
SHOW STATUS LIKE 'Created_tmp_disk_tables';
Variable_name Value
Created_tmp_disk_tables 1
FLUSH STATUS;
SET @@internal_tmp_mem_storage_engine = DEFAULT;
24 changes: 24 additions & 0 deletions mysql-test/t/bug112704.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--source include/have_debug.inc

--echo # CTE clone handler mismatch when TempTable falls back to InnoDB

SET @@internal_tmp_mem_storage_engine = 'TempTable';

--echo # Case 1: Non-recursive CTE — handler mismatch fix + status counter check
FLUSH STATUS;
SET DEBUG='+d,temptable_create_return_full';
WITH v AS (VALUES ROW(1), ROW(2)) SELECT * FROM v AS v1 JOIN v AS v2 ORDER BY v1.column_0, v2.column_0;
SHOW STATUS LIKE 'Created_tmp_disk_tables';

--echo # Case 2: Recursive CTE — recursive ref clone also uses the shortcut path
FLUSH STATUS;
WITH RECURSIVE r AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM r WHERE n < 5
)
SELECT * FROM r ORDER BY n;
SET DEBUG='-d,temptable_create_return_full';
SHOW STATUS LIKE 'Created_tmp_disk_tables';
FLUSH STATUS;
SET @@internal_tmp_mem_storage_engine = DEFAULT;
16 changes: 16 additions & 0 deletions sql/iterators/basic_row_iterators.cc
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,22 @@ bool FollowTailIterator::DoInit() {
// connect to it on first run here.
assert(table()->in_use == nullptr || table()->in_use == thd());
table()->in_use = thd();
// The CTE's primary materialization may have fallen back TempTable ->
// InnoDB, updating share->db_plugin. This clone's handler (set at
// preparation time from share->db_type()) may now be stale. Detect and
// fix before opening.
if (table()->file->ht != table()->s->db_type()) {
::destroy_at(table()->file);
table()->file = get_new_handler(table()->s, false,
table()->s->alloc_for_tmp_file_handler,
table()->s->db_type());
if (table()->file == nullptr) return true;
table()->file->change_table_ptr(table(), table()->s);
if (table()->file->set_ha_share_ref(&table()->s->ha_share)) {
::destroy_at(table()->file);
return true;
}
}
if (open_tmp_table(table())) {
return true;
}
Expand Down
17 changes: 17 additions & 0 deletions sql/sql_derived.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,23 @@ bool Table_ref::create_materialized_table(THD *thd) {
if (t->is_created()) {
assert(table->in_use == nullptr || table->in_use == thd);
table->in_use = thd;
// CTE clones share a TABLE_SHARE but have separate handlers (set at
// preparation time). If the first clone triggered TempTable->InnoDB
// fallback, this clone's handler still points to TempTable. Detect the
// mismatch and replace with the correct engine to avoid opening the
// wrong handler (which would fail with "table doesn't exist").
if (table->file->ht != t->file->ht) {
::destroy_at(table->file);
table->file = get_new_handler(table->s, false,
table->s->alloc_for_tmp_file_handler,
t->file->ht);
if (table->file == nullptr) return true;
table->file->change_table_ptr(table, table->s);
if (table->file->set_ha_share_ref(&table->s->ha_share)) {
::destroy_at(table->file);
return true;
}
}
if (open_tmp_table(table)) return true; /* purecov: inspected */
break;
}
Expand Down
13 changes: 12 additions & 1 deletion sql/sql_tmp_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2320,6 +2320,13 @@ static bool create_tmp_table_with_fallback(THD *thd, TABLE *table) {
table->file->create(share->table_name.str, table, &create_info, nullptr);
if (error == HA_ERR_RECORD_FILE_FULL &&
table->s->db_type() == temptable_hton) {
// Update share->db_plugin so db_type() reflects InnoDB instead of stale
// TempTable. Other clones share this TABLE_SHARE and depend on db_type()
// to determine the actual storage engine.
plugin_unlock(nullptr, share->db_plugin);
share->db_plugin = ha_lock_engine(nullptr, innodb_hton);
create_info.db_type = innodb_hton;
::destroy_at(table->file);
table->file = get_new_handler(
table->s, false, share->alloc_for_tmp_file_handler, innodb_hton);
error = table->file->create(share->table_name.str, table, &create_info,
Expand All @@ -2331,7 +2338,11 @@ static bool create_tmp_table_with_fallback(THD *thd, TABLE *table) {
table->db_stat = 0;
return true;
} else {
if (table->s->db_type() != temptable_hton) {
// For non-fallback cases (TempTable succeeded, or initial InnoDB request):
// increment counter if table is on disk (not in-memory like
// TempTable/MEMORY).
if (table->s->db_type() != temptable_hton &&
table->s->db_type() != heap_hton) {
thd->inc_status_created_tmp_disk_tables();
}
return false;
Expand Down