diff options
Diffstat (limited to 'sql/table.cc')
-rw-r--r-- | sql/table.cc | 724 |
1 files changed, 485 insertions, 239 deletions
diff --git a/sql/table.cc b/sql/table.cc index d5dfc414792..025612ca87b 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, 2020, MariaDB + Copyright (c) 2008, 2021, 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 @@ -25,9 +25,9 @@ // primary_key_name #include "sql_parse.h" // free_items #include "strfunc.h" // unhex_type2 -#include "sql_partition.h" // mysql_unpack_partition, +#include "ha_partition.h" // PART_EXT + // mysql_unpack_partition, // fix_partition_func, partition_info -#include "sql_acl.h" // *_ACL, acl_getroot_no_password #include "sql_base.h" #include "create_options.h" #include "sql_trigger.h" @@ -70,6 +70,8 @@ struct extra2_fields LEX_CUSTRING field_flags; LEX_CUSTRING system_period; LEX_CUSTRING application_period; + LEX_CUSTRING field_data_type_info; + LEX_CUSTRING without_overlaps; void reset() { bzero((void*)this, sizeof(*this)); } }; @@ -277,7 +279,8 @@ TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, DBUG_ASSERT(name != NULL); #ifdef WITH_WSREP - if (my_strcasecmp(system_charset_info, db->str, "mysql") == 0 && + if (db->str && + my_strcasecmp(system_charset_info, db->str, "mysql") == 0 && my_strcasecmp(system_charset_info, name->str, "wsrep_streaming_log") == 0) { return TABLE_CATEGORY_INFORMATION; @@ -286,7 +289,7 @@ TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, if (is_infoschema_db(db)) return TABLE_CATEGORY_INFORMATION; - if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db)) + if (is_perfschema_db(db)) return TABLE_CATEGORY_PERFORMANCE; if (lex_string_eq(&MYSQL_SCHEMA_NAME, db)) @@ -336,7 +339,8 @@ 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_share", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); + init_sql_alloc(key_memory_table_share, &mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, + MYF(0)); if (multi_alloc_root(&mem_root, &share, sizeof(*share), &key_buff, key_length, @@ -359,11 +363,10 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, 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) + table_alias_charset->strnncoll(key, 6, "mysql", 6) == 0) share->not_usable_by_query_cache= 1; - init_sql_alloc(&share->stats_cb.mem_root, "share_stats", + init_sql_alloc(PSI_INSTRUMENT_ME, &share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); @@ -425,8 +428,9 @@ 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, "tmp_table_share", TABLE_ALLOC_BLOCK_SIZE, - 0, MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC)); + init_sql_alloc(key_memory_table_share, &share->mem_root, + 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; @@ -626,9 +630,13 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) path); if (flags & GTS_FORCE_DISCOVERY) { + const char *path2= share->normalized_path.str; DBUG_ASSERT(flags & GTS_TABLE); DBUG_ASSERT(flags & GTS_USE_DISCOVERY); - mysql_file_delete_with_symlink(key_file_frm, path, "", MYF(0)); + /* Delete .frm and .par files */ + mysql_file_delete_with_symlink(key_file_frm, path2, reg_ext, MYF(0)); + mysql_file_delete_with_symlink(key_file_partition_ddl_log, path2, PAR_EXT, + MYF(0)); file= -1; } else @@ -686,7 +694,8 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) frmlen= uint4korr(head+10); set_if_smaller(frmlen, FRM_MAX_SIZE); // safety - if (!(buf= (uchar*)my_malloc(frmlen, MYF(MY_THREAD_SPECIFIC|MY_WME)))) + if (!(buf= (uchar*)my_malloc(PSI_INSTRUMENT_ME, frmlen, + MYF(MY_THREAD_SPECIFIC|MY_WME)))) goto err; memcpy(buf, head, sizeof(head)); @@ -987,6 +996,38 @@ void Column_definition_attributes::frm_unpack_basic(const uchar *buff) } +void Column_definition_attributes::frm_pack_numeric_with_dec(uchar *buff) const +{ + DBUG_ASSERT(f_decimals(pack_flag) == 0); + uint tmp_pack_flag= pack_flag | (decimals << FIELDFLAG_DEC_SHIFT); + int2store(buff + 3, length); + int2store(buff + 8, tmp_pack_flag); + buff[10]= (uchar) unireg_check; +} + + +bool +Column_definition_attributes::frm_unpack_numeric_with_dec(TABLE_SHARE *share, + const uchar *buff) +{ + frm_unpack_basic(buff); + decimals= f_decimals(pack_flag); + pack_flag&= ~FIELDFLAG_DEC_MASK; + return frm_unpack_charset(share, buff); +} + + +bool +Column_definition_attributes::frm_unpack_temporal_with_dec(TABLE_SHARE *share, + uint intlen, + const uchar *buff) +{ + frm_unpack_basic(buff); + decimals= temporal_dec(intlen); + return frm_unpack_charset(share, buff); +} + + void Column_definition_attributes::frm_pack_charset(uchar *buff) const { buff[11]= (uchar) (charset->number >> 8); @@ -1100,7 +1141,6 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, Field **vfield_ptr= table->vfield; Field **dfield_ptr= table->default_field; Virtual_column_info **check_constraint_ptr= table->check_constraints; - sql_mode_t saved_mode= thd->variables.sql_mode; Query_arena backup_arena; Virtual_column_info *vcol= 0; StringBuffer<MAX_FIELD_WIDTH> expr_str; @@ -1127,7 +1167,8 @@ 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 | MODE_EMPTY_STRING_IS_NULL); + Sql_mode_instant_remove sms(thd, MODE_NO_BACKSLASH_ESCAPES | + MODE_EMPTY_STRING_IS_NULL); while (pos < end) { @@ -1228,10 +1269,10 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, for (key_index= 0; key_index < table->s->keys; key_index++) { key=table->key_info + key_index; - parts= key->user_defined_key_parts; + parts= key->user_defined_key_parts; if (key->key_part[parts].fieldnr == field->field_index + 1) - break; - } + break; + } if (!key || key->algorithm != HA_KEY_ALG_LONG_HASH) goto end; KEY_PART_INFO *keypart; @@ -1310,7 +1351,6 @@ end: thd->stmt_arena= backup_stmt_arena_ptr; if (save_character_set_client) thd->update_charset(save_character_set_client, save_collation); - thd->variables.sql_mode= saved_mode; DBUG_RETURN(res); } @@ -1484,6 +1524,14 @@ static size_t extra2_read_len(const uchar **extra2, const uchar *extra2_end) return length; } +static +bool read_extra2_section_once(const uchar *extra2, size_t len, LEX_CUSTRING *section) +{ + if (section->str) + return true; + *section= {extra2, len}; + return false; +} static bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) @@ -1503,6 +1551,8 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) size_t length= extra2_read_len(&extra2, e2end); if (!length) DBUG_RETURN(true); + + bool fail= false; switch (type) { case EXTRA2_TABLEDEF_VERSION: if (fields->version.str) // see init_from_sql_statement_string() @@ -1517,43 +1567,38 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) } break; case EXTRA2_ENGINE_TABLEOPTS: - if (fields->options.str) - DBUG_RETURN(true); - fields->options.str= extra2; - fields->options.length= length; + fail= read_extra2_section_once(extra2, length, &fields->options); break; case EXTRA2_DEFAULT_PART_ENGINE: fields->engine.set((const char*)extra2, length); break; case EXTRA2_GIS: - if (fields->gis.str) - DBUG_RETURN(true); - fields->gis.str= extra2; - fields->gis.length= length; + fail= read_extra2_section_once(extra2, length, &fields->gis); break; case EXTRA2_PERIOD_FOR_SYSTEM_TIME: - if (fields->system_period.str || length != 2 * frm_fieldno_size) - DBUG_RETURN(true); - fields->system_period.str = extra2; - fields->system_period.length= length; + fail= read_extra2_section_once(extra2, length, &fields->system_period) + || length != 2 * frm_fieldno_size; break; case EXTRA2_FIELD_FLAGS: - if (fields->field_flags.str) - DBUG_RETURN(true); - fields->field_flags.str= extra2; - fields->field_flags.length= length; + fail= read_extra2_section_once(extra2, length, &fields->field_flags); break; case EXTRA2_APPLICATION_TIME_PERIOD: - if (fields->application_period.str) - DBUG_RETURN(true); - fields->application_period.str= extra2; - fields->application_period.length= length; + fail= read_extra2_section_once(extra2, length, &fields->application_period); + break; + case EXTRA2_PERIOD_WITHOUT_OVERLAPS: + fail= read_extra2_section_once(extra2, length, &fields->without_overlaps); + break; + case EXTRA2_FIELD_DATA_TYPE_INFO: + fail= read_extra2_section_once(extra2, length, &fields->field_data_type_info); break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) DBUG_RETURN(true); } + if (fail) + DBUG_RETURN(true); + extra2+= length; } if (extra2 != e2end) @@ -1563,9 +1608,92 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) } +class Field_data_type_info_array +{ +public: + class Elem + { + LEX_CSTRING m_type_info; + public: + void set(const LEX_CSTRING &type_info) + { + m_type_info= type_info; + } + const LEX_CSTRING &type_info() const + { + return m_type_info; + } + }; +private: + Elem *m_array; + uint m_count; + bool alloc(MEM_ROOT *root, uint count) + { + DBUG_ASSERT(!m_array); + DBUG_ASSERT(!m_count); + size_t nbytes= sizeof(Elem) * count; + if (!(m_array= (Elem*) alloc_root(root, nbytes))) + return true; + m_count= count; + bzero((void*) m_array, nbytes); + return false; + } + static uint32 read_length(uchar **pos, const uchar *end) + { + ulonglong num= safe_net_field_length_ll(pos, end - *pos); + if (num > UINT_MAX32) + return 0; + return (uint32) num; + } + static bool read_string(LEX_CSTRING *to, uchar **pos, const uchar *end) + { + to->length= read_length(pos, end); + if (*pos + to->length > end) + return true; // Not enough data + to->str= (const char *) *pos; + *pos+= to->length; + return false; + } +public: + Field_data_type_info_array() + :m_array(NULL), m_count(0) + { } + uint count() const + { + return m_count; + } + const Elem& element(uint i) const + { + DBUG_ASSERT(i < m_count); + return m_array[i]; + } + bool parse(MEM_ROOT *root, uint count, LEX_CUSTRING &image) + { + const uchar *pos= image.str; + const uchar *end= pos + image.length; + if (alloc(root, count)) + return true; + for (uint i= 0; i < count && pos < end; i++) + { + LEX_CSTRING type_info; + uint fieldnr= read_length((uchar**) &pos, end); + if ((fieldnr == 0 && i > 0) || fieldnr >= count) + return true; // Bad data + if (read_string(&type_info, (uchar**) &pos, end) || type_info.length == 0) + return true; // Bad data + m_array[fieldnr].set(type_info); + } + return pos < end; // Error if some data is still left + } +}; + + /** Read data from a binary .frm file image into a TABLE_SHARE + @param write Write the .frm and .par file. These are not created if + the function returns an error. + @note frm bytes at the following offsets are unused in MariaDB 10.0: @@ -1576,12 +1704,13 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) 42..46 are unused since 5.0 (were for RAID support) Also, there're few unused bytes in forminfo. - */ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, const uchar *frm_image, - size_t frm_length) + size_t frm_length, + const uchar *par_image, + size_t par_length) { TABLE_SHARE *share= this; uint new_frm_ver, field_pack_length, new_field_pack_flag; @@ -1615,23 +1744,31 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint len; uint ext_key_parts= 0; plugin_ref se_plugin= 0; - bool vers_can_native= false; - + bool vers_can_native= false, frm_created= 0; + Field_data_type_info_array field_data_type_info_array; MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; extra2_fields extra2; - DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); keyinfo= &first_keyinfo; thd->mem_root= &share->mem_root; - if (write && write_frm_image(frm_image, frm_length)) - goto err; - if (frm_length < FRM_HEADER_SIZE + FRM_FORMINFO_SIZE) goto err; + if (write) + { + frm_created= 1; + if (write_frm_image(frm_image, frm_length)) + goto err; +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (par_image) + if (write_par_image(par_image, par_length)) + goto err; +#endif + } + share->frm_version= frm_image[2]; /* Check if .frm file created by MySQL 5.0. In this case we want to @@ -1734,7 +1871,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { const CHARSET_INFO *cs= thd->variables.collation_database; /* unknown charset in frm_image[38] or pre-3.23 frm */ - if (use_mb(cs)) + if (cs->use_mb()) { /* Warn that we may be changing the size of character columns */ sql_print_warning("'%s' had no or invalid character set, " @@ -1811,7 +1948,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, name.length= str_db_type_length; plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name, false); - if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin)) + if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin) && + legacy_db_type != DB_TYPE_S3) { if (se_plugin) { @@ -1967,6 +2105,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH) hash_fields++; +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (par_image && plugin_data(se_plugin, handlerton*) == partition_hton) + { + /* + Discovery returned a partition plugin. Change to use it. The partition + engine will then use discovery to find the rest of the plugin tables, + which may be in the original engine used for discovery + */ + share->db_plugin= se_plugin; + } +#endif if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin)) goto err; // wrong engine (someone changed the frm under our feet?) @@ -2106,10 +2255,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH; if (use_hash) - use_hash= !my_hash_init(&share->name_hash, - system_charset_info, - share->fields,0,0, - (my_hash_get_key) get_field_name,0,0); + use_hash= !my_hash_init(PSI_INSTRUMENT_ME, &share->name_hash, + system_charset_info, share->fields, 0, 0, + (my_hash_get_key) get_field_name, 0, 0); if (share->mysql_version >= 50700 && share->mysql_version < 100000 && vcol_screen_length) @@ -2136,7 +2284,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } else { - DBUG_PRINT("info", ("Setting system versioning informations")); + DBUG_PRINT("info", ("Setting system versioning information")); if (init_period_from_extra2(&vers, extra2.system_period.str, extra2.system_period.str + extra2.system_period.length)) goto err; @@ -2162,9 +2310,37 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (init_period_from_extra2(&period, pos, end)) goto err; + if (extra2_str_size(period.name.length) + + extra2_str_size(period.constr_name.length) + + 2 * frm_fieldno_size + != extra2.application_period.length) + goto err; status_var_increment(thd->status_var.feature_application_time_periods); } + if (extra2.without_overlaps.str) + { + if (extra2.application_period.str == NULL) + goto err; + const uchar *key_pos= extra2.without_overlaps.str; + period.unique_keys= read_frm_keyno(key_pos); + for (uint k= 0; k < period.unique_keys; k++) + { + key_pos+= frm_keyno_size; + uint key_nr= read_frm_keyno(key_pos); + key_info[key_nr].without_overlaps= true; + } + + if ((period.unique_keys + 1) * frm_keyno_size + != extra2.without_overlaps.length) + goto err; + } + + if (extra2.field_data_type_info.length && + field_data_type_info_array.parse(old_root, share->fields, + extra2.field_data_type_info)) + goto err; + for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint interval_nr= 0, recpos; @@ -2193,7 +2369,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, comment_pos+= comment_length; } - if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL) + if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL + && likely(share->mysql_version >= 100000)) { /* MariaDB version 10.0 version. @@ -2243,11 +2420,53 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, interval_nr= (uint) strpos[12]; enum_field_types field_type= (enum_field_types) strpos[13]; if (!(handler= Type_handler::get_handler_by_real_type(field_type))) - goto err; // Not supported field type + { + if (field_type == 245 && + share->mysql_version >= 50700) // a.k.a MySQL 5.7 JSON + { + share->incompatible_version|= HA_CREATE_USED_ENGINE; + const LEX_CSTRING mysql_json{STRING_WITH_LEN("MYSQL_JSON")}; + handler= Type_handler::handler_by_name_or_error(thd, mysql_json); + } + + if (!handler) + goto err; // Not supported field type + } + handler= handler->type_handler_frm_unpack(strpos); if (handler->Column_definition_attributes_frm_unpack(&attr, share, strpos, &extra2.gis)) goto err; + + if (field_data_type_info_array.count()) + { + const LEX_CSTRING &info= field_data_type_info_array. + element(i).type_info(); + DBUG_EXECUTE_IF("frm_data_type_info", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: [%u] name='%s' type_info='%.*s'", + i, share->fieldnames.type_names[i], + (uint) info.length, info.str);); + + if (info.length) + { + const Type_handler *h= Type_handler::handler_by_name_or_error(thd, + info); + /* + This code will eventually be extended here: + - If the handler was not found by name, we could + still open the table using the fallback type handler "handler", + at least for a limited set of commands. + - If the handler was found by name, we could check + that "h" and "handler" have the same type code + (and maybe some other properties) to make sure + that the FRM data is consistent. + */ + if (!h) + goto err; + handler= h; + } + } } if (((uint) strpos[10]) & MYSQL57_GENERATED_FIELD) @@ -2279,6 +2498,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, attr.length= (uint) strpos[3]; recpos= uint2korr(strpos+4), attr.pack_flag= uint2korr(strpos+6); + if (f_is_num(attr.pack_flag)) + { + attr.decimals= f_decimals(attr.pack_flag); + attr.pack_flag&= ~FIELDFLAG_DEC_MASK; + } attr.pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files attr.unireg_check= (Field::utype) MTYP_TYPENR((uint) strpos[8]); interval_nr= (uint) strpos[10]; @@ -2607,7 +2831,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part + (keyinfo-1)->ext_key_parts; uint add_keyparts_for_this_key= add_first_key_parts; - uint length_bytes= 0, len_null_byte= 0, ext_key_length= 0; + uint len_null_byte= 0, ext_key_length= 0; Field *field; if ((keyinfo-1)->algorithm == HA_KEY_ALG_LONG_HASH) @@ -2619,19 +2843,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, */ for (i= 0; i < keyinfo->user_defined_key_parts; i++) { + uint length_bytes= 0; uint fieldnr= keyinfo->key_part[i].fieldnr; field= share->field[fieldnr-1]; if (field->null_ptr) len_null_byte= HA_KEY_NULL_LENGTH; - if ((field->type() == MYSQL_TYPE_BLOB || - field->real_type() == MYSQL_TYPE_VARCHAR || - field->type() == MYSQL_TYPE_GEOMETRY) && - keyinfo->algorithm != HA_KEY_ALG_LONG_HASH ) - { - length_bytes= HA_KEY_BLOB_LENGTH; - } + if (keyinfo->algorithm != HA_KEY_ALG_LONG_HASH) + length_bytes= field->key_part_length_bytes(); ext_key_length+= keyinfo->key_part[i].length + len_null_byte + length_bytes; @@ -2720,21 +2940,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, keyinfo->flags|=HA_NULL_PART_KEY; keyinfo->key_length+= HA_KEY_NULL_LENGTH; } - if (field->type() == MYSQL_TYPE_BLOB || - field->real_type() == MYSQL_TYPE_VARCHAR || - field->type() == MYSQL_TYPE_GEOMETRY) - { - if (field->type() == MYSQL_TYPE_BLOB || - field->type() == MYSQL_TYPE_GEOMETRY) - key_part->key_part_flag|= HA_BLOB_PART; - else - key_part->key_part_flag|= HA_VAR_LENGTH_PART; - key_part->store_length+=HA_KEY_BLOB_LENGTH; - if (i < keyinfo->user_defined_key_parts) - keyinfo->key_length+= HA_KEY_BLOB_LENGTH; - } - if (field->type() == MYSQL_TYPE_BIT) - key_part->key_part_flag|= HA_BIT_PART; + + key_part->key_part_flag|= field->key_part_flag(); + uint16 key_part_length_bytes= field->key_part_length_bytes(); + key_part->store_length+= key_part_length_bytes; + if (i < keyinfo->user_defined_key_parts) + keyinfo->key_length+= key_part_length_bytes; if (i == 0 && key != primary_key) field->flags |= (((keyinfo->flags & HA_NOSAME || @@ -3055,6 +3266,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, DBUG_RETURN(0); err: + if (frm_created) + { + char path[FN_REFLEN+1]; + strxnmov(path, FN_REFLEN, normalized_path.str, reg_ext, NullS); + my_delete(path, MYF(0)); +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (par_image) + { + strxnmov(path, FN_REFLEN, normalized_path.str, PAR_EXT, NullS); + my_delete(path, MYF(0)); + } +#endif + } share->db_plugin= NULL; share->error= OPEN_FRM_CORRUPTED; share->open_errno= my_errno; @@ -3128,7 +3352,6 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, const char *sql, size_t sql_length) { - sql_mode_t saved_mode= thd->variables.sql_mode; CHARSET_INFO *old_cs= thd->variables.character_set_client; Parser_state parser_state; bool error; @@ -3156,7 +3379,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, if (parser_state.init(thd, sql_copy, sql_length)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); - thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE; + Sql_mode_instant_set sms(thd, MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE); thd->variables.character_set_client= system_charset_info; tmp_disable_binlog(thd); old_lex= thd->lex; @@ -3205,7 +3428,6 @@ ret: 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 (unlikely(thd->is_error() || error)) { @@ -3221,7 +3443,19 @@ ret: bool TABLE_SHARE::write_frm_image(const uchar *frm, size_t len) { - return writefrm(normalized_path.str, db.str, table_name.str, false, frm, len); + char file_name[FN_REFLEN+1]; + strxnmov(file_name, sizeof(file_name)-1, normalized_path.str, reg_ext, + NullS); + return writefile(file_name, db.str, table_name.str, false, + frm, len); +} + +bool TABLE_SHARE::write_par_image(const uchar *par, size_t len) +{ + char file_name[FN_REFLEN+1]; + strxnmov(file_name, sizeof(file_name)-1, normalized_path.str, PAR_EXT, + NullS); + return writefile(file_name, db.str, table_name.str, false, par, len); } @@ -3365,7 +3599,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, to be part of the virtual column */ Item::vcol_func_processor_result res; - res.errors= 0; int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res); if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE))) @@ -3581,6 +3814,55 @@ static void print_long_unique_table(TABLE *table) } #endif +bool copy_keys_from_share(TABLE *outparam, MEM_ROOT *root) +{ + TABLE_SHARE *share= outparam->s; + if (share->key_parts) + { + KEY *key_info, *key_info_end; + KEY_PART_INFO *key_part; + + if (!multi_alloc_root(root, &key_info, share->keys*sizeof(KEY), + &key_part, share->ext_key_parts*sizeof(KEY_PART_INFO), + NullS)) + return 1; + + outparam->key_info= key_info; + + memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys); + memcpy(key_part, key_info->key_part, sizeof(*key_part)*share->ext_key_parts); + + my_ptrdiff_t adjust_ptrs= PTR_BYTE_DIFF(key_part, key_info->key_part); + for (key_info_end= key_info + share->keys ; + key_info < key_info_end ; + key_info++) + { + key_info->table= outparam; + key_info->key_part= reinterpret_cast<KEY_PART_INFO*> + (reinterpret_cast<char*>(key_info->key_part) + adjust_ptrs); + if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) + key_info->flags&= ~HA_NOSAME; + } + for (KEY_PART_INFO *key_part_end= key_part+share->ext_key_parts; + key_part < key_part_end; + key_part++) + { + Field *field= key_part->field= outparam->field[key_part->fieldnr - 1]; + if (field->key_length() != key_part->length && + !(field->flags & BLOB_FLAG)) + { + /* + We are using only a prefix of the column as a key: + Create a new field for the key part that matches the index + */ + field= key_part->field=field->make_new_field(root, outparam, 0); + field->field_length= key_part->length; + } + } + } + return 0; +} + /* Open a table based on a TABLE_SHARE @@ -3634,14 +3916,15 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, outparam->write_row_record= NULL; if (share->incompatible_version && - !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR))) + !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR | + HA_OPEN_FOR_FLUSH))) { /* one needs to run mysql_upgrade on the table */ error= OPEN_FRM_NEEDS_REBUILD; goto err; } - init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0, - MYF(0)); + init_sql_alloc(key_memory_TABLE, &outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, + 0, MYF(0)); /* We have to store the original alias in mem_root as constraints and virtual @@ -3719,6 +4002,15 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, sizeof(Field*))))) goto err; /* purecov: inspected */ + /* Allocate storage for range optimizer */ + if (!multi_alloc_root(&outparam->mem_root, + &outparam->opt_range, + share->keys * sizeof(TABLE::OPT_RANGE), + &outparam->const_key_parts, + share->keys * sizeof(key_part_map), + NullS)) + goto err; + outparam->field= field_ptr; record= (uchar*) outparam->record[0]-1; /* Fieldstart = 1 */ @@ -3744,57 +4036,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; - /* Fix key->name and key_part->field */ - if (share->key_parts) - { - KEY *key_info, *key_info_end; - KEY_PART_INFO *key_part; - uint n_length; - n_length= share->keys*sizeof(KEY) + share->ext_key_parts*sizeof(KEY_PART_INFO); - if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length))) - goto err; - outparam->key_info= key_info; - key_part= (reinterpret_cast<KEY_PART_INFO*>(key_info+share->keys)); - - memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys); - memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) * - share->ext_key_parts)); - - for (key_info_end= key_info + share->keys ; - key_info < key_info_end ; - key_info++) - { - KEY_PART_INFO *key_part_end; - - key_info->table= outparam; - key_info->key_part= key_part; - - key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts : - key_info->user_defined_key_parts) ; - if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) - { - key_part_end++; - key_info->flags&= ~HA_NOSAME; - } - for ( ; key_part < key_part_end; key_part++) - { - Field *field= key_part->field= outparam->field[key_part->fieldnr - 1]; - if (field->key_length() != key_part->length && - !(field->flags & BLOB_FLAG)) - { - /* - We are using only a prefix of the column as a key: - Create a new field for the key part that matches the index - */ - field= key_part->field=field->make_new_field(&outparam->mem_root, - outparam, 0); - const_cast<uint32_t&>(field->field_length)= key_part->length; - } - } - if (!share->use_ext_keys) - key_part+= key_info->ext_key_parts - key_info->user_defined_key_parts; - } - } + if (copy_keys_from_share(outparam, &outparam->mem_root)) + goto err; /* Process virtual and default columns, if any. @@ -3825,7 +4068,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, outparam->check_constraints= check_constraint_ptr; vcol_init_mode mode= VCOL_INIT_DEPENDENCY_FAILURE_IS_WARNING; -#if MYSQL_VERSION_ID > 100500 switch (thd->lex->sql_command) { case SQLCOM_CREATE_TABLE: @@ -3840,7 +4082,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, default: break; } -#endif if (parse_vcol_defs(thd, &outparam->mem_root, outparam, &error_reported, mode)) @@ -3999,7 +4240,7 @@ partititon_err: the fact that table doesn't in fact exist and remove the stray .frm file. */ - if (share->db_type()->discover_table && + if (outparam->file->partition_ht()->discover_table && (ha_err == ENOENT || ha_err == HA_ERR_NO_SUCH_TABLE)) error= OPEN_FRM_DISCOVER; @@ -4519,21 +4760,17 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res) StringBuffer<MAX_FIELD_WIDTH> str; bool rc; THD *thd= field->get_thd(); - sql_mode_t sql_mode_backup= thd->variables.sql_mode; - thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH); field->val_str(&str); if ((rc= !str.length() || !(to= strmake_root(mem, str.ptr(), str.length())))) { res->length(0); - goto ex; + return rc; } res->set(to, str.length(), field->charset()); - -ex: - thd->variables.sql_mode= sql_mode_backup; - return rc; + return false; } @@ -4677,7 +4914,7 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars { #if defined(USE_MB) && defined(USE_MB_IDENT) last_char_is_space= my_isspace(system_charset_info, *name); - if (use_mb(system_charset_info)) + if (system_charset_info->use_mb()) { int len=my_ismbchar(system_charset_info, name, end); if (len) @@ -4712,7 +4949,7 @@ bool check_column_name(const char *name) { #if defined(USE_MB) && defined(USE_MB_IDENT) last_char_is_space= my_isspace(system_charset_info, *name); - if (use_mb(system_charset_info)) + if (system_charset_info->use_mb()) { int len=my_ismbchar(system_charset_info, name, name+system_charset_info->mbmaxlen); @@ -5180,17 +5417,14 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) range_rowid_filter_cost_info_elems= 0; range_rowid_filter_cost_info_ptr= NULL; range_rowid_filter_cost_info= NULL; - update_handler= NULL; - check_unique_buf= NULL; vers_write= s->versioned; - quick_condition_rows=0; + opt_range_condition_rows=0; no_cache= false; - initialize_quick_structures(); + initialize_opt_range_structures(); #ifdef HAVE_REPLICATION /* used in RBR Triggers */ master_had_triggers= 0; #endif - /* Catch wrong handling of the auto_increment_field_not_null. */ DBUG_ASSERT(!auto_increment_field_not_null); auto_increment_field_not_null= FALSE; @@ -5204,6 +5438,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) (*f_ptr)->cond_selectivity= 1.0; } + notnull_cond= 0; DBUG_ASSERT(!file->keyread_enabled()); restore_record(this, s->default_values); @@ -6087,7 +6322,7 @@ TABLE_LIST *TABLE_LIST::last_leaf_for_name_resolution() want_access Acess which we require */ -void TABLE_LIST::register_want_access(ulong want_access) +void TABLE_LIST::register_want_access(privilege_t want_access) { /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ want_access&= ~SHOW_VIEW_ACL; @@ -6142,7 +6377,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd) } else { - if (thd->security_ctx->master_access & SUPER_ACL) + if (thd->security_ctx->master_access & PRIV_REVEAL_MISSING_DEFINER) { my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); @@ -6256,7 +6491,7 @@ bool TABLE_LIST::prepare_security(THD *thd) thd->security_ctx= save_security_ctx; #else while ((tbl= tb++)) - tbl->grant.privilege= ~NO_ACCESS; + tbl->grant.privilege= ALL_KNOWN_ACL; #endif DBUG_RETURN(FALSE); } @@ -6522,8 +6757,8 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, &view->view->first_select_lex()->context: &thd->lex->first_select_lex()->context); Item *item= (new (thd->mem_root) - Item_direct_view_ref(thd, context, field_ref, view->alias.str, - name, view)); + Item_direct_view_ref(thd, context, field_ref, view->alias, + *name, view)); if (!item) return NULL; /* @@ -6910,11 +7145,11 @@ void TABLE::mark_index_columns(uint index, MY_BITMAP *bitmap) Restore to use normal column maps after key read NOTES - This reverse the change done by mark_columns_used_by_index + This reverse the change done by mark_index_columns WARNING For this to work, one must have the normal table maps in place - when calling mark_columns_used_by_index + when calling mark_index_columns */ void TABLE::restore_column_maps_after_keyread(MY_BITMAP *backup) @@ -7231,8 +7466,7 @@ void TABLE::mark_columns_per_binlog_row_image() If in RBR we may need to mark some extra columns, depending on the binlog-row-image command line argument. */ - if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) && - thd->is_current_stmt_binlog_format_row() && + if (file->row_logging && !ha_check_storage_engine_flag(s->db_type(), HTON_NO_BINLOG_ROW_OPT)) { /* if there is no PK, then mark all columns for the BI. */ @@ -7587,12 +7821,28 @@ void TABLE::restore_blob_values(String *blob_storage) bool TABLE::alloc_keys(uint key_count) { - key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count)); + KEY *new_key_info; + key_part_map *new_const_key_parts; + DBUG_ASSERT(s->tmp_table == INTERNAL_TMP_TABLE); + + if (!multi_alloc_root(&mem_root, + &new_key_info, sizeof(*key_info)*(s->keys+key_count), + &new_const_key_parts, + sizeof(*new_const_key_parts)*(s->keys+key_count), + NullS)) + return TRUE; if (s->keys) - memmove(key_info, s->key_info, sizeof(KEY)*s->keys); - s->key_info= key_info; + { + memmove(new_key_info, s->key_info, sizeof(*key_info) * s->keys); + memmove(new_const_key_parts, const_key_parts, + s->keys * sizeof(const_key_parts)); + } + s->key_info= key_info= new_key_info; + const_key_parts= new_const_key_parts; + bzero((char*) (const_key_parts + s->keys), + sizeof(*const_key_parts) * key_count); max_keys= s->keys+key_count; - return !(key_info); + return FALSE; } @@ -7651,14 +7901,9 @@ void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info, { key_part_info->store_length+= HA_KEY_NULL_LENGTH; } - if (field->type() == MYSQL_TYPE_BLOB || - field->type() == MYSQL_TYPE_GEOMETRY || - field->real_type() == MYSQL_TYPE_VARCHAR) - { - key_part_info->store_length+= HA_KEY_BLOB_LENGTH; - key_part_info->key_part_flag|= - field->type() == MYSQL_TYPE_BLOB ? HA_BLOB_PART: HA_VAR_LENGTH_PART; - } + + key_part_info->key_part_flag|= field->key_part_flag(); + key_part_info->store_length+= field->key_part_length_bytes(); key_part_info->type= (uint8) field->key_type(); key_part_info->key_type = @@ -8073,7 +8318,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.str); + my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0), hint->key_name.str, alias.str); return 1; } @@ -8178,11 +8423,10 @@ size_t max_row_length(TABLE *table, MY_BITMAP const *cols, const uchar *data) 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.str, table_list->table_name.str, - table_list->lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ, - MDL_TRANSACTION); + MDL_REQUEST_INIT(&table_list->mdl_request, MDL_key::TABLE, + 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); } @@ -8577,6 +8821,40 @@ void TABLE::evaluate_update_default_function() DBUG_VOID_RETURN; } +/** + Compare two records by a specific key (that has WITHOUT OVERLAPS clause) + + @return true, key values are equal and periods overlap + false, either key values differ or periods don't overlap + */ +bool TABLE::check_period_overlaps(const KEY &key, + const uchar *lhs, const uchar *rhs) +{ + DBUG_ASSERT(key.without_overlaps); + uint base_part_nr= key.user_defined_key_parts - 2; + for (uint part_nr= 0; part_nr < base_part_nr; part_nr++) + { + Field *f= key.key_part[part_nr].field; + if (key.key_part[part_nr].null_bit) + if (f->is_null_in_record(lhs) || f->is_null_in_record(rhs)) + return false; + uint kp_len= key.key_part[part_nr].length; + if (f->cmp_prefix(f->ptr_in_record(lhs), f->ptr_in_record(rhs), + kp_len) != 0) + return false; + } + + uint period_start= key.user_defined_key_parts - 1; + uint period_end= key.user_defined_key_parts - 2; + const Field *fs= key.key_part[period_start].field; + const Field *fe= key.key_part[period_end].field; + + if (fs->cmp(fe->ptr_in_record(lhs), fs->ptr_in_record(rhs)) <= 0) + return false; + if (fs->cmp(fs->ptr_in_record(lhs), fe->ptr_in_record(rhs)) >= 0) + return false; + return true; +} void TABLE::vers_update_fields() { @@ -9142,8 +9420,8 @@ void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock) /* we call it only when table is opened and it is "leaf" table*/ DBUG_ASSERT(table); lock_type= lock; - /* table->file->get_table() can be 0 for derived tables */ - if (table->file && table->file->get_table()) + /* If not derived tables */ + if (table->file && table->file->is_open()) table->file->set_lock_type(lock); if (is_merged_derived()) { @@ -9215,35 +9493,6 @@ void re_setup_keyinfo_hash(KEY *key_info) key_info->ext_key_parts= 1; key_info->flags&= ~HA_NOSAME; } -/** - @brief clone of current handler. - Creates a clone of handler used in update for - unique hash key. -*/ -void TABLE::clone_handler_for_update() -{ - if (this->update_handler) - return; - handler *update_handler= NULL; - if (!s->long_unique_table) - return; - update_handler= file->clone(s->normalized_path.str, - in_use->mem_root); - update_handler->ha_external_lock(in_use, F_RDLCK); - this->update_handler= update_handler; - return; -} - -/** - @brief Deletes update handler object -*/ -void TABLE::delete_update_handler() -{ - update_handler->ha_external_lock(in_use, F_UNLCK); - update_handler->ha_close(); - delete update_handler; - this->update_handler= NULL; -} LEX_CSTRING *fk_option_name(enum_fk_option opt) { @@ -9576,7 +9825,7 @@ bool TR_table::check(bool error) } Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]); - st_typelib *typelib= iso_level->typelib; + const st_typelib *typelib= iso_level->typelib; if (typelib->count != 4) goto wrong_enum; @@ -9602,12 +9851,12 @@ bool TR_table::check(bool error) return false; } -bool vers_select_conds_t::resolve_units(THD *thd) +bool vers_select_conds_t::check_units(THD *thd) { DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED); DBUG_ASSERT(start.item); - return start.resolve_unit(thd) || - end.resolve_unit(thd); + return start.check_unit(thd) || + end.check_unit(thd); } bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const @@ -9633,22 +9882,21 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const } -bool Vers_history_point::resolve_unit(THD *thd) +bool Vers_history_point::check_unit(THD *thd) { if (!item) return false; if (item->fix_fields_if_needed(thd, &item)) return true; - return item->this_item()->real_type_handler()-> - 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"); + const Type_handler *t= item->this_item()->real_type_handler(); + DBUG_ASSERT(t); + if (!t->vers()) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + t->name().ptr(), "FOR SYSTEM_TIME"); + return true; + } + return false; } @@ -9722,22 +9970,20 @@ bool TABLE::export_structure(THD *thd, Row_definition_list *defs) return false; } -/* +/** @brief - Initialize all the quick structures that are used to stored the + Initialize all the opt_range structures that are used to stored the estimates when the range optimizer is run. - @details - This is specifically needed when we read the TABLE structure from the - table cache. There can be some garbage data from previous queries - that need to be reset here. + As these are initialized by the range optimizer for all index + marked in opt_range_keys, we only mark the memory as undefined + to be able to find wrong usage of data with valgrind or MSAN. */ -void TABLE::initialize_quick_structures() +inline void TABLE::initialize_opt_range_structures() { - 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)); + TRASH_ALLOC((void*)&opt_range_keys, sizeof(opt_range_keys)); + TRASH_ALLOC(opt_range, s->keys * sizeof(*opt_range)); + TRASH_ALLOC(const_key_parts, s->keys * sizeof(*const_key_parts)); } /* |