2016-06-10 22:19:59 +02:00
|
|
|
/*
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
Copyright (c) 2016, 2021, MariaDB Corporation.
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
All methods pertaining to temporary tables.
|
|
|
|
*/
|
|
|
|
|
2017-06-18 05:42:16 +02:00
|
|
|
#include "mariadb.h"
|
2016-06-10 22:19:59 +02:00
|
|
|
#include "sql_acl.h" /* TMP_TABLE_ACLS */
|
|
|
|
#include "sql_base.h" /* tdc_create_key */
|
|
|
|
#include "lock.h" /* mysql_lock_remove */
|
|
|
|
#include "log_event.h" /* Query_log_event */
|
|
|
|
#include "sql_show.h" /* append_identifier */
|
|
|
|
#include "sql_handler.h" /* mysql_ha_rm_temporary_tables */
|
|
|
|
#include "rpl_rli.h" /* rpl_group_info */
|
|
|
|
|
|
|
|
#define IS_USER_TABLE(A) ((A->tmp_table == TRANSACTIONAL_TMP_TABLE) || \
|
|
|
|
(A->tmp_table == NON_TRANSACTIONAL_TMP_TABLE))
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check whether temporary tables exist. The decision is made based on the
|
|
|
|
existence of TMP_TABLE_SHAREs in Open_tables_state::temporary_tables list.
|
|
|
|
|
|
|
|
@return false Temporary tables exist
|
|
|
|
true No temporary table exist
|
|
|
|
*/
|
|
|
|
bool THD::has_thd_temporary_tables()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::has_thd_temporary_tables");
|
|
|
|
bool result= (temporary_tables && !temporary_tables->is_empty());
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a temporary table, open it and return the TABLE handle.
|
|
|
|
|
|
|
|
@param frm [IN] Binary frm image
|
|
|
|
@param path [IN] File path (without extension)
|
|
|
|
@param db [IN] Schema name
|
|
|
|
@param table_name [IN] Table name
|
|
|
|
|
|
|
|
@return Success A pointer to table object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
2019-02-06 08:41:36 +01:00
|
|
|
TABLE *THD::create_and_open_tmp_table(LEX_CUSTRING *frm,
|
2016-06-10 22:19:59 +02:00
|
|
|
const char *path,
|
|
|
|
const char *db,
|
|
|
|
const char *table_name,
|
2017-12-22 13:56:09 +01:00
|
|
|
bool open_internal_tables)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::create_and_open_tmp_table");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *table= NULL;
|
|
|
|
|
2019-02-06 08:41:36 +01:00
|
|
|
if ((share= create_temporary_table(frm, path, db, table_name)))
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
MDEV-10139 Support for SEQUENCE objects
Working features:
CREATE OR REPLACE [TEMPORARY] SEQUENCE [IF NOT EXISTS] name
[ INCREMENT [ BY | = ] increment ]
[ MINVALUE [=] minvalue | NO MINVALUE ]
[ MAXVALUE [=] maxvalue | NO MAXVALUE ]
[ START [ WITH | = ] start ] [ CACHE [=] cache ] [ [ NO ] CYCLE ]
ENGINE=xxx COMMENT=".."
SELECT NEXT VALUE FOR sequence_name;
SELECT NEXTVAL(sequence_name);
SELECT PREVIOUS VALUE FOR sequence_name;
SELECT LASTVAL(sequence_name);
SHOW CREATE SEQUENCE sequence_name;
SHOW CREATE TABLE sequence_name;
CREATE TABLE sequence-structure ... SEQUENCE=1
ALTER TABLE sequence RENAME TO sequence2;
RENAME TABLE sequence TO sequence2;
DROP [TEMPORARY] SEQUENCE [IF EXISTS] sequence_names
Missing features
- SETVAL(value,sequence_name), to be used with replication.
- Check replication, including checking that sequence tables are marked
not transactional.
- Check that a commit happens for NEXT VALUE that changes table data (may
already work)
- ALTER SEQUENCE. ANSI SQL version of setval.
- Share identical sequence entries to not add things twice to table list.
- testing insert/delete/update/truncate/load data
- Run and fix Alibaba sequence tests (part of mysql-test/suite/sql_sequence)
- Write documentation for NEXT VALUE / PREVIOUS_VALUE
- NEXTVAL in DEFAULT
- Ensure that NEXTVAL in DEFAULT uses database from base table
- Two NEXTVAL for same row should give same answer.
- Oracle syntax sequence_table.nextval, without any FOR or FROM.
- Sequence tables are treated as 'not read constant tables' by SELECT; Would
be better if we would have a separate list for sequence tables so that
select doesn't know about them, except if refereed to with FROM.
Other things done:
- Improved output for safemalloc backtrack
- frm_type_enum changed to Table_type
- Removed lex->is_view and replaced with lex->table_type. This allows
use to more easy check if item is view, sequence or table.
- Added table flag HA_CAN_TABLES_WITHOUT_ROLLBACK, needed for handlers
that want's to support sequences
- Added handler calls:
- engine_name(), to simplify getting engine name for partition and sequences
- update_first_row(), to be able to do efficient sequence implementations.
- Made binlog_log_row() global to be able to call it from ha_sequence.cc
- Added handler variable: row_already_logged, to be able to flag that the
changed row is already logging to replication log.
- Added CF_DB_CHANGE and CF_SCHEMA_CHANGE flags to simplify
deny_updates_if_read_only_option()
- Added sp_add_cfetch() to avoid new conflicts in sql_yacc.yy
- Moved code for add_table_options() out from sql_show.cc::show_create_table()
- Added String::append_longlong() and used it in sql_show.cc to simplify code.
- Added extra option to dd_frm_type() and ha_table_exists to indicate if
the table is a sequence. Needed by DROP SQUENCE to not drop a table.
2017-03-25 22:36:56 +01:00
|
|
|
open_options|= HA_OPEN_FOR_CREATE;
|
2019-02-05 15:41:33 +01:00
|
|
|
table= open_temporary_table(share, table_name);
|
MDEV-10139 Support for SEQUENCE objects
Working features:
CREATE OR REPLACE [TEMPORARY] SEQUENCE [IF NOT EXISTS] name
[ INCREMENT [ BY | = ] increment ]
[ MINVALUE [=] minvalue | NO MINVALUE ]
[ MAXVALUE [=] maxvalue | NO MAXVALUE ]
[ START [ WITH | = ] start ] [ CACHE [=] cache ] [ [ NO ] CYCLE ]
ENGINE=xxx COMMENT=".."
SELECT NEXT VALUE FOR sequence_name;
SELECT NEXTVAL(sequence_name);
SELECT PREVIOUS VALUE FOR sequence_name;
SELECT LASTVAL(sequence_name);
SHOW CREATE SEQUENCE sequence_name;
SHOW CREATE TABLE sequence_name;
CREATE TABLE sequence-structure ... SEQUENCE=1
ALTER TABLE sequence RENAME TO sequence2;
RENAME TABLE sequence TO sequence2;
DROP [TEMPORARY] SEQUENCE [IF EXISTS] sequence_names
Missing features
- SETVAL(value,sequence_name), to be used with replication.
- Check replication, including checking that sequence tables are marked
not transactional.
- Check that a commit happens for NEXT VALUE that changes table data (may
already work)
- ALTER SEQUENCE. ANSI SQL version of setval.
- Share identical sequence entries to not add things twice to table list.
- testing insert/delete/update/truncate/load data
- Run and fix Alibaba sequence tests (part of mysql-test/suite/sql_sequence)
- Write documentation for NEXT VALUE / PREVIOUS_VALUE
- NEXTVAL in DEFAULT
- Ensure that NEXTVAL in DEFAULT uses database from base table
- Two NEXTVAL for same row should give same answer.
- Oracle syntax sequence_table.nextval, without any FOR or FROM.
- Sequence tables are treated as 'not read constant tables' by SELECT; Would
be better if we would have a separate list for sequence tables so that
select doesn't know about them, except if refereed to with FROM.
Other things done:
- Improved output for safemalloc backtrack
- frm_type_enum changed to Table_type
- Removed lex->is_view and replaced with lex->table_type. This allows
use to more easy check if item is view, sequence or table.
- Added table flag HA_CAN_TABLES_WITHOUT_ROLLBACK, needed for handlers
that want's to support sequences
- Added handler calls:
- engine_name(), to simplify getting engine name for partition and sequences
- update_first_row(), to be able to do efficient sequence implementations.
- Made binlog_log_row() global to be able to call it from ha_sequence.cc
- Added handler variable: row_already_logged, to be able to flag that the
changed row is already logging to replication log.
- Added CF_DB_CHANGE and CF_SCHEMA_CHANGE flags to simplify
deny_updates_if_read_only_option()
- Added sp_add_cfetch() to avoid new conflicts in sql_yacc.yy
- Moved code for add_table_options() out from sql_show.cc::show_create_table()
- Added String::append_longlong() and used it in sql_show.cc to simplify code.
- Added extra option to dd_frm_type() and ha_table_exists to indicate if
the table is a sequence. Needed by DROP SQUENCE to not drop a table.
2017-03-25 22:36:56 +01:00
|
|
|
open_options&= ~HA_OPEN_FOR_CREATE;
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Failed to open a temporary table instance. As we are not passing
|
|
|
|
the created TMP_TABLE_SHARE to the caller, we must remove it from
|
|
|
|
the list and free it here.
|
|
|
|
*/
|
|
|
|
if (!table)
|
|
|
|
{
|
|
|
|
/* Remove the TABLE_SHARE from the list of temporary tables. */
|
|
|
|
temporary_tables->remove(share);
|
|
|
|
|
|
|
|
/* Free the TMP_TABLE_SHARE. */
|
|
|
|
free_tmp_table_share(share, false);
|
2017-12-22 13:56:09 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open any related tables */
|
|
|
|
if (open_internal_tables && table->internal_tables &&
|
2019-02-05 15:41:33 +01:00
|
|
|
open_and_lock_internal_tables(table, true))
|
2017-12-22 13:56:09 +01:00
|
|
|
{
|
|
|
|
drop_temporary_table(table, NULL, false);
|
|
|
|
DBUG_RETURN(0);
|
2016-06-10 22:19:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check whether an open table with db/table name is in use.
|
|
|
|
|
|
|
|
@param db [IN] Database name
|
|
|
|
@param table_name [IN] Table name
|
2018-05-24 17:56:33 +02:00
|
|
|
@param state [IN] State of temp table to open
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
@return Success Pointer to first used table instance.
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TABLE *THD::find_temporary_table(const char *db,
|
2018-05-24 17:56:33 +02:00
|
|
|
const char *table_name,
|
|
|
|
Temporary_table_state state)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_temporary_table");
|
|
|
|
|
|
|
|
TABLE *table;
|
|
|
|
char key[MAX_DBKEY_LENGTH];
|
|
|
|
uint key_length;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
if (!has_temporary_tables())
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
key_length= create_tmp_table_def_key(key, db, table_name);
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
2018-05-24 17:56:33 +02:00
|
|
|
table= find_temporary_table(key, key_length, state);
|
2016-06-10 22:19:59 +02:00
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check whether an open table specified in TABLE_LIST is in use.
|
|
|
|
|
|
|
|
@return tl [IN] TABLE_LIST
|
|
|
|
|
|
|
|
@return Success Pointer to first used table instance.
|
|
|
|
Failure NULL
|
|
|
|
*/
|
2018-05-24 17:56:33 +02:00
|
|
|
TABLE *THD::find_temporary_table(const TABLE_LIST *tl,
|
|
|
|
Temporary_table_state state)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_temporary_table");
|
2018-05-24 17:56:33 +02:00
|
|
|
TABLE *table= find_temporary_table(tl->get_db_name(), tl->get_table_name(),
|
|
|
|
state);
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check whether a temporary table exists with the specified key.
|
|
|
|
The key, in this case, is not the usual key used for temporary tables.
|
|
|
|
It does not contain server_id & pseudo_thread_id. This function is
|
|
|
|
essentially used use to check whether there is any temporary table
|
|
|
|
which _shadows_ a base table.
|
|
|
|
(see: Query_cache::send_result_to_client())
|
|
|
|
|
|
|
|
@return Success A pointer to table share object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TMP_TABLE_SHARE *THD::find_tmp_table_share_w_base_key(const char *key,
|
|
|
|
uint key_length)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_tmp_table_share_w_base_key");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TMP_TABLE_SHARE *result= NULL;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
if (!has_temporary_tables())
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
All_tmp_tables_list::Iterator it(*temporary_tables);
|
|
|
|
while ((share= it++))
|
|
|
|
{
|
|
|
|
if ((share->table_cache_key.length - TMP_TABLE_KEY_EXTRA) == key_length
|
|
|
|
&& !memcmp(share->table_cache_key.str, key, key_length))
|
|
|
|
{
|
|
|
|
result= share;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Lookup the TMP_TABLE_SHARE using the given db/table_name.The server_id and
|
|
|
|
pseudo_thread_id used to generate table definition key is taken from THD
|
|
|
|
(see create_tmp_table_def_key()). Return NULL is none found.
|
|
|
|
|
|
|
|
@return Success A pointer to table share object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *db,
|
|
|
|
const char *table_name)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_tmp_table_share");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
char key[MAX_DBKEY_LENGTH];
|
|
|
|
uint key_length;
|
|
|
|
|
|
|
|
key_length= create_tmp_table_def_key(key, db, table_name);
|
|
|
|
share= find_tmp_table_share(key, key_length);
|
|
|
|
|
|
|
|
DBUG_RETURN(share);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Lookup TMP_TABLE_SHARE using the specified TABLE_LIST element.
|
|
|
|
Return NULL is none found.
|
|
|
|
|
|
|
|
@param tl [IN] Table
|
|
|
|
|
|
|
|
@return Success A pointer to table share object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const TABLE_LIST *tl)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_tmp_table_share");
|
|
|
|
TMP_TABLE_SHARE *share= find_tmp_table_share(tl->get_db_name(),
|
|
|
|
tl->get_table_name());
|
|
|
|
DBUG_RETURN(share);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Lookup TMP_TABLE_SHARE using the specified table definition key.
|
|
|
|
Return NULL is none found.
|
|
|
|
|
|
|
|
@return Success A pointer to table share object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
2018-02-06 13:55:58 +01:00
|
|
|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, size_t key_length)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_tmp_table_share");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TMP_TABLE_SHARE *result= NULL;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
if (!has_temporary_tables())
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
All_tmp_tables_list::Iterator it(*temporary_tables);
|
|
|
|
while ((share= it++))
|
|
|
|
{
|
|
|
|
if (share->table_cache_key.length == key_length &&
|
|
|
|
!(memcmp(share->table_cache_key.str, key, key_length)))
|
|
|
|
{
|
|
|
|
result= share;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find a temporary table specified by TABLE_LIST instance in the open table
|
|
|
|
list and prepare its TABLE instance for use. If
|
|
|
|
|
|
|
|
This function tries to resolve this table in the list of temporary tables
|
|
|
|
of this thread. Temporary tables are thread-local and "shadow" base
|
|
|
|
tables with the same name.
|
|
|
|
|
|
|
|
@note In most cases one should use THD::open_tables() instead
|
|
|
|
of this call.
|
|
|
|
|
|
|
|
@note One should finalize process of opening temporary table for table
|
|
|
|
list element by calling open_and_process_table(). This function
|
|
|
|
is responsible for table version checking and handling of merge
|
|
|
|
tables.
|
|
|
|
|
|
|
|
@note We used to check global_read_lock before opening temporary tables.
|
|
|
|
However, that limitation was artificial and is removed now.
|
|
|
|
|
|
|
|
@param tl [IN] TABLE_LIST
|
|
|
|
|
|
|
|
@return Error status.
|
|
|
|
@retval false On success. If a temporary table exists
|
|
|
|
for the given key, tl->table is set.
|
|
|
|
@retval true On error. my_error() has been called.
|
|
|
|
*/
|
|
|
|
bool THD::open_temporary_table(TABLE_LIST *tl)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::open_temporary_table");
|
2018-01-07 17:03:44 +01:00
|
|
|
DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db.str, tl->table_name.str));
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *table= NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Code in open_table() assumes that TABLE_LIST::table can be non-zero only
|
|
|
|
for pre-opened temporary tables.
|
|
|
|
*/
|
|
|
|
DBUG_ASSERT(tl->table == NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
This function should not be called for cases when derived or I_S
|
|
|
|
tables can be met since table list elements for such tables can
|
|
|
|
have invalid db or table name.
|
|
|
|
Instead THD::open_tables() should be used.
|
|
|
|
*/
|
|
|
|
DBUG_ASSERT(!tl->derived && !tl->schema_table);
|
|
|
|
|
|
|
|
if (tl->open_type == OT_BASE_ONLY || !has_temporary_tables())
|
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
2019-12-12 13:41:51 +01:00
|
|
|
if (!tl->db.str)
|
2019-12-07 22:15:38 +01:00
|
|
|
{
|
|
|
|
DBUG_PRINT("info",
|
|
|
|
("Table reference to a temporary table must have database set"));
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
2017-06-07 16:17:51 +02:00
|
|
|
/*
|
|
|
|
Temporary tables are not safe for parallel replication. They were
|
|
|
|
designed to be visible to one thread only, so have no table locking.
|
|
|
|
Thus there is no protection against two conflicting transactions
|
|
|
|
committing in parallel and things like that.
|
|
|
|
|
|
|
|
So for now, anything that uses temporary tables will be serialised
|
|
|
|
with anything before it, when using parallel replication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (rgi_slave &&
|
|
|
|
rgi_slave->is_parallel_exec &&
|
|
|
|
find_temporary_table(tl) &&
|
|
|
|
wait_for_prior_commit())
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
2016-06-10 22:19:59 +02:00
|
|
|
/*
|
|
|
|
First check if there is a reusable open table available in the
|
|
|
|
open table list.
|
|
|
|
*/
|
|
|
|
if (find_and_use_tmp_table(tl, &table))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(true); /* Error */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
No reusable table was found. We will have to open a new instance.
|
|
|
|
*/
|
|
|
|
if (!table && (share= find_tmp_table_share(tl)))
|
|
|
|
{
|
2019-02-05 15:41:33 +01:00
|
|
|
table= open_temporary_table(share, tl->get_table_name());
|
MDEV-19076: rpl_parallel_temptable result mismatch '-33 optimistic'
Problem:
========
The test now fails with the following trace:
CURRENT_TEST: rpl.rpl_parallel_temptable
--- /mariadb/10.4/mysql-test/suite/rpl/r/rpl_parallel_temptable.result
+++ /mariadb/10.4/mysql-test/suite/rpl/r/rpl_parallel_temptable.reject
@@ -194,7 +194,6 @@
30 conservative
31 conservative
32 optimistic
-33 optimistic
Analysis:
=========
The part of test which fails with result content mismatch is given below.
CREATE TEMPORARY TABLE t4 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t4 VALUES (32);
INSERT INTO t4 VALUES (33);
INSERT INTO t1 SELECT a, "optimistic" FROM t4;
slave_parallel_mode=optimistic
The expectation of the above test script is, INSERT FROM SELECT should read both
32, 33 and populate table 't1'. But this expectation fails occasionally.
All three INSERT statements are handed over to three different slave parallel
workers. Temporary tables are not safe for parallel replication. They were
designed to be visible to one thread only, so have no table locking. Thus there
is no protection against two conflicting transactions committing in parallel and
things like that.
So anything that uses temporary tables will be serialized with anything before
it, when using parallel replication by using a "wait_for_prior_commit" function
call. This will ensure that the each transaction is executed sequentially.
But there exists a code path in which the above wait doesn't happen. Because of
this at times INSERT from SELECT doesn't wait for the INSERT (33) to complete
and it completes its executes and enters commit stage. Hence only row 32 is
found in those cases resulting in test failure.
The wait needs to be added within "open_temporary_table" call. The code looks
like this within "open_temporary_table".
Each thread tries to open temporary table in 3 different ways:
case 1: Find a temporary table which is already in use by using
find_temporary_table(tl) && wait_for_prior_commit()
case 2: If above failed then try to look for temporary table which is marked for
free for reuse. This internally calls "wait_for_prior_commit()" if table
is found.
find_and_use_tmp_table(tl, &table)
case 3: If none of the above open a new table handle from table share.
if (!table && (share= find_tmp_table_share(tl)))
{ table= open_temporary_table(share, tl->get_table_name(), true); }
At present the "wait_for_prior_commit" happens only in case 1 & 2.
Fix:
====
On slave add a call for "wait_for_prior_commit" for case 3.
The above wait on slave will solve the issue. A more detailed fix would be to
mark temporary tables as not safe for parallel execution on the master side.
In order to do that, on the master side, mark the Gtid_log_event specific flag
FL_TRANSACTIONAL to be false all the time. So that they are not scheduled
parallely.
2019-05-16 13:06:20 +02:00
|
|
|
/*
|
|
|
|
Temporary tables are not safe for parallel replication. They were
|
|
|
|
designed to be visible to one thread only, so have no table locking.
|
|
|
|
Thus there is no protection against two conflicting transactions
|
|
|
|
committing in parallel and things like that.
|
|
|
|
|
|
|
|
So for now, anything that uses temporary tables will be serialised
|
|
|
|
with anything before it, when using parallel replication.
|
|
|
|
*/
|
|
|
|
if (table && rgi_slave &&
|
|
|
|
rgi_slave->is_parallel_exec &&
|
|
|
|
wait_for_prior_commit())
|
|
|
|
DBUG_RETURN(true);
|
2019-06-18 23:35:44 +02:00
|
|
|
|
|
|
|
if (!table && is_error())
|
|
|
|
DBUG_RETURN(true); // Error when opening table
|
2016-06-10 22:19:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!table)
|
|
|
|
{
|
|
|
|
if (tl->open_type == OT_TEMPORARY_ONLY &&
|
|
|
|
tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
|
|
|
|
{
|
2018-01-07 17:03:44 +01:00
|
|
|
my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db.str, tl->table_name.str);
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
if (tl->partition_names)
|
|
|
|
{
|
|
|
|
/* Partitioned temporary tables is not supported. */
|
|
|
|
DBUG_ASSERT(!table->part_info);
|
|
|
|
my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
table->query_id= query_id;
|
|
|
|
thread_specific_used= true;
|
|
|
|
|
|
|
|
/* It is neither a derived table nor non-updatable view. */
|
|
|
|
tl->updatable= true;
|
|
|
|
tl->table= table;
|
|
|
|
|
|
|
|
table->init(this, tl);
|
|
|
|
|
|
|
|
DBUG_PRINT("info", ("Using temporary table"));
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Pre-open temporary tables corresponding to table list elements.
|
|
|
|
|
|
|
|
@note One should finalize process of opening temporary tables
|
|
|
|
by calling open_tables(). This function is responsible
|
|
|
|
for table version checking and handling of merge tables.
|
|
|
|
|
|
|
|
@param tl [IN] TABLE_LIST
|
|
|
|
|
|
|
|
@return false On success. If a temporary table exists
|
|
|
|
for the given element, tl->table is set.
|
|
|
|
true On error. my_error() has been called.
|
|
|
|
*/
|
|
|
|
bool THD::open_temporary_tables(TABLE_LIST *tl)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::open_temporary_tables");
|
|
|
|
|
|
|
|
TABLE_LIST *first_not_own= lex->first_not_own_table();
|
|
|
|
|
|
|
|
for (TABLE_LIST *table= tl; table && table != first_not_own;
|
|
|
|
table= table->next_global)
|
|
|
|
{
|
|
|
|
if (table->derived || table->schema_table)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Derived and I_S tables will be handled by a later call to open_tables().
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (open_temporary_table(table))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
|
|
|
|
creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread.
|
|
|
|
|
|
|
|
Temporary tables created in a sql slave is closed by
|
|
|
|
Relay_log_info::close_temporary_tables().
|
|
|
|
|
|
|
|
@return false Success
|
|
|
|
true Failure
|
|
|
|
*/
|
|
|
|
bool THD::close_temporary_tables()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::close_temporary_tables");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *table;
|
|
|
|
|
|
|
|
bool error= false;
|
|
|
|
|
|
|
|
if (!has_thd_temporary_tables())
|
|
|
|
{
|
|
|
|
if (temporary_tables)
|
|
|
|
{
|
|
|
|
my_free(temporary_tables);
|
|
|
|
temporary_tables= NULL;
|
|
|
|
}
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_ASSERT(!rgi_slave);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Ensure we don't have open HANDLERs for tables we are about to close.
|
|
|
|
This is necessary when THD::close_temporary_tables() is called as
|
|
|
|
part of execution of BINLOG statement (e.g. for format description event).
|
|
|
|
*/
|
|
|
|
mysql_ha_rm_temporary_tables(this);
|
|
|
|
|
|
|
|
/* Close all open temporary tables. */
|
|
|
|
All_tmp_tables_list::Iterator it(*temporary_tables);
|
|
|
|
while ((share= it++))
|
|
|
|
{
|
|
|
|
/* Traverse the table list. */
|
|
|
|
while ((table= share->all_tmp_tables.pop_front()))
|
|
|
|
{
|
2019-03-10 18:55:35 +01:00
|
|
|
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
|
2016-06-10 22:19:59 +02:00
|
|
|
free_temporary_table(table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write DROP TEMPORARY TABLE query log events to binary log.
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
|
|
|
error= log_events_and_free_tmp_shares();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while ((share= temporary_tables->pop_front()))
|
|
|
|
{
|
|
|
|
free_tmp_table_share(share, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* By now, there mustn't be any elements left in the list. */
|
|
|
|
DBUG_ASSERT(temporary_tables->is_empty());
|
|
|
|
|
|
|
|
my_free(temporary_tables);
|
|
|
|
temporary_tables= NULL;
|
|
|
|
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Rename a temporary table.
|
|
|
|
|
|
|
|
@param table [IN] Table handle
|
|
|
|
@param db [IN] New schema name
|
|
|
|
@param table_name [IN] New table name
|
|
|
|
|
|
|
|
@return false Success
|
|
|
|
true Error
|
|
|
|
*/
|
|
|
|
bool THD::rename_temporary_table(TABLE *table,
|
2018-01-07 17:03:44 +01:00
|
|
|
const LEX_CSTRING *db,
|
|
|
|
const LEX_CSTRING *table_name)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
char *key;
|
|
|
|
uint key_length;
|
|
|
|
TABLE_SHARE *share= table->s;
|
2018-01-07 17:03:44 +01:00
|
|
|
DBUG_ENTER("THD::rename_temporary_table");
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Temporary tables are renamed by simply changing their table definition key.
|
|
|
|
*/
|
2018-01-07 17:03:44 +01:00
|
|
|
key_length= create_tmp_table_def_key(key, db->str, table_name->str);
|
2016-06-10 22:19:59 +02:00
|
|
|
share->set_table_cache_key(key, key_length);
|
|
|
|
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Drop a temporary table.
|
|
|
|
|
|
|
|
Try to locate the table in the list of open temporary tables.
|
|
|
|
If the table is found:
|
|
|
|
- If the table is locked with LOCK TABLES or by prelocking,
|
|
|
|
unlock it and remove it from the list of locked tables
|
|
|
|
(THD::lock). Currently only transactional temporary tables
|
|
|
|
are locked.
|
|
|
|
- Close the temporary table, remove its .FRM.
|
|
|
|
- Remove the table share from the list of temporary table shares.
|
|
|
|
|
|
|
|
This function is used to drop user temporary tables, as well as
|
|
|
|
internal tables created in CREATE TEMPORARY TABLE ... SELECT
|
|
|
|
or ALTER TABLE.
|
|
|
|
|
|
|
|
@param table [IN] Temporary table to be deleted
|
|
|
|
@param is_trans [OUT] Is set to the type of the table:
|
|
|
|
transactional (e.g. innodb) as true or
|
|
|
|
non-transactional (e.g. myisam) as false.
|
|
|
|
@paral delete_table [IN] Whether to delete the table files?
|
|
|
|
|
|
|
|
@return false Table was dropped
|
|
|
|
true Error
|
|
|
|
*/
|
2019-03-10 18:55:35 +01:00
|
|
|
bool THD::drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::drop_temporary_table");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *tab;
|
|
|
|
bool result= false;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
DBUG_ASSERT(table);
|
|
|
|
DBUG_PRINT("tmptable", ("Dropping table: '%s'.'%s'",
|
|
|
|
table->s->db.str, table->s->table_name.str));
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
share= tmp_table_share(table);
|
|
|
|
|
|
|
|
/* Table might be in use by some outer statement. */
|
|
|
|
All_share_tables_list::Iterator it(share->all_tmp_tables);
|
|
|
|
while ((tab= it++))
|
|
|
|
{
|
|
|
|
if (tab != table && tab->query_id != 0)
|
|
|
|
{
|
|
|
|
/* Found a table instance in use. This table cannot be be dropped. */
|
|
|
|
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
|
|
|
|
result= true;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_trans)
|
|
|
|
*is_trans= table->file->has_transactions();
|
|
|
|
|
|
|
|
/*
|
|
|
|
Iterate over the list of open tables and close them.
|
|
|
|
*/
|
|
|
|
while ((tab= share->all_tmp_tables.pop_front()))
|
|
|
|
{
|
2016-06-19 14:07:03 +02:00
|
|
|
/*
|
|
|
|
We need to set the THD as it may be different in case of
|
|
|
|
parallel replication
|
|
|
|
*/
|
|
|
|
tab->in_use= this;
|
2019-03-10 18:55:35 +01:00
|
|
|
if (delete_table)
|
|
|
|
tab->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
|
2016-06-10 22:19:59 +02:00
|
|
|
free_temporary_table(tab);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_ASSERT(temporary_tables);
|
|
|
|
|
|
|
|
/* Remove the TABLE_SHARE from the list of temporary tables. */
|
|
|
|
temporary_tables->remove(share);
|
|
|
|
|
|
|
|
/* Free the TABLE_SHARE and/or delete the files. */
|
|
|
|
free_tmp_table_share(share, delete_table);
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Delete the temporary table files.
|
|
|
|
|
|
|
|
@param base [IN] Handlerton for table to be deleted.
|
|
|
|
@param path [IN] Path to the table to be deleted (i.e. path
|
|
|
|
to its .frm without an extension).
|
|
|
|
|
|
|
|
@return false Success
|
|
|
|
true Error
|
|
|
|
*/
|
|
|
|
bool THD::rm_temporary_table(handlerton *base, const char *path)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::rm_temporary_table");
|
|
|
|
|
|
|
|
bool error= false;
|
|
|
|
handler *file;
|
|
|
|
char frm_path[FN_REFLEN + 1];
|
|
|
|
|
|
|
|
strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
|
MDEV-27753 Incorrect ENGINE type of table after crash for CONNECT table
whenever possible, partitioning should use the full
partition plugin name, not the one byte legacy code.
Normally, ha_partition can get the engine plugin from
table_share->default_part_plugin.
But in some cases, e.g. in DROP TABLE, the table isn't
opened, table_share is NULL, and ha_partition has to parse
the frm, much like dd_frm_type() does.
temporary_tables.cc, sql_table.cc:
When dropping a table, it must be deleted in the engine
first, then frm file. Because frm can be the only true
source of metadata that the engine might need for DROP.
table.cc:
when opening a partitioned table, if the engine for
partitions is not found, do not fallback to MyISAM.
2022-03-11 20:18:22 +01:00
|
|
|
|
|
|
|
file= get_new_handler((TABLE_SHARE*) 0, mem_root, base);
|
2016-06-10 22:19:59 +02:00
|
|
|
if (file && file->ha_delete_table(path))
|
|
|
|
{
|
|
|
|
error= true;
|
|
|
|
sql_print_warning("Could not remove temporary table: '%s', error: %d",
|
|
|
|
path, my_errno);
|
|
|
|
}
|
|
|
|
delete file;
|
MDEV-27753 Incorrect ENGINE type of table after crash for CONNECT table
whenever possible, partitioning should use the full
partition plugin name, not the one byte legacy code.
Normally, ha_partition can get the engine plugin from
table_share->default_part_plugin.
But in some cases, e.g. in DROP TABLE, the table isn't
opened, table_share is NULL, and ha_partition has to parse
the frm, much like dd_frm_type() does.
temporary_tables.cc, sql_table.cc:
When dropping a table, it must be deleted in the engine
first, then frm file. Because frm can be the only true
source of metadata that the engine might need for DROP.
table.cc:
when opening a partitioned table, if the engine for
partitions is not found, do not fallback to MyISAM.
2022-03-11 20:18:22 +01:00
|
|
|
|
|
|
|
if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
|
|
|
|
{
|
|
|
|
error= true;
|
|
|
|
}
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Mark all temporary tables which were used by the current statement or
|
|
|
|
sub-statement as free for reuse, but only if the query_id can be cleared.
|
|
|
|
|
|
|
|
@remark For temp tables associated with a open SQL HANDLER the query_id
|
|
|
|
is not reset until the HANDLER is closed.
|
|
|
|
*/
|
|
|
|
void THD::mark_tmp_tables_as_free_for_reuse()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::mark_tmp_tables_as_free_for_reuse");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *table;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
if (query_id == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Thread has not executed any statement and has not used any
|
|
|
|
temporary tables.
|
|
|
|
*/
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_temporary_tables())
|
|
|
|
{
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
All_tmp_tables_list::Iterator it(*temporary_tables);
|
|
|
|
while ((share= it++))
|
|
|
|
{
|
|
|
|
All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
|
|
|
|
while ((table= tables_it++))
|
|
|
|
{
|
|
|
|
if ((table->query_id == query_id) && !table->open_by_handler)
|
|
|
|
{
|
2019-02-27 21:43:57 +01:00
|
|
|
if (table->update_handler)
|
|
|
|
table->delete_update_handler();
|
2016-06-10 22:19:59 +02:00
|
|
|
mark_tmp_table_as_free_for_reuse(table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rgi_slave)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Temporary tables are shared with other by sql execution threads.
|
|
|
|
As a safety measure, clear the pointer to the common area.
|
|
|
|
*/
|
|
|
|
temporary_tables= NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Reset a single temporary table. Effectively this "closes" one temporary
|
|
|
|
table in a session.
|
|
|
|
|
|
|
|
@param table Temporary table
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
|
|
|
void THD::mark_tmp_table_as_free_for_reuse(TABLE *table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::mark_tmp_table_as_free_for_reuse");
|
|
|
|
|
|
|
|
DBUG_ASSERT(table->s->tmp_table);
|
|
|
|
|
|
|
|
table->query_id= 0;
|
|
|
|
table->file->ha_reset();
|
|
|
|
|
|
|
|
/* Detach temporary MERGE children from temporary parent. */
|
|
|
|
DBUG_ASSERT(table->file);
|
|
|
|
table->file->extra(HA_EXTRA_DETACH_CHILDREN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Reset temporary table lock type to it's default value (TL_WRITE).
|
|
|
|
|
|
|
|
Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
|
|
|
|
.. SELECT FROM tmp and UPDATE may under some circumstances modify
|
|
|
|
the lock type of the tables participating in the statement. This
|
|
|
|
isn't a problem for non-temporary tables since their lock type is
|
|
|
|
reset at every open, but the same does not occur for temporary
|
|
|
|
tables for historical reasons.
|
|
|
|
|
|
|
|
Furthermore, the lock type of temporary tables is not really that
|
|
|
|
important because they can only be used by one query at a time.
|
|
|
|
Nonetheless, it's safer from a maintenance point of view to reset
|
|
|
|
the lock type of this singleton TABLE object as to not cause problems
|
|
|
|
when the table is reused.
|
|
|
|
|
|
|
|
Even under LOCK TABLES mode its okay to reset the lock type as
|
|
|
|
LOCK TABLES is allowed (but ignored) for a temporary table.
|
|
|
|
*/
|
|
|
|
table->reginfo.lock_type= TL_WRITE;
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-10 22:58:08 +02:00
|
|
|
/**
|
|
|
|
Remove and return the specified table's TABLE_SHARE from the temporary
|
|
|
|
tables list.
|
|
|
|
|
|
|
|
@param table [IN] Table
|
|
|
|
|
|
|
|
@return TMP_TABLE_SHARE of the specified table.
|
|
|
|
*/
|
|
|
|
TMP_TABLE_SHARE *THD::save_tmp_table_share(TABLE *table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::save_tmp_table_share");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
|
|
|
|
lock_temporary_tables();
|
|
|
|
DBUG_ASSERT(temporary_tables);
|
|
|
|
share= tmp_table_share(table);
|
|
|
|
temporary_tables->remove(share);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
|
|
|
|
DBUG_RETURN(share);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Add the specified TMP_TABLE_SHARE to the temporary tables list.
|
|
|
|
|
|
|
|
@param share [IN] Table share
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
|
|
|
void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::restore_tmp_table_share");
|
|
|
|
|
|
|
|
lock_temporary_tables();
|
|
|
|
DBUG_ASSERT(temporary_tables);
|
|
|
|
temporary_tables->push_front(share);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-10 22:19:59 +02:00
|
|
|
/**
|
|
|
|
If its a replication slave, report whether slave temporary tables
|
|
|
|
exist (Relay_log_info::save_temporary_tables) or report about THD
|
|
|
|
temporary table (Open_tables_state::temporary_tables) otherwise.
|
|
|
|
|
|
|
|
@return false Temporary tables exist
|
|
|
|
true No temporary table exist
|
|
|
|
*/
|
2021-05-24 12:23:03 +02:00
|
|
|
bool THD::has_temporary_tables()
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::has_temporary_tables");
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
bool result=
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
|
|
rgi_slave ? (rgi_slave->rli->save_temporary_tables &&
|
|
|
|
!rgi_slave->rli->save_temporary_tables->is_empty()) :
|
|
|
|
#endif
|
|
|
|
has_thd_temporary_tables();
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a table definition key.
|
|
|
|
|
|
|
|
@param key [OUT] Buffer for the key to be created (must
|
|
|
|
be of size MAX_DBKRY_LENGTH)
|
|
|
|
@param db [IN] Database name
|
|
|
|
@param table_name [IN] Table name
|
|
|
|
|
|
|
|
@return Key length.
|
|
|
|
|
|
|
|
@note
|
|
|
|
The table key is create from:
|
|
|
|
db + \0
|
|
|
|
table_name + \0
|
|
|
|
|
|
|
|
Additionally, we add the following to make each temporary table unique on
|
|
|
|
the slave.
|
|
|
|
|
|
|
|
4 bytes of master thread id
|
|
|
|
4 bytes of pseudo thread id
|
|
|
|
*/
|
|
|
|
uint THD::create_tmp_table_def_key(char *key, const char *db,
|
|
|
|
const char *table_name)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::create_tmp_table_def_key");
|
|
|
|
|
|
|
|
uint key_length;
|
|
|
|
|
|
|
|
key_length= tdc_create_key(key, db, table_name);
|
|
|
|
int4store(key + key_length, variables.server_id);
|
|
|
|
int4store(key + key_length + 4, variables.pseudo_thread_id);
|
|
|
|
key_length += TMP_TABLE_KEY_EXTRA;
|
|
|
|
|
|
|
|
DBUG_RETURN(key_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a temporary table.
|
|
|
|
|
|
|
|
@param frm [IN] Binary frm image
|
|
|
|
@param path [IN] File path (without extension)
|
|
|
|
@param db [IN] Schema name
|
|
|
|
@param table_name [IN] Table name
|
|
|
|
|
|
|
|
@return Success A pointer to table share object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
2019-02-06 08:41:36 +01:00
|
|
|
TMP_TABLE_SHARE *THD::create_temporary_table(LEX_CUSTRING *frm,
|
2016-06-10 22:19:59 +02:00
|
|
|
const char *path,
|
|
|
|
const char *db,
|
|
|
|
const char *table_name)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::create_temporary_table");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
char key_cache[MAX_DBKEY_LENGTH];
|
|
|
|
char *saved_key_cache;
|
|
|
|
char *tmp_path;
|
|
|
|
uint key_length;
|
|
|
|
bool locked;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
/* Temporary tables are not safe for parallel replication. */
|
|
|
|
if (rgi_slave &&
|
|
|
|
rgi_slave->is_parallel_exec &&
|
|
|
|
wait_for_prior_commit())
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
|
|
|
|
/* Create the table definition key for the temporary table. */
|
|
|
|
key_length= create_tmp_table_def_key(key_cache, db, table_name);
|
|
|
|
|
|
|
|
if (!(share= (TMP_TABLE_SHARE *) my_malloc(sizeof(TMP_TABLE_SHARE) +
|
|
|
|
strlen(path) + 1 + key_length,
|
|
|
|
MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL); /* Out of memory */
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_path= (char *)(share + 1);
|
|
|
|
saved_key_cache= strmov(tmp_path, path) + 1;
|
|
|
|
memcpy(saved_key_cache, key_cache, key_length);
|
|
|
|
|
|
|
|
init_tmp_table_share(this, share, saved_key_cache, key_length,
|
|
|
|
strend(saved_key_cache) + 1, tmp_path);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Prefer using frm image over file. The image might not be available in
|
|
|
|
ALTER TABLE, when the discovering engine took over the ownership (see
|
|
|
|
TABLE::read_frm_image).
|
|
|
|
*/
|
|
|
|
res= (frm->str)
|
|
|
|
? share->init_from_binary_frm_image(this, false, frm->str, frm->length)
|
|
|
|
: open_table_def(this, share, GTS_TABLE | GTS_USE_DISCOVERY);
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
No need to lock share->mutex as this is not needed for temporary tables.
|
|
|
|
*/
|
|
|
|
free_table_share(share);
|
|
|
|
my_free(share);
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
share->m_psi= PSI_CALL_get_table_share(true, share);
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
/* Initialize the all_tmp_tables list. */
|
|
|
|
share->all_tmp_tables.empty();
|
|
|
|
|
|
|
|
/*
|
|
|
|
We need to alloc & initialize temporary_tables if this happens
|
|
|
|
to be the very first temporary table.
|
|
|
|
*/
|
|
|
|
if (!temporary_tables)
|
|
|
|
{
|
|
|
|
if ((temporary_tables=
|
|
|
|
(All_tmp_tables_list *) my_malloc(sizeof(All_tmp_tables_list),
|
|
|
|
MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
temporary_tables->empty();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL); /* Out of memory */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add share to the head of the temporary table share list. */
|
|
|
|
temporary_tables->push_front(share);
|
|
|
|
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(share);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find a table with the specified key.
|
|
|
|
|
|
|
|
@param key [IN] Key
|
|
|
|
@param key_length [IN] Key length
|
|
|
|
@param state [IN] Open table state to look for
|
|
|
|
|
|
|
|
@return Success Pointer to the table instance.
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TABLE *THD::find_temporary_table(const char *key, uint key_length,
|
|
|
|
Temporary_table_state state)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_temporary_table");
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TABLE *table;
|
|
|
|
TABLE *result= NULL;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
locked= lock_temporary_tables();
|
|
|
|
|
|
|
|
All_tmp_tables_list::Iterator it(*temporary_tables);
|
|
|
|
while ((share= it++))
|
|
|
|
{
|
|
|
|
if (share->table_cache_key.length == key_length &&
|
|
|
|
!(memcmp(share->table_cache_key.str, key, key_length)))
|
|
|
|
{
|
|
|
|
/* A matching TMP_TABLE_SHARE is found. */
|
|
|
|
All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
|
|
|
|
|
2018-12-16 18:22:35 +01:00
|
|
|
bool found= false;
|
|
|
|
while (!found && (table= tables_it++))
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
2018-12-16 18:22:35 +01:00
|
|
|
case TMP_TABLE_IN_USE: found= table->query_id > 0; break;
|
|
|
|
case TMP_TABLE_NOT_IN_USE: found= table->query_id == 0; break;
|
|
|
|
case TMP_TABLE_ANY: found= true; break;
|
2016-06-10 22:19:59 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-22 17:02:24 +02:00
|
|
|
if (table && unlikely(table->needs_reopen()))
|
2018-12-16 18:32:05 +01:00
|
|
|
{
|
|
|
|
share->all_tmp_tables.remove(table);
|
|
|
|
free_temporary_table(table);
|
|
|
|
it.rewind();
|
|
|
|
continue;
|
|
|
|
}
|
2018-12-16 18:22:35 +01:00
|
|
|
result= table;
|
|
|
|
break;
|
2016-06-10 22:19:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (locked)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(m_tmp_tables_locked);
|
|
|
|
unlock_temporary_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Open a table from the specified TABLE_SHARE with the given alias.
|
|
|
|
|
|
|
|
@param share [IN] Table share
|
|
|
|
@param alias [IN] Table alias
|
|
|
|
|
|
|
|
@return Success A pointer to table object
|
|
|
|
Failure NULL
|
|
|
|
*/
|
|
|
|
TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
|
2019-02-05 15:41:33 +01:00
|
|
|
const char *alias_arg)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
2018-01-07 17:03:44 +01:00
|
|
|
TABLE *table;
|
|
|
|
LEX_CSTRING alias= {alias_arg, strlen(alias_arg) };
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_ENTER("THD::open_temporary_table");
|
|
|
|
|
|
|
|
|
|
|
|
if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(NULL); /* Out of memory */
|
|
|
|
}
|
|
|
|
|
2018-01-07 17:03:44 +01:00
|
|
|
if (open_table_from_share(this, share, &alias,
|
2019-02-05 15:41:33 +01:00
|
|
|
(uint) HA_OPEN_KEYFILE,
|
MDEV-10139 Support for SEQUENCE objects
Working features:
CREATE OR REPLACE [TEMPORARY] SEQUENCE [IF NOT EXISTS] name
[ INCREMENT [ BY | = ] increment ]
[ MINVALUE [=] minvalue | NO MINVALUE ]
[ MAXVALUE [=] maxvalue | NO MAXVALUE ]
[ START [ WITH | = ] start ] [ CACHE [=] cache ] [ [ NO ] CYCLE ]
ENGINE=xxx COMMENT=".."
SELECT NEXT VALUE FOR sequence_name;
SELECT NEXTVAL(sequence_name);
SELECT PREVIOUS VALUE FOR sequence_name;
SELECT LASTVAL(sequence_name);
SHOW CREATE SEQUENCE sequence_name;
SHOW CREATE TABLE sequence_name;
CREATE TABLE sequence-structure ... SEQUENCE=1
ALTER TABLE sequence RENAME TO sequence2;
RENAME TABLE sequence TO sequence2;
DROP [TEMPORARY] SEQUENCE [IF EXISTS] sequence_names
Missing features
- SETVAL(value,sequence_name), to be used with replication.
- Check replication, including checking that sequence tables are marked
not transactional.
- Check that a commit happens for NEXT VALUE that changes table data (may
already work)
- ALTER SEQUENCE. ANSI SQL version of setval.
- Share identical sequence entries to not add things twice to table list.
- testing insert/delete/update/truncate/load data
- Run and fix Alibaba sequence tests (part of mysql-test/suite/sql_sequence)
- Write documentation for NEXT VALUE / PREVIOUS_VALUE
- NEXTVAL in DEFAULT
- Ensure that NEXTVAL in DEFAULT uses database from base table
- Two NEXTVAL for same row should give same answer.
- Oracle syntax sequence_table.nextval, without any FOR or FROM.
- Sequence tables are treated as 'not read constant tables' by SELECT; Would
be better if we would have a separate list for sequence tables so that
select doesn't know about them, except if refereed to with FROM.
Other things done:
- Improved output for safemalloc backtrack
- frm_type_enum changed to Table_type
- Removed lex->is_view and replaced with lex->table_type. This allows
use to more easy check if item is view, sequence or table.
- Added table flag HA_CAN_TABLES_WITHOUT_ROLLBACK, needed for handlers
that want's to support sequences
- Added handler calls:
- engine_name(), to simplify getting engine name for partition and sequences
- update_first_row(), to be able to do efficient sequence implementations.
- Made binlog_log_row() global to be able to call it from ha_sequence.cc
- Added handler variable: row_already_logged, to be able to flag that the
changed row is already logging to replication log.
- Added CF_DB_CHANGE and CF_SCHEMA_CHANGE flags to simplify
deny_updates_if_read_only_option()
- Added sp_add_cfetch() to avoid new conflicts in sql_yacc.yy
- Moved code for add_table_options() out from sql_show.cc::show_create_table()
- Added String::append_longlong() and used it in sql_show.cc to simplify code.
- Added extra option to dd_frm_type() and ha_table_exists to indicate if
the table is a sequence. Needed by DROP SQUENCE to not drop a table.
2017-03-25 22:36:56 +01:00
|
|
|
EXTRA_RECORD,
|
|
|
|
(ha_open_options |
|
|
|
|
(open_options & HA_OPEN_FOR_CREATE)),
|
2019-02-05 15:41:33 +01:00
|
|
|
table, false))
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
my_free(table);
|
|
|
|
DBUG_RETURN(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
|
|
|
|
table->grant.privilege= TMP_TABLE_ACLS;
|
MDEV-10139 Support for SEQUENCE objects
Working features:
CREATE OR REPLACE [TEMPORARY] SEQUENCE [IF NOT EXISTS] name
[ INCREMENT [ BY | = ] increment ]
[ MINVALUE [=] minvalue | NO MINVALUE ]
[ MAXVALUE [=] maxvalue | NO MAXVALUE ]
[ START [ WITH | = ] start ] [ CACHE [=] cache ] [ [ NO ] CYCLE ]
ENGINE=xxx COMMENT=".."
SELECT NEXT VALUE FOR sequence_name;
SELECT NEXTVAL(sequence_name);
SELECT PREVIOUS VALUE FOR sequence_name;
SELECT LASTVAL(sequence_name);
SHOW CREATE SEQUENCE sequence_name;
SHOW CREATE TABLE sequence_name;
CREATE TABLE sequence-structure ... SEQUENCE=1
ALTER TABLE sequence RENAME TO sequence2;
RENAME TABLE sequence TO sequence2;
DROP [TEMPORARY] SEQUENCE [IF EXISTS] sequence_names
Missing features
- SETVAL(value,sequence_name), to be used with replication.
- Check replication, including checking that sequence tables are marked
not transactional.
- Check that a commit happens for NEXT VALUE that changes table data (may
already work)
- ALTER SEQUENCE. ANSI SQL version of setval.
- Share identical sequence entries to not add things twice to table list.
- testing insert/delete/update/truncate/load data
- Run and fix Alibaba sequence tests (part of mysql-test/suite/sql_sequence)
- Write documentation for NEXT VALUE / PREVIOUS_VALUE
- NEXTVAL in DEFAULT
- Ensure that NEXTVAL in DEFAULT uses database from base table
- Two NEXTVAL for same row should give same answer.
- Oracle syntax sequence_table.nextval, without any FOR or FROM.
- Sequence tables are treated as 'not read constant tables' by SELECT; Would
be better if we would have a separate list for sequence tables so that
select doesn't know about them, except if refereed to with FROM.
Other things done:
- Improved output for safemalloc backtrack
- frm_type_enum changed to Table_type
- Removed lex->is_view and replaced with lex->table_type. This allows
use to more easy check if item is view, sequence or table.
- Added table flag HA_CAN_TABLES_WITHOUT_ROLLBACK, needed for handlers
that want's to support sequences
- Added handler calls:
- engine_name(), to simplify getting engine name for partition and sequences
- update_first_row(), to be able to do efficient sequence implementations.
- Made binlog_log_row() global to be able to call it from ha_sequence.cc
- Added handler variable: row_already_logged, to be able to flag that the
changed row is already logging to replication log.
- Added CF_DB_CHANGE and CF_SCHEMA_CHANGE flags to simplify
deny_updates_if_read_only_option()
- Added sp_add_cfetch() to avoid new conflicts in sql_yacc.yy
- Moved code for add_table_options() out from sql_show.cc::show_create_table()
- Added String::append_longlong() and used it in sql_show.cc to simplify code.
- Added extra option to dd_frm_type() and ha_table_exists to indicate if
the table is a sequence. Needed by DROP SQUENCE to not drop a table.
2017-03-25 22:36:56 +01:00
|
|
|
share->tmp_table= (table->file->has_transaction_manager() ?
|
2016-06-10 22:19:59 +02:00
|
|
|
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
|
2017-06-18 08:58:13 +02:00
|
|
|
share->not_usable_by_query_cache= 1;
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
table->pos_in_table_list= 0;
|
|
|
|
table->query_id= query_id;
|
|
|
|
|
|
|
|
/* Add table to the head of table list. */
|
|
|
|
share->all_tmp_tables.push_front(table);
|
|
|
|
|
|
|
|
/* Increment Slave_open_temp_table_definitions status variable count. */
|
|
|
|
if (rgi_slave)
|
2020-04-15 18:38:25 +02:00
|
|
|
slave_open_temp_tables++;
|
2016-06-10 22:19:59 +02:00
|
|
|
|
2018-02-14 02:27:54 +01:00
|
|
|
DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s table: %p",
|
|
|
|
table->s->db.str,
|
2017-09-19 19:45:17 +02:00
|
|
|
table->s->table_name.str, table));
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find a reusable table in the open table list using the specified TABLE_LIST.
|
|
|
|
|
|
|
|
@param tl [IN] Table list
|
|
|
|
@param out_table [OUT] Pointer to the requested TABLE object
|
|
|
|
|
|
|
|
@return Success false
|
|
|
|
Failure true
|
|
|
|
*/
|
2018-12-14 14:28:30 +01:00
|
|
|
bool THD::find_and_use_tmp_table(const TABLE_LIST *tl, TABLE **out_table)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::find_and_use_tmp_table");
|
|
|
|
|
|
|
|
char key[MAX_DBKEY_LENGTH];
|
|
|
|
uint key_length;
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
key_length= create_tmp_table_def_key(key, tl->get_db_name(),
|
|
|
|
tl->get_table_name());
|
2018-12-14 14:28:30 +01:00
|
|
|
result= use_temporary_table(find_temporary_table(key, key_length,
|
|
|
|
TMP_TABLE_NOT_IN_USE),
|
|
|
|
out_table);
|
2016-06-10 22:19:59 +02:00
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Mark table as in-use.
|
|
|
|
|
|
|
|
@param table [IN] Table to be marked in-use
|
|
|
|
@param out_table [OUT] Pointer to the specified table
|
|
|
|
|
|
|
|
@return false Success
|
|
|
|
true Error
|
|
|
|
*/
|
|
|
|
bool THD::use_temporary_table(TABLE *table, TABLE **out_table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::use_temporary_table");
|
|
|
|
|
|
|
|
*out_table= table;
|
2016-06-19 14:07:03 +02:00
|
|
|
|
|
|
|
/* The following can happen if find_temporary_table() returns NULL */
|
2016-06-10 22:19:59 +02:00
|
|
|
if (!table)
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Temporary tables are not safe for parallel replication. They were
|
|
|
|
designed to be visible to one thread only, so have no table locking.
|
|
|
|
Thus there is no protection against two conflicting transactions
|
|
|
|
committing in parallel and things like that.
|
|
|
|
|
|
|
|
So for now, anything that uses temporary tables will be serialised
|
|
|
|
with anything before it, when using parallel replication.
|
|
|
|
|
|
|
|
TODO: We might be able to introduce a reference count or something
|
|
|
|
on temp tables, and have slave worker threads wait for it to reach
|
|
|
|
zero before being allowed to use the temp table. Might not be worth
|
|
|
|
it though, as statement-based replication using temporary tables is
|
|
|
|
in any case rather fragile.
|
|
|
|
*/
|
|
|
|
if (rgi_slave &&
|
|
|
|
rgi_slave->is_parallel_exec &&
|
|
|
|
wait_for_prior_commit())
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
|
|
|
/*
|
|
|
|
We need to set the THD as it may be different in case of
|
|
|
|
parallel replication
|
|
|
|
*/
|
2016-06-19 14:07:03 +02:00
|
|
|
table->in_use= this;
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Close a temporary table.
|
|
|
|
|
|
|
|
@param table [IN] Table handle
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
|
|
|
void THD::close_temporary_table(TABLE *table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::close_temporary_table");
|
|
|
|
|
2017-09-19 19:45:17 +02:00
|
|
|
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'%p alias: '%s'",
|
2016-06-10 22:19:59 +02:00
|
|
|
table->s->db.str, table->s->table_name.str,
|
2017-09-19 19:45:17 +02:00
|
|
|
table, table->alias.c_ptr()));
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
closefrm(table);
|
|
|
|
my_free(table);
|
|
|
|
|
|
|
|
if (rgi_slave)
|
|
|
|
{
|
|
|
|
/* Natural invariant of temporary_tables */
|
|
|
|
DBUG_ASSERT(slave_open_temp_tables || !temporary_tables);
|
|
|
|
/* Decrement Slave_open_temp_table_definitions status variable count. */
|
2020-04-15 18:38:25 +02:00
|
|
|
slave_open_temp_tables--;
|
2016-06-10 22:19:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Write query log events with "DROP TEMPORARY TABLES .." for each pseudo
|
|
|
|
thread to the binary log.
|
|
|
|
|
|
|
|
@return false Success
|
|
|
|
true Error
|
|
|
|
*/
|
|
|
|
bool THD::log_events_and_free_tmp_shares()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::log_events_and_free_tmp_shares");
|
|
|
|
|
|
|
|
DBUG_ASSERT(!rgi_slave);
|
|
|
|
|
|
|
|
TMP_TABLE_SHARE *share;
|
|
|
|
TMP_TABLE_SHARE *sorted;
|
|
|
|
TMP_TABLE_SHARE *prev_sorted;
|
|
|
|
// Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE.
|
|
|
|
bool was_quote_show= true;
|
|
|
|
bool error= false;
|
|
|
|
bool found_user_tables= false;
|
|
|
|
// Better add "IF EXISTS" in case a RESET MASTER has been done.
|
|
|
|
const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
|
|
|
|
char buf[FN_REFLEN];
|
|
|
|
|
|
|
|
String s_query(buf, sizeof(buf), system_charset_info);
|
|
|
|
s_query.copy(stub, sizeof(stub) - 1, system_charset_info);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Insertion sort of temporary tables by pseudo_thread_id to build ordered
|
|
|
|
list of sublists of equal pseudo_thread_id.
|
|
|
|
*/
|
|
|
|
All_tmp_tables_list::Iterator it_sorted(*temporary_tables);
|
|
|
|
All_tmp_tables_list::Iterator it_unsorted(*temporary_tables);
|
|
|
|
uint sorted_count= 0;
|
|
|
|
while((share= it_unsorted++))
|
|
|
|
{
|
|
|
|
if (IS_USER_TABLE(share))
|
|
|
|
{
|
|
|
|
prev_sorted= NULL;
|
|
|
|
|
|
|
|
if (!found_user_tables) found_user_tables= true;
|
|
|
|
|
|
|
|
for (uint i= 0; i < sorted_count; i ++)
|
|
|
|
{
|
|
|
|
sorted= it_sorted ++;
|
|
|
|
|
|
|
|
if (!IS_USER_TABLE(sorted) ||
|
|
|
|
(tmpkeyval(sorted) > tmpkeyval(share)))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Insert this share before the current element in
|
|
|
|
the sorted part of the list.
|
|
|
|
*/
|
|
|
|
temporary_tables->remove(share);
|
|
|
|
|
|
|
|
if (prev_sorted)
|
|
|
|
{
|
|
|
|
temporary_tables->insert_after(prev_sorted, share);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temporary_tables->push_front(share);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev_sorted= sorted;
|
|
|
|
}
|
|
|
|
it_sorted.rewind();
|
|
|
|
}
|
|
|
|
sorted_count ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
We always quote db & table names.
|
|
|
|
*/
|
|
|
|
if (found_user_tables &&
|
|
|
|
!(was_quote_show= MY_TEST(variables.option_bits &
|
|
|
|
OPTION_QUOTE_SHOW_CREATE)))
|
|
|
|
{
|
|
|
|
variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Scan sorted temporary tables to generate sequence of DROP.
|
|
|
|
*/
|
|
|
|
share= temporary_tables->pop_front();
|
|
|
|
while (share)
|
|
|
|
{
|
|
|
|
if (IS_USER_TABLE(share))
|
|
|
|
{
|
|
|
|
bool save_thread_specific_used= thread_specific_used;
|
|
|
|
my_thread_id save_pseudo_thread_id= variables.pseudo_thread_id;
|
|
|
|
char db_buf[FN_REFLEN];
|
|
|
|
String db(db_buf, sizeof(db_buf), system_charset_info);
|
2019-07-25 11:08:50 +02:00
|
|
|
bool at_least_one_create_logged;
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Set pseudo_thread_id to be that of the processed table.
|
|
|
|
*/
|
|
|
|
variables.pseudo_thread_id= tmpkeyval(share);
|
|
|
|
|
|
|
|
db.copy(share->db.str, share->db.length, system_charset_info);
|
|
|
|
/*
|
|
|
|
Reset s_query() if changed by previous loop.
|
|
|
|
*/
|
|
|
|
s_query.length(sizeof(stub) - 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Loop forward through all tables that belong to a common database
|
|
|
|
within the sublist of common pseudo_thread_id to create single
|
|
|
|
DROP query.
|
|
|
|
*/
|
2019-07-25 11:08:50 +02:00
|
|
|
for (at_least_one_create_logged= false;
|
2016-06-10 22:19:59 +02:00
|
|
|
share && IS_USER_TABLE(share) &&
|
|
|
|
tmpkeyval(share) == variables.pseudo_thread_id &&
|
|
|
|
share->db.length == db.length() &&
|
|
|
|
memcmp(share->db.str, db.ptr(), db.length()) == 0;
|
|
|
|
/* Get the next TABLE_SHARE in the list. */
|
|
|
|
share= temporary_tables->pop_front())
|
|
|
|
{
|
2019-07-25 11:08:50 +02:00
|
|
|
if (share->table_creation_was_logged)
|
|
|
|
{
|
|
|
|
at_least_one_create_logged= true;
|
|
|
|
/*
|
|
|
|
We are going to add ` around the table names and possible more
|
|
|
|
due to special characters.
|
2019-07-25 14:31:11 +02:00
|
|
|
*/
|
|
|
|
append_identifier(this, &s_query, &share->table_name);
|
2019-07-25 11:08:50 +02:00
|
|
|
s_query.append(',');
|
|
|
|
}
|
2016-06-10 22:19:59 +02:00
|
|
|
rm_temporary_table(share->db_type(), share->path.str);
|
|
|
|
free_table_share(share);
|
|
|
|
my_free(share);
|
|
|
|
}
|
|
|
|
|
2019-07-25 11:08:50 +02:00
|
|
|
if (at_least_one_create_logged)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
2019-07-25 11:08:50 +02:00
|
|
|
clear_error();
|
|
|
|
CHARSET_INFO *cs_save= variables.character_set_client;
|
|
|
|
variables.character_set_client= system_charset_info;
|
|
|
|
thread_specific_used= true;
|
|
|
|
|
|
|
|
Query_log_event qinfo(this, s_query.ptr(),
|
|
|
|
s_query.length() - 1 /* to remove trailing ',' */,
|
|
|
|
false, true, false, 0);
|
|
|
|
qinfo.db= db.ptr();
|
|
|
|
qinfo.db_len= db.length();
|
|
|
|
variables.character_set_client= cs_save;
|
|
|
|
|
|
|
|
get_stmt_da()->set_overwrite_status(true);
|
|
|
|
transaction.stmt.mark_dropped_temp_table();
|
2019-07-25 14:31:11 +02:00
|
|
|
bool error2= mysql_bin_log.write(&qinfo);
|
|
|
|
if (unlikely(error|= error2))
|
2019-07-25 11:08:50 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If we're here following THD::cleanup, thence the connection
|
|
|
|
has been closed already. So lets print a message to the
|
|
|
|
error log instead of pushing yet another error into the
|
|
|
|
stmt_da.
|
|
|
|
|
|
|
|
Also, we keep the error flag so that we propagate the error
|
|
|
|
up in the stack. This way, if we're the SQL thread we notice
|
|
|
|
that THD::close_tables failed. (Actually, the SQL
|
|
|
|
thread only calls THD::close_tables while applying
|
|
|
|
old Start_log_event_v3 events.)
|
2019-07-25 14:31:11 +02:00
|
|
|
*/
|
2019-07-25 11:08:50 +02:00
|
|
|
sql_print_error("Failed to write the DROP statement for "
|
|
|
|
"temporary tables to binary log");
|
|
|
|
}
|
2016-06-10 22:19:59 +02:00
|
|
|
|
2019-07-25 11:08:50 +02:00
|
|
|
get_stmt_da()->set_overwrite_status(false);
|
|
|
|
}
|
2016-06-10 22:19:59 +02:00
|
|
|
variables.pseudo_thread_id= save_pseudo_thread_id;
|
|
|
|
thread_specific_used= save_thread_specific_used;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
free_tmp_table_share(share, true);
|
|
|
|
/* Get the next TABLE_SHARE in the list. */
|
|
|
|
share= temporary_tables->pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!was_quote_show)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Restore option.
|
|
|
|
*/
|
|
|
|
variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Delete the files and free the specified table share.
|
|
|
|
|
|
|
|
@param share [IN] TABLE_SHARE to free
|
|
|
|
@param delete_table [IN] Whether to delete the table files?
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
2019-03-10 18:55:35 +01:00
|
|
|
void THD::free_tmp_table_share(TMP_TABLE_SHARE *share, bool delete_table)
|
2016-06-10 22:19:59 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::free_tmp_table_share");
|
|
|
|
|
|
|
|
if (delete_table)
|
|
|
|
{
|
|
|
|
rm_temporary_table(share->db_type(), share->path.str);
|
|
|
|
}
|
|
|
|
free_table_share(share);
|
|
|
|
my_free(share);
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Free the specified table object.
|
|
|
|
|
|
|
|
@param table [IN] Table object to free.
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
|
|
|
void THD::free_temporary_table(TABLE *table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::free_temporary_table");
|
|
|
|
|
|
|
|
/*
|
|
|
|
If LOCK TABLES list is not empty and contains this table, unlock the table
|
|
|
|
and remove the table from this list.
|
|
|
|
*/
|
|
|
|
mysql_lock_remove(this, lock, table);
|
|
|
|
|
|
|
|
close_temporary_table(table);
|
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
On replication slave, acquire the Relay_log_info's data_lock and use slave
|
|
|
|
temporary tables.
|
|
|
|
|
|
|
|
@return true Lock acquired
|
|
|
|
false Lock wasn't acquired
|
|
|
|
*/
|
|
|
|
bool THD::lock_temporary_tables()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::lock_temporary_tables");
|
|
|
|
|
|
|
|
/* Do not proceed if a lock has already been taken. */
|
|
|
|
if (m_tmp_tables_locked)
|
|
|
|
{
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2016-06-10 22:19:59 +02:00
|
|
|
if (rgi_slave)
|
|
|
|
{
|
|
|
|
mysql_mutex_lock(&rgi_slave->rli->data_lock);
|
|
|
|
temporary_tables= rgi_slave->rli->save_temporary_tables;
|
|
|
|
m_tmp_tables_locked= true;
|
|
|
|
}
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
#endif
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
DBUG_RETURN(m_tmp_tables_locked);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
On replication slave, release the Relay_log_info::data_lock previously
|
|
|
|
acquired to use slave temporary tables.
|
|
|
|
|
|
|
|
@return void
|
|
|
|
*/
|
|
|
|
void THD::unlock_temporary_tables()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("THD::unlock_temporary_tables");
|
|
|
|
|
|
|
|
if (!m_tmp_tables_locked)
|
|
|
|
{
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2016-06-10 22:19:59 +02:00
|
|
|
if (rgi_slave)
|
|
|
|
{
|
|
|
|
rgi_slave->rli->save_temporary_tables= temporary_tables;
|
|
|
|
temporary_tables= NULL; /* Safety */
|
|
|
|
mysql_mutex_unlock(&rgi_slave->rli->data_lock);
|
|
|
|
m_tmp_tables_locked= false;
|
|
|
|
}
|
Fix all warnings given by UBSAN
The easiest way to compile and test the server with UBSAN is to run:
./BUILD/compile-pentium64-ubsan
and then run mysql-test-run.
After this commit, one should be able to run this without any UBSAN
warnings. There is still a few compiler warnings that should be fixed
at some point, but these do not expose any real bugs.
The 'special' cases where we disable, suppress or circumvent UBSAN are:
- ref10 source (as here we intentionally do some shifts that UBSAN
complains about.
- x86 version of optimized int#korr() methods. UBSAN do not like unaligned
memory access of integers. Fixed by using byte_order_generic.h when
compiling with UBSAN
- We use smaller thread stack with ASAN and UBSAN, which forced me to
disable a few tests that prints the thread stack size.
- Verifying class types does not work for shared libraries. I added
suppression in mysql-test-run.pl for this case.
- Added '#ifdef WITH_UBSAN' when using integer arithmetic where it is
safe to have overflows (two cases, in item_func.cc).
Things fixed:
- Don't left shift signed values
(byte_order_generic.h, mysqltest.c, item_sum.cc and many more)
- Don't assign not non existing values to enum variables.
- Ensure that bool and enum values are properly initialized in
constructors. This was needed as UBSAN checks that these types has
correct values when one copies an object.
(gcalc_tools.h, ha_partition.cc, item_sum.cc, partition_element.h ...)
- Ensure we do not called handler functions on unallocated objects or
deleted objects.
(events.cc, sql_acl.cc).
- Fixed bugs in Item_sp::Item_sp() where we did not call constructor
on Query_arena object.
- Fixed several cast of objects to an incompatible class!
(Item.cc, Item_buff.cc, item_timefunc.cc, opt_subselect.cc, sql_acl.cc,
sql_select.cc ...)
- Ensure we do not do integer arithmetic that causes over or underflows.
This includes also ++ and -- of integers.
(Item_func.cc, Item_strfunc.cc, item_timefunc.cc, sql_base.cc ...)
- Added JSON_VALUE_UNITIALIZED to json_value_types and ensure that
value_type is initialized to this instead of to -1, which is not a valid
enum value for json_value_types.
- Ensure we do not call memcpy() when second argument could be null.
- Fixed that Item_func_str::make_empty_result() creates an empty string
instead of a null string (safer as it ensures we do not do arithmetic
on null strings).
Other things:
- Changed struct st_position to an OBJECT and added an initialization
function to it to ensure that we do not copy or use uninitialized
members. The change to a class was also motived that we used "struct
st_position" and POSITION randomly trough the code which was
confusing.
- Notably big rewrite in sql_acl.cc to avoid using deleted objects.
- Changed in sql_partition to use '^' instead of '-'. This is safe as
the operator is either 0 or 0x8000000000000000ULL.
- Added check for select_nr < INT_MAX in JOIN::build_explain() to
avoid bug when get_select() could return NULL.
- Reordered elements in POSITION for better alignment.
- Changed sql_test.cc::print_plan() to use pointers instead of objects.
- Fixed bug in find_set() where could could execute '1 << -1'.
- Added variable have_sanitizer, used by mtr. (This variable was before
only in 10.5 and up). It can now have one of two values:
ASAN or UBSAN.
- Moved ~Archive_share() from ha_archive.cc to ha_archive.h and marked
it virtual. This was an effort to get UBSAN to work with loaded storage
engines. I kept the change as the new place is better.
- Added in CONNECT engine COLBLK::SetName(), to get around a wrong cast
in tabutil.cpp.
- Added HAVE_REPLICATION around usage of rgi_slave, to get embedded
server to compile with UBSAN. (Patch from Marko).
- Added #ifdef for powerpc64 to avoid a bug in old gcc versions related
to integer arithmetic.
Changes that should not be needed but had to be done to suppress warnings
from UBSAN:
- Added static_cast<<uint16_t>> around shift to get rid of a LOT of
compiler warnings when using UBSAN.
- Had to change some '/' of 2 base integers to shift to get rid of
some compile time warnings.
Reviewed by:
- Json changes: Alexey Botchkov
- Charset changes in ctype-uca.c: Alexander Barkov
- InnoDB changes & Embedded server: Marko Mäkelä
- sql_acl.cc changes: Vicențiu Ciorbaru
- build_explain() changes: Sergey Petrunia
2021-04-18 14:29:13 +02:00
|
|
|
#endif
|
2016-06-10 22:19:59 +02:00
|
|
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2018-09-12 14:36:45 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Close unused TABLE instances for given temporary table.
|
|
|
|
|
|
|
|
@param tl [IN] TABLE_LIST
|
|
|
|
|
|
|
|
Initial use case was TRUNCATE, which expects only one instance (which is used
|
|
|
|
by TRUNCATE itself) to be open. Most probably some ALTER TABLE variants and
|
|
|
|
REPAIR may have similar expectations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl)
|
|
|
|
{
|
|
|
|
TMP_TABLE_SHARE *share= find_tmp_table_share(tl);
|
|
|
|
|
|
|
|
if (share)
|
|
|
|
{
|
|
|
|
All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
|
|
|
|
|
|
|
|
while (TABLE *table= tables_it++)
|
|
|
|
{
|
|
|
|
if (table->query_id == 0)
|
|
|
|
{
|
|
|
|
/* Note: removing current list element doesn't invalidate iterator. */
|
|
|
|
share->all_tmp_tables.remove(table);
|
|
|
|
free_temporary_table(table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|