diff options
author | Alexander Barkov <bar@mariadb.com> | 2019-07-11 14:50:39 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2019-07-11 14:50:39 +0400 |
commit | 040bbebce9eb75167086f4b7815e8280686c3629 (patch) | |
tree | 83a05ef1f0712f6000e01a868f0c08858ea016f3 | |
parent | 265a7d1613d29e2bdd4c91a5a75971b331519dbe (diff) | |
download | mariadb-git-bb-10.5-bar-mdev20042.tar.gz |
MDEV-20042 Implement EXTRA2_FIELD_DATA_TYPE_INFO in FRMbb-10.5-bar-mdev20042
-rw-r--r-- | mysql-test/main/frm-debug.result | 21 | ||||
-rw-r--r-- | mysql-test/main/frm-debug.test | 19 | ||||
-rw-r--r-- | sql/sql_string.h | 13 | ||||
-rw-r--r-- | sql/sql_type.cc | 14 | ||||
-rw-r--r-- | sql/sql_type.h | 4 | ||||
-rw-r--r-- | sql/table.cc | 103 | ||||
-rw-r--r-- | sql/unireg.cc | 91 | ||||
-rw-r--r-- | sql/unireg.h | 3 |
8 files changed, 267 insertions, 1 deletions
diff --git a/mysql-test/main/frm-debug.result b/mysql-test/main/frm-debug.result new file mode 100644 index 00000000000..d4d71caacbd --- /dev/null +++ b/mysql-test/main/frm-debug.result @@ -0,0 +1,21 @@ +# +# MDEV-20042 Implement EXTRA2_FIELD_DATA_TYPE_INFO in FRM +# +SET SESSION debug_dbug="+d,frm_data_type_info"; +CREATE TABLE t1 (c01 INT, c02 CHAR(20), c03 TEXT, c04 DOUBLE); +Warnings: +Note 1105 build_frm_image: Field data type info length: 0 +DROP TABLE t1; +SET SESSION debug_dbug="-d,frm_data_type_info"; +SET SESSION debug_dbug="+d,frm_data_type_info"; +SET SESSION debug_dbug="+d,frm_data_type_info_emulate"; +CREATE TABLE t1 (c01 INT, c02 CHAR(20), c03 TEXT, c04 DOUBLE); +Warnings: +Note 1105 build_frm_image: Field data type info length: 12 +Note 1105 DBUG: [0] name='c01' type_info='' +Note 1105 DBUG: [1] name='c02' type_info='char' +Note 1105 DBUG: [2] name='c03' type_info='blob' +Note 1105 DBUG: [3] name='c04' type_info='' +DROP TABLE t1; +SET SESSION debug_dbug="-d,frm_data_type_info_emulate"; +SET SESSION debug_dbug="-d,frm_data_type_info"; diff --git a/mysql-test/main/frm-debug.test b/mysql-test/main/frm-debug.test new file mode 100644 index 00000000000..95207354bdc --- /dev/null +++ b/mysql-test/main/frm-debug.test @@ -0,0 +1,19 @@ +--source include/have_debug.inc + +--echo # +--echo # MDEV-20042 Implement EXTRA2_FIELD_DATA_TYPE_INFO in FRM +--echo # + +# This should have empty EXTRA2_FIELD_DATA_TYPE_INFO +SET SESSION debug_dbug="+d,frm_data_type_info"; +CREATE TABLE t1 (c01 INT, c02 CHAR(20), c03 TEXT, c04 DOUBLE); +DROP TABLE t1; +SET SESSION debug_dbug="-d,frm_data_type_info"; + +# This should have non-empty EXTRA2_FIELD_DATA_TYPE_INFO +SET SESSION debug_dbug="+d,frm_data_type_info"; +SET SESSION debug_dbug="+d,frm_data_type_info_emulate"; +CREATE TABLE t1 (c01 INT, c02 CHAR(20), c03 TEXT, c04 DOUBLE); +DROP TABLE t1; +SET SESSION debug_dbug="-d,frm_data_type_info_emulate"; +SET SESSION debug_dbug="-d,frm_data_type_info"; diff --git a/sql/sql_string.h b/sql/sql_string.h index b6df9f29ced..1eac2200625 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -528,6 +528,10 @@ public: q_append(s, size); return false; } + bool append(const LEX_CSTRING &s) + { + return append(s.str, s.length); + } bool append(const Binary_string &s) { return append(s.ptr(), s.length()); @@ -1001,6 +1005,15 @@ public: }; +template<size_t buff_sz> +class BinaryStringBuffer : public Binary_string +{ + char buff[buff_sz]; +public: + BinaryStringBuffer() : Binary_string(buff, buff_sz) { length(0); } +}; + + class String_space: public String { public: diff --git a/sql/sql_type.cc b/sql/sql_type.cc index b912197103f..0268e36ca5b 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -8367,6 +8367,20 @@ const Name & Type_handler_timestamp_common::default_value() const /***************************************************************************/ +bool Type_handler::Column_definition_data_type_info_image(Binary_string *to, + const Column_definition &def) + const +{ + // Have *some* columns write type info (let's use string fields as an example) + DBUG_EXECUTE_IF("frm_data_type_info_emulate", + if (cmp_type() == STRING_RESULT) + return to->append(name().lex_cstring());); + return false; +} + + +/***************************************************************************/ + LEX_CSTRING Charset::collation_specific_name() const { /* diff --git a/sql/sql_type.h b/sql/sql_type.h index 4ea3f860614..0182913b618 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -3076,6 +3076,7 @@ public: } const char *ptr() const { return LEX_CSTRING::str; } uint length() const { return (uint) LEX_CSTRING::length; } + const LEX_CSTRING &lex_cstring() const { return *this; } bool eq(const LEX_CSTRING &other) const { return !my_strnncoll(system_charset_info, @@ -3490,6 +3491,9 @@ public: { return 0; } + virtual bool Column_definition_data_type_info_image(Binary_string *to, + const Column_definition &def) + const; // Check if the implicit default value is Ok in the current sql_mode virtual bool validate_implicit_default_value(THD *thd, const Column_definition &def) diff --git a/sql/table.cc b/sql/table.cc index d65b4c7af08..ea333cb2ecd 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -59,6 +59,7 @@ struct extra2_fields LEX_CUSTRING field_flags; LEX_CUSTRING system_period; LEX_CUSTRING application_period; + LEX_CUSTRING field_data_type_info; void reset() { bzero((void*)this, sizeof(*this)); } }; @@ -1508,6 +1509,12 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields) fields->application_period.str= extra2; fields->application_period.length= length; break; + case EXTRA2_FIELD_DATA_TYPE_INFO: + if (fields->field_data_type_info.str) + DBUG_RETURN(true); + fields->field_data_type_info.str= extra2; + fields->field_data_type_info.length= length; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1522,6 +1529,86 @@ 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 @@ -1572,6 +1659,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint ext_key_parts= 0; plugin_ref se_plugin= 0; bool vers_can_native= false; + Field_data_type_info_array field_data_type_info_array; MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; @@ -2111,6 +2199,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, status_var_increment(thd->status_var.feature_application_time_periods); } + 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; @@ -2195,6 +2288,16 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, strpos, &extra2.gis)) goto err; + + if (field_data_type_info_array.count()) + { + 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) field_data_type_info_array.element(i).type_info().length, + field_data_type_info_array.element(i).type_info().str);); + } } if (((uint) strpos[10]) & MYSQL57_GENERATED_FIELD) diff --git a/sql/unireg.cc b/sql/unireg.cc index b82927a9d47..7130b3e5d8a 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -171,6 +171,61 @@ static uint gis_field_options_image(uchar *buff, } +class Field_data_type_info_image: public BinaryStringBuffer<512> +{ + static uchar *store_length(uchar *pos, ulonglong length) + { + return net_store_length(pos, length); + } + static uchar *store_string(uchar *pos, const LEX_CSTRING &str) + { + pos= store_length(pos, str.length); + memcpy(pos, str.str, str.length); + return pos + str.length; + } + static uint store_length_required_length(ulonglong length) + { + return net_length_size(length); + } +public: + Field_data_type_info_image() { } + bool append(uint fieldnr, const Column_definition &def) + { + BinaryStringBuffer<64> type_info; + if (def.type_handler()-> + Column_definition_data_type_info_image(&type_info, def) || + type_info.length() > 0xFFFF/*Some reasonable limit*/) + return true; // Error + if (!type_info.length()) + return false; + size_t need_length= store_length_required_length(fieldnr) + + store_length_required_length(type_info.length()) + + type_info.length(); + if (reserve(need_length)) + return true; // Error + uchar *pos= (uchar *) end(); + pos= store_length(pos, fieldnr); + pos= store_string(pos, type_info.lex_cstring()); + size_t new_length= (const char *) pos - ptr(); + DBUG_ASSERT(new_length < alloced_length()); + length((uint32) new_length); + return false; + } + bool append(List<Create_field> &fields) + { + uint fieldnr= 0; + Create_field *field; + List_iterator<Create_field> it(fields); + for (field= it++; field; field= it++, fieldnr++) + { + if (append(fieldnr, *field)) + return true; // Error + } + return false; + } +}; + + /** Create a frm (table definition) file @@ -210,6 +265,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, uchar *frm_ptr, *pos; LEX_CUSTRING frm= {0,0}; StringBuffer<MAX_FIELD_WIDTH> vcols; + Field_data_type_info_image field_data_type_info_image; DBUG_ENTER("build_frm_image"); /* If fixed row records, we need one bit to check for deleted rows */ @@ -263,6 +319,22 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, gis_extra2_len= gis_field_options_image(NULL, create_fields); DBUG_PRINT("info", ("Options length: %u", options_len)); + if (field_data_type_info_image.append(create_fields)) + { + my_printf_error(ER_CANT_CREATE_TABLE, + "Cannot create table %`s: " + "Building the field data type info image failed.", + MYF(0), table.str); + DBUG_RETURN(frm); + } + DBUG_PRINT("info", ("Field data type info length: %u", + (uint) field_data_type_info_image.length())); + DBUG_EXECUTE_IF("frm_data_type_info", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, + "build_frm_image: Field data type info length: %u", + (uint) field_data_type_info_image.length());); + if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN, ER_TOO_LONG_TABLE_COMMENT, table.str)) DBUG_RETURN(frm); @@ -308,6 +380,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, if (gis_extra2_len) extra2_size+= 1 + extra2_str_size(gis_extra2_len); + if (field_data_type_info_image.length()) + extra2_size+= 1 + extra2_str_size(field_data_type_info_image.length()); + if (create_info->versioned()) { extra2_size+= 1 + extra2_str_size(2 * frm_fieldno_size); @@ -380,6 +455,22 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table, pos+= gis_field_options_image(pos, create_fields); } + if (field_data_type_info_image.length()) + { + if (field_data_type_info_image.length() > 0xFFFF) + { + my_printf_error(ER_CANT_CREATE_TABLE, + "Cannot create table %`s: " + "field data type info image is too large. " + "Decrease the number of columns with " + "extended data types.", + MYF(0), table.str); + goto err; + } + *pos= EXTRA2_FIELD_DATA_TYPE_INFO; + pos= extra2_write_str(pos + 1, field_data_type_info_image.lex_cstring()); + } + // PERIOD if (create_info->period_info.is_set()) { diff --git a/sql/unireg.h b/sql/unireg.h index 8e9fa27ea6a..419fbc4bd80 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -176,7 +176,8 @@ enum extra2_frm_value_type { #define EXTRA2_ENGINE_IMPORTANT 128 EXTRA2_ENGINE_TABLEOPTS=128, - EXTRA2_FIELD_FLAGS=129 + EXTRA2_FIELD_FLAGS=129, + EXTRA2_FIELD_DATA_TYPE_INFO=130 }; enum extra2_field_flags { |