summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc572
1 files changed, 426 insertions, 146 deletions
diff --git a/sql/table.cc b/sql/table.cc
index 640ab8267fb..9a9dee6e0ca 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -41,6 +41,7 @@
#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
@@ -66,7 +67,7 @@ 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;
@@ -1431,48 +1432,38 @@ 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->table_check_constraints *
- sizeof(Virtual_column_info*)+
- (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);
- share->check_constraints= ((Virtual_column_info**)
- (share->intervals+interval_count));
- table_check_constraints= share->check_constraints;
- interval_array= (const char **) (table_check_constraints+
- share->table_check_constraints);
- names= (char*) (interval_array+share->fields+interval_parts+keys+3);
if (!interval_count)
share->intervals= 0; // For better debugging
- memcpy((char*) names, strpos+(share->fields*field_pack_length),
- (uint) (n_length+int_length));
- comment_pos= names+(n_length+int_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= (uchar*) (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 */
@@ -1561,6 +1552,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
LEX_STRING comment;
Virtual_column_info *vcol_info= 0;
uint gis_length, gis_decimals, srid= 0;
+ Field::utype unireg_check;
if (new_frm_ver >= 3)
{
@@ -1776,22 +1768,36 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
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);
+ if (unireg_check == Field::TIMESTAMP_DNUN_FIELD)
+ unireg_check= Field::TIMESTAMP_UN_FIELD;
+ if (unireg_check == Field::TIMESTAMP_DN_FIELD)
+ unireg_check= Field::NONE;
+
*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::utype) MTYP_TYPENR(unireg_type))
+ {
+ char buf[32];
+ if (reg_field->decimals())
+ my_snprintf(buf, sizeof(buf), "CURRENT_TIMESTAMP(%d)", reg_field->decimals());
+ else
+ strmov(buf, "CURRENT_TIMESTAMP");
+
+ reg_field->default_value= new (&share->mem_root) Virtual_column_info();
+ reg_field->default_value->stored_in_db= 1;
+ thd->make_lex_string(&reg_field->default_value->expr_str, buf, strlen(buf));
+ share->default_expressions++;
+ }
+
reg_field->field_index= i;
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
@@ -1831,13 +1837,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->stored_rec_length>=recpos)
share->stored_rec_length= recpos-1;
}
- if (reg_field->has_insert_default_function())
- has_insert_default_function= 1;
if (reg_field->has_update_default_function())
+ {
has_update_default_function= 1;
- if (reg_field->has_insert_default_function() ||
- reg_field->has_update_default_function())
- share->default_fields++;
+ if (!reg_field->default_value)
+ share->default_fields++;
+ }
}
*field_ptr=0; // End marker
/* Sanity checks: */
@@ -1943,6 +1948,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
(keyinfo-1)->ext_key_parts;
+ uint add_keyparts_for_this_key= add_first_key_parts;
/*
Do not extend the key that contains a component
@@ -1954,19 +1960,20 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->field[fieldnr-1]->key_length() !=
keyinfo->key_part[i].length)
{
- add_first_key_parts= 0;
+ add_keyparts_for_this_key= 0;
break;
}
}
- if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->user_defined_key_parts)
+ if (add_keyparts_for_this_key < (keyinfo->ext_key_parts -
+ keyinfo->user_defined_key_parts))
{
share->ext_key_parts-= keyinfo->ext_key_parts;
key_part_map ext_key_part_map= keyinfo->ext_key_part_map;
keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->ext_key_part_map= 0;
- for (i= 0; i < add_first_key_parts; i++)
+ for (i= 0; i < add_keyparts_for_this_key; i++)
{
if (ext_key_part_map & 1<<i)
{
@@ -2223,16 +2230,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
case 1: // Generated stored field
vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->vcol_info);
reg_field->vcol_info= vcol_info;
share->virtual_fields++;
share->virtual_stored_fields++; // For insert/load data
break;
case 2: // Default expression
vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->default_value);
reg_field->default_value= vcol_info;
share->default_expressions++;
break;
case 3: // Field check constraint
+ DBUG_ASSERT(!reg_field->check_constraint);
reg_field->check_constraint= vcol_info;
share->field_check_constraints++;
break;
@@ -2299,6 +2309,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
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;
@@ -2512,6 +2523,68 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
}
+static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_vcol_expr");
+
+ const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+
+ const char *save_where= thd->where;
+ thd->where= "virtual column function";
+
+ int error= vcol->expr_item->fix_fields(thd, &vcol->expr_item);
+
+ thd->mark_used_columns= save_mark_used_columns;
+ thd->where= save_where;
+
+ if (unlikely(error))
+ {
+ my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), vcol->expr_str);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/** rerun fix_fields for vcols that returns time- or session- dependent values
+
+ @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)
+{
+ DBUG_ENTER("fix_session_vcol_expr");
+ if (!(vcol->flags & (VCOL_TIME_FUNC|VCOL_SESSION_FUNC)))
+ DBUG_RETURN(0);
+
+ vcol->expr_item->cleanup();
+ DBUG_ASSERT(!vcol->expr_item->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);
+}
+
+
/*
@brief
Perform semantic analysis of the defining expression for a virtual column
@@ -2539,53 +2612,31 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
FALSE Otherwise
*/
-static bool fix_vcol_expr(THD *thd, TABLE *table, Field *field,
- Virtual_column_info *vcol)
+static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, Field *field,
+ Virtual_column_info *vcol)
{
Item* func_expr= vcol->expr_item;
- bool result= TRUE;
- TABLE_LIST tables;
- int error= 0;
- const char *save_where;
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- DBUG_ENTER("fix_vcol_expr");
+ DBUG_ENTER("fix_and_check_vcol_expr");
DBUG_PRINT("info", ("vcol: %p", vcol));
DBUG_ASSERT(func_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 */
- thd->in_stored_expression= 1;
- if (!func_expr->fixed)
- error= func_expr->fix_fields(thd, &vcol->expr_item);
- thd->in_stored_expression= 0;
+ 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"));
- my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), vcol->expr_str);
- goto end;
- }
/* fix_fields could've changed the expression */
func_expr= vcol->expr_item;
- /* Number of columns will be checked later */
- thd->where= save_where;
+ /* 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;
- }
-
- /* Check that we are not refering to any not yet initialized fields */
- if (field)
- {
- if (func_expr->walk(&Item::check_field_expression_processor, 0, field))
- goto end;
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ DBUG_RETURN(1);
}
/*
@@ -2595,31 +2646,29 @@ static bool fix_vcol_expr(THD *thd, TABLE *table, Field *field,
Item::vcol_func_processor_result res;
res.errors= 0;
- error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
+ int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
if (error || (res.errors & VCOL_IMPOSSIBLE))
- {
+ { // this can only happen if the frm was corrupted
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
- "???", field->field_name);
- goto end;
+ "???", field ? field->field_name : "?????");
+ DBUG_RETURN(1);
}
vcol->flags= res.errors;
/*
Mark what kind of default / virtual fields the table has
*/
- if (vcol->stored_in_db && vcol->flags & VCOL_NON_DETERMINISTIC)
- table->s->non_determinstic_insert= 1;
+ if (vcol->stored_in_db &&
+ vcol->flags & (VCOL_NON_DETERMINISTIC | VCOL_SESSION_FUNC))
+ table->s->non_determinstic_insert= true;
- result= FALSE;
-
-end:
+ if (vcol->flags & VCOL_SESSION_FUNC)
+ table->s->vcols_need_refixing= true;
- 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
@@ -2639,7 +2688,7 @@ end:
pointer to this item is placed into in a Virtual_column_info object
that is created. After this the function performs
semantic analysis of the item by calling the the function
- fix_vcol_expr(). Since the defining expression is part of the table
+ 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
@@ -2690,14 +2739,10 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd,
vcol_expr->length +
parse_vcol_keyword.length + 3)))
DBUG_RETURN(0);
- memcpy(vcol_expr_str,
- (char*) parse_vcol_keyword.str,
- parse_vcol_keyword.length);
+ memcpy(vcol_expr_str, parse_vcol_keyword.str, parse_vcol_keyword.length);
str_len= parse_vcol_keyword.length;
vcol_expr_str[str_len++]= '(';
- memcpy(vcol_expr_str + str_len,
- (char*) vcol_expr->str,
- vcol_expr->length);
+ memcpy(vcol_expr_str + str_len, vcol_expr->str, vcol_expr->length);
str_len+= vcol_expr->length;
vcol_expr_str[str_len++]= ')';
vcol_expr_str[str_len++]= 0;
@@ -2740,21 +2785,14 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd,
thd->update_charset(&my_charset_utf8mb4_general_ci,
table->s->table_charset);
}
- thd->in_stored_expression= 1;
error= parse_sql(thd, &parser_state, NULL);
- thd->in_stored_expression= 0;
if (error)
goto err;
- /*
- mark if expression will be stored in the table. This is also used by
- fix_vcol_expr() to mark if we are using non deterministic functions.
- */
vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
vcol_storage.vcol_info->name= vcol->name;
vcol_storage.vcol_info->utf8= vcol->utf8;
- /* Validate the Item tree. */
- if (!fix_vcol_expr(thd, table, field, vcol_storage.vcol_info))
+ if (!fix_and_check_vcol_expr(thd, table, field, vcol_storage.vcol_info))
{
vcol_info= vcol_storage.vcol_info; // Expression ok
goto end;
@@ -2774,6 +2812,14 @@ end:
DBUG_RETURN(vcol_info);
}
+static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
+{
+ bool res= vcol &&
+ vcol->expr_item->walk(&Item::check_field_expression_processor, 0,
+ field);
+ return res;
+}
+
/*
Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
*/
@@ -3038,34 +3084,29 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
}
field->default_value= vcol;
- if (is_create_table &&
- !(vcol->flags & (VCOL_UNKNOWN | VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC)))
- {
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
- my_ptrdiff_t off= share->default_values - outparam->record[0];
- field->move_field_offset(off);
- int res= vcol->expr_item->save_in_field(field, 1);
- field->move_field_offset(-off);
- thd->count_cuted_fields= old_count_cuted_fields;
- if (res != 0 && res != 3)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), field->field_name);
- error= OPEN_FRM_CORRUPTED;
- goto err;
- }
- }
*(dfield_ptr++)= *field_ptr;
}
else
- if ((field->has_insert_default_function() ||
- field->has_update_default_function()))
+ if (field->has_update_default_function())
*(dfield_ptr++)= *field_ptr;
}
*vfield_ptr= 0; // End marker
*dfield_ptr= 0; // End marker
+ /* Check that expressions aren't refering to not yet initialized fields */
+ for (field_ptr= outparam->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))
+ {
+ error= OPEN_FRM_CORRUPTED;
+ goto err;
+ }
+ }
+
/* Update to use trigger fields */
switch_defaults_to_nullable_trigger_fields(outparam);
@@ -6275,7 +6316,7 @@ void TABLE::mark_columns_needed_for_update()
to compare records and detect data change.
*/
if ((file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) &&
- default_field && has_default_function(true))
+ default_field && s->has_update_default_function)
bitmap_union(read_set, write_set);
DBUG_VOID_RETURN;
}
@@ -6573,17 +6614,13 @@ void TABLE::mark_default_fields_for_write(bool is_insert)
for (field_ptr= default_field; *field_ptr; field_ptr++)
{
field= (*field_ptr);
- if (field->default_value)
+ if (is_insert && field->default_value)
{
- if (is_insert)
- {
- bitmap_set_bit(write_set, field->field_index);
- field->default_value->expr_item->
- walk(&Item::register_field_in_read_map, 1, 0);
- }
+ bitmap_set_bit(write_set, field->field_index);
+ field->default_value->expr_item->
+ walk(&Item::register_field_in_read_map, 1, 0);
}
- else if ((is_insert && field->has_insert_default_function()) ||
- (!is_insert && field->has_update_default_function()))
+ else if (!is_insert && field->has_update_default_function())
bitmap_set_bit(write_set, field->field_index);
}
DBUG_VOID_RETURN;
@@ -7314,7 +7351,8 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
{
if (!update_command)
{
- if (field->default_value)
+ if (field->default_value &&
+ (field->default_value->flags || field->flags & BLOB_FLAG))
res|= (field->default_value->expr_item->save_in_field(field, 0) < 0);
else
res|= field->evaluate_insert_default_function();
@@ -7451,7 +7489,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);
@@ -7462,6 +7500,59 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
}
+bool TABLE::insert_all_rows_into(THD *thd, TABLE *dest, bool with_cleanup)
+{
+ int write_err= 0;
+
+ DBUG_ENTER("TABLE::insert_all_rows_into");
+
+ if (with_cleanup)
+ {
+ if ((write_err= dest->file->ha_delete_all_rows()))
+ goto err;
+ }
+
+ if (file->indexes_are_disabled())
+ dest->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 (dest->no_rows)
+ dest->file->extra(HA_EXTRA_NO_ROWS);
+ else
+ {
+ /* update table->file->stats.records */
+ file->info(HA_STATUS_VARIABLE);
+ dest->file->ha_start_bulk_insert(file->stats.records);
+ }
+
+ while (!file->ha_rnd_next(dest->record[1]))
+ {
+ write_err= dest->file->ha_write_tmp_row(dest->record[1]);
+ if (write_err)
+ goto err;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ goto err_killed;
+ }
+ }
+ if (!dest->no_rows && dest->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
@@ -7502,20 +7593,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
@@ -7623,7 +7718,8 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
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();
@@ -7793,6 +7889,7 @@ bool TABLE_LIST::is_with_table()
return derived && derived->with_element;
}
+
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
@@ -7808,3 +7905,186 @@ double KEY::actual_rec_per_key(uint i)
return (is_statistics_from_stat_tables ?
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->walk(&Item::exclusive_dependence_on_table_processor,
+ 0, (void *) &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;
+ 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;
+ new_cond->argument_list()->push_back(fix, thd->mem_root);
+ }
+ 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)
+ 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;
+}