mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
58780b5afb
Problem: The problem happened because of a conceptual flaw in the server code: a. The table level CHARSET/COLLATE clause affected all data types, including numeric and temporal ones: CREATE TABLE t1 (a INT) CHARACTER SET utf8 [COLLATE utf8_general_ci]; In the above example, the Column_definition_attributes (and then the FRM record) for the column "a" erroneously inherited "utf8" as its character set. b. The "ALTER TABLE t1 CONVERT TO CHARACTER SET csname" statement also erroneously affected Column_definition_attributes::charset for numeric and temporal data types and wrote "csname" as their character set into FRM files. So now we have arbitrary non-relevant charset ID values for numeric and temporal data types in all FRM files in the world :) The code in the server and the other engines did not seem to be affected by this flaw. Only InnoDB inplace ALTER was affected. Solution: Fixing the code in the way that only character string data types (CHAR,VARCHAR,TEXT,ENUM,SET): - inherit the table level CHARSET/COLLATE clause - get the charset value according to "CONVERT TO CHARACTER SET csname". Numeric and temporal data types now always get &my_charset_numeric in Column_definition_attributes::charset and always write its ID into FRM files: - no matter what the table level CHARSET/COLLATE clause is, and - no matter what "CONVERT TO CHARACTER SET" says. Details: 1. Adding helper classes to pass small parts of HA_CREATE_INFO into Type_handler methods: - Column_derived_attributes - to pass table level CHARSET/COLLATE, so columns that do not have explicit CHARSET/COLLATE clauses can derive them from the table level, e.g. CREATE TABLE t1 (a VARCHAR(1), b CHAR(1)) CHARACTER SET utf8; - Column_bulk_alter_attributes - to pass bulk attribute changes generated by the ALTER related code. These bulk changes affect multiple columns at the same time: ALTER TABLE ... CONVERT TO CHARACTER SET csname; Note, passing the whole HA_CREATE_INFO directly to Type_handler would not be good: HA_CREATE_INFO is huge and would need not desired dependencies in sql_type.h and sql_type.cc. The Type_handler API should use smallest possible data types! 2. Type_handler::Column_definition_prepare_stage1() is now responsible to set Column_definition::charset properly, according to the data type, for example: - For string data types, Column_definition_attributes::charset is set from the table level CHARSET/COLLATE clause (if not specified explicitly in the column definition). - For numeric and temporal fields, Column_definition_attributes::charset is set to &my_charset_numeric, no matter what the table level CHARSET/COLLATE says. - For GEOMETRY, Column_definition_attributes::charset is set to &my_charset_bin, no matter what the table level CHARSET/COLLATE says. Previously this code (setting `charset`) was outside of of Column_definition_prepare_stage1(), namely in mysql_prepare_create_table(), and was erroneously called for all data types. 3. Adding Type_handler::Column_definition_bulk_alter(), to handle "ALTER TABLE .. CONVERT TO". Previously this code was inside get_sql_field_charset() and was erroneously called for all data types. 4. Removing the Schema_specification_st parameter from Type_handler::Column_definition_redefine_stage1(). Column_definition_attributes::charset is now fully properly initialized by Column_definition_prepare_stage1(). So we don't need access to the table level CHARSET/COLLATE clause in Column_definition_redefine_stage1() any more. 5. Other changes: - Removing global function get_sql_field_charset() - Moving the part of the former get_sql_field_charset(), which was responsible to inherit the table level CHARSET/COLLATE clause to new methods: -- Column_definition_attributes::explicit_or_derived_charset() and -- Column_definition::prepare_charset_for_string(). This code is only needed for string data types. Previously it was erroneously called for all data types. - Moving another part, which was responsible to apply the "CONVERT TO" clause, to Type_handler_general_purpose_string::Column_definition_bulk_alter(). - Replacing the call for get_sql_field_charset() in sql_partition.cc to sql_field->explicit_or_derived_charset() - it is perfectly enough. The old code was redundant: get_sql_field_charset() was called from sql_partition.cc only when there were no a "CONVERT TO CHARACTER SET" clause involved, so its purpose was only to inherit the table level CHARSET/COLLATE clause. - Moving the code handling the BINCMP_FLAG flag from mysql_prepare_create_table() to Column_definition::prepare_charset_for_string(): This code is responsible to resolve the BINARY comparison style into the corresponding _bin collation, to do the following transparent rewrite: CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8; -> CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin); This code is only needed for string data types. Previously it was erroneously called for all data types. 6. Renaming Table_scope_and_contents_source_pod_st::table_charset to alter_table_convert_to_charset, because the only purpose it's used for is handlering "ALTER .. CONVERT". The new name is much more self-descriptive.
286 lines
11 KiB
C++
286 lines
11 KiB
C++
/* Copyright (c) 2006, 2014, Oracle and/or its affiliates.
|
|
Copyright (c) 2011, 2017, MariaDB Corporation.
|
|
|
|
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-1335 USA */
|
|
|
|
#ifndef SQL_TABLE_INCLUDED
|
|
#define SQL_TABLE_INCLUDED
|
|
|
|
#include <my_sys.h> // pthread_mutex_t
|
|
#include "m_string.h" // LEX_CUSTRING
|
|
|
|
class Alter_info;
|
|
class Alter_table_ctx;
|
|
class Column_definition;
|
|
class Create_field;
|
|
struct TABLE_LIST;
|
|
class THD;
|
|
struct TABLE;
|
|
struct handlerton;
|
|
class handler;
|
|
typedef struct st_ha_check_opt HA_CHECK_OPT;
|
|
struct HA_CREATE_INFO;
|
|
struct Table_specification_st;
|
|
typedef struct st_key KEY;
|
|
typedef struct st_key_cache KEY_CACHE;
|
|
typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE;
|
|
typedef struct st_order ORDER;
|
|
|
|
enum ddl_log_entry_code
|
|
{
|
|
/*
|
|
DDL_LOG_EXECUTE_CODE:
|
|
This is a code that indicates that this is a log entry to
|
|
be executed, from this entry a linked list of log entries
|
|
can be found and executed.
|
|
DDL_LOG_ENTRY_CODE:
|
|
An entry to be executed in a linked list from an execute log
|
|
entry.
|
|
DDL_IGNORE_LOG_ENTRY_CODE:
|
|
An entry that is to be ignored
|
|
*/
|
|
DDL_LOG_EXECUTE_CODE = 'e',
|
|
DDL_LOG_ENTRY_CODE = 'l',
|
|
DDL_IGNORE_LOG_ENTRY_CODE = 'i'
|
|
};
|
|
|
|
enum ddl_log_action_code
|
|
{
|
|
/*
|
|
The type of action that a DDL_LOG_ENTRY_CODE entry is to
|
|
perform.
|
|
DDL_LOG_DELETE_ACTION:
|
|
Delete an entity
|
|
DDL_LOG_RENAME_ACTION:
|
|
Rename an entity
|
|
DDL_LOG_REPLACE_ACTION:
|
|
Rename an entity after removing the previous entry with the
|
|
new name, that is replace this entry.
|
|
DDL_LOG_EXCHANGE_ACTION:
|
|
Exchange two entities by renaming them a -> tmp, b -> a, tmp -> b.
|
|
*/
|
|
DDL_LOG_DELETE_ACTION = 'd',
|
|
DDL_LOG_RENAME_ACTION = 'r',
|
|
DDL_LOG_REPLACE_ACTION = 's',
|
|
DDL_LOG_EXCHANGE_ACTION = 'e'
|
|
};
|
|
|
|
enum enum_ddl_log_exchange_phase {
|
|
EXCH_PHASE_NAME_TO_TEMP= 0,
|
|
EXCH_PHASE_FROM_TO_NAME= 1,
|
|
EXCH_PHASE_TEMP_TO_FROM= 2
|
|
};
|
|
|
|
|
|
typedef struct st_ddl_log_entry
|
|
{
|
|
const char *name;
|
|
const char *from_name;
|
|
const char *handler_name;
|
|
const char *tmp_name;
|
|
uint next_entry;
|
|
uint entry_pos;
|
|
enum ddl_log_entry_code entry_type;
|
|
enum ddl_log_action_code action_type;
|
|
/*
|
|
Most actions have only one phase. REPLACE does however have two
|
|
phases. The first phase removes the file with the new name if
|
|
there was one there before and the second phase renames the
|
|
old name to the new name.
|
|
*/
|
|
char phase;
|
|
} DDL_LOG_ENTRY;
|
|
|
|
typedef struct st_ddl_log_memory_entry
|
|
{
|
|
uint entry_pos;
|
|
struct st_ddl_log_memory_entry *next_log_entry;
|
|
struct st_ddl_log_memory_entry *prev_log_entry;
|
|
struct st_ddl_log_memory_entry *next_active_log_entry;
|
|
} DDL_LOG_MEMORY_ENTRY;
|
|
|
|
|
|
enum enum_explain_filename_mode
|
|
{
|
|
EXPLAIN_ALL_VERBOSE= 0,
|
|
EXPLAIN_PARTITIONS_VERBOSE,
|
|
EXPLAIN_PARTITIONS_AS_COMMENT
|
|
};
|
|
|
|
/* Maximum length of GEOM_POINT Field */
|
|
#define MAX_LEN_GEOM_POINT_FIELD 25
|
|
|
|
/* depends on errmsg.txt Database `db`, Table `t` ... */
|
|
#define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63
|
|
|
|
#define WFRM_WRITE_SHADOW 1
|
|
#define WFRM_INSTALL_SHADOW 2
|
|
#define WFRM_KEEP_SHARE 4
|
|
|
|
/* Flags for conversion functions. */
|
|
static const uint FN_FROM_IS_TMP= 1 << 0;
|
|
static const uint FN_TO_IS_TMP= 1 << 1;
|
|
static const uint FN_IS_TMP= FN_FROM_IS_TMP | FN_TO_IS_TMP;
|
|
static const uint NO_FRM_RENAME= 1 << 2;
|
|
static const uint FRM_ONLY= 1 << 3;
|
|
/** Don't remove table in engine. Remove only .FRM and maybe .PAR files. */
|
|
static const uint NO_HA_TABLE= 1 << 4;
|
|
/** Don't resolve MySQL's fake "foo.sym" symbolic directory names. */
|
|
static const uint SKIP_SYMDIR_ACCESS= 1 << 5;
|
|
/** Don't check foreign key constraints while renaming table */
|
|
static const uint NO_FK_CHECKS= 1 << 6;
|
|
|
|
uint filename_to_tablename(const char *from, char *to, size_t to_length,
|
|
bool stay_quiet = false);
|
|
uint tablename_to_filename(const char *from, char *to, size_t to_length);
|
|
uint check_n_cut_mysql50_prefix(const char *from, char *to, size_t to_length);
|
|
bool check_mysql50_prefix(const char *name);
|
|
uint build_table_filename(char *buff, size_t bufflen, const char *db,
|
|
const char *table, const char *ext, uint flags);
|
|
uint build_table_shadow_filename(char *buff, size_t bufflen,
|
|
ALTER_PARTITION_PARAM_TYPE *lpt);
|
|
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen);
|
|
bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
|
|
Table_specification_st *create_info,
|
|
Alter_info *alter_info);
|
|
|
|
/*
|
|
mysql_create_table_no_lock can be called in one of the following
|
|
mutually exclusive situations:
|
|
|
|
- Just a normal ordinary CREATE TABLE statement that explicitly
|
|
defines the table structure.
|
|
|
|
- CREATE TABLE ... SELECT. It is special, because only in this case,
|
|
the list of fields is allowed to have duplicates, as long as one of the
|
|
duplicates comes from the select list, and the other doesn't. For
|
|
example in
|
|
|
|
CREATE TABLE t1 (a int(5) NOT NUL) SELECT b+10 as a FROM t2;
|
|
|
|
the list in alter_info->create_list will have two fields `a`.
|
|
|
|
- ALTER TABLE, that creates a temporary table #sql-xxx, which will be later
|
|
renamed to replace the original table.
|
|
|
|
- ALTER TABLE as above, but which only modifies the frm file, it only
|
|
creates an frm file for the #sql-xxx, the table in the engine is not
|
|
created.
|
|
|
|
- Assisted discovery, CREATE TABLE statement without the table structure.
|
|
|
|
These situations are distinguished by the following "create table mode"
|
|
values, where a CREATE ... SELECT is denoted by any non-negative number
|
|
(which should be the number of fields in the SELECT ... part), and other
|
|
cases use constants as defined below.
|
|
*/
|
|
#define C_CREATE_SELECT(X) ((X) > 0 ? (X) : 0)
|
|
#define C_ORDINARY_CREATE 0
|
|
#define C_ALTER_TABLE -1
|
|
#define C_ALTER_TABLE_FRM_ONLY -2
|
|
#define C_ASSISTED_DISCOVERY -3
|
|
|
|
int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db,
|
|
const LEX_CSTRING *table_name,
|
|
Table_specification_st *create_info,
|
|
Alter_info *alter_info, bool *is_trans,
|
|
int create_table_mode, TABLE_LIST *table);
|
|
|
|
handler *mysql_create_frm_image(THD *thd,
|
|
const LEX_CSTRING &db,
|
|
const LEX_CSTRING &table_name,
|
|
HA_CREATE_INFO *create_info,
|
|
Alter_info *alter_info,
|
|
int create_table_mode,
|
|
KEY **key_info,
|
|
uint *key_count,
|
|
LEX_CUSTRING *frm);
|
|
|
|
int mysql_discard_or_import_tablespace(THD *thd,
|
|
TABLE_LIST *table_list,
|
|
bool discard);
|
|
|
|
bool mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|
HA_CREATE_INFO *create_info,
|
|
Alter_info *alter_info,
|
|
Alter_table_ctx *alter_ctx);
|
|
bool mysql_trans_prepare_alter_copy_data(THD *thd);
|
|
bool mysql_trans_commit_alter_copy_data(THD *thd);
|
|
bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name,
|
|
HA_CREATE_INFO *create_info,
|
|
TABLE_LIST *table_list,
|
|
Alter_info *alter_info,
|
|
uint order_num, ORDER *order, bool ignore);
|
|
bool mysql_compare_tables(TABLE *table,
|
|
Alter_info *alter_info,
|
|
HA_CREATE_INFO *create_info,
|
|
bool *metadata_equal);
|
|
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy);
|
|
bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
|
|
TABLE_LIST *src_table,
|
|
Table_specification_st *create_info);
|
|
bool mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
|
|
const LEX_CSTRING *old_name, const LEX_CSTRING *new_db,
|
|
const LEX_CSTRING *new_name, uint flags);
|
|
|
|
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list);
|
|
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list);
|
|
|
|
bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list,
|
|
HA_CHECK_OPT* check_opt);
|
|
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
|
|
bool drop_temporary, bool drop_sequence);
|
|
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|
bool drop_temporary, bool drop_view,
|
|
bool drop_sequence,
|
|
bool log_query, bool dont_free_locks);
|
|
bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
|
|
const LEX_CSTRING *table_name, bool temporary_table);
|
|
bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
|
|
const LEX_CSTRING *table_name, uint flags,
|
|
const char *table_path=0);
|
|
void close_cached_table(THD *thd, TABLE *table);
|
|
void sp_prepare_create_field(THD *thd, Column_definition *sql_field);
|
|
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
|
|
int write_bin_log(THD *thd, bool clear_error,
|
|
char const *query, ulong query_length,
|
|
bool is_trans= FALSE);
|
|
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
|
|
DDL_LOG_MEMORY_ENTRY **active_entry);
|
|
bool write_execute_ddl_log_entry(uint first_entry,
|
|
bool complete,
|
|
DDL_LOG_MEMORY_ENTRY **active_entry);
|
|
bool deactivate_ddl_log_entry(uint entry_no);
|
|
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry);
|
|
bool sync_ddl_log();
|
|
void release_ddl_log();
|
|
void execute_ddl_log_recovery();
|
|
bool execute_ddl_log_entry(THD *thd, uint first_entry);
|
|
|
|
template<typename T> class List;
|
|
void promote_first_timestamp_column(List<Create_field> *column_definitions);
|
|
|
|
/*
|
|
These prototypes where under INNODB_COMPATIBILITY_HOOKS.
|
|
*/
|
|
uint explain_filename(THD* thd, const char *from, char *to, uint to_length,
|
|
enum_explain_filename_mode explain_mode);
|
|
|
|
|
|
extern MYSQL_PLUGIN_IMPORT const char *primary_key_name;
|
|
extern mysql_mutex_t LOCK_gdl;
|
|
|
|
bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *);
|
|
|
|
#endif /* SQL_TABLE_INCLUDED */
|