summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc843
1 files changed, 615 insertions, 228 deletions
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;
}
/*