diff options
author | Sergei Golubchik <serg@mariadb.org> | 2016-07-23 16:55:52 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2016-08-27 16:59:12 +0200 |
commit | 12d2c4fcd0bc3fbe74759e8285f2c93ad348e749 (patch) | |
tree | e8f0ec7fab2a8ec1365e10a5ddde62e7af2e885d /sql | |
parent | 4070d55735f1642e563b8d60fc2e9771f4963a3f (diff) | |
download | mariadb-git-12d2c4fcd0bc3fbe74759e8285f2c93ad348e749.tar.gz |
optimize constant default expressions
to be calculated at the CREATE TABLE time and stored in
the default row image.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.h | 6 | ||||
-rw-r--r-- | sql/item.cc | 6 | ||||
-rw-r--r-- | sql/sql_table.cc | 190 | ||||
-rw-r--r-- | sql/table.cc | 47 | ||||
-rw-r--r-- | sql/unireg.cc | 4 |
5 files changed, 139 insertions, 114 deletions
diff --git a/sql/field.h b/sql/field.h index f3328d560b8..45d2c3a7f00 100644 --- a/sql/field.h +++ b/sql/field.h @@ -574,7 +574,11 @@ inline bool is_temporal_type_with_time(enum_field_types type) } } -/* Bits for type of vcol expression */ +/* + Flags for Virtual_column_info. If none is set, the expression must be + a constant with no side-effects, so it's calculated at CREATE TABLE time, + stored in table->record[2], and not recalculated for every statement. +*/ #define VCOL_FIELD_REF 1 #define VCOL_NON_DETERMINISTIC 2 #define VCOL_SESSION_FUNC 4 /* uses session data, e.g. USER or DAYNAME */ diff --git a/sql/item.cc b/sql/item.cc index 2fb507cd720..420e0df71bd 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -956,8 +956,8 @@ bool Item_field::check_field_expression_processor(void *arg) { if (field->flags & NO_DEFAULT_VALUE_FLAG) return 0; - if ((field->default_value || field->has_insert_default_function() || - field->vcol_info)) + if ((field->default_value && field->default_value->flags) + || field->has_insert_default_function() || field->vcol_info) { Field *org_field= (Field*) arg; if (field == org_field || @@ -8232,7 +8232,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) set_field(def_field); if (field->default_value) { - if (field->default_value->expr_item) // it's NULL during CREATE TABLE + if (thd->mark_used_columns != MARK_COLUMNS_NONE) field->default_value->expr_item->walk(&Item::register_field_in_read_map, 1, 0); IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3ca2783f773..364d8eda773 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3255,35 +3255,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, !(sql_field->charset= find_bin_collation(sql_field->charset))) 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_item->basic_const_item() && - save_cs != sql_field->default_value->expr_item->collation.collation && - (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || - sql_field->sql_type == MYSQL_TYPE_STRING || - sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_TINY_BLOB || - sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB || - sql_field->sql_type == MYSQL_TYPE_LONG_BLOB || - sql_field->sql_type == MYSQL_TYPE_BLOB || - sql_field->sql_type == MYSQL_TYPE_ENUM)) - { - Item *item; - if (!(item= sql_field->default_value->expr_item-> - safe_charset_converter(thd, save_cs))) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - /* Fix for prepare statement */ - thd->change_item_tree(&sql_field->default_value->expr_item, item); - } - if (sql_field->sql_type == MYSQL_TYPE_SET || sql_field->sql_type == MYSQL_TYPE_ENUM) { @@ -3349,37 +3320,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->sql_type == MYSQL_TYPE_SET) { uint32 field_length; - if (sql_field->default_value && - sql_field->default_value->expr_item->basic_const_item()) - { - char *not_used; - uint not_used2; - bool not_found= 0; - String str, *def= sql_field->default_value->expr_item->val_str(&str); - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - if ((sql_field->flags & NOT_NULL_FLAG) != 0) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - - /* else, NULL is an allowed value */ - (void) find_set(interval, NULL, 0, - cs, ¬_used, ¬_used2, ¬_found); - } - else /* not NULL */ - { - (void) find_set(interval, def->ptr(), def->length(), - cs, ¬_used, ¬_used2, ¬_found); - } - - if (not_found) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } calculate_interval_lengths(cs, interval, &dummy, &field_length); sql_field->length= field_length + (interval->count - 1); } @@ -3387,30 +3327,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { uint32 field_length; DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM); - if (sql_field->default_value && - sql_field->default_value->expr_item->basic_const_item()) - { - String str, *def= sql_field->default_value->expr_item->val_str(&str); - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - if ((sql_field->flags & NOT_NULL_FLAG) != 0) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - - /* else, the defaults yield the correct length for NULLs. */ - } - else /* not NULL */ - { - def->length(cs->cset->lengthsp(cs, def->ptr(), def->length())); - if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */ - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } - } calculate_interval_lengths(cs, interval, &field_length, &dummy); sql_field->length= field_length; } @@ -3430,6 +3346,112 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (prepare_blob_field(thd, sql_field)) DBUG_RETURN(TRUE); + if (sql_field->default_value) + { + Virtual_column_info *def= sql_field->default_value; + + if (!sql_field->has_default_expression()) + def->expr_str= null_lex_str; + + if (!def->expr_item->basic_const_item() && !def->flags) + { + Item *expr= def->expr_item; + int err= !expr->fixed && // may be already fixed if ALTER TABLE + expr->fix_fields(thd, &expr); + if (!err) + { + if (expr->result_type() == REAL_RESULT) + { // don't convert floats to string and back, it can be lossy + double res= expr->val_real(); + if (expr->null_value) + expr= new (thd->mem_root) Item_null(thd); + else + expr= new (thd->mem_root) Item_float(thd, res, expr->decimals); + } + else + { + StringBuffer<MAX_FIELD_WIDTH> buf; + String *res= expr->val_str(&buf); + if (expr->null_value) + expr= new (thd->mem_root) Item_null(thd); + else + { + char *str= (char*) thd->strmake(res->ptr(), res->length()); + expr= new (thd->mem_root) Item_string(thd, str, res->length(), res->charset()); + } + } + thd->change_item_tree(&def->expr_item, expr); + } + } + } + + /* + 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_item->basic_const_item() && + save_cs != sql_field->default_value->expr_item->collation.collation && + (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || + sql_field->sql_type == MYSQL_TYPE_STRING || + sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_TINY_BLOB || + sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_field->sql_type == MYSQL_TYPE_LONG_BLOB || + sql_field->sql_type == MYSQL_TYPE_BLOB || + sql_field->sql_type == MYSQL_TYPE_ENUM)) + { + Item *item; + if (!(item= sql_field->default_value->expr_item-> + safe_charset_converter(thd, save_cs))) + { + /* Could not convert */ + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(TRUE); + } + /* Fix for prepare statement */ + thd->change_item_tree(&sql_field->default_value->expr_item, item); + } + + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item() && + (sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_ENUM)) + { + StringBuffer<MAX_FIELD_WIDTH> str; + String *def= sql_field->default_value->expr_item->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->sql_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 (not_found) + { + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(TRUE); + } + } + if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; diff --git a/sql/table.cc b/sql/table.cc index ca6a5a1b5a6..77736430fa3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2593,13 +2593,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, Field *field, DBUG_RETURN(1); } - /* Check that we are not refering to any not yet initialized fields */ - if (field) - { - if (func_expr->walk(&Item::check_field_expression_processor, 0, field)) - DBUG_RETURN(1); - } - /* Walk through the Item tree checking if all items are valid to be part of the virtual column @@ -2777,6 +2770,14 @@ end: DBUG_RETURN(vcol_info); } +static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) +{ + bool res= vcol && + vcol->expr_item->walk(&Item::check_field_expression_processor, 0, + field); + return res; +} + /* Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE */ @@ -3041,22 +3042,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, goto err; } field->default_value= vcol; - if (is_create_table && !vcol->flags) - { - enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; - thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values - my_ptrdiff_t off= share->default_values - outparam->record[0]; - field->move_field_offset(off); - int res= vcol->expr_item->save_in_field(field, 1); - field->move_field_offset(-off); - thd->count_cuted_fields= old_count_cuted_fields; - if (res != 0 && res != 3) - { - my_error(ER_INVALID_DEFAULT, MYF(0), field->field_name); - error= OPEN_FRM_CORRUPTED; - goto err; - } - } *(dfield_ptr++)= *field_ptr; } else @@ -3068,6 +3053,19 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, *vfield_ptr= 0; // End marker *dfield_ptr= 0; // End marker + /* Check that expressions aren't refering to not yet initialized fields */ + for (field_ptr= outparam->field; *field_ptr; field_ptr++) + { + Field *field= *field_ptr; + if (check_vcol_forward_refs(field, field->vcol_info) || + check_vcol_forward_refs(field, field->check_constraint) || + check_vcol_forward_refs(field, field->default_value)) + { + error= OPEN_FRM_CORRUPTED; + goto err; + } + } + /* Update to use trigger fields */ switch_defaults_to_nullable_trigger_fields(outparam); @@ -7316,7 +7314,8 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) { if (!update_command) { - if (field->default_value) + if (field->default_value && + (field->default_value->flags || field->flags & BLOB_FLAG)) res|= (field->default_value->expr_item->save_in_field(field, 0) < 0); else res|= field->evaluate_insert_default_function(); diff --git a/sql/unireg.cc b/sql/unireg.cc index ff46e03ab3e..add09411acb 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -634,7 +634,7 @@ static bool pack_header(THD *thd, uchar *forminfo, if (add_expr_length(thd, &field->vcol_info, &expression_length)) DBUG_RETURN(1); - if (field->has_default_expression()) + 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)) @@ -983,7 +983,7 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, if (field->vcol_info) pack_expression(&buff, field->vcol_info, field_nr, field->vcol_info->stored_in_db ? 1 : 0); - if (field->has_default_expression()) + 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); |