diff --git a/mysql-test/main/ps_mem_leaks.result b/mysql-test/main/ps_mem_leaks.result index fa4f7e36ed967..df1285fb2ee63 100644 --- a/mysql-test/main/ps_mem_leaks.result +++ b/mysql-test/main/ps_mem_leaks.result @@ -367,4 +367,25 @@ a # Clean up DEALLOCATE PREPARE stmt; DROP TABLE t1; +# +# MDEV-39265: Assertion `(mem_root->flags & 4) == 0` failed upon 2nd execution `USING DEFAULT` with sequence +# +CREATE SEQUENCE s; +CREATE TABLE t (a INT DEFAULT(NEXTVAL(s))); +INSERT INTO t VALUES (1), (2); +PREPARE stmt FROM "UPDATE t SET a = ?"; +# Verify no SIGABRT +EXECUTE stmt USING 3; +EXECUTE stmt USING DEFAULT; +# Verify the there are no repeated allocations on statement mem_root +SET @saved_dbug = @@SESSION.debug_dbug; +SET SESSION debug_dbug="+d,assert_no_alloc_internal_tables"; +EXECUTE stmt USING DEFAULT; +EXECUTE stmt USING 4; +EXECUTE stmt USING DEFAULT; +EXECUTE stmt USING DEFAULT; +SET SESSION debug_dbug= @saved_dbug; +DEALLOCATE PREPARE stmt; +DROP SEQUENCE s; +DROP TABLE t; # End of 10.6 tests diff --git a/mysql-test/main/ps_mem_leaks.test b/mysql-test/main/ps_mem_leaks.test index 3374519908fda..ef67f13659c34 100644 --- a/mysql-test/main/ps_mem_leaks.test +++ b/mysql-test/main/ps_mem_leaks.test @@ -364,4 +364,34 @@ EXECUTE stmt USING 4; DEALLOCATE PREPARE stmt; DROP TABLE t1; +--echo # +--echo # MDEV-39265: Assertion `(mem_root->flags & 4) == 0` failed upon 2nd execution `USING DEFAULT` with sequence +--echo # + +CREATE SEQUENCE s; +CREATE TABLE t (a INT DEFAULT(NEXTVAL(s))); +INSERT INTO t VALUES (1), (2); + +PREPARE stmt FROM "UPDATE t SET a = ?"; + +--echo # Verify no SIGABRT +EXECUTE stmt USING 3; +EXECUTE stmt USING DEFAULT; + +--echo # Verify the there are no repeated allocations on statement mem_root + +SET @saved_dbug = @@SESSION.debug_dbug; +SET SESSION debug_dbug="+d,assert_no_alloc_internal_tables"; + +EXECUTE stmt USING DEFAULT; +EXECUTE stmt USING 4; +EXECUTE stmt USING DEFAULT; +EXECUTE stmt USING DEFAULT; + +SET SESSION debug_dbug= @saved_dbug; + +DEALLOCATE PREPARE stmt; +DROP SEQUENCE s; +DROP TABLE t; + --echo # End of 10.6 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 79d7d7d32324a..e2c6015bcec88 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4762,7 +4762,29 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, continue; } + /* + Debug hook: Verify we only allocate once per internal table. + */ + DBUG_EXECUTE_IF("assert_no_alloc_internal_tables", { DBUG_ASSERT(0); }); + + /* + When a prepared statement uses DEFAULT (like sequence tables) in its + second or further execution AND if the table is not already on statement's + mem_root, temporarily allow allocating on statement mem_root. + */ +#ifdef PROTECT_STATEMENT_MEMROOT + const bool read_only_mem_root= (thd->mem_root->flags & ROOT_FLAG_READ_ONLY); + if (read_only_mem_root) + thd->mem_root->flags&= ~ROOT_FLAG_READ_ONLY; +#endif + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + +#ifdef PROTECT_STATEMENT_MEMROOT + if (read_only_mem_root) + thd->mem_root->flags|= ROOT_FLAG_READ_ONLY; +#endif + if (!tl) DBUG_RETURN(TRUE); tl->init_one_table_for_prelocking(&tables->db,