diff options
Diffstat (limited to 'sql/table.cc')
-rw-r--r-- | sql/table.cc | 1301 |
1 files changed, 1007 insertions, 294 deletions
diff --git a/sql/table.cc b/sql/table.cc index 1004f583448..cf5a95e0929 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2018, MariaDB + Copyright (c) 2008, 2020, MariaDB 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 @@ -17,7 +17,7 @@ /* Some general useful functions */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "table.h" #include "key.h" // find_ref_key @@ -42,6 +42,8 @@ #include "sql_view.h" #include "rpl_filter.h" #include "sql_cte.h" +#include "ha_sequence.h" +#include "sql_show.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -60,28 +62,34 @@ public: static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *, TABLE *, String *, Virtual_column_info **, bool *); -static bool check_vcol_forward_refs(Field *, Virtual_column_info *); +static bool check_vcol_forward_refs(Field *, Virtual_column_info *, + bool check_constraint); /* INFORMATION_SCHEMA name */ -LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; +LEX_CSTRING INFORMATION_SCHEMA_NAME= {STRING_WITH_LEN("information_schema")}; /* PERFORMANCE_SCHEMA name */ -LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")}; +LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME= {STRING_WITH_LEN("performance_schema")}; /* MYSQL_SCHEMA name */ -LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")}; +LEX_CSTRING MYSQL_SCHEMA_NAME= {STRING_WITH_LEN("mysql")}; /* GENERAL_LOG name */ -LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; +LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")}; /* SLOW_LOG name */ -LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")}; +LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")}; + +LEX_CSTRING TRANSACTION_REG_NAME= {STRING_WITH_LEN("transaction_registry")}; +LEX_CSTRING MYSQL_USER_NAME= {STRING_WITH_LEN("user")}; +LEX_CSTRING MYSQL_DB_NAME= {STRING_WITH_LEN("db")}; +LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")}; /* Keyword added as a prefix when parsing the defining expression for a virtual column read from the column definition saved in the frm file */ -static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; +static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; static int64 last_table_id; @@ -94,7 +102,7 @@ static bool fix_type_pointers(const char ***typelib_value_names, static uint find_field(Field **fields, uchar *record, uint start, uint length); -inline bool is_system_table_name(const char *name, uint length); +inline bool is_system_table_name(const char *name, size_t length); /************************************************************************** Object_creation_ctx implementation. @@ -173,8 +181,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_NO_CREATION_CTX, ER_THD(thd, ER_VIEW_NO_CREATION_CTX), - (const char *) view->db, - (const char *) view->table_name); + view->db.str, + view->table_name.str); ctx->m_client_cs= system_charset_info; ctx->m_connection_cl= system_charset_info; @@ -199,16 +207,16 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, { sql_print_warning("View '%s'.'%s': there is unknown charset/collation " "names (client: '%s'; connection: '%s').", - (const char *) view->db, - (const char *) view->table_name, + view->db.str, + view->table_name.str, (const char *) view->view_client_cs_name.str, (const char *) view->view_connection_cl_name.str); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_INVALID_CREATION_CTX, ER_THD(thd, ER_VIEW_INVALID_CREATION_CTX), - (const char *) view->db, - (const char *) view->table_name); + view->db.str, + view->table_name.str); } return ctx; @@ -221,8 +229,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, static uchar *get_field_name(Field **buff, size_t *length, my_bool not_used __attribute__((unused))) { - *length= (uint) strlen((*buff)->field_name); - return (uchar*) (*buff)->field_name; + *length= (uint) (*buff)->field_name.length; + return (uchar*) (*buff)->field_name.str; } @@ -230,63 +238,50 @@ static uchar *get_field_name(Field **buff, size_t *length, Returns pointer to '.frm' extension of the file name. SYNOPSIS - fn_rext() + fn_frm_ext() name file name DESCRIPTION Checks file name part starting with the rightmost '.' character, and returns it if it is equal to '.frm'. - TODO - It is a good idea to get rid of this function modifying the code - to garantee that the functions presently calling fn_rext() always - get arguments in the same format: either with '.frm' or without '.frm'. - RETURN VALUES - Pointer to the '.frm' extension. If there is no extension, - or extension is not '.frm', pointer at the end of file name. + Pointer to the '.frm' extension or NULL if not a .frm file */ -char *fn_rext(char *name) +const char *fn_frm_ext(const char *name) { - char *res= strrchr(name, '.'); + const char *res= strrchr(name, '.'); if (res && !strcmp(res, reg_ext)) return res; - return name + strlen(name); + return 0; } -TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) + +TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, + const LEX_CSTRING *name) { DBUG_ASSERT(db != NULL); DBUG_ASSERT(name != NULL); - if (is_infoschema_db(db->str, db->length)) + if (is_infoschema_db(db)) return TABLE_CATEGORY_INFORMATION; - if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) && - (my_strcasecmp(system_charset_info, - PERFORMANCE_SCHEMA_DB_NAME.str, - db->str) == 0)) + if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db)) return TABLE_CATEGORY_PERFORMANCE; - if ((db->length == MYSQL_SCHEMA_NAME.length) && - (my_strcasecmp(system_charset_info, - MYSQL_SCHEMA_NAME.str, - db->str) == 0)) + if (lex_string_eq(&MYSQL_SCHEMA_NAME, db)) { if (is_system_table_name(name->str, name->length)) return TABLE_CATEGORY_SYSTEM; - if ((name->length == GENERAL_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - GENERAL_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&GENERAL_LOG_NAME, name)) return TABLE_CATEGORY_LOG; - if ((name->length == SLOW_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - SLOW_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&SLOW_LOG_NAME, name)) + return TABLE_CATEGORY_LOG; + + if (lex_string_eq(&TRANSACTION_REG_NAME, name)) return TABLE_CATEGORY_LOG; } @@ -322,7 +317,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, path_length= build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); - init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); + init_sql_alloc(&mem_root, "table_share", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); if (multi_alloc_root(&mem_root, &share, sizeof(*share), &key_buff, key_length, @@ -335,15 +330,22 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, share->path.str= path_buff; share->path.length= path_length; - strmov(share->path.str, path); + strmov(path_buff, path); share->normalized_path.str= share->path.str; share->normalized_path.length= path_length; share->table_category= get_table_category(& share->db, & share->table_name); share->open_errno= ENOENT; - /* The following will be fixed in open_table_from_share */ - share->cached_row_logging_check= 1; - - init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); + /* The following will be updated in open_table_from_share */ + share->can_do_row_logging= 1; + if (share->table_category == TABLE_CATEGORY_LOG) + share->no_replicate= 1; + if (key_length > 6 && + my_strnncoll(table_alias_charset, (const uchar*) key, 6, + (const uchar*) "mysql", 6) == 0) + share->not_usable_by_query_cache= 1; + + init_sql_alloc(&share->stats_cb.mem_root, "share_stats", + TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); mysql_mutex_init(key_TABLE_SHARE_LOCK_share, @@ -404,8 +406,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, This can't be MY_THREAD_SPECIFIC for slaves as they are freed during cleanup() from Relay_log_info::close_temporary_tables() */ - init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, - MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC)); + init_sql_alloc(&share->mem_root, "tmp_table_share", TABLE_ALLOC_BLOCK_SIZE, + 0, MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC)); share->table_category= TABLE_CATEGORY_TEMPORARY; share->tmp_table= INTERNAL_TMP_TABLE; share->db.str= (char*) key; @@ -418,8 +420,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, share->normalized_path.str= (char*) path; share->path.length= share->normalized_path.length= strlen(path); share->frm_version= FRM_VER_CURRENT; - - share->cached_row_logging_check= 0; // No row logging + share->not_usable_by_query_cache= 1; + share->can_do_row_logging= 0; // No row logging /* table_map_id is also used for MERGE tables to suppress repeated @@ -450,6 +452,7 @@ void TABLE_SHARE::destroy() } delete_stat_values_for_table_share(this); + delete sequence; free_root(&stats_cb.mem_root, MYF(0)); /* The mutexes are initialized only for shares that are part of the TDC */ @@ -520,7 +523,7 @@ void free_table_share(TABLE_SHARE *share) and should not contain user tables. */ -inline bool is_system_table_name(const char *name, uint length) +inline bool is_system_table_name(const char *name, size_t length) { CHARSET_INFO *ci= system_charset_info; @@ -634,7 +637,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) share->is_view= 1; if (flags & GTS_VIEW) { - LEX_STRING pathstr= { path, length }; + LEX_CSTRING pathstr= { path, length }; /* Create view file parser and hold it in TABLE_SHARE member view_def. @@ -682,7 +685,11 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) frmlen= read_length + sizeof(head); share->init_from_binary_frm_image(thd, false, buf, frmlen); - error_given= true; // init_from_binary_frm_image has already called my_error() + /* + Don't give any additional errors. If there would be a problem, + init_from_binary_frm_image would call my_error() itself. + */ + error_given= true; my_free(buf); goto err_not_open; @@ -691,7 +698,10 @@ err: mysql_file_close(file, MYF(MY_WME)); err_not_open: - if (share->error && !error_given) + /* Mark that table was created earlier and thus should have been logged */ + share->table_creation_was_logged= 1; + + if (unlikely(share->error && !error_given)) { share->open_errno= my_errno; open_table_error(share, share->error, share->open_errno); @@ -1041,7 +1051,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, thd->stmt_arena= table->expr_arena; thd->update_charset(&my_charset_utf8mb4_general_ci, table->s->table_charset); expr_str.append(&parse_vcol_keyword); - thd->variables.sql_mode &= ~MODE_NO_BACKSLASH_ESCAPES; + thd->variables.sql_mode &= ~(MODE_NO_BACKSLASH_ESCAPES | MODE_EMPTY_STRING_IS_NULL); while (pos < end) { @@ -1154,9 +1164,9 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, for (field_ptr= table->field; *field_ptr; field_ptr++) { Field *field= *field_ptr; - if (check_vcol_forward_refs(field, field->vcol_info) || - check_vcol_forward_refs(field, field->check_constraint) || - check_vcol_forward_refs(field, field->default_value)) + if (check_vcol_forward_refs(field, field->vcol_info, 0) || + check_vcol_forward_refs(field, field->check_constraint, 1) || + check_vcol_forward_refs(field, field->default_value, 0)) goto end; } @@ -1204,7 +1214,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, const uchar *frm_image_end = frm_image + frm_length; uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos); const uchar *disk_buff, *strpos; - ulong pos, record_offset; + ulong pos, record_offset; ulong rec_buff_length; handler *handler_file= 0; KEY *keyinfo; @@ -1215,15 +1225,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum legacy_db_type legacy_db_type; my_bitmap_map *bitmaps; bool null_bits_are_used; - uint vcol_screen_length, UNINIT_VAR(options_len); + uint vcol_screen_length; + size_t UNINIT_VAR(options_len); uchar *vcol_screen_pos; const uchar *options= 0; - uint UNINIT_VAR(gis_options_len); + size_t UNINIT_VAR(gis_options_len); const uchar *gis_options= 0; KEY first_keyinfo; uint len; uint ext_key_parts= 0; plugin_ref se_plugin= 0; + const uchar *system_period= 0; + bool vers_can_native= false; + const uchar *extra2_field_flags= 0; + size_t extra2_field_flags_length= 0; + MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); @@ -1259,7 +1275,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (*extra2 != '/') // old frm had '/' there { const uchar *e2end= extra2 + len; - while (extra2 + 3 < e2end) + while (extra2 + 3 <= e2end) { uchar type= *extra2++; size_t length= *extra2++; @@ -1300,7 +1316,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, case EXTRA2_DEFAULT_PART_ENGINE: #ifdef WITH_PARTITION_STORAGE_ENGINE { - LEX_STRING name= { (char*)extra2, length }; + LEX_CSTRING name= { (char*)extra2, length }; share->default_part_plugin= ha_resolve_by_name(NULL, &name, false); if (!share->default_part_plugin) goto err; @@ -1317,6 +1333,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif /*HAVE_SPATIAL*/ break; + case EXTRA2_PERIOD_FOR_SYSTEM_TIME: + if (system_period || length != 2 * sizeof(uint16)) + goto err; + system_period = extra2; + break; + case EXTRA2_FIELD_FLAGS: + if (extra2_field_flags) + goto err; + extra2_field_flags= extra2; + extra2_field_flags_length= length; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1356,6 +1383,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->db_create_options= db_create_options= uint2korr(frm_image+30); share->db_options_in_use= share->db_create_options; share->mysql_version= uint4korr(frm_image+51); + share->table_type= TABLE_TYPE_NORMAL; share->null_field_first= 0; if (!frm_image[32]) // New frm file in 3.23 { @@ -1369,6 +1397,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX); share->page_checksum= (ha_choice) enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX); + if (((ha_choice) enum_value_with_check(thd, share, "sequence", + (frm_image[39] >> 4) & 3, + HA_CHOICE_MAX)) == HA_CHOICE_YES) + { + share->table_type= TABLE_TYPE_SEQUENCE; + share->sequence= new (&share->mem_root) SEQUENCE(); + share->non_determinstic_insert= true; + } share->row_type= (enum row_type) enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); @@ -1455,7 +1491,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (next_chunk + 2 < buff_end) { uint str_db_type_length= uint2korr(next_chunk); - LEX_STRING name; + LEX_CSTRING name; name.str= (char*) next_chunk + 2; name.length= str_db_type_length; @@ -1502,7 +1538,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, else if (!tmp_plugin) { /* purecov: begin inspected */ - name.str[name.length]=0; + ((char*) name.str)[name.length]=0; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); goto err; /* purecov: end */ @@ -1553,7 +1589,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { if (keyinfo->flags & HA_USES_PARSER) { - LEX_STRING parser_name; + LEX_CSTRING parser_name; if (next_chunk >= buff_end) { DBUG_PRINT("error", @@ -1620,14 +1656,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->rec_buff_length= rec_buff_length; if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length))) goto err; /* purecov: inspected */ - MEM_NOACCESS(record, rec_buff_length); - MEM_UNDEFINED(record, share->reclength); + /* Mark bytes after record as not accessable to catch overrun bugs */ + MEM_NOACCESS(record + share->reclength, rec_buff_length - share->reclength); share->default_values= record; memcpy(record, frm_image + record_offset, share->reclength); disk_buff= frm_image + pos + FRM_FORMINFO_SIZE; - share->fields= uint2korr(forminfo+258); + if (extra2_field_flags && extra2_field_flags_length != share->fields) + goto err; pos= uint2korr(forminfo+260); /* Length of all screens */ n_length= uint2korr(forminfo+268); interval_count= uint2korr(forminfo+270); @@ -1638,6 +1675,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_screen_length= uint2korr(forminfo+286); share->virtual_fields= share->default_expressions= share->field_check_constraints= share->default_fields= 0; + share->visible_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1770,6 +1808,28 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, strpos, vcol_screen_pos); } + /* Set system versioning information. */ + if (system_period == NULL) + { + versioned= VERS_UNDEFINED; + row_start_field= 0; + row_end_field= 0; + } + else + { + DBUG_PRINT("info", ("Setting system versioning informations")); + uint16 row_start= uint2korr(system_period); + uint16 row_end= uint2korr(system_period + sizeof(uint16)); + if (row_start >= share->fields || row_end >= share->fields) + goto err; + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + versioned= VERS_TIMESTAMP; + vers_can_native= handler_file->vers_can_native(thd); + row_start_field= row_start; + row_end_field= row_end; + status_var_increment(thd->status_var.feature_system_versioning); + } // if (system_period == NULL) + for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; @@ -1778,10 +1838,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_field_types field_type; CHARSET_INFO *charset=NULL; Field::geometry_type geom_type= Field::GEOM_GEOMETRY; - LEX_STRING comment; + LEX_CSTRING comment; + LEX_CSTRING name; Virtual_column_info *vcol_info= 0; uint gis_length, gis_decimals, srid= 0; Field::utype unireg_check; + const Type_handler *handler; + uint32 flags= 0; if (new_frm_ver >= 3) { @@ -1824,7 +1887,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, char tmp[10]; if (!csname || csname[0] =='?') { - my_snprintf(tmp, sizeof(tmp), "#%d", cs_new); + my_snprintf(tmp, sizeof(tmp), "#%u", cs_new); csname= tmp; } my_printf_error(ER_UNKNOWN_COLLATION, @@ -1993,14 +2056,47 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos); } + if (versioned) + { + if (i == row_start_field) + flags|= VERS_SYS_START_FLAG; + else if (i == row_end_field) + flags|= VERS_SYS_END_FLAG; + + if (flags & VERS_SYSTEM_FIELD) + { + switch (field_type) + { + case MYSQL_TYPE_TIMESTAMP2: + break; + case MYSQL_TYPE_LONGLONG: + if (vers_can_native) + { + versioned= VERS_TRX_ID; + break; + } + /* Fallthrough */ + default: + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), fieldnames.type_names[i], + versioned == VERS_TIMESTAMP ? "TIMESTAMP(6)" : "BIGINT(20) UNSIGNED", + table_name.str); + goto err; + } + } + } + /* Convert pre-10.2.2 timestamps to use Field::default_value */ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type); + name.str= fieldnames.type_names[i]; + name.length= strlen(name.str); + if (!(handler= Type_handler::get_handler_by_real_type(field_type))) + goto err; // Not supported field type *field_ptr= reg_field= make_field(share, &share->mem_root, record+recpos, (uint32) field_length, - null_pos, null_bit_pos, pack_flag, field_type, charset, + null_pos, null_bit_pos, pack_flag, handler, charset, geom_type, srid, unireg_check, (interval_nr ? share->intervals+interval_nr-1 : NULL), - share->fieldnames.type_names[i]); + &name, flags); if (!reg_field) // Not supported field type goto err; @@ -2016,6 +2112,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, reg_field->field_index= i; reg_field->comment=comment; reg_field->vcol_info= vcol_info; + reg_field->flags|= flags; + if (extra2_field_flags) + { + uchar flags= *extra2_field_flags++; + if (flags & VERS_OPTIMIZED_UPDATE) + reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG; + + reg_field->invisible= f_visibility(flags); + } + if (reg_field->invisible == INVISIBLE_USER) + status_var_increment(thd->status_var.feature_invisible_columns); + if (!reg_field->invisible) + share->visible_fields++; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { null_bits_are_used= 1; @@ -2033,8 +2142,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (vcol_info) { - vcol_info->name.str= const_cast<char*>(reg_field->field_name); - vcol_info->name.length = strlen(reg_field->field_name); + vcol_info->name= reg_field->field_name; if (mysql57_null_bits && !vcol_info->stored_in_db) { /* MySQL 5.7 has null bits last */ @@ -2152,18 +2260,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, for (uint key=0 ; key < keys ; key++,keyinfo++) { uint usable_parts= 0; - keyinfo->name=(char*) share->keynames.type_names[key]; - keyinfo->name_length= strlen(keyinfo->name); + keyinfo->name.str= share->keynames.type_names[key]; + keyinfo->name.length= strlen(keyinfo->name.str); keyinfo->cache_name= (uchar*) alloc_root(&share->mem_root, share->table_cache_key.length+ - keyinfo->name_length + 1); + keyinfo->name.length + 1); if (keyinfo->cache_name) // If not out of memory { uchar *pos= keyinfo->cache_name; memcpy(pos, share->table_cache_key.str, share->table_cache_key.length); - memcpy(pos + share->table_cache_key.length, keyinfo->name, - keyinfo->name_length+1); + memcpy(pos + share->table_cache_key.length, keyinfo->name.str, + keyinfo->name.length+1); } if (ext_key_parts > share->key_parts && key) @@ -2266,6 +2374,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); + if (field->invisible > INVISIBLE_USER && !field->vers_sys_field()) + keyinfo->flags |= HA_INVISIBLE_KEY; if (field->null_ptr) { key_part->null_offset=(uint) ((uchar*) field->null_ptr - @@ -2378,7 +2488,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART | HA_BIT_PART)) && key_part->type != HA_KEYTYPE_FLOAT && - key_part->type == HA_KEYTYPE_DOUBLE) + key_part->type != HA_KEYTYPE_DOUBLE) key_part->key_part_flag|= HA_CAN_MEMCMP; } keyinfo->usable_key_parts= usable_parts; // Filesort @@ -2465,15 +2575,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE; vcol_info->set_vcol_type((enum_vcol_info_type) type); - vcol_info->name.length= name_length; if (name_length) + { vcol_info->name.str= strmake_root(&share->mem_root, (char*)vcol_screen_pos, name_length); - else - { - vcol_info->name.str= const_cast<char*>(reg_field->field_name); - vcol_info->name.length = strlen(reg_field->field_name); + vcol_info->name.length= name_length; } + else + vcol_info->name= reg_field->field_name; vcol_screen_pos+= name_length + expr_length; switch (type) { @@ -2597,19 +2706,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, bitmap_clear_all(share->check_set); } - delete handler_file; #ifndef DBUG_OFF if (use_hash) (void) my_hash_check(&share->name_hash); #endif share->db_plugin= se_plugin; + delete handler_file; + share->error= OPEN_FRM_OK; thd->status_var.opened_shares++; thd->mem_root= old_root; DBUG_RETURN(0); - err: +err: + share->db_plugin= NULL; share->error= OPEN_FRM_CORRUPTED; share->open_errno= my_errno; delete handler_file; @@ -2631,7 +2742,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, HA_CREATE_INFO *create_info= &lex->create_info; // ... not CREATE TABLE - if (lex->sql_command != SQLCOM_CREATE_TABLE) + if (lex->sql_command != SQLCOM_CREATE_TABLE && + lex->sql_command != SQLCOM_CREATE_SEQUENCE) return 1; // ... create like if (lex->create_info.like()) @@ -2671,6 +2783,9 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, (create_info->db_type && create_info->db_type != engine)) return 1; } + // ... WITH SYSTEM VERSIONING + if (create_info->versioned()) + return 1; return 0; } @@ -2691,7 +2806,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, uint unused2; handlerton *hton= plugin_hton(db_plugin); LEX_CUSTRING frm= {0,0}; - LEX_STRING db_backup= { thd->db, thd->db_length }; + LEX_CSTRING db_backup= thd->db; DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string"); /* @@ -2718,11 +2833,11 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, else thd->set_n_backup_active_arena(arena, &backup); - thd->reset_db(db.str, db.length); + thd->reset_db(&db); lex_start(thd); - if ((error= parse_sql(thd, & parser_state, NULL) || - sql_unusable_for_discovery(thd, hton, sql_copy))) + if (unlikely((error= parse_sql(thd, & parser_state, NULL) || + sql_unusable_for_discovery(thd, hton, sql_copy)))) goto ret; thd->lex->create_info.db_type= hton; @@ -2734,7 +2849,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, thd->lex->create_info.tabledef_version= tabledef_version; promote_first_timestamp_column(&thd->lex->alter_info.create_list); - file= mysql_create_frm_image(thd, db.str, table_name.str, + file= mysql_create_frm_image(thd, &db, &table_name, &thd->lex->create_info, &thd->lex->alter_info, C_ORDINARY_CREATE, &unused1, &unused2, &frm); error|= file == 0; @@ -2750,21 +2865,22 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, ret: my_free(const_cast<uchar*>(frm.str)); lex_end(thd->lex); - thd->reset_db(db_backup.str, db_backup.length); + thd->reset_db(&db_backup); thd->lex= old_lex; if (arena) thd->restore_active_arena(arena, &backup); reenable_binlog(thd); thd->variables.sql_mode= saved_mode; thd->variables.character_set_client= old_cs; - if (thd->is_error() || error) + if (unlikely(thd->is_error() || error)) { thd->clear_error(); - my_error(ER_SQL_DISCOVER_ERROR, MYF(0), - plugin_name(db_plugin)->str, db.str, table_name.str, - sql_copy); + my_error(ER_SQL_DISCOVER_ERROR, MYF(0), hton_name(hton)->str, + db.str, table_name.str, sql_copy); DBUG_RETURN(HA_ERR_GENERIC); } + /* Treat the table as normal table from binary logging point of view */ + table_creation_was_logged= 1; DBUG_RETURN(0); } @@ -2805,12 +2921,12 @@ static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol) { DBUG_ENTER("fix_vcol_expr"); - const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns; - thd->mark_used_columns= MARK_COLUMNS_NONE; + const enum enum_column_usage saved_column_usage= thd->column_usage; + thd->column_usage= COLUMNS_WRITE; int error= vcol->expr->fix_fields(thd, &vcol->expr); - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; if (unlikely(error)) { @@ -2917,14 +3033,14 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, res.errors= 0; int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res); - if (error || (res.errors & VCOL_IMPOSSIBLE)) + if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE))) { // this can only happen if the frm was corrupted my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, vcol->get_vcol_type_name(), vcol->name.str); DBUG_RETURN(1); } - else if (res.errors & VCOL_AUTO_INC) + else if (unlikely(res.errors & VCOL_AUTO_INC)) { /* An auto_increment field may not be used in an expression for @@ -3005,9 +3121,17 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table, lex.last_field= &vcol_storage; error= parse_sql(thd, &parser_state, NULL); - if (error) + if (unlikely(error)) goto end; + if (lex.current_select->table_list.first[0].next_global) + { + /* We are using NEXT VALUE FOR sequence. Remember table name for open */ + TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global; + sequence->next_global= table->internal_tables; + table->internal_tables= sequence; + } + vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type()); vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; vcol_storage.vcol_info->name= vcol->name; @@ -3026,11 +3150,19 @@ end: DBUG_RETURN(vcol_info); } -static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) +static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol, + bool check_constraint) { - bool res= vcol && - vcol->expr->walk(&Item::check_field_expression_processor, 0, - field); + bool res; + uint32 flags= field->flags; + if (check_constraint) + { + /* Check constraints can refer it itself */ + field->flags|= NO_DEFAULT_VALUE_FLAG; + } + res= (vcol && + vcol->expr->walk(&Item::check_field_expression_processor, 0, field)); + field->flags= flags; return res; } @@ -3048,6 +3180,7 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) prgflag READ_ALL etc.. ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. outparam result table + partitions_to_open open only these partitions. RETURN VALUES 0 ok @@ -3060,13 +3193,12 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) */ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, - const char *alias, uint db_stat, uint prgflag, + const LEX_CSTRING *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, - bool is_create_table) + bool is_create_table, List<String> *partitions_to_open) { enum open_frm_error error; uint records, i, bitmap_size, bitmap_count; - size_t tmp_length; const char *tmp_alias; bool error_reported= FALSE; uchar *record, *bitmaps; @@ -3093,17 +3225,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, error= OPEN_FRM_NEEDS_REBUILD; goto err; } - init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); + init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0, + MYF(0)); /* We have to store the original alias in mem_root as constraints and virtual functions may store pointers to it */ - tmp_length= strlen(alias); - if (!(tmp_alias= strmake_root(&outparam->mem_root, alias, tmp_length))) + if (!(tmp_alias= strmake_root(&outparam->mem_root, alias->str, alias->length))) goto err; - outparam->alias.set(tmp_alias, tmp_length, table_alias_charset); + outparam->alias.set(tmp_alias, alias->length, table_alias_charset); outparam->quick_keys.init(); outparam->covering_keys.init(); outparam->intersect_keys.init(); @@ -3125,34 +3257,51 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!db_stat); } + if (share->sequence && outparam->file) + { + ha_sequence *file; + /* SEQUENCE table. Create a sequence handler over the original handler */ + if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share, + &outparam->mem_root))) + goto err; + file->register_original_handler(outparam->file); + outparam->file= file; + } + outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock= F_UNLCK; records=0; if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN)) records=1; - if (prgflag & (READ_ALL+EXTRA_RECORD)) + if (prgflag & (READ_ALL + EXTRA_RECORD)) + { records++; - - if (!(record= (uchar*) alloc_root(&outparam->mem_root, - share->rec_buff_length * records))) - goto err; /* purecov: inspected */ - MEM_NOACCESS(record, share->rec_buff_length * records); + if (share->versioned) + records++; + } if (records == 0) { /* We are probably in hard repair, and the buffers should not be used */ - outparam->record[0]= outparam->record[1]= share->default_values; + record= share->default_values; } else { - outparam->record[0]= record; - if (records > 1) - outparam->record[1]= record+ share->rec_buff_length; - else - outparam->record[1]= outparam->record[0]; // Safety + if (!(record= (uchar*) alloc_root(&outparam->mem_root, + share->rec_buff_length * records))) + goto err; /* purecov: inspected */ } - MEM_UNDEFINED(outparam->record[0], share->reclength); - MEM_UNDEFINED(outparam->record[1], share->reclength); + + for (i= 0; i < 3;) + { + outparam->record[i]= record; + if (++i < records) + record+= share->rec_buff_length; + } + /* Mark bytes between records as not accessable to catch overrun bugs */ + for (i= 0; i < records; i++) + MEM_NOACCESS(outparam->record[i] + share->reclength, + share->rec_buff_length - share->reclength); if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root, (uint) ((share->fields+1)* @@ -3178,6 +3327,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DEBUG_SYNC(thd, "TABLE_after_field_clone"); + outparam->vers_write= share->versioned; + if (share->found_next_number_field) outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; @@ -3275,6 +3426,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, break; } #endif + if (parse_vcol_defs(thd, &outparam->mem_root, outparam, &error_reported, mode)) { @@ -3287,6 +3439,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } #ifdef WITH_PARTITION_STORAGE_ENGINE + bool work_part_info_used; if (share->partition_info_str_len && outparam->file) { /* @@ -3307,7 +3460,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); thd->stmt_arena= &part_func_arena; bool tmp; - bool work_part_info_used; tmp= mysql_unpack_partition(thd, share->partition_info_str, share->partition_info_str_len, @@ -3421,14 +3573,15 @@ partititon_err: int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str, (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), - ha_open_flags); + ha_open_flags, 0, partitions_to_open); if (ha_err) { share->open_errno= ha_err; /* Set a flag if the table is crashed and it can be auto. repaired */ share->crashed= (outparam->file->auto_repair(ha_err) && !(ha_open_flags & HA_OPEN_FOR_REPAIR)); - outparam->file->print_error(ha_err, MYF(0)); + if (!thd->is_error()) + outparam->file->print_error(ha_err, MYF(0)); error_reported= TRUE; if (ha_err == HA_ERR_TABLE_DEF_CHANGED) @@ -3462,24 +3615,20 @@ partititon_err: } } - if (share->table_category == TABLE_CATEGORY_LOG) - { - outparam->no_replicate= TRUE; - } - else if (outparam->file) + if (db_stat) { + /* Set some flags in share on first open of the table */ handler::Table_flags flags= outparam->file->ha_table_flags(); - outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE - | HA_BINLOG_ROW_CAPABLE)) - || MY_TEST(flags & HA_HAS_OWN_BINLOGGING); - } - else - { - outparam->no_replicate= FALSE; + if (! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE | + HA_BINLOG_ROW_CAPABLE)) || + MY_TEST(flags & HA_HAS_OWN_BINLOGGING)) + share->no_replicate= TRUE; + if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE) + share->not_usable_by_query_cache= TRUE; } - if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str)) - outparam->s->cached_row_logging_check= 0; // No row based replication + if (share->no_replicate || !binlog_filter->db_ok(share->db.str)) + share->can_do_row_logging= 0; // No row based replication /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) @@ -3735,30 +3884,6 @@ fix_type_pointers(const char ***typelib_value_names, } /* fix_type_pointers */ -TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings) -{ - TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); - if (!result) - return 0; - result->count=strings.elements; - result->name=""; - uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1); - if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes))) - return 0; - result->type_lengths= (uint*) (result->type_names + result->count + 1); - List_iterator<String> it(strings); - String *tmp; - for (uint i=0; (tmp=it++) ; i++) - { - result->type_names[i]= tmp->ptr(); - result->type_lengths[i]= tmp->length(); - } - result->type_names[result->count]= 0; // End marker - result->type_lengths[result->count]= 0; - return result; -} - - /* Search after a field with given start & length If an exact field isn't found, return longest field with starts @@ -3807,24 +3932,13 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length) May fail with some multibyte charsets though. */ -void append_unescaped(String *res, const char *pos, uint length) +void append_unescaped(String *res, const char *pos, size_t length) { const char *end= pos+length; res->append('\''); for (; pos != end ; pos++) { -#if defined(USE_MB) && MYSQL_VERSION_ID < 40100 - uint mblen; - if (use_mb(default_charset_info) && - (mblen= my_ismbchar(default_charset_info, pos, end))) - { - res->append(pos, mblen); - pos+= mblen; - continue; - } -#endif - switch (*pos) { case 0: /* Must be escaped for 'mysql' */ res->append('\\'); @@ -3858,7 +3972,7 @@ void append_unescaped(String *res, const char *pos, uint length) void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys, KEY *key_info) { - ulong key_comment_total_bytes= 0; + size_t key_comment_total_bytes= 0; uint i; DBUG_ENTER("prepare_frm_header"); @@ -3868,7 +3982,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, if (create_info->min_rows > UINT_MAX32) create_info->min_rows= UINT_MAX32; - uint key_length, tmp_key_length, tmp, csid; + size_t key_length, tmp_key_length, tmp, csid; bzero((char*) fileinfo, FRM_HEADER_SIZE); /* header */ fileinfo[0]=(uchar) 254; @@ -3920,7 +4034,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, create_info->default_table_charset->number : 0); fileinfo[38]= (uchar) csid; fileinfo[39]= (uchar) ((uint) create_info->transactional | - ((uint) create_info->page_checksum << 2)); + ((uint) create_info->page_checksum << 2) | + ((create_info->sequence ? HA_CHOICE_YES : 0) << 4)); fileinfo[40]= (uchar) create_info->row_type; /* Bytes 41-46 were for RAID support; now reused for other purposes */ fileinfo[41]= (uchar) (csid >> 8); @@ -3958,6 +4073,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; create_info->option_list= share->option_list; + create_info->sequence= MY_TEST(share->sequence); DBUG_VOID_RETURN; } @@ -4093,7 +4209,7 @@ bool ok_for_lower_case_names(const char *name) bool check_db_name(LEX_STRING *org_name) { char *name= org_name->str; - uint name_length= org_name->length; + size_t name_length= org_name->length; bool check_for_path_chars; if ((check_for_path_chars= check_mysql50_prefix(name))) @@ -4130,7 +4246,6 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars size_t name_length= 0; const char *end= name+length; - if (!check_for_path_chars && (check_for_path_chars= check_mysql50_prefix(name))) { @@ -4282,7 +4397,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) { Field *field= table->field[i]; - if (strncmp(field->field_name, field_def->name.str, + if (strncmp(field->field_name.str, field_def->name.str, field_def->name.length)) { /* @@ -4294,7 +4409,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) "expected column '%s' at position %d, found '%s'.", table->s->db.str, table->alias.c_ptr(), field_def->name.str, i, - field->field_name); + field->field_name.str); } field->sql_type(sql_type); /* @@ -4400,7 +4515,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) } } - if (! error) + if (likely(! error)) table->s->table_field_def_cache= table_def; end: @@ -4628,10 +4743,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) if (thd->lex->need_correct_ident()) alias_name_used= my_strcasecmp(table_alias_charset, s->table_name.str, - tl->alias); + tl->alias.str); /* Fix alias if table name changes. */ - if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias)) - alias.copy(tl->alias, strlen(tl->alias), alias.charset()); + if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias.str)) + alias.copy(tl->alias.str, tl->alias.length, alias.charset()); tablenr= thd->current_tablenr++; used_fields= 0; @@ -4649,7 +4764,9 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) created= TRUE; cond_selectivity= 1.0; cond_selectivity_sampling_explain= NULL; + vers_write= s->versioned; quick_condition_rows=0; + no_cache= false; initialize_quick_structures(); #ifdef HAVE_REPLICATION /* used in RBR Triggers */ @@ -4725,10 +4842,13 @@ bool TABLE::fill_item_list(List<Item> *item_list) const is the same as the number of columns in the table. */ -void TABLE::reset_item_list(List<Item> *item_list) const +void TABLE::reset_item_list(List<Item> *item_list, uint skip) const { List_iterator_fast<Item> it(*item_list); - for (Field **ptr= field; *ptr; ptr++) + Field **ptr= field; + for ( ; skip && *ptr; skip--) + ptr++; + for (; *ptr; ptr++) { Item_field *item_field= (Item_field*) it++; DBUG_ASSERT(item_field != 0); @@ -4744,7 +4864,7 @@ void TABLE::reset_item_list(List<Item> *item_list) const buffer buffer for md5 writing */ -void TABLE_LIST::calc_md5(char *buffer) +void TABLE_LIST::calc_md5(const char *buffer) { uchar digest[16]; compute_md5_hash(digest, select_stmt.str, @@ -4782,7 +4902,7 @@ bool TABLE_LIST::create_field_translation(THD *thd) bool res= FALSE; DBUG_ENTER("TABLE_LIST::create_field_translation"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (alias ? alias : "<NULL>"), + (alias.str ? alias.str : "<NULL>"), get_unit())); if (thd->stmt_arena->is_conventional() || @@ -4810,16 +4930,20 @@ bool TABLE_LIST::create_field_translation(THD *thd) */ if (is_view() && get_unit()->prepared && !field_translation_updated) { + field_translation_updated= TRUE; + if (static_cast<uint>(field_translation_end - field_translation) < + select->item_list.elements) + goto allocate; while ((item= it++)) { field_translation[field_count++].item= item; } - field_translation_updated= TRUE; } DBUG_RETURN(FALSE); } +allocate: arena= thd->activate_stmt_arena_if_needed(&backup); /* Create view fields translation table */ @@ -4835,8 +4959,9 @@ bool TABLE_LIST::create_field_translation(THD *thd) while ((item= it++)) { - DBUG_ASSERT(item->name && item->name[0]); - transl[field_count].name= thd->strdup(item->name); + DBUG_ASSERT(item->name.str && item->name.str[0]); + transl[field_count].name.str= thd->strmake(item->name.str, item->name.length); + transl[field_count].name.length= item->name.length; transl[field_count++].item= item; } field_translation= transl; @@ -4928,10 +5053,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, { if (where->fixed) where->update_used_tables(); - if (!where->fixed && where->fix_fields(thd, &where)) - { + else if (where->fix_fields(thd, &where)) DBUG_RETURN(TRUE); - } /* check that it is not VIEW in which we insert with INSERT SELECT @@ -4961,12 +5084,12 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, } if (tbl == 0) { - if (*conds && !(*conds)->fixed) - res= (*conds)->fix_fields(thd, conds); + if (*conds) + res= (*conds)->fix_fields_if_needed_for_bool(thd, conds); if (!res) *conds= and_conds(thd, *conds, where->copy_andor_structure(thd)); - if (*conds && !(*conds)->fixed && !res) - res= (*conds)->fix_fields(thd, conds); + if (*conds && !res) + res= (*conds)->fix_fields_if_needed_for_bool(thd, conds); } if (arena) thd->restore_active_arena(arena, &backup); @@ -5026,7 +5149,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) DBUG_ENTER("merge_on_conds"); Item *cond= NULL; - DBUG_PRINT("info", ("alias: %s", table->alias)); + DBUG_PRINT("info", ("alias: %s", table->alias.str)); if (table->on_expr) cond= table->on_expr->copy_andor_structure(thd); if (!table->view) @@ -5113,12 +5236,8 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) { const char *save_where= thd->where; thd->where= "check option"; - if ((!check_option->fixed && - check_option->fix_fields(thd, &check_option)) || - check_option->check_cols(1)) - { + if (check_option->fix_fields_if_needed_for_bool(thd, &check_option)) DBUG_RETURN(TRUE); - } thd->where= save_where; } DBUG_RETURN(FALSE); @@ -5254,9 +5373,9 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) { TABLE_LIST *main_view= top_table(); const char *name_db= (main_view->view ? main_view->view_db.str : - main_view->db); + main_view->db.str); const char *name_table= (main_view->view ? main_view->view_name.str : - main_view->table_name); + main_view->table_name.str); my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0), name_db, name_table); return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR; @@ -5279,6 +5398,8 @@ int TABLE::verify_constraints(bool ignore_failure) if (check_constraints && !(in_use->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)) { + if (versioned() && !vers_end_field()->is_max()) + return VIEW_CHECK_OK; for (Virtual_column_info **chk= check_constraints ; *chk ; chk++) { /* @@ -5312,7 +5433,6 @@ int TABLE::verify_constraints(bool ignore_failure) return(!in_use->is_error() ? VIEW_CHECK_OK : VIEW_CHECK_ERROR); } - /* Find table in underlying tables by mask and check that only this table belong to given mask @@ -5582,7 +5702,7 @@ void TABLE_LIST::register_want_access(ulong want_access) bool TABLE_LIST::prepare_view_security_context(THD *thd) { DBUG_ENTER("TABLE_LIST::prepare_view_security_context"); - DBUG_PRINT("enter", ("table: %s", alias)); + DBUG_PRINT("enter", ("table: %s", alias.str)); DBUG_ASSERT(!prelocking_placeholder && view); if (view_suid) @@ -5590,7 +5710,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd) DBUG_PRINT("info", ("This table is suid view => load contest")); DBUG_ASSERT(view && view_sctx); if (acl_getroot(view_sctx, definer.user.str, definer.host.str, - definer.host.str, thd->db)) + definer.host.str, thd->db.str)) { if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) || (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) @@ -5655,7 +5775,7 @@ Security_context *TABLE_LIST::find_view_security_context(THD *thd) if (upper_view) { DBUG_PRINT("info", ("Securety context of view %s will be used", - upper_view->alias)); + upper_view->alias.str)); sctx= upper_view->view_sctx; DBUG_ASSERT(sctx); } @@ -5696,7 +5816,7 @@ bool TABLE_LIST::prepare_security(THD *thd) while ((tbl= tb++)) { DBUG_ASSERT(tbl->referencing_view); - char *local_db, *local_table_name; + const char *local_db, *local_table_name; if (tbl->view) { local_db= tbl->view_db.str; @@ -5704,8 +5824,8 @@ bool TABLE_LIST::prepare_security(THD *thd) } else { - local_db= tbl->db; - local_table_name= tbl->table_name; + local_db= tbl->db.str; + local_table_name= tbl->table_name.str; } fill_effective_table_privileges(thd, &tbl->grant, local_db, local_table_name); @@ -5821,15 +5941,15 @@ Natural_join_column::Natural_join_column(Item_field *field_param, } -const char *Natural_join_column::name() +LEX_CSTRING *Natural_join_column::name() { if (view_field) { DBUG_ASSERT(table_field == NULL); - return view_field->name; + return &view_field->name; } - return table_field->field_name; + return &table_field->field_name; } @@ -5839,7 +5959,7 @@ Item *Natural_join_column::create_item(THD *thd) { DBUG_ASSERT(table_field == NULL); return create_view_field(thd, table_ref, &view_field->item, - view_field->name); + &view_field->name); } return table_field; } @@ -5856,30 +5976,29 @@ Field *Natural_join_column::field() } -const char *Natural_join_column::table_name() +const char *Natural_join_column::safe_table_name() { DBUG_ASSERT(table_ref); - return table_ref->alias; + return table_ref->alias.str ? table_ref->alias.str : ""; } -const char *Natural_join_column::db_name() +const char *Natural_join_column::safe_db_name() { if (view_field) - return table_ref->view_db.str; + return table_ref->view_db.str ? table_ref->view_db.str : ""; /* Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ - DBUG_ASSERT(!strcmp(table_ref->db, - table_ref->table->s->db.str) || + DBUG_ASSERT(!cmp(&table_ref->db, + &table_ref->table->s->db) || (table_ref->schema_table && - is_infoschema_db(table_ref->table->s->db.str, - table_ref->table->s->db.length)) || - table_ref->is_materialized_derived()); - return table_ref->db; + is_infoschema_db(&table_ref->table->s->db)) || + table_ref->is_materialized_derived()); + return table_ref->db.str ? table_ref->db.str : ""; } @@ -5908,9 +6027,9 @@ void Field_iterator_view::set(TABLE_LIST *table) } -const char *Field_iterator_table::name() +LEX_CSTRING *Field_iterator_table::name() { - return (*ptr)->field_name; + return &(*ptr)->field_name; } @@ -5919,6 +6038,7 @@ Item *Field_iterator_table::create_item(THD *thd) SELECT_LEX *select= thd->lex->current_select; Item_field *item= new (thd->mem_root) Item_field(thd, &select->context, *ptr); + DBUG_ASSERT(strlen(item->name.str) == item->name.length); if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS && select->join) @@ -5931,19 +6051,19 @@ Item *Field_iterator_table::create_item(THD *thd) } -const char *Field_iterator_view::name() +LEX_CSTRING *Field_iterator_view::name() { - return ptr->name; + return &ptr->name; } Item *Field_iterator_view::create_item(THD *thd) { - return create_view_field(thd, view, &ptr->item, ptr->name); + return create_view_field(thd, view, &ptr->item, &ptr->name); } Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, - const char *name) + LEX_CSTRING *name) { bool save_wrapper= thd->lex->select_lex.no_wrap_view_item; Item *field= *field_ref; @@ -5979,7 +6099,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, Name_resolution_context *context= view->view ? &view->view->select_lex.context : &thd->lex->select_lex.context; Item *item= (new (thd->mem_root) - Item_direct_view_ref(thd, context, field_ref, view->alias, + Item_direct_view_ref(thd, context, field_ref, view->alias.str, name, view)); if (!item) return NULL; @@ -6045,7 +6165,7 @@ void Field_iterator_table_ref::set_field_iterator() table_ref->table->s->fields)))); field_it= &natural_join_it; DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join", - table_ref->alias)); + table_ref->alias.str)); } /* This is a merge view, so use field_translation. */ else if (table_ref->field_translation) @@ -6053,7 +6173,7 @@ void Field_iterator_table_ref::set_field_iterator() DBUG_ASSERT(table_ref->is_merged_derived()); field_it= &view_field_it; DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view", - table_ref->alias)); + table_ref->alias.str)); } /* This is a base table or stored view. */ else @@ -6061,7 +6181,7 @@ void Field_iterator_table_ref::set_field_iterator() DBUG_ASSERT(table_ref->table || table_ref->view); field_it= &table_field_it; DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table", - table_ref->alias)); + table_ref->alias.str)); } field_it->set(table_ref); DBUG_VOID_RETURN; @@ -6103,12 +6223,12 @@ const char *Field_iterator_table_ref::get_table_name() if (table_ref->is_derived()) return table_ref->table->s->table_name.str; else if (table_ref->is_natural_join) - return natural_join_it.column_ref()->table_name(); + return natural_join_it.column_ref()->safe_table_name(); - DBUG_ASSERT(!strcmp(table_ref->table_name, + DBUG_ASSERT(!strcmp(table_ref->table_name.str, table_ref->table->s->table_name.str) || table_ref->schema_table); - return table_ref->table_name; + return table_ref->table_name.str; } @@ -6117,19 +6237,18 @@ const char *Field_iterator_table_ref::get_db_name() if (table_ref->view) return table_ref->view_db.str; else if (table_ref->is_natural_join) - return natural_join_it.column_ref()->db_name(); + return natural_join_it.column_ref()->safe_db_name(); /* Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ - DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) || + DBUG_ASSERT(!cmp(&table_ref->db, &table_ref->table->s->db) || (table_ref->schema_table && - is_infoschema_db(table_ref->table->s->db.str, - table_ref->table->s->db.length))); + is_infoschema_db(&table_ref->table->s->db))); - return table_ref->db; + return table_ref->db.str; } @@ -6480,9 +6599,12 @@ void TABLE::mark_columns_needed_for_delete() need_signal= true; } } - if (check_constraints) + + if (s->versioned) { - mark_check_constraint_columns_for_read(); + bitmap_set_bit(read_set, s->vers_start_field()->field_index); + bitmap_set_bit(read_set, s->vers_end_field()->field_index); + bitmap_set_bit(write_set, s->vers_end_field()->field_index); need_signal= true; } @@ -6500,7 +6622,7 @@ void TABLE::mark_columns_needed_for_delete() updated columns to be read. If this is no the case, we do like in the delete case and mark - if neeed, either the primary key column or all columns to be read. + if needed, either the primary key column or all columns to be read. (see mark_columns_needed_for_delete() for details) If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will @@ -6528,7 +6650,7 @@ void TABLE::mark_columns_needed_for_update() for (KEY *k= key_info; k < end; k++) { KEY_PART_INFO *kpend= k->key_part + k->ext_key_parts; - bool any_written= false, all_read= true; + int any_written= 0, all_read= 1; for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++) { int idx= kp->fieldnr - 1; @@ -6570,6 +6692,15 @@ void TABLE::mark_columns_needed_for_update() need_signal= true; } } + if (s->versioned) + { + /* + For System Versioning we have to read all columns since we store + a copy of previous row with modified row_end back to a table. + */ + bitmap_union(read_set, &s->all_set); + need_signal= true; + } if (check_constraints) { mark_check_constraint_columns_for_read(); @@ -6730,8 +6861,16 @@ void TABLE::mark_columns_per_binlog_row_image() binary log will include all columns read anyway. */ mark_columns_used_by_index_no_reset(s->primary_key, read_set); - /* Only write columns that have changed */ - rpl_write_set= write_set; + if (versioned()) + { + // TODO: After MDEV-18432 we don't pass history rows, so remove this: + rpl_write_set= &s->all_set; + } + else + { + /* Only write columns that have changed */ + rpl_write_set= write_set; + } break; default: @@ -6837,6 +6976,58 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl DBUG_RETURN(bitmap_updated); } + +/** + Check if a virtual not stored column field is in read set + + @retval FALSE No virtual not stored column is used + @retval TRUE At least one virtual not stored column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_read() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(read_set, tmp_vfield->field_index) && + !tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + +/** + Check if a stored virtual column field is marked for write + + This can be used to check if any column that is part of a virtual + stored column is changed + + @retval FALSE No stored virtual column is used + @retval TRUE At least one stored virtual column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_write() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(write_set, tmp_vfield->field_index) && + tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + /* Mark fields used by check constraints into s->check_set. Mark all fields used in an expression that is part of an index @@ -6935,6 +7126,7 @@ void TABLE::mark_default_fields_for_write(bool is_insert) DBUG_VOID_RETURN; } + void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from) { my_ptrdiff_t diff= to - from; @@ -7196,7 +7388,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, if (unique) keyinfo->flags|= HA_NOSAME; sprintf(buf, "key%i", key); - if (!(keyinfo->name= strdup_root(&mem_root, buf))) + keyinfo->name.length= strlen(buf); + if (!(keyinfo->name.str= strmake_root(&mem_root, buf, keyinfo->name.length))) return TRUE; keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root, sizeof(ulong)*key_parts); @@ -7220,6 +7413,26 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, key_part_info++; } + /* + For the case when there is a derived table that would give distinct rows, + the index statistics are passed to the join optimizer to tell that a ref + access to all the fields of the derived table will produce only one row. + */ + + st_select_lex_unit* derived= pos_in_table_list ? + pos_in_table_list->derived: NULL; + if (derived) + { + st_select_lex* first= derived->first_select(); + uint select_list_items= first->get_item_list()->elements; + if (key_parts == select_list_items) + { + if ((!first->is_part_of_union() && (first->options & SELECT_DISTINCT)) || + derived->check_distinct_in_union()) + keyinfo->rec_per_key[key_parts - 1]= 1; + } + } + set_if_bigger(s->max_key_length, keyinfo->key_length); s->keys++; return FALSE; @@ -7475,7 +7688,7 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl) (pos= find_type(&tbl->s->keynames, hint->key_name.str, hint->key_name.length, 1)) <= 0) { - my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias); + my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias.str); return 1; } @@ -7581,7 +7794,7 @@ void init_mdl_requests(TABLE_LIST *table_list) { for ( ; table_list ; table_list= table_list->next_global) table_list->mdl_request.init(MDL_key::TABLE, - table_list->db, table_list->table_name, + table_list->db.str, table_list->table_name.str, table_list->lock_type >= TL_WRITE_ALLOW_WRITE ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_TRANSACTION); @@ -7765,7 +7978,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) if (vcol_info->expr->save_in_field(vf, 0)) field_error= error= 1; DBUG_PRINT("info", ("field '%s' - updated error: %d", - vf->field_name, field_error)); + vf->field_name.str, field_error)); if (swap_values && (vf->flags & BLOB_FLAG)) { /* @@ -7779,7 +7992,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) } else { - DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name)); + DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name.str)); } } if (handler_pushed) @@ -7851,7 +8064,7 @@ int TABLE::update_default_fields(bool ignore_errors) res|= (field->default_value->expr->save_in_field(field, 0) < 0); if (!ignore_errors && res) { - my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name); + my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str); break; } res= 0; @@ -7861,6 +8074,7 @@ int TABLE::update_default_fields(bool ignore_errors) DBUG_RETURN(res); } + void TABLE::evaluate_update_default_function() { DBUG_ENTER("TABLE::evaluate_update_default_function"); @@ -7876,6 +8090,39 @@ void TABLE::evaluate_update_default_function() } +void TABLE::vers_update_fields() +{ + bitmap_set_bit(write_set, vers_start_field()->field_index); + bitmap_set_bit(write_set, vers_end_field()->field_index); + + if (!vers_write) + { + file->column_bitmaps_signal(); + return; + } + + if (versioned(VERS_TIMESTAMP) && + vers_start_field()->store_timestamp(in_use->query_start(), + in_use->query_start_sec_part())) + { + DBUG_ASSERT(0); + } + + vers_end_field()->set_max(); + bitmap_set_bit(read_set, vers_end_field()->field_index); + file->column_bitmaps_signal(); + if (vfield) + update_virtual_fields(file, VCOL_UPDATE_FOR_READ); +} + + +void TABLE::vers_update_end() +{ + if (vers_end_field()->store_timestamp(in_use->query_start(), + in_use->query_start_sec_part())) + DBUG_ASSERT(0); +} + /** Reset markers that fields are being updated */ @@ -7984,7 +8231,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const for (Field **fld= field; *fld; fld++) { if (!bitmap_is_set(write_set, (*fld)->field_index) && - !((*fld)->flags & NO_DEFAULT_VALUE_FLAG)) + !((*fld)->flags & (NO_DEFAULT_VALUE_FLAG | VERS_SYSTEM_FIELD))) { if (!(*fld)->is_null_in_record(s->default_values) && (*fld)->validate_value_in_record_with_warn(thd, s->default_values) && @@ -8023,7 +8270,7 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd, tmp_table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL); file->ha_index_or_rnd_end(); - if (file->ha_rnd_init_with_error(1)) + if (unlikely(file->ha_rnd_init_with_error(1))) DBUG_RETURN(1); if (tmp_table->no_rows) @@ -8035,10 +8282,10 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd, tmp_table->file->ha_start_bulk_insert(file->stats.records); } - while (!file->ha_rnd_next(tmp_table->record[0])) + while (likely(!file->ha_rnd_next(tmp_table->record[0]))) { write_err= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]); - if (write_err) + if (unlikely(write_err)) { bool is_duplicate; if (tmp_table->file->is_fatal_error(write_err, HA_CHECK_DUP) && @@ -8049,11 +8296,8 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd, DBUG_RETURN(1); } - if (thd->check_killed()) - { - thd->send_kill_message(); + if (unlikely(thd->check_killed())) goto err_killed; - } } if (!tmp_table->no_rows && tmp_table->file->ha_end_bulk_insert()) goto err; @@ -8220,7 +8464,21 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) (first_table && first_table->is_multitable())) set_multitable(); - unit->derived= this; + if (!unit->derived) + unit->derived= this; + else if (!is_with_table_recursive_reference() && unit->derived != this) + { + if (unit->derived->is_with_table_recursive_reference()) + unit->derived= this; + else if (vers_conditions.eq(unit->derived->vers_conditions)) + vers_conditions.empty(); + else + { + my_error(ER_CONFLICTING_FOR_SYSTEM_TIME, MYF(0)); + return TRUE; + } + } + if (init_view && !view) { /* This is all what we can do for a derived table for now. */ @@ -8294,9 +8552,8 @@ int TABLE_LIST::fetch_number_of_rows() return 0; } if (is_materialized_derived() && !fill_me) - { - table->file->stats.records= ((select_union*)derived->result)->records; + table->file->stats.records= get_unit()->result->est_records; set_if_bigger(table->file->stats.records, 2); table->used_stat_records= table->file->stats.records; } @@ -8356,7 +8613,10 @@ bool TABLE_LIST::change_refs_to_fields() if (!used_items.elements) return FALSE; - materialized_items= (Item**)thd->calloc(sizeof(void*) * table->s->fields); + Item **materialized_items= + (Item **)thd->calloc(sizeof(void *) * table->s->fields); + if (!materialized_items) + return TRUE; while ((ref= (Item_direct_ref*)li++)) { @@ -8413,7 +8673,6 @@ bool TABLE_LIST::is_with_table() return derived && derived->with_element; } - uint TABLE_SHARE::actual_n_key_parts(THD *thd) { return use_ext_keys && @@ -8590,8 +8849,8 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) if (!(item->used_tables() == tab_map)) continue; Item_func_eq *eq= 0; - Item *left_item_clone= left_item->build_clone(thd, thd->mem_root); - Item *right_item_clone= item->build_clone(thd, thd->mem_root); + Item *left_item_clone= left_item->build_clone(thd); + Item *right_item_clone= item->build_clone(thd); if (left_item_clone && right_item_clone) { left_item_clone->set_item_equal(NULL); @@ -8621,7 +8880,7 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) return new_cond; } else if (cond->get_extraction_flag() != NO_EXTRACTION_FL) - return cond->build_clone(thd, thd->mem_root); + return cond->build_clone(thd); return 0; } @@ -8645,6 +8904,460 @@ bool fk_modifies_child(enum_fk_option opt) return can_write[opt]; } +enum TR_table::enabled TR_table::use_transaction_registry= TR_table::MAYBE; + +TR_table::TR_table(THD* _thd, bool rw) : + thd(_thd), open_tables_backup(NULL) +{ + init_one_table(&MYSQL_SCHEMA_NAME, &TRANSACTION_REG_NAME, + NULL, rw ? TL_WRITE : TL_READ); +} + +bool TR_table::open() +{ + DBUG_ASSERT(!table); + open_tables_backup= new Open_tables_backup; + if (!open_tables_backup) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return true; + } + + All_tmp_tables_list *temporary_tables= thd->temporary_tables; + bool error= !open_log_table(thd, this, open_tables_backup); + thd->temporary_tables= temporary_tables; + + if (use_transaction_registry == MAYBE) + error= check(error); + + use_transaction_registry= error ? NO : YES; + + return error; +} + +TR_table::~TR_table() +{ + if (table) + { + thd->temporary_tables= NULL; + close_log_table(thd, open_tables_backup); + } + delete open_tables_backup; +} + +void TR_table::store(uint field_id, ulonglong val) +{ + table->field[field_id]->store(val, true); + table->field[field_id]->set_notnull(); +} + +void TR_table::store(uint field_id, timeval ts) +{ + table->field[field_id]->store_timestamp(ts.tv_sec, ts.tv_usec); + table->field[field_id]->set_notnull(); +} + +enum_tx_isolation TR_table::iso_level() const +{ + enum_tx_isolation res= (enum_tx_isolation) ((*this)[FLD_ISO_LEVEL]->val_int() - 1); + DBUG_ASSERT(res <= ISO_SERIALIZABLE); + return res; +} + +bool TR_table::update(ulonglong start_id, ulonglong end_id) +{ + if (!table && open()) + return true; + + store(FLD_BEGIN_TS, thd->transaction_time()); + thd->set_time(); + timeval end_time= {thd->query_start(), int(thd->query_start_sec_part())}; + store(FLD_TRX_ID, start_id); + store(FLD_COMMIT_ID, end_id); + store(FLD_COMMIT_TS, end_time); + store_iso_level(thd->tx_isolation); + + int error= table->file->ha_write_row(table->record[0]); + if (unlikely(error)) + table->file->print_error(error, MYF(0)); + return error; +} + +#define newx new (thd->mem_root) +bool TR_table::query(ulonglong trx_id) +{ + if (!table && open()) + return false; + SQL_SELECT_auto select; + READ_RECORD info; + int error; + List<TABLE_LIST> dummy; + SELECT_LEX &slex= thd->lex->select_lex; + Name_resolution_context_backup backup(slex.context, *this); + Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]); + Item *value= newx Item_int(thd, trx_id); + COND *conds= newx Item_func_eq(thd, field, value); + if (unlikely((error= setup_conds(thd, this, dummy, &conds)))) + return false; + select= make_select(table, 0, 0, conds, NULL, 0, &error); + if (unlikely(error || !select)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return false; + } + // FIXME: (performance) force index 'transaction_id' + error= init_read_record(&info, thd, table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + while (!(error= info.read_record()) && !thd->killed && !thd->is_error()) + { + if (select->skip_record(thd) > 0) + return true; + } + my_error(ER_VERS_NO_TRX_ID, MYF(0), (longlong) trx_id); + return false; +} + +bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) +{ + if (!table && open()) + return false; + SQL_SELECT_auto select; + READ_RECORD info; + int error; + List<TABLE_LIST> dummy; + SELECT_LEX &slex= thd->lex->select_lex; + Name_resolution_context_backup backup(slex.context, *this); + Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]); + Item *value= newx Item_datetime_literal(thd, &commit_time, 6); + COND *conds; + if (backwards) + conds= newx Item_func_ge(thd, field, value); + else + conds= newx Item_func_le(thd, field, value); + if (unlikely((error= setup_conds(thd, this, dummy, &conds)))) + return false; + // FIXME: (performance) force index 'commit_timestamp' + select= make_select(table, 0, 0, conds, NULL, 0, &error); + if (unlikely(error || !select)) + return false; + error= init_read_record(&info, thd, table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + + // With PK by transaction_id the records are ordered by PK, so we have to + // scan TRT fully and collect min (backwards == true) + // or max (backwards == false) stats. + bool found= false; + MYSQL_TIME found_ts; + while (!(error= info.read_record()) && !thd->killed && !thd->is_error()) + { + int res= select->skip_record(thd); + if (res > 0) + { + MYSQL_TIME commit_ts; + if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0)) + { + found= false; + break; + } + int c; + if (!found || ((c= my_time_compare(&commit_ts, &found_ts)) && + (backwards ? c < 0 : c > 0))) + { + found_ts= commit_ts; + found= true; + // TODO: (performance) make ORDER DESC and break after first found. + // Otherwise it is O(n) scan (+copy)! + store_record(table, record[1]); + } + } + else if (res < 0) + { + found= false; + break; + } + } + if (found) + restore_record(table, record[1]); + return found; +} +#undef newx + +bool TR_table::query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0, + ulonglong commit_id1, enum_tx_isolation iso_level1, + ulonglong commit_id0) +{ + if (trx_id1 == trx_id0) + { + return false; + } + + if (trx_id1 == ULONGLONG_MAX || trx_id0 == 0) + { + result= true; + return false; + } + + if (trx_id0 == ULONGLONG_MAX || trx_id1 == 0) + { + result= false; + return false; + } + + if (!commit_id1) + { + if (!query(trx_id1)) + return true; + + commit_id1= (*this)[FLD_COMMIT_ID]->val_int(); + iso_level1= iso_level(); + } + + if (!commit_id0) + { + if (!query(trx_id0)) + return true; + + commit_id0= (*this)[FLD_COMMIT_ID]->val_int(); + } + + // Trivial case: TX1 started after TX0 committed + if (trx_id1 > commit_id0 + // Concurrent transactions: TX1 committed after TX0 and TX1 is read (un)committed + || (commit_id1 > commit_id0 && iso_level1 < ISO_REPEATABLE_READ)) + { + result= true; + } + else // All other cases: TX1 does not see TX0 + { + result= false; + } + + return false; +} + +void TR_table::warn_schema_incorrect(const char *reason) +{ + if (MYSQL_VERSION_ID == table->s->mysql_version) + { + sql_print_error("%`s.%`s schema is incorrect: %s.", + db.str, table_name.str, reason); + } + else + { + sql_print_error("%`s.%`s schema is incorrect: %s. Created with MariaDB %d, " + "now running %d.", + db.str, table_name.str, reason, MYSQL_VERSION_ID, + static_cast<int>(table->s->mysql_version)); + } +} + +bool TR_table::check(bool error) +{ + if (error) + { + sql_print_warning("%`s.%`s does not exist (open failed).", db.str, + table_name.str); + return true; + } + + if (table->file->ht->db_type != DB_TYPE_INNODB) + { + warn_schema_incorrect("Wrong table engine (expected InnoDB)"); + return true; + } + +#define WARN_SCHEMA(...) \ + char reason[128]; \ + snprintf(reason, 128, __VA_ARGS__); \ + warn_schema_incorrect(reason); + + if (table->s->fields != FIELD_COUNT) + { + WARN_SCHEMA("Wrong field count (expected %d)", FIELD_COUNT); + return true; + } + + if (table->field[FLD_TRX_ID]->type() != MYSQL_TYPE_LONGLONG) + { + WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_TRX_ID); + return true; + } + + if (table->field[FLD_COMMIT_ID]->type() != MYSQL_TYPE_LONGLONG) + { + WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_COMMIT_ID); + return true; + } + + if (table->field[FLD_BEGIN_TS]->type() != MYSQL_TYPE_TIMESTAMP) + { + WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_BEGIN_TS); + return true; + } + + if (table->field[FLD_COMMIT_TS]->type() != MYSQL_TYPE_TIMESTAMP) + { + WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_COMMIT_TS); + return true; + } + + if (table->field[FLD_ISO_LEVEL]->type() != MYSQL_TYPE_STRING || + !(table->field[FLD_ISO_LEVEL]->flags & ENUM_FLAG)) + { + wrong_enum: + WARN_SCHEMA("Wrong field %d type (expected ENUM('READ-UNCOMMITTED', " + "'READ-COMMITTED', 'REPEATABLE-READ', 'SERIALIZABLE'))", + FLD_ISO_LEVEL); + return true; + } + + Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]); + st_typelib *typelib= iso_level->typelib; + + if (typelib->count != 4) + goto wrong_enum; + + if (strcmp(typelib->type_names[0], "READ-UNCOMMITTED") || + strcmp(typelib->type_names[1], "READ-COMMITTED") || + strcmp(typelib->type_names[2], "REPEATABLE-READ") || + strcmp(typelib->type_names[3], "SERIALIZABLE")) + { + goto wrong_enum; + } + + if (!table->key_info || !table->key_info->key_part) + goto wrong_pk; + + if (strcmp(table->key_info->key_part->field->field_name.str, "transaction_id")) + { + wrong_pk: + WARN_SCHEMA("Wrong PRIMARY KEY (expected `transaction_id`)"); + return true; + } + + return false; +} + +bool vers_select_conds_t::resolve_units(THD *thd) +{ + DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED); + DBUG_ASSERT(start.item); + return start.resolve_unit(thd) || + end.resolve_unit(thd); +} + +bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const +{ + if (type != conds.type) + return false; + switch (type) { + case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_ALL: + return true; + case SYSTEM_TIME_BEFORE: + break; + case SYSTEM_TIME_HISTORY: + break; + case SYSTEM_TIME_AS_OF: + return start.eq(conds.start); + case SYSTEM_TIME_FROM_TO: + case SYSTEM_TIME_BETWEEN: + return start.eq(conds.start) && end.eq(conds.end); + } + DBUG_ASSERT(0); + return false; +} + + +bool Vers_history_point::resolve_unit(THD *thd) +{ + if (!item) + return false; + if (item->fix_fields_if_needed(thd, &item)) + return true; + return item->this_item()->type_handler_for_system_time()-> + Vers_history_point_resolve_unit(thd, this); +} + + +void Vers_history_point::bad_expression_data_type_error(const char *type) const +{ + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + type, "FOR SYSTEM_TIME"); +} + + +void Vers_history_point::fix_item() +{ + if (item && item->decimals == 0 && item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype() == Item_func::NOW_FUNC) + item->decimals= 6; +} + + +bool Vers_history_point::eq(const vers_history_point_t &point) const +{ + return unit == point.unit && item->eq(point.item, false); +} + +void Vers_history_point::print(String *str, enum_query_type query_type, + const char *prefix, size_t plen) const +{ + const static LEX_CSTRING unit_type[]= + { + { STRING_WITH_LEN("") }, + { STRING_WITH_LEN("TIMESTAMP ") }, + { STRING_WITH_LEN("TRANSACTION ") } + }; + str->append(prefix, plen); + str->append(unit_type + unit); + item->print(str, query_type); +} + +Field *TABLE::find_field_by_name(LEX_CSTRING *str) const +{ + Field **tmp; + size_t length= str->length; + if (s->name_hash.records) + { + tmp= (Field**) my_hash_search(&s->name_hash, (uchar*) str->str, length); + return tmp ? field[tmp - s->field] : NULL; + } + else + { + for (tmp= field; *tmp; tmp++) + { + if ((*tmp)->field_name.length == length && + !lex_string_cmp(system_charset_info, &(*tmp)->field_name, str)) + return *tmp; + } + } + return NULL; +} + + +bool TABLE::export_structure(THD *thd, Row_definition_list *defs) +{ + for (Field **src= field; *src; src++) + { + uint offs; + if (defs->find_row_field_by_name(&src[0]->field_name, &offs)) + { + my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name.str); + return true; + } + Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src); + if (!def) + return true; + def->flags&= (uint) ~NOT_NULL_FLAG; + if ((def->sp_prepare_create_field(thd, thd->mem_root)) || + (defs->push_back(def, thd->mem_root))) + return true; + } + return false; +} /* @brief @@ -8658,10 +9371,10 @@ bool fk_modifies_child(enum_fk_option opt) void TABLE::initialize_quick_structures() { - bzero(quick_rows, sizeof(quick_rows)); - bzero(quick_key_parts, sizeof(quick_key_parts)); - bzero(quick_costs, sizeof(quick_costs)); - bzero(quick_n_ranges, sizeof(quick_n_ranges)); + TRASH_ALLOC(quick_rows, sizeof(quick_rows)); + TRASH_ALLOC(quick_key_parts, sizeof(quick_key_parts)); + TRASH_ALLOC(quick_costs, sizeof(quick_costs)); + TRASH_ALLOC(quick_n_ranges, sizeof(quick_n_ranges)); } /* |