summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc2202
1 files changed, 1575 insertions, 627 deletions
diff --git a/sql/table.cc b/sql/table.cc
index e1edcc0b407..585cb13a86c 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -23,7 +23,6 @@
#include "key.h" // find_ref_key
#include "sql_table.h" // build_table_filename,
// primary_key_name
-#include "sql_trigger.h"
#include "sql_parse.h" // free_items
#include "strfunc.h" // unhex_type2
#include "sql_partition.h" // mysql_unpack_partition,
@@ -31,6 +30,7 @@
#include "sql_acl.h" // *_ACL, acl_getroot_no_password
#include "sql_base.h"
#include "create_options.h"
+#include "sql_trigger.h"
#include <m_ctype.h>
#include "my_md5.h"
#include "my_bit.h"
@@ -40,6 +40,16 @@
#include "discover.h"
#include "mdl.h" // MDL_wait_for_graph_visitor
#include "sql_view.h"
+#include "rpl_filter.h"
+#include "sql_cte.h"
+
+/* For MySQL 5.7 virtual fields */
+#define MYSQL57_GENERATED_FIELD 128
+#define MYSQL57_GCOL_HEADER_SIZE 4
+
+static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
+ TABLE *, String *, Virtual_column_info **, bool *);
+static bool check_vcol_forward_refs(Field *, Virtual_column_info *);
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
@@ -60,7 +70,9 @@ LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
Keyword added as a prefix when parsing the defining expression for a
virtual column read from the column definition saved in the frm file
*/
-LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+
+static int64 last_table_id;
/* Functions defined in this file */
@@ -118,10 +130,7 @@ Default_object_creation_ctx::create_backup_ctx(THD *thd) const
void Default_object_creation_ctx::change_env(THD *thd) const
{
- thd->variables.character_set_client= m_client_cs;
- thd->variables.collation_connection= m_connection_cl;
-
- thd->update_charset();
+ thd->update_charset(m_client_cs, m_connection_cl);
}
/**************************************************************************
@@ -276,7 +285,8 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
SYNOPSIS
alloc_table_share()
- TABLE_LIST Take database and table name from there
+ db Database name
+ table_name Table name
key Table cache key (db \0 table_name \0...)
key_length Length of key
@@ -316,7 +326,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
share->normalized_path.length= path_length;
share->table_category= get_table_category(& share->db, & share->table_name);
share->open_errno= ENOENT;
- share->cached_row_logging_check= -1;
+ /* The following will be fixed in open_table_from_share */
+ share->cached_row_logging_check= 1;
init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
@@ -325,7 +336,16 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
&share->LOCK_share, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
&share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
- tdc_assign_new_table_id(share);
+
+ /*
+ There is one reserved number that cannot be used. Remember to
+ change this when 6-byte global table id's are introduced.
+ */
+ do
+ {
+ share->table_map_id=(ulong) my_atomic_add64_explicit(&last_table_id, 1,
+ MY_MEMORY_ORDER_RELAXED);
+ } while (unlikely(share->table_map_id == ~0UL));
}
DBUG_RETURN(share);
}
@@ -339,7 +359,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
thd thread handle
share Share to fill
key Table_cache_key, as generated from tdc_create_key.
- must start with db name.
+ must start with db name.
key_length Length of key
table_name Table name
path Path to file (possible in lower case) without .frm
@@ -379,9 +399,9 @@ 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= -1;
+ share->cached_row_logging_check= 0; // No row logging
/*
table_map_id is also used for MERGE tables to suppress repeated
@@ -522,7 +542,7 @@ inline bool is_system_table_name(const char *name, uint length)
my_tolower(ci, name[1]) == 'n' &&
my_tolower(ci, name[2]) == 'n' &&
my_tolower(ci, name[3]) == 'o')) ||
-
+
/* mysql.event table */
(my_tolower(ci, name[0]) == 'e' &&
my_tolower(ci, name[1]) == 'v' &&
@@ -697,8 +717,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
When in the future we support others schemes of extending of
secondary keys with components of the primary key we'll have
- to change the type of this flag for an enumeration type.
- */
+ to change the type of this flag for an enumeration type.
+ */
for (i=0 ; i < keys ; i++, keyinfo++)
{
@@ -895,6 +915,236 @@ 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) generated field */
+ bool stored_in_db= 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)++;
+ }
+ }
+}
+
+
+/** Parse TABLE_SHARE::vcol_defs
+
+ unpack_vcol_info_from_frm
+ 5.7
+ byte 1 = 1
+ byte 2,3 = expr length
+ byte 4 = stored_in_db
+ expression
+ 10.1-
+ byte 1 = 1 | 2
+ byte 2 = sql_type ; but TABLE::init_from_binary_frm_image()
+ byte 3 = stored_in_db ; has put expr_length here
+ [byte 4] = optional interval_id for sql_type (if byte 1 == 2)
+ expression
+ 10.2+
+ byte 1 = type
+ byte 2,3 = field_number
+ byte 4,5 = length of expression
+ byte 6 = length of name
+ name
+ expression
+*/
+bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
+ bool *error_reported)
+{
+ CHARSET_INFO *save_character_set_client= thd->variables.character_set_client;
+ CHARSET_INFO *save_collation= thd->variables.collation_connection;
+ Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
+ const uchar *pos= table->s->vcol_defs.str;
+ const uchar *end= pos + table->s->vcol_defs.length;
+ Field **field_ptr= table->field - 1;
+ Field **vfield_ptr= table->vfield;
+ Field **dfield_ptr= table->default_field;
+ Virtual_column_info **check_constraint_ptr= table->check_constraints;
+ sql_mode_t saved_mode= thd->variables.sql_mode;
+ Query_arena backup_arena;
+ Virtual_column_info *vcol= 0;
+ StringBuffer<MAX_FIELD_WIDTH> expr_str;
+ bool res= 1;
+ DBUG_ENTER("parse_vcol_defs");
+
+ if (check_constraint_ptr)
+ memcpy(table->check_constraints + table->s->field_check_constraints,
+ table->s->check_constraints,
+ table->s->table_check_constraints * sizeof(Virtual_column_info*));
+
+ DBUG_ASSERT(table->expr_arena == NULL);
+ /*
+ We need to use CONVENTIONAL_EXECUTION here to ensure that
+ any new items created by fix_fields() are not reverted.
+ */
+ table->expr_arena= new (alloc_root(mem_root, sizeof(Query_arena)))
+ Query_arena(mem_root, Query_arena::STMT_CONVENTIONAL_EXECUTION);
+ if (!table->expr_arena)
+ DBUG_RETURN(1);
+
+ thd->set_n_backup_active_arena(table->expr_arena, &backup_arena);
+ thd->stmt_arena= table->expr_arena;
+ thd->update_charset(&my_charset_utf8mb4_general_ci, table->s->table_charset);
+ expr_str.append(&parse_vcol_keyword);
+ thd->variables.sql_mode &= ~MODE_NO_BACKSLASH_ESCAPES;
+
+ while (pos < end)
+ {
+ uint type, expr_length;
+ if (table->s->mysql_version >= 100202)
+ {
+ uint field_nr, name_length;
+ /* see pack_expression() for how data is stored */
+ type= pos[0];
+ field_nr= uint2korr(pos+1);
+ expr_length= uint2korr(pos+3);
+ name_length= pos[5];
+ pos+= FRM_VCOL_NEW_HEADER_SIZE + name_length;
+ field_ptr= table->field + field_nr;
+ }
+ else
+ {
+ /*
+ see below in ::init_from_binary_frm_image for how data is stored
+ in versions below 10.2 (that includes 5.7 too)
+ */
+ while (*++field_ptr && !(*field_ptr)->vcol_info) /* no-op */;
+ if (!*field_ptr)
+ {
+ open_table_error(table->s, OPEN_FRM_CORRUPTED, 1);
+ goto end;
+ }
+ type= (*field_ptr)->vcol_info->stored_in_db
+ ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL;
+ expr_length= uint2korr(pos+1);
+ if (table->s->mysql_version > 50700 && table->s->mysql_version < 100000)
+ pos+= 4; // MySQL from 5.7
+ else
+ pos+= pos[0] == 2 ? 4 : 3; // MariaDB from 5.2 to 10.1
+ }
+
+ expr_str.length(parse_vcol_keyword.length);
+ expr_str.append((char*)pos, expr_length);
+ thd->where= vcol_type_name(static_cast<enum_vcol_info_type>(type));
+
+ switch (type) {
+ case VCOL_GENERATED_VIRTUAL:
+ case VCOL_GENERATED_STORED:
+ vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ &((*field_ptr)->vcol_info), error_reported);
+ *(vfield_ptr++)= *field_ptr;
+ break;
+ case VCOL_DEFAULT:
+ vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ &((*field_ptr)->default_value),
+ error_reported);
+ *(dfield_ptr++)= *field_ptr;
+ if (vcol && (vcol->flags & (VCOL_NON_DETERMINISTIC | VCOL_SESSION_FUNC)))
+ table->s->non_determinstic_insert= true;
+ break;
+ case VCOL_CHECK_FIELD:
+ vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ &((*field_ptr)->check_constraint),
+ error_reported);
+ *check_constraint_ptr++= (*field_ptr)->check_constraint;
+ break;
+ case VCOL_CHECK_TABLE:
+ vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ check_constraint_ptr, error_reported);
+ check_constraint_ptr++;
+ break;
+ }
+ if (!vcol)
+ goto end;
+ pos+= expr_length;
+ }
+
+ /* Now, initialize CURRENT_TIMESTAMP fields */
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ if (field->has_default_now_unireg_check())
+ {
+ expr_str.length(parse_vcol_keyword.length);
+ expr_str.append(STRING_WITH_LEN("current_timestamp("));
+ expr_str.append_ulonglong(field->decimals());
+ expr_str.append(')');
+ vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ &((*field_ptr)->default_value),
+ error_reported);
+ *(dfield_ptr++)= *field_ptr;
+ if (!field->default_value->expr)
+ goto end;
+ }
+ else if (field->has_update_default_function() && !field->default_value)
+ *(dfield_ptr++)= *field_ptr;
+ }
+
+ if (vfield_ptr)
+ *vfield_ptr= 0;
+
+ if (dfield_ptr)
+ *dfield_ptr= 0;
+
+ if (check_constraint_ptr)
+ *check_constraint_ptr= 0;
+
+ /* Check that expressions aren't refering to not yet initialized fields */
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ if (check_vcol_forward_refs(field, field->vcol_info) ||
+ check_vcol_forward_refs(field, field->check_constraint) ||
+ check_vcol_forward_refs(field, field->default_value))
+ goto end;
+ }
+
+ res=0;
+end:
+ thd->restore_active_arena(table->expr_arena, &backup_arena);
+ thd->stmt_arena= backup_stmt_arena_ptr;
+ if (save_character_set_client)
+ thd->update_charset(save_character_set_client, save_collation);
+ thd->variables.sql_mode= saved_mode;
+ DBUG_RETURN(res);
+}
+
/**
Read data from a binary .frm file image into a TABLE_SHARE
@@ -919,14 +1169,13 @@ 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 extra_rec_buf_length;
+ uint com_length, null_bit_pos, UNINIT_VAR(mysql57_vcol_null_bit_pos), bitmap_count;
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, *UNINIT_VAR(mysql57_vcol_null_pos);
const uchar *disk_buff, *strpos;
ulong pos, record_offset;
ulong rec_buff_length;
@@ -939,7 +1188,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;
@@ -947,14 +1196,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint len;
uint ext_key_parts= 0;
plugin_ref se_plugin= 0;
- keyinfo= &first_keyinfo;
- share->ext_key_parts= 0;
- MEM_ROOT **root_ptr, *old_root;
+ MEM_ROOT *old_root= thd->mem_root;
+ Virtual_column_info **table_check_constraints;
DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
- root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
- old_root= *root_ptr;
- *root_ptr= &share->mem_root;
+ keyinfo= &first_keyinfo;
+ share->ext_key_parts= 0;
+ thd->mem_root= &share->mem_root;
if (write && write_frm_image(frm_image, frm_length))
goto err;
@@ -962,6 +1210,16 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (frm_length < FRM_HEADER_SIZE + FRM_FORMINFO_SIZE)
goto err;
+ share->frm_version= frm_image[2];
+ /*
+ Check if .frm file created by MySQL 5.0. In this case we want to
+ display CHAR fields as CHAR and not as VARCHAR.
+ We do it this way as we want to keep the old frm version to enable
+ MySQL 4.1 to read these files.
+ */
+ if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && frm_image[33] == 5)
+ share->frm_version= FRM_VER_TRUE_VARCHAR;
+
new_field_pack_flag= frm_image[27];
new_frm_ver= (frm_image[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
@@ -1050,16 +1308,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (forminfo + FRM_FORMINFO_SIZE >= frm_image_end)
goto err;
- share->frm_version= frm_image[2];
- /*
- Check if .frm file created by MySQL 5.0. In this case we want to
- display CHAR fields as CHAR and not as VARCHAR.
- We do it this way as we want to keep the old frm version to enable
- MySQL 4.1 to read these files.
- */
- if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && frm_image[33] == 5)
- share->frm_version= FRM_VER_TRUE_VARCHAR;
-
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (frm_image[61] && !share->default_part_plugin)
{
@@ -1087,7 +1335,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint cs_new= upgrade_collation(share->mysql_version, cs_org);
if (cs_org != cs_new)
share->incompatible_version|= HA_CREATE_USED_CHARSET;
-
+
share->avg_row_length= uint4korr(frm_image+34);
share->transactional= (ha_choice)
enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX);
@@ -1101,6 +1349,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)
{
@@ -1338,8 +1587,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin))
goto err; // wrong engine (someone changed the frm under our feet?)
- extra_rec_buf_length= uint2korr(frm_image+59);
- rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length);
+ rec_buff_length= ALIGN_SIZE(share->reclength + 1);
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length)))
goto err; /* purecov: inspected */
@@ -1359,8 +1607,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)
{
@@ -1371,42 +1619,41 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, keys,n_length,int_length, com_length, vcol_screen_length));
+ if (!multi_alloc_root(&share->mem_root,
+ &share->field, (uint)(share->fields+1)*sizeof(Field*),
+ &share->intervals, (uint)interval_count*sizeof(TYPELIB),
+ &share->check_constraints, (uint) share->table_check_constraints * sizeof(Virtual_column_info*),
+ &interval_array, (uint) (share->fields+interval_parts+ keys+3)*sizeof(char *),
+ &names, (uint) (n_length+int_length),
+ &comment_pos, (uint) com_length,
+ &vcol_screen_pos, vcol_screen_length,
+ NullS))
- if (!(field_ptr = (Field **)
- alloc_root(&share->mem_root,
- (uint) ((share->fields+1)*sizeof(Field*)+
- interval_count*sizeof(TYPELIB)+
- (share->fields+interval_parts+
- keys+3)*sizeof(char *)+
- (n_length+int_length+com_length+
- vcol_screen_length)))))
- goto err; /* purecov: inspected */
+ goto err;
- share->field= field_ptr;
+ field_ptr= share->field;
+ table_check_constraints= share->check_constraints;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length+
vcol_screen_length));
strpos= disk_buff+pos;
- share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
- interval_array= (const char **) (share->intervals+interval_count);
- names= (char*) (interval_array+share->fields+interval_parts+keys+3);
if (!interval_count)
share->intervals= 0; // For better debugging
- memcpy((char*) names, strpos+(share->fields*field_pack_length),
- (uint) (n_length+int_length));
- comment_pos= names+(n_length+int_length);
+
+ share->vcol_defs.str= vcol_screen_pos;
+ share->vcol_defs.length= vcol_screen_length;
+
+ memcpy(names, strpos+(share->fields*field_pack_length), 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);
memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length,
vcol_screen_length);
fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
goto err;
- fix_type_pointers(&interval_array, share->intervals, interval_count,
- &names);
+ fix_type_pointers(&interval_array, share->intervals, interval_count, &names);
{
/* Set ENUM and SET lengths */
@@ -1468,6 +1715,21 @@ 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;
+ mysql57_vcol_null_pos= null_pos;
+ mysql57_vcol_null_bit_pos= null_bit_pos;
+ mysql57_calculate_null_position(share, &mysql57_vcol_null_pos,
+ &mysql57_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;
@@ -1478,8 +1740,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
LEX_STRING comment;
Virtual_column_info *vcol_info= 0;
- bool fld_stored_in_db= TRUE;
uint gis_length, gis_decimals, srid= 0;
+ Field::utype unireg_check;
if (new_frm_ver >= 3)
{
@@ -1536,7 +1798,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
if (!interval_nr) // Expect non-null expression
goto err;
- /*
+ /*
+ MariaDB version 10.0 version.
The interval_id byte in the .frm file stores the length of the
expression statement for a virtual column.
*/
@@ -1556,38 +1819,59 @@ 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;
+
+ /*
+ MySQL 5.7 generated fields
+
+ byte 1 = 1
+ byte 2,3 = expr length
+ byte 4 = stored_in_db
+ byte 5.. = expr
+ */
+ 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= vcol_screen_pos[3];
+ vcol_info->utf8= 0;
+ 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
- byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
- [byte 4] = optional interval_id for sql_type (only if byte 1 == 2)
+ byte 3 = flags. 1 for stored_in_db
+ [byte 4] = optional interval_id for sql_type (if byte 1 == 2)
next byte ... = virtual column expression (text data)
*/
- vcol_info= new Virtual_column_info();
+
+ vcol_info= new (&share->mem_root) Virtual_column_info();
bool opt_interval_id= (uint)vcol_screen_pos[0] == 2;
field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
if (opt_interval_id)
interval_nr= (uint)vcol_screen_pos[3];
else if ((uint)vcol_screen_pos[0] != 1)
goto err;
-
- fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
+ bool stored= vcol_screen_pos[2] & 1;
+ vcol_info->stored_in_db= stored;
+ vcol_info->set_vcol_type(stored ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL);
vcol_expr_length= vcol_info_length -
- (uint)(FRM_VCOL_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),
- vcol_expr_length)))
- goto err;
- if (opt_interval_id)
- interval_nr= (uint) vcol_screen_pos[3];
- vcol_info->expr_str.length= vcol_expr_length;
+ (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id));
+ vcol_info->utf8= 0; // before 10.2.1 the charset was unknown
+ int2store(vcol_screen_pos+1, vcol_expr_length); // for parse_vcol_defs()
vcol_screen_pos+= vcol_info_length;
- share->vfields++;
+ share->virtual_fields++;
}
}
else
@@ -1606,7 +1890,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/*
Try to choose the best 4.1 type:
- for 4.0 "CHAR(N) BINARY" or "VARCHAR(N) BINARY"
- try to find a binary collation for character set.
+ try to find a binary collation for character set.
- for other types (e.g. BLOB) just use my_charset_bin.
*/
if (!f_is_blob(pack_flag))
@@ -1624,13 +1908,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bzero((char*) &comment, sizeof(comment));
}
+ /* Remove >32 decimals from old files */
+ if (share->mysql_version < 100200)
+ pack_flag&= ~FIELDFLAG_LONG_DECIMAL;
+
if (interval_nr && charset->mbminlen > 1)
{
/* Unescape UCS2 intervals from HEX notation */
TYPELIB *interval= share->intervals + interval_nr - 1;
unhex_type2(interval);
}
-
+
#ifndef TO_BE_DELETED_ON_PRODUCTION
if (field_type == MYSQL_TYPE_NEWDECIMAL && !share->mysql_version)
{
@@ -1658,27 +1946,35 @@ 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, mysql57_vcol_null_pos);
+ swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
+ }
+
+ /* Convert pre-10.2.2 timestamps to use Field::default_value */
+ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type);
*field_ptr= reg_field=
- make_field(share, &share->mem_root, record+recpos,
- (uint32) field_length,
- null_pos, null_bit_pos,
- pack_flag,
- field_type,
- charset,
- geom_type, srid,
- (Field::utype) MTYP_TYPENR(unireg_type),
- (interval_nr ?
- share->intervals+interval_nr-1 :
- (TYPELIB*) 0),
+ make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
+ null_pos, null_bit_pos, pack_flag, field_type, charset,
+ geom_type, srid, unireg_check,
+ (interval_nr ? share->intervals+interval_nr-1 : NULL),
share->fieldnames.type_names[i]);
if (!reg_field) // Not supported field type
goto err;
+ if (unireg_check == Field::TIMESTAMP_DNUN_FIELD ||
+ unireg_check == Field::TIMESTAMP_DN_FIELD)
+ {
+ reg_field->default_value= new (&share->mem_root) Virtual_column_info();
+ reg_field->default_value->set_vcol_type(VCOL_DEFAULT);
+ reg_field->default_value->stored_in_db= 1;
+ share->default_expressions++;
+ }
reg_field->field_index= i;
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
- reg_field->stored_in_db= fld_stored_in_db;
if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
null_bits_are_used= 1;
@@ -1693,6 +1989,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!(null_bit_pos= (null_bit_pos + 1) & 7))
null_pos++;
}
+
+ if (vcol_info)
+ {
+ vcol_info->name.str= const_cast<char*>(reg_field->field_name);
+ vcol_info->name.length = strlen(reg_field->field_name);
+ if (mysql57_null_bits && !vcol_info->stored_in_db)
+ {
+ /* MySQL 5.7 has null bits last */
+ swap_variables(uchar*, null_pos, mysql57_vcol_null_pos);
+ swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
+ }
+ }
+
if (f_no_default(pack_flag))
reg_field->flags|= NO_DEFAULT_VALUE_FLAG;
@@ -1701,21 +2010,32 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (use_hash && my_hash_insert(&share->name_hash, (uchar*) field_ptr))
goto err;
- if (!reg_field->stored_in_db)
+ if (!reg_field->stored_in_db())
{
share->stored_fields--;
if (share->stored_rec_length>=recpos)
share->stored_rec_length= recpos-1;
}
- if (reg_field->has_insert_default_function() ||
- reg_field->has_update_default_function())
- ++share->default_fields;
+ if (reg_field->has_update_default_function())
+ {
+ has_update_default_function= 1;
+ if (!reg_field->default_value)
+ 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, mysql57_vcol_null_pos);
+ swap_variables(uint, null_bit_pos, mysql57_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)
{
@@ -1787,6 +2107,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
}
+ key_first_info= keyinfo;
for (uint key=0 ; key < keys ; key++,keyinfo++)
{
uint usable_parts= 0;
@@ -1804,9 +2125,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
keyinfo->name_length+1);
}
- if (!key)
- key_first_info= keyinfo;
-
if (ext_key_parts > share->key_parts && key)
{
KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
@@ -1886,7 +2204,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
keyinfo->key_part= new_key_part;
}
}
-
+
/* Fix fulltext keys for old .frm files */
if (share->key_info[key].flags & HA_FULLTEXT)
share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
@@ -2060,6 +2378,99 @@ 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)
+ {
+ uchar *vcol_screen_end= vcol_screen_pos + vcol_screen_length;
+
+ /* Skip header */
+ vcol_screen_pos+= FRM_VCOL_NEW_BASE_SIZE;
+ share->vcol_defs.str+= FRM_VCOL_NEW_BASE_SIZE;
+ share->vcol_defs.length-= FRM_VCOL_NEW_BASE_SIZE;
+
+ /*
+ Read virtual columns, default values and check constraints
+ See pack_expression() for how data is stored
+ */
+ while (vcol_screen_pos < vcol_screen_end)
+ {
+ Virtual_column_info *vcol_info;
+ uint type= (uint) vcol_screen_pos[0];
+ uint field_nr= uint2korr(vcol_screen_pos+1);
+ uint expr_length= uint2korr(vcol_screen_pos+3);
+ uint name_length= (uint) vcol_screen_pos[5];
+
+ if (!(vcol_info= new (&share->mem_root) Virtual_column_info()))
+ goto err;
+
+ /* The following can only be true for check_constraints */
+
+ if (field_nr != UINT_MAX16)
+ {
+ DBUG_ASSERT(field_nr < share->fields);
+ reg_field= share->field[field_nr];
+ }
+ else
+ {
+ reg_field= 0;
+ DBUG_ASSERT(name_length);
+ }
+
+ vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE;
+ vcol_info->set_vcol_type((enum_vcol_info_type) type);
+ vcol_info->name.length= name_length;
+ if (name_length)
+ vcol_info->name.str= strmake_root(&share->mem_root,
+ (char*)vcol_screen_pos, name_length);
+ else
+ {
+ vcol_info->name.str= const_cast<char*>(reg_field->field_name);
+ vcol_info->name.length = strlen(reg_field->field_name);
+ }
+ vcol_screen_pos+= name_length + expr_length;
+
+ switch (type) {
+ case VCOL_GENERATED_VIRTUAL:
+ {
+ uint recpos;
+ reg_field->vcol_info= vcol_info;
+ share->virtual_fields++;
+ share->stored_fields--;
+ if (reg_field->flags & BLOB_FLAG)
+ share->virtual_not_stored_blob_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 VCOL_GENERATED_STORED:
+ vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->vcol_info);
+ reg_field->vcol_info= vcol_info;
+ share->virtual_fields++;
+ break;
+ case VCOL_DEFAULT:
+ vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->default_value);
+ reg_field->default_value= vcol_info;
+ share->default_expressions++;
+ break;
+ case VCOL_CHECK_FIELD:
+ DBUG_ASSERT(!reg_field->check_constraint);
+ reg_field->check_constraint= vcol_info;
+ share->field_check_constraints++;
+ break;
+ case VCOL_CHECK_TABLE:
+ *(table_check_constraints++)= vcol_info;
+ break;
+ }
+ }
+ }
+ DBUG_ASSERT((uint) (table_check_constraints - share->check_constraints) ==
+ (uint) (share->table_check_constraints -
+ share->field_check_constraints));
+
if (options)
{
DBUG_ASSERT(options_len);
@@ -2102,7 +2513,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
the correct null_bytes can now be set, since bitfields have been taken
into account
*/
- share->null_bytes= (null_pos - (uchar*) null_flags +
+ share->null_bytes= (uint)(null_pos - (uchar*) null_flags +
(null_bit_pos + 7) / 8);
share->last_null_bit_pos= null_bit_pos;
share->null_bytes_for_compare= null_bits_are_used ? share->null_bytes : 0;
@@ -2111,11 +2522,33 @@ 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)
+ {
+ feature_check_constraint++;
+ 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
@@ -2126,7 +2559,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->db_plugin= se_plugin;
share->error= OPEN_FRM_OK;
thd->status_var.opened_shares++;
- *root_ptr= old_root;
+ thd->mem_root= old_root;
DBUG_RETURN(0);
err:
@@ -2139,7 +2572,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!thd->is_error())
open_table_error(share, OPEN_FRM_CORRUPTED, share->open_errno);
- *root_ptr= old_root;
+ thd->mem_root= old_root;
DBUG_RETURN(HA_ERR_NOT_A_TABLE);
}
@@ -2186,7 +2619,7 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
const char *sql, size_t sql_length)
{
- ulonglong saved_mode= thd->variables.sql_mode;
+ sql_mode_t saved_mode= thd->variables.sql_mode;
CHARSET_INFO *old_cs= thd->variables.character_set_client;
Parser_state parser_state;
bool error;
@@ -2200,7 +2633,6 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
handlerton *hton= plugin_hton(db_plugin);
LEX_CUSTRING frm= {0,0};
LEX_STRING db_backup= { thd->db, thd->db_length };
-
DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string");
/*
@@ -2310,28 +2742,63 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
}
-/*
- @brief
- Clear GET_FIXED_FIELDS_FLAG in all fields of a table
+static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_vcol_expr");
- @param
- table The table for whose fields the flags are to be cleared
+ const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
- @note
- This routine is used for error handling purposes.
+ int error= vcol->expr->fix_fields(thd, &vcol->expr);
- @return
- none
-*/
+ thd->mark_used_columns= save_mark_used_columns;
+
+ if (unlikely(error))
+ {
+ StringBuffer<MAX_FIELD_WIDTH> str;
+ vcol->print(&str);
+ my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), str.c_ptr_safe());
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/** rerun fix_fields for vcols that returns time- or session- dependent values
-static void clear_field_flag(TABLE *table)
+ @note this is done for all vcols for INSERT/UPDATE/DELETE,
+ and only as needed for SELECTs.
+*/
+bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol)
{
- Field **ptr;
- DBUG_ENTER("clear_field_flag");
+ DBUG_ENTER("fix_session_vcol_expr");
+ if (!(vcol->flags & (VCOL_TIME_FUNC|VCOL_SESSION_FUNC)))
+ DBUG_RETURN(0);
- for (ptr= table->field; *ptr; ptr++)
- (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG);
- DBUG_VOID_RETURN;
+ vcol->expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0);
+ DBUG_ASSERT(!vcol->expr->fixed);
+ DBUG_RETURN(fix_vcol_expr(thd, vcol));
+}
+
+
+/** invoke fix_session_vcol_expr for a vcol
+
+ @note this is called for generated column or a DEFAULT expression from
+ their corresponding fix_fields on SELECT.
+*/
+bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
+ Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_session_vcol_expr_for_read");
+ TABLE_LIST *tl= field->table->pos_in_table_list;
+ if (!tl || tl->lock_type >= TL_WRITE_ALLOW_WRITE)
+ DBUG_RETURN(0);
+ Security_context *save_security_ctx= thd->security_ctx;
+ if (tl->security_ctx)
+ thd->security_ctx= tl->security_ctx;
+ bool res= fix_session_vcol_expr(thd, vcol);
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(res);
}
@@ -2339,264 +2806,174 @@ static void clear_field_flag(TABLE *table)
@brief
Perform semantic analysis of the defining expression for a virtual column
- @param
- thd The thread object
- @param
- table The table containing the virtual column
- @param
- vcol_field The virtual field whose defining expression is to be analyzed
+ @param thd The thread object
+ @param table The table containing the virtual column
+ @param field Field if this is a DEFAULT or AS, otherwise NULL
+ @param vcol The Virtual_column object
+
@details
The function performs semantic analysis of the defining expression for
the virtual column vcol_field. The expression is used to compute the
values of this column.
- @note
- The function exploits the fact that the fix_fields method sets the flag
- GET_FIXED_FIELDS_FLAG for all fields in the item tree.
- This flag must always be unset before returning from this function
- since it is used for other purposes as well.
-
@retval
TRUE An error occurred, something was wrong with the function
@retval
FALSE Otherwise
*/
-bool fix_vcol_expr(THD *thd,
- TABLE *table,
- Field *vcol_field)
+static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
+ Virtual_column_info *vcol)
{
- Virtual_column_info *vcol_info= vcol_field->vcol_info;
- Item* func_expr= vcol_info->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;
+ Item* func_expr= vcol->expr;
+ DBUG_ENTER("fix_and_check_vcol_expr");
+ DBUG_PRINT("info", ("vcol: %p", vcol));
DBUG_ASSERT(func_expr);
- DBUG_ENTER("fix_vcol_expr");
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ if (func_expr->fixed)
+ DBUG_RETURN(0); // nothing to do
- save_where= thd->where;
- thd->where= "virtual column function";
+ if (fix_vcol_expr(thd, vcol))
+ DBUG_RETURN(1);
- /* 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 */
+ if (vcol->flags)
+ DBUG_RETURN(0); // already checked, no need to do it again
- if (unlikely(error))
- {
- DBUG_PRINT("info",
- ("Field in virtual column expression does not belong to the table"));
- goto end;
- }
- thd->where= save_where;
+ /* fix_fields could've changed the expression */
+ func_expr= vcol->expr;
+
+ /* this was checked in check_expression(), but the frm could be mangled... */
if (unlikely(func_expr->result_type() == ROW_RESULT))
{
- my_error(ER_ROW_EXPR_FOR_VCOL, MYF(0));
- goto end;
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ DBUG_RETURN(1);
}
-#ifdef PARANOID
+
/*
Walk through the Item tree checking if all items are valid
- to be part of the virtual column
+ to be part of the virtual column
*/
- error= func_expr->walk(&Item::check_vcol_func_processor, 0, NULL);
- if (error)
- {
- my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name);
- goto end;
- }
-#endif
- if (unlikely(func_expr->const_item()))
+ Item::vcol_func_processor_result res;
+ res.errors= 0;
+
+ int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
+ if (error || (res.errors & VCOL_IMPOSSIBLE))
{
- my_error(ER_CONST_EXPR_IN_VCOL, MYF(0));
- goto end;
+ // this can only happen if the frm was corrupted
+ my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
+ vcol->get_vcol_type_name(), vcol->name.str);
+ DBUG_RETURN(1);
}
- /* Ensure that this virtual column is not based on another virtual field. */
- ptr= table->field;
- while ((field= *(ptr++)))
+ else if (res.errors & VCOL_AUTO_INC)
{
- if ((field->flags & GET_FIXED_FIELDS_FLAG) &&
- (field->vcol_info))
- {
- my_error(ER_VCOL_BASED_ON_VCOL, MYF(0));
- goto end;
- }
- }
- result= FALSE;
+ /*
+ An auto_increment field may not be used in an expression for
+ a check constraint, a default value or a generated column
-end:
+ Note that this error condition is not detected during parsing
+ of the statement because the field item does not have a field
+ pointer at that time
+ */
+ myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_JUST_WARNING : 0;
+ my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(warn),
+ "AUTO_INCREMENT", vcol->get_vcol_type_name(), res.name);
+ if (!warn)
+ DBUG_RETURN(1);
+ }
+ vcol->flags= res.errors;
- /* Clear GET_FIXED_FIELDS_FLAG for the fields of the table */
- clear_field_flag(table);
+ if (vcol->flags & VCOL_SESSION_FUNC)
+ table->s->vcols_need_refixing= true;
- table->get_fields_in_item_tree= FALSE;
- thd->mark_used_columns= save_mark_used_columns;
- table->map= 0; //Restore old value
-
- DBUG_RETURN(result);
+ DBUG_RETURN(0);
}
+
/*
@brief
Unpack the definition of a virtual column from its linear representation
- @param
- thd The thread object
- @param
- mem_root The mem_root object where to allocated memory
- @param
- table The table containing the virtual column
- @param
- field The field for the virtual
- @param
- vcol_expr The string representation of the defining expression
- @param[out]
- error_reported The flag to inform the caller that no other error
- messages are to be generated
+ @param thd The thread object
+ @param mem_root Where to allocate memory
+ @param table The table containing the virtual column
+ @param field Field if this is a DEFAULT or AS, otherwise NULL
+ @param vcol The Virtual_column object
+ @param[out] error_reported Flag to inform the caller that no
+ other error messages are to be generated
@details
- The function takes string representation 'vcol_expr' of the defining
- expression for the virtual field 'field' of the table 'table' and
- parses it, building an item object for it. The pointer to this item is
- placed into in field->vcol_info.expr_item. After this the function performs
- semantic analysis of the item by calling the the function fix_vcol_expr.
- Since the defining expression is part of the table definition the item for
- it is created in table->memroot within 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_and_check_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.
+ Before passing 'vcol_expr' to the parser the function wraps it in
+ parentheses and prepends a special keyword.
- @retval
- FALSE If a success
- @retval
- TRUE Otherwise
+ @retval Virtual_column_info* Success
+ @retval 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)
+
+static Virtual_column_info *
+unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table,
+ String *expr_str, Virtual_column_info **vcol_ptr,
+ bool *error_reported)
{
- bool rc;
- char *vcol_expr_str;
- int str_len;
- CHARSET_INFO *old_character_set_client;
- Query_arena *backup_stmt_arena_ptr;
- Query_arena backup_arena;
- Query_arena *vcol_arena= 0;
Create_field vcol_storage; // placeholder for vcol_info
Parser_state parser_state;
+ Virtual_column_info *vcol= *vcol_ptr, *vcol_info= 0;
LEX *old_lex= thd->lex;
LEX lex;
+ bool error;
DBUG_ENTER("unpack_vcol_info_from_frm");
- DBUG_ASSERT(vcol_expr);
-
- old_character_set_client= thd->variables.character_set_client;
- backup_stmt_arena_ptr= thd->stmt_arena;
- /*
- Step 1: Construct the input string for the parser.
- The string to be parsed has to be of the following format:
- "PARSE_VCOL_EXPR (<expr_string_from_frm>)".
- */
+ DBUG_ASSERT(vcol->expr == NULL);
- if (!(vcol_expr_str= (char*) alloc_root(mem_root,
- vcol_expr->length +
- parse_vcol_keyword.length + 3)))
- {
- DBUG_RETURN(TRUE);
- }
- memcpy(vcol_expr_str,
- (char*) parse_vcol_keyword.str,
- parse_vcol_keyword.length);
- str_len= parse_vcol_keyword.length;
- memcpy(vcol_expr_str + str_len, "(", 1);
- str_len++;
- memcpy(vcol_expr_str + str_len,
- (char*) vcol_expr->str,
- vcol_expr->length);
- str_len+= vcol_expr->length;
- memcpy(vcol_expr_str + str_len, ")", 1);
- str_len++;
- memcpy(vcol_expr_str + str_len, "\0", 1);
- str_len++;
-
- if (parser_state.init(thd, vcol_expr_str, str_len))
- goto err;
-
- /*
- Step 2: Setup thd for parsing.
- */
- vcol_arena= table->expr_arena;
- if (!vcol_arena)
- {
- /*
- We need to use CONVENTIONAL_EXECUTION here to ensure that
- any new items created by fix_fields() are not reverted.
- */
- Query_arena expr_arena(mem_root,
- Query_arena::STMT_CONVENTIONAL_EXECUTION);
- if (!(vcol_arena= (Query_arena *) alloc_root(mem_root,
- sizeof(Query_arena))))
- goto err;
- *vcol_arena= expr_arena;
- table->expr_arena= vcol_arena;
- }
- thd->set_n_backup_active_arena(vcol_arena, &backup_arena);
- thd->stmt_arena= vcol_arena;
+ if (parser_state.init(thd, expr_str->c_ptr_safe(), expr_str->length()))
+ goto end;
if (init_lex_with_single_table(thd, table, &lex))
- goto err;
+ goto end;
- lex.parse_vcol_expr= TRUE;
+ lex.parse_vcol_expr= true;
lex.last_field= &vcol_storage;
- /*
- Step 3: Use the parser to build an Item object from vcol_expr_str.
- */
- if (parse_sql(thd, &parser_state, NULL))
- {
- goto err;
- }
- /* From now on use vcol_info generated by the parser. */
- field->vcol_info= vcol_storage.vcol_info;
+ error= parse_sql(thd, &parser_state, NULL);
+ if (error)
+ goto end;
- /* Validate the Item tree. */
- if (fix_vcol_expr(thd, table, field))
+ vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type());
+ vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
+ vcol_storage.vcol_info->name= vcol->name;
+ vcol_storage.vcol_info->utf8= vcol->utf8;
+ if (!fix_and_check_vcol_expr(thd, table, vcol_storage.vcol_info))
{
- *error_reported= TRUE;
- field->vcol_info= 0;
- goto err;
+ *vcol_ptr= vcol_info= vcol_storage.vcol_info; // Expression ok
+ DBUG_ASSERT(vcol_info->expr);
+ goto end;
}
- rc= FALSE;
- goto end;
+ *error_reported= TRUE;
-err:
- rc= TRUE;
- thd->free_items();
end:
- thd->stmt_arena= backup_stmt_arena_ptr;
- if (vcol_arena)
- thd->restore_active_arena(vcol_arena, &backup_arena);
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);
}
-/*
- Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
-*/
+static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
+{
+ bool res= vcol &&
+ vcol->expr->walk(&Item::check_field_expression_processor, 0,
+ field);
+ return res;
+}
/*
Open a table based on a TABLE_SHARE
@@ -2632,11 +3009,11 @@ 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,
- share->table_name.str, (long) outparam));
+ DBUG_PRINT("enter",("name: '%s.%s' form: %p", share->db.str,
+ share->table_name.str, outparam));
thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VIEW; // not a view
@@ -2660,7 +3037,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
outparam->quick_keys.init();
outparam->covering_keys.init();
- outparam->merge_keys.init();
outparam->intersect_keys.init();
outparam->keys_in_use_for_query.init();
@@ -2689,7 +3065,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
records++;
if (!(record= (uchar*) alloc_root(&outparam->mem_root,
- share->rec_buff_length * records)))
+ share->rec_buff_length * records)))
goto err; /* purecov: inspected */
MEM_NOACCESS(record, share->rec_buff_length * records);
@@ -2761,7 +3137,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
key_info->key_part= key_part;
key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts :
- key_info->user_defined_key_parts) ;
+ key_info->user_defined_key_parts) ;
for ( ; key_part < key_part_end; key_part++)
{
Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
@@ -2786,54 +3162,39 @@ 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 +
+ share->field_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. */
- for (field_ptr= outparam->field; *field_ptr; field_ptr++)
+ 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;
+
+ if (parse_vcol_defs(thd, &outparam->mem_root, outparam, &error_reported))
{
- if (share->vfields && (*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))
- {
- error= OPEN_FRM_CORRUPTED;
- goto err;
- }
- *(vfield_ptr++)= *field_ptr;
- }
- if (share->default_fields &&
- ((*field_ptr)->has_insert_default_function() ||
- (*field_ptr)->has_update_default_function()))
- *(dfield_ptr++)= *field_ptr;
+ error= OPEN_FRM_CORRUPTED;
+ goto err;
}
- if (share->vfields)
- *vfield_ptr= 0; // End marker
- if (share->default_fields)
- *dfield_ptr= 0; // End marker
+
+ /* Update to use trigger fields */
+ switch_defaults_to_nullable_trigger_fields(outparam);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -2904,7 +3265,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)))
{
@@ -2917,14 +3278,10 @@ partititon_err:
/* Allocate bitmaps */
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;
+ bitmap_count= 7;
+ if (share->virtual_fields)
bitmap_count++;
- }
+
if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root,
bitmap_size * bitmap_count)))
goto err;
@@ -2935,13 +3292,21 @@ 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 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;
}
+
+ 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;
@@ -2960,13 +3325,8 @@ partititon_err:
/* The table struct is now initialized; Open the table */
if (db_stat)
{
- if (db_stat & HA_OPEN_TEMPORARY)
- ha_open_flags|= HA_OPEN_TMP_TABLE;
- else if ((db_stat & HA_WAIT_IF_LOCKED) ||
- (specialflag & SPECIAL_WAIT_IF_LOCKED))
+ if (specialflag & SPECIAL_WAIT_IF_LOCKED)
ha_open_flags|= HA_OPEN_WAIT_IF_LOCKED;
- else if (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO))
- ha_open_flags|= HA_OPEN_ABORT_IF_LOCKED;
else
ha_open_flags|= HA_OPEN_IGNORE_IF_LOCKED;
@@ -2996,11 +3356,13 @@ partititon_err:
if (share->db_type()->discover_table &&
(ha_err == ENOENT || ha_err == HA_ERR_NO_SUCH_TABLE))
error= OPEN_FRM_DISCOVER;
-
+
goto err;
}
}
+ outparam->mark_columns_used_by_virtual_fields();
+
if (share->table_category == TABLE_CATEGORY_LOG)
{
outparam->no_replicate= TRUE;
@@ -3017,6 +3379,9 @@ partititon_err:
outparam->no_replicate= FALSE;
}
+ if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str))
+ outparam->s->cached_row_logging_check= 0; // No row based replication
+
/* Increment the opened_tables counter, only when open flags set. */
if (db_stat)
thd->status_var.opened_tables++;
@@ -3035,6 +3400,8 @@ partititon_err:
outparam->file= 0; // For easier error checking
outparam->db_stat=0;
thd->lex->context_analysis_only= save_context_analysis_only;
+ if (outparam->expr_arena)
+ outparam->expr_arena->free_items();
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
outparam->alias.free();
DBUG_RETURN (error);
@@ -3047,21 +3414,16 @@ partititon_err:
SYNOPSIS
closefrm()
table TABLE object to free
- free_share Is 1 if we also want to free table_share
*/
-int closefrm(register TABLE *table, bool free_share)
+int closefrm(TABLE *table)
{
int error=0;
DBUG_ENTER("closefrm");
- DBUG_PRINT("enter", ("table: 0x%lx", (long) table));
+ DBUG_PRINT("enter", ("table: %p", table));
if (table->db_stat)
- {
- if (table->s->deleting)
- table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
error=table->file->ha_close();
- }
table->alias.free();
if (table->expr_arena)
table->expr_arena->free_items();
@@ -3084,13 +3446,6 @@ int closefrm(register TABLE *table, bool free_share)
table->part_info= 0;
}
#endif
- if (free_share)
- {
- if (table->s->tmp_table == NO_TMP_TABLE)
- tdc_release_share(table->s);
- else
- free_table_share(table->s);
- }
free_root(&table->mem_root, MYF(0));
DBUG_RETURN(error);
}
@@ -3098,7 +3453,7 @@ int closefrm(register TABLE *table, bool free_share)
/* Deallocate temporary blob storage */
-void free_blobs(register TABLE *table)
+void free_blobs(TABLE *table)
{
uint *ptr, *end;
for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
@@ -3375,7 +3730,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_length == 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);
@@ -3426,15 +3782,15 @@ 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->check_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);
int4store(fileinfo+55, create_info->extra_size);
/*
- 59-60 is reserved for extra_rec_buf_length,
+ 59-60 is unused since 10.2.4
61 for default_part_db_type
*/
int2store(fileinfo+62, create_info->key_block_size);
@@ -3488,11 +3844,11 @@ rename_file_ext(const char * from,const char * to,const char * ext)
bool get_field(MEM_ROOT *mem, Field *field, String *res)
{
- char buff[MAX_FIELD_WIDTH], *to;
- String str(buff,sizeof(buff),&my_charset_bin);
+ char *to;
+ StringBuffer<MAX_FIELD_WIDTH> str;
bool rc;
THD *thd= field->get_thd();
- ulonglong sql_mode_backup= thd->variables.sql_mode;
+ sql_mode_t sql_mode_backup= thd->variables.sql_mode;
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
field->val_str(&str);
@@ -3772,13 +4128,13 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
is backward compatible.
*/
}
- char buffer[1024];
+ else
+ {
+ StringBuffer<1024> sql_type(system_charset_info);
+ sql_type.extra_allocation(256); // Allocate min 256 characters at once
for (i=0 ; i < table_def->count; i++, field_def++)
{
- String sql_type(buffer, sizeof(buffer), system_charset_info);
sql_type.length(0);
- /* Allocate min 256 characters at once */
- sql_type.extra_allocation(256);
if (i < table->s->fields)
{
Field *field= table->field[i];
@@ -3859,6 +4215,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
error= TRUE;
}
}
+ }
if (table_def->primary_key_parts)
{
@@ -3977,7 +4334,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
tdc->all_tables_refs++;
mysql_mutex_unlock(&tdc->LOCK_table_share);
- TDC_element::All_share_tables_list::Iterator tables_it(tdc->all_tables);
+ All_share_tables_list::Iterator tables_it(tdc->all_tables);
/*
In case of multiple searches running in parallel, avoid going
@@ -4167,7 +4524,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
(*f_ptr)->cond_selectivity= 1.0;
}
- DBUG_ASSERT(key_read == 0);
+ DBUG_ASSERT(!file->keyread_enabled());
restore_record(this, s->default_values);
@@ -4370,7 +4727,7 @@ bool TABLE_LIST::setup_underlying(THD *thd)
if (!view || (!field_translation && merge_underlying_list))
{
SELECT_LEX *select= get_single_select();
-
+
if (create_field_translation(thd))
DBUG_RETURN(TRUE);
@@ -4486,13 +4843,14 @@ bool TABLE_LIST::single_table_updatable()
{
if (!updatable)
return false;
- if (view_tables && view_tables->elements == 1)
+ if (view && view->select_lex.table_list.elements == 1)
{
/*
We need to check deeply only single table views. Multi-table views
will be turned to multi-table updates and then checked by leaf tables
*/
- return view_tables->head()->single_table_updatable();
+ return (((TABLE_LIST *)view->select_lex.table_list.first)->
+ single_table_updatable());
}
return true;
}
@@ -4637,7 +4995,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
void TABLE_LIST::hide_view_error(THD *thd)
{
- if (thd->killed || thd->get_internal_handler())
+ if ((thd->killed && !thd->is_error())|| thd->get_internal_handler())
return;
/* Hide "Unknown column" or "Unknown function" error */
DBUG_ASSERT(thd->is_error());
@@ -4723,7 +5081,7 @@ void TABLE_LIST::cleanup_items()
/*
- check CHECK OPTION condition
+ check CHECK OPTION condition both for view and underlying table
SYNOPSIS
TABLE_LIST::view_check_option()
@@ -4735,10 +5093,12 @@ void TABLE_LIST::cleanup_items()
VIEW_CHECK_SKIP FAILED, but continue
*/
+
int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
{
if (check_option)
{
+ /* VIEW's CHECK OPTION CLAUSE */
Counting_error_handler ceh;
thd->push_internal_handler(&ceh);
bool res= check_option->val_int() == 0;
@@ -4748,20 +5108,63 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
if (res)
{
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);
+ my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0),
+ name_db, name_table);
+ return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
+ }
+ }
+ return table->verify_constraints(ignore_failure);
+}
+
+
+int TABLE::verify_constraints(bool ignore_failure)
+{
+ /*
+ We have to check is_error() first as we are checking it for each
+ constraint to catch fatal warnings.
+ */
+ if (in_use->is_error())
+ return (VIEW_CHECK_ERROR);
+
+ /* 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++)
+ {
+ /*
+ yes! NULL is ok.
+ see 4.23.3.4 Table check constraints, part 2, SQL:2016
+ */
+ if (((*chk)->expr->val_int() == 0 && !(*chk)->expr->null_value) ||
+ in_use->is_error())
{
- 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);
+ StringBuffer<MAX_FIELD_WIDTH> field_error(system_charset_info);
+ enum_vcol_info_type vcol_type= (*chk)->get_vcol_type();
+ DBUG_ASSERT(vcol_type == VCOL_CHECK_TABLE ||
+ vcol_type == VCOL_CHECK_FIELD);
+ if (vcol_type == VCOL_CHECK_FIELD)
+ {
+ field_error.append(s->table_name.str);
+ field_error.append(".");
+ }
+ field_error.append((*chk)->name.str);
+ my_error(ER_CONSTRAINT_FAILED,
+ MYF(ignore_failure ? ME_JUST_WARNING : 0), field_error.c_ptr(),
+ s->db.str, s->table_name.str);
+ return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
}
- 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);
+ /*
+ We have to check in_use() as checking constraints may have generated
+ warnings that should be treated as errors
+ */
+ return(!in_use->is_error() ? VIEW_CHECK_OK : VIEW_CHECK_ERROR);
}
@@ -5180,7 +5583,8 @@ void TABLE_LIST::set_check_merged()
It is not simple to check all, but at least this should be checked:
this select is not excluded or the exclusion came from above.
*/
- DBUG_ASSERT(!derived->first_select()->exclude_from_table_unique_test ||
+ DBUG_ASSERT(derived->is_excluded() ||
+ !derived->first_select()->exclude_from_table_unique_test ||
derived->outer_select()->
exclude_from_table_unique_test);
}
@@ -5193,6 +5597,7 @@ void TABLE_LIST::set_check_materialized()
if (view)
derived= &view->unit;
DBUG_ASSERT(derived);
+ DBUG_ASSERT(!derived->is_excluded());
if (!derived->first_select()->exclude_from_table_unique_test)
derived->set_unique_exclude();
else
@@ -5426,9 +5831,10 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
{
DBUG_RETURN(field);
}
+ Name_resolution_context *context= view->view ? &view->view->select_lex.context :
+ &thd->lex->select_lex.context;
Item *item= (new (thd->mem_root)
- Item_direct_view_ref(thd, &view->view->select_lex.context,
- field_ref, view->alias,
+ Item_direct_view_ref(thd, context, field_ref, view->alias,
name, view));
if (!item)
return NULL;
@@ -5549,6 +5955,8 @@ const char *Field_iterator_table_ref::get_table_name()
{
if (table_ref->view)
return table_ref->view_name.str;
+ if (table_ref->is_derived())
+ return table_ref->table->s->table_name.str;
else if (table_ref->is_natural_join)
return natural_join_it.column_ref()->table_name();
@@ -5651,8 +6059,8 @@ Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_
/* The field belongs to a merge view or information schema table. */
Field_translator *translated_field= view_field_it.field_translator();
nj_col= new Natural_join_column(translated_field, table_ref);
- field_count= table_ref->field_translation_end -
- table_ref->field_translation;
+ field_count= (uint)(table_ref->field_translation_end -
+ table_ref->field_translation);
}
else
{
@@ -5748,12 +6156,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
}
@@ -5783,50 +6191,34 @@ void TABLE::prepare_for_position()
}
-/*
- Mark that only fields from one key is used
-
- NOTE:
- This changes the bitmap to use the tmp bitmap
- After this, you can't access any other columns in the table until
- bitmaps are reset, for example with TABLE::clear_column_bitmaps()
- or TABLE::restore_column_maps_after_mark_index()
-*/
-
-void TABLE::mark_columns_used_by_index(uint index)
+MY_BITMAP *TABLE::prepare_for_keyread(uint index, MY_BITMAP *map)
{
- MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("TABLE::mark_columns_used_by_index");
-
- enable_keyread();
- bitmap_clear_all(bitmap);
- mark_columns_used_by_index_no_reset(index, bitmap);
- column_bitmaps_set(bitmap, bitmap);
- DBUG_VOID_RETURN;
+ MY_BITMAP *backup= read_set;
+ DBUG_ENTER("TABLE::prepare_for_keyread");
+ if (!no_keyread)
+ file->ha_start_keyread(index);
+ if (map != read_set || !(file->index_flags(index, 0, 1) & HA_CLUSTERED_INDEX))
+ {
+ mark_columns_used_by_index(index, map);
+ column_bitmaps_set(map);
+ }
+ DBUG_RETURN(backup);
}
/*
- Add fields used by a specified index to the table's read_set.
-
- NOTE:
- The original state can be restored with
- restore_column_maps_after_mark_index().
+ Mark that only fields from one key is used. Useful before keyread.
*/
-void TABLE::add_read_columns_used_by_index(uint index)
+void TABLE::mark_columns_used_by_index(uint index, MY_BITMAP *bitmap)
{
- MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("TABLE::add_read_columns_used_by_index");
+ DBUG_ENTER("TABLE::mark_columns_used_by_index");
- enable_keyread();
- bitmap_copy(bitmap, read_set);
+ bitmap_clear_all(bitmap);
mark_columns_used_by_index_no_reset(index, bitmap);
- column_bitmaps_set(bitmap, write_set);
DBUG_VOID_RETURN;
}
-
/*
Restore to use normal column maps after key read
@@ -5838,12 +6230,11 @@ void TABLE::add_read_columns_used_by_index(uint index)
when calling mark_columns_used_by_index
*/
-void TABLE::restore_column_maps_after_mark_index()
+void TABLE::restore_column_maps_after_keyread(MY_BITMAP *backup)
{
DBUG_ENTER("TABLE::restore_column_maps_after_mark_index");
-
- disable_keyread();
- default_column_bitmaps();
+ file->ha_end_keyread();
+ read_set= backup;
file->column_bitmaps_signal();
DBUG_VOID_RETURN;
}
@@ -5853,21 +6244,15 @@ void TABLE::restore_column_maps_after_mark_index()
mark columns used by key, but don't reset other fields
*/
-void TABLE::mark_columns_used_by_index_no_reset(uint index,
- MY_BITMAP *bitmap)
+void TABLE::mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *bitmap)
{
KEY_PART_INFO *key_part= key_info[index].key_part;
- KEY_PART_INFO *key_part_end= (key_part +
- key_info[index].user_defined_key_parts);
+ KEY_PART_INFO *key_part_end= (key_part + key_info[index].user_defined_key_parts);
for (;key_part != key_part_end; key_part++)
- {
bitmap_set_bit(bitmap, key_part->fieldnr-1);
- if (key_part->field->vcol_info &&
- key_part->field->vcol_info->expr_item)
- key_part->field->vcol_info->
- expr_item->walk(&Item::register_field_in_bitmap,
- 1, (uchar *) bitmap);
- }
+ if (file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX &&
+ s->primary_key != MAX_KEY && s->primary_key != index)
+ mark_columns_used_by_index_no_reset(s->primary_key, bitmap);
}
@@ -5914,6 +6299,7 @@ void TABLE::mark_auto_increment_column()
void TABLE::mark_columns_needed_for_delete()
{
+ bool need_signal= false;
mark_columns_per_binlog_row_image();
if (triggers)
@@ -5923,10 +6309,15 @@ void TABLE::mark_columns_needed_for_delete()
Field **reg_field;
for (reg_field= field ; *reg_field ; reg_field++)
{
- if ((*reg_field)->flags & PART_KEY_FLAG)
- bitmap_set_bit(read_set, (*reg_field)->field_index);
+ Field *cur_field= *reg_field;
+ if (cur_field->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG))
+ {
+ bitmap_set_bit(read_set, cur_field->field_index);
+ if (cur_field->vcol_info)
+ bitmap_set_bit(vcol_set, cur_field->field_index);
+ }
}
- file->column_bitmaps_signal();
+ need_signal= true;
}
if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
{
@@ -5940,9 +6331,17 @@ void TABLE::mark_columns_needed_for_delete()
else
{
mark_columns_used_by_index_no_reset(s->primary_key, read_set);
- file->column_bitmaps_signal();
+ need_signal= true;
}
}
+ if (check_constraints)
+ {
+ mark_check_constraint_columns_for_read();
+ need_signal= true;
+ }
+
+ if (need_signal)
+ file->column_bitmaps_signal();
}
@@ -5966,23 +6365,43 @@ void TABLE::mark_columns_needed_for_delete()
void TABLE::mark_columns_needed_for_update()
{
- DBUG_ENTER("mark_columns_needed_for_update");
+ DBUG_ENTER("TABLE::mark_columns_needed_for_update");
+ bool need_signal= false;
mark_columns_per_binlog_row_image();
if (triggers)
triggers->mark_fields_used(TRG_EVENT_UPDATE);
+ if (default_field)
+ mark_default_fields_for_write(FALSE);
+ if (vfield)
+ need_signal|= mark_virtual_columns_for_write(FALSE);
if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE)
{
- /* Mark all used key columns for read */
- Field **reg_field;
- for (reg_field= field ; *reg_field ; reg_field++)
+ KEY *end= key_info + s->keys;
+ for (KEY *k= key_info; k < end; k++)
{
- /* Merge keys is all keys that had a column refered to in the query */
- if (merge_keys.is_overlapping((*reg_field)->part_of_key))
- bitmap_set_bit(read_set, (*reg_field)->field_index);
+ KEY_PART_INFO *kpend= k->key_part + k->ext_key_parts;
+ bool any_written= false, all_read= true;
+ for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++)
+ {
+ int idx= kp->fieldnr - 1;
+ any_written|= bitmap_is_set(write_set, idx);
+ all_read&= bitmap_is_set(read_set, idx);
+ }
+ if (any_written && !all_read)
+ {
+ for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++)
+ {
+ int idx= kp->fieldnr - 1;
+ if (bitmap_fast_test_and_set(read_set, idx))
+ continue;
+ if (field[idx]->vcol_info)
+ mark_virtual_col(field[idx]);
+ }
+ }
}
- file->column_bitmaps_signal();
+ need_signal= true;
}
if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
{
@@ -5996,11 +6415,28 @@ void TABLE::mark_columns_needed_for_update()
else
{
mark_columns_used_by_index_no_reset(s->primary_key, read_set);
- file->column_bitmaps_signal();
+ need_signal= true;
}
}
- /* Mark all virtual columns needed for update */
- mark_virtual_columns_for_write(FALSE);
+ if (check_constraints)
+ {
+ mark_check_constraint_columns_for_read();
+ need_signal= true;
+ }
+
+ /*
+ 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 && s->has_update_default_function)
+ {
+ bitmap_union(read_set, write_set);
+ need_signal= true;
+ }
+ if (need_signal)
+ file->column_bitmaps_signal();
DBUG_VOID_RETURN;
}
@@ -6014,6 +6450,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)
@@ -6029,8 +6466,14 @@ 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);
+ if (check_constraints)
+ mark_check_constraint_columns_for_read();
+ DBUG_VOID_RETURN;
}
/*
@@ -6143,6 +6586,12 @@ void TABLE::mark_columns_per_binlog_row_image()
DBUG_ASSERT(FALSE);
}
}
+ /*
+ We have to ensure that all virtual columns that are part of read set
+ are calculated.
+ */
+ if (vcol_set)
+ bitmap_union(vcol_set, read_set);
file->column_bitmaps_signal();
}
@@ -6173,9 +6622,9 @@ bool TABLE::mark_virtual_col(Field *field)
DBUG_ASSERT(field->vcol_info);
if (!(res= bitmap_fast_test_and_set(vcol_set, field->field_index)))
{
- Item *vcol_item= field->vcol_info->expr_item;
+ Item *vcol_item= field->vcol_info->expr;
DBUG_ASSERT(vcol_item);
- vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ vcol_item->walk(&Item::register_field_in_read_map, 1, 0);
}
return res;
}
@@ -6183,20 +6632,21 @@ bool TABLE::mark_virtual_col(Field *field)
/*
@brief Mark virtual columns for update/insert commands
-
- @param insert_fl <-> virtual columns are marked for insert command
+
+ @param insert_fl true if virtual columns are marked for insert command
+ For the moment this is not used, may be used in future.
@details
The function marks virtual columns used in a update/insert commands
in the vcol_set bitmap.
For an insert command a virtual column is always marked in write_set if
it is a stored column.
- If a virtual column is from write_set it is always marked in vcol_set.
+ If a virtual column is from write_set it is always marked in vcol_set.
If a stored virtual column is not from write_set but it is computed
through columns from write_set it is also marked in vcol_set, and,
besides, it is added to write_set.
- @return void
+ @return whether a bitmap was updated
@note
Let table t1 have columns a,b,c and let column c be a stored virtual
@@ -6206,76 +6656,106 @@ bool TABLE::mark_virtual_col(Field *field)
column b will be placed into read_set.
If column c was a virtual column, but not a stored virtual column
then it would not be added to any of the sets. Column b would not
- be added to read_set either.
+ be added to read_set either.
*/
-void TABLE::mark_virtual_columns_for_write(bool insert_fl)
+bool TABLE::mark_virtual_columns_for_write(bool insert_fl
+ __attribute__((unused)))
{
Field **vfield_ptr, *tmp_vfield;
- bool bitmap_updated= FALSE;
-
- if (!vfield)
- return;
-
- if (!vfield)
- return;
+ bool bitmap_updated= false;
+ DBUG_ENTER("mark_virtual_columns_for_write");
for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
{
tmp_vfield= *vfield_ptr;
if (bitmap_is_set(write_set, tmp_vfield->field_index))
- bitmap_updated= mark_virtual_col(tmp_vfield);
- else if (tmp_vfield->stored_in_db)
+ bitmap_updated|= mark_virtual_col(tmp_vfield);
+ else if (tmp_vfield->vcol_info->stored_in_db ||
+ (tmp_vfield->flags & (PART_KEY_FLAG | FIELD_IN_PART_FUNC_FLAG |
+ PART_INDIRECT_KEY_FLAG)))
{
- bool mark_fl= insert_fl;
- if (!mark_fl)
- {
- MY_BITMAP *save_read_set;
- Item *vcol_item= tmp_vfield->vcol_info->expr_item;
- DBUG_ASSERT(vcol_item);
- bitmap_clear_all(&tmp_set);
- save_read_set= read_set;
- read_set= &tmp_set;
- vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
- read_set= save_read_set;
- bitmap_intersect(&tmp_set, write_set);
- mark_fl= !bitmap_is_clear_all(&tmp_set);
- }
- if (mark_fl)
- {
- bitmap_set_bit(write_set, tmp_vfield->field_index);
- mark_virtual_col(tmp_vfield);
- bitmap_updated= TRUE;
- }
- }
+ bitmap_set_bit(write_set, tmp_vfield->field_index);
+ mark_virtual_col(tmp_vfield);
+ bitmap_updated= true;
+ }
}
if (bitmap_updated)
file->column_bitmaps_signal();
+ DBUG_RETURN(bitmap_updated);
}
+/*
+ Mark fields used by check constraints into s->check_set.
+ Mark all fields used in an expression that is part of an index
+ with PART_INDIRECT_KEY_FLAG
-/**
- 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
+ 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_virtual_fields(void)
{
- Field **dfield_ptr, *dfield;
- bool res= false;
- for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ MY_BITMAP *save_read_set;
+ Field **vfield_ptr;
+
+ /* If there is virtual fields are already initialized */
+ if (s->check_set_initialized)
+ return;
+
+ if (s->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_lock(&s->LOCK_share);
+ if (s->check_set)
{
- dfield= (*dfield_ptr);
- if (is_update)
- res= dfield->has_update_default_function();
- else
- res= dfield->has_insert_default_function();
- if (res)
- return res;
+ /* Mark fields used by check constraint */
+ save_read_set= read_set;
+ read_set= s->check_set;
+
+ for (Virtual_column_info **chk= check_constraints ; *chk ; chk++)
+ (*chk)->expr->walk(&Item::register_field_in_read_map, 1, 0);
+ read_set= save_read_set;
}
- return res;
+
+ /*
+ mark all fields that part of a virtual indexed field with
+ PART_INDIRECT_KEY_FLAG. This is used to ensure that all fields
+ that are part of an index exits before write/delete/update.
+
+ As this code is only executed once per open share, it's reusing
+ existing functionality instead of adding an extra argument to
+ add_field_to_set_processor or adding another processor.
+ */
+ if (vfield)
+ {
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->flags & PART_KEY_FLAG)
+ (*vfield_ptr)->vcol_info->expr->walk(&Item::add_field_to_set_processor,
+ 1, this);
+ }
+ for (uint i= 0 ; i < s->fields ; i++)
+ {
+ if (bitmap_is_set(&tmp_set, i))
+ {
+ s->field[i]->flags|= PART_INDIRECT_KEY_FLAG;
+ field[i]->flags|= PART_INDIRECT_KEY_FLAG;
+ }
+ }
+ bitmap_clear_all(&tmp_set);
+ }
+ s->check_set_initialized= 1;
+ if (s->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_unlock(&s->LOCK_share);
+}
+
+/* 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);
+ if (vcol_set)
+ bitmap_union(vcol_set, s->check_set);
}
@@ -6283,18 +6763,80 @@ 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)
+{
+ DBUG_ENTER("mark_default_fields_for_write");
+ Field **field_ptr, *field;
+ for (field_ptr= default_field; *field_ptr; field_ptr++)
+ {
+ field= (*field_ptr);
+ if (is_insert && field->default_value)
+ {
+ bitmap_set_bit(write_set, field->field_index);
+ field->default_value->expr->
+ walk(&Item::register_field_in_read_map, 1, 0);
+ }
+ else if (!is_insert && field->has_update_default_function())
+ bitmap_set_bit(write_set, field->field_index);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from)
{
- Field **dfield_ptr, *dfield;
- enum_sql_command cmd= in_use->lex->sql_command;
- for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ my_ptrdiff_t diff= to - from;
+ if (diff)
{
- 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);
+ do
+ {
+ (*ptr)->move_field_offset(diff);
+ } while (*(++ptr));
+ }
+}
+
+
+/*
+ Store all allocated virtual fields blob values
+ Used by InnoDB when calculating virtual fields for it's own internal
+ records
+*/
+
+void TABLE::remember_blob_values(String *blob_storage)
+{
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->type() == MYSQL_TYPE_BLOB &&
+ !(*vfield_ptr)->vcol_info->stored_in_db)
+ {
+ Field_blob *blob= ((Field_blob*) *vfield_ptr);
+ memcpy((void*) blob_storage, (void*) &blob->value, sizeof(blob->value));
+ blob_storage++;
+ blob->value.release();
+ }
+ }
+}
+
+
+/*
+ Restore all allocated virtual fields blob values
+ Used by InnoDB when calculating virtual fields for it's own internal
+ records
+*/
+
+void TABLE::restore_blob_values(String *blob_storage)
+{
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->type() == MYSQL_TYPE_BLOB &&
+ !(*vfield_ptr)->vcol_info->stored_in_db)
+ {
+ Field_blob *blob= ((Field_blob*) *vfield_ptr);
+ blob->value.free();
+ memcpy((void*) &blob->value, (void*) blob_storage, sizeof(blob->value));
+ blob_storage++;
+ }
}
}
@@ -6664,7 +7206,7 @@ void TABLE_LIST::reinit_before_use(THD *thd)
*/
Item_subselect *TABLE_LIST::containing_subselect()
-{
+{
return (select_lex ? select_lex->master_unit()->item : 0);
}
@@ -6694,7 +7236,7 @@ Item_subselect *TABLE_LIST::containing_subselect()
is equivalent to
USE INDEX (i1,i2)
and means "consider only i1 and i2".
-
+
Similarly
USE INDEX () USE INDEX (i1)
is equivalent to
@@ -6703,7 +7245,7 @@ Item_subselect *TABLE_LIST::containing_subselect()
It is OK to have the same index several times, e.g. "USE INDEX (i1,i1)" is
not an error.
-
+
Different kind of hints (USE/FORCE/IGNORE) are processed in the following
order:
1. All indexes in USE (or FORCE) INDEX are added to the mask.
@@ -6822,8 +7364,8 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
}
/*
- TODO: get rid of tbl->force_index (on if any FORCE INDEX is specified) and
- create tbl->force_index_join instead.
+ TODO: get rid of tbl->force_index (on if any FORCE INDEX is specified)
+ and create tbl->force_index_join instead.
Then use the correct force_index_XX instead of the global one.
*/
if (!index_join[INDEX_HINT_FORCE].is_clear_all() ||
@@ -6853,21 +7395,27 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
}
-size_t max_row_length(TABLE *table, const uchar *data)
+size_t max_row_length(TABLE *table, MY_BITMAP const *cols, const uchar *data)
{
TABLE_SHARE *table_s= table->s;
size_t length= table_s->reclength + 2 * table_s->fields;
uint *const beg= table_s->blob_field;
uint *const end= beg + table_s->blob_fields;
+ my_ptrdiff_t const rec_offset= (my_ptrdiff_t) (data - table->record[0]);
+ DBUG_ENTER("max_row_length");
for (uint *ptr= beg ; ptr != end ; ++ptr)
{
- Field_blob* const blob= (Field_blob*) table->field[*ptr];
- length+= blob->get_length((const uchar*)
- (data + blob->offset(table->record[0]))) +
- HA_KEY_BLOB_LENGTH;
+ Field * const field= table->field[*ptr];
+ if (bitmap_is_set(cols, field->field_index) &&
+ !field->is_null(rec_offset))
+ {
+ Field_blob * const blob= (Field_blob*) field;
+ length+= blob->get_length(rec_offset) + 8; /* max blob store length */
+ }
}
- return length;
+ DBUG_PRINT("exit", ("length: %lld", (longlong) length));
+ DBUG_RETURN(length);
}
@@ -6941,19 +7489,32 @@ bool is_simple_order(ORDER *order)
return TRUE;
}
+class Turn_errors_to_warnings_handler : public Internal_error_handler
+{
+public:
+ Turn_errors_to_warnings_handler() {}
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level *level,
+ const char* msg,
+ Sql_condition ** cond_hdl)
+ {
+ *cond_hdl= NULL;
+ if (*level == Sql_condition::WARN_LEVEL_ERROR)
+ *level= Sql_condition::WARN_LEVEL_WARN;
+ return(0);
+ }
+};
+
/*
@brief Compute values for virtual columns used in query
- @param thd Thread handle
- @param table The TABLE object
- @param vcol_update_mode Specifies what virtual column are computed
+ @param update_mode Specifies what virtual column are computed
@details
The function computes the values of the virtual columns of the table and
stores them in the table record buffer.
- Only fields from vcol_set are computed: all of them, if vcol_update_mode is
- set to VCOL_UPDATE_FOR_WRITE, and, only those with the stored_in_db flag
- set to false, if vcol_update_mode is equal to VCOL_UPDATE_FOR_READ.
@retval
0 Success
@@ -6961,36 +7522,131 @@ bool is_simple_order(ORDER *order)
>0 Error occurred when storing a virtual field value
*/
-int update_virtual_fields(THD *thd, TABLE *table,
- enum enum_vcol_update_mode vcol_update_mode)
+int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
{
- DBUG_ENTER("update_virtual_fields");
- Field **vfield_ptr, *vfield;
- int error __attribute__ ((unused))= 0;
- DBUG_ASSERT(table && table->vfield);
-
+ DBUG_ENTER("TABLE::update_virtual_fields");
+ DBUG_PRINT("enter", ("update_mode: %d", update_mode));
+ Field **vfield_ptr, *vf;
Query_arena backup_arena;
- thd->set_n_backup_active_arena(table->expr_arena, &backup_arena);
+ Turn_errors_to_warnings_handler Suppress_errors;
+ int error;
+ bool handler_pushed= 0, update_all_columns= 1;
+ DBUG_ASSERT(vfield);
+
+ if (h->keyread_enabled())
+ DBUG_RETURN(0);
+
+ error= 0;
+ in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
+
+ /* When reading or deleting row, ignore errors from virtual columns */
+ if (update_mode == VCOL_UPDATE_FOR_READ ||
+ update_mode == VCOL_UPDATE_FOR_DELETE ||
+ update_mode == VCOL_UPDATE_INDEXED)
+ {
+ in_use->push_internal_handler(&Suppress_errors);
+ handler_pushed= 1;
+ }
+ else if (update_mode == VCOL_UPDATE_FOR_REPLACE &&
+ in_use->is_current_stmt_binlog_format_row() &&
+ in_use->variables.binlog_row_image != BINLOG_ROW_IMAGE_MINIMAL)
+ {
+ /*
+ If we are doing a replace with not minimal binary logging, we have to
+ calculate all virtual columns.
+ */
+ update_all_columns= 1;
+ }
/* Iterate over virtual fields in the table */
- for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
{
- vfield= (*vfield_ptr);
- DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item);
- if (bitmap_is_set(table->vcol_set, vfield->field_index) &&
- (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db))
+ vf= (*vfield_ptr);
+ Virtual_column_info *vcol_info= vf->vcol_info;
+ DBUG_ASSERT(vcol_info);
+ DBUG_ASSERT(vcol_info->expr);
+
+ bool update= 0, swap_values= 0;
+ switch (update_mode) {
+ case VCOL_UPDATE_FOR_READ:
+ update= (!vcol_info->stored_in_db &&
+ bitmap_is_set(vcol_set, vf->field_index));
+ swap_values= 1;
+ break;
+ case VCOL_UPDATE_FOR_DELETE:
+ case VCOL_UPDATE_FOR_WRITE:
+ update= bitmap_is_set(vcol_set, vf->field_index);
+ break;
+ case VCOL_UPDATE_FOR_REPLACE:
+ update= ((!vcol_info->stored_in_db &&
+ (vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) &&
+ bitmap_is_set(vcol_set, vf->field_index)) ||
+ update_all_columns);
+ if (update && (vf->flags & BLOB_FLAG))
+ {
+ /*
+ The row has been read into record[1] and Field_blob::value
+ contains the value for record[0]. Swap value and read_value
+ to ensure that the virtual column data for the read row will
+ be in read_value at the end of this function
+ */
+ ((Field_blob*) vf)->swap_value_and_read_value();
+ /* Ensure we call swap_value_and_read_value() after update */
+ swap_values= 1;
+ }
+ break;
+ case VCOL_UPDATE_INDEXED:
+ case VCOL_UPDATE_INDEXED_FOR_UPDATE:
+ /* Read indexed fields that was not updated in VCOL_UPDATE_FOR_READ */
+ update= (!vcol_info->stored_in_db &&
+ (vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) &&
+ !bitmap_is_set(vcol_set, vf->field_index));
+ swap_values= 1;
+ break;
+ }
+
+ if (update)
{
+ int field_error __attribute__((unused)) = 0;
/* Compute the actual value of the virtual fields */
- error= vfield->vcol_info->expr_item->save_in_field(vfield, 0);
- DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name));
+ if (vcol_info->expr->save_in_field(vf, 0))
+ field_error= error= 1;
+ DBUG_PRINT("info", ("field '%s' - updated error: %d",
+ vf->field_name, field_error));
+ if (swap_values && (vf->flags & BLOB_FLAG))
+ {
+ /*
+ Remember the read value to allow other update_virtual_field() calls
+ for the same blob field for the row to be updated.
+ Field_blob->read_value always contains the virtual column data for
+ any read row.
+ */
+ ((Field_blob*) vf)->swap_value_and_read_value();
+ }
}
else
{
- DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name));
+ DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name));
}
}
- thd->restore_active_arena(table->expr_arena, &backup_arena);
- DBUG_RETURN(0);
+ if (handler_pushed)
+ in_use->pop_internal_handler();
+ in_use->restore_active_arena(expr_arena, &backup_arena);
+
+ /* Return 1 only of we got a fatal error, not a warning */
+ DBUG_RETURN(in_use->is_error());
+}
+
+int TABLE::update_virtual_field(Field *vf)
+{
+ Query_arena backup_arena;
+ DBUG_ENTER("TABLE::update_virtual_field");
+ in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
+ bitmap_clear_all(&tmp_set);
+ vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set);
+ vf->vcol_info->expr->save_in_field(vf, 0);
+ in_use->restore_active_arena(expr_arena, &backup_arena);
+ DBUG_RETURN(in_use->is_error());
}
@@ -7004,47 +7660,65 @@ 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;
+ Query_arena backup_arena;
+ Field **field_ptr;
int res= 0;
- enum_sql_command cmd= in_use->lex->sql_command;
-
+ DBUG_ENTER("TABLE::update_default_fields");
DBUG_ASSERT(default_field);
+ in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
+
/* Iterate over fields with default functions in the table */
- for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ for (field_ptr= default_field; *field_ptr ; field_ptr++)
{
- dfield= (*dfield_ptr);
+ Field *field= (*field_ptr);
/*
- If an explicit default value for a filed overrides the default,
+ If an explicit default value for a field overrides the default,
do not update the field with its automatic default value.
*/
- if (!(dfield->flags & HAS_EXPLICIT_VALUE))
+ if (!field->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 (field->default_value &&
+ (field->default_value->flags || field->flags & BLOB_FLAG))
+ res|= (field->default_value->expr->save_in_field(field, 0) < 0);
+ }
+ else
+ res|= field->evaluate_update_default_function();
+ if (!ignore_errors && res)
+ {
+ my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name);
+ break;
+ }
+ res= 0;
}
}
+ in_use->restore_active_arena(expr_arena, &backup_arena);
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");
+ bitmap_clear_all(&has_value_set);
+ DBUG_VOID_RETURN;
}
/*
@@ -7153,7 +7827,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
/*
We're here if:
- validate_value_in_record_with_warn() failed and
- strict mode converted WARN to ERROR
+ strict mo validate_default_values_of_unset_fieldsde converted WARN to ERROR
- or the connection was killed, or closed unexpectedly
*/
DBUG_RETURN(true);
@@ -7164,6 +7838,71 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
}
+bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
+ TABLE *tmp_table,
+ TMP_TABLE_PARAM *tmp_table_param,
+ bool with_cleanup)
+{
+ int write_err= 0;
+
+ DBUG_ENTER("TABLE::insert_all_rows_into_tmp_table");
+
+ if (with_cleanup)
+ {
+ if ((write_err= tmp_table->file->ha_delete_all_rows()))
+ goto err;
+ }
+
+ if (file->indexes_are_disabled())
+ tmp_table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
+ file->ha_index_or_rnd_end();
+
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
+
+ if (tmp_table->no_rows)
+ tmp_table->file->extra(HA_EXTRA_NO_ROWS);
+ else
+ {
+ /* update table->file->stats.records */
+ file->info(HA_STATUS_VARIABLE);
+ tmp_table->file->ha_start_bulk_insert(file->stats.records);
+ }
+
+ while (!file->ha_rnd_next(tmp_table->record[0]))
+ {
+ write_err= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
+ if (write_err)
+ {
+ bool is_duplicate;
+ if (tmp_table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
+ create_internal_tmp_table_from_heap(thd, tmp_table,
+ tmp_table_param->start_recinfo,
+ &tmp_table_param->recinfo,
+ write_err, 1, &is_duplicate))
+ DBUG_RETURN(1);
+
+ }
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ goto err_killed;
+ }
+ }
+ if (!tmp_table->no_rows && tmp_table->file->ha_end_bulk_insert())
+ goto err;
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("error",("Got error: %d",write_err));
+ file->print_error(write_err, MYF(0));
+err_killed:
+ (void) file->ha_rnd_end();
+ DBUG_RETURN(1);
+}
+
+
+
/*
@brief Reset const_table flag
@@ -7204,20 +7943,24 @@ void TABLE_LIST::reset_const_table()
bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
{
- SELECT_LEX_UNIT *unit;
+ SELECT_LEX_UNIT *unit= get_unit();
DBUG_ENTER("handle_derived");
DBUG_PRINT("enter", ("phases: 0x%x", phases));
- if ((unit= get_unit()))
+
+ if (unit)
{
- for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
- if (sl->handle_derived(lex, phases))
- DBUG_RETURN(TRUE);
- DBUG_RETURN(mysql_handle_single_derived(lex, this, phases));
+ if (!is_with_table_recursive_reference())
+ {
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ if (sl->handle_derived(lex, phases))
+ DBUG_RETURN(TRUE);
+ }
+ if (mysql_handle_single_derived(lex, this, phases))
+ DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
}
-
/**
@brief
Return unit of this derived table/view
@@ -7323,8 +8066,10 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
/* A subquery might be forced to be materialized due to a side-effect. */
if (!is_materialized_derived() && first_select->is_mergeable() &&
optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
+ !thd->lex->can_not_use_merged() &&
!(thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
- thd->lex->sql_command == SQLCOM_DELETE_MULTI))
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI) &&
+ !is_recursive_with_table())
set_merged_derived();
else
set_materialized_derived();
@@ -7345,7 +8090,9 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
*/
if (is_merged_derived())
{
- if (is_view() || unit->prepared)
+ if (is_view() ||
+ (unit->prepared &&
+ !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)))
create_field_translation(thd);
}
@@ -7374,7 +8121,7 @@ int TABLE_LIST::fetch_number_of_rows()
{
if (jtbm_subselect->is_jtbm_merged)
{
- table->file->stats.records= jtbm_subselect->jtbm_record_count;
+ table->file->stats.records= (ha_rows)jtbm_subselect->jtbm_record_count;
set_if_bigger(table->file->stats.records, 2);
table->used_stat_records= table->file->stats.records;
}
@@ -7495,6 +8242,12 @@ void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock)
}
}
+bool TABLE_LIST::is_with_table()
+{
+ return derived && derived->with_element;
+}
+
+
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
@@ -7511,6 +8264,201 @@ double KEY::actual_rec_per_key(uint i)
read_stats->get_avg_frequency(i) : (double) rec_per_key[i]);
}
+
+/**
+ @brief
+ Mark subformulas of a condition unusable for the condition pushed into table
+
+ @param cond The condition whose subformulas are to be marked
+
+ @details
+ This method recursively traverses the AND-OR condition cond and for each subformula
+ of the codition it checks whether it can be usable for the extraction of a condition
+ that can be pushed into this table. The subformulas that are not usable are
+ marked with the flag NO_EXTRACTION_FL.
+ @note
+ This method is called before any call of TABLE_LIST::build_pushable_cond_for_table.
+ The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
+ for the subformula when extracting the pushable condition.
+*/
+
+void TABLE_LIST::check_pushable_cond_for_table(Item *cond)
+{
+ table_map tab_map= table->map;
+ cond->clear_extraction_flag();
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ uint count= 0;
+ Item *item;
+ while ((item=li++))
+ {
+ check_pushable_cond_for_table(item);
+ if (item->get_extraction_flag() != NO_EXTRACTION_FL)
+ count++;
+ else if (!and_cond)
+ break;
+ }
+ if ((and_cond && count == 0) || item)
+ {
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+ if (and_cond)
+ li.rewind();
+ while ((item= li++))
+ item->clear_extraction_flag();
+ }
+ }
+ else if (!cond->excl_dep_on_table(tab_map))
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+}
+
+
+/**
+ @brief
+ Build condition extractable from the given one depended only on this table
+
+ @param thd The thread handle
+ @param cond The condition from which the pushable one is to be extracted
+
+ @details
+ For the given condition cond this method finds out what condition depended
+ only on this table can be extracted from cond. If such condition C exists
+ the method builds the item for it.
+ The method uses the flag NO_EXTRACTION_FL set by the preliminary call of
+ the method TABLE_LIST::check_pushable_cond_for_table to figure out whether
+ a subformula depends only on this table or not.
+ @note
+ The built condition C is always implied by the condition cond
+ (cond => C). The method tries to build the most restictive such
+ condition (i.e. for any other condition C' such that cond => C'
+ we have C => C').
+ @note
+ The build item is not ready for usage: substitution for the field items
+ has to be done and it has to be re-fixed.
+
+ @retval
+ the built condition pushable into this table if such a condition exists
+ NULL if there is no such a condition
+*/
+
+Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
+{
+ table_map tab_map= table->map;
+ bool is_multiple_equality= cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC;
+ if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
+ return 0;
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool cond_and= false;
+ Item_cond *new_cond;
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ cond_and= true;
+ new_cond=new (thd->mem_root) Item_cond_and(thd);
+ }
+ else
+ new_cond= new (thd->mem_root) Item_cond_or(thd);
+ if (!new_cond)
+ return 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ bool is_fix_needed= false;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ if (!cond_and)
+ return 0;
+ continue;
+ }
+ Item *fix= build_pushable_cond_for_table(thd, item);
+ if (!fix && !cond_and)
+ return 0;
+ if (!fix)
+ continue;
+
+ if (fix->type() == Item::COND_ITEM &&
+ ((Item_cond*) fix)->functype() == Item_func::COND_AND_FUNC)
+ is_fix_needed= true;
+
+ new_cond->argument_list()->push_back(fix, thd->mem_root);
+ }
+ if (is_fix_needed && new_cond->fix_fields(thd, 0))
+ return 0;
+
+ switch (new_cond->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ return new_cond;
+ }
+ }
+ else if (is_multiple_equality)
+ {
+ if (!(cond->used_tables() & tab_map))
+ return 0;
+ Item *new_cond= NULL;
+ int i= 0;
+ Item_equal *item_equal= (Item_equal *) cond;
+ Item *left_item = item_equal->get_const();
+ Item_equal_fields_iterator it(*item_equal);
+ Item *item;
+ if (!left_item)
+ {
+ while ((item=it++))
+ if (item->used_tables() == tab_map)
+ {
+ left_item= item;
+ break;
+ }
+ }
+ if (!left_item)
+ return 0;
+ while ((item=it++))
+ {
+ if (!(item->used_tables() == tab_map))
+ continue;
+ Item_func_eq *eq= 0;
+ Item *left_item_clone= left_item->build_clone(thd, thd->mem_root);
+ Item *right_item_clone= item->build_clone(thd, thd->mem_root);
+ if (left_item_clone && right_item_clone)
+ {
+ left_item_clone->set_item_equal(NULL);
+ right_item_clone->set_item_equal(NULL);
+ eq= new (thd->mem_root) Item_func_eq(thd, right_item_clone,
+ left_item_clone);
+ }
+ if (eq)
+ {
+ i++;
+ switch (i)
+ {
+ case 1:
+ new_cond= eq;
+ break;
+ case 2:
+ new_cond= new (thd->mem_root) Item_cond_and(thd, new_cond, eq);
+ break;
+ default:
+ ((Item_cond_and*)new_cond)->argument_list()->push_back(eq,
+ thd->mem_root);
+ }
+ }
+ }
+ if (new_cond)
+ new_cond->fix_fields(thd, &new_cond);
+ return new_cond;
+ }
+ else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
+ return cond->build_clone(thd, thd->mem_root);
+ return 0;
+}
+
LEX_CSTRING *fk_option_name(enum_fk_option opt)
{
static LEX_CSTRING names[]=