diff options
author | Igor Babaev <igor@askmonty.org> | 2009-10-16 15:57:48 -0700 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2009-10-16 15:57:48 -0700 |
commit | f7a75b999b4b0e51c647fa19df35db517e0b6721 (patch) | |
tree | 0852514919a2ab6af19814df4842b52a7ecd3b57 /sql | |
parent | 8ea19fa73e86a3c27917a92affd6a9e43763c7ce (diff) | |
download | mariadb-git-f7a75b999b4b0e51c647fa19df35db517e0b6721.tar.gz |
The main commit of Andrey Zhakov's patch introducing vurtual(computed) columns.
The original patch has been ameliorated by Sanja and Igor.
Diffstat (limited to 'sql')
40 files changed, 1660 insertions, 115 deletions
diff --git a/sql/field.cc b/sql/field.cc index 5bce6a412fc..7f01a53dd6f 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -57,7 +57,7 @@ const char field_separator=','; ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) #define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) -#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index))) +#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(&table->vcol_set, field_index))) /* Rules for merging different types of fields in UNION @@ -1312,7 +1312,8 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), null_bit(null_bit_arg), - is_created_from_null_item(FALSE) + is_created_from_null_item(FALSE), + vcol_info(0), stored_in_db(TRUE) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; @@ -1611,7 +1612,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, int Field_num::store_decimal(const my_decimal *val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; longlong i= convert_decimal2longlong(val, unsigned_flag, &err); return test(err | store(i, unsigned_flag)); @@ -1683,7 +1684,7 @@ void Field_num::make_field(Send_field *field) int Field_str::store_decimal(const my_decimal *d) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; double val; /* TODO: use decimal2string? */ int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR & @@ -1760,7 +1761,7 @@ bool Field::get_time(MYSQL_TIME *ltime) int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= (uint) my_TIME_to_str(ltime, buff); return store(buff, length, &my_charset_bin); @@ -1887,7 +1888,7 @@ void Field_decimal::overflow(bool negative) int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); const uchar *from= (uchar*) from_arg; @@ -2258,7 +2259,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) int Field_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; if (unsigned_flag && nr < 0) { overflow(1); @@ -2303,7 +2304,7 @@ int Field_decimal::store(double nr) int Field_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[22]; uint length, int_part; char fyllchar; @@ -2539,7 +2540,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, bool Field_new_decimal::store_value(const my_decimal *decimal_value) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; DBUG_ENTER("Field_new_decimal::store_value"); #ifndef DBUG_OFF @@ -2584,7 +2585,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) int Field_new_decimal::store(const char *from, uint length, CHARSET_INFO *charset_arg) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err; my_decimal decimal_value; DBUG_ENTER("Field_new_decimal::store(char*)"); @@ -2651,7 +2652,7 @@ int Field_new_decimal::store(const char *from, uint length, int Field_new_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; my_decimal decimal_value; int err; DBUG_ENTER("Field_new_decimal::store(double)"); @@ -2686,7 +2687,7 @@ int Field_new_decimal::store(double nr) int Field_new_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; my_decimal decimal_value; int err; @@ -2708,7 +2709,7 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val) int Field_new_decimal::store_decimal(const my_decimal *decimal_value) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; return store_value(decimal_value); } @@ -2929,7 +2930,7 @@ Field_new_decimal::unpack(uchar* to, int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error; longlong rnd; @@ -2941,7 +2942,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) int Field_tiny::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -2984,7 +2985,7 @@ int Field_tiny::store(double nr) int Field_tiny::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (unsigned_flag) @@ -3104,7 +3105,7 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; int error; longlong rnd; @@ -3125,7 +3126,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) int Field_short::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int16 res; nr=rint(nr); @@ -3177,7 +3178,7 @@ int Field_short::store(double nr) int Field_short::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int16 res; @@ -3351,7 +3352,7 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; int error; longlong rnd; @@ -3365,7 +3366,7 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) int Field_medium::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3411,7 +3412,7 @@ int Field_medium::store(double nr) int Field_medium::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (unsigned_flag) @@ -3541,7 +3542,7 @@ void Field_medium::sql_type(String &res) const int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long store_tmp; int error; longlong rnd; @@ -3562,7 +3563,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) int Field_long::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int32 res; nr=rint(nr); @@ -3614,7 +3615,7 @@ int Field_long::store(double nr) int Field_long::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int32 res; @@ -3788,7 +3789,7 @@ void Field_long::sql_type(String &res) const int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; char *end; ulonglong tmp; @@ -3818,7 +3819,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) int Field_longlong::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; longlong res; @@ -3870,7 +3871,7 @@ int Field_longlong::store(double nr) int Field_longlong::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (nr < 0) // Only possible error @@ -4096,7 +4097,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) int Field_float::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= truncate(&nr, FLT_MAX); float j= (float)nr; @@ -4358,7 +4359,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) int Field_double::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= truncate(&nr, DBL_MAX); #ifdef WORDS_BIGENDIAN @@ -4785,7 +4786,7 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; my_time_t tmp= 0; int error; @@ -4848,7 +4849,7 @@ int Field_timestamp::store(double nr) int Field_timestamp::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; my_time_t timestamp= 0; int error; @@ -5147,7 +5148,7 @@ int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type) int Field_time::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (nr > (double)TIME_MAX_VALUE) @@ -5185,7 +5186,7 @@ int Field_time::store(double nr) int Field_time::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val) @@ -5356,7 +5357,7 @@ void Field_time::sql_type(String &res) const int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char *end; int error; longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error); @@ -5404,7 +5405,7 @@ int Field_year::store(double nr) int Field_year::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) { *ptr= 0; @@ -5477,7 +5478,7 @@ void Field_year::sql_type(String &res) const int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; uint32 tmp; int error; @@ -5532,7 +5533,7 @@ int Field_date::store(double nr) int Field_date::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME not_used; int error; longlong initial_nr= nr; @@ -5711,7 +5712,7 @@ void Field_date::sql_type(String &res) const int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; MYSQL_TIME l_time; int error; @@ -5761,7 +5762,7 @@ int Field_newdate::store(double nr) int Field_newdate::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; longlong tmp; int error; @@ -5797,7 +5798,7 @@ int Field_newdate::store(longlong nr, bool unsigned_val) int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (time_type == MYSQL_TIMESTAMP_DATE || @@ -5944,7 +5945,7 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME time_tmp; int error; ulonglong tmp= 0; @@ -5997,7 +5998,7 @@ int Field_datetime::store(double nr) int Field_datetime::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME not_used; int error; longlong initial_nr= nr; @@ -6035,7 +6036,7 @@ int Field_datetime::store(longlong nr, bool unsigned_val) int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; longlong tmp; int error= 0; /* @@ -6338,7 +6339,7 @@ Field_longstr::report_if_important_data(const char *ptr, const char *end, int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -6379,7 +6380,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) int Field_str::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint length; uint local_char_length= field_length / charset()->mbmaxlen; @@ -6994,7 +6995,7 @@ int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -7658,7 +7659,7 @@ void Field_blob::put_length(uchar *pos, uint32 length) int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length, new_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -8420,7 +8421,7 @@ void Field_enum::store_type(ulonglong value) int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; uint32 not_used; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -8469,7 +8470,7 @@ int Field_enum::store(double nr) int Field_enum::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if ((ulonglong) nr > typelib->count || nr == 0) { @@ -8638,7 +8639,7 @@ Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table, int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; bool got_warning= 0; int err= 0; char *not_used; @@ -8678,7 +8679,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) int Field_set::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; ulonglong max_nr= set_bits(ulonglong, typelib->count); if ((ulonglong) nr > max_nr) @@ -8942,7 +8943,7 @@ uint Field_bit::is_equal(Create_field *new_field) int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; for (; length && !*from; from++, length--) // skip left 0's @@ -9353,7 +9354,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; uchar bits= (uchar) (field_length & 7); @@ -9461,6 +9462,8 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, ((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) | (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) | (is_unsigned ? 0 : FIELDFLAG_DECIMAL)); + vcol_info= 0; + stored_in_db= TRUE; } @@ -9480,6 +9483,7 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, @param fld_interval_list Interval list (if any) @param fld_charset Field charset @param fld_geom_type Field geometry type (if any) + @param fld_vcol_info Virtual column data @retval FALSE on success @@ -9492,13 +9496,14 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, uint fld_type_modifier, Item *fld_default_value, Item *fld_on_update_value, LEX_STRING *fld_comment, char *fld_change, List<String> *fld_interval_list, - CHARSET_INFO *fld_charset, uint fld_geom_type) + CHARSET_INFO *fld_charset, uint fld_geom_type, + Virtual_column_info *fld_vcol_info) { uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; DBUG_ENTER("Create_field::init()"); - + field= 0; field_name= fld_name; def= fld_default_value; @@ -9523,6 +9528,33 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, interval_list.empty(); comment= *fld_comment; + vcol_info= fld_vcol_info; + stored_in_db= TRUE; + + /* Initialize data for a computed field */ + if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL) + { + DBUG_ASSERT(vcol_info && vcol_info->expr_item); + stored_in_db= vcol_info->is_stored(); + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + if (vcol_info->expr_item->walk(&Item::check_vcol_func_processor, 0, NULL)) + { + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); + DBUG_RETURN(TRUE); + } + + /* + Make a field created for the real type. + Note that regular and computed fields differ from each other only by + Field::vcol_info. It is is always NULL for a column that is not + computed. + */ + sql_type= fld_type= vcol_info->get_real_type(); + } + /* Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP. @@ -9813,7 +9845,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } case MYSQL_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ - } + } /* Remember the value of length */ char_length= length; @@ -9912,7 +9944,6 @@ uint pack_length_to_packflag(uint type) return 0; // This shouldn't happen } - Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, @@ -10106,6 +10137,8 @@ Create_field::Create_field(Field *old_field,Field *orig_field) charset= old_field->charset(); // May be NULL ptr comment= old_field->comment; decimals= old_field->decimals(); + vcol_info= old_field->vcol_info; + stored_in_db= old_field->stored_in_db; /* Fix if the original table had 4 byte pointer blobs */ if (flags & BLOB_FLAG) diff --git a/sql/field.h b/sql/field.h index ac8e7dae3c5..4f1f62ba405 100644 --- a/sql/field.h +++ b/sql/field.h @@ -45,6 +45,80 @@ inline uint get_set_pack_length(int elements) return len > 4 ? 8 : len; } +/* + Virtual_column_info is the class to contain additional + characteristics that is specific for a virtual/computed + field such as: + - the defining expression that is evaluated to compute the value + of the field + - whether the field is to be stored in the database + - whether the field is used in a partitioning expression +*/ + +class Virtual_column_info: public Sql_alloc +{ +private: + /* + The following data is only updated by the parser and read + when a Create_field object is created/initialized. + */ + enum_field_types field_type; /* Real field type*/ + /* Flag indicating that the field is physically stored in the database */ + my_bool stored_in_db; + /* Flag indicating that the field used in a partitioning expression */ + my_bool in_partitioning_expr; + +public: + /* The expression to compute the value of the virtual column */ + Item *expr_item; + /* Text representation of the defining expression */ + LEX_STRING expr_str; + /* + The list of items created when the defining expression for the virtual + column is being parsed and validated. These items are freed in the closefrm + function when the table containing this virtual column is removed from + the TABLE cache. + TODO. Items for all different virtual columns of a table should be put into + one list attached to the TABLE structure. + */ + Item *item_free_list; + + Virtual_column_info() + : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), + stored_in_db(FALSE), in_partitioning_expr(FALSE), + expr_item(NULL), item_free_list(NULL) + { + expr_str.str= NULL; + expr_str.length= 0; + }; + ~Virtual_column_info() {} + enum_field_types get_real_type() + { + return field_type; + } + void set_field_type(enum_field_types fld_type) + { + /* Calling this function can only be done once. */ + field_type= fld_type; + } + bool is_stored() + { + return stored_in_db; + } + void set_stored_in_db_flag(bool stored) + { + stored_in_db= stored; + } + bool is_in_partitioning_expr() + { + return in_partitioning_expr; + } + void mark_as_in_partitioning_expr() + { + in_partitioning_expr= TRUE; + } +}; + class Field { Field(const Item &); /* Prevent use of these */ @@ -57,7 +131,7 @@ public: uchar *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is /* - Note that you can use table->in_use as replacement for current_thd member + Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ struct st_table *table; // Pointer for table @@ -67,10 +141,10 @@ public: /* Field is part of the following keys */ key_map key_start, part_of_key, part_of_key_not_clustered; key_map part_of_sortkey; - /* - We use three additional unireg types for TIMESTAMP to overcome limitation - of current binary format of .frm file. We'd like to be able to support - NOW() as default and on update value for such fields but unable to hold + /* + We use three additional unireg types for TIMESTAMP to overcome limitation + of current binary format of .frm file. We'd like to be able to support + NOW() as default and on update value for such fields but unable to hold this info anywhere except unireg_check field. This issue will be resolved in more clean way with transition to new text based .frm format. See also comment for Field_timestamp::Field_timestamp(). @@ -103,6 +177,19 @@ public: */ bool is_created_from_null_item; + /* + This is additional data provided for any computed(virtual) field. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info *vcol_info; + /* + Flag indicating that the field is physically stored in tables + rather than just computed from other fields. + As of now, FALSE can be set only for computed virtual columns. + */ + bool stored_in_db; + Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg); @@ -2044,6 +2131,20 @@ public: uint8 row,col,sc_length,interval_id; // For rea_create_table uint offset,pack_flag; + + /* + This is additinal data provided for any computed(virtual) field. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info *vcol_info; + /* + Flag indicating that the field is physically stored in tables + rather than just computed from other fields. + As of now, FALSE can be set only for computed virtual columns. + */ + bool stored_in_db; + Create_field() :after(0) {} Create_field(Field *field, Field *orig_field); /* Used to make a clone of this object for ALTER/CREATE TABLE */ @@ -2060,7 +2161,8 @@ public: char *decimals, uint type_modifier, Item *default_value, Item *on_update_value, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, + Virtual_column_info *vcol_info); }; diff --git a/sql/filesort.cc b/sql/filesort.cc index 552ea27970f..26f39be7f9e 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -565,6 +565,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, { if ((error= select->quick->get_next())) break; + if (!error) + update_virtual_fields(sort_form); file->position(sort_form->record[0]); DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE);); } @@ -582,6 +584,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, else { error=file->rnd_next(sort_form->record[0]); + if (!error) + update_virtual_fields(sort_form); if (!flag) { my_store_ptr(ref_pos,ref_length,record); // Position to row diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 10e011ec06f..9b9fe645f15 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2436,7 +2436,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(1); m_start_key.length= 0; m_rec0= table->record[0]; - m_rec_length= table_share->reclength; + m_rec_length= table_share->stored_rec_length; alloc_len= m_tot_parts * (m_rec_length + PARTITION_BYTES_IN_POS); alloc_len+= table_share->max_key_length; if (!m_ordered_rec_buffer) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 3a7084c87ed..d52cb4ba5b0 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -245,6 +245,7 @@ public: DBUG_RETURN(0); } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share); + bool check_if_supported_virtual_columns(void) { return TRUE;} virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes); private: diff --git a/sql/handler.h b/sql/handler.h index fb1904f23dc..ed26f66837a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1760,6 +1760,17 @@ public: LEX_STRING *engine_name() { return hton_name(ht); } + /* + @brief + Check whether the engine supports virtual columns + + @retval + FALSE if the engine does not support virtual columns + @retval + TRUE if the engine supports virtual columns + */ + virtual bool check_if_supported_virtual_columns(void) { return FALSE;} + protected: /* Service methods for use by storage engines. */ void ha_statistic_increment(ulong SSV::*offset) const; diff --git a/sql/item.cc b/sql/item.cc index b2ab28a77fd..4e6b45ceeb3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -681,9 +681,24 @@ bool Item_field::register_field_in_read_map(uchar *arg) TABLE *table= (TABLE *) arg; if (field->table == table || !table) bitmap_set_bit(field->table->read_set, field->field_index); + if (field->vcol_info && field->vcol_info->expr_item) + return field->vcol_info->expr_item->walk(&Item::register_field_in_read_map, + 1, arg); return 0; } +/* + @brief + Mark field in bitmap supplied as *arg +*/ + +bool Item_field::register_field_in_bitmap(uchar *arg) +{ + MY_BITMAP *bitmap= (MY_BITMAP *) arg; + DBUG_ASSERT(bitmap); + bitmap_set_bit(bitmap, field->field_index); + return 0; +} bool Item::check_cols(uint c) { @@ -4453,6 +4468,21 @@ error: return TRUE; } +/* + @brief + Mark virtual columns as used in a partitioning expression +*/ + +bool Item_field::vcol_in_partition_func_processor(uchar *int_arg) +{ + DBUG_ASSERT(fixed); + if (field->vcol_info) + { + field->vcol_info->mark_as_in_partitioning_expr(); + } + return FALSE; +} + Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) { diff --git a/sql/item.h b/sql/item.h index 74c4ca701f7..2d3e2609a76 100644 --- a/sql/item.h +++ b/sql/item.h @@ -18,6 +18,24 @@ #pragma interface /* gcc class implementation */ #endif +inline +bool trace_unsupported_func(const char *where, const char *processor_name) +{ + char buff[64]; + sprintf(buff, "%s::%s", where, processor_name); + DBUG_ENTER(buff); + sprintf(buff, "%s returns TRUE: unsupported function", processor_name); + DBUG_PRINT("info", (buff)); + DBUG_RETURN(TRUE); +} + +inline +bool trace_unsupported_by_check_vcol_func_processor(const char *where) +{ + return trace_unsupported_func(where, "check_vcol_func_processor"); +} + + class Protocol; struct TABLE_LIST; void item_init(void); /* Init item functions */ @@ -895,6 +913,11 @@ public: virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; } virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } /* + The next function differs from the previous one that a bitmap to be updated + is passed as uchar *arg. + */ + virtual bool register_field_in_bitmap(uchar *arg) { return 0; } + /* Check if a partition function is allowed SYNOPSIS check_partition_func_processor() @@ -946,11 +969,43 @@ public: fields. */ virtual bool check_partition_func_processor(uchar *bool_arg) { return TRUE;} + /* + @brief + Processor used to mark virtual columns used in partitioning expression + + @param + arg always ignored + + @retval + FALSE always + */ + virtual bool vcol_in_partition_func_processor(uchar *arg) + { + return FALSE; + } + virtual bool subst_argument_checker(uchar **arg) - { + { if (*arg) - *arg= NULL; - return TRUE; + *arg= NULL; + return TRUE; + } + /* + @brief + Processor used to check acceptability of an item in the defining + expression for a virtual column + + @param + arg always ignored + + @retval + FALSE the item is accepted in the definition of a virtual column + @retval + TRUE otherwise + */ + virtual bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(full_name()); } virtual Item *equal_fields_propagator(uchar * arg) { return this; } @@ -1327,6 +1382,10 @@ public: { return value_item->send(protocol, str); } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("name_const"); + } }; bool agg_item_collations(DTCollation &c, const char *name, @@ -1346,6 +1405,7 @@ public: virtual Item_num *neg()= 0; Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; #define NO_CACHED_FIELD_INDEX ((uint)(-1)) @@ -1505,7 +1565,10 @@ public: bool collect_item_field_processor(uchar * arg); bool find_item_in_field_list_processor(uchar *arg); bool register_field_in_read_map(uchar *arg); + bool register_field_in_bitmap(uchar *arg); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool vcol_in_partition_func_processor(uchar *bool_arg); + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); void cleanup(); bool result_as_longlong() @@ -1566,6 +1629,7 @@ public: Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; class Item_null_result :public Item_null @@ -1579,7 +1643,11 @@ public: save_in_field(result_field, no_conversions); } bool check_partition_func_processor(uchar *int_arg) {return TRUE;} -}; + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(full_name()); + } +}; /* Item represents one placeholder ('?') of prepared statement */ @@ -1720,6 +1788,7 @@ public: /** Item is a argument to a limit clause. */ bool limit_clause_param; void set_param_type_and_swap_value(Item_param *from); + }; @@ -1755,6 +1824,7 @@ public: { return (uint)(max_length - test(value < 0)); } bool eq(const Item *, bool binary_cmp) const; bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar arg) { return FALSE;} }; @@ -1773,6 +1843,7 @@ public: Item_num *neg (); uint decimal_precision() const { return max_length; } bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -1814,6 +1885,7 @@ public: bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -1971,6 +2043,7 @@ public: } virtual void print(String *str, enum_query_type query_type); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} /** Return TRUE if character-set-introducer was explicitly specified in the @@ -2024,7 +2097,7 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end); class Item_static_string_func :public Item_string { const char *func_name; -public: + public: Item_static_string_func(const char *name_par, const char *str, uint length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) @@ -2038,6 +2111,10 @@ public: } bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name); + } }; @@ -2049,6 +2126,10 @@ public: CHARSET_INFO *cs= NULL): Item_string(name_arg, length, cs) {} + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("safe_string"); + } }; @@ -2128,6 +2209,7 @@ public: bool eq(const Item *item, bool binary_cmp) const; virtual Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -2158,6 +2240,7 @@ public: save_in_field(result_field, no_conversions); } void cleanup(); + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -2287,7 +2370,10 @@ public: if (ref && result_type() == ROW_RESULT) (*ref)->bring_value(); } - + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("ref"); + } }; @@ -2563,6 +2649,10 @@ public: table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } bool is_null() { return null_value; } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("copy"); + } /* Override the methods below as pure virtual to make sure all the @@ -2805,6 +2895,10 @@ public: return arg->walk(processor, walk_subquery, args) || (this->*processor)(args); } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("values"); + } }; @@ -2901,6 +2995,10 @@ private: BEFORE INSERT of BEFORE UPDATE trigger. */ bool read_only; + virtual bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("trigger"); + } }; @@ -2960,6 +3058,11 @@ public: { return this == item; } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("cache"); + } + }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 7aa455b1b4a..a1d2bdc9c06 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3940,10 +3940,30 @@ bool Item_func_set_user_var::register_field_in_read_map(uchar *arg) TABLE *table= (TABLE *) arg; if (result_field->table == table || !table) bitmap_set_bit(result_field->table->read_set, result_field->field_index); + if (result_field->vcol_info) + return result_field->vcol_info-> + expr_item->walk(&Item::register_field_in_read_map, 1, arg); } return 0; } +/* + Mark field in bitmap supplied as *arg + +*/ + +bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg) +{ + MY_BITMAP *bitmap = (MY_BITMAP *) arg; + DBUG_ASSERT(bitmap); + if (result_field) + { + if (!bitmap) + return 1; + bitmap_set_bit(bitmap, result_field->field_index); + } + return 0; +} /** Set value to user variable. diff --git a/sql/item_func.h b/sql/item_func.h index eee3a7ef0f6..8231963dfd8 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -339,6 +339,7 @@ public: void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + bool check_vcol_func_processor(uchar *int_arg) { return TRUE;} }; @@ -399,6 +400,7 @@ public: Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {} void result_precision(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -434,6 +436,7 @@ public: my_decimal *decimal_op(my_decimal *); void result_precision(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -466,6 +469,7 @@ public: } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -480,6 +484,7 @@ public: void result_precision(); void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -496,6 +501,7 @@ public: void fix_num_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -509,6 +515,7 @@ public: const char *func_name() const { return "abs"; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; // A class to handle logarithmic and trigonometric functions @@ -664,6 +671,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -676,6 +684,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; /* This handles round and truncate */ @@ -707,6 +716,10 @@ public: void update_used_tables(); bool fix_fields(THD *thd, Item **ref); void cleanup() { first_eval= TRUE; Item_real_func::cleanup(); } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } private: void seed_random (Item * val); }; @@ -989,6 +1002,10 @@ public: max_length= args[0]->max_length; } bool fix_fields(THD *thd, Item **ref); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1002,6 +1019,10 @@ public: const char *func_name() const { return "benchmark"; } void fix_length_and_dec() { max_length=1; maybe_null=0; } virtual void print(String *str, enum_query_type query_type); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1017,6 +1038,10 @@ public: used_tables_cache|= RAND_TABLE_BIT; } longlong val_int(); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1266,6 +1291,10 @@ class Item_func_get_lock :public Item_int_func longlong val_int(); const char *func_name() const { return "get_lock"; } void fix_length_and_dec() { max_length=1; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; class Item_func_release_lock :public Item_int_func @@ -1276,6 +1305,10 @@ public: longlong val_int(); const char *func_name() const { return "release_lock"; } void fix_length_and_dec() { max_length=1; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; /* replication functions */ @@ -1289,6 +1322,10 @@ public: longlong val_int(); const char *func_name() const { return "master_pos_wait"; } void fix_length_and_dec() { max_length=21; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1360,6 +1397,7 @@ public: } void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); } bool register_field_in_read_map(uchar *arg); + bool register_field_in_bitmap(uchar *arg); bool set_entry(THD *thd, bool create_if_not_exists); void cleanup(); }; @@ -1534,6 +1572,11 @@ public: bool fix_index(); void init_search(bool no_order); + bool check_vcol_func_processor(uchar *int_arg) + { + /* TODO: consider adding in support for the MATCH-based virtual columns */ + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1553,6 +1596,17 @@ public: longlong val_int(); const char *func_name() const { return "is_free_lock"; } void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { +#if 0 + DBUG_ENTER("Item_func_is_free_lock::check_vcol_func_processor"); + DBUG_PRINT("info", + ("check_vcol_func_processor returns TRUE: unsupported function")); + DBUG_RETURN(TRUE); +#else + return trace_unsupported_by_check_vcol_func_processor(func_name()); +#endif + } }; class Item_func_is_used_lock :public Item_int_func @@ -1563,6 +1617,10 @@ public: longlong val_int(); const char *func_name() const { return "is_used_lock"; } void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; /* For type casts */ @@ -1582,6 +1640,11 @@ public: longlong val_int(); const char *func_name() const { return "row_count"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } + bool check_vcol_func_processor(uchar *int_arg) + { + + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1690,6 +1753,10 @@ public: { return sp_result_field; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1700,6 +1767,10 @@ public: longlong val_int(); const char *func_name() const { return "found_rows"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1714,5 +1785,9 @@ public: void fix_length_and_dec() { max_length= 21; unsigned_flag=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/item_row.h b/sql/item_row.h index 67441f49603..71679100448 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -76,4 +76,5 @@ public: bool check_cols(uint c); bool null_inside() { return with_null; }; void bring_value(); -}; + bool check_vcol_func_processor(uchar *int_arg) {return FALSE; } + }; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 8625add8d2d..6b424989c58 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -344,6 +344,10 @@ public: String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length = 13; } const char *func_name() const { return "encrypt"; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; #include "sql_crypt.h" @@ -381,6 +385,11 @@ public: call */ virtual const char *fully_qualified_func_name() const = 0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor( + fully_qualified_func_name()); + } }; @@ -644,6 +653,10 @@ public: maybe_null=1; max_length=MAX_BLOB_WIDTH; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -840,5 +853,9 @@ public: } const char *func_name() const{ return "uuid"; } String *val_str(String *); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 19d58c65259..63a9acb95f0 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -136,6 +136,10 @@ public: bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool mark_as_eliminated_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("subselect"); + } /** Get the SELECT_LEX structure associated with this Item. diff --git a/sql/item_sum.h b/sql/item_sum.h index e884452d6e6..d0723fa0ab0 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -399,6 +399,10 @@ public: Item *get_arg(int i) { return args[i]; } Item *set_arg(int i, THD *thd, Item *new_val); uint get_arg_count() { return arg_count; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -679,6 +683,10 @@ public: } void fix_length_and_dec() {} enum Item_result result_type () const { return hybrid_type; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("avg_field"); + } }; @@ -747,6 +755,10 @@ public: } void fix_length_and_dec() {} enum Item_result result_type () const { return hybrid_type; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("var_field"); + } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 9e3c2e8c89f..26e47e43d05 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -70,6 +70,7 @@ public: enum_monotonicity_info get_monotonicity_info() const; longlong val_int_endpoint(bool left_endp, bool *incl_endp); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -86,6 +87,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -111,6 +113,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -124,6 +127,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *int_arg) {return FALSE;} }; @@ -140,6 +144,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -156,6 +161,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -172,6 +178,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -188,6 +195,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -204,6 +212,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -234,6 +243,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -252,6 +262,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -282,6 +293,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; class Item_func_dayname :public Item_func_weekday @@ -294,6 +306,7 @@ class Item_func_dayname :public Item_func_weekday enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -310,6 +323,14 @@ public: decimals=0; max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_vcol_func_processor(uchar *int_arg) + { + /* + TODO: Allow UNIX_TIMESTAMP called with an argument to be a part + of the expression for a virtual column + */ + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -325,6 +346,7 @@ public: max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -445,6 +467,10 @@ public: */ virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; bool result_as_longlong() { return TRUE; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -481,6 +507,10 @@ public: void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -521,6 +551,10 @@ public: void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -578,6 +612,7 @@ public: const char *func_name() const { return "from_days"; } bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -705,6 +740,7 @@ class Item_extract :public Item_int_func bool eq(const Item *item, bool binary_cmp) const; virtual void print(String *str, enum_query_type query_type); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -953,6 +989,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 8a5804ffd63..f2e33dc2d44 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -220,6 +220,11 @@ public: collation.collation= pxml->charset(); } const char *func_name() const { return "nodeset"; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } + }; @@ -526,6 +531,10 @@ public: enum Type type() const { return XPATH_NODESET_CMP; }; const char *func_name() const { return "xpath_nodeset_to_const_comparator"; } bool is_bool_func() { return 1; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } longlong val_int() { diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index dadbb5ccf42..ce33d161c79 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -40,6 +40,10 @@ public: } void fix_length_and_dec(); String *parse_xml(String *raw_xml, String *parsed_xml_buf); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/lex.h b/sql/lex.h index d9c382347c9..0fb6dcf8368 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -62,6 +62,7 @@ static SYMBOL symbols[] = { { "ALL", SYM(ALL)}, { "ALGORITHM", SYM(ALGORITHM_SYM)}, { "ALTER", SYM(ALTER)}, + { "ALWAYS", SYM(ALWAYS_SYM)}, { "ANALYZE", SYM(ANALYZE_SYM)}, { "AND", SYM(AND_SYM)}, { "ANY", SYM(ANY_SYM)}, @@ -220,6 +221,7 @@ static SYMBOL symbols[] = { { "FULL", SYM(FULL)}, { "FULLTEXT", SYM(FULLTEXT_SYM)}, { "FUNCTION", SYM(FUNCTION_SYM)}, + { "GENERATED", SYM(GENERATED_SYM)}, { "GEOMETRY", SYM(GEOMETRY_SYM)}, { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)}, { "GET_FORMAT", SYM(GET_FORMAT)}, @@ -385,14 +387,16 @@ static SYMBOL symbols[] = { { "OUTFILE", SYM(OUTFILE)}, { "OWNER", SYM(OWNER_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, - { "PARSER", SYM(PARSER_SYM)}, { "PAGE", SYM(PAGE_SYM)}, { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, + { "PARSER", SYM(PARSER_SYM)}, + { "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)}, { "PARTIAL", SYM(PARTIAL)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, { "PARTITIONS", SYM(PARTITIONS_SYM)}, { "PASSWORD", SYM(PASSWORD)}, + { "PERSISTENT", SYM(PERSISTENT_SYM)}, { "PHASE", SYM(PHASE_SYM)}, { "PLUGIN", SYM(PLUGIN_SYM)}, { "PLUGINS", SYM(PLUGINS_SYM)}, @@ -581,13 +585,14 @@ static SYMBOL symbols[] = { { "VARCHARACTER", SYM(VARCHAR)}, { "VARIABLES", SYM(VARIABLES)}, { "VARYING", SYM(VARYING)}, + { "VIEW", SYM(VIEW_SYM)}, + { "VIRTUAL", SYM(VIRTUAL_SYM)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, { "WHEN", SYM(WHEN_SYM)}, { "WHERE", SYM(WHERE)}, { "WHILE", SYM(WHILE_SYM)}, - { "VIEW", SYM(VIEW_SYM)}, { "WITH", SYM(WITH)}, { "WORK", SYM(WORK_SYM)}, { "WRAPPER", SYM(WRAPPER_SYM)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 63d5621742e..2d2c4d0ca31 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1350,6 +1350,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, bool allow_rowid, uint *cached_field_index_ptr); Field * find_field_in_table_sef(TABLE *table, const char *name); +int update_virtual_fields(TABLE *table, bool ignore_stored= FALSE); #endif /* MYSQL_SERVER */ @@ -1470,14 +1471,16 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, + Virtual_column_info *vcol_info); Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, Item *default_value, Item *on_update_value, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, + Virtual_column_info *vcol_info); void store_position_for_column(const char *name); bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc); bool push_new_name_resolution_context(THD *thd, diff --git a/sql/procedure.h b/sql/procedure.h index ceb586766b1..30a8a0efccb 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -42,7 +42,11 @@ public: { init_make_field(tmp_field,field_type()); } - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("proc"); + } }; class Item_proc_real :public Item_proc diff --git a/sql/records.cc b/sql/records.cc index 8bab6fd46a4..774377dfa50 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -323,6 +323,7 @@ static int rr_quick(READ_RECORD *info) break; } } + update_virtual_fields(info->table); return tmp; } @@ -395,6 +396,8 @@ int rr_sequential(READ_RECORD *info) break; } } + if (!tmp) + update_virtual_fields(info->table); return tmp; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index df0d7d97d2d..e3fc6a3e8a9 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6206,3 +6206,30 @@ ER_TOO_MANY_CONCURRENT_TRXS WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED eng "Non-ASCII separator arguments are not fully supported" + +ER_VCOL_BASED_ON_VCOL + eng "A computed column cannot be based on a computed column" + +ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED + eng "Function or expression is not allowed for column '%s'." + +ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN + eng "Generated value for computed column '%s' cannot be converted to type '%s'." + +ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN + eng "Primary key cannot be defined upon a computed column." + +ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN + eng "Key/Index cannot be defined on a non-stored computed column." + +ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN + eng "Cannot define foreign key with %s clause on a computed column." + +ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN + eng "The value specified for computed column '%s' in table '%s' ignored." + +ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN + eng "'%s' is not yet supported for computed columns." + +ER_CONST_EXPR_IN_VCOL + eng "Constant expression in computed column function is not allowed." diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e7310787a35..6656520b6ef 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -816,6 +816,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name, m_return_field_def.interval, field_name ? field_name : (const char *) m_name.str); + field->vcol_info= m_return_field_def.vcol_info; + field->stored_in_db= m_return_field_def.stored_in_db; if (field) field->init(table); @@ -2197,7 +2199,8 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, &lex->interval_list, lex->charset ? lex->charset : thd->variables.collation_database, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info)) return TRUE; if (field_def->interval_list.elements) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d94a35259f4..c14d2000229 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5594,6 +5594,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) table->covering_keys.intersect(field->part_of_key); table->merge_keys.merge(field->part_of_key); + if (field->vcol_info) + table->mark_virtual_col(field); + if (thd->mark_used_columns == MARK_COLUMNS_READ) current_bitmap= table->read_set; else @@ -7887,6 +7890,12 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, { /* Mark fields as used to allow storage engine to optimze access */ bitmap_set_bit(field->table->read_set, field->field_index); + /* + Mark virtual fields for write and others that the virtual fields + depend on for read. + */ + if (field->vcol_info) + field->table->mark_virtual_col(field); if (table) { table->covering_keys.intersect(field->part_of_key); @@ -8098,7 +8107,10 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, Item *value, *fld; Item_field *field; TABLE *table= 0; + List<TABLE> tbl_list; + bool abort_on_warning_saved= thd->abort_on_warning; DBUG_ENTER("fill_record"); + tbl_list.empty(); /* Reset the table->auto_increment_field_not_null as it is valid for @@ -8132,14 +8144,54 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, table= rfield->table; if (rfield == table->next_number_field) table->auto_increment_field_not_null= TRUE; + if (rfield->vcol_info && + value->type() != Item::DEFAULT_VALUE_ITEM && + value->type() != Item::NULL_ITEM && + table->s->table_category != TABLE_CATEGORY_TEMPORARY) + { + thd->abort_on_warning= FALSE; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, + ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + rfield->field_name, table->s->table_name.str); + thd->abort_on_warning= abort_on_warning_saved; + } if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) { my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); goto err; } + tbl_list.push_back(table); } + /* Update virtual fields*/ + thd->abort_on_warning= FALSE; + if (tbl_list.head()) + { + List_iterator_fast<TABLE> it(tbl_list); + TABLE *prev_table= 0; + while ((table= it++)) + { + /* + Do simple optimization to prevent unnecessary re-generating + values for virtual fields + */ + if (table != prev_table) + { + prev_table= table; + if (table->vfield) + { + if (update_virtual_fields(table, TRUE)) + { + goto err; + } + } + } + } + } + thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); err: + thd->abort_on_warning= abort_on_warning_saved; if (table) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(TRUE); @@ -8175,9 +8227,31 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, Table_triggers_list *triggers, enum trg_event_type event) { - return (fill_record(thd, fields, values, ignore_errors) || - (triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE))); + bool result; + result= (fill_record(thd, fields, values, ignore_errors) || + (triggers && triggers->process_triggers(thd, event, + TRG_ACTION_BEFORE, TRUE))); + /* + Re-calculate virtual fields to cater for cases when base columns are + updated by the triggers. + */ + if (!result && triggers) + { + TABLE *table= 0; + List_iterator_fast<Item> f(fields); + Item *fld; + Item_field *item_field; + if (fields.elements) + { + fld= (Item_field*)f++; + item_field= fld->filed_for_view_update(); + if (item_field && item_field->field && + (table= item_field->field->table) && + table->vfield) + result= update_virtual_fields(table, TRUE); + } + } + return result; } @@ -8205,11 +8279,14 @@ bool fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> v(values); + List<TABLE> tbl_list; Item *value; TABLE *table= 0; + bool abort_on_warning_saved= thd->abort_on_warning; DBUG_ENTER("fill_record"); Field *field; + tbl_list.empty(); /* Reset the table->auto_increment_field_not_null as it is valid for only one row. @@ -8229,12 +8306,52 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) table= field->table; if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; + if (field->vcol_info && + value->type() != Item::DEFAULT_VALUE_ITEM && + value->type() != Item::NULL_ITEM && + table->s->table_category != TABLE_CATEGORY_TEMPORARY) + { + thd->abort_on_warning= FALSE; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, + ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + field->field_name, table->s->table_name.str); + thd->abort_on_warning= abort_on_warning_saved; + } if (value->save_in_field(field, 0) < 0) goto err; + tbl_list.push_back(table); } + /* Update virtual fields*/ + thd->abort_on_warning= FALSE; + if (tbl_list.head()) + { + List_iterator_fast<TABLE> t(tbl_list); + TABLE *prev_table= 0; + while ((table= t++)) + { + /* + Do simple optimization to prevent unnecessary re-generating + values for virtual fields + */ + if (table != prev_table) + { + prev_table= table; + if (table->vfield) + { + if (update_virtual_fields(table, TRUE)) + { + goto err; + } + } + } + } + } + thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); err: + thd->abort_on_warning= abort_on_warning_saved; if (table) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(TRUE); @@ -8270,9 +8387,22 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, Table_triggers_list *triggers, enum trg_event_type event) { - return (fill_record(thd, ptr, values, ignore_errors) || - (triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE))); + bool result; + result= (fill_record(thd, ptr, values, ignore_errors) || + (triggers && triggers->process_triggers(thd, event, + TRG_ACTION_BEFORE, TRUE))); + /* + Re-calculate virtual fields to cater for cases when base columns are + updated by the triggers. + */ + if (!result && triggers && *ptr) + { + TABLE *table= (*ptr)->table; + if (table->vfield) + result= update_virtual_fields(table, TRUE); + } + return result; + } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0805eda59e4..4fb737e0f00 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -194,6 +194,59 @@ bool foreign_key_prefix(Key *a, Key *b) #endif } +/* + @brief + Check if the foreign key options are compatible with the specification + of the columns on which the key is created + + @retval + FALSE The foreign key options are compatible with key columns + @retval + TRUE Otherwise +*/ +bool Foreign_key::validate(List<Create_field> &table_fields) +{ + Create_field *sql_field; + Key_part_spec *column; + List_iterator<Key_part_spec> cols(columns); + List_iterator<Create_field> it(table_fields); + DBUG_ENTER("Foreign_key::validate"); + while ((column= cols++)) + { + it.rewind(); + while ((sql_field= it++) && + my_strcasecmp(system_charset_info, + column->field_name, + sql_field->field_name)) {} + if (!sql_field) + { + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name); + DBUG_RETURN(TRUE); + } + if (type == Key::FOREIGN_KEY && sql_field->vcol_info) + { + if (delete_opt == FK_OPTION_SET_NULL) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON DELETE SET NULL"); + DBUG_RETURN(TRUE); + } + if (update_opt == FK_OPTION_SET_NULL) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON UPDATE SET NULL"); + DBUG_RETURN(TRUE); + } + if (update_opt == FK_OPTION_CASCADE) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON UPDATE CASCADE"); + DBUG_RETURN(TRUE); + } + } + } + DBUG_RETURN(FALSE); +} /**************************************************************************** ** Thread specific functions diff --git a/sql/sql_class.h b/sql/sql_class.h index 60ab952a3ab..0736db63b4b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -249,6 +249,8 @@ public: */ virtual Key *clone(MEM_ROOT *mem_root) const { return new (mem_root) Foreign_key(*this, mem_root); } + /* Used to validate foreign key options */ + bool validate(List<Create_field> &table_fields); }; typedef struct st_mysql_lock diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 1e92d95573a..d5d3be1bc28 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -666,6 +666,8 @@ retry: } goto ok; } + /* Generate values for virtual fields */ + update_virtual_fields(table); if (cond && !cond->val_int()) continue; if (num_rows >= offset_limit_cnt) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 710da7067e4..31bf2ddd4d8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -280,6 +280,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, } } } + /* Mark virtual columns used in the insert statement */ + if (table->vfield) + table->mark_virtual_columns_for_write(); // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 6d047197992..7b628c23156 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -340,6 +340,7 @@ void lex_start(THD *thd) lex->reset_query_tables_list(FALSE); lex->expr_allows_subselect= TRUE; lex->use_only_table_context= FALSE; + lex->parse_vcol_expr= FALSE; lex->name.str= 0; lex->name.length= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4b9b35819fe..d0ded22897c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1544,6 +1544,7 @@ typedef struct st_lex : public Query_tables_list LEX_USER *grant_user; XID *xid; THD *thd; + Virtual_column_info *vcol_info; /* maintain a list of used plugins for this LEX */ DYNAMIC_ARRAY plugins; @@ -1626,6 +1627,14 @@ typedef struct st_lex : public Query_tables_list syntax error back. */ bool expr_allows_subselect; + /* + A special command "PARSE_VCOL_EXPR" is defined for the parser + to translate a defining expression of a virtual column into an + Item object. + The following flag is used to prevent other applications to use + this command. + */ + bool parse_vcol_expr; thr_lock_type lock_option; enum SSL_type ssl_type; /* defined in violite.h */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 20033e23b93..48aea1fdd57 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6055,7 +6055,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type) + uint uint_geom_type, + Virtual_column_info *vcol_info) { register Create_field *new_field; LEX *lex= thd->lex; @@ -6141,7 +6142,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (!(new_field= new Create_field()) || new_field->init(thd, field_name->str, type, length, decimals, type_modifier, default_value, on_update_value, comment, change, - interval_list, cs, uint_geom_type)) + interval_list, cs, uint_geom_type, vcol_info)) DBUG_RETURN(1); lex->alter_info.create_list.push_back(new_field); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 236df78d455..3ed535e9be5 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -975,7 +975,8 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, thd->lex->use_only_table_context= TRUE; thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; - error= func_expr->fix_fields(thd, (Item**)&func_expr); + if (!(error= func_expr->fix_fields(thd, (Item**)&func_expr))) + func_expr->walk(&Item::vcol_in_partition_func_processor, 0, NULL); thd->lex->use_only_table_context= save_use_only_table_context; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 014b63c057c..ffc4845520b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9761,6 +9761,10 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps) bitmap_init(&table->tmp_set, (my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)), field_count, FALSE); + bitmap_init(&table->vcol_set, + (my_bitmap_map*) (bitmaps+ 2+bitmap_buffer_size(field_count)), + field_count, FALSE); + /* write_set and all_set are copies of read_set */ table->def_write_set= table->def_read_set; table->s->all_set= table->def_read_set; @@ -9908,7 +9912,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, &tmpname, (uint) strlen(path)+1, &group_buff, (group && ! using_unique_constraint ? param->group_length : 0), - &bitmaps, bitmap_buffer_size(field_count)*2, + &bitmaps, bitmap_buffer_size(field_count)*3, NullS)) { if (temp_pool_slot != MY_BIT_NONE) @@ -10512,7 +10516,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list) &share, sizeof(*share), &field, (field_count + 1) * sizeof(Field*), &blob_field, (field_count+1) *sizeof(uint), - &bitmaps, bitmap_buffer_size(field_count)*2, + &bitmaps, bitmap_buffer_size(field_count)*3, NullS)) return 0; @@ -11475,6 +11479,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, return NESTED_LOOP_KILLED; /* purecov: inspected */ } DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond)); + update_virtual_fields(join_tab->table); if (!select_cond || select_cond->val_int()) { /* @@ -11684,6 +11689,8 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last) return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */ } SQL_SELECT *select=join_tab->select; + if (rc == NESTED_LOOP_OK) + update_virtual_fields(join_tab->table); if (rc == NESTED_LOOP_OK && (!join_tab->cache.select || !join_tab->cache.select->skip_record())) { @@ -11867,6 +11874,7 @@ join_read_system(JOIN_TAB *tab) empty_record(table); // Make empty record return -1; } + update_virtual_fields(table); store_record(table,record[1]); } else if (!table->status) // Only happens with left join @@ -11915,6 +11923,7 @@ join_read_const(JOIN_TAB *tab) return report_error(table, error); return -1; } + update_virtual_fields(table); store_record(table,record[1]); } else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join @@ -12174,6 +12183,7 @@ join_read_next(READ_RECORD *info) int error; if ((error=info->file->index_next(info->record))) return report_error(info->table, error); + return 0; } @@ -12201,6 +12211,7 @@ join_read_last(JOIN_TAB *tab) error= table->file->prepare_index_scan(); if (error || (error= tab->table->file->index_last(tab->table->record[0]))) return report_error(table, error); + return 0; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b1f64a5a703..2c52e8eb918 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1185,6 +1185,19 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); + if (field->vcol_info) + { + packet->append(STRING_WITH_LEN(" AS (")); + packet->append(field->vcol_info->expr_str.str, + field->vcol_info->expr_str.length, + system_charset_info); + packet->append(STRING_WITH_LEN(")")); + if (field->stored_in_db) + packet->append(STRING_WITH_LEN(" PERSISTENT")); + else + packet->append(STRING_WITH_LEN(" VIRTUAL")); + } + if (field->has_charset() && !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) { @@ -1215,7 +1228,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" NULL")); } - if (get_field_default_value(thd, table, field, &def_value, 1)) + if (!field->vcol_info && + get_field_default_value(thd, table, field, &def_value, 1)) { packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); @@ -3908,6 +3922,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, field->unireg_check != Field::TIMESTAMP_DN_FIELD) table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"), cs); + if (field->vcol_info) + table->field[16]->store(STRING_WITH_LEN("VIRTUAL"), cs); table->field[18]->store(field->comment.str, field->comment.length, cs); if (schema_table_store_record(thd, table)) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3aa9722c6ea..970228ac2e5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2441,7 +2441,8 @@ int prepare_create_field(Create_field *sql_field, (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); break; } - if (!(sql_field->flags & NOT_NULL_FLAG)) + if (!(sql_field->flags & NOT_NULL_FLAG) || + (sql_field->vcol_info)) /* Make virtual columns allow NULL values */ sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL; if (sql_field->flags & NO_DEFAULT_VALUE_FLAG) sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; @@ -2755,6 +2756,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, null_fields--; sql_field->flags= dup_field->flags; sql_field->interval= dup_field->interval; + sql_field->vcol_info= dup_field->vcol_info; + sql_field->stored_in_db= dup_field->stored_in_db; it2.remove(); // Remove first (create) definition select_field_pos--; break; @@ -2787,7 +2790,23 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->offset= record_offset; if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) auto_increment++; - record_offset+= sql_field->pack_length; + /* + For now skip fields that are not physically stored in the database + (virtual fields) and update their offset later + (see the next loop). + */ + if (sql_field->stored_in_db) + record_offset+= sql_field->pack_length; + } + /* Update virtual fields' offset*/ + it.rewind(); + while ((sql_field=it++)) + { + if (!sql_field->stored_in_db) + { + sql_field->offset= record_offset; + record_offset+= sql_field->pack_length; + } } if (timestamps_with_niladic > 1) { @@ -2837,6 +2856,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::FOREIGN_KEY) { fk_key_count++; + if (((Foreign_key *)key)->validate(alter_info->create_list)) + DBUG_RETURN(TRUE); Foreign_key *fk_key= (Foreign_key*) key; if (fk_key->ref_columns.elements && fk_key->ref_columns.elements != fk_key->columns.elements) @@ -3123,6 +3144,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } #endif + if (!sql_field->stored_in_db) + { + /* Key fields must always be physically stored. */ + my_error(ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN, MYF(0)); + DBUG_RETURN(TRUE); + } + if (key->type == Key::PRIMARY && sql_field->vcol_info) + { + my_error(ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN, MYF(0)); + DBUG_RETURN(TRUE); + } if (!(sql_field->flags & NOT_NULL_FLAG)) { if (key->type == Key::PRIMARY) @@ -5644,6 +5676,19 @@ compare_tables(TABLE *table, DBUG_RETURN(0); } + /* + Check if the altered column is computed and either + is stored or is used in the partitioning expression. + TODO: Mark such a column with an alter flag only if + the defining expression has changed. + */ + if (field->vcol_info && + (field->stored_in_db || field->vcol_info->is_in_partitioning_expr())) + { + *need_copy_table= ALTER_TABLE_DATA_CHANGED; + DBUG_RETURN(0); + } + /* Don't pack rows in old tables if the user has requested this. */ if (create_info->row_type == ROW_TYPE_DYNAMIC || (tmp_new_field->flags & BLOB_FLAG) || @@ -6008,6 +6053,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (def) { // Field is changed def->field=field; + if (field->stored_in_db != def->stored_in_db) + { + my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, + MYF(0), + "Changing the STORED status"); + goto err; + } if (!def->after) { new_create_list.push_back(def); @@ -6220,6 +6272,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Key *key; while ((key=key_it++)) // Add new keys { + if (key->type == Key::FOREIGN_KEY && + ((Foreign_key *)key)->validate(new_create_list)) + goto err; if (key->type != Key::FOREIGN_KEY) new_key_list.push_back(key); if (key->name && @@ -7600,6 +7655,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); + to->mark_virtual_columns_for_write(); init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE); errpos= 4; if (ignore) @@ -7614,6 +7670,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, error= 1; break; } + update_virtual_fields(from); thd->row_count++; /* Return error if source table isn't empty. */ if (error_if_not_empty) @@ -7634,6 +7691,12 @@ copy_data_between_tables(TABLE *from,TABLE *to, copy_ptr->do_copy(copy_ptr); } prev_insert_id= to->file->next_insert_id; + update_virtual_fields(to, TRUE); + if (thd->is_error()) + { + error= 1; + break; + } error=to->file->ha_write_row(to->record[0]); to->auto_increment_field_not_null= FALSE; if (error) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e08e44d34d7..a9d18c12f97 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -549,6 +549,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ALGORITHM_SYM %token ALL /* SQL-2003-R */ %token ALTER /* SQL-2003-R */ +%token ALWAYS_SYM %token ANALYZE_SYM %token AND_AND_SYM /* OPERATOR */ %token AND_SYM /* SQL-2003-R */ @@ -715,6 +716,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FULLTEXT_SYM %token FUNCTION_SYM /* SQL-2003-R */ %token GE +%token GENERATED_SYM %token GEOMETRYCOLLECTION %token GEOMETRY_SYM %token GET_FORMAT /* MYSQL-FUNC */ @@ -892,11 +894,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PAGE_CHECKSUM_SYM %token PARAM_MARKER %token PARSER_SYM +%token PARSE_VCOL_EXPR_SYM %token PARTIAL /* SQL-2003-N */ %token PARTITIONING_SYM %token PARTITIONS_SYM %token PARTITION_SYM /* SQL-2003-R */ %token PASSWORD +%token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM %token PLUGIN_SYM @@ -1090,6 +1094,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM +%token VIRTUAL_SYM %token VIEW_SYM /* SQL-2003-N */ %token WAIT_SYM %token WARNINGS @@ -1149,7 +1154,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); text_string opt_gconcat_separator %type <num> - type int_type real_type order_dir lock_option + type int_type real_type order_dir lock_option field_def udf_type if_exists opt_local opt_table_options table_options table_option opt_if_not_exists opt_no_write_to_binlog delete_option opt_temporary all_or_any opt_distinct @@ -1305,6 +1310,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); init_key_options key_options key_opts key_opt key_using_alg server_def server_options_list server_option definer_opt no_definer definer + parse_vcol_expr vcol_opt_specifier vcol_opt_attribute + vcol_opt_attribute_list vcol_attribute END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1436,6 +1443,7 @@ statement: | lock | optimize | keycache + | parse_vcol_expr | partition_entry | preload | prepare @@ -4765,8 +4773,9 @@ field_spec: lex->default_value= lex->on_update_value= 0; lex->comment=null_lex_str; lex->charset=NULL; + lex->vcol_info= 0; } - type opt_attribute + field_def { LEX *lex=Lex; if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3, @@ -4774,11 +4783,101 @@ field_spec: lex->default_value, lex->on_update_value, &lex->comment, lex->change,&lex->interval_list,lex->charset, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info)) MYSQL_YYABORT; } ; +field_def: + type opt_attribute {} + | type opt_generated_always AS '(' virtual_column_func ')' + vcol_opt_specifier + vcol_opt_attribute + { + $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL; + Lex->vcol_info->set_field_type((enum enum_field_types) $1); + } + ; + +opt_generated_always: + /* empty */ + | GENERATED_SYM ALWAYS_SYM {} + ; + +vcol_opt_specifier: + /* empty */ + { + Lex->vcol_info->set_stored_in_db_flag(FALSE); + } + | VIRTUAL_SYM + { + Lex->vcol_info->set_stored_in_db_flag(FALSE); + } + | PERSISTENT_SYM + { + Lex->vcol_info->set_stored_in_db_flag(TRUE); + } + ; + +vcol_opt_attribute: + /* empty */ {} + | vcol_opt_attribute_list {} + ; + +vcol_opt_attribute_list: + vcol_opt_attribute_list vcol_attribute {} + | vcol_attribute + ; + +vcol_attribute: + UNIQUE_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } + ; + +parse_vcol_expr: + PARSE_VCOL_EXPR_SYM '(' virtual_column_func ')' + { + /* + "PARSE_VCOL_EXPR" can only be used by the SQL server + when reading a '*.frm' file. + Prevent the end user from invoking this command. + */ + if (!Lex->parse_vcol_expr) + { + my_message(ER_SYNTAX_ERROR, ER(ER_SYNTAX_ERROR), MYF(0)); + MYSQL_YYABORT; + } + } + ; + +virtual_column_func: + remember_name expr remember_end + { + Lex->vcol_info= new Virtual_column_info(); + if (!Lex->vcol_info) + { + mem_alloc_error(sizeof(Virtual_column_info)); + MYSQL_YYABORT; + } + uint expr_len= (uint)($3 - $1) - 1; + Lex->vcol_info->expr_str.str= (char* ) sql_memdup($1 + 1, expr_len); + Lex->vcol_info->expr_str.length= expr_len; + Lex->vcol_info->expr_item= $2; + } + ; + type: int_type opt_field_length field_options { $$=$1; } | real_type opt_precision field_options { $$=$1; } @@ -5866,8 +5965,9 @@ alter_list_item: lex->comment=null_lex_str; lex->charset= NULL; lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->vcol_info= 0; } - type opt_attribute + field_def { LEX *lex=Lex; if (add_field_to_list(lex->thd,&$3, @@ -5876,7 +5976,8 @@ alter_list_item: lex->default_value, lex->on_update_value, &lex->comment, $3.str, &lex->interval_list, lex->charset, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info)) MYSQL_YYABORT; } opt_place @@ -11430,6 +11531,7 @@ keyword_sp: | AGAINST {} | AGGREGATE_SYM {} | ALGORITHM_SYM {} + | ALWAYS_SYM {} | ANY_SYM {} | AT_SYM {} | AUTHORS_SYM {} @@ -11499,6 +11601,7 @@ keyword_sp: | FIRST_SYM {} | FIXED_SYM {} | FRAC_SECOND_SYM {} + | GENERATED_SYM {} | GEOMETRY_SYM {} | GEOMETRYCOLLECTION {} | GET_FORMAT {} @@ -11586,6 +11689,7 @@ keyword_sp: | PARTITIONING_SYM {} | PARTITIONS_SYM {} | PASSWORD {} + | PERSISTENT_SYM {} | PHASE_SYM {} | PLUGIN_SYM {} | PLUGINS_SYM {} @@ -11678,6 +11782,7 @@ keyword_sp: | USE_FRM {} | VARIABLES {} | VIEW_SYM {} + | VIRTUAL_SYM {} | VALUE_SYM {} | WARNINGS {} | WAIT_SYM {} @@ -13338,6 +13443,7 @@ sf_tail: lex->length= lex->dec= NULL; lex->interval_list.empty(); lex->type= 0; + lex->vcol_info= 0; } type /* $11 */ { /* $12 */ diff --git a/sql/table.cc b/sql/table.cc index edf671b5a3b..003cab73a3d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -33,6 +33,12 @@ LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; /* SLOW_LOG name */ LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")}; +/* + Keyword added as a prefix when parsing the defining expression for a + virtual column read from the column definition saved in the frm file +*/ +LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; + /* Functions defined in this file */ void open_table_error(TABLE_SHARE *share, int error, int db_errno, @@ -663,10 +669,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; uint key_info_length, com_length, null_bit_pos; + uint vcol_screen_length; uint extra_rec_buf_length; uint i,j; bool use_hash; - char *keynames, *names, *comment_pos; + char *keynames, *names, *comment_pos, *vcol_screen_pos; uchar *record; uchar *disk_buff, *strpos, *null_flags, *null_pos; ulong pos, record_offset, *rec_per_key, rec_buff_length; @@ -836,6 +843,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; share->reclength = uint2korr((head+16)); + share->stored_rec_length= share->reclength; if (*(head+26) == 1) share->system= 1; /* one-record-database */ #ifdef HAVE_CRYPTED_FRM @@ -1040,24 +1048,28 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, int_length= uint2korr(head+274); share->null_fields= uint2korr(head+282); com_length= uint2korr(head+284); + vcol_screen_length= uint2korr(head+286); + share->vfields= 0; + share->stored_fields= share->fields; share->comment.length= (int) (head[46]); share->comment.str= strmake_root(&share->mem_root, (char*) head+47, share->comment.length); - DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length)); - + DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length)); if (!(field_ptr = (Field **) alloc_root(&share->mem_root, (uint) ((share->fields+1)*sizeof(Field*)+ interval_count*sizeof(TYPELIB)+ (share->fields+interval_parts+ keys+3)*sizeof(char *)+ - (n_length+int_length+com_length))))) + (n_length+int_length+com_length+ + vcol_screen_length))))) goto err; /* purecov: inspected */ share->field= field_ptr; read_length=(uint) (share->fields * field_pack_length + - pos+ (uint) (n_length+int_length+com_length)); + pos+ (uint) (n_length+int_length+com_length+ + vcol_screen_length)); if (read_string(file,(uchar**) &disk_buff,read_length)) goto err; /* purecov: inspected */ #ifdef HAVE_CRYPTED_FRM @@ -1078,7 +1090,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, memcpy((char*) names, strpos+(share->fields*field_pack_length), (uint) (n_length+int_length)); comment_pos= names+(n_length+int_length); - memcpy(comment_pos, disk_buff+read_length-com_length, com_length); + memcpy(comment_pos, disk_buff+read_length-com_length-vcol_screen_length, + com_length); + vcol_screen_pos= names+(n_length+int_length+com_length); + memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length, + vcol_screen_length); fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); if (share->fieldnames.count != share->fields) @@ -1146,10 +1162,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; + uint vcol_info_length=0; + uint vcol_expr_length=0; enum_field_types field_type; CHARSET_INFO *charset=NULL; Field::geometry_type geom_type= Field::GEOM_GEOMETRY; LEX_STRING comment; + Virtual_column_info *vcol_info= 0; + bool fld_stored_in_db= TRUE; if (new_frm_ver >= 3) { @@ -1184,6 +1204,18 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, goto err; } } + + if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL) + { + DBUG_ASSERT(interval_nr); // Expect non-null expression + /* + The interval_id byte in the .frm file stores the length of the + expression statement for a virtual column. + */ + vcol_info_length= interval_nr; + interval_nr= 0; + } + if (!comment_length) { comment.str= (char*) ""; @@ -1195,6 +1227,34 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, comment.length= comment_length; comment_pos+= comment_length; } + + if (vcol_info_length) + { + /* + Get virtual column data stored in the .frm file as follows: + byte 1 = 1 (always 1 to allow for future extensions) + byte 2 = sql_type + byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) + byte 4-... = virtual column expression (text data) + */ + vcol_info= new Virtual_column_info(); + if ((uint)vcol_screen_pos[0] != 1) + { + error= 4; + goto err; + } + field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; + fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; + vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE; + if (!(vcol_info->expr_str.str= + (char *)memdup_root(&share->mem_root, + vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE, + vcol_expr_length))) + goto err; + vcol_info->expr_str.length= vcol_expr_length; + vcol_screen_pos+= vcol_info_length; + share->vfields++; + } } else { @@ -1285,6 +1345,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, reg_field->field_index= i; reg_field->comment=comment; + reg_field->vcol_info= vcol_info; + reg_field->stored_in_db= fld_stored_in_db; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { if ((null_bit_pos+= field_length & 7) > 7) @@ -1309,8 +1371,17 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (use_hash) (void) my_hash_insert(&share->name_hash, (uchar*) field_ptr); // never fail + if (!reg_field->stored_in_db) + { + share->stored_fields--; + if (share->stored_rec_length>=recpos) + share->stored_rec_length= recpos-1; + } } *field_ptr=0; // End marker + /* Sanity checks: */ + DBUG_ASSERT(share->fields>=share->stored_fields); + DBUG_ASSERT(share->reclength>=share->stored_rec_length); /* Fix key->name and key_part->field */ if (key_parts) @@ -1597,6 +1668,297 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, DBUG_RETURN(error); } /* open_binary_frm */ +/* + @brief + Clear GET_FIXED_FIELDS_FLAG in all fields of a table + + @param + table The table for whose fields the flags are to be cleared + + @note + This routine is used for error handling purposes. + + @return + none +*/ + +static void clear_field_flag(TABLE *table) +{ + Field **ptr; + DBUG_ENTER("clear_field_flag"); + + for (ptr= table->field; *ptr; ptr++) + (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG); + DBUG_VOID_RETURN; +} + + +/* + @brief + Perform semantic analysis of the defining expression for a virtual column + + @param + thd The thread object + @param + table The table containing the virtual column + @param + vcol_field The virtual field whose defining expression is to be analyzed + + @details + The function performs semantic analysis of the defining expression for + the virtual column vcol_field. The expression is used to compute the + values of this column. + + @note + The function exploits the fact that the fix_fields method sets the flag + GET_FIXED_FIELDS_FLAG for all fields in the item tree. + This flag must always be unset before returning from this function + since it is used for other purposes as well. + + @retval + TRUE An error occurred, something was wrong with the function + @retval + FALSE Otherwise +*/ + +bool fix_vcol_expr(THD *thd, + TABLE *table, + Field *vcol_field) +{ + Virtual_column_info *vcol_info= vcol_field->vcol_info; + Item* func_expr= vcol_info->expr_item; + uint dir_length, home_dir_length; + bool result= TRUE; + TABLE_LIST tables; + TABLE_LIST *save_table_list, *save_first_table, *save_last_table; + int error; + Name_resolution_context *context; + const char *save_where; + char* db_name; + char db_name_string[FN_REFLEN]; + bool save_use_only_table_context; + Field **ptr, *field; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + DBUG_ASSERT(func_expr); + DBUG_ENTER("fix_vcol_expr"); + + /* + Set-up the TABLE_LIST object to be a list with a single table + Set the object to zero to create NULL pointers and set alias + and real name to table name and get database name from file name. + */ + + bzero((void*)&tables, sizeof(TABLE_LIST)); + tables.alias= tables.table_name= (char*) table->s->table_name.str; + tables.table= table; + tables.next_local= 0; + tables.next_name_resolution_table= 0; + strmov(db_name_string, table->s->normalized_path.str); + dir_length= dirname_length(db_name_string); + db_name_string[dir_length - 1]= 0; + home_dir_length= dirname_length(db_name_string); + db_name= &db_name_string[home_dir_length]; + tables.db= db_name; + + thd->mark_used_columns= MARK_COLUMNS_NONE; + + context= thd->lex->current_context(); + table->map= 1; //To ensure correct calculation of const item + table->get_fields_in_item_tree= TRUE; + save_table_list= context->table_list; + save_first_table= context->first_name_resolution_table; + save_last_table= context->last_name_resolution_table; + context->table_list= &tables; + context->first_name_resolution_table= &tables; + context->last_name_resolution_table= NULL; + func_expr->walk(&Item::change_context_processor, 0, (uchar*) context); + save_where= thd->where; + thd->where= "virtual column function"; + + /* Save the context before fixing the fields*/ + save_use_only_table_context= thd->lex->use_only_table_context; + thd->lex->use_only_table_context= TRUE; + /* Fix fields referenced to by the virtual column function */ + error= func_expr->fix_fields(thd, (Item**)0); + /* Restore the original context*/ + thd->lex->use_only_table_context= save_use_only_table_context; + context->table_list= save_table_list; + context->first_name_resolution_table= save_first_table; + context->last_name_resolution_table= save_last_table; + + if (unlikely(error)) + { + DBUG_PRINT("info", + ("Field in virtual column expression does not belong to the table")); + goto end; + } + thd->where= save_where; +#ifdef PARANOID + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + error= func_expr->walk(&Item::check_vcol_func_processor, 0, NULL); + if (error) + { + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); + goto end; + } +#endif + if (unlikely(func_expr->const_item())) + { + my_error(ER_CONST_EXPR_IN_VCOL, MYF(0)); + goto end; + } + /* Ensure that this virtual column is not based on another virtual field. */ + ptr= table->field; + while ((field= *(ptr++))) + { + if ((field->flags & GET_FIXED_FIELDS_FLAG) && + (field->vcol_info)) + { + my_error(ER_VCOL_BASED_ON_VCOL, MYF(0)); + goto end; + } + } + result= FALSE; + +end: + + /* Clear GET_FIXED_FIELDS_FLAG for the fields of the table */ + clear_field_flag(table); + + table->get_fields_in_item_tree= FALSE; + thd->mark_used_columns= save_mark_used_columns; + table->map= 0; //Restore old value + + DBUG_RETURN(result); +} + +/* + @brief + Unpack the definition of a virtual column from its linear representation + + @parm + thd The thread object + @param + table The table containing the virtual column + @param + field The field for the virtual + @param + vcol_expr The string representation of the defining expression + @param[out] + error_reported The flag to inform the caller that no other error + messages are to be generated + + @details + The function takes string representation 'vcol_expr' of the defining + expression for the virtual field 'field' of the table 'table' and + parses it, building an item object for it. The pointer to this item is + placed into in field->vcol_info.expr_item. After this the function performs + semantic analysis of the item by calling the the function fix_vcol_expr. + Since the defining expression is part of the table definition the item + for it is created in table->memroot within a separate Query_arena. + The free_list of this arena is saved in field->vcol_info.item_free_list + to be freed when the table defition is removed from the TABLE_SHARE cache. + + @note + Before passing 'vcol_expr" to the parser the function embraces it in + parenthesis and prepands it a special keyword. + + @retval + FALSE If a success + @retval + TRUE Otherwise +*/ +bool unpack_vcol_info_from_frm(THD *thd, + TABLE *table, + Field *field, + LEX_STRING *vcol_expr, + bool *error_reported) +{ + bool rc= FALSE; + DBUG_ENTER("unpack_vcol_info_from_frm"); + DBUG_ASSERT(vcol_expr); + + /* + Step 1: Construct the input string for the parser. + The string to be parsed has to be of the following format: + "PARSE_VCOL_EXPR (<expr_string_from_frm>)". + */ + char *vcol_expr_str; + int str_len= 0; + CHARSET_INFO *old_character_set_client; + + if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root, + vcol_expr->length + + parse_vcol_keyword.length + 3))) + { + DBUG_RETURN(TRUE); + } + memcpy(vcol_expr_str, + (char*) parse_vcol_keyword.str, + parse_vcol_keyword.length); + str_len= parse_vcol_keyword.length; + memcpy(vcol_expr_str + str_len, "(", 1); + str_len++; + memcpy(vcol_expr_str + str_len, + (char*) vcol_expr->str, + vcol_expr->length); + str_len+= vcol_expr->length; + memcpy(vcol_expr_str + str_len, ")", 1); + str_len++; + memcpy(vcol_expr_str + str_len, "\0", 1); + str_len++; + Parser_state parser_state(thd, vcol_expr_str, str_len); + + /* + Step 2: Setup thd for parsing. + */ + Query_arena *backup_stmt_arena_ptr= thd->stmt_arena; + Query_arena backup_arena; + Query_arena vcol_arena(&table->mem_root, Query_arena::INITIALIZED); + thd->set_n_backup_active_arena(&vcol_arena, &backup_arena); + thd->stmt_arena= &vcol_arena; + + thd->lex->parse_vcol_expr= TRUE; + old_character_set_client= thd->variables.character_set_client; + + /* + Step 3: Use the parser to build an Item object from vcol_expr_str. + */ + if (parse_sql(thd, &parser_state, NULL)) + { + goto err; + } + /* From now on use vcol_info generated by the parser. */ + field->vcol_info= thd->lex->vcol_info; + + /* Validate the Item tree. */ + if (fix_vcol_expr(thd, table, field)) + { + *error_reported= TRUE; + field->vcol_info= 0; + goto err; + } + field->vcol_info->item_free_list= thd->free_list; + goto end; + +err: + rc= TRUE; + thd->lex->parse_vcol_expr= FALSE; + thd->free_items(); +end: + thd->stmt_arena= backup_stmt_arena_ptr; + thd->restore_active_arena(&vcol_arena, &backup_arena); + thd->variables.character_set_client= old_character_set_client; + + DBUG_RETURN(rc); +} + +/* + Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE +*/ /* Open a table based on a TABLE_SHARE @@ -1631,7 +1993,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, uint records, i, bitmap_size; bool error_reported= FALSE; uchar *record, *bitmaps; - Field **field_ptr; + Field **field_ptr, **vfield_ptr; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, share->table_name.str, (long) outparam)); @@ -1785,6 +2147,34 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } } + /* + Process virtual columns, if any. + */ + if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, + (uint) ((share->vfields+1)* + sizeof(Field*))))) + goto err; + + outparam->vfield= vfield_ptr; + + for (field_ptr= outparam->field; *field_ptr; field_ptr++) + { + if ((*field_ptr)->vcol_info) + { + if (unpack_vcol_info_from_frm(thd, + outparam, + *field_ptr, + &(*field_ptr)->vcol_info->expr_str, + &error_reported)) + { + error= 4; // in case no error is reported + goto err; + } + *(vfield_ptr++)= *field_ptr; + } + } + *vfield_ptr= 0; // End marker + #ifdef WITH_PARTITION_STORAGE_ENGINE if (share->partition_info_len && outparam->file) { @@ -1852,10 +2242,24 @@ partititon_err: } #endif + /* Check virtual columns against table's storage engine. */ + if (share->vfields && + ((outparam->file && + !outparam->file->check_if_supported_virtual_columns()) || + (!outparam->file && share->db_type() && + share->db_type()->db_type == DB_TYPE_CSV_DB))) // Workaround for CSV + { + my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, + MYF(0), + "Specified storage engine"); + error_reported= TRUE; + goto err; + } + /* Allocate bitmaps */ bitmap_size= share->column_bitmap_size; - if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*3))) + if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*4))) goto err; bitmap_init(&outparam->def_read_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); @@ -1863,6 +2267,8 @@ partititon_err: (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE); bitmap_init(&outparam->tmp_set, (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE); + bitmap_init(&outparam->vcol_set, + (my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE); outparam->default_column_bitmaps(); /* The table struct is now initialized; Open the table */ @@ -1966,7 +2372,11 @@ int closefrm(register TABLE *table, bool free_share) if (table->field) { for (Field **ptr=table->field ; *ptr ; ptr++) + { + if ((*ptr)->vcol_info) + free_items((*ptr)->vcol_info->item_free_list); delete *ptr; + } table->field= 0; } delete table->file; @@ -4302,6 +4712,7 @@ void st_table::clear_column_bitmaps() bitmap_clear_all(&table->def_write_set); */ bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2); + bzero((char*) def_read_set.bitmap, s->column_bitmap_size*4); column_bitmaps_set(&def_read_set, &def_write_set); } @@ -4387,7 +4798,14 @@ void st_table::mark_columns_used_by_index_no_reset(uint index, KEY_PART_INFO *key_part_end= (key_part + key_info[index].key_parts); for (;key_part != key_part_end; key_part++) + { bitmap_set_bit(bitmap, key_part->fieldnr-1); + if (key_part->field->vcol_info && + key_part->field->vcol_info->expr_item) + key_part->field->vcol_info-> + expr_item->walk(&Item::register_field_in_bitmap, + 1, (uchar *) bitmap); + } } @@ -4514,6 +4932,8 @@ void st_table::mark_columns_needed_for_update() file->column_bitmaps_signal(); } } + /* Mark all virtual columns needed for update */ + mark_virtual_columns_for_write(); DBUG_VOID_RETURN; } @@ -4540,9 +4960,100 @@ void st_table::mark_columns_needed_for_insert() } if (found_next_number_field) mark_auto_increment_column(); + /* Mark virtual columns for insert */ + mark_virtual_columns_for_write(); +} + + +/* + @brief Mark a column as virtual used by the query + + @param field the field for the column to be marked + + @details + The function marks the column for 'field' as virtual (computed) + in the bitmap vcol_set. + If the column is marked for the first time the expression to compute + the column is traversed and all columns that are occurred there are + marked in the read_set of the table. + + @retval + TRUE if column is marked for the first time + @retval + FALSE otherwise +*/ + +bool st_table::mark_virtual_col(Field *field) +{ + bool res; + DBUG_ASSERT(field->vcol_info); + if (!(res= bitmap_fast_test_and_set(&vcol_set, field->field_index))) + { + Item *vcol_item= field->vcol_info->expr_item; + DBUG_ASSERT(vcol_item); + vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + } + return res; } +/* + @brief Mark virtual columns for update/insert commands + + @details + The function marks virtual columns used in a update/insert commands + in the vcol_set bitmap. + If a virtual column is from write_set it is always marked in vcol_set. + If a stored virtual column is not from write_set but it is computed + through columns from write_set it is also marked in vcol_set, and, + besides, it is added to write_set. + + @return void + + @note + Let table t1 have columns a,b,c and let column c be a stored virtual + column computed through columns a and b. Then for the query + UPDATE t1 SET a=1 + column c will be placed into vcol_set and into write_set while + column b will be placed into read_set. + If column c was a virtual column, but not a stored virtual column + then it would not be added to any of the sets. Column b would not + be added to read_set either. +*/ + +void st_table::mark_virtual_columns_for_write(void) +{ + Field **vfield_ptr, *tmp_vfield; + bool bitmap_updated= FALSE; + + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + tmp_vfield= *vfield_ptr; + if (bitmap_is_set(write_set, tmp_vfield->field_index)) + bitmap_updated= mark_virtual_col(tmp_vfield); + else if (tmp_vfield->stored_in_db) + { + MY_BITMAP *save_read_set; + Item *vcol_item= tmp_vfield->vcol_info->expr_item; + DBUG_ASSERT(vcol_item); + bitmap_clear_all(&tmp_set); + save_read_set= read_set; + read_set= &tmp_set; + vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + read_set= save_read_set; + bitmap_intersect(&tmp_set, write_set); + if (!bitmap_is_clear_all(&tmp_set)) + { + bitmap_set_bit(write_set, tmp_vfield->field_index); + mark_virtual_col(tmp_vfield); + bitmap_updated= TRUE; + } + } + } + if (bitmap_updated) + file->column_bitmaps_signal(); +} + /** @brief Check if this is part of a MERGE table with attached children. @@ -4550,7 +5061,7 @@ void st_table::mark_columns_needed_for_insert() @retval TRUE children are attached @retval FALSE no MERGE part or children not attached - @detail + @details A MERGE table consists of a parent TABLE and zero or more child TABLEs. Each of these TABLEs is called a part of a MERGE table. */ @@ -4796,6 +5307,54 @@ size_t max_row_length(TABLE *table, const uchar *data) return length; } +/* + @brief Compute values for virtual columns used in query + + @param table The TABLE object + @param for_write Requests to compute only fields needed for write + + @details + The function computes the values of the virtual columns of the table and + stores them in the table record buffer. + Only fields from vcol_set are computed, and, when the flag for_write is not + set to TRUE, a virtual field is computed only if it's not stored. + The flag for_write is set to TRUE for row insert/update operations. + + @retval + 0 Success + @retval + >0 Error occurred when storing a virtual field value +*/ + +int update_virtual_fields(TABLE *table, bool for_write) +{ + DBUG_ENTER("update_virtual_fields"); + Field **vfield_ptr, *vfield; + int error= 0; + if (!table || !table->vfield) + DBUG_RETURN(0); + + /* Iterate over virtual fields in the table */ + for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) + { + vfield= (*vfield_ptr); + DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item); + /* Only update those fields that are marked in the vcol_set bitmap */ + if (bitmap_is_set(&table->vcol_set, vfield->field_index) && + (for_write || !vfield->stored_in_db)) + { + /* Compute the actual value of the virtual fields */ + error= vfield->vcol_info->expr_item->save_in_field(vfield, 0); + DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name)); + } + else + { + DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name)); + } + } + DBUG_RETURN(0); +} + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index a7ae50b8e72..12b6e5b050e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -352,6 +352,8 @@ typedef struct st_table_share ulong version, mysql_version; ulong timestamp_offset; /* Set to offset+1 of record */ ulong reclength; /* Recordlength */ + /* Stored record length. No generated-only virtual fields are included */ + ulong stored_rec_length; plugin_ref db_plugin; /* storage engine plugin */ inline handlerton *db_type() const /* table_type for handler */ @@ -372,6 +374,8 @@ typedef struct st_table_share uint key_block_size; /* create key_block_size, if used */ uint null_bytes, last_null_bit_pos; uint fields; /* Number of fields */ + /* Number of stored fields, generated-only virtual fields are not included */ + uint stored_fields; uint rec_buff_length; /* Size of table->record[] buffer */ uint keys, key_parts; uint max_key_length, max_unique_length, total_key_length; @@ -393,6 +397,7 @@ typedef struct st_table_share uint error, open_errno, errarg; /* error from open_table_def() */ uint column_bitmap_size; uchar frm_version; + uint vfields; /* Number of computed (virtual) fields */ bool null_field_first; bool system; /* Set if system table (one record) */ bool crypted; /* If .frm file is crypted */ @@ -657,6 +662,7 @@ struct st_table { Field *next_number_field; /* Set if next_number is activated */ Field *found_next_number_field; /* Set on open */ Field_timestamp *timestamp_field; + Field **vfield; /* Pointer to virtual fields*/ /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; @@ -666,6 +672,7 @@ struct st_table { uchar *null_flags; my_bitmap_map *bitmap_init_value; MY_BITMAP def_read_set, def_write_set, tmp_set; /* containers */ + MY_BITMAP vcol_set; /* set of used virtual columns */ MY_BITMAP *read_set, *write_set; /* Active column sets */ /* The ID of the query that opened and is using this table. Has different @@ -819,6 +826,8 @@ struct st_table { void mark_columns_needed_for_update(void); void mark_columns_needed_for_delete(void); void mark_columns_needed_for_insert(void); + bool mark_virtual_col(Field *field); + void mark_virtual_columns_for_write(void); inline void column_bitmaps_set(MY_BITMAP *read_set_arg, MY_BITMAP *write_set_arg) { diff --git a/sql/unireg.cc b/sql/unireg.cc index 3b1836aeb88..cb1e343ff7d 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -583,7 +583,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, { uint length,int_count,int_length,no_empty, int_parts; uint time_stamp_pos,null_fields; - ulong reclength, totlength, n_length, com_length; + ulong reclength, totlength, n_length, com_length, vcol_info_length; DBUG_ENTER("pack_header"); if (create_fields.elements > MAX_FIELDS) @@ -594,8 +594,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, totlength= 0L; reclength= data_offset; - no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields= - com_length=0; + no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0; + com_length=vcol_info_length=0; n_length=2L; /* Check fields */ @@ -623,6 +623,30 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, field->field_name, tmp_len); field->comment.length= tmp_len; } + if (field->vcol_info) + { + tmp_len= + system_charset_info->cset->charpos(system_charset_info, + field->vcol_info->expr_str.str, + field->vcol_info->expr_str.str + + field->vcol_info->expr_str.length, + VIRTUAL_COLUMN_EXPRESSION_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", + (uint) VIRTUAL_COLUMN_EXPRESSION_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+ + (uint)FRM_VCOL_HEADER_SIZE; + } totlength+= field->length; com_length+= field->comment.length; @@ -642,8 +666,6 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, !time_stamp_pos) time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1; length=field->pack_length; - /* Ensure we don't have any bugs when generating offsets */ - DBUG_ASSERT(reclength == field->offset + data_offset); if ((uint) field->offset+ (uint) data_offset+ length > reclength) reclength=(uint) (field->offset+ data_offset + length); n_length+= (ulong) strlen(field->field_name)+1; @@ -710,7 +732,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, /* Hack to avoid bugs with small static rows in MySQL */ reclength=max(file->min_record_length(table_options),reclength); if (info_length+(ulong) create_fields.elements*FCOMP+288+ - n_length+int_length+com_length > 65535L || int_count > 255) + n_length+int_length+com_length+vcol_info_length > 65535L || + int_count > 255) { my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0)); DBUG_RETURN(1); @@ -718,7 +741,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, bzero((char*)forminfo,288); length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+ - com_length); + com_length+vcol_info_length); int2store(forminfo,length); forminfo[256] = (uint8) screens; int2store(forminfo+258,create_fields.elements); @@ -735,7 +758,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, int2store(forminfo+280,22); /* Rows needed */ int2store(forminfo+282,null_fields); int2store(forminfo+284,com_length); - /* Up to forminfo+288 is free to use for additional information */ + int2store(forminfo+286,vcol_info_length); + /* forminfo+288 is free to use for additional information */ DBUG_RETURN(0); } /* pack_header */ @@ -774,7 +798,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields, ulong data_offset) { reg2 uint i; - uint int_count, comment_length=0; + uint int_count, comment_length= 0, vcol_info_length=0; uchar buff[MAX_FIELD_WIDTH]; Create_field *field; DBUG_ENTER("pack_fields"); @@ -787,6 +811,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields, while ((field=it++)) { uint recpos; + uint cur_vcol_expr_len= 0; buff[0]= (uchar) field->row; buff[1]= (uchar) field->col; buff[2]= (uchar) field->sc_length; @@ -809,6 +834,17 @@ static bool pack_fields(File file, List<Create_field> &create_fields, buff[14]= (uchar) field->charset->number; else 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 + + (uint)FRM_VCOL_HEADER_SIZE; + vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE; + 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); @@ -903,6 +939,34 @@ static bool pack_fields(File file, List<Create_field> &create_fields, DBUG_RETURN(1); } } + if (vcol_info_length) + { + it.rewind(); + int_count=0; + while ((field=it++)) + { + /* + Pack each virtual field as follows: + byte 1 = 1 (always 1 to allow for future extensions) + byte 2 = sql_type + byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) + byte 4-... = virtual column expression (text data) + */ + if (field->vcol_info && field->vcol_info->expr_str.length) + { + buff[0]= (uchar)1; + buff[1]= (uchar) field->sql_type; + buff[2]= (uchar) field->stored_in_db; + if (my_write(file, buff, 3, MYF_RW)) + DBUG_RETURN(1); + if (my_write(file, + (uchar*) field->vcol_info->expr_str.str, + field->vcol_info->expr_str.length, + MYF_RW)) + DBUG_RETURN(1); + } + } + } DBUG_RETURN(0); } diff --git a/sql/unireg.h b/sql/unireg.h index 4fbde3b8631..ec4ecf6a10f 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -212,6 +212,13 @@ #define DEFAULT_KEY_CACHE_NAME "default" +/* The length of the header part for each virtual column in the .frm file */ +#define FRM_VCOL_HEADER_SIZE 3 + +/* Maximum length of the defining expression for a virtual columns */ +#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE + + /* Include prototypes for unireg */ #include "mysqld_error.h" |