summaryrefslogtreecommitdiff
path: root/sql/unireg.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/unireg.cc')
-rw-r--r--sql/unireg.cc287
1 files changed, 199 insertions, 88 deletions
diff --git a/sql/unireg.cc b/sql/unireg.cc
index f565fc97d86..d3a9b832aaf 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -40,11 +40,17 @@
#define ALLOCA_THRESHOLD 2048
static uint pack_keys(uchar *,uint, KEY *, ulong);
-static bool pack_header(THD *, uchar *, List<Create_field> &, uint, ulong, handler *);
+static bool pack_header(THD *, uchar *, List<Create_field> &, HA_CREATE_INFO *,
+ ulong, handler *);
static uint get_interval_id(uint *,List<Create_field> &, Create_field *);
-static bool pack_fields(uchar *, List<Create_field> &, ulong);
+static bool pack_fields(uchar **, List<Create_field> &, HA_CREATE_INFO*,
+ ulong);
+static void pack_constraints(uchar **buff, List<Virtual_column_info> *constr);
static size_t packed_fields_length(List<Create_field> &);
-static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, ulong);
+static size_t packed_constraints_length(THD *, HA_CREATE_INFO*,
+ List<Virtual_column_info> *);
+static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint,
+ ulong);
/*
write the length as
@@ -121,13 +127,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
create_info->null_bits++;
data_offset= (create_info->null_bits + 7) / 8;
- error= pack_header(thd, forminfo, create_fields, create_info->table_options,
+ error= pack_header(thd, forminfo, create_fields, create_info,
data_offset, db_file);
if (error)
DBUG_RETURN(frm);
- reclength=uint2korr(forminfo+266);
+ reclength= uint2korr(forminfo+266);
/* Calculate extra data segment length */
str_db_type= *hton_name(create_info->db_type);
@@ -219,8 +225,10 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
filepos= frm.length;
frm.length+= FRM_FORMINFO_SIZE; // forminfo
frm.length+= packed_fields_length(create_fields);
+ frm.length+= create_info->expression_length;
- if (frm.length > FRM_MAX_SIZE)
+ if (frm.length > FRM_MAX_SIZE ||
+ create_info->expression_length > UINT_MAX32)
{
my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table);
DBUG_RETURN(frm);
@@ -326,7 +334,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
}
memcpy(frm_ptr + filepos, forminfo, 288);
- if (pack_fields(frm_ptr + filepos + 288, create_fields, data_offset))
+ pos= frm_ptr + filepos + 288;
+ if (pack_fields(&pos, create_fields, create_info, data_offset))
goto err;
{
@@ -488,15 +497,107 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
} /* pack_keys */
+/**
+ Calculate and check length of stored expression (virtual, def, check)
+
+ Convert string to utf8, if it isn't already
+
+ @param thd Thread handler. Used for memory allocation
+ @param v_col Virtual expression. Can be 0
+ @param CREATE INFO For characterset
+ @param length Sum total lengths here
+
+ Note to make calls easier, one can call this with v_col == 0
+
+ @return 0 ok
+ @return 1 error (out of memory or wrong characters in expression)
+*/
+
+static bool add_expr_length(THD *thd, Virtual_column_info **v_col_ptr,
+ size_t *length)
+{
+ Virtual_column_info *v_col= *v_col_ptr;
+ if (!v_col)
+ return 0;
+
+ /*
+ Convert string to utf8 for storage.
+ */
+ if (!v_col->utf8)
+ {
+ /*
+ This v_col comes from the parser (e.g. CREATE TABLE) or
+ from old (before 10.2.1) frm.
+
+ We have to create a new Virtual_column_info as for alter table,
+ the current one may be shared with the original table.
+ */
+ Virtual_column_info *new_vcol= new (thd->mem_root) Virtual_column_info();
+ LEX_STRING to;
+ if (thd->copy_with_error(&my_charset_utf8mb4_general_ci,
+ &to,
+ thd->variables.character_set_client,
+ v_col->expr_str.str, v_col->expr_str.length))
+ return 1;
+ *new_vcol= *v_col;
+ new_vcol->expr_str= to;
+ new_vcol->utf8= 1;
+ *v_col_ptr= new_vcol;
+ v_col= new_vcol;
+ }
+
+ /*
+ Sum up the length of the expression string, it's optional name
+ and the header.
+ */
+ (*length)+= (FRM_VCOL_NEW_HEADER_SIZE + v_col->name.length +
+ v_col->expr_str.length);
+ return 0;
+}
+
+
+/*
+ pack_expression
+
+ The data is stored as:
+ 1 byte type (0 virtual, 1 virtual stored, 2 def, 3 check)
+ 2 bytes field_number
+ 2 bytes length of expression
+ 1 byte length of name
+ name
+ next bytes column expression (text data)
+*/
+
+static void pack_expression(uchar **buff, Virtual_column_info *vcol,
+ uint offset, uint type)
+{
+ (*buff)[0]= (uchar) type;
+ int2store((*buff)+1, offset);
+ /*
+ expr_str.length < 64K as we have checked that the total size of the
+ frm file is < 64K
+ */
+ int2store((*buff)+3, vcol->expr_str.length);
+ (*buff)[5]= vcol->name.length;
+ (*buff)+= FRM_VCOL_NEW_HEADER_SIZE;
+ memcpy((*buff), vcol->name.str, vcol->name.length);
+ (*buff)+= vcol->name.length;
+ memcpy((*buff), vcol->expr_str.str, vcol->expr_str.length);
+ (*buff)+= vcol->expr_str.length;
+}
+
+
/* Make formheader */
static bool pack_header(THD *thd, uchar *forminfo,
List<Create_field> &create_fields,
- uint table_options, ulong data_offset, handler *file)
+ HA_CREATE_INFO *create_info, ulong data_offset,
+ handler *file)
{
uint length,int_count,int_length,no_empty, int_parts;
uint time_stamp_pos,null_fields;
- ulong reclength, totlength, n_length, com_length, vcol_info_length;
+ uint table_options= create_info->table_options;
+ size_t reclength, totlength, n_length, com_length, expression_length;
DBUG_ENTER("pack_header");
if (create_fields.elements > MAX_FIELDS)
@@ -508,8 +609,19 @@ static bool pack_header(THD *thd, uchar *forminfo,
totlength= 0L;
reclength= data_offset;
no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0;
- com_length=vcol_info_length=0;
+ com_length= 0;
n_length=2L;
+ create_info->field_check_constraints= 0;
+
+ if (create_info->check_constraint_list->elements)
+ {
+ expression_length= packed_constraints_length(thd, create_info,
+ create_info->check_constraint_list);
+ if (!expression_length)
+ DBUG_RETURN(1); // Wrong characterset
+ }
+ else
+ expression_length= 0;
/* Check fields */
List_iterator<Create_field> it(create_fields);
@@ -520,30 +632,13 @@ static bool pack_header(THD *thd, uchar *forminfo,
ER_TOO_LONG_FIELD_COMMENT, field->field_name))
DBUG_RETURN(1);
- if (field->vcol_info)
- {
- uint col_expr_maxlen= field->virtual_col_expr_maxlen();
- uint tmp_len= my_charpos(system_charset_info,
- field->vcol_info->expr_str.str,
- field->vcol_info->expr_str.str +
- field->vcol_info->expr_str.length,
- col_expr_maxlen);
-
- if (tmp_len < field->vcol_info->expr_str.length)
- {
- my_error(ER_WRONG_STRING_LENGTH, MYF(0),
- field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION",
- col_expr_maxlen);
- DBUG_RETURN(1);
- }
- /*
- Sum up the length of the expression string and the length of the
- mandatory header to the total length of info on the defining
- expressions saved in the frm file for virtual columns.
- */
- vcol_info_length+= field->vcol_info->expr_str.length+
- FRM_VCOL_HEADER_SIZE(field->interval);
- }
+ if (add_expr_length(thd, &field->vcol_info, &expression_length))
+ DBUG_RETURN(1);
+ if (field->default_value && field->default_value->expr_str.length)
+ if (add_expr_length(thd, &field->default_value, &expression_length))
+ DBUG_RETURN(1);
+ if (add_expr_length(thd, &field->check_constraint, &expression_length))
+ DBUG_RETURN(1);
totlength+= field->length;
com_length+= field->comment.length;
@@ -554,8 +649,8 @@ static bool pack_header(THD *thd, uchar *forminfo,
MTYP_NOEMPTY_BIT);
no_empty++;
}
- /*
- We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE
+ /*
+ We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE
as auto-update field.
*/
if (field->sql_type == MYSQL_TYPE_TIMESTAMP &&
@@ -616,6 +711,8 @@ static bool pack_header(THD *thd, uchar *forminfo,
}
if (f_maybe_null(field->pack_flag))
null_fields++;
+ if (field->check_constraint)
+ create_info->field_check_constraints++;
}
int_length+=int_count*2; // 255 prefix + 0 suffix
@@ -625,23 +722,30 @@ static bool pack_header(THD *thd, uchar *forminfo,
my_error(ER_TOO_BIG_ROWSIZE, MYF(0), static_cast<long>(file->max_record_length()));
DBUG_RETURN(1);
}
+
+ if (expression_length)
+ {
+ expression_length+= FRM_VCOL_NEW_BASE_SIZE;
+ create_info->expression_length= expression_length;
+ }
+
/* Hack to avoid bugs with small static rows in MySQL */
reclength=MY_MAX(file->min_record_length(table_options),reclength);
if ((ulong) create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+
- n_length+int_length+com_length+vcol_info_length > 65535L ||
+ n_length+int_length+com_length+expression_length > 65535L ||
int_count > 255)
{
- my_message(ER_TOO_MANY_FIELDS, ER_THD(thd, ER_TOO_MANY_FIELDS), MYF(0));
+ my_message(ER_TOO_MANY_FIELDS, "Table definition is too large", MYF(0));
DBUG_RETURN(1);
}
bzero((char*)forminfo,FRM_FORMINFO_SIZE);
length=(create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+n_length+int_length+
- com_length+vcol_info_length);
+ com_length+expression_length);
int2store(forminfo,length);
forminfo[256] = 0;
int2store(forminfo+258,create_fields.elements);
- int2store(forminfo+260,0);
+ int2store(forminfo+260,0); // Screen length, not used anymore
int2store(forminfo+262,totlength);
int2store(forminfo+264,no_empty);
int2store(forminfo+266,reclength);
@@ -654,7 +758,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
int2store(forminfo+280,22); /* Rows needed */
int2store(forminfo+282,null_fields);
int2store(forminfo+284,com_length);
- int2store(forminfo+286,vcol_info_length);
+ int2store(forminfo+286,expression_length);
DBUG_RETURN(0);
} /* pack_header */
@@ -707,26 +811,46 @@ static size_t packed_fields_length(List<Create_field> &create_fields)
}
length++;
}
- if (field->vcol_info)
- {
- length+= field->vcol_info->expr_str.length +
- FRM_VCOL_HEADER_SIZE(field->interval);
- }
+
length+= FCOMP;
length+= strlen(field->field_name)+1;
length+= field->comment.length;
}
- length++;
- length++;
+ length+= 2;
DBUG_RETURN(length);
}
+
+static size_t packed_constraints_length(THD *thd, HA_CREATE_INFO *info,
+ List<Virtual_column_info> *constr)
+{
+ List_iterator<Virtual_column_info> it(*constr);
+ size_t length= 0;
+ Virtual_column_info *check;
+
+ while ((check= it++))
+ if (add_expr_length(thd, it.ref(), &length))
+ return 0;
+ return length;
+}
+
+static void pack_constraints(uchar **buff, List<Virtual_column_info> *constr)
+{
+ List_iterator<Virtual_column_info> it(*constr);
+ Virtual_column_info *check;
+ while ((check= it++))
+ pack_expression(buff, check, UINT_MAX32, 4);
+}
+
+
/* Save fields, fieldnames and intervals */
-static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
+static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
+ HA_CREATE_INFO *create_info,
ulong data_offset)
{
- uint int_count, comment_length= 0, vcol_info_length=0;
+ uchar *buff= *buff_arg;
+ uint int_count, comment_length= 0;
Create_field *field;
DBUG_ENTER("pack_fields");
@@ -736,7 +860,6 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
while ((field=it++))
{
uint recpos;
- uint cur_vcol_expr_len= 0;
int2store(buff+3, field->length);
/* The +1 is here becasue the col offset in .frm file have offset 1 */
recpos= field->offset+1 + (uint) data_offset;
@@ -745,7 +868,7 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
DBUG_ASSERT(field->unireg_check < 256);
buff[10]= (uchar) field->unireg_check;
buff[12]= (uchar) field->interval_id;
- buff[13]= (uchar) field->sql_type;
+ buff[13]= (uchar) field->sql_type;
if (field->sql_type == MYSQL_TYPE_GEOMETRY)
{
buff[11]= 0;
@@ -763,17 +886,7 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
{
buff[11]= buff[14]= 0; // Numerical
}
- if (field->vcol_info)
- {
- /*
- Use the interval_id place in the .frm file to store the length of
- the additional data saved for the virtual field
- */
- buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length +
- FRM_VCOL_HEADER_SIZE(field->interval);
- vcol_info_length+= cur_vcol_expr_len;
- buff[13]= (uchar) MYSQL_TYPE_VIRTUAL;
- }
+
int2store(buff+15, field->comment.length);
comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id);
@@ -844,7 +957,6 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
*buff++= sep;
}
*buff++= 0;
-
}
}
}
@@ -857,35 +969,31 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
buff+= field->comment.length;
}
}
- if (vcol_info_length)
+
+ if (create_info->expression_length)
{
+ /* Store header for packed fields (extra space for future) */
+ bzero(buff, FRM_VCOL_NEW_BASE_SIZE);
+ buff+= FRM_VCOL_NEW_BASE_SIZE;
+
+ /* Store expressions */
it.rewind();
- while ((field=it++))
+ for (uint field_nr=0 ; (field= it++) ; field_nr++)
{
- /*
- Pack each virtual field as follows:
- byte 1 = interval_id == 0 ? 1 : 2
- byte 2 = sql_type
- byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
- [byte 4] = possible interval_id for sql_type
- next byte ... = virtual column expression (text data)
- */
- if (field->vcol_info && field->vcol_info->expr_str.length)
- {
- *buff++= (uchar) (1 + MY_TEST(field->interval));
- *buff++= (uchar) field->sql_type;
- *buff++= (uchar) field->vcol_info->stored_in_db;
- if (field->interval)
- *buff++= (uchar) field->interval_id;
- memcpy(buff, field->vcol_info->expr_str.str, field->vcol_info->expr_str.length);
- buff+= field->vcol_info->expr_str.length;
- }
+ if (field->vcol_info)
+ pack_expression(&buff, field->vcol_info, field_nr,
+ field->vcol_info->stored_in_db ? 1 : 0);
+ if (field->default_value && field->default_value->expr_str.length)
+ pack_expression(&buff, field->default_value, field_nr, 2);
+ if (field->check_constraint)
+ pack_expression(&buff, field->check_constraint, field_nr, 3);
}
+ pack_constraints(&buff, create_info->check_constraint_list);
}
+ *buff_arg= buff;
DBUG_RETURN(0);
}
-
/* save an empty record on start of formfile */
static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
@@ -931,7 +1039,10 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
field->sql_type,
field->charset,
field->geom_type, field->srid,
- field->unireg_check,
+ field->unireg_check == Field::TIMESTAMP_DNUN_FIELD
+ ? Field::TIMESTAMP_UN_FIELD
+ : field->unireg_check == Field::TIMESTAMP_DN_FIELD
+ ? Field::NONE : field->unireg_check,
field->save_interval ? field->save_interval :
field->interval,
field->field_name);
@@ -955,9 +1066,9 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
type= (Field::utype) MTYP_TYPENR(field->unireg_check);
- if (field->def)
+ if (field->default_value && !field->has_default_expression())
{
- int res= field->def->save_in_field(regfield, 1);
+ int res= field->default_value->expr_item->save_in_field(regfield, 1);
/* If not ok or warning of level 'note' */
if (res != 0 && res != 3)
{