summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMichael Widenius <monty@mariadb.org>2016-06-29 09:14:22 +0200
committerSergei Golubchik <serg@mariadb.org>2016-06-30 11:43:02 +0200
commitdb7edfed17efe6bc3684b0fbacc0b0249e4f0fa2 (patch)
treef1f484057487a73d32f379a5fdaacd53bfa27b5e /sql
parent23d03a1b1e486da353f20964a1b91068bec209c0 (diff)
downloadmariadb-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')
-rw-r--r--sql/field.cc204
-rw-r--r--sql/field.h69
-rw-r--r--sql/ha_partition.cc11
-rw-r--r--sql/ha_partition.h1
-rw-r--r--sql/handler.cc2
-rw-r--r--sql/handler.h12
-rw-r--r--sql/item.cc69
-rw-r--r--sql/item.h72
-rw-r--r--sql/item_func.h100
-rw-r--r--sql/item_row.h2
-rw-r--r--sql/item_strfunc.cc56
-rw-r--r--sql/item_strfunc.h64
-rw-r--r--sql/item_subselect.h4
-rw-r--r--sql/item_sum.h16
-rw-r--r--sql/item_timefunc.cc63
-rw-r--r--sql/item_timefunc.h132
-rw-r--r--sql/item_xmlfunc.cc8
-rw-r--r--sql/item_xmlfunc.h4
-rw-r--r--sql/lex.h3
-rw-r--r--sql/log_event.cc14
-rw-r--r--sql/log_event.h5
-rw-r--r--sql/partition_info.cc3
-rw-r--r--sql/procedure.h4
-rw-r--r--sql/rpl_rli.cc5
-rw-r--r--sql/share/errmsg-utf8.txt24
-rw-r--r--sql/sql_alter.cc1
-rw-r--r--sql/sql_alter.h4
-rw-r--r--sql/sql_base.cc42
-rw-r--r--sql/sql_base.h3
-rw-r--r--sql/sql_class.cc10
-rw-r--r--sql/sql_class.h12
-rw-r--r--sql/sql_const.h2
-rw-r--r--sql/sql_insert.cc150
-rw-r--r--sql/sql_lex.cc81
-rw-r--r--sql/sql_lex.h19
-rw-r--r--sql/sql_load.cc29
-rw-r--r--sql/sql_priv.h3
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_show.cc45
-rw-r--r--sql/sql_table.cc199
-rw-r--r--sql/sql_update.cc30
-rw-r--r--sql/sql_yacc.yy161
-rw-r--r--sql/sys_vars.cc6
-rw-r--r--sql/table.cc843
-rw-r--r--sql/table.h45
-rw-r--r--sql/unireg.cc217
-rw-r--r--sql/unireg.h2
-rw-r--r--sql/wsrep_applier.cc3
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(&ltime);
-
- /* 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, &ltime);
+ /* 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, &ltime);
+ }
*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, &ltime);
+ }
*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(&ltime);
- 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(&ltime);
- 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= &copy->def_read_set;
copy->write_set= &copy->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> &not_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> &not_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> &not_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;