summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2019-07-11 14:50:39 +0400
committerAlexander Barkov <bar@mariadb.com>2019-07-11 14:50:39 +0400
commit040bbebce9eb75167086f4b7815e8280686c3629 (patch)
tree83a05ef1f0712f6000e01a868f0c08858ea016f3
parent265a7d1613d29e2bdd4c91a5a75971b331519dbe (diff)
downloadmariadb-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.result21
-rw-r--r--mysql-test/main/frm-debug.test19
-rw-r--r--sql/sql_string.h13
-rw-r--r--sql/sql_type.cc14
-rw-r--r--sql/sql_type.h4
-rw-r--r--sql/table.cc103
-rw-r--r--sql/unireg.cc91
-rw-r--r--sql/unireg.h3
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 {