diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-05-17 12:21:39 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-05-17 12:21:39 +0400 |
commit | fba7fbbc5c7bb1d05488108a29b854ee8ef0066a (patch) | |
tree | c18d848238c5291d42b1fd5b827d58055b5a59d2 /sql/sql_table.cc | |
parent | f1b729d35270383048842f2023a5c42769d6637f (diff) | |
download | mariadb-git-fba7fbbc5c7bb1d05488108a29b854ee8ef0066a.tar.gz |
MDEV-9397 Split field.cc:calc_pack_length() into virtual methods in Type_handler
- Adding new virtual methods in Type_handler:
* Column_definition_prepare_stage1()
* Column_definition_prepare_stage2()
* calc_pack_length()
- Using new methods to remove type specific code in:
* Global function calc_pack_length()
* Column_definition::prepare_create_field()
* The loop body mysql_prepare_create_table()
* Column_definition::sp_prepare_create_field()
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 474 |
1 files changed, 220 insertions, 254 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f985f6e73fc..e202552ac8a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2845,23 +2845,82 @@ bool check_duplicates_in_interval(const char *set_or_name, } +bool Column_definition::prepare_stage2_blob(handler *file, + ulonglong table_flags, + uint field_flags) +{ + if (table_flags & HA_NO_BLOBS) + { + my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type()); + return true; + } + pack_flag= field_flags | + pack_length_to_packflag(pack_length - portable_sizeof_char_ptr); + if (charset->state & MY_CS_BINSORT) + pack_flag|= FIELDFLAG_BINARY; + length= 8; // Unireg field length + return false; +} + + +bool Column_definition::prepare_stage2_typelib(const char *type_name, + uint field_flags, + uint *dup_val_count) +{ + pack_flag= pack_length_to_packflag(pack_length) | field_flags; + if (charset->state & MY_CS_BINSORT) + pack_flag|= FIELDFLAG_BINARY; + return check_duplicates_in_interval(type_name, field_name.str, interval, + charset, dup_val_count); +} + + +uint Column_definition::pack_flag_numeric(uint dec) const +{ + return (FIELDFLAG_NUMBER | + (flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) | + (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | + (dec << FIELDFLAG_DEC_SHIFT)); +} + + +bool Column_definition::prepare_stage2_varchar(ulonglong table_flags) +{ +#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR + if (table_flags & HA_NO_VARCHAR) + { + /* convert VARCHAR to CHAR because handler is not yet up to date */ + set_handler(&type_handler_var_string); + pack_length= type_handler()->calc_pack_length((uint) length); + if ((length / charset->mbmaxlen) > MAX_FIELD_CHARLENGTH) + { + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str, + static_cast<ulong>(MAX_FIELD_CHARLENGTH)); + return true; + } + } +#endif + pack_flag= (charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0; + return false; +} + + /* Prepare a Column_definition instance for packing Members such as pack_flag are valid after this call. - @param IN/OUT blob_columns - count for BLOBs + @param IN handler - storage engine handler, + or NULL if preparing for an SP variable @param IN table_flags - table flags @retval false - ok @retval true - error (not supported type, bad definition, etc) */ -bool Column_definition::prepare_create_field(uint *blob_columns, - ulonglong table_flags) +bool Column_definition::prepare_stage2(handler *file, + ulonglong table_flags) { - uint dup_val_count; - uint decimals_orig= decimals; - DBUG_ENTER("Column_definition::prepare_create_field"); + DBUG_ENTER("Column_definition::prepare_stage2"); /* This code came from mysql_prepare_create_table. @@ -2869,122 +2928,9 @@ bool Column_definition::prepare_create_field(uint *blob_columns, */ DBUG_ASSERT(charset); - switch (real_field_type()) { - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_LONG_BLOB: - pack_flag= FIELDFLAG_BLOB | - pack_length_to_packflag(pack_length - portable_sizeof_char_ptr); - if (charset->state & MY_CS_BINSORT) - pack_flag|= FIELDFLAG_BINARY; - length= 8; // Unireg field length - (*blob_columns)++; - break; - case MYSQL_TYPE_GEOMETRY: -#ifdef HAVE_SPATIAL - if (!(table_flags & HA_CAN_GEOMETRY)) - { - my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY"); - DBUG_RETURN(1); - } - pack_flag= FIELDFLAG_GEOM | - pack_length_to_packflag(pack_length - portable_sizeof_char_ptr); - if (charset->state & MY_CS_BINSORT) - pack_flag|= FIELDFLAG_BINARY; - length= 8; // Unireg field length - (*blob_columns)++; - break; -#else - my_error(ER_FEATURE_DISABLED, MYF(0), - sym_group_geom.name, sym_group_geom.needed_define); + if (type_handler()->Column_definition_prepare_stage2(this, file, table_flags)) DBUG_RETURN(true); -#endif /*HAVE_SPATIAL*/ - case MYSQL_TYPE_VARCHAR: -#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR - if (table_flags & HA_NO_VARCHAR) - { - /* convert VARCHAR to CHAR because handler is not yet up to date */ - set_handler(&type_handler_var_string); - pack_length= calc_pack_length(real_field_type(), (uint) length); - if ((length / charset->mbmaxlen) > MAX_FIELD_CHARLENGTH) - { - my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str, - static_cast<ulong>(MAX_FIELD_CHARLENGTH)); - DBUG_RETURN(true); - } - } -#endif - /* fall through */ - case MYSQL_TYPE_STRING: - pack_flag= 0; - if (charset->state & MY_CS_BINSORT) - pack_flag|= FIELDFLAG_BINARY; - break; - case MYSQL_TYPE_ENUM: - pack_flag= pack_length_to_packflag(pack_length) | FIELDFLAG_INTERVAL; - if (charset->state & MY_CS_BINSORT) - pack_flag|= FIELDFLAG_BINARY; - if (check_duplicates_in_interval("ENUM", field_name.str, interval, - charset, &dup_val_count)) - DBUG_RETURN(true); - break; - case MYSQL_TYPE_SET: - pack_flag= pack_length_to_packflag(pack_length) | FIELDFLAG_BITFIELD; - if (charset->state & MY_CS_BINSORT) - pack_flag|= FIELDFLAG_BINARY; - if (check_duplicates_in_interval("SET", field_name.str, interval, - charset, &dup_val_count)) - DBUG_RETURN(true); - /* Check that count of unique members is not more then 64 */ - if (interval->count - dup_val_count > sizeof(longlong)*8) - { - my_error(ER_TOO_BIG_SET, MYF(0), field_name.str); - DBUG_RETURN(true); - } - break; - case MYSQL_TYPE_DATE: // Rest of string types - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIME2: - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_NULL: - pack_flag= f_settype((uint) real_field_type()); - break; - case MYSQL_TYPE_BIT: - /* - We have sql_field->pack_flag already set here, see - mysql_prepare_create_table(). - */ - break; - case MYSQL_TYPE_NEWDECIMAL: - pack_flag= (FIELDFLAG_NUMBER | - (flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) | - (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | - (decimals_orig << FIELDFLAG_DEC_SHIFT)); - break; - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - /* - User specified FLOAT() or DOUBLE() without precision. Change to - FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB - versions. - */ - if (decimals_orig >= FLOATING_POINT_DECIMALS) - decimals_orig= FLOATING_POINT_DECIMALS; - /* fall-trough */ - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_TIMESTAMP2: - /* fall-through */ - default: - pack_flag= (FIELDFLAG_NUMBER | - (flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) | - (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | - f_settype((uint) real_field_type()) | - (decimals_orig << FIELDFLAG_DEC_SHIFT)); - break; - } + if (!(flags & NOT_NULL_FLAG) || (vcol_info)) /* Make virtual columns allow NULL values */ pack_flag|= FIELDFLAG_MAYBE_NULL; @@ -3007,7 +2953,7 @@ bool Column_definition::prepare_create_field(uint *blob_columns, cs Character set */ -CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, +CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, HA_CREATE_INFO *create_info) { CHARSET_INFO *cs= sql_field->charset; @@ -3146,6 +3092,143 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, } +bool Column_definition::prepare_stage1_typelib(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + /* + Pass the last parameter to prepare_interval_field() as follows: + - If we are preparing for an SP variable (file is NULL), we pass "false", + to force allocation and full copying of TYPELIB values on the given + mem_root, even if no character set conversion is needed. This is needed + because a life cycle of an SP variable is longer than the current query. + + - If we are preparing for a CREATE TABLE, (file != NULL), we pass "true". + This will create the typelib in runtime memory - we will free the + occupied memory at the same time when we free this + sql_field -- at the end of execution. + Pass "true" as the last argument to reuse "interval_list" + values in "interval" in cases when no character conversion is needed, + to avoid extra copying. + */ + if (prepare_interval_field(mem_root, file != NULL)) + return true; // E.g. wrong values with commas: SET('a,b') + create_length_to_internal_length_typelib(); + + DBUG_ASSERT(file || !default_value); // SP variables have no default_value + if (default_value && default_value->expr->basic_const_item()) + { + if ((charset != default_value->expr->collation.collation && + prepare_stage1_convert_default(thd, mem_root, charset)) || + prepare_stage1_check_typelib_default()) + return true; + } + return false; +} + + +bool Column_definition::prepare_stage1_string(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + create_length_to_internal_length_string(); + if (prepare_blob_field(thd)) + return true; + DBUG_ASSERT(file || !default_value); // SP variables have no default_value + /* + Convert the default value from client character + set into the column character set if necessary. + We can only do this for constants as we have not yet run fix_fields. + */ + if (default_value && + default_value->expr->basic_const_item() && + charset != default_value->expr->collation.collation) + { + if (prepare_stage1_convert_default(thd, mem_root, charset)) + return true; + } + return false; +} + + +bool Column_definition::prepare_stage1_bit(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + pack_flag= FIELDFLAG_NUMBER; + if (!(table_flags & HA_CAN_BIT_FIELD)) + pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; + create_length_to_internal_length_bit(); + return false; +} + + +bool Column_definition::prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + return type_handler()->Column_definition_prepare_stage1(thd, mem_root, + this, file, + table_flags); +} + + +bool Column_definition::prepare_stage1_convert_default(THD *thd, + MEM_ROOT *mem_root, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(thd->mem_root == mem_root); + Item *item; + if (!(item= default_value->expr->safe_charset_converter(thd, cs))) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + return true; // Could not convert + } + /* Fix for prepare statement */ + thd->change_item_tree(&default_value->expr, item); + return false; +} + + +bool Column_definition::prepare_stage1_check_typelib_default() +{ + StringBuffer<MAX_FIELD_WIDTH> str; + String *def= default_value->expr->val_str(&str); + bool not_found; + if (def == NULL) /* SQL "NULL" maps to NULL */ + { + not_found= flags & NOT_NULL_FLAG; + } + else + { + not_found= false; + if (real_field_type() == MYSQL_TYPE_SET) + { + char *not_used; + uint not_used2; + find_set(interval, def->ptr(), def->length(), + charset, ¬_used, ¬_used2, ¬_found); + } + else /* MYSQL_TYPE_ENUM */ + { + def->length(charset->cset->lengthsp(charset, + def->ptr(), def->length())); + not_found= !find_type2(interval, def->ptr(), def->length(), charset); + } + } + if (not_found) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + return true; + } + return false; +} + + /* Preparation for table creation @@ -3180,7 +3263,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { const char *key_name; Create_field *sql_field,*dup_field; - uint field,null_fields,blob_columns,max_key_length; + uint field,null_fields,max_key_length; ulong record_offset= 0; KEY *key_info; KEY_PART_INFO *key_part_info; @@ -3193,7 +3276,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); - null_fields=blob_columns=0; + null_fields= 0; create_info->varchar= 0; max_key_length= file->max_key_length(); @@ -3215,8 +3298,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, select_field_pos= alter_info->create_list.elements - select_field_count; for (field_no=0; (sql_field=it++) ; field_no++) { - CHARSET_INFO *save_cs; - /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're @@ -3224,105 +3305,18 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ sql_field->length= sql_field->char_length; /* Set field charset. */ - save_cs= sql_field->charset= get_sql_field_charset(sql_field, create_info); + sql_field->charset= get_sql_field_charset(sql_field, create_info); if ((sql_field->flags & BINCMP_FLAG) && - !(sql_field->charset= find_bin_collation(sql_field->charset))) - DBUG_RETURN(TRUE); - - if (sql_field->real_field_type() == MYSQL_TYPE_SET || - sql_field->real_field_type() == MYSQL_TYPE_ENUM) - { - /* - Create the typelib in runtime memory - we will free the - occupied memory at the same time when we free this - sql_field -- at the end of execution. - Pass "true" as the last argument to reuse "interval_list" - values in "interval" in cases when no character conversion is needed, - to avoid extra copying. - */ - if (sql_field->prepare_interval_field(thd->mem_root, true)) - DBUG_RETURN(true); // E.g. wrong values with commas: SET('a,b') - } - - if (sql_field->real_field_type() == MYSQL_TYPE_BIT) - { - sql_field->pack_flag= FIELDFLAG_NUMBER; - if (file->ha_table_flags() & HA_CAN_BIT_FIELD) - total_uneven_bit_length+= sql_field->length & 7; - else - sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; - } - - sql_field->create_length_to_internal_length(); - if (sql_field->prepare_blob_field(thd)) - DBUG_RETURN(TRUE); - - /* - Convert the default value from client character - set into the column character set if necessary. - We can only do this for constants as we have not yet run fix_fields. - */ - if (sql_field->default_value && - sql_field->default_value->expr->basic_const_item() && - save_cs != sql_field->default_value->expr->collation.collation && - (sql_field->real_field_type() == MYSQL_TYPE_VAR_STRING || - sql_field->real_field_type() == MYSQL_TYPE_STRING || - sql_field->real_field_type() == MYSQL_TYPE_SET || - sql_field->real_field_type() == MYSQL_TYPE_TINY_BLOB || - sql_field->real_field_type() == MYSQL_TYPE_MEDIUM_BLOB || - sql_field->real_field_type() == MYSQL_TYPE_LONG_BLOB || - sql_field->real_field_type() == MYSQL_TYPE_BLOB || - sql_field->real_field_type() == MYSQL_TYPE_ENUM)) - { - Item *item; - if (!(item= sql_field->default_value->expr-> - safe_charset_converter(thd, save_cs))) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name.str); - DBUG_RETURN(TRUE); - } - /* Fix for prepare statement */ - thd->change_item_tree(&sql_field->default_value->expr, item); - } + !(sql_field->charset= find_bin_collation(sql_field->charset))) + DBUG_RETURN(true); - if (sql_field->default_value && - sql_field->default_value->expr->basic_const_item() && - (sql_field->real_field_type() == MYSQL_TYPE_SET || - sql_field->real_field_type() == MYSQL_TYPE_ENUM)) - { - StringBuffer<MAX_FIELD_WIDTH> str; - String *def= sql_field->default_value->expr->val_str(&str); - bool not_found; - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - not_found= sql_field->flags & NOT_NULL_FLAG; - } - else - { - not_found= false; - if (sql_field->real_field_type() == MYSQL_TYPE_SET) - { - char *not_used; - uint not_used2; - find_set(sql_field->interval, def->ptr(), def->length(), - sql_field->charset, ¬_used, ¬_used2, ¬_found); - } - else /* MYSQL_TYPE_ENUM */ - { - def->length(sql_field->charset->cset->lengthsp(sql_field->charset, - def->ptr(), def->length())); - not_found= !find_type2(sql_field->interval, def->ptr(), - def->length(), sql_field->charset); - } - } + if (sql_field->prepare_stage1(thd, thd->mem_root, + file, file->ha_table_flags())) + DBUG_RETURN(true); - if (not_found) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name.str); - DBUG_RETURN(TRUE); - } - } + if (sql_field->real_field_type() == MYSQL_TYPE_BIT && + file->ha_table_flags() & HA_CAN_BIT_FIELD) + total_uneven_bit_length+= sql_field->length & 7; if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; @@ -3419,7 +3413,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { DBUG_ASSERT(sql_field->charset != 0); - if (sql_field->prepare_create_field(&blob_columns, file->ha_table_flags())) + if (sql_field->prepare_stage2(file, file->ha_table_flags())) DBUG_RETURN(TRUE); if (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) create_info->varchar= TRUE; @@ -3461,12 +3455,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(TRUE); } - if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS)) - { - my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type()); - DBUG_RETURN(TRUE); - } - /* CREATE TABLE[with auto_increment column] SELECT is unsafe as the rows inserted in the created table depends on the order of the rows fetched @@ -4237,7 +4225,7 @@ bool Column_definition::prepare_blob_field(THD *thd) { /* The user has given a length to the blob column */ set_handler(Type_handler::blob_type_handler(length)); - pack_length= calc_pack_length(real_field_type(), 0); + pack_length= type_handler()->calc_pack_length(0); } length= 0; } @@ -4262,30 +4250,8 @@ bool Column_definition::prepare_blob_field(THD *thd) bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) { - if (real_field_type() == MYSQL_TYPE_SET || - real_field_type() == MYSQL_TYPE_ENUM) - { - /* - Pass "false" as the last argument to allocate TYPELIB values on mem_root, - even if no character set conversion is needed. - */ - if (prepare_interval_field(mem_root, false)) - return true; // E.g. wrong values with commas: SET('a,b') - } - - if (real_field_type() == MYSQL_TYPE_BIT) - pack_flag= FIELDFLAG_NUMBER | FIELDFLAG_TREAT_BIT_AS_CHAR; - create_length_to_internal_length(); - DBUG_ASSERT(default_value == 0); - /* - prepare_blob_field() can return an error on attempt to create a too long - VARCHAR/VARBINARY field when the current sql_mode does not allow automatic - conversion to TEXT/BLOB. - */ - if (prepare_blob_field(thd)) - return true; - uint unused1; - return prepare_create_field(&unused1, HA_CAN_GEOMETRY); + return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) || + prepare_stage2(NULL, HA_CAN_GEOMETRY); } |