diff options
Diffstat (limited to 'sql/unireg.cc')
-rw-r--r-- | sql/unireg.cc | 287 |
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) { |