diff options
author | Michael Widenius <monty@mariadb.org> | 2016-06-29 09:14:22 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2016-06-30 11:43:02 +0200 |
commit | db7edfed17efe6bc3684b0fbacc0b0249e4f0fa2 (patch) | |
tree | f1f484057487a73d32f379a5fdaacd53bfa27b5e /sql | |
parent | 23d03a1b1e486da353f20964a1b91068bec209c0 (diff) | |
download | mariadb-git-db7edfed17efe6bc3684b0fbacc0b0249e4f0fa2.tar.gz |
MDEV-7563 Support CHECK constraint as in (or close to) SQL Standard
MDEV-10134 Add full support for DEFAULT
- Added support for using tables with MySQL 5.7 virtual fields,
including MySQL 5.7 syntax
- Better error messages also for old cases
- CREATE ... SELECT now also updates timestamp columns
- Blob can now have default values
- Added new system variable "check_constraint_checks", to turn of
CHECK constraint checking if needed.
- Removed some engine independent tests in suite vcol to only test myisam
- Moved some tests from 'include' to 't'. Should some day be done for all tests.
- FRM version increased to 11 if one uses virtual fields or constraints
- Changed to use a bitmap to check if a field has got a value, instead of
setting HAS_EXPLICIT_VALUE bit in field flags
- Expressions can now be up to 65K in total
- Ensure we are not refering to uninitialized fields when handling virtual fields or defaults
- Changed check_vcol_func_processor() to return a bitmap of used types
- Had to change some functions that calculated cached value in fix_fields to do
this in val() or getdate() instead.
- store_now_in_TIME() now takes a THD argument
- fill_record() now updates default values
- Add a lookahead for NOT NULL, to be able to handle DEFAULT 1+1 NOT NULL
- Automatically generate a name for constraints that doesn't have a name
- Added support for ALTER TABLE DROP CONSTRAINT
- Ensure that partition functions register virtual fields used. This fixes
some bugs when using virtual fields in a partitioning function
Diffstat (limited to 'sql')
48 files changed, 1934 insertions, 922 deletions
diff --git a/sql/field.cc b/sql/field.cc index f15e9979ff2..7145be366c7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1674,7 +1674,8 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, 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), - read_stats(NULL), collected_stats(0), vcol_info(0) + read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0), + default_value(0) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; @@ -2290,6 +2291,13 @@ Field *Field::clone(MEM_ROOT *root, my_ptrdiff_t diff) return tmp; } +void Field::set_default_expression() +{ + table->in_use->reset_arena_for_cached_items(table->expr_arena); + (void) default_value->expr_item->save_in_field(this, 0); + table->in_use->reset_arena_for_cached_items(0); +} + /**************************************************************************** Field_null, a field that always return NULL @@ -9739,9 +9747,44 @@ void Column_definition::create_length_to_internal_length(void) } -static inline bool is_item_func(Item* x) +bool check_expression(Virtual_column_info *vcol, const char *type, + const char *name, bool must_be_determinstic) { - return x != NULL && x->type() == Item::FUNC_ITEM; + bool ret; + Item::vcol_func_processor_result res; + /* We use 2 bytes to store the expression length */ + if (vcol->expr_str.length > UINT_MAX32) + { + my_error(ER_EXPRESSION_IS_TOO_BIG, MYF(0), type, name); + return TRUE; + } + + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + + res.errors= 0; + ret= vcol->expr_item->walk(&Item::check_vcol_func_processor, 0, + (uchar*) &res); + vcol->non_deterministic= MY_TEST(res.errors & VCOL_NON_DETERMINISTIC); + + if (ret || + (res.errors & + (VCOL_IMPOSSIBLE | + (must_be_determinstic ? VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC: 0)))) + { + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, + type, name); + return TRUE; + } + /* + Safe to call before fix_fields as long as vcol's don't include sub + queries (which is now checked in check_vcol_func_processor) + */ + if (vcol->expr_item->check_cols(1)) + return TRUE; + return FALSE; } @@ -9755,65 +9798,76 @@ bool Column_definition::check(THD *thd) /* Initialize data for a computed field */ if (vcol_info) { - DBUG_ASSERT(vcol_info->expr_item); - vcol_info->set_field_type(sql_type); - /* - 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); + if (check_expression(vcol_info, "VIRTUAL", field_name, + vcol_info->stored_in_db)) DBUG_RETURN(TRUE); - } } - if (def) + if (check_constraint && + check_expression(check_constraint, "CHECK", field_name, 0)) + DBUG_RETURN(1); + + if (default_value) { - /* - Default value should be literal => basic constants => - no need fix_fields() + Item *def_expr= default_value->expr_item; - We allow only one function as part of default value - - NOW() as default for TIMESTAMP and DATETIME type. - */ - if (def->type() == Item::FUNC_ITEM && - (static_cast<Item_func*>(def)->functype() != Item_func::NOW_FUNC || - (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME) || - def->decimals < length)) - { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + if (check_expression(default_value, "DEFAULT", field_name, 0)) DBUG_RETURN(TRUE); - } - else if (def->type() == Item::NULL_ITEM) + + /* Constant's are stored in the 'empty_record', except for blobs */ + if (def_expr->basic_const_item()) { - def= 0; - if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) + if (def_expr->type() == Item::NULL_ITEM) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); - DBUG_RETURN(1); + default_value= 0; + if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + DBUG_RETURN(1); + } } } - else if (flags & AUTO_INCREMENT_FLAG) - { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); - DBUG_RETURN(TRUE); - } + } + if (default_value && (flags & AUTO_INCREMENT_FLAG)) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + DBUG_RETURN(1); } - if (is_item_func(def)) + if (default_value && !default_value->expr_item->basic_const_item()) { - /* There is a function default for insertions. */ - def= NULL; - unireg_check= (is_item_func(on_update) ? - Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates. - Field::TIMESTAMP_DN_FIELD); // only for insertions. + Item *def_expr= default_value->expr_item; + + unireg_check= Field::NONE; + /* + NOW() for TIMESTAMP and DATETIME fields are handled as in MariaDB 10.1 + by marking them in unireg_check. + */ + if (def_expr->type() == Item::FUNC_ITEM && + (static_cast<Item_func*>(def_expr)->functype() == + Item_func::NOW_FUNC && + (mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME))) + { + /* + We are not checking the number of decimals for timestamps + to allow one to write (for historical reasons) + TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP + Instead we are going to use the number of decimals specifed by the + column. + */ + default_value= 0; + unireg_check= (on_update ? + Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates. + Field::TIMESTAMP_DN_FIELD); // only for insertions. + } + else if (on_update) + unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates } else { /* No function default for insertions. Either NULL or a constant. */ - if (is_item_func(on_update)) + if (on_update) unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates else unireg_check= ((flags & AUTO_INCREMENT_FLAG) ? @@ -9897,33 +9951,6 @@ bool Column_definition::check(THD *thd) case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_GEOMETRY: - if (def) - { - /* Allow empty as default value. */ - String str,*res; - res= def->val_str(&str); - /* - A default other than '' is always an error, and any non-NULL - specified default is an error in strict mode. - */ - if (res->length() || thd->is_strict_mode()) - { - my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), - field_name); /* purecov: inspected */ - DBUG_RETURN(TRUE); - } - else - { - /* - Otherwise a default of '' is just a warning. - */ - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_BLOB_CANT_HAVE_DEFAULT, - ER_THD(thd, ER_BLOB_CANT_HAVE_DEFAULT), - field_name); - } - def= 0; - } flags|= BLOB_FLAG; break; case MYSQL_TYPE_YEAR: @@ -10045,7 +10072,7 @@ bool Column_definition::check(THD *thd) We need to do this check here and in mysql_create_prepare_table() as sp_head::fill_field_definition() calls this function. */ - if (!def && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG)) + if (!default_value && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG)) { /* TIMESTAMP columns get implicit DEFAULT value when @@ -10060,7 +10087,7 @@ bool Column_definition::check(THD *thd) if (!(flags & BLOB_FLAG) && ((length > max_field_charlength && - (sql_type != MYSQL_TYPE_VARCHAR || def)) || + sql_type != MYSQL_TYPE_VARCHAR) || (length == 0 && sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET && sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR && @@ -10437,6 +10464,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field, comment= old_field->comment; decimals= old_field->decimals(); vcol_info= old_field->vcol_info; + default_value= old_field->default_value; + check_constraint= old_field->check_constraint; option_list= old_field->option_list; switch (sql_type) { @@ -10497,7 +10526,6 @@ Column_definition::Column_definition(THD *thd, Field *old_field, interval= ((Field_enum*) old_field)->typelib; else interval=0; - def=0; char_length= length; /* @@ -10506,14 +10534,18 @@ Column_definition::Column_definition(THD *thd, Field *old_field, - The column allows a default. - - The column type is not a BLOB type. + - The column type is not a BLOB type (as BLOB's doesn't have constant + defaults) - The original column (old_field) was properly initialized with a record buffer pointer. + + - The column didn't have a default expression */ if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && old_field->ptr != NULL && - orig_field != NULL) + orig_field != NULL && + !default_value) { bool default_now= false; if (real_type_with_now_as_default(sql_type)) @@ -10538,7 +10570,11 @@ Column_definition::Column_definition(THD *thd, Field *old_field, StringBuffer<MAX_FIELD_WIDTH> tmp(charset); String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv)); char *pos= (char*) thd->strmake(res->ptr(), res->length()); - def= new (thd->mem_root) Item_string(thd, pos, res->length(), charset); + default_value= new (thd->mem_root) Virtual_column_info(); + default_value->expr_str.str= pos; + default_value->expr_str.length= res->length(); + default_value->expr_item= + new (thd->mem_root) Item_string(thd, pos, res->length(), charset); } } } @@ -10590,6 +10626,20 @@ Create_field *Create_field::clone(MEM_ROOT *mem_root) const return res; } +/** + Return true if default is an expression that must be saved explicitely + + This is: + - Not basic constants + - If field is a BLOB (Which doesn't support normal DEFAULT) +*/ + +bool Column_definition::has_default_expression() +{ + return (unlikely(default_value) && + (!default_value->expr_item->basic_const_item() || + (flags & BLOB_FLAG))); +} /** maximum possible display length for blob. diff --git a/sql/field.h b/sql/field.h index 27b87dd0472..a7e2af197f3 100644 --- a/sql/field.h +++ b/sql/field.h @@ -394,7 +394,8 @@ enum Derivation #define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII /* The length of the header part for each virtual column in the .frm file */ -#define FRM_VCOL_HEADER_SIZE(b) (3 + MY_TEST(b)) +#define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b)) +#define FRM_VCOL_NEW_HEADER_SIZE 7 class Count_distinct_field; @@ -569,18 +570,20 @@ private: public: /* Flag indicating that the field is physically stored in the database */ bool stored_in_db; + bool non_deterministic; /* The expression to compute the value of the virtual column */ Item *expr_item; /* Text representation of the defining expression */ LEX_STRING expr_str; + LEX_STRING name; /* Name of constraint */ Virtual_column_info() : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), - in_partitioning_expr(FALSE), stored_in_db(FALSE), + in_partitioning_expr(FALSE), stored_in_db(FALSE), non_deterministic(FALSE), expr_item(NULL) { - expr_str.str= NULL; - expr_str.length= 0; + expr_str.str= name.str= NULL; + name.length= 0; }; ~Virtual_column_info() {} enum_field_types get_real_type() @@ -735,11 +738,12 @@ public: Column_statistics_collected *collected_stats; /* - This is additional data provided for any computed(virtual) field. - In particular it includes a pointer to the item by which this field + This is additional data provided for any computed(virtual) field, + default function or check constraint. + In particular it includes a pointer to the item by which this field can be computed from other fields. */ - Virtual_column_info *vcol_info; + Virtual_column_info *vcol_info, *check_constraint, *default_value; Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -879,8 +883,15 @@ public: my_ptrdiff_t l_offset= (my_ptrdiff_t) (record - table->record[0]); return ptr + l_offset; } + void set_default_expression(); virtual void set_default() { + if (default_value) + { + set_default_expression(); + return; + } + /* Copy constant value stored in s->default_values */ my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values - table->record[0]); memcpy(ptr, ptr + l_offset, pack_length()); @@ -891,14 +902,14 @@ public: bool has_insert_default_function() const { - return unireg_check == TIMESTAMP_DN_FIELD || - unireg_check == TIMESTAMP_DNUN_FIELD; + return (unireg_check == TIMESTAMP_DN_FIELD || + unireg_check == TIMESTAMP_DNUN_FIELD); } bool has_update_default_function() const { - return unireg_check == TIMESTAMP_UN_FIELD || - unireg_check == TIMESTAMP_DNUN_FIELD; + return (unireg_check == TIMESTAMP_UN_FIELD || + unireg_check == TIMESTAMP_DNUN_FIELD); } /* @@ -907,9 +918,15 @@ public: */ void set_has_explicit_value() { - flags|= HAS_EXPLICIT_VALUE; + if (table->has_value_set) /* If we have default functions */ + bitmap_set_bit(table->has_value_set, field_index); + } + bool has_explicit_value() + { + /* This function is only called when we have default functions */ + DBUG_ASSERT(table->has_value_set); + return bitmap_is_set(table->has_value_set, field_index); } - virtual void set_explicit_default(Item *value); /** @@ -3690,7 +3707,7 @@ class Column_definition: public Sql_alloc public: const char *field_name; LEX_STRING comment; // Comment for field - Item *def, *on_update; // Default value + Item *on_update; // ON UPDATE NOW() enum enum_field_types sql_type; /* At various stages in execution this can be length of field in bytes or @@ -3713,20 +3730,23 @@ public: uint 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; + Virtual_column_info + *vcol_info, // Virtual field + *default_value, // Default value + *check_constraint; // Check constraint Column_definition(): comment(null_lex_str), - def(0), on_update(0), sql_type(MYSQL_TYPE_NULL), - flags(0), pack_length(0), key_length(0), interval(0), - srid(0), geom_type(Field::GEOM_GEOMETRY), + on_update(0), sql_type(MYSQL_TYPE_NULL), + flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), + interval(0), srid(0), geom_type(Field::GEOM_GEOMETRY), option_list(NULL), - vcol_info(0) + vcol_info(0), default_value(0), check_constraint(0) { interval_list.empty(); } @@ -3750,11 +3770,6 @@ public: ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); } - uint virtual_col_expr_maxlen() - { - return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL); - } - bool has_default_function() const { return (unireg_check == Field::TIMESTAMP_DN_FIELD || @@ -3779,6 +3794,8 @@ public: return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0, field_name_arg); } + /* Return true if default is an expression that must be saved explicitely */ + bool has_default_expression(); }; @@ -3875,6 +3892,8 @@ uint32 calc_pack_length(enum_field_types type,uint32 length); int set_field_to_null(Field *field); int set_field_to_null_with_conversions(Field *field, bool no_conversions); int convert_null_to_field_value_or_error(Field *field); +bool check_expression(Virtual_column_info *vcol, const char *type, + const char *name, bool must_be_deterministic); /* The following are for the interface with the .frm file diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 173a5f709c1..f5ad7ca8123 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -8459,6 +8459,17 @@ uint ha_partition::min_record_length(uint options) const return max; } +/* + Register that we want to read partition columns. This is needed to ensure + that virtual columns are properly updated before we access them. +*/ + +void ha_partition::register_columns_for_write() +{ + if (m_part_info->part_expr) + m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, + (uchar *) 0); +} /**************************************************************************** MODULE compare records diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 3ea8d4a855d..2d7e26ce724 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -356,6 +356,7 @@ public: virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share); virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes); + virtual void register_columns_for_write(); private: int copy_partitions(ulonglong * const copied, ulonglong * const deleted); void cleanup_new_partition(uint part_count); diff --git a/sql/handler.cc b/sql/handler.cc index de8a29f9c1e..d3ad6a085d2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3710,7 +3710,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt) } } } - if (table->s->frm_version != FRM_VER_TRUE_VARCHAR) + if (table->s->frm_version < FRM_VER_TRUE_VARCHAR) return HA_ADMIN_NEEDS_ALTER; if ((error= check_collation_compatibility())) diff --git a/sql/handler.h b/sql/handler.h index 957de82fdc3..ef8feacc8d3 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,6 +42,7 @@ #include <mysql/psi/mysql_table.h> class Alter_info; +class Virtual_column_info; // the following is for checking tables @@ -1632,6 +1633,7 @@ struct Schema_specification_st - LIKE another_table_name ... // Copy structure from another table - [AS] SELECT ... // Copy structure from a subquery */ + struct Table_scope_and_contents_source_st { CHARSET_INFO *table_charset; @@ -1647,6 +1649,8 @@ struct Table_scope_and_contents_source_st ulong avg_row_length; ulong used_fields; ulong key_block_size; + ulong expression_lengths; + ulong field_check_constraints; /* number of pages to sample during stats estimation, if used, otherwise 0. @@ -1679,6 +1683,7 @@ struct Table_scope_and_contents_source_st ha_table_option_struct *option_struct; ///< structure with parsed table options ha_field_option_struct **fields_option_struct; ///< array of field option structures ha_index_option_struct **indexes_option_struct; ///< array of index option structures + List<Virtual_column_info> *constraint_list; /* The following is used to remember the old state for CREATE OR REPLACE */ TABLE *table; @@ -1823,7 +1828,7 @@ public: attribute has really changed we might choose to set flag pessimistically, for example, relying on parser output only. */ - typedef ulong HA_ALTER_FLAGS; + typedef ulonglong HA_ALTER_FLAGS; // Add non-unique, non-primary index static const HA_ALTER_FLAGS ADD_INDEX = 1L << 0; @@ -1932,6 +1937,10 @@ public: // ALTER TABLE for a partitioned table static const HA_ALTER_FLAGS ALTER_PARTITIONED = 1L << 31; + static const HA_ALTER_FLAGS ALTER_ADD_CONSTRAINT = 1LL << 32; + + static const HA_ALTER_FLAGS ALTER_DROP_CONSTRAINT = 1LL << 33; + /** Create options (like MAX_ROWS) for the new version of table. @@ -2856,6 +2865,7 @@ public: size_t pack_frm_len); int ha_drop_partitions(const char *path); int ha_rename_partitions(const char *path); + virtual void register_columns_for_write() {} void adjust_next_insert_id_after_explicit_value(ulonglong nr); int update_auto_increment(); diff --git a/sql/item.cc b/sql/item.cc index 05d4cc86f7a..3dba0c42a85 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -932,6 +932,48 @@ bool Item_field::register_field_in_write_map(uchar *arg) return 0; } +/** + Check that we are not refering to any not yet initialized fields + + Fields are initialized in this order: + - All fields that have default value as a constant are initialized first. + - Then all fields that has a default expression, in field_index order. + - Last all virtual fields, in field_index order. + + This means: + - For default fields we can't access the same field or a field after + itself that doesn't have a non-constant default value. + - A virtual fields can't access itself or a virtual field after itself. + + This is used by fix_vcol_expr() when a table is opened + + We don't have to check fields that are marked as NO_DEFAULT_VALUE + as the upper level will ensure that all these will be given a value. +*/ + +bool Item_field::check_field_expression_processor(uchar *arg) +{ + if (field->flags & NO_DEFAULT_VALUE_FLAG) + return 0; + if ((field->default_value || field->has_insert_default_function() || + field->vcol_info)) + { + Field *org_field= (Field*) arg; + if (field == org_field || + (!org_field->vcol_info && field->vcol_info) || + (((field->vcol_info && org_field->vcol_info) || + (!field->vcol_info && !org_field->vcol_info)) && + field->field_index >= org_field->field_index)) + { + my_error(ER_EXPRESSION_REFERS_TO_UNINIT_FIELD, + MYF(0), + org_field->field_name, field->field_name); + return 1; + } + } + return 0; +} + bool Item::check_cols(uint c) { @@ -1345,6 +1387,30 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) return res; } +#ifndef DBUG_OFF +static inline +void mark_unsupported_func(const char *where, const char *processor_name) +{ + char buff[64]; + sprintf(buff, "%s::%s", where ? where: "", processor_name); + DBUG_ENTER(buff); + sprintf(buff, "%s returns TRUE: unsupported function", processor_name); + DBUG_PRINT("info", ("%s", buff)); + DBUG_VOID_RETURN; +} +#else +#define mark_unsupported_func(X,Y) {} +#endif + +bool mark_unsupported_function(const char *where, uchar *store, uint result) +{ + Item::vcol_func_processor_result *res= + (Item::vcol_func_processor_result*) store; + mark_unsupported_func(where, "check_vcol_func_processor"); + res->errors|= result; /* Store type of expression */ + res->name= where ? where : ""; + return false; +} /***************************************************************************** Item_sp_variable methods @@ -8135,7 +8201,8 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) } field_arg= (Item_field *)real_arg; - if (field_arg->field->flags & NO_DEFAULT_VALUE_FLAG) + if ((field_arg->field->flags & NO_DEFAULT_VALUE_FLAG) || + field_arg->field->default_value) { my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name); goto error; diff --git a/sql/item.h b/sql/item.h index 22e44719706..1b0a7a26d56 100644 --- a/sql/item.h +++ b/sql/item.h @@ -32,26 +32,12 @@ C_MODE_START #include <ma_dyncol.h> C_MODE_END -#ifndef DBUG_OFF -static 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", ("%s", buff)); - DBUG_RETURN(TRUE); -} -#else -#define trace_unsupported_func(X,Y) TRUE -#endif - -static inline -bool trace_unsupported_by_check_vcol_func_processor(const char *where) -{ - return trace_unsupported_func(where, "check_vcol_func_processor"); -} +/* Bits for type of vcol expression */ +#define VCOL_DETERMINISTIC 0 /* Normal (no bit set) */ +#define VCOL_NON_DETERMINISTIC 1 +#define VCOL_TIME_FUNC 2 +#define VCOL_IMPOSSIBLE 4 +#define VCOL_UNKNOWN 8 /* UDF used; Need fix_fields() to know */ class Protocol; struct TABLE_LIST; @@ -74,6 +60,8 @@ char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg) return (tmp > UINT_MAX32) ? (uint32) UINT_MAX32 : (uint32) tmp; } +bool mark_unsupported_function(const char *where, uchar *store, uint result); + /* Bits for the split_sum_func() function */ #define SPLIT_SUM_SKIP_REGISTERED 1 /* Skip registered funcs */ @@ -1606,11 +1594,18 @@ public: @retval TRUE otherwise */ + struct vcol_func_processor_result + { + uint errors; /* Bits of possible errors */ + const char *name; /* Not supported function */ + }; virtual bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(full_name()); + return mark_unsupported_function(full_name(), arg, VCOL_IMPOSSIBLE); } + virtual bool check_field_expression_processor(uchar *arg) { return FALSE; } + /* arg points to REPLACE_EQUAL_FIELD_ARG object */ virtual Item *replace_equal_field(THD *thd, uchar *arg) { return this; } /* @@ -1987,6 +1982,7 @@ public: Item_basic_constant(THD *thd): Item_basic_value(thd), used_table_map(0) {}; void set_used_tables(table_map map) { used_table_map= map; } table_map used_tables() const { return used_table_map; } + bool check_vcol_func_processor(uchar *arg) { return FALSE;} /* to prevent drop fixed flag (no need parent cleanup call) */ void cleanup() { @@ -2255,7 +2251,7 @@ public: } bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("name_const"); + return mark_unsupported_function("name_const", arg, VCOL_IMPOSSIBLE); } }; @@ -2265,7 +2261,6 @@ public: Item_num(THD *thd): Item_basic_constant(thd) { collation.set_numeric(); } Item *safe_charset_converter(THD *thd, 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)) @@ -2524,6 +2519,7 @@ public: 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_field_expression_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg); bool update_table_bitmaps_processor(uchar *arg); bool switch_to_nullable_fields_processor(uchar *arg); @@ -2630,7 +2626,6 @@ public: Item *safe_charset_converter(THD *thd, 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 @@ -2646,7 +2641,7 @@ 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(full_name()); + return mark_unsupported_function(full_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -2791,7 +2786,7 @@ public: { return this; } bool append_for_log(THD *thd, String *str); - + bool check_vcol_func_processor(uchar *int_arg) {return FALSE;} private: virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it); @@ -2840,8 +2835,6 @@ public: { return (uint) (max_length - MY_TEST(value < 0)); } bool eq(const Item *item, bool binary_cmp) const { return int_eq(value, item); } - bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} - bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -2904,8 +2897,6 @@ public: uint decimal_precision() const { return decimal_value.precision(); } 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;} }; @@ -3087,7 +3078,6 @@ 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 @@ -3221,7 +3211,7 @@ 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); + return mark_unsupported_function(func_name, arg, VCOL_IMPOSSIBLE); } }; @@ -3236,7 +3226,7 @@ public: {} bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("safe_string"); + return mark_unsupported_function("safe_string", arg, VCOL_IMPOSSIBLE); } }; @@ -3324,7 +3314,6 @@ public: return const_charset_converter(thd, tocs, true); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool basic_const_item() const { return 1; } bool eq(const Item *item, bool binary_cmp) const { @@ -3453,7 +3442,6 @@ public: Item_result cmp_type() const { return TIME_RESULT; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool is_null() { return is_null_from_temporal(); } @@ -4061,7 +4049,7 @@ public: } bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("ref"); + return mark_unsupported_function("ref", arg, VCOL_IMPOSSIBLE); } bool basic_const_item() const { return ref && (*ref)->basic_const_item(); } bool is_outer_field() const @@ -4279,7 +4267,7 @@ public: { return orig_item->is_expensive_processor(arg); } bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("cache"); + return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE); } }; @@ -4660,7 +4648,7 @@ public: bool is_null() { return null_value; } bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("copy"); + return mark_unsupported_function("copy", arg, VCOL_IMPOSSIBLE); } /* @@ -4951,9 +4939,9 @@ public: (this->*processor)(args); } bool check_partition_func_processor(uchar *int_arg) {return TRUE;} - bool check_vcol_func_processor(uchar *arg_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("values"); + return mark_unsupported_function("values", arg, VCOL_IMPOSSIBLE); } }; @@ -5042,7 +5030,7 @@ private: public: bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("trigger"); + return mark_unsupported_function("trigger", arg, VCOL_IMPOSSIBLE); } }; @@ -5129,7 +5117,7 @@ public: } bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("cache"); + return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE); } /** Check if saved item has a non-NULL value. diff --git a/sql/item_func.h b/sql/item_func.h index e00c17e707c..fa9789fe34d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -64,7 +64,7 @@ public: SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING, SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, SP_RELATE_FUNC, NOT_FUNC, NOT_ALL_FUNC, - NOW_FUNC, TRIG_COND_FUNC, + NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC, NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC }; @@ -612,7 +612,10 @@ 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;} + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_NON_DETERMINISTIC); + } }; @@ -705,7 +708,7 @@ public: Item_func_additive_op(THD *thd, Item *a, Item *b): Item_num_op(thd, 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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -744,7 +747,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -777,7 +780,7 @@ public: } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -792,7 +795,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -808,7 +811,7 @@ public: void fix_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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -822,7 +825,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; // A class to handle logarithmic and trigonometric functions @@ -985,7 +988,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -998,7 +1001,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; /* This handles round and truncate */ @@ -1031,9 +1034,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_NON_DETERMINISTIC); } private: void seed_random (Item * val); @@ -1326,9 +1329,9 @@ public: unsigned_flag=1; } bool fix_fields(THD *thd, Item **ref); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1343,9 +1346,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1365,9 +1368,9 @@ public: } bool is_expensive() { return 1; } longlong val_int(); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1460,6 +1463,10 @@ public: table_map not_null_tables() const { return 0; } bool is_expensive() { return 1; } virtual void print(String *str, enum_query_type query_type); + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_UNKNOWN); + } }; @@ -1631,9 +1638,9 @@ class Item_func_get_lock :public Item_int_func } bool const_item() const { return 0; } bool is_expensive() { return 1; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1651,9 +1658,9 @@ public: } bool const_item() const { return 0; } bool is_expensive() { return 1; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1671,9 +1678,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1687,9 +1694,9 @@ public: longlong val_int(); const char *func_name() const { return "master_gtid_wait"; } void fix_length_and_dec() { max_length=10+1+10+1+20+1; maybe_null=0;} - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1715,7 +1722,10 @@ public: Item_func_user_var(THD *thd, Item_func_user_var *item) :Item_hybrid_func(thd, item), m_var_entry(item->m_var_entry), name(item->name) { } - bool check_vcol_func_processor(uchar *int_arg) { return true; } + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function("user_var", arg, VCOL_IMPOSSIBLE); + } }; @@ -1922,7 +1932,10 @@ public: bool eq(const Item *item, bool binary_cmp) const; void cleanup(); - bool check_vcol_func_processor(uchar *int_arg) { return TRUE;} + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_NON_DETERMINISTIC); + } }; @@ -1968,10 +1981,9 @@ public: bool fix_index(); void init_search(THD *thd, bool no_order); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - /* TODO: consider adding in support for the MATCH-based virtual columns */ - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } private: /** @@ -2027,9 +2039,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -2041,9 +2053,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -2091,10 +2103,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -2218,9 +2229,9 @@ public: return sp_result_field; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } bool limit_index_condition_pushdown_processor(uchar *opt_arg) { @@ -2236,9 +2247,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -2253,9 +2264,10 @@ public: longlong val_int(); void fix_length_and_dec() { max_length= 21; unsigned_flag=1; } - bool check_vcol_func_processor(uchar *int_arg) + table_map used_tables() const { return RAND_TABLE_BIT; } + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_NON_DETERMINISTIC); } }; diff --git a/sql/item_row.h b/sql/item_row.h index ddb6f0835f2..f632c786a30 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -119,7 +119,7 @@ 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; } + bool check_vcol_func_processor(uchar *arg) {return FALSE; } }; #endif /* ITEM_ROW_INCLUDED */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 7c1c5f7da7d..497a1b9a80b 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2363,14 +2363,21 @@ String *Item_func_database::val_str(String *str) BUG#28086) binlog_format=MIXED, but is incorrectly replicated to '' if binlog_format=STATEMENT. */ -bool Item_func_user::init(const char *user, const char *host) + +bool Item_func_user::init(THD *thd, const char *user, const char *host) { DBUG_ASSERT(fixed == 1); + /* Check if we have already calculated the value for this thread */ + if (thd->query_id == last_query_id) + return FALSE; + last_query_id= thd->query_id; + null_value= 0; + // For system threads (e.g. replication SQL thread) user may be empty if (user) { - CHARSET_INFO *cs= str_value.charset(); + CHARSET_INFO *cs= system_charset_info; size_t res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen; if (str_value.alloc((uint) res_length)) @@ -2379,38 +2386,41 @@ bool Item_func_user::init(const char *user, const char *host) return TRUE; } + str_value.set_charset(cs); res_length=cs->cset->snprintf(cs, (char*)str_value.ptr(), (uint) res_length, "%s@%s", user, host); str_value.length((uint) res_length); str_value.mark_as_const(); } + else + str_value.set("", 0, system_charset_info); return FALSE; } - -bool Item_func_user::fix_fields(THD *thd, Item **ref) +String *Item_func_user::val_str(String *str) { - return (Item_func_sysconst::fix_fields(thd, ref) || - init(thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip)); + THD *thd= current_thd; + init(thd, thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip); + return null_value ? 0 : &str_value; } - -bool Item_func_current_user::fix_fields(THD *thd, Item **ref) +String *Item_func_current_user::val_str(String *str) { - if (Item_func_sysconst::fix_fields(thd, ref)) - return TRUE; - - Security_context *ctx= context->security_ctx - ? context->security_ctx : thd->security_ctx; - return init(ctx->priv_user, ctx->priv_host); + THD *thd= current_thd; + Security_context *ctx= (context->security_ctx ? + context->security_ctx : thd->security_ctx); + init(thd, ctx->priv_user, ctx->priv_host); + return null_value ? 0 : &str_value; } + bool Item_func_current_role::fix_fields(THD *thd, Item **ref) { - if (Item_func_sysconst::fix_fields(thd, ref)) - return 1; + return Item_func_sysconst::fix_fields(thd,ref) || init(thd); +} +bool Item_func_current_role::init(THD *thd) +{ Security_context *ctx= context->security_ctx ? context->security_ctx : thd->security_ctx; @@ -2420,13 +2430,21 @@ bool Item_func_current_role::fix_fields(THD *thd, Item **ref) system_charset_info)) return 1; - str_value.mark_as_const(); return 0; } - null_value= maybe_null= 1; + null_value= 1; return 0; } +String *Item_func_current_role::val_str(String *) +{ + return (null_value ? 0 : &str_value); +} + +int Item_func_current_role::save_in_field(Field *field, bool no_conversions) +{ + return save_str_value_in_field(field, &str_value); +} void Item_func_soundex::fix_length_and_dec() { diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 72cdf06adde..3675707db41 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -517,9 +517,9 @@ 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) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return FALSE; } }; @@ -572,10 +572,10 @@ public: call */ virtual const char *fully_qualified_func_name() const = 0; - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor( - fully_qualified_func_name()); + return mark_unsupported_function(fully_qualified_func_name(), arg, + VCOL_NON_DETERMINISTIC); } }; @@ -598,19 +598,14 @@ public: class Item_func_user :public Item_func_sysconst { protected: - bool init (const char *user, const char *host); + query_id_t last_query_id; + bool init(THD *thd, const char *user, const char *host); public: - Item_func_user(THD *thd): Item_func_sysconst(thd) - { - str_value.set("", 0, system_charset_info); - } - String *val_str(String *) - { - DBUG_ASSERT(fixed == 1); - return (null_value ? 0 : &str_value); - } - bool fix_fields(THD *thd, Item **ref); + Item_func_user(THD *thd): Item_func_sysconst(thd), last_query_id(0) + {} + + String *val_str(String *); void fix_length_and_dec() { max_length= (username_char_length + @@ -618,10 +613,6 @@ public: } const char *func_name() const { return "user"; } const char *fully_qualified_func_name() const { return "user()"; } - int save_in_field(Field *field, bool no_conversions) - { - return save_str_value_in_field(field, &str_value); - } }; @@ -632,9 +623,15 @@ class Item_func_current_user :public Item_func_user public: Item_func_current_user(THD *thd, Name_resolution_context *context_arg): Item_func_user(thd), context(context_arg) {} - bool fix_fields(THD *thd, Item **ref); + String *val_str(String *); const char *func_name() const { return "current_user"; } const char *fully_qualified_func_name() const { return "current_user()"; } + /* This is because of the stored Name_resolution_context */ + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(fully_qualified_func_name(), arg, + VCOL_IMPOSSIBLE); + } }; @@ -647,15 +644,20 @@ public: Item_func_sysconst(thd), context(context_arg) {} bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec() - { max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; } - int save_in_field(Field *field, bool no_conversions) - { return save_str_value_in_field(field, &str_value); } + { + max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; + maybe_null=1; + } + bool init(THD *thd); + int save_in_field(Field *field, bool no_conversions); const char *func_name() const { return "current_role"; } const char *fully_qualified_func_name() const { return "current_role()"; } - String *val_str(String *) + String *val_str(String *); + /* This is because of the stored Name_resolution_context */ + bool check_vcol_func_processor(uchar *arg) { - DBUG_ASSERT(fixed == 1); - return (null_value ? 0 : &str_value); + return mark_unsupported_function(fully_qualified_func_name(), arg, + VCOL_IMPOSSIBLE); } }; @@ -916,9 +918,9 @@ public: maybe_null=1; max_length=MAX_BLOB_WIDTH; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -1183,9 +1185,9 @@ public: } const char *func_name() const{ return "uuid"; } String *val_str(String *); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_NON_DETERMINISTIC); } }; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 58b5a948048..e5adfaa6bee 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -220,9 +220,9 @@ public: bool eliminate_subselect_processor(uchar *arg); bool set_fake_select_as_master_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("subselect"); + return mark_unsupported_function("subselect", arg, VCOL_IMPOSSIBLE); } /** Callback to test if an IN predicate is expensive. diff --git a/sql/item_sum.h b/sql/item_sum.h index e766e69a1c5..8fcc4e203bc 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -547,9 +547,9 @@ public: virtual void remove() { DBUG_ASSERT(0); } virtual void cleanup(); - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } virtual void setup_window_func(THD *thd, Window_spec *window_spec) {} @@ -1159,6 +1159,10 @@ public: table_map used_tables() const { return (table_map) 1L; } void set_result_field(Field *) { DBUG_ASSERT(0); } void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); } + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(name, arg, VCOL_IMPOSSIBLE); + } }; @@ -1172,10 +1176,6 @@ public: { } enum Type type() const { return FIELD_AVG_ITEM; } bool is_null() { update_null_value(); return null_value; } - bool check_vcol_func_processor(uchar *int_arg) - { - return trace_unsupported_by_check_vcol_func_processor("avg_field"); - } }; @@ -1230,10 +1230,6 @@ public: bool is_null() { update_null_value(); return null_value; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } enum Item_result result_type () const { return REAL_RESULT; } - bool check_vcol_func_processor(uchar *int_arg) - { - return trace_unsupported_by_check_vcol_func_processor("var_field"); - } }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 16edb35c392..69eb3d63dd8 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1465,7 +1465,7 @@ void Item_temporal_func::fix_length_and_dec() We set maybe_null to 1 as default as any bad argument with date or time can get us to return NULL. */ - maybe_null= 1; + maybe_null= (arg_count > 0); if (decimals) { if (decimals == NOT_FIXED_DEC) @@ -1581,24 +1581,12 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) } -void Item_func_curdate::fix_length_and_dec() -{ - store_now_in_TIME(<ime); - - /* We don't need to set second_part and neg because they already 0 */ - ltime.hour= ltime.minute= ltime.second= 0; - ltime.time_type= MYSQL_TIMESTAMP_DATE; - Item_datefunc::fix_length_and_dec(); - maybe_null= false; -} - /** Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole CURDATE function. */ -void Item_func_curdate_local::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_curdate_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); thd->time_zone_used= 1; } @@ -1608,9 +1596,8 @@ void Item_func_curdate_local::store_now_in_TIME(MYSQL_TIME *now_time) Converts current time in my_time_t to MYSQL_TIME represenatation for UTC time zone. Defines time zone (UTC) used for whole UTC_DATE function. */ -void Item_func_curdate_utc::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_curdate_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); /* We are not flagging this query as using time zone, since it uses fixed @@ -1622,6 +1609,17 @@ void Item_func_curdate_utc::store_now_in_TIME(MYSQL_TIME *now_time) bool Item_func_curdate::get_date(MYSQL_TIME *res, ulonglong fuzzy_date __attribute__((unused))) { + THD *thd= current_thd; + query_id_t query_id= thd->query_id; + /* Cache value for this query */ + if (last_query_id != query_id) + { + last_query_id= query_id; + store_now_in_TIME(thd, <ime); + /* We don't need to set second_part and neg because they already 0 */ + ltime.hour= ltime.minute= ltime.second= 0; + ltime.time_type= MYSQL_TIMESTAMP_DATE; + } *res=ltime; return 0; } @@ -1641,6 +1639,14 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items) bool Item_func_curtime::get_date(MYSQL_TIME *res, ulonglong fuzzy_date __attribute__((unused))) { + THD *thd= current_thd; + query_id_t query_id= thd->query_id; + /* Cache value for this query */ + if (last_query_id != query_id) + { + last_query_id= query_id; + store_now_in_TIME(thd, <ime); + } *res= ltime; return 0; } @@ -1661,9 +1667,8 @@ static void set_sec_part(ulong sec_part, MYSQL_TIME *ltime, Item *item) Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole CURTIME function. */ -void Item_func_curtime_local::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_curtime_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); now_time->year= now_time->month= now_time->day= 0; now_time->time_type= MYSQL_TIMESTAMP_TIME; @@ -1676,9 +1681,8 @@ void Item_func_curtime_local::store_now_in_TIME(MYSQL_TIME *now_time) Converts current time in my_time_t to MYSQL_TIME represenatation for UTC time zone. Defines time zone (UTC) used for whole UTC_TIME function. */ -void Item_func_curtime_utc::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_curtime_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); now_time->year= now_time->month= now_time->day= 0; now_time->time_type= MYSQL_TIMESTAMP_TIME; @@ -1704,9 +1708,8 @@ bool Item_func_now::fix_fields(THD *thd, Item **items) Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole NOW function. */ -void Item_func_now_local::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_now_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); set_sec_part(thd->query_start_sec_part(), now_time, this); thd->time_zone_used= 1; @@ -1717,9 +1720,8 @@ void Item_func_now_local::store_now_in_TIME(MYSQL_TIME *now_time) Converts current time in my_time_t to MYSQL_TIME represenatation for UTC time zone. Defines time zone (UTC) used for whole UTC_TIMESTAMP function. */ -void Item_func_now_utc::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_now_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); set_sec_part(thd->query_start_sec_part(), now_time, this); /* @@ -1732,6 +1734,14 @@ void Item_func_now_utc::store_now_in_TIME(MYSQL_TIME *now_time) bool Item_func_now::get_date(MYSQL_TIME *res, ulonglong fuzzy_date __attribute__((unused))) { + THD *thd= current_thd; + query_id_t query_id= thd->query_id; + /* Cache value for this query */ + if (last_query_id != query_id) + { + last_query_id= query_id; + store_now_in_TIME(thd, <ime); + } *res= ltime; return 0; } @@ -1741,9 +1751,8 @@ bool Item_func_now::get_date(MYSQL_TIME *res, Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole SYSDATE function. */ -void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time) +void Item_func_sysdate_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) { - THD *thd= current_thd; my_hrtime_t now= my_hrtime(); thd->variables.time_zone->gmt_sec_to_TIME(now_time, hrtime_to_my_time(now)); set_sec_part(hrtime_sec_part(now), now_time, this); @@ -1754,7 +1763,7 @@ void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time) bool Item_func_sysdate_local::get_date(MYSQL_TIME *res, ulonglong fuzzy_date __attribute__((unused))) { - store_now_in_TIME(res); + store_now_in_TIME(current_thd, res); return 0; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 175f3b06c1a..7bd3cf58a7a 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -85,7 +85,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -139,7 +139,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -173,7 +173,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -190,11 +190,14 @@ public: String *val_str(String *str); 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;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); } + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); + } }; @@ -211,7 +214,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -232,7 +235,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_time_args(); @@ -253,7 +256,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_time_args(); @@ -274,7 +277,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -295,7 +298,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_time_args(); @@ -330,7 +333,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -353,7 +356,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -388,7 +391,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_date_args(); @@ -406,7 +409,10 @@ class Item_func_dayname :public Item_func_weekday enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } 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;} + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); + } }; @@ -454,13 +460,11 @@ public: { return !has_timestamp_args(); } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *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()); + if (arg_count) + return FALSE; + return mark_unsupported_function(func_name(), arg, VCOL_TIME_FUNC); } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); @@ -476,7 +480,7 @@ public: Item_func_seconds_hybrid(thd, item) {} const char *func_name() const { return "time_to_sec"; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_time_args(); @@ -596,25 +600,21 @@ public: class Item_func_curtime :public Item_timefunc { MYSQL_TIME ltime; + query_id_t last_query_id; public: - Item_func_curtime(THD *thd, uint dec): Item_timefunc(thd) { decimals= dec; } + Item_func_curtime(THD *thd, uint dec): Item_timefunc(thd), last_query_id(0) + { decimals= dec; } bool fix_fields(THD *, Item **); - void fix_length_and_dec() - { - store_now_in_TIME(<ime); - Item_timefunc::fix_length_and_dec(); - maybe_null= false; - } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); /* Abstract method that defines which time zone is used for conversion. Converts time current time in my_time_t representation to broken-down MYSQL_TIME representation using UTC-SYSTEM or per-thread time zone. */ - virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; - bool check_vcol_func_processor(uchar *int_arg) + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_TIME_FUNC); } }; @@ -624,7 +624,7 @@ class Item_func_curtime_local :public Item_func_curtime public: Item_func_curtime_local(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "curtime"; } - virtual void store_now_in_TIME(MYSQL_TIME *now_time); + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); }; @@ -633,7 +633,7 @@ class Item_func_curtime_utc :public Item_func_curtime public: Item_func_curtime_utc(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "utc_time"; } - virtual void store_now_in_TIME(MYSQL_TIME *now_time); + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); }; @@ -641,15 +641,16 @@ public: class Item_func_curdate :public Item_datefunc { + query_id_t last_query_id; MYSQL_TIME ltime; public: - Item_func_curdate(THD *thd): Item_datefunc(thd) {} - void fix_length_and_dec(); + Item_func_curdate(THD *thd): Item_datefunc(thd), last_query_id(0) {} bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; - bool check_vcol_func_processor(uchar *int_arg) + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, + VCOL_TIME_FUNC); } }; @@ -659,7 +660,7 @@ class Item_func_curdate_local :public Item_func_curdate public: Item_func_curdate_local(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "curdate"; } - void store_now_in_TIME(MYSQL_TIME *now_time); + void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); }; @@ -668,30 +669,29 @@ class Item_func_curdate_utc :public Item_func_curdate public: Item_func_curdate_utc(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "utc_date"; } - void store_now_in_TIME(MYSQL_TIME *now_time); + void store_now_in_TIME(THD* thd, MYSQL_TIME *now_time); }; /* Abstract CURRENT_TIMESTAMP function. See also Item_func_curtime */ - class Item_func_now :public Item_datetimefunc { MYSQL_TIME ltime; + query_id_t last_query_id; public: - Item_func_now(THD *thd, uint dec): Item_datetimefunc(thd) { decimals= dec; } + Item_func_now(THD *thd, uint dec): Item_datetimefunc(thd), last_query_id(0) + { decimals= dec; } bool fix_fields(THD *, Item **); - void fix_length_and_dec() - { - store_now_in_TIME(<ime); - Item_temporal_func::fix_length_and_dec(); - maybe_null= false; - } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; - bool check_vcol_func_processor(uchar *int_arg) + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + /* + NOW is safe for replication as slaves will run with same time as + master + */ + return mark_unsupported_function(func_name(), arg, VCOL_TIME_FUNC); } }; @@ -701,7 +701,7 @@ class Item_func_now_local :public Item_func_now public: Item_func_now_local(THD *thd, uint dec): Item_func_now(thd, dec) {} const char *func_name() const { return "now"; } - virtual void store_now_in_TIME(MYSQL_TIME *now_time); + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); virtual enum Functype functype() const { return NOW_FUNC; } }; @@ -711,7 +711,14 @@ class Item_func_now_utc :public Item_func_now public: Item_func_now_utc(THD *thd, uint dec): Item_func_now(thd, dec) {} const char *func_name() const { return "utc_timestamp"; } - virtual void store_now_in_TIME(MYSQL_TIME *now_time); + virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); + virtual enum Functype functype() const { return NOW_UTC_FUNC; } + virtual bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, + VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); + } + }; @@ -725,14 +732,15 @@ public: Item_func_sysdate_local(THD *thd, uint dec): Item_func_now(thd, dec) {} bool const_item() const { return 0; } const char *func_name() const { return "sysdate"; } - void store_now_in_TIME(MYSQL_TIME *now_time); + void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - void update_used_tables() + table_map used_tables() const { return RAND_TABLE_BIT; } + bool check_vcol_func_processor(uchar *arg) { - Item_func_now::update_used_tables(); - maybe_null= 0; - used_tables_cache|= RAND_TABLE_BIT; + return mark_unsupported_function(func_name(), arg, + VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); } + virtual enum Functype functype() const { return SYSDATE_FUNC; } }; @@ -743,7 +751,7 @@ public: const char *func_name() const { return "from_days"; } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return has_date_args() || has_time_args(); @@ -766,6 +774,10 @@ public: void fix_length_and_dec(); uint format_length(const String *format); bool eq(const Item *item, bool binary_cmp) const; + bool check_vcol_func_processor(uchar *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); + } }; @@ -859,7 +871,7 @@ class Item_extract :public Item_int_func bool eq(const Item *item, bool binary_cmp) const; 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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { switch (int_type) { @@ -1039,7 +1051,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;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) { return !has_time_args(); diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index ba17d2c48c3..f5a15114c09 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -234,9 +234,9 @@ public: const_item_cache= false; } const char *func_name() const { return "nodeset"; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } }; @@ -572,9 +572,9 @@ public: Item_bool_func(thd, nodeset, cmpfunc), pxml(p) {} enum Type type() const { return XPATH_NODESET_CMP; }; const char *func_name() const { return "xpath_nodeset_to_const_comparator"; } - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor(func_name()); + return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } longlong val_int() diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 3758025fc90..ed12793742e 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -91,10 +91,6 @@ public: { return const_item_cache && (!nodeset_func || nodeset_func->const_item()); } - 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 f7a183e1862..0aaec5ffdda 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -552,7 +552,8 @@ static SYMBOL symbols[] = { { "SOME", SYM(ANY_SYM)}, { "SONAME", SYM(SONAME_SYM)}, { "SOUNDS", SYM(SOUNDS_SYM)}, - { "SOURCE", SYM(SOURCE_SYM)}, + { "SOURCE", SYM(SOURCE_SYM)}, + { "STORED", SYM(STORED_SYM)}, { "SPATIAL", SYM(SPATIAL_SYM)}, { "SPECIFIC", SYM(SPECIFIC_SYM)}, { "REF_SYSTEM_ID", SYM(REF_SYSTEM_ID_SYM)}, diff --git a/sql/log_event.cc b/sql/log_event.cc index 492c83c9e96..dfd84b99d49 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4034,6 +4034,8 @@ void Query_log_event::print_query_header(IO_CACHE* file, "@@session.unique_checks", &need_comma); print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2, "@@session.autocommit", &need_comma); + print_set_option(file, tmp, OPTION_NO_CHECK_CONSTRAINT_CHECKS, ~flags2, + "@@session.check_constraint_checks", &need_comma); my_b_printf(file,"%s\n", print_event_info->delimiter); print_event_info->flags2= flags2; } @@ -9336,9 +9338,11 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, (!tbl_arg && !cols && tid == ~0UL)); if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS) - set_flags(NO_FOREIGN_KEY_CHECKS_F); + set_flags(NO_FOREIGN_KEY_CHECKS_F); if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS) - set_flags(RELAXED_UNIQUE_CHECKS_F); + set_flags(RELAXED_UNIQUE_CHECKS_F); + if (thd_arg->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS) + set_flags(NO_CHECK_CONSTRAINT_CHECKS_F); /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, @@ -9762,6 +9766,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS; else thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS; + + if (get_flags(NO_CHECK_CONSTRAINT_CHECKS_F)) + thd->variables.option_bits|= OPTION_NO_CHECK_CONSTRAINT_CHECKS; + else + thd->variables.option_bits&= ~OPTION_NO_CHECK_CONSTRAINT_CHECKS; + /* A small test to verify that objects have consistent types */ DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS)); diff --git a/sql/log_event.h b/sql/log_event.h index bc850c22a14..0ce0e563796 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4238,7 +4238,10 @@ public: Indicates that rows in this event are complete, that is contain values for all columns of the table. */ - COMPLETE_ROWS_F = (1U << 3) + COMPLETE_ROWS_F = (1U << 3), + + /* Value of the OPTION_NO_CHECK_CONSTRAINT_CHECKS flag in thd->options */ + NO_CHECK_CONSTRAINT_CHECKS_F = (1U << 7) }; typedef uint16 flag_set; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 43a00607fe6..78b7e7fca41 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -483,11 +483,12 @@ bool partition_info::set_used_partition(List<Item> &fields, if (fields.elements || !values.elements) { - if (fill_record(thd, table, fields, values, false)) + if (fill_record(thd, table, fields, values, false, !copy_default_values)) goto err; } else { + /* All fields has a value */ if (fill_record(thd, table, table->field, values, false, false)) goto err; } diff --git a/sql/procedure.h b/sql/procedure.h index 1452f33652a..9dd4af19cb8 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -53,9 +53,9 @@ public: init_make_field(tmp_field,field_type()); } unsigned int size_of() { return sizeof(*this);} - bool check_vcol_func_processor(uchar *int_arg) + bool check_vcol_func_processor(uchar *arg) { - return trace_unsupported_by_check_vcol_func_processor("proc"); + return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE); } }; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index f8f06d613f4..c1a1c440922 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1812,8 +1812,9 @@ void rpl_group_info::cleanup_context(THD *thd, bool error) /* Cleanup for the flags that have been set at do_apply_event. */ - thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS; - thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS; + thd->variables.option_bits&= ~(OPTION_NO_FOREIGN_KEY_CHECKS | + OPTION_RELAXED_UNIQUE_CHECKS | + OPTION_NO_CHECK_CONSTRAINT_CHECKS); /* Reset state related to long_find_row notes in the error log: diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 7dfc6223763..ac1dd9758ea 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -2074,7 +2074,7 @@ ER_CANT_DROP_FIELD_OR_KEY 42000 cze "Nemohu zrušit '%-.192s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíče" dan "Kan ikke udføre DROP '%-.192s'. Undersøg om feltet/nøglen eksisterer." nla "Kan '%-.192s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat." - eng "Can't DROP '%-.192s'; check that column/key exists" + eng "Can't DROP '%-.192s'; check that constraint/column/key exists" est "Ei suuda kustutada '%-.192s'. Kontrolli kas tulp/võti eksisteerib" fre "Ne peut effacer (DROP) '%-.192s'. Vérifiez s'il existe" ger "Kann '%-.192s' nicht löschen. Existiert die Spalte oder der Schlüssel?" @@ -2092,7 +2092,7 @@ ER_CANT_DROP_FIELD_OR_KEY 42000 serbian "Ne mogu da izvršim komandu drop 'DROP' na '%-.192s'. Proverite da li ta kolona (odnosno ključ) postoji" slo "Nemôžem zrušiť (DROP) '%-.192s'. Skontrolujte, či neexistujú záznamy/kľúče" spa "No puedo ELIMINAR '%-.192s'. compuebe que el campo/clave existe" - swe "Kan inte ta bort '%-.192s'. Kontrollera att fältet/nyckel finns" + swe "Kan inte ta bort '%-.192s'. Kontrollera att begränsningen/fältet/nyckel finns" ukr "Не можу DROP '%-.192s'. Перевірте, чи цей стовбець/ключ існує" ER_INSERT_INFO cze "Záznamů: %ld Zdvojených: %ld Varování: %ld" @@ -5290,11 +5290,11 @@ ER_VIEW_NONUPD_CHECK ger "CHECK OPTION auf nicht-aktualisierbarem View '%-.192s.%-.192s'" rus "CHECK OPTION для необновляемого VIEW '%-.192s.%-.192s'" ukr "CHECK OPTION для VIEW '%-.192s.%-.192s' що не може бути оновленним" -ER_VIEW_CHECK_FAILED - eng "CHECK OPTION failed '%-.192s.%-.192s'" - ger "CHECK OPTION fehlgeschlagen: '%-.192s.%-.192s'" - rus "проверка CHECK OPTION для VIEW '%-.192s.%-.192s' провалилась" - ukr "Перевірка CHECK OPTION для VIEW '%-.192s.%-.192s' не пройшла" +ER_CONSTRAINT_FAILED + eng "CONSTRAINT '%s' failed for '%-.192s.%-.192s'" + ger "CONSTRAINT '%s' fehlgeschlagen: '%-.192s.%-.192s'" + rus "проверка CONSTRAINT '%s' для '%-.192s.%-.192s' провалилась" + ukr "Перевірка CONSTRAINT '%s' для '%-.192s.%-.192s' не пройшла" ER_PROCACCESS_DENIED_ERROR 42000 eng "%-.32s command denied to user '%s'@'%s' for routine '%-.192s'" ger "Befehl %-.32s nicht zulässig für Benutzer '%s'@'%s' in Routine '%-.192s'" @@ -6970,7 +6970,7 @@ start-error-number 1900 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'" + eng "Function or expression '%s' is not allowed for '%s' of column/constraint '%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 @@ -7203,3 +7203,11 @@ ER_WINDOW_FUNCTION_DONT_HAVE_FRAME eng "This window function may not have a window frame" ER_INVALID_NTILE_ARGUMENT eng "Argument of NTILE must be greater than 0" +ER_EXPRESSION_IS_TOO_BIG + eng "%s expression is too big for '%s'" +ER_ERROR_EVALUATING_EXPRESSION + eng "Got an error evaluating stored expression %`.192s" +ER_CALCULATING_DEFAULT_VALUE + eng "Got an error when calculating default value for '%s'" +ER_EXPRESSION_REFERS_TO_UNINIT_FIELD 01000 + eng "Expression for field %`-.64s is refering to uninitialized field %`-.64s" diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 737d3cca729..8c14b4d3733 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -25,6 +25,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) alter_list(rhs.alter_list, mem_root), key_list(rhs.key_list, mem_root), create_list(rhs.create_list, mem_root), + constraint_list(rhs.constraint_list, mem_root), flags(rhs.flags), keys_onoff(rhs.keys_onoff), partition_names(rhs.partition_names, mem_root), diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 526442e83e2..b9dc01b1e42 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -124,6 +124,8 @@ public: // Set for ADD [COLUMN] FIRST | AFTER static const uint ALTER_COLUMN_ORDER = 1L << 26; + static const uint ALTER_ADD_CONSTRAINT = 1L << 27; + static const uint ALTER_DROP_CONSTRAINT = 1L << 28; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; @@ -172,6 +174,7 @@ public: List<Key> key_list; // List of columns, used by both CREATE and ALTER TABLE. List<Create_field> create_list; + List<Virtual_column_info> constraint_list; // Type of ALTER TABLE operation. uint flags; // Enable or disable keys. @@ -200,6 +203,7 @@ public: alter_list.empty(); key_list.empty(); create_list.empty(); + constraint_list.empty(); flags= 0; keys_onoff= LEAVE_AS_IS; num_parts= 0; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e8a741931ed..fbc26846fda 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7681,11 +7681,14 @@ err_no_arena: @param fields Item_fields list to be filled @param values values to fill with @param ignore_errors TRUE if we should ignore errors + @param update TRUE if update query @details fill_record() may set table->auto_increment_field_not_null and a caller should make sure that it is reset after their last call to this function. + default functions are executed for inserts. + virtual fields are always updated @return Status @retval true An error occurred. @@ -7694,7 +7697,7 @@ err_no_arena: bool fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, - bool ignore_errors) + bool ignore_errors, bool update) { List_iterator_fast<Item> f(fields),v(values); Item *value, *fld; @@ -7760,12 +7763,16 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, DBUG_ASSERT(vcol_table == 0 || vcol_table == table); vcol_table= table; } - /* Update virtual fields*/ + + if (!update && table_arg->default_field && + table_arg->update_default_fields(0, ignore_errors)) + goto err; + /* Update virtual fields */ thd->abort_on_warning= FALSE; if (vcol_table && vcol_table->vfield && update_virtual_fields(thd, vcol_table, vcol_table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE)) + VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; @@ -7802,6 +7809,32 @@ void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *table) /** + Prepare Virtual fields and field with default expressions to use + trigger fields + + This means redirecting from table->field to + table->field_to_fill(), if needed. +*/ + +void switch_to_nullable_trigger_fields(Field **info, TABLE *table) +{ + Field **trigger_field= table->field_to_fill(); + + /* True if we have virtual fields and non_null fields and before triggers */ + if (info && trigger_field != table->field) + { + Field **field_ptr; + for (field_ptr= info; *field_ptr ; field_ptr++) + { + Field *field= (*field_ptr); + field->default_value->expr_item->walk(&Item::switch_to_nullable_fields_processor, 1, (uchar*) trigger_field); + *field_ptr= (trigger_field[field->field_index]); + } + } +} + + +/** Test NOT NULL constraint after BEFORE triggers */ static bool not_null_fields_have_null_values(TABLE *table) @@ -7862,7 +7895,8 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, bool result; Table_triggers_list *triggers= table->triggers; - result= fill_record(thd, table, fields, values, ignore_errors); + result= fill_record(thd, table, fields, values, ignore_errors, + event == TRG_EVENT_UPDATE); if (!result && triggers) result= triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE) || diff --git a/sql/sql_base.h b/sql/sql_base.h index 8750e6989e1..b76e36d92b6 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -139,6 +139,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, const char *table_name); void close_thread_tables(THD *thd); void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *); +void switch_to_nullable_trigger_fields(Field **info, TABLE *table); bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, List<Item> &values, @@ -161,7 +162,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> *sum_func_list, bool allow_sum_func); void unfix_fields(List<Item> &items); bool fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, - List<Item> &values, bool ignore_errors); + List<Item> &values, bool ignore_errors, bool update); bool fill_record(THD *thd, TABLE *table, Field **field, List<Item> &values, bool ignore_errors, bool use_value); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d016bfdae50..f216845ad10 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5675,6 +5675,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) bool has_write_table_auto_increment_not_first_in_pk= FALSE; bool has_auto_increment_write_tables_not_first= FALSE; bool found_first_not_own_table= FALSE; + bool has_write_tables_with_unsafe_statements= FALSE; /* A pointer to a previous table that was changed. @@ -5775,11 +5776,14 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (table->lock_type >= TL_WRITE_ALLOW_WRITE) { + bool trans; if (prev_write_table && prev_write_table->file->ht != table->table->file->ht) multi_write_engine= TRUE; + if (table->table->s->non_determinstic_insert) + has_write_tables_with_unsafe_statements= true; - my_bool trans= table->table->file->has_transactions(); + trans= table->table->file->has_transactions(); if (table->table->s->tmp_table) lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE : @@ -5830,6 +5834,10 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (has_write_table_auto_increment_not_first_in_pk) lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST); + + if (has_write_tables_with_unsafe_statements) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + /* A query that modifies autoinc column in sub-statement can make the master and slave inconsistent. diff --git a/sql/sql_class.h b/sql/sql_class.h index 58d720f211e..c901ae7785a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -226,6 +226,7 @@ typedef struct st_copy_info { List<Item> *update_values; /* for VIEW ... WITH CHECK OPTION */ TABLE_LIST *view; + TABLE_LIST *table_list; /* Normal table */ } COPY_INFO; @@ -256,7 +257,7 @@ public: class Alter_drop :public Sql_alloc { public: - enum drop_type {KEY, COLUMN, FOREIGN_KEY }; + enum drop_type {KEY, COLUMN, FOREIGN_KEY, CONSTRAINT_CHECK }; const char *name; enum drop_type type; bool drop_if_exists; @@ -277,9 +278,9 @@ public: class Alter_column :public Sql_alloc { public: const char *name; - Item *def; - Alter_column(const char *par_name,Item *literal) - :name(par_name), def(literal) {} + Virtual_column_info *default_value; + Alter_column(const char *par_name, Virtual_column_info *literal) + :name(par_name), default_value(literal) {} /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone @@ -4628,7 +4629,8 @@ public: Alter_info *alter_info_arg, List<Item> &select_fields,enum_duplicates duplic, bool ignore, TABLE_LIST *select_tables_arg): - select_insert(thd_arg, NULL, NULL, &select_fields, 0, 0, duplic, ignore), + select_insert(thd_arg, table_arg, NULL, &select_fields, 0, 0, duplic, + ignore), create_table(table_arg), create_info(create_info_par), select_tables(select_tables_arg), diff --git a/sql/sql_const.h b/sql/sql_const.h index 31ee4603dc9..c8e60305eab 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -101,6 +101,8 @@ #define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */ #define FRM_VER_TRUE_VARCHAR (FRM_VER+4) /* 10 */ +#define FRM_VER_EXPRESSSIONS (FRM_VER+5) /* 11 */ +#define FRM_VER_CURRENT FRM_VER_EXPRESSSIONS /*************************************************************************** Configuration parameters diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e3f26ccf377..ee6309aff51 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -282,12 +282,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name); DBUG_RETURN(-1); } - if (table->default_field) - table->mark_default_fields_for_write(); } - /* Mark virtual columns used in the insert statement */ - if (table->vfield) - table->mark_virtual_columns_for_write(TRUE); // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); @@ -359,7 +354,7 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, return -1; if (table->default_field) - table->mark_default_fields_for_write(); + table->mark_default_fields_for_write(FALSE); if (table->found_next_number_field) { @@ -801,6 +796,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, info.update_fields= &update_fields; info.update_values= &update_values; info.view= (table_list->view ? table_list : 0); + info.table_list= table_list; /* Count warnings for all inserts. @@ -901,6 +897,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, INSERT INTO t1 VALUES () */ restore_record(table,s->default_values); // Get empty record + table->reset_default_fields(); if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0, TRG_EVENT_INSERT)) { @@ -961,11 +958,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, break; } } - if (table->default_field && table->update_default_fields()) - { - error= 1; - break; - } if ((res= table_list->view_check_option(thd, (values_list.elements == 1 ? @@ -1504,18 +1496,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!table) table= table_list->table; - - if (!fields.elements && table->vfield) - { - for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) - { - if ((*vfield_ptr)->vcol_info->stored_in_db) - { - thd->lex->unit.insert_table_with_stored_vcol= table; - break; - } - } - } + if (table->s->virtual_stored_fields) + thd->lex->unit.insert_table_with_stored_vcol= table; if (!select_insert) { @@ -1702,16 +1684,17 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) DBUG_ASSERT(table->insert_values != NULL); store_record(table,insert_values); restore_record(table,record[1]); + table->reset_default_fields(); /* in INSERT ... ON DUPLICATE KEY UPDATE the set of modified fields can change per row. Thus, we have to do reset_default_fields() per row. Twice (before insert and before update). */ - table->reset_default_fields(); DBUG_ASSERT(info->update_fields->elements == info->update_values->elements); - if (fill_record_n_invoke_before_triggers(thd, table, *info->update_fields, + if (fill_record_n_invoke_before_triggers(thd, table, + *info->update_fields, *info->update_values, info->ignore, TRG_EVENT_UPDATE)) @@ -1728,20 +1711,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) */ if (different_records && table->default_field) { - bool res; - enum_sql_command cmd= thd->lex->sql_command; - thd->lex->sql_command= SQLCOM_UPDATE; - res= table->update_default_fields(); - thd->lex->sql_command= cmd; - if (res) + if (table->update_default_fields(1, info->ignore)) goto err; } - table->reset_default_fields(); /* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */ - if (info->view && - (res= info->view->view_check_option(current_thd, info->ignore)) == - VIEW_CHECK_SKIP) + res= info->table_list->view_check_option(table->in_use, info->ignore); + if (res == VIEW_CHECK_SKIP) goto ok_or_after_trg_err; if (res == VIEW_CHECK_ERROR) goto before_trg_err; @@ -2361,11 +2337,12 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) { my_ptrdiff_t adjust_ptrs; Field **field,**org_field, *found_next_number_field; - Field **UNINIT_VAR(vfield), **UNINIT_VAR(dfield_ptr); + Field **vfield= 0, **dfield_ptr= 0; TABLE *copy; TABLE_SHARE *share; uchar *bitmap; char *copy_tmp; + uint bitmaps_used; DBUG_ENTER("Delayed_insert::get_local_table"); /* First request insert thread to get a lock */ @@ -2419,13 +2396,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) copy_tmp= (char*) client_thd->alloc(sizeof(*copy)+ (share->fields+1)*sizeof(Field**)+ share->reclength + - share->column_bitmap_size*3); + share->column_bitmap_size*4); if (!copy_tmp) goto error; - if (share->vfields) + if (share->virtual_fields) { - vfield= (Field **) client_thd->alloc((share->vfields+1)*sizeof(Field*)); + vfield= (Field **) client_thd->alloc((share->virtual_fields+1)* + sizeof(Field*)); if (!vfield) goto error; } @@ -2437,12 +2415,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) /* Assign the pointers for the field pointers array and the record. */ field= copy->field= (Field**) (copy + 1); bitmap= (uchar*) (field + share->fields + 1); - copy->record[0]= (bitmap + share->column_bitmap_size*3); + copy->record[0]= (bitmap + share->column_bitmap_size*4); memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength); - if (share->default_fields) + if (share->default_fields || share->default_expressions) { - copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)* - sizeof(Field**)); + copy->default_field= (Field**) + client_thd->alloc((share->default_fields + + share->default_expressions + 1)* + sizeof(Field*)); if (!copy->default_field) goto error; dfield_ptr= copy->default_field; @@ -2465,48 +2445,61 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) if (!(*field= (*org_field)->make_new_field(client_thd->mem_root, copy, 1))) goto error; + (*field)->unireg_check= (*org_field)->unireg_check; (*field)->orig_table= copy; // Remove connection (*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0] if (*org_field == found_next_number_field) (*field)->table->found_next_number_field= *field; - if (share->default_fields && - ((*org_field)->has_insert_default_function() || - (*org_field)->has_update_default_function())) - { - /* Put the newly copied field into the set of default fields. */ - *dfield_ptr= *field; - (*dfield_ptr)->unireg_check= (*org_field)->unireg_check; - dfield_ptr++; - } } *field=0; - if (share->vfields) + if (share->virtual_fields || share->default_expressions || + share->default_fields) { + bool error_reported= FALSE; if (!(copy->def_vcol_set= (MY_BITMAP*) alloc_root(client_thd->mem_root, sizeof(MY_BITMAP)))) goto error; copy->vfield= vfield; for (field= copy->field; *field; field++) { + Virtual_column_info *vcol; if ((*field)->vcol_info) { - bool error_reported= FALSE; - if (unpack_vcol_info_from_frm(client_thd, - client_thd->mem_root, - copy, - *field, - &(*field)->vcol_info->expr_str, - &error_reported)) + if (!(vcol= unpack_vcol_info_from_frm(client_thd, + client_thd->mem_root, + copy, + 0, + (*field)->vcol_info, + &error_reported))) goto error; + (*field)->vcol_info= vcol; *vfield++= *field; } + if ((*field)->default_value) + { + if (!(vcol= unpack_vcol_info_from_frm(client_thd, + client_thd->mem_root, + copy, + 0, + (*field)->default_value, + &error_reported))) + goto error; + (*field)->default_value= vcol; + *dfield_ptr++= *field; + } + if ((*field)->has_insert_default_function() || + (*field)->has_update_default_function()) + *dfield_ptr++= *field; } - *vfield= 0; + if (vfield) + *vfield= 0; + if (dfield_ptr) + *dfield_ptr= 0; } - if (share->default_fields) - *dfield_ptr= NULL; + switch_to_nullable_trigger_fields(copy->vfield, copy); + switch_to_nullable_trigger_fields(copy->default_field, copy); /* Adjust in_use for pointing to client thread */ copy->in_use= client_thd; @@ -2518,15 +2511,28 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) copy->def_read_set.bitmap= (my_bitmap_map*) bitmap; copy->def_write_set.bitmap= ((my_bitmap_map*) (bitmap + share->column_bitmap_size)); - if (share->vfields) + bitmaps_used= 2; + if (share->virtual_fields) { my_bitmap_init(copy->def_vcol_set, - (my_bitmap_map*) (bitmap + 2*share->column_bitmap_size), + (my_bitmap_map*) (bitmap + + bitmaps_used*share->column_bitmap_size), share->fields, FALSE); + bitmaps_used++; copy->vcol_set= copy->def_vcol_set; } + if (share->default_fields) + { + if (!(copy->has_value_set= (MY_BITMAP*) alloc_root(client_thd->mem_root, + sizeof(MY_BITMAP)))) + goto error; + my_bitmap_init(copy->has_value_set, + (my_bitmap_map*) (bitmap + + bitmaps_used*share->column_bitmap_size), + share->fields, FALSE); + } copy->tmp_set.bitmap= 0; // To catch errors - bzero((char*) bitmap, share->column_bitmap_size * (share->vfields ? 3 : 2)); + bzero((char*) bitmap, share->column_bitmap_size * bitmaps_used); copy->read_set= ©->def_read_set; copy->write_set= ©->def_write_set; @@ -2793,11 +2799,11 @@ bool Delayed_insert::open_and_lock_table() return TRUE; } - if (table->triggers) + if (table->triggers || table->check_constraints) { /* - Table has triggers. This is not an error, but we do - not support triggers with delayed insert. Terminate the delayed + Table has triggers or check constraints. This is not an error, but we do + not support these with delayed insert. Terminate the delayed thread without an error and thus request lock upgrade. */ return TRUE; @@ -3425,8 +3431,8 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par, info.ignore= ignore_check_option_errors; info.update_fields= update_fields; info.update_values= update_values; - if (table_list_par) - info.view= (table_list_par->view ? table_list_par : 0); + info.view= (table_list_par->view ? table_list_par : 0); + info.table_list= table_list_par; } @@ -3657,7 +3663,7 @@ int select_insert::send_data(List<Item> &values) thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields store_values(values); - if (table->default_field && table->update_default_fields()) + if (table->default_field && table->update_default_fields(0, info.ignore)) DBUG_RETURN(1); thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; if (thd->is_error()) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index adc443affab..5d8caf6d2ab 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -279,8 +279,8 @@ Lex_input_stream::reset(char *buffer, unsigned int length) { yylineno= 1; yylval= NULL; - lookahead_token= -1; - lookahead_yylval= NULL; + lookahead_token= lookahead_token2= -1; + lookahead_yylval= lookahead_yylval2= NULL; m_ptr= buffer; m_tok_start= NULL; m_tok_end= NULL; @@ -1241,14 +1241,18 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return it. */ token= lip->lookahead_token; - lip->lookahead_token= -1; *yylval= *(lip->lookahead_yylval); - lip->lookahead_yylval= NULL; - lip->add_digest_token(token, yylval); + lip->lookahead_token= lip->lookahead_token2; + lip->lookahead_yylval= lip->lookahead_yylval2; + lip->m_cpp_tok_start= lip->lookahead_cpp_start; + lip->m_cpp_tok_end= lip->lookahead_cpp_end; + lip->lookahead_token2= -1; + lip->lookahead_yylval2= NULL; return token; } token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); switch(token) { case WITH: @@ -1260,12 +1264,11 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) which sql_yacc.yy can process. */ token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); switch(token) { case CUBE_SYM: - lip->add_digest_token(WITH_CUBE_SYM, yylval); return WITH_CUBE_SYM; case ROLLUP_SYM: - lip->add_digest_token(WITH_ROLLUP_SYM, yylval); return WITH_ROLLUP_SYM; default: /* @@ -1274,15 +1277,73 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) lip->lookahead_yylval= lip->yylval; lip->yylval= NULL; lip->lookahead_token= token; - lip->add_digest_token(WITH, yylval); + lip->lookahead_cpp_start= lip->get_cpp_tok_start(); + lip->lookahead_cpp_end= lip->get_cpp_tok_end(); return WITH; } break; + case NOT_SYM: + { + const char *cpp_tok_start, *m_tok_end, *tok_start; + /* + To be able to handle "DEFAULT 1 NOT NULL" which classes with + "DEFAULT 1 NOT IN (..)" we must combine NOT NULL to one lex token + NOT_NULL_SYM. We also have to ensure that NOT NULL IS are still + separate tokens to ensure that NOT NULL IS TRUE is evaluated as + NOT (NULL IS TRUE) + */ + cpp_tok_start= lip->get_cpp_tok_start(); // Save position of NOT + tok_start= lip->get_tok_start(); // For errors + m_tok_end= lip->get_cpp_tok_end(); + + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + + if (token == NULL_SYM) + { + /* Check that next is not 'IS' */ + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + if (token != IS) + { + /* Save the token following 'NOT NULL' */ + lip->lookahead_token= token; + lip->lookahead_yylval= lip->yylval; + lip->lookahead_cpp_start= lip->get_cpp_tok_start(); + lip->lookahead_cpp_end= lip->get_cpp_tok_end(); + token= NOT_NULL_SYM; + } + else + { + /* Save NULL and IS and return NOT */ + lip->lookahead_token2= IS; + lip->lookahead_token= NULL_SYM; + lip->lookahead_yylval2= lip->yylval; + lip->lookahead_yylval= lip->yylval; + lip->lookahead_cpp_start= cpp_tok_start; + lip->lookahead_cpp_end= lip->get_cpp_tok_end(); + token= NOT_SYM; + } + } + else + { + /* Save the token following 'NOT' */ + lip->lookahead_token= token; + lip->lookahead_yylval= lip->yylval; + lip->lookahead_cpp_start= lip->get_cpp_tok_start(); + lip->lookahead_cpp_end= lip->get_cpp_tok_end(); + token= NOT_SYM; + } + lip->yylval= NULL; + /* Restore parser position for the current token */ + lip->m_cpp_tok_start= cpp_tok_start; + lip->m_cpp_tok_end= m_tok_end; + lip->m_tok_start= tok_start; + break; + } default: break; } - - lip->add_digest_token(token, yylval); return token; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 58fa7ec9a2d..dad31620804 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2083,6 +2083,11 @@ public: return m_cpp_tok_start; } + void set_cpp_tok_start(const char *pos) + { + m_cpp_tok_start= pos; + } + /** Get the token end position, in the raw buffer. */ const char *get_tok_end() { @@ -2168,10 +2173,12 @@ public: or -1, if no token was parsed in advance. Note: 0 is a legal token, and represents YYEOF. */ - int lookahead_token; + int lookahead_token, lookahead_token2; - /** LALR(2) resolution, value of the look ahead token.*/ + /** LALR(3) resolution, value of the look ahead token.*/ LEX_YYSTYPE lookahead_yylval; + LEX_YYSTYPE lookahead_yylval2; + const char *lookahead_cpp_start, *lookahead_cpp_end; bool get_text(LEX_STRING *to, uint sep, int pre_skip, int post_skip); @@ -2299,6 +2306,7 @@ public: Current statement digest instrumentation. */ sql_digest_state* m_digest; + friend int MYSQLlex(union YYSTYPE *yylval, THD *thd); }; /** @@ -2973,6 +2981,13 @@ public: alter_info.key_list.push_back(last_key); return false; } + // Add a constraint as a part of CREATE TABLE or ALTER TABLE + bool add_constraint(LEX_STRING *name, Virtual_column_info *constr) + { + constr->name= *name; + alter_info.constraint_list.push_back(constr); + return false; + } void set_command(enum_sql_command command, DDL_options_st options) { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index a4044dd0d59..951022e5085 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -399,9 +399,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, set_fields, MARK_COLUMNS_WRITE, 0, 0) || check_that_all_fields_are_given_values(thd, table, table_list)) DBUG_RETURN(TRUE); - /* Add all fields with default functions to table->write_set. */ - if (table->default_field) - table->mark_default_fields_for_write(); /* Fix the expressions in SET clause */ if (setup_fields(thd, Ref_ptr_array(), set_values, MARK_COLUMNS_READ, 0, 0)) DBUG_RETURN(TRUE); @@ -412,18 +409,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table->prepare_triggers_for_insert_stmt_or_event(); table->mark_columns_needed_for_insert(); - - if (table->vfield) - { - for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) - { - if ((*vfield_ptr)->vcol_info->stored_in_db) - { - thd->lex->unit.insert_table_with_stored_vcol= table; - break; - } - } - } + if (table->s->virtual_stored_fields) + thd->lex->unit.insert_table_with_stored_vcol= table; uint tot_length=0; bool use_blobs= 0, use_vars= 0; @@ -989,8 +976,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (thd->killed || fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, ignore_check_option_errors, - TRG_EVENT_INSERT) || - (table->default_field && table->update_default_fields())) + TRG_EVENT_INSERT)) DBUG_RETURN(1); switch (table_list->view_check_option(thd, ignore_check_option_errors)) { @@ -1211,10 +1197,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } if (thd->killed || - fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, + fill_record_n_invoke_before_triggers(thd, table, set_fields, + set_values, ignore_check_option_errors, - TRG_EVENT_INSERT) || - (table->default_field && table->update_default_fields())) + TRG_EVENT_INSERT)) DBUG_RETURN(1); switch (table_list->view_check_option(thd, @@ -1393,8 +1379,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (thd->killed || fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, ignore_check_option_errors, - TRG_EVENT_INSERT) || - (table->default_field && table->update_default_fields())) + TRG_EVENT_INSERT)) DBUG_RETURN(1); switch (table_list->view_check_option(thd, diff --git a/sql/sql_priv.h b/sql/sql_priv.h index b15a80a889a..5ac58f8cda7 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -126,7 +126,7 @@ #define TMP_TABLE_ALL_COLUMNS (1ULL << 12) // SELECT, intern #define OPTION_WARNINGS (1ULL << 13) // THD, user #define OPTION_AUTO_IS_NULL (1ULL << 14) // THD, user, binlog -#define OPTION_FOUND_COMMENT (1ULL << 15) // SELECT, intern, parser +#define OPTION_NO_CHECK_CONSTRAINT_CHECKS (1ULL << 14) #define OPTION_SAFE_UPDATES (1ULL << 16) // THD, user #define OPTION_BUFFER_RESULT (1ULL << 17) // SELECT, user #define OPTION_BIN_LOG (1ULL << 18) // THD, user @@ -183,6 +183,7 @@ #define OPTION_ALLOW_BATCH (1ULL << 36) // THD, intern (slave) #define OPTION_SKIP_REPLICATION (1ULL << 37) // THD, user #define OPTION_RPL_SKIP_PARALLEL (1ULL << 38) +#define OPTION_FOUND_COMMENT (1ULL << 39) // SELECT, intern, parser /* The rest of the file is included in the server only */ #ifndef MYSQL_CLIENT diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 61755817b6d..3bd308d6d72 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16016,7 +16016,7 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps, uint field_count) { uint bitmap_size= bitmap_buffer_size(field_count); - DBUG_ASSERT(table->s->vfields == 0 && table->def_vcol_set == 0); + DBUG_ASSERT(table->s->virtual_fields == 0 && table->def_vcol_set == 0); my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, FALSE); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 26b20bee530..2cc57f0ea8c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1625,15 +1625,22 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, */ has_now_default= field->has_insert_default_function(); - has_default= (!(field->flags & NO_DEFAULT_VALUE_FLAG) && - field->unireg_check != Field::NEXT_NUMBER && - !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) - && has_now_default)); + has_default= (field->default_value || + (!(field->flags & NO_DEFAULT_VALUE_FLAG) && + field->unireg_check != Field::NEXT_NUMBER && + !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + && has_now_default))); def_value->length(0); if (has_default) { - if (has_now_default) + if (field->default_value) + { + def_value->set(field->default_value->expr_str.str, + field->default_value->expr_str.length, + system_charset_info); + } + else if (has_now_default) { def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP")); if (field->decimals() > 0) @@ -1922,6 +1929,13 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, !(sql_mode & MODE_NO_FIELD_OPTIONS)) packet->append(STRING_WITH_LEN(" AUTO_INCREMENT")); } + if (field->check_constraint) + { + packet->append(STRING_WITH_LEN(" CHECK (")); + packet->append(field->check_constraint->expr_str.str, + field->check_constraint->expr_str.length); + packet->append(STRING_WITH_LEN(")")); + } if (field->comment.length) { @@ -2011,6 +2025,27 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, file->free_foreign_key_create_info(for_str); } + /* Add table level check constraints */ + if (share->table_check_constraints) + { + for (uint i= share->field_check_constraints; + i < share->table_check_constraints ; i++) + { + Virtual_column_info *check= table->check_constraints[i]; + + packet->append(STRING_WITH_LEN(",\n ")); + if (check->name.length) + { + packet->append(STRING_WITH_LEN("CONSTRAINT ")); + append_identifier(thd, packet, check->name.str, check->name.length); + } + packet->append(STRING_WITH_LEN(" CHECK (")); + packet->append(check->expr_str.str, + check->expr_str.length); + packet->append(STRING_WITH_LEN(")")); + } + } + packet->append(STRING_WITH_LEN("\n)")); if (show_table_options) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6cd73978eed..a05afb39174 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -65,6 +65,9 @@ const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start, KEY *end); +static void make_unique_constraint_name(THD *thd, LEX_STRING *name, + List<Virtual_column_info> *vcol, + uint *nr); static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, List<Create_field> &create, bool ignore, uint order_num, ORDER *order, @@ -3082,7 +3085,7 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, - column_definition->def == NULL && // no constant default, + column_definition->default_value == NULL && // no constant default, column_definition->unireg_check == Field::NONE && // no function default column_definition->vcol_info == NULL) { @@ -3255,32 +3258,30 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Convert the default value from client character set into the column character set if necessary. + We can only do this for constants as we have not yet run fix_fields. */ - if (sql_field->def && - save_cs != sql_field->def->collation.collation && + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item() && + save_cs != sql_field->default_value->expr_item->collation.collation && (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || sql_field->sql_type == MYSQL_TYPE_STRING || sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_TINY_BLOB || + sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_field->sql_type == MYSQL_TYPE_LONG_BLOB || + sql_field->sql_type == MYSQL_TYPE_BLOB || sql_field->sql_type == MYSQL_TYPE_ENUM)) { - /* - Starting from 5.1 we work here with a copy of Create_field - created by the caller, not with the instance that was - originally created during parsing. It's OK to create - a temporary item and initialize with it a member of the - copy -- this item will be thrown away along with the copy - at the end of execution, and thus not introduce a dangling - pointer in the parsed tree of a prepared statement or a - stored procedure statement. - */ - sql_field->def= sql_field->def->safe_charset_converter(thd, save_cs); - - if (sql_field->def == NULL) + Item *item; + if (!(item= sql_field->default_value->expr_item-> + safe_charset_converter(thd, save_cs))) { /* Could not convert */ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); DBUG_RETURN(TRUE); } + /* Fix for prepare statement */ + thd->change_item_tree(&sql_field->default_value->expr_item, item); } if (sql_field->sql_type == MYSQL_TYPE_SET || @@ -3348,12 +3349,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->sql_type == MYSQL_TYPE_SET) { uint32 field_length; - if (sql_field->def != NULL) + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item()) { char *not_used; uint not_used2; bool not_found= 0; - String str, *def= sql_field->def->val_str(&str); + String str, *def= sql_field->default_value->expr_item->val_str(&str); if (def == NULL) /* SQL "NULL" maps to NULL */ { if ((sql_field->flags & NOT_NULL_FLAG) != 0) @@ -3385,9 +3387,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { uint32 field_length; DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM); - if (sql_field->def != NULL) + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item()) { - String str, *def= sql_field->def->val_str(&str); + String str, *def= sql_field->default_value->expr_item->val_str(&str); if (def == NULL) /* SQL "NULL" maps to NULL */ { if ((sql_field->flags & NOT_NULL_FLAG) != 0) @@ -3464,7 +3467,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length-= sql_field->length & 7; - sql_field->def= dup_field->def; + sql_field->default_value= dup_field->default_value; sql_field->sql_type= dup_field->sql_type; /* @@ -4129,7 +4132,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, it is NOT NULL, not an AUTO_INCREMENT field, not a TIMESTAMP and not updated trough a NOW() function. */ - if (!sql_field->def && + if (!sql_field->default_value && !sql_field->has_default_function() && (sql_field->flags & NOT_NULL_FLAG) && !is_timestamp_type(sql_field->sql_type)) @@ -4139,7 +4142,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && - !sql_field->def && + !sql_field->default_value && is_timestamp_type(sql_field->sql_type) && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) @@ -4163,6 +4166,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + /* Check table level constraints */ + create_info->constraint_list= &alter_info->constraint_list; + { + uint nr= 1; + List_iterator_fast<Virtual_column_info> c_it(alter_info->constraint_list); + Virtual_column_info *check; + while ((check= c_it++)) + { + if (!check->name.length) + make_unique_constraint_name(thd, &check->name, + &alter_info->constraint_list, + &nr); + + if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str); + DBUG_RETURN(TRUE); + } + if (check_expression(check, "CONSTRAINT CHECK", + check->name.str ? check->name.str : "", 0)) + DBUG_RETURN(TRUE); + } + } + /* Give warnings for not supported table options */ #if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; @@ -4278,7 +4306,7 @@ static bool prepare_blob_field(THD *thd, Column_definition *sql_field) /* Convert long VARCHAR columns to TEXT or BLOB */ char warn_buff[MYSQL_ERRMSG_SIZE]; - if (sql_field->def || thd->is_strict_mode()) + if (thd->is_strict_mode()) { my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name, static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / @@ -4357,7 +4385,7 @@ void sp_prepare_create_field(THD *thd, Column_definition *sql_field) FIELDFLAG_TREAT_BIT_AS_CHAR; } sql_field->create_length_to_internal_length(); - DBUG_ASSERT(sql_field->def == 0); + DBUG_ASSERT(sql_field->default_value == 0); /* Can't go wrong as sql_field->def is not defined */ (void) prepare_blob_field(thd, sql_field); } @@ -5122,6 +5150,38 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) return (char*) "not_specified"; // Should never happen } +/** + Make an unique name for constraints without a name +*/ + +static void make_unique_constraint_name(THD *thd, LEX_STRING *name, + List<Virtual_column_info> *vcol, + uint *nr) +{ + char buff[MAX_FIELD_NAME], *end; + List_iterator_fast<Virtual_column_info> it(*vcol); + + end=strmov(buff, "CONSTRAINT_"); + for (;;) + { + Virtual_column_info *check; + char *real_end= int10_to_str((*nr)++, end, 10); + it.rewind(); + while ((check= it++)) + { + if (check->name.str && + !my_strcasecmp(system_charset_info, buff, check->name.str)) + break; + } + if (!check) // Found unique name + { + name->length= (size_t) (real_end - buff); + name->str= thd->strmake(buff, name->length); + return; + } + } +} + /**************************************************************************** ** Alter a table definition @@ -6176,6 +6236,10 @@ static bool fill_alter_inplace_info(THD *thd, /* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */ if (alter_info->flags & Alter_info::ALTER_RECREATE) ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE; + if (alter_info->flags & Alter_info::ALTER_ADD_CONSTRAINT) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CONSTRAINT; + if (alter_info->flags & Alter_info::ALTER_DROP_CONSTRAINT) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CONSTRAINT; /* If we altering table with old VARCHAR fields we will be automatically @@ -6923,6 +6987,14 @@ static bool is_inplace_alter_impossible(TABLE *table, if (!table->s->mysql_version) DBUG_RETURN(true); + /* + If we are using a MySQL 5.7 table with virtual fields, ALTER TABLE must + recreate the table as we need to rewrite generated fields + */ + if (table->s->mysql_version > 50700 && table->s->mysql_version < 100000 && + table->s->virtual_fields) + DBUG_RETURN(TRUE); + DBUG_RETURN(false); } @@ -7333,6 +7405,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, List_iterator<Create_field> find_it(new_create_list); List_iterator<Create_field> field_it(new_create_list); List<Key_part_spec> key_parts; + List<Virtual_column_info> new_constraint_list; uint db_create_options= (table->s->db_create_options & ~(HA_OPTION_PACK_RECORD)); uint used_fields; @@ -7477,12 +7550,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } if (alter) { - if (def->sql_type == MYSQL_TYPE_BLOB) - { - my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change); - goto err; - } - if ((def->def=alter->def)) // Use new default + if ((def->default_value= alter->default_value)) def->flags&= ~NO_DEFAULT_VALUE_FLAG; else def->flags|= NO_DEFAULT_VALUE_FLAG; @@ -7756,6 +7824,33 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } + /* Add all table level constraints which are not in the drop list */ + if (table->s->table_check_constraints) + { + TABLE_SHARE *share= table->s; + + for (uint i= share->field_check_constraints; + i < share->table_check_constraints ; i++) + { + Virtual_column_info *check= table->check_constraints[i]; + Alter_drop *drop; + drop_it.rewind(); + while ((drop=drop_it++)) + { + if (drop->type == Alter_drop::CONSTRAINT_CHECK && + !my_strcasecmp(system_charset_info, check->name.str, drop->name)) + { + drop_it.remove(); + break; + } + } + if (!drop) + new_constraint_list.push_back(check, thd->mem_root); + } + } + /* Add new constraints */ + new_constraint_list.append(&alter_info->constraint_list); + if (alter_info->drop_list.elements) { Alter_drop *drop; @@ -7764,8 +7859,15 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, switch (drop->type) { case Alter_drop::KEY: case Alter_drop::COLUMN: - my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), - alter_info->drop_list.head()->name); + case Alter_drop::CONSTRAINT_CHECK: + if (drop->drop_if_exists) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_CANT_DROP_FIELD_OR_KEY, + ER_THD(thd, ER_CANT_DROP_FIELD_OR_KEY), + drop->name); + else + my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), + alter_info->drop_list.head()->name); goto err; case Alter_drop::FOREIGN_KEY: // Leave the DROP FOREIGN KEY names in the alter_info->drop_list. @@ -7811,6 +7913,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, rc= FALSE; alter_info->create_list.swap(new_create_list); alter_info->key_list.swap(new_key_list); + alter_info->constraint_list.swap(new_constraint_list); err: DBUG_RETURN(rc); } @@ -8848,13 +8951,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set, &altered_table->s->all_set); restore_record(altered_table, s->default_values); // Create empty record - if (altered_table->default_field && altered_table->update_default_fields()) + /* Check that we can call default functions with default field values */ + altered_table->reset_default_fields(); + if (altered_table->default_field && + altered_table->update_default_fields(0, 1)) goto err_new_table_cleanup; // Ask storage engine whether to use copy or in-place enum_alter_inplace_result inplace_supported= - table->file->check_if_supported_inplace_alter(altered_table, - &ha_alter_info); + HA_ALTER_INPLACE_NOT_SUPPORTED; + if (!(ha_alter_info.handler_flags & + Alter_inplace_info::ALTER_ADD_CONSTRAINT) || + (thd->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)) + inplace_supported= + table->file->check_if_supported_inplace_alter(altered_table, + &ha_alter_info); switch (inplace_supported) { case HA_ALTER_INPLACE_EXCLUSIVE_LOCK: @@ -9435,7 +9546,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, Old fields keep their current values, and therefore should not be present in the set of autoupdate fields. */ - if ((*ptr)->has_insert_default_function()) + if ((*ptr)->default_value || + ((*ptr)->has_insert_default_function())) { *(dfield_ptr++)= *ptr; ++to->s->default_fields; @@ -9482,7 +9594,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); - to->mark_virtual_columns_for_write(TRUE); + /* Add virtual columns to vcol_set to ensure they are updated */ + if (to->vfield) + to->mark_virtual_columns_for_write(TRUE); if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, file_sort, 1, 1, FALSE)) goto err; @@ -9492,8 +9606,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->extra(HA_EXTRA_IGNORE_DUP_KEY); thd->get_stmt_da()->reset_current_row_for_warning(); restore_record(to, s->default_values); // Create empty record - if (to->default_field && to->update_default_fields()) - goto err; + to->reset_default_fields(); thd->progress.max_counter= from->file->records(); time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10; @@ -9534,8 +9647,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, copy_ptr->do_copy(copy_ptr); } prev_insert_id= to->file->next_insert_id; + if (to->default_field) + to->update_default_fields(0, ignore); if (to->vfield) update_virtual_fields(thd, to, VCOL_UPDATE_FOR_WRITE); + + /* This will set thd->is_error() if fatal failure */ + if (to->verify_constraints(ignore) == VIEW_CHECK_SKIP) + continue; if (thd->is_error()) { error= 1; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 76454d7c56f..52162e43643 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -353,8 +353,6 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); } - if (table->default_field) - table->mark_default_fields_for_write(); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ @@ -394,14 +392,6 @@ int mysql_update(THD *thd, } } - /* - If a timestamp field settable on UPDATE is present then to avoid wrong - update force the table handler to retrieve write-only fields to be able - to compare records and detect data change. - */ - if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - table->default_field && table->has_default_function(true)) - bitmap_union(table->read_set, table->write_set); // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all(); @@ -763,7 +753,7 @@ int mysql_update(THD *thd, if (!can_compare_record || compare_record(table)) { - if (table->default_field && table->update_default_fields()) + if (table->default_field && table->update_default_fields(1, ignore)) { error= 1; break; @@ -1716,17 +1706,8 @@ int multi_update::prepare(List<Item> ¬_used_values, { table->read_set= &table->def_read_set; bitmap_union(table->read_set, &table->tmp_set); - /* - If a timestamp field settable on UPDATE is present then to avoid wrong - update force the table handler to retrieve write-only fields to be able - to compare records and detect data change. - */ - if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - table->default_field && table->has_default_function(true)) - bitmap_union(table->read_set, table->write_set); } } - if (error) DBUG_RETURN(1); @@ -2117,11 +2098,11 @@ int multi_update::send_data(List<Item> ¬_used_values) table->status|= STATUS_UPDATED; store_record(table,record[1]); - if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], + if (fill_record_n_invoke_before_triggers(thd, table, + *fields_for_table[offset], *values_for_table[offset], 0, TRG_EVENT_UPDATE)) DBUG_RETURN(1); - /* Reset the table->auto_increment_field_not_null as it is valid for only one row. @@ -2132,7 +2113,7 @@ int multi_update::send_data(List<Item> ¬_used_values) { int error; - if (table->default_field && table->update_default_fields()) + if (table->default_field && table->update_default_fields(1, ignore)) DBUG_RETURN(1); if ((error= cur_table->view_check_option(thd, ignore)) != @@ -2422,7 +2403,8 @@ int multi_update::do_updates() if (!can_compare_record || compare_record(table)) { int error; - if (table->default_field && (error= table->update_default_fields())) + if (table->default_field && + (error= table->update_default_fields(1, ignore))) goto err2; if (table->vfield && update_virtual_fields(thd, table, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c971373d31b..42100d6b4cf 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -936,6 +936,31 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) MYSQL_YYABORT; \ } while(0) +Virtual_column_info *add_virtual_expression(THD *thd, char *txt, + size_t size, Item *expr) +{ + CHARSET_INFO *cs= thd->charset(); + Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); + if (!v) + { + mem_alloc_error(sizeof(Virtual_column_info)); + return 0; + } + /* + We have to remove white space as remember_cur_pos may have pointed to end + of previous expression. + */ + while (cs->state_map[*(uchar*)txt] == MY_LEX_SKIP) + { + txt++; + size--; + } + v->expr_str.str= (char* ) thd->strmake(txt, size); + v->expr_str.length= size; + v->expr_item= expr; + return v; +} + %} %union { int num; @@ -985,6 +1010,7 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) class sp_name *spname; class sp_variable *spvar; class With_clause *with_clause; + class Virtual_column_info *virtual_column; handlerton *db_type; st_select_lex *select_lex; @@ -1425,6 +1451,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NO_SYM /* SQL-2003-R */ %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG +%token NOT_NULL_SYM %token NTILE_SYM %token NULL_SYM /* SQL-2003-R */ %token NUM @@ -1605,6 +1632,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token STD_SYM %token STOP_SYM %token STORAGE_SYM +%token STORED_SYM %token STRAIGHT_JOIN %token STRING_SYM %token SUBCLASS_ORIGIN_SYM /* SQL-2003-N */ @@ -1750,7 +1778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_ident_opt_wild create_like %type <simple_string> - remember_name remember_end opt_db remember_tok_start + remember_name remember_end opt_db remember_tok_start remember_cur_pos wild_and_where field_length opt_field_length opt_field_length_default_1 @@ -1829,7 +1857,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_wild simple_expr udf_expr expr_or_default set_expr_or_default geometry_function - signed_literal now_or_signed_literal opt_escape + opt_escape sp_opt_default simple_ident_nospvar simple_ident_q field_or_var limit_option @@ -1928,6 +1956,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <myvar> select_outvar +%type <virtual_column> opt_check_constraint check_constraint virtual_column_func + %type <NONE> analyze_stmt_command query verb_clause create change select do drop insert replace insert2 @@ -1941,7 +1971,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); persistent_column_stat_spec persistent_index_stat_spec table_column_list table_index_list table_index_name check start checksum - field_list field_list_item kill key_def + field_list field_list_item kill key_def constraint_def keycache_list keycache_list_or_parts assign_to_keycache assign_to_keycache_parts preload_list preload_list_or_parts preload_keys preload_keys_parts @@ -5076,7 +5106,9 @@ partition_entry: ; partition: - BY part_type_def opt_num_parts opt_sub_part part_defs + BY + { Lex->safe_to_cache_query= 1; } + part_type_def opt_num_parts opt_sub_part part_defs ; part_type_def: @@ -5242,11 +5274,7 @@ sub_part_field_item: part_func_expr: bit_expr { - LEX *lex= Lex; - bool not_corr_func; - not_corr_func= !lex->safe_to_cache_query; - lex->safe_to_cache_query= 1; - if (not_corr_func) + if (!Lex->safe_to_cache_query) { my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); MYSQL_YYABORT; @@ -6139,11 +6167,14 @@ field_list: field_list_item: column_def { } | key_def + | constraint_def ; column_def: - field_spec opt_check_constraint { $$= $1; } - | field_spec references { $$= $1; } + field_spec opt_check_constraint + { $$= $1; Lex->last_field->check_constraint= $2; } + | field_spec references + { $$= $1; } ; key_def: @@ -6227,16 +6258,31 @@ key_def: /* Only used for ALTER TABLE. Ignored otherwise. */ lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY; } - | opt_constraint check_constraint { } - ; + ; + +constraint_def: + opt_constraint check_constraint + { + Lex->add_constraint(&$1, $2); + } + ; opt_check_constraint: - /* empty */ - | check_constraint + /* empty */ { $$= (Virtual_column_info*) 0; } + | check_constraint { $$= $1;} ; check_constraint: - CHECK_SYM '(' expr ')' + CHECK_SYM '(' remember_name expr remember_end ')' + { + Virtual_column_info *v= + add_virtual_expression(thd, $3+1, (uint)($5 - $3) - 1, $4); + if (!v) + { + MYSQL_YYABORT; + } + $$= v; + } ; opt_constraint: @@ -6290,6 +6336,7 @@ field_def: opt_attribute | opt_generated_always AS '(' virtual_column_func ')' + { Lex->last_field->vcol_info= $4; } vcol_opt_specifier vcol_opt_attribute ; @@ -6311,6 +6358,10 @@ vcol_opt_specifier: { Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); } + | STORED_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); + } ; vcol_opt_attribute: @@ -6352,23 +6403,20 @@ parse_vcol_expr: my_message(ER_SYNTAX_ERROR, ER_THD(thd, ER_SYNTAX_ERROR), MYF(0)); MYSQL_YYABORT; } + Lex->last_field->vcol_info= $3; } ; virtual_column_func: - remember_name expr remember_end + remember_cur_pos expr remember_end { - Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); + Virtual_column_info *v= + add_virtual_expression(thd, $1, (uint)($3 - $1), $2); if (!v) { - mem_alloc_error(sizeof(Virtual_column_info)); MYSQL_YYABORT; } - uint expr_len= (uint)($3 - $1) - 1; - v->expr_str.str= (char* ) thd->memdup($1 + 1, expr_len); - v->expr_str.length= expr_len; - v->expr_item= $2; - Lex->last_field->vcol_info= v; + $$= v; } ; @@ -6664,8 +6712,8 @@ opt_attribute_list: attribute: NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; } - | not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } - | DEFAULT now_or_signed_literal { Lex->last_field->def= $2; } + | NOT_NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } + | DEFAULT virtual_column_func { Lex->last_field->default_value= $2; } | ON UPDATE_SYM NOW_SYM opt_default_time_precision { Item *item= new (thd->mem_root) Item_func_now_local(thd, $4); @@ -6752,18 +6800,6 @@ type_with_opt_collate: } ; - -now_or_signed_literal: - NOW_SYM opt_default_time_precision - { - $$= new (thd->mem_root) Item_func_now_local(thd, $2); - if ($$ == NULL) - MYSQL_YYABORT; - } - | signed_literal - { $$=$1; } - ; - charset: CHAR_SYM SET {} | CHARSET {} @@ -7716,6 +7752,10 @@ alter_list_item: Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | Alter_info::ALTER_ADD_INDEX; } + | ADD constraint_def + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_CONSTRAINT; + } | CHANGE opt_column opt_if_exists_table_element field_ident field_spec opt_place { @@ -7742,6 +7782,17 @@ alter_list_item: lex->alter_info.drop_list.push_back(ad, thd->mem_root); lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN; } + | DROP CONSTRAINT opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::CONSTRAINT_CHECK, + $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_CONSTRAINT; + } | DROP FOREIGN KEY_SYM opt_if_exists_table_element field_ident { LEX *lex=Lex; @@ -7785,7 +7836,7 @@ alter_list_item: lex->alter_info.keys_onoff= Alter_info::ENABLE; lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; } - | ALTER opt_column field_ident SET DEFAULT signed_literal + | ALTER opt_column field_ident SET DEFAULT virtual_column_func { LEX *lex=Lex; Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6); @@ -7798,7 +7849,7 @@ alter_list_item: { LEX *lex=Lex; Alter_column *ac= (new (thd->mem_root) - Alter_column($3.str, (Item*) 0)); + Alter_column($3.str, (Virtual_column_info*) 0)); if (ac == NULL) MYSQL_YYABORT; lex->alter_info.alter_list.push_back(ac, thd->mem_root); @@ -8899,6 +8950,12 @@ remember_tok_start: } ; +remember_cur_pos: + { + $$= (char*) YYLIP->get_cpp_ptr(); + } + ; + remember_name: { $$= (char*) YYLIP->get_cpp_tok_start(); @@ -9097,6 +9154,12 @@ bool_pri: if ($$ == NULL) MYSQL_YYABORT; } + | bool_pri IS NOT_NULL_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM { $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); @@ -9455,6 +9518,13 @@ simple_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NOT_NULL_SYM + { + /* Replace NOT NULL with NULL */ + $$= new (thd->mem_root) Item_null(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } | '+' simple_expr %prec NEG { $$= $2; @@ -13965,16 +14035,6 @@ param_marker: } ; -signed_literal: - literal { $$ = $1; } - | '+' NUM_literal { $$ = $2; } - | '-' NUM_literal - { - $2->max_length++; - $$= $2->neg(thd); - } - ; - literal: text_literal { $$ = $1; } | NUM_literal { $$ = $1; } @@ -14736,6 +14796,7 @@ keyword: | SONAME_SYM {} | START_SYM {} | STOP_SYM {} + | STORED_SYM {} | TRUNCATE_SYM {} | UNICODE_SYM {} | UNINSTALL_SYM {} diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index a722a1cd1c7..79743dbfc13 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3695,6 +3695,12 @@ static Sys_var_bit Sys_unique_checks( REVERSE(OPTION_RELAXED_UNIQUE_CHECKS), DEFAULT(TRUE), NO_MUTEX_GUARD, IN_BINLOG); +static Sys_var_bit Sys_no_check_constraint( + "check_constraint_checks", "check_constraint_checks", + SESSION_VAR(option_bits), NO_CMD_LINE, + REVERSE(OPTION_NO_CHECK_CONSTRAINT_CHECKS), + DEFAULT(TRUE), NO_MUTEX_GUARD, IN_BINLOG); + #ifdef ENABLED_PROFILING static bool update_profiling(sys_var *self, THD *thd, enum_var_type type) { diff --git a/sql/table.cc b/sql/table.cc index 208d5da37c7..038c7c41588 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2015, MariaDB + Copyright (c) 2008, 2016, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,6 +42,11 @@ #include "sql_view.h" #include "rpl_filter.h" +/* For MySQL 5.7 virtual fields */ +#define MYSQL57_GENERATED_FIELD 128 +#define MYSQL57_GCOL_HEADER_SIZE 4 + + /* INFORMATION_SCHEMA name */ LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; @@ -393,7 +398,7 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, share->path.str= (char*) path; share->normalized_path.str= (char*) path; share->path.length= share->normalized_path.length= strlen(path); - share->frm_version= FRM_VER_TRUE_VARCHAR; + share->frm_version= FRM_VER_CURRENT; share->cached_row_logging_check= 0; // No row logging @@ -908,6 +913,52 @@ static uint upgrade_collation(ulong mysql_version, uint cs_number) } +/* + In MySQL 5.7 the null bits for not stored virtual fields are last. + Calculate the position for these bits +*/ + +static void mysql57_calculate_null_position(TABLE_SHARE *share, + uchar **null_pos, + uint *null_bit_pos, + const uchar *strpos, + const uchar *vcol_screen_pos) +{ + uint field_pack_length= 17; + + for (uint i=0 ; i < share->fields; i++, strpos+= field_pack_length) + { + uint field_length, pack_flag; + enum_field_types field_type; + + if ((strpos[10] & MYSQL57_GENERATED_FIELD)) + { + /* Skip virtual not stored field */ + bool stored_in_db= (bool) (uint) (vcol_screen_pos[3]); + vcol_screen_pos+= (uint2korr(vcol_screen_pos + 1) + + MYSQL57_GCOL_HEADER_SIZE); + if (! stored_in_db) + continue; + } + field_length= uint2korr(strpos+3); + pack_flag= uint2korr(strpos+8); + field_type= (enum_field_types) (uint) strpos[13]; + if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) + { + if (((*null_bit_pos)+= field_length & 7) > 7) + { + (*null_pos)++; + (*null_bit_pos)-= 8; + } + } + if (f_maybe_null(pack_flag)) + { + if (!((*null_bit_pos)= ((*null_bit_pos) + 1) & 7)) + (*null_pos)++; + } + } +} + /** Read data from a binary .frm file image into a TABLE_SHARE @@ -932,14 +983,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint new_frm_ver, field_pack_length, new_field_pack_flag; uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; - uint com_length, null_bit_pos; + uint com_length, null_bit_pos, mysql_vcol_null_bit_pos, bitmap_count; uint extra_rec_buf_length; uint i; - bool use_hash; + bool use_hash, mysql57_null_bits= 0; char *keynames, *names, *comment_pos; const uchar *forminfo, *extra2; const uchar *frm_image_end = frm_image + frm_length; - uchar *record, *null_flags, *null_pos; + uchar *record, *null_flags, *null_pos, *mysql_vcol_null_pos; const uchar *disk_buff, *strpos; ulong pos, record_offset; ulong rec_buff_length; @@ -952,7 +1003,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, my_bitmap_map *bitmaps; bool null_bits_are_used; uint vcol_screen_length, UNINIT_VAR(options_len); - char *vcol_screen_pos; + uchar *vcol_screen_pos; const uchar *options= 0; uint UNINIT_VAR(gis_options_len); const uchar *gis_options= 0; @@ -963,6 +1014,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, keyinfo= &first_keyinfo; share->ext_key_parts= 0; MEM_ROOT *old_root= thd->mem_root; + Virtual_column_info **table_check_constraints; DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); thd->mem_root= &share->mem_root; @@ -1112,6 +1164,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->null_field_first= 1; share->stats_sample_pages= uint2korr(frm_image+42); share->stats_auto_recalc= (enum_stats_auto_recalc)(frm_image[44]); + share->table_check_constraints= uint2korr(frm_image+45); } if (!share->table_charset) { @@ -1369,8 +1422,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->null_fields= uint2korr(forminfo+282); com_length= uint2korr(forminfo+284); vcol_screen_length= uint2korr(forminfo+286); - share->vfields= 0; - share->default_fields= 0; + share->virtual_fields= share->default_expressions= + share->field_check_constraints= share->default_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1386,6 +1439,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, alloc_root(&share->mem_root, (uint) ((share->fields+1)*sizeof(Field*)+ interval_count*sizeof(TYPELIB)+ + share->table_check_constraints * + sizeof(Virtual_column_info*)+ (share->fields+interval_parts+ keys+3)*sizeof(char *)+ (n_length+int_length+com_length+ @@ -1399,7 +1454,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, strpos= disk_buff+pos; share->intervals= (TYPELIB*) (field_ptr+share->fields+1); - interval_array= (const char **) (share->intervals+interval_count); + share->check_constraints= ((Virtual_column_info**) + (share->intervals+interval_count)); + table_check_constraints= share->check_constraints; + interval_array= (const char **) (table_check_constraints+ + share->table_check_constraints); names= (char*) (interval_array+share->fields+interval_parts+keys+3); if (!interval_count) share->intervals= 0; // For better debugging @@ -1408,7 +1467,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, comment_pos= names+(n_length+int_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); + vcol_screen_pos= (uchar*) (names+(n_length+int_length+com_length)); memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length, vcol_screen_length); @@ -1478,6 +1537,22 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->fields,0,0, (my_hash_get_key) get_field_name,0,0); + if (share->mysql_version >= 50700 && share->mysql_version < 100000 && + vcol_screen_length) + { + /* + MySQL 5.7 stores the null bits for not stored fields last. + Calculate the position for them. + */ + mysql57_null_bits= 1; + mysql_vcol_null_pos= null_pos; + mysql_vcol_null_bit_pos= null_bit_pos; + mysql57_calculate_null_position(share, &mysql_vcol_null_pos, + &mysql_vcol_null_bit_pos, + strpos, + vcol_screen_pos); + } + for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; @@ -1565,9 +1640,32 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, comment_pos+= comment_length; } + if (unireg_type & MYSQL57_GENERATED_FIELD) + { + unireg_type&= MYSQL57_GENERATED_FIELD; + + if ((uint)(vcol_screen_pos)[0] != 1) + goto err; + vcol_info= new (&share->mem_root) Virtual_column_info(); + vcol_info_length= uint2korr(vcol_screen_pos + 1); + DBUG_ASSERT(vcol_info_length); + vcol_info->stored_in_db= (bool) (uint) vcol_screen_pos[3]; + if (!(vcol_info->expr_str.str= + (char *)memdup_root(&share->mem_root, + vcol_screen_pos + MYSQL57_GCOL_HEADER_SIZE, + vcol_info_length))) + goto err; + vcol_info->expr_str.length= vcol_info_length; + vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;; + share->virtual_fields++; + vcol_info_length= 0; + } + if (vcol_info_length) { /* + Old virtual field information before 10.2 + Get virtual column data stored in the .frm file as follows: byte 1 = 1 | 2 byte 2 = sql_type @@ -1585,18 +1683,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; vcol_expr_length= vcol_info_length - - (uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id)); + (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id)); if (!(vcol_info->expr_str.str= (char *)memdup_root(&share->mem_root, vcol_screen_pos + - (uint) FRM_VCOL_HEADER_SIZE(opt_interval_id), + (uint) FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id), vcol_expr_length))) goto err; if (opt_interval_id) interval_nr= (uint) vcol_screen_pos[3]; vcol_info->expr_str.length= vcol_expr_length; vcol_screen_pos+= vcol_info_length; - share->vfields++; + share->virtual_fields++; } } else @@ -1671,6 +1769,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif + if (mysql57_null_bits && vcol_info && !vcol_info->stored_in_db) + { + swap_variables(uchar*, null_pos, mysql_vcol_null_pos); + swap_variables(uint, null_bit_pos, mysql_vcol_null_bit_pos); + } + *field_ptr= reg_field= make_field(share, &share->mem_root, record+recpos, (uint32) field_length, @@ -1709,6 +1813,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (!(null_bit_pos= (null_bit_pos + 1) & 7)) null_pos++; } + + if (mysql57_null_bits && vcol_info && !vcol_info->stored_in_db) + { + /* MySQL 5.7 has null bits last */ + swap_variables(uchar*, null_pos, mysql_vcol_null_pos); + swap_variables(uint, null_bit_pos, mysql_vcol_null_bit_pos); + } + if (f_no_default(pack_flag)) reg_field->flags|= NO_DEFAULT_VALUE_FLAG; @@ -1723,15 +1835,27 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (share->stored_rec_length>=recpos) share->stored_rec_length= recpos-1; } + if (reg_field->has_insert_default_function()) + has_insert_default_function= 1; + if (reg_field->has_update_default_function()) + has_update_default_function= 1; if (reg_field->has_insert_default_function() || reg_field->has_update_default_function()) - ++share->default_fields; + share->default_fields++; } *field_ptr=0; // End marker /* Sanity checks: */ DBUG_ASSERT(share->fields>=share->stored_fields); DBUG_ASSERT(share->reclength>=share->stored_rec_length); + if (mysql57_null_bits) + { + /* We want to store the value for the last bits */ + swap_variables(uchar*, null_pos, mysql_vcol_null_pos); + swap_variables(uint, null_bit_pos, mysql_vcol_null_bit_pos); + DBUG_ASSERT((null_pos + (null_bit_pos + 7) / 8) <= share->field[0]->ptr); + } + /* Fix key->name and key_part->field */ if (key_parts) { @@ -2037,6 +2161,90 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, null_length, 255); } + /* Handle virtual expressions */ + if (vcol_screen_length && share->frm_version >= FRM_VER_EXPRESSSIONS) + { + /* + Read virtual columns, default values and check constraints + See pack_expression() for how data is stored + */ + for (uchar *vcol_screen_end= vcol_screen_pos + vcol_screen_length ; + vcol_screen_pos < vcol_screen_end ; ) + { + Virtual_column_info *vcol_info; + uint field_nr= uint2korr(vcol_screen_pos); + uint expr_length= uint2korr(vcol_screen_pos+2); + uint type= (uint) vcol_screen_pos[4]; + uint name_length= (uint) vcol_screen_pos[5]; + uint flags= (uint) vcol_screen_pos[6]; + LEX_STRING name; + char *expr; + + vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE; + + name.str= 0; + if ((name.length= name_length)) + { + if (!(name.str= strmake_root(&share->mem_root, + (char*) vcol_screen_pos, + name_length))) + goto err; + } + vcol_screen_pos+= name_length; + if (!(vcol_info= new (&share->mem_root) Virtual_column_info()) || + !(expr= (char *) strmake_root(&share->mem_root, + (char*) vcol_screen_pos, + expr_length))) + goto err; + vcol_info->name= name; + + /* The following can only be true for check_constraints */ + if (field_nr != UINT_MAX32) + reg_field= share->field[field_nr]; + + vcol_info->expr_str.str= expr; + vcol_info->expr_str.length= expr_length; + vcol_screen_pos+= expr_length; + vcol_info->non_deterministic= flags & 1; + vcol_info->stored_in_db= 0; + + switch (type) { + case 0: // Virtual computed field + { + uint recpos; + reg_field->vcol_info= vcol_info; + share->virtual_fields++; + share->stored_fields--; + /* Correct stored_rec_length as non stored fields are last */ + recpos= (uint) (reg_field->ptr - record); + if (share->stored_rec_length >= recpos) + share->stored_rec_length= recpos-1; + break; + } + case 1: // Virtual stored field + vcol_info->stored_in_db= 1; + reg_field->vcol_info= vcol_info; + share->virtual_fields++; + share->virtual_stored_fields++; // For insert/load data + break; + case 2: // Default expression + vcol_info->stored_in_db= 1; + reg_field->default_value= vcol_info; + share->default_expressions++; + break; + case 3: // Field check constraint + reg_field->check_constraint= vcol_info; + share->field_check_constraints++; + break; + case 4: // Table check constraint + *(table_check_constraints++)= vcol_info; + break; + } + } + } + DBUG_ASSERT((table_check_constraints - share->check_constraints) == + share->table_check_constraints - share->field_check_constraints); + if (options) { DBUG_ASSERT(options_len); @@ -2088,11 +2296,32 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->column_bitmap_size= bitmap_buffer_size(share->fields); + bitmap_count= 1; + if (share->table_check_constraints) + { + if (!(share->check_set= (MY_BITMAP*) + alloc_root(&share->mem_root, sizeof(*share->check_set)))) + goto err; + bitmap_count++; + } if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, - share->column_bitmap_size))) + share->column_bitmap_size * + bitmap_count))) goto err; my_bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); bitmap_set_all(&share->all_set); + if (share->check_set) + { + /* + Bitmap for fields used by CHECK constraint. Will be filled up + at first usage of table. + */ + my_bitmap_init(share->check_set, + (my_bitmap_map*) ((uchar*) bitmaps + + share->column_bitmap_size), + share->fields, FALSE); + bitmap_clear_all(share->check_set); + } delete handler_file; #ifndef DBUG_OFF @@ -2285,31 +2514,6 @@ void TABLE_SHARE::free_frm_image(const uchar *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 @@ -2319,6 +2523,8 @@ static void clear_field_flag(TABLE *table) table The table containing the virtual column @param vcol_field The virtual field whose defining expression is to be analyzed + @param vcol The Virtual_column object + @details The function performs semantic analysis of the defining expression for @@ -2326,31 +2532,31 @@ static void clear_field_flag(TABLE *table) 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. - + If the virtual column has stored_in_db set and it uses non deterministic + function then table->non_determinstic_insert is set. + This is used in replication to ensure that row based replication is used + for inserts. + @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) +static bool fix_vcol_expr(THD *thd, + TABLE *table, + Field *field, + Virtual_column_info *vcol) { - Virtual_column_info *vcol_info= vcol_field->vcol_info; - Item* func_expr= vcol_info->expr_item; + Item* func_expr= vcol->expr_item; bool result= TRUE; TABLE_LIST tables; int error= 0; const char *save_where; - Field **ptr, *field; enum_mark_columns save_mark_used_columns= thd->mark_used_columns; - DBUG_ASSERT(func_expr); DBUG_ENTER("fix_vcol_expr"); + DBUG_PRINT("info", ("vcol: %p", vcol)); + DBUG_ASSERT(func_expr); thd->mark_used_columns= MARK_COLUMNS_NONE; @@ -2359,59 +2565,62 @@ bool fix_vcol_expr(THD *thd, /* Fix fields referenced to by the virtual column function */ if (!func_expr->fixed) - error= func_expr->fix_fields(thd, &vcol_info->expr_item); - /* fix_fields could change the expression */ - func_expr= vcol_info->expr_item; - /* Number of columns will be checked later */ - + error= func_expr->fix_fields(thd, &vcol->expr_item); if (unlikely(error)) { DBUG_PRINT("info", ("Field in virtual column expression does not belong to the table")); + my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), vcol->expr_str); goto end; } + /* fix_fields could change the expression */ + func_expr= vcol->expr_item; + + /* + Mark what kind of default / virtual fields the table has + Here we assume that things has not changed since table was created. + If we decide to not trust functions, we could instead call + expr_item->walk(&Item::check_vcol_func_processor) + */ + if (vcol->stored_in_db && vcol->non_deterministic) + table->s->non_determinstic_insert= 1; + + /* Number of columns will be checked later */ thd->where= save_where; if (unlikely(func_expr->result_type() == ROW_RESULT)) { my_error(ER_ROW_EXPR_FOR_VCOL, MYF(0)); goto end; } + + /* Check that we are not refering to any not yet initialized fields */ + if (field) + { + if (func_expr->walk(&Item::check_field_expression_processor, 0, + (uchar*) field)) + goto end; + } + #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) + Item::vcol_func_processor_result res; + res.errors= 0; + + error= func_expr->walk(&Item::check_vcol_func_processor, 0, (uchar*) &res); + if (error || (res.errors & VCOL_IMPOSSIBLE)) { - my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, + 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 @@ -2437,31 +2646,33 @@ end: 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 the special arena TABLE::expr_arena. + + The function takes string expression from the 'vcol' object of the + table 'table' and parses it, building an item object for it. The + pointer to this item is placed into in a Virtual_column_info object + that is created. 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 the + special arena TABLE::expr_arena or in the thd memroot for INSERT DELAYED @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 + Virtual_column_info* If a success @retval - TRUE Otherwise + NULL Error */ -bool unpack_vcol_info_from_frm(THD *thd, - MEM_ROOT *mem_root, - TABLE *table, - Field *field, - LEX_STRING *vcol_expr, - bool *error_reported) -{ - bool rc; + +Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, + MEM_ROOT *mem_root, + TABLE *table, + Field *field, + Virtual_column_info *vcol, + bool *error_reported) +{ char *vcol_expr_str; int str_len; CHARSET_INFO *old_character_set_client; @@ -2470,6 +2681,8 @@ bool unpack_vcol_info_from_frm(THD *thd, Query_arena *vcol_arena= 0; Create_field vcol_storage; // placeholder for vcol_info Parser_state parser_state; + Virtual_column_info *vcol_info= 0; + LEX_STRING *vcol_expr= &vcol->expr_str; LEX *old_lex= thd->lex; LEX lex; DBUG_ENTER("unpack_vcol_info_from_frm"); @@ -2486,10 +2699,8 @@ bool unpack_vcol_info_from_frm(THD *thd, if (!(vcol_expr_str= (char*) alloc_root(mem_root, vcol_expr->length + - parse_vcol_keyword.length + 3))) - { - DBUG_RETURN(TRUE); - } + parse_vcol_keyword.length + 3))) + DBUG_RETURN(0); memcpy(vcol_expr_str, (char*) parse_vcol_keyword.str, parse_vcol_keyword.length); @@ -2542,25 +2753,22 @@ bool unpack_vcol_info_from_frm(THD *thd, { goto err; } - /* From now on use vcol_info generated by the parser. */ - field->vcol_info= vcol_storage.vcol_info; - - /* copy the stored_in_db property, the parser doesn't generate it */ - field->vcol_info->stored_in_db= - table->s->field[field->field_index]->vcol_info->stored_in_db; - + /* + mark if expression will be stored in the table. This is also used by + fix_vcol_expr() to mark if we are using non deterministic functions. + */ + vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; + vcol_storage.vcol_info->non_deterministic= vcol->non_deterministic; + vcol_storage.vcol_info->name= vcol->name; /* Validate the Item tree. */ - if (fix_vcol_expr(thd, table, field)) + if (!fix_vcol_expr(thd, table, field, vcol_storage.vcol_info)) { - *error_reported= TRUE; - field->vcol_info= 0; - goto err; + vcol_info= vcol_storage.vcol_info; // Expression ok + goto end; } - rc= FALSE; - goto end; + *error_reported= TRUE; err: - rc= TRUE; thd->free_items(); end: thd->stmt_arena= backup_stmt_arena_ptr; @@ -2569,7 +2777,7 @@ end: end_lex_with_single_table(thd, table, old_lex); thd->variables.character_set_client= old_character_set_client; - DBUG_RETURN(rc); + DBUG_RETURN(vcol_info); } /* @@ -2610,7 +2818,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, uint records, i, bitmap_size, bitmap_count; bool error_reported= FALSE; uchar *record, *bitmaps; - Field **field_ptr, **UNINIT_VAR(vfield_ptr), **UNINIT_VAR(dfield_ptr); + Field **field_ptr; uint8 save_context_analysis_only= thd->lex->context_analysis_only; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, @@ -2761,54 +2969,122 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, /* Process virtual and default columns, if any. */ - if (share->vfields) - { - if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, - (uint) ((share->vfields+1)* - sizeof(Field*))))) + if (share->virtual_fields || share->default_fields || + share->default_expressions || share->table_check_constraints) + { + Field **vfield_ptr, **dfield_ptr; + Virtual_column_info **check_constraint_ptr; + + if (!multi_alloc_root(&outparam->mem_root, + &vfield_ptr, (uint) ((share->virtual_fields + 1)* + sizeof(Field*)), + &dfield_ptr, (uint) ((share->default_fields + + share->default_expressions +1)* + sizeof(Field*)), + &check_constraint_ptr, + (uint) ((share->table_check_constraints + 1)* + sizeof(Virtual_column_info*)), + NullS)) goto err; - - outparam->vfield= vfield_ptr; - } - - if (share->default_fields) - { - if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root, - (uint) ((share->default_fields+1)* - sizeof(Field*))))) - goto err; - - outparam->default_field= dfield_ptr; - } - - if (share->vfields || share->default_fields) - { - /* Reuse the same loop both for virtual and default fields. */ + if (share->virtual_fields) + outparam->vfield= vfield_ptr; + if (share->default_fields + share->default_expressions) + outparam->default_field= dfield_ptr; + if (share->table_check_constraints || share->field_check_constraints) + outparam->check_constraints= check_constraint_ptr; + + /* Reuse the same loop both for virtual, default and check fields */ for (field_ptr= outparam->field; *field_ptr; field_ptr++) { - if (share->vfields && (*field_ptr)->vcol_info) + if ((*field_ptr)->vcol_info) { - if (unpack_vcol_info_from_frm(thd, - &outparam->mem_root, - outparam, - *field_ptr, - &(*field_ptr)->vcol_info->expr_str, - &error_reported)) + Virtual_column_info *vcol; + (*field_ptr)->vcol_info->name.str= (char*) (*field_ptr)->field_name; + if (!(vcol= unpack_vcol_info_from_frm(thd, + &outparam->mem_root, + outparam, + *field_ptr, + (*field_ptr)->vcol_info, + &error_reported))) { error= OPEN_FRM_CORRUPTED; goto err; } + (*field_ptr)->vcol_info= vcol; *(vfield_ptr++)= *field_ptr; } - if (share->default_fields && - ((*field_ptr)->has_insert_default_function() || + + if ((*field_ptr)->check_constraint) + { + Virtual_column_info *vcol; + (*field_ptr)->check_constraint->name.str= + (char*) (*field_ptr)->field_name; + if (!(vcol= unpack_vcol_info_from_frm(thd, + &outparam->mem_root, + outparam, + 0, + (*field_ptr)->check_constraint, + &error_reported))) + { + error= OPEN_FRM_CORRUPTED; + goto err; + } + (*field_ptr)->check_constraint= vcol; + *(check_constraint_ptr++)= vcol; + } + + if ((*field_ptr)->default_value) + { + Virtual_column_info *vcol; + (*field_ptr)->default_value->name.str= + (char*) (*field_ptr)->field_name; + if (!(vcol= unpack_vcol_info_from_frm(thd, + &outparam->mem_root, + outparam, + *field_ptr, + (*field_ptr)->default_value, + &error_reported))) + { + error= OPEN_FRM_CORRUPTED; + goto err; + } + (*field_ptr)->default_value= vcol; + *(dfield_ptr++)= *field_ptr; + } + + if (((*field_ptr)->has_insert_default_function() || (*field_ptr)->has_update_default_function())) *(dfield_ptr++)= *field_ptr; + + } + *vfield_ptr= 0; // End marker + *dfield_ptr= 0; // End marker + + /* Update to use trigger fields */ + switch_to_nullable_trigger_fields(outparam->vfield, outparam); + switch_to_nullable_trigger_fields(outparam->default_field, outparam); + + /* Copy table level constraints to check_constraint_ptr */ + for (i= 0 ; + i < share->table_check_constraints - share->field_check_constraints; + i++) + { + if (!(*check_constraint_ptr= + unpack_vcol_info_from_frm(thd, + &outparam->mem_root, + outparam, + 0, + share->check_constraints[i], + &error_reported))) + { + error= OPEN_FRM_CORRUPTED; + goto err; + } + (*check_constraint_ptr)->name= share->check_constraints[i]->name; + check_constraint_ptr++; } - if (share->vfields) - *vfield_ptr= 0; // End marker - if (share->default_fields) - *dfield_ptr= 0; // End marker + + *check_constraint_ptr= 0; // End marker } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -2879,7 +3155,7 @@ partititon_err: #endif /* Check virtual columns against table's storage engine. */ - if (share->vfields && + if (share->virtual_fields && (outparam->file && !(outparam->file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS))) { @@ -2893,13 +3169,11 @@ partititon_err: bitmap_size= share->column_bitmap_size; bitmap_count= 6; - if (share->vfields) - { - if (!(outparam->def_vcol_set= (MY_BITMAP*) - alloc_root(&outparam->mem_root, sizeof(*outparam->def_vcol_set)))) - goto err; + if (share->virtual_fields) bitmap_count++; - } + if (outparam->default_field) + bitmap_count++; + if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size * bitmap_count))) goto err; @@ -2910,13 +3184,27 @@ partititon_err: my_bitmap_init(&outparam->def_write_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); bitmaps+= bitmap_size; - if (share->vfields) + + /* Don't allocate vcol_bitmap or explicit_value if we don't need it */ + if (share->virtual_fields) { - /* Don't allocate vcol_bitmap if we don't need it */ + if (!(outparam->def_vcol_set= (MY_BITMAP*) + alloc_root(&outparam->mem_root, sizeof(*outparam->def_vcol_set)))) + goto err; my_bitmap_init(outparam->def_vcol_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); bitmaps+= bitmap_size; } + if (outparam->default_field) + { + if (!(outparam->has_value_set= (MY_BITMAP*) + alloc_root(&outparam->mem_root, sizeof(*outparam->has_value_set)))) + goto err; + my_bitmap_init(outparam->has_value_set, + (my_bitmap_map*) bitmaps, share->fields, FALSE); + bitmaps+= bitmap_size; + } + my_bitmap_init(&outparam->tmp_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); bitmaps+= bitmap_size; @@ -2976,6 +3264,8 @@ partititon_err: } } + outparam->mark_columns_used_by_check_constraints(); + if (share->table_category == TABLE_CATEGORY_LOG) { outparam->no_replicate= TRUE; @@ -3341,7 +3631,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, /* header */ fileinfo[0]=(uchar) 254; fileinfo[1]= 1; - fileinfo[2]= FRM_VER + 3 + MY_TEST(create_info->varchar); + fileinfo[2]= (create_info->expression_lengths == 0 ? FRM_VER_TRUE_VARCHAR : + FRM_VER_EXPRESSSIONS); DBUG_ASSERT(ha_storage_engine_is_enabled(create_info->db_type)); fileinfo[3]= (uchar) ha_legacy_type(create_info->db_type); @@ -3392,9 +3683,9 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, /* Bytes 41-46 were for RAID support; now reused for other purposes */ fileinfo[41]= (uchar) (csid >> 8); int2store(fileinfo+42, create_info->stats_sample_pages & 0xffff); - fileinfo[44]= (uchar) create_info->stats_auto_recalc; - fileinfo[45]= 0; - fileinfo[46]= 0; + fileinfo[44]= (uchar) create_info->stats_auto_recalc; + int2store(fileinfo+45, (create_info->constraint_list->elements+ + create_info->field_check_constraints)); int4store(fileinfo+47, key_length); tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store int4store(fileinfo+51, tmp); @@ -4678,8 +4969,28 @@ void TABLE_LIST::cleanup_items() } +static int check_constraint_error(THD *thd, const char *db_name, + const char *table_name, + const LEX_STRING *constraint_name, + bool ignore_failure) +{ + if (ignore_failure) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_CONSTRAINT_FAILED, + ER_THD(thd, ER_CONSTRAINT_FAILED), + constraint_name->str ? constraint_name->str : "", + db_name, table_name); + return(VIEW_CHECK_SKIP); + } + my_error(ER_CONSTRAINT_FAILED, MYF(0), + constraint_name->str ? constraint_name->str : "", + db_name, table_name); + return VIEW_CHECK_ERROR; +} + /* - check CHECK OPTION condition + check CHECK OPTION condition both for view and underlying table SYNOPSIS TABLE_LIST::view_check_option() @@ -4691,24 +5002,45 @@ void TABLE_LIST::cleanup_items() VIEW_CHECK_SKIP FAILED, but continue */ +const LEX_STRING view_check_name= { C_STRING_WITH_LEN("WITH CHECK OPTION") }; + + int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) { + /* VIEW's CHECK OPTION CLAUSE */ if (check_option && check_option->val_int() == 0) { TABLE_LIST *main_view= top_table(); - if (ignore_failure) + const char *name_db= (main_view->view ? main_view->view_db.str : + main_view->db); + const char *name_table= (main_view->view ? main_view->view_name.str : + main_view->table_name); + return check_constraint_error(thd, name_db, name_table, &view_check_name, + ignore_failure); + } + return table->verify_constraints(ignore_failure); +} + + +int TABLE::verify_constraints(bool ignore_failure) +{ + /* go trough check option clauses for fields and table */ + if (check_constraints && + !(in_use->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)) + { + for (Virtual_column_info **chk= check_constraints ; *chk ; chk++) { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_VIEW_CHECK_FAILED, - ER_THD(thd, ER_VIEW_CHECK_FAILED), - main_view->view_db.str, main_view->view_name.str); - return(VIEW_CHECK_SKIP); + if ((*chk)->expr_item->val_int() == 0) + { + return check_constraint_error(in_use, + s->db.str, + s->table_name.str, + &(*chk)->name, + ignore_failure); + } } - my_error(ER_VIEW_CHECK_FAILED, MYF(0), main_view->view_db.str, - main_view->view_name.str); - return(VIEW_CHECK_ERROR); } - return(VIEW_CHECK_OK); + return VIEW_CHECK_OK; } @@ -5694,12 +6026,12 @@ void TABLE::clear_column_bitmaps() Reset column read/write usage. It's identical to: bitmap_clear_all(&table->def_read_set); bitmap_clear_all(&table->def_write_set); - if (s->vfields) bitmap_clear_all(table->def_vcol_set); + if (s->virtual_fields) bitmap_clear_all(table->def_vcol_set); The code assumes that the bitmaps are allocated after each other, as guaranteed by open_table_from_share() */ bzero((char*) def_read_set.bitmap, - s->column_bitmap_size * (s->vfields ? 3 : 2)); + s->column_bitmap_size * (s->virtual_fields ? 3 : 2)); column_bitmaps_set(&def_read_set, &def_write_set, def_vcol_set); rpl_write_set= 0; // Safety } @@ -5889,6 +6221,8 @@ void TABLE::mark_columns_needed_for_delete() file->column_bitmaps_signal(); } } + if (check_constraints) + mark_check_constraint_columns_for_read(); } @@ -5945,8 +6279,23 @@ void TABLE::mark_columns_needed_for_update() file->column_bitmaps_signal(); } } + file->register_columns_for_write(); + if (default_field) + mark_default_fields_for_write(FALSE); /* Mark all virtual columns needed for update */ - mark_virtual_columns_for_write(FALSE); + if (vfield) + mark_virtual_columns_for_write(FALSE); + if (check_constraints) + mark_check_constraint_columns_for_read(); + + /* + If a timestamp field settable on UPDATE is present then to avoid wrong + update force the table handler to retrieve write-only fields to be able + to compare records and detect data change. + */ + if ((file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && + default_field && has_default_function(true)) + bitmap_union(read_set, write_set); DBUG_VOID_RETURN; } @@ -5960,6 +6309,7 @@ void TABLE::mark_columns_needed_for_update() void TABLE::mark_columns_needed_for_insert() { + DBUG_ENTER("mark_columns_needed_for_insert"); mark_columns_per_binlog_row_image(); if (triggers) @@ -5975,8 +6325,15 @@ void TABLE::mark_columns_needed_for_insert() } if (found_next_number_field) mark_auto_increment_column(); + if (default_field) + mark_default_fields_for_write(TRUE); /* Mark virtual columns for insert */ - mark_virtual_columns_for_write(TRUE); + if (vfield) + mark_virtual_columns_for_write(TRUE); + file->register_columns_for_write(); + if (check_constraints) + mark_check_constraint_columns_for_read(); + DBUG_VOID_RETURN; } /* @@ -6163,9 +6520,6 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl) if (!vfield) return; - if (!vfield) - return; - for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) { tmp_vfield= *vfield_ptr; @@ -6199,29 +6553,35 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl) file->column_bitmaps_signal(); } - -/** - Check if a table has a default function either for INSERT or UPDATE-like - operation - @retval true there is a default function - @retval false there is no default function +/* + Mark fields used by check constraints. + This is done once for the TABLE_SHARE the first time the table is opened. + The marking must be done non-destructively to handle the case when + this could be run in parallely by two threads */ -bool TABLE::has_default_function(bool is_update) +void TABLE::mark_columns_used_by_check_constraints(void) { - Field **dfield_ptr, *dfield; - bool res= false; - for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) - { - dfield= (*dfield_ptr); - if (is_update) - res= dfield->has_update_default_function(); - else - res= dfield->has_insert_default_function(); - if (res) - return res; - } - return res; + MY_BITMAP *save_read_set; + /* If there is no check constraints or if check_set is already initialized */ + if (!s->check_set || s->check_set_initialized) + return; + + save_read_set= read_set; + read_set= s->check_set; + + for (Virtual_column_info **chk= check_constraints ; *chk ; chk++) + (*chk)->expr_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + + read_set= save_read_set; + s->check_set_initialized= 1; +} + +/* Add fields used by CHECK CONSTRAINT to read map */ + +void TABLE::mark_check_constraint_columns_for_read(void) +{ + bitmap_union(read_set, s->check_set); } @@ -6229,19 +6589,27 @@ bool TABLE::has_default_function(bool is_update) Add all fields that have a default function to the table write set. */ -void TABLE::mark_default_fields_for_write() +void TABLE::mark_default_fields_for_write(bool is_insert) { - Field **dfield_ptr, *dfield; - enum_sql_command cmd= in_use->lex->sql_command; - for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + DBUG_ENTER("mark_default_fields_for_write"); + Field **field_ptr, *field; + for (field_ptr= default_field; *field_ptr; field_ptr++) { - dfield= (*dfield_ptr); - if (((sql_command_flags[cmd] & CF_INSERTS_DATA) && - dfield->has_insert_default_function()) || - ((sql_command_flags[cmd] & CF_UPDATES_DATA) && - dfield->has_update_default_function())) - bitmap_set_bit(write_set, dfield->field_index); + field= (*field_ptr); + if (field->default_value) + { + if (is_insert) + { + bitmap_set_bit(write_set, field->field_index); + field->default_value->expr_item-> + walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + } + } + else if ((is_insert && field->has_insert_default_function()) || + (!is_insert && field->has_update_default_function())) + bitmap_set_bit(write_set, field->field_index); } + DBUG_VOID_RETURN; } @@ -6936,47 +7304,66 @@ int update_virtual_fields(THD *thd, TABLE *table, definition and the current operation one or the other kind of update function is evaluated. + @param update_command True if command was an update else insert + @param ignore_errors True if we should ignore errors + @retval 0 Success @retval - >0 Error occurred when storing a virtual field value + >0 Error occurred when storing a virtual field value and + ignore_errors == 0. If set then an error was generated. */ -int TABLE::update_default_fields() +int TABLE::update_default_fields(bool update_command, bool ignore_errors) { DBUG_ENTER("update_default_fields"); Field **dfield_ptr, *dfield; int res= 0; - enum_sql_command cmd= in_use->lex->sql_command; - DBUG_ASSERT(default_field); + in_use->reset_arena_for_cached_items(expr_arena); + /* Iterate over fields with default functions in the table */ - for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + for (dfield_ptr= default_field; *dfield_ptr ; dfield_ptr++) { dfield= (*dfield_ptr); /* If an explicit default value for a filed overrides the default, do not update the field with its automatic default value. */ - if (!(dfield->flags & HAS_EXPLICIT_VALUE)) + if (!dfield->has_explicit_value()) { - if (sql_command_flags[cmd] & CF_INSERTS_DATA) - res= dfield->evaluate_insert_default_function(); - if (sql_command_flags[cmd] & CF_UPDATES_DATA) - res= dfield->evaluate_update_default_function(); - if (res) - DBUG_RETURN(res); + if (!update_command) + { + if (dfield->default_value) + res|= (dfield->default_value->expr_item->save_in_field(dfield, 0) < 0); + else + res|= dfield->evaluate_insert_default_function(); + } + else + res|= dfield->evaluate_update_default_function(); + if (!ignore_errors && res) + { + my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), dfield->field_name); + break; + } + res= 0; } } + in_use->reset_arena_for_cached_items(0); DBUG_RETURN(res); } +/** + Reset markers that fields are being updated +*/ + void TABLE::reset_default_fields() { - if (default_field) - for (Field **df= default_field; *df; df++) - (*df)->flags&= ~HAS_EXPLICIT_VALUE; + DBUG_ENTER("reset_default_fields"); + if (has_value_set) + bitmap_clear_all(has_value_set); + DBUG_VOID_RETURN; } /* diff --git a/sql/table.h b/sql/table.h index 08e823716eb..034930a8988 100644 --- a/sql/table.h +++ b/sql/table.h @@ -51,6 +51,8 @@ class Field; class Table_statistics; class With_element; struct TDC_element; +class Virtual_column_info; +class Table_triggers_list; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -328,8 +330,6 @@ enum enum_vcol_update_mode VCOL_UPDATE_ALL }; -class Field_blob; -class Table_triggers_list; /** Category of table found in the table share. @@ -567,6 +567,7 @@ struct TABLE_SHARE TYPELIB *intervals; /* pointer to interval info */ mysql_mutex_t LOCK_ha_data; /* To protect access to ha_data */ mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */ + MY_BITMAP *check_set; /* Fields used by check constrant */ TDC_element *tdc; @@ -579,6 +580,7 @@ struct TABLE_SHARE Field **field; Field **found_next_number_field; KEY *key_info; /* data of keys in database */ + Virtual_column_info **check_constraints; uint *blob_field; /* Index to blobs in Field arrray*/ TABLE_STATISTICS_CB stats_cb; @@ -666,7 +668,9 @@ struct TABLE_SHARE uint open_errno; /* error from open_table_def() */ uint column_bitmap_size; uchar frm_version; - uint vfields; /* Number of computed (virtual) fields */ + uint virtual_fields; + uint default_expressions; + uint table_check_constraints, field_check_constraints; uint default_fields; /* Number of default fields */ bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; @@ -676,6 +680,11 @@ struct TABLE_SHARE bool is_view; bool can_cmp_whole_record; bool table_creation_was_logged; + bool non_determinstic_insert; + bool virtual_stored_fields; + bool check_set_initialized; + bool has_update_default_function; + bool has_insert_default_function; ulong table_map_id; /* for row-based replication */ /* @@ -1055,6 +1064,7 @@ public: Field **vfield; /* Pointer to virtual fields*/ /* Fields that are updated automatically on INSERT or UPDATE. */ Field **default_field; + Virtual_column_info **check_constraints; /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; @@ -1078,6 +1088,7 @@ public: MY_BITMAP *read_set, *write_set, *rpl_write_set; /* Set if using virtual fields */ MY_BITMAP *vcol_set, *def_vcol_set; + MY_BITMAP *has_value_set; /* The ID of the query that opened and is using this table. Has different @@ -1295,8 +1306,22 @@ public: void mark_columns_per_binlog_row_image(void); bool mark_virtual_col(Field *field); void mark_virtual_columns_for_write(bool insert_fl); - void mark_default_fields_for_write(); - bool has_default_function(bool is_update); + void mark_default_fields_for_write(bool insert_fl); + void mark_columns_used_by_check_constraints(void); + void mark_check_constraint_columns_for_read(void); + int verify_constraints(bool ignore_failure); + /** + Check if a table has a default function either for INSERT or UPDATE-like + operation + @retval true there is a default function + @retval false there is no default function + */ + inline bool has_default_function(bool is_update) + { + return (is_update ? + s->has_update_default_function : + s->has_insert_default_function); + } inline void column_bitmaps_set(MY_BITMAP *read_set_arg, MY_BITMAP *write_set_arg) { @@ -1406,7 +1431,7 @@ public: uint actual_n_key_parts(KEY *keyinfo); ulong actual_key_flags(KEY *keyinfo); - int update_default_fields(); + int update_default_fields(bool update, bool ignore_errors); void reset_default_fields(); inline ha_rows stat_records() { return used_stat_records; } @@ -2606,9 +2631,11 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, bool is_create_table); -bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, - TABLE *table, Field *field, - LEX_STRING *vcol_expr, bool *error_reported); +Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, + TABLE *table, + Field *field, + Virtual_column_info *vcol, + bool *error_reported); TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, const char *key, uint key_length); void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, diff --git a/sql/unireg.cc b/sql/unireg.cc index f565fc97d86..c99266dbff1 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -40,10 +40,12 @@ #define ALLOCA_THRESHOLD 2048 static uint pack_keys(uchar *,uint, KEY *, ulong); -static bool pack_header(THD *, uchar *, List<Create_field> &, uint, ulong, handler *); +static bool pack_header(THD *, uchar *, List<Create_field> &, HA_CREATE_INFO *info, ulong, handler *); static uint get_interval_id(uint *,List<Create_field> &, Create_field *); -static bool pack_fields(uchar *, List<Create_field> &, ulong); +static bool pack_fields(uchar **, List<Create_field> &, ulong); +static void pack_constraints(uchar **buff, List<Virtual_column_info> *constr); static size_t packed_fields_length(List<Create_field> &); +static size_t packed_constraints_length(List<Virtual_column_info> *constr); static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, ulong); /* @@ -106,7 +108,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, LEX_STRING str_db_type; uint reclength, key_info_length, i; ulong key_buff_length; - ulong filepos, data_offset; + ulong filepos, data_offset, expr_length; uint options_len; uint gis_extra2_len= 0; uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; @@ -121,13 +123,19 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, create_info->null_bits++; data_offset= (create_info->null_bits + 7) / 8; - error= pack_header(thd, forminfo, create_fields, create_info->table_options, + error= pack_header(thd, forminfo, create_fields, create_info, data_offset, db_file); if (error) DBUG_RETURN(frm); - reclength=uint2korr(forminfo+266); + reclength= uint2korr(forminfo+266); + expr_length= packed_constraints_length(create_info->constraint_list); + + /* Correct expression length stored by pack_header */ + expr_length+= uint2korr(forminfo+286); + int2store(forminfo+286, expr_length); + create_info->expression_lengths= expr_length; /* Calculate extra data segment length */ str_db_type= *hton_name(create_info->db_type); @@ -219,8 +227,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, filepos= frm.length; frm.length+= FRM_FORMINFO_SIZE; // forminfo frm.length+= packed_fields_length(create_fields); + frm.length+= expr_length; - if (frm.length > FRM_MAX_SIZE) + if (frm.length > FRM_MAX_SIZE || expr_length > UINT_MAX32) { my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table); DBUG_RETURN(frm); @@ -326,8 +335,10 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, } memcpy(frm_ptr + filepos, forminfo, 288); - if (pack_fields(frm_ptr + filepos + 288, create_fields, data_offset)) + pos= frm_ptr + filepos + 288; + if (pack_fields(&pos, create_fields, data_offset)) goto err; + pack_constraints(&pos, create_info->constraint_list); { /* @@ -488,15 +499,73 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, } /* pack_keys */ +/** + Calculate and check length of stored expression (virtual, def, check) + + @param v_col Virtual expression. Can be 0 + @param length Sum total lengths here + + Note to make calls easier, one can call this with v_col == 0 +*/ + +static void add_expr_length(Virtual_column_info *v_col, size_t *length) +{ + if (!v_col) + return; + /* + Sum up the length of the expression string, it's optional name + and the header. + */ + (*length)+= (FRM_VCOL_NEW_HEADER_SIZE + v_col->name.length + + v_col->expr_str.length); + return; +} + + +/* + pack_expression + + The data is stored as: + 2 bytes field_number + 2 bytes length of expression + 1 byte type (0 virtual, 1 virtual stored, 2 def, 3 check) + 1 byte length of name + 1 byte flags; 1 = non deterministic expression + name + next bytes column expression (text data) +*/ + +static void pack_expression(uchar **buff, Virtual_column_info *vcol, + uint offset, uint type) +{ + int2store((*buff), offset); + /* + expr_str.length < 64K as we have checked that the total size of the + frm file is < 64K + */ + int2store((*buff)+2, vcol->expr_str.length); + (*buff)[4]= (uchar) type; + (*buff)[5]= vcol->name.length; + (*buff)[6]= vcol->non_deterministic; // 1 bit used for now + (*buff)+= FRM_VCOL_NEW_HEADER_SIZE; + memcpy((*buff), vcol->name.str, vcol->name.length); + (*buff)+= vcol->name.length; + memcpy((*buff), vcol->expr_str.str, vcol->expr_str.length); + (*buff)+= vcol->expr_str.length; +} + + /* Make formheader */ static bool pack_header(THD *thd, uchar *forminfo, List<Create_field> &create_fields, - uint table_options, ulong data_offset, handler *file) + HA_CREATE_INFO *create_info, ulong data_offset, + handler *file) { uint length,int_count,int_length,no_empty, int_parts; uint time_stamp_pos,null_fields; - ulong reclength, totlength, n_length, com_length, vcol_info_length; + uint table_options= create_info->table_options; + size_t reclength, totlength, n_length, com_length, expression_length; DBUG_ENTER("pack_header"); if (create_fields.elements > MAX_FIELDS) @@ -508,8 +577,9 @@ static bool pack_header(THD *thd, uchar *forminfo, totlength= 0L; reclength= data_offset; no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0; - com_length=vcol_info_length=0; + com_length=expression_length=0; n_length=2L; + create_info->field_check_constraints= 0; /* Check fields */ List_iterator<Create_field> it(create_fields); @@ -520,30 +590,10 @@ static bool pack_header(THD *thd, uchar *forminfo, ER_TOO_LONG_FIELD_COMMENT, field->field_name)) DBUG_RETURN(1); - if (field->vcol_info) - { - uint col_expr_maxlen= field->virtual_col_expr_maxlen(); - uint tmp_len= my_charpos(system_charset_info, - field->vcol_info->expr_str.str, - field->vcol_info->expr_str.str + - field->vcol_info->expr_str.length, - col_expr_maxlen); - - if (tmp_len < field->vcol_info->expr_str.length) - { - my_error(ER_WRONG_STRING_LENGTH, MYF(0), - field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION", - col_expr_maxlen); - DBUG_RETURN(1); - } - /* - Sum up the length of the expression string and the length of the - mandatory header to the total length of info on the defining - expressions saved in the frm file for virtual columns. - */ - vcol_info_length+= field->vcol_info->expr_str.length+ - FRM_VCOL_HEADER_SIZE(field->interval); - } + add_expr_length(field->vcol_info, &expression_length); + if (field->has_default_expression()) + add_expr_length(field->default_value, &expression_length); + add_expr_length(field->check_constraint, &expression_length); totlength+= field->length; com_length+= field->comment.length; @@ -616,6 +666,8 @@ static bool pack_header(THD *thd, uchar *forminfo, } if (f_maybe_null(field->pack_flag)) null_fields++; + if (field->check_constraint) + create_info->field_check_constraints++; } int_length+=int_count*2; // 255 prefix + 0 suffix @@ -628,7 +680,7 @@ static bool pack_header(THD *thd, uchar *forminfo, /* Hack to avoid bugs with small static rows in MySQL */ reclength=MY_MAX(file->min_record_length(table_options),reclength); if ((ulong) create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+ - n_length+int_length+com_length+vcol_info_length > 65535L || + n_length+int_length+com_length+expression_length > 65535L || int_count > 255) { my_message(ER_TOO_MANY_FIELDS, ER_THD(thd, ER_TOO_MANY_FIELDS), MYF(0)); @@ -637,11 +689,11 @@ static bool pack_header(THD *thd, uchar *forminfo, bzero((char*)forminfo,FRM_FORMINFO_SIZE); length=(create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+n_length+int_length+ - com_length+vcol_info_length); + com_length+expression_length); int2store(forminfo,length); forminfo[256] = 0; int2store(forminfo+258,create_fields.elements); - int2store(forminfo+260,0); + int2store(forminfo+260,0); // Screen length, not used anymore int2store(forminfo+262,totlength); int2store(forminfo+264,no_empty); int2store(forminfo+266,reclength); @@ -654,7 +706,7 @@ static bool pack_header(THD *thd, uchar *forminfo, int2store(forminfo+280,22); /* Rows needed */ int2store(forminfo+282,null_fields); int2store(forminfo+284,com_length); - int2store(forminfo+286,vcol_info_length); + int2store(forminfo+286,expression_length); DBUG_RETURN(0); } /* pack_header */ @@ -707,11 +759,11 @@ static size_t packed_fields_length(List<Create_field> &create_fields) } length++; } - if (field->vcol_info) - { - length+= field->vcol_info->expr_str.length + - FRM_VCOL_HEADER_SIZE(field->interval); - } + + add_expr_length(field->vcol_info, &length); + if (field->has_default_expression()) + add_expr_length(field->default_value, &length); + add_expr_length(field->check_constraint, &length); length+= FCOMP; length+= strlen(field->field_name)+1; length+= field->comment.length; @@ -721,12 +773,34 @@ static size_t packed_fields_length(List<Create_field> &create_fields) DBUG_RETURN(length); } + +static size_t packed_constraints_length(List<Virtual_column_info> *constr) +{ + List_iterator<Virtual_column_info> it(*constr); + size_t length= 0; + Virtual_column_info *check; + while ((check= it++)) + add_expr_length(check, &length); + return length; +} + +static void pack_constraints(uchar **buff, List<Virtual_column_info> *constr) +{ + List_iterator<Virtual_column_info> it(*constr); + Virtual_column_info *check; + while ((check= it++)) + pack_expression(buff, check, UINT_MAX32, 4); +} + + /* Save fields, fieldnames and intervals */ -static bool pack_fields(uchar *buff, List<Create_field> &create_fields, +static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, ulong data_offset) { - uint int_count, comment_length= 0, vcol_info_length=0; + uchar *buff= *buff_arg; + uint int_count, comment_length= 0; + bool has_expressions= 0; Create_field *field; DBUG_ENTER("pack_fields"); @@ -736,7 +810,6 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields, while ((field=it++)) { uint recpos; - uint cur_vcol_expr_len= 0; int2store(buff+3, field->length); /* The +1 is here becasue the col offset in .frm file have offset 1 */ recpos= field->offset+1 + (uint) data_offset; @@ -763,17 +836,10 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields, { buff[11]= buff[14]= 0; // Numerical } - if (field->vcol_info) - { - /* - Use the interval_id place in the .frm file to store the length of - the additional data saved for the virtual field - */ - buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length + - FRM_VCOL_HEADER_SIZE(field->interval); - vcol_info_length+= cur_vcol_expr_len; - buff[13]= (uchar) MYSQL_TYPE_VIRTUAL; - } + if (field->vcol_info || field->has_default_expression() || + field->check_constraint) + has_expressions= 1; + int2store(buff+15, field->comment.length); comment_length+= field->comment.length; set_if_bigger(int_count,field->interval_id); @@ -857,35 +923,26 @@ static bool pack_fields(uchar *buff, List<Create_field> &create_fields, buff+= field->comment.length; } } - if (vcol_info_length) + + if (has_expressions) { + /* Store expressions */ it.rewind(); - while ((field=it++)) + for (uint field_nr=0 ; (field= it++) ; field_nr++) { - /* - Pack each virtual field as follows: - byte 1 = interval_id == 0 ? 1 : 2 - byte 2 = sql_type - byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) - [byte 4] = possible interval_id for sql_type - next byte ... = virtual column expression (text data) - */ - if (field->vcol_info && field->vcol_info->expr_str.length) - { - *buff++= (uchar) (1 + MY_TEST(field->interval)); - *buff++= (uchar) field->sql_type; - *buff++= (uchar) field->vcol_info->stored_in_db; - if (field->interval) - *buff++= (uchar) field->interval_id; - memcpy(buff, field->vcol_info->expr_str.str, field->vcol_info->expr_str.length); - buff+= field->vcol_info->expr_str.length; - } + if (field->vcol_info) + pack_expression(&buff, field->vcol_info, field_nr, + field->vcol_info->stored_in_db ? 1 : 0); + if (field->has_default_expression()) + pack_expression(&buff, field->default_value, field_nr, 2); + if (field->check_constraint) + pack_expression(&buff, field->check_constraint, field_nr, 3); } } + *buff_arg= buff; DBUG_RETURN(0); } - /* save an empty record on start of formfile */ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, @@ -955,9 +1012,9 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, type= (Field::utype) MTYP_TYPENR(field->unireg_check); - if (field->def) + if (field->default_value && !field->has_default_expression()) { - int res= field->def->save_in_field(regfield, 1); + int res= field->default_value->expr_item->save_in_field(regfield, 1); /* If not ok or warning of level 'note' */ if (res != 0 && res != 3) { diff --git a/sql/unireg.h b/sql/unireg.h index 251597c1884..8e74519862e 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -211,7 +211,7 @@ static inline bool is_binary_frm_header(uchar *head) return head[0] == 254 && head[1] == 1 && head[2] >= FRM_VER - && head[2] <= FRM_VER+4; + && head[2] <= FRM_VER_CURRENT; } #endif diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 6086748b6d0..b4557cfe1b7 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -252,6 +252,9 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx, else thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS; + /* With galera we assume that the master has done the constraint checks */ + thd->variables.option_bits|= OPTION_NO_CHECK_CONSTRAINT_CHECKS; + if (flags & WSREP_FLAG_ISOLATION) { thd->wsrep_apply_toi= true; |