summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc789
1 files changed, 590 insertions, 199 deletions
diff --git a/sql/table.cc b/sql/table.cc
index 24b78d24b71..7bb5986f0f1 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009-2011, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,6 +37,7 @@
#include "my_md5.h"
#include "my_bit.h"
#include "sql_select.h"
+#include "sql_derived.h"
#include "mdl.h" // MDL_wait_for_graph_visitor
/* INFORMATION_SCHEMA name */
@@ -836,8 +838,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->db_record_offset= 1;
if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
share->blob_ptr_size= portable_sizeof_char_ptr;
- /* Set temporarily a good value for db_low_byte_first */
- share->db_low_byte_first= test(legacy_db_type != DB_TYPE_ISAM);
error=4;
share->max_rows= uint4korr(head+18);
share->min_rows= uint4korr(head+22);
@@ -1592,7 +1592,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
key_part->null_bit= field->null_bit;
key_part->store_length+=HA_KEY_NULL_LENGTH;
keyinfo->flags|=HA_NULL_PART_KEY;
- keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
keyinfo->key_length+= HA_KEY_NULL_LENGTH;
}
if (field->type() == MYSQL_TYPE_BLOB ||
@@ -1604,7 +1603,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
key_part->key_part_flag|= HA_BLOB_PART;
else
key_part->key_part_flag|= HA_VAR_LENGTH_PART;
- keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
key_part->store_length+=HA_KEY_BLOB_LENGTH;
keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
}
@@ -1682,6 +1680,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
#endif
key_part->key_part_flag|= HA_PART_KEY_SEG;
}
+ if (field->real_maybe_null())
+ key_part->key_part_flag|= HA_NULL_PART;
/*
Sometimes we can compare key parts for equality with memcmp.
But not always.
@@ -1793,7 +1793,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->can_cmp_whole_record= (share->blob_fields == 0 &&
share->varchar_fields == 0);
- share->db_low_byte_first= handler_file->low_byte_first();
share->column_bitmap_size= bitmap_buffer_size(share->fields);
if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root,
@@ -2058,7 +2057,12 @@ bool unpack_vcol_info_from_frm(THD *thd,
vcol_arena= table->expr_arena;
if (!vcol_arena)
{
- Query_arena expr_arena(&table->mem_root, Query_arena::STMT_INITIALIZED);
+ /*
+ We need to use CONVENTIONAL_EXECUTION here to ensure that
+ any new items created by fix_fields() are not reverted.
+ */
+ Query_arena expr_arena(&table->mem_root,
+ Query_arena::STMT_CONVENTIONAL_EXECUTION);
if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root,
sizeof(Query_arena))))
goto err;
@@ -2144,7 +2148,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
bool error_reported= FALSE;
uchar *record, *bitmaps;
Field **field_ptr, **vfield_ptr;
- uint8 save_view_prepare_mode= thd->lex->context_analysis_only;
+ 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));
@@ -2160,7 +2164,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
- if (!(outparam->alias= my_strdup(alias, MYF(MY_WME))))
+ if (outparam->alias.copy(alias, strlen(alias), table_alias_charset))
goto err;
outparam->quick_keys.init();
outparam->covering_keys.init();
@@ -2393,12 +2397,11 @@ partititon_err:
/* Check virtual columns against table's storage engine. */
if (share->vfields &&
- ((outparam->file &&
- !outparam->file->check_if_supported_virtual_columns())))
+ !(outparam->file &&
+ (outparam->file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS)))
{
- my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN,
- MYF(0), share->db_plugin ? plugin_name(share->db_plugin)->str :
- "Specified storage engine");
+ my_error(ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS, MYF(0),
+ plugin_name(share->db_plugin)->str);
error_reported= TRUE;
goto err;
}
@@ -2406,7 +2409,7 @@ partititon_err:
/* Allocate bitmaps */
bitmap_size= share->column_bitmap_size;
- if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*4)))
+ if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*5)))
goto err;
bitmap_init(&outparam->def_read_set,
(my_bitmap_map*) bitmaps, share->fields, FALSE);
@@ -2416,6 +2419,8 @@ partititon_err:
(my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE);
bitmap_init(&outparam->tmp_set,
(my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE);
+ bitmap_init(&outparam->eq_join_set,
+ (my_bitmap_map*) (bitmaps+bitmap_size*4), share->fields, FALSE);
outparam->default_column_bitmaps();
/* The table struct is now initialized; Open the table */
@@ -2479,7 +2484,7 @@ partititon_err:
HA_HAS_OWN_BINLOGGING);
thd->status_var.opened_tables++;
- thd->lex->context_analysis_only= save_view_prepare_mode;
+ thd->lex->context_analysis_only= save_context_analysis_only;
DBUG_RETURN (0);
err:
@@ -2492,9 +2497,9 @@ partititon_err:
#endif
outparam->file= 0; // For easier error checking
outparam->db_stat=0;
- thd->lex->context_analysis_only= save_view_prepare_mode;
+ thd->lex->context_analysis_only= save_context_analysis_only;
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
- my_free((void *) outparam->alias);
+ outparam->alias.free();
DBUG_RETURN (error);
}
@@ -2518,10 +2523,9 @@ int closefrm(register TABLE *table, bool free_share)
{
if (table->s->deleting)
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
- error=table->file->close();
+ error=table->file->ha_close();
}
- my_free((void *) table->alias);
- table->alias= 0;
+ table->alias.free();
if (table->expr_arena)
table->expr_arena->free_items();
if (table->field)
@@ -2742,13 +2746,17 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
{
int err_no;
char buff[FN_REFLEN];
- myf errortype= ME_ERROR+ME_WAITTANG;
+ myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log
DBUG_ENTER("open_table_error");
switch (error) {
case 7:
case 1:
- if (db_errno == ENOENT)
+ /*
+ Test if file didn't exists. We have to also test for EINVAL as this
+ may happen on windows when opening a file with a not legal file name
+ */
+ if (db_errno == ENOENT || db_errno == EINVAL)
my_error(ER_NO_SUCH_TABLE, MYF(0), share->db.str, share->table_name.str);
else
{
@@ -3010,7 +3018,7 @@ File create_frm(THD *thd, const char *name, const char *db,
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
create_flags|= O_EXCL | O_NOFOLLOW;
- /* Fix this when we have new .frm files; Current limit is 4G rows (QQ) */
+ /* Fix this when we have new .frm files; Current limit is 4G rows (TODO) */
if (create_info->max_rows > UINT_MAX32)
create_info->max_rows= UINT_MAX32;
if (create_info->min_rows > UINT_MAX32)
@@ -3383,7 +3391,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
const TABLE_FIELD_TYPE *field_def= table_def->field;
DBUG_ENTER("table_check_intact");
DBUG_PRINT("info",("table: %s expected_count: %d",
- table->alias, table_def->count));
+ table->alias.c_ptr(), table_def->count));
/* Whether the table definition has already been validated. */
if (table->s->table_field_def_cache == table_def)
@@ -3398,7 +3406,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE,
ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
- table->alias, table_def->count, table->s->fields,
+ table->alias.c_ptr(), table_def->count, table->s->fields,
static_cast<int>(table->s->mysql_version),
MYSQL_VERSION_ID);
DBUG_RETURN(TRUE);
@@ -3406,7 +3414,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
else if (MYSQL_VERSION_ID == table->s->mysql_version)
{
report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,
- ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
+ ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED),
+ table->alias.c_ptr(),
table_def->count, table->s->fields);
DBUG_RETURN(TRUE);
}
@@ -3418,11 +3427,13 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
is backward compatible.
*/
}
- char buffer[STRING_BUFFER_USUAL_SIZE];
+ char buffer[1024];
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];
@@ -3437,7 +3448,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
*/
report_error(0, "Incorrect definition of table %s.%s: "
"expected column '%s' at position %d, found '%s'.",
- table->s->db.str, table->alias, field_def->name.str, i,
+ table->s->db.str, table->alias.c_ptr(),
+ field_def->name.str, i,
field->field_name);
}
field->sql_type(sql_type);
@@ -3463,7 +3475,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
report_error(0, "Incorrect definition of table %s.%s: "
"expected column '%s' at position %d to have type "
- "%s, found type %s.", table->s->db.str, table->alias,
+ "%s, found type %s.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->type.str,
sql_type.c_ptr_safe());
error= TRUE;
@@ -3473,7 +3486,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "Incorrect definition of table %s.%s: "
"expected the type of column '%s' at position %d "
"to have character set '%s' but the type has no "
- "character set.", table->s->db.str, table->alias,
+ "character set.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->cset.str);
error= TRUE;
}
@@ -3483,7 +3497,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "Incorrect definition of table %s.%s: "
"expected the type of column '%s' at position %d "
"to have character set '%s' but found "
- "character set '%s'.", table->s->db.str, table->alias,
+ "character set '%s'.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->cset.str,
field->charset()->csname);
error= TRUE;
@@ -3494,7 +3509,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "Incorrect definition of table %s.%s: "
"expected column '%s' at position %d to have type %s "
" but the column is not found.",
- table->s->db.str, table->alias,
+ table->s->db.str, table->alias.c_ptr(),
field_def->name.str, i, field_def->type.str);
error= TRUE;
}
@@ -3713,12 +3728,8 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
s->table_name.str,
tl->alias);
/* Fix alias if table name changes. */
- if (strcmp(alias, tl->alias))
- {
- uint length= (uint) strlen(tl->alias)+1;
- alias= (char*) my_realloc((char*) alias, length, MYF(MY_WME));
- memcpy((char*) alias, tl->alias, length);
- }
+ if (strcmp(alias.c_ptr(), tl->alias))
+ alias.copy(tl->alias, strlen(tl->alias), alias.charset());
tablenr= thd->current_tablenr++;
used_fields= 0;
@@ -3733,6 +3744,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
fulltext_searched= 0;
file->ha_start_of_new_statement();
reginfo.impossible_range= 0;
+ created= TRUE;
/* Catch wrong handling of the auto_increment_field_not_null. */
DBUG_ASSERT(!auto_increment_field_not_null);
@@ -3750,6 +3762,14 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
/* mark the record[0] uninitialized */
TRASH(record[0], s->reclength);
+ /*
+ Initialize the null marker bits, to ensure that if we are doing a read
+ of only selected columns (like in keyread), all null markers are
+ initialized.
+ */
+ memset(record[0], 255, s->null_bytes);
+ memset(record[1], 255, s->null_bytes);
+
/* Tables may be reused in a sub statement. */
DBUG_ASSERT(!file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
}
@@ -3835,129 +3855,112 @@ void TABLE_LIST::calc_md5(char *buffer)
/**
- @brief Set underlying table for table place holder of view.
-
- @details
-
- Replace all views that only use one table with the table itself. This
- allows us to treat the view as a simple table and even update it (it is a
- kind of optimization).
+ @brief
+ Create field translation for mergeable derived table/view.
- @note
+ @param thd Thread handle
- This optimization is potentially dangerous as it makes views
- masquerade as base tables: Views don't have the pointer TABLE_LIST::table
- set to non-@c NULL.
+ @details
+ Create field translation for mergeable derived table/view.
- We may have the case where a view accesses tables not normally accessible
- in the current Security_context (only in the definer's
- Security_context). According to the table's GRANT_INFO (TABLE::grant),
- access is fulfilled, but this is implicitly meant in the definer's security
- context. Hence we must never look at only a TABLE's GRANT_INFO without
- looking at the one of the referring TABLE_LIST.
+ @return FALSE ok.
+ @return TRUE an error occur.
*/
-void TABLE_LIST::set_underlying_merge()
+bool TABLE_LIST::create_field_translation(THD *thd)
{
- TABLE_LIST *tbl;
+ Item *item;
+ Field_translator *transl;
+ SELECT_LEX *select= get_single_select();
+ List_iterator_fast<Item> it(select->item_list);
+ uint field_count= 0;
+ Query_arena *arena= thd->stmt_arena, backup;
+ bool res= FALSE;
- if ((tbl= merge_underlying_list))
+ used_items.empty();
+
+ if (field_translation)
{
- /* This is a view. Process all tables of view */
- DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE);
- do
+ /*
+ Update items in the field translation aftet view have been prepared.
+ It's needed because some items in the select list, like IN subselects,
+ might be substituted for optimized ones.
+ */
+ if (is_view() && get_unit()->prepared && !field_translation_updated)
{
- if (tbl->merge_underlying_list) // This is a view
+ while ((item= it++))
{
- DBUG_ASSERT(tbl->view &&
- tbl->effective_algorithm == VIEW_ALGORITHM_MERGE);
- /*
- This is the only case where set_ancestor is called on an object
- that may not be a view (in which case ancestor is 0)
- */
- tbl->merge_underlying_list->set_underlying_merge();
+ field_translation[field_count++].item= item;
}
- } while ((tbl= tbl->next_local));
-
- if (!multitable_view)
- {
- table= merge_underlying_list->table;
- schema_table= merge_underlying_list->schema_table;
+ field_translation_updated= TRUE;
}
+
+ return FALSE;
+ }
+
+ if (arena->is_conventional())
+ arena= 0; // For easier test
+ else
+ thd->set_n_backup_active_arena(arena, &backup);
+
+ /* Create view fields translation table */
+
+ if (!(transl=
+ (Field_translator*)(thd->stmt_arena->
+ alloc(select->item_list.elements *
+ sizeof(Field_translator)))))
+ {
+ res= TRUE;
+ goto exit;
+ }
+
+ while ((item= it++))
+ {
+ transl[field_count].name= item->name;
+ transl[field_count++].item= item;
}
+ field_translation= transl;
+ field_translation_end= transl + field_count;
+
+exit:
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ return res;
}
-/*
- setup fields of placeholder of merged VIEW
+/**
+ @brief
+ Create field translation for mergeable derived table/view.
- SYNOPSIS
- TABLE_LIST::setup_underlying()
- thd - thread handler
+ @param thd Thread handle
- DESCRIPTION
- It is:
- - preparing translation table for view columns
- If there are underlying view(s) procedure first will be called for them.
+ @details
+ Create field translation for mergeable derived table/view.
- RETURN
- FALSE - OK
- TRUE - error
+ @return FALSE ok.
+ @return TRUE an error occur.
*/
bool TABLE_LIST::setup_underlying(THD *thd)
{
DBUG_ENTER("TABLE_LIST::setup_underlying");
- if (!field_translation && merge_underlying_list)
+ if (!view || (!field_translation && merge_underlying_list))
{
- Field_translator *transl;
- SELECT_LEX *select= &view->select_lex;
- Item *item;
- TABLE_LIST *tbl;
- List_iterator_fast<Item> it(select->item_list);
- uint field_count= 0;
-
- if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*) &field_count))
- {
- DBUG_RETURN(TRUE);
- }
-
- for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
- {
- if (tbl->merge_underlying_list &&
- tbl->setup_underlying(thd))
- {
- DBUG_RETURN(TRUE);
- }
- }
-
- /* Create view fields translation table */
-
- if (!(transl=
- (Field_translator*)(thd->stmt_arena->
- alloc(select->item_list.elements *
- sizeof(Field_translator)))))
- {
+ SELECT_LEX *select= get_single_select();
+
+ if (create_field_translation(thd))
DBUG_RETURN(TRUE);
- }
-
- while ((item= it++))
- {
- transl[field_count].name= item->name;
- transl[field_count++].item= item;
- }
- field_translation= transl;
- field_translation_end= transl + field_count;
- /* TODO: use hash for big number of fields */
/* full text function moving to current select */
- if (view->select_lex.ftfunc_list->elements)
+ if (select->ftfunc_list->elements)
{
Item_func_match *ifm;
SELECT_LEX *current_select= thd->lex->current_select;
List_iterator_fast<Item_func_match>
- li(*(view->select_lex.ftfunc_list));
+ li(*(select_lex->ftfunc_list));
while ((ifm= li++))
current_select->ftfunc_list->push_front(ifm);
}
@@ -3967,7 +3970,7 @@ bool TABLE_LIST::setup_underlying(THD *thd)
/*
- Prepare where expression of view
+ Prepare where expression of derived table/view
SYNOPSIS
TABLE_LIST::prep_where()
@@ -3991,7 +3994,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
- if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
+ if (tbl->is_view_or_derived() &&
+ tbl->prep_where(thd, conds, no_where_clause))
{
DBUG_RETURN(TRUE);
}
@@ -3999,6 +4003,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
if (where)
{
+ if (where->fixed)
+ where->update_used_tables();
if (!where->fixed && where->fix_fields(thd, &where))
{
DBUG_RETURN(TRUE);
@@ -4031,7 +4037,13 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
}
}
if (tbl == 0)
+ {
+ if (*conds && !(*conds)->fixed)
+ (*conds)->fix_fields(thd, conds);
*conds= and_conds(*conds, where->copy_andor_structure(thd));
+ if (*conds && !(*conds)->fixed)
+ (*conds)->fix_fields(thd, conds);
+ }
if (arena)
thd->restore_active_arena(arena, &backup);
where_processed= TRUE;
@@ -4070,10 +4082,11 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
DBUG_PRINT("info", ("alias: %s", table->alias));
if (table->on_expr)
cond= table->on_expr->copy_andor_structure(thd);
- if (!table->nested_join)
+ if (!table->view)
DBUG_RETURN(cond);
- List_iterator<TABLE_LIST> li(table->nested_join->join_list);
- while (TABLE_LIST *tbl= li++)
+ for (TABLE_LIST *tbl= (TABLE_LIST*)table->view->select_lex.table_list.first;
+ tbl;
+ tbl= tbl->next_local)
{
if (tbl->view && !is_cascaded)
continue;
@@ -4113,7 +4126,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
DBUG_ENTER("TABLE_LIST::prep_check_option");
bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED;
-
+ TABLE_LIST *merge_underlying_list= view->select_lex.get_table_list();
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
@@ -4130,7 +4143,6 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
if (where)
{
- DBUG_ASSERT(where->fixed);
check_option= where->copy_andor_structure(thd);
}
if (is_cascaded)
@@ -4226,10 +4238,14 @@ void TABLE_LIST::hide_view_error(THD *thd)
TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find)
{
/* is this real table and table which we are looking for? */
- if (table == table_to_find && merge_underlying_list == 0)
+ if (table == table_to_find && view == 0)
return this;
+ if (!view)
+ return 0;
- for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
+ for (TABLE_LIST *tbl= view->select_lex.get_table_list();
+ tbl;
+ tbl= tbl->next_local)
{
TABLE_LIST *result;
if ((result= tbl->find_underlying_table(table_to_find)))
@@ -4311,7 +4327,12 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
table_map map,
TABLE_LIST *view_arg)
{
- for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
+ if (!select_lex)
+ return FALSE;
+ DBUG_ASSERT(is_merged_derived());
+ for (TABLE_LIST *tbl= get_single_select()->get_table_list();
+ tbl;
+ tbl= tbl->next_local)
{
if (tbl->table)
{
@@ -4353,8 +4374,10 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
}
else
{
- DBUG_ASSERT(view && merge_underlying_list);
- for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
+ DBUG_ASSERT(is_view_or_derived() && is_merged_derived());
+ for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first;
+ tbl;
+ tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
return TRUE;
}
@@ -4380,7 +4403,7 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
*/
bool TABLE_LIST::is_leaf_for_name_resolution()
{
- return (view || is_natural_join || is_join_columns_complete ||
+ return (is_merged_derived() || is_natural_join || is_join_columns_complete ||
!nested_join);
}
@@ -4518,7 +4541,11 @@ void TABLE_LIST::register_want_access(ulong want_access)
if (table)
table->grant.want_privilege= want_access;
}
- for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
+ if (!view)
+ return;
+ for (TABLE_LIST *tbl= view->select_lex.get_table_list();
+ tbl;
+ tbl= tbl->next_local)
tbl->register_want_access(want_access);
}
@@ -4754,14 +4781,23 @@ const char *Natural_join_column::db_name()
table_ref->table->s->db.str) ||
(table_ref->schema_table &&
is_infoschema_db(table_ref->table->s->db.str,
- table_ref->table->s->db.length)));
+ table_ref->table->s->db.length)) ||
+ table_ref->is_materialized_derived());
return table_ref->db;
}
GRANT_INFO *Natural_join_column::grant()
{
- if (view_field)
+/* if (view_field)
+ return &(table_ref->grant);
+ return &(table_ref->table->grant);*/
+ /*
+ Have to check algorithm because merged derived also has
+ field_translation.
+ */
+//if (table_ref->effective_algorithm == DTYPE_ALGORITHM_MERGE)
+ if (table_ref->is_merged_derived())
return &(table_ref->grant);
return &(table_ref->table->grant);
}
@@ -4842,7 +4878,17 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
{
DBUG_RETURN(field);
}
- Item *item= new Item_direct_view_ref(view, field_ref, name);
+ Item *item= new Item_direct_view_ref(&view->view->select_lex.context,
+ field_ref, view->alias,
+ name, view);
+ /*
+ Force creation of nullable item for the result tmp table for outer joined
+ views/derived tables.
+ */
+ if (view->table && view->table->maybe_null)
+ item->maybe_null= TRUE;
+ /* Save item in case we will need to fall back to materialization. */
+ view->used_items.push_back(item);
DBUG_RETURN(item);
}
@@ -4896,8 +4942,7 @@ void Field_iterator_table_ref::set_field_iterator()
/* This is a merge view, so use field_translation. */
else if (table_ref->field_translation)
{
- DBUG_ASSERT(table_ref->view &&
- table_ref->effective_algorithm == VIEW_ALGORITHM_MERGE);
+ DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
table_ref->alias));
@@ -5522,43 +5567,93 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl)
/**
+ @brief
Allocate space for keys
- @param key_count number of keys to allocate.
+ @param key_count number of keys to allocate
@details
- Allocates space enough to fit 'key_count' keys for this table.
+ The function allocates memory to fit 'key_count' keys for this table.
- @return FALSE space was successfully allocated.
- @return TRUE an error occur.
+ @return FALSE space was successfully allocated
+ @return TRUE an error occur
*/
bool TABLE::alloc_keys(uint key_count)
{
- DBUG_ASSERT(!s->keys);
key_info= s->key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*key_count);
+ s->keys= 0;
max_keys= key_count;
return !(key_info);
}
+void TABLE::create_key_part_by_field(KEY *keyinfo,
+ KEY_PART_INFO *key_part_info,
+ Field *field, uint fieldnr)
+{
+ field->flags|= PART_KEY_FLAG;
+ key_part_info->null_bit= field->null_bit;
+ key_part_info->null_offset= (uint) (field->null_ptr -
+ (uchar*) record[0]);
+ key_part_info->field= field;
+ key_part_info->fieldnr= fieldnr;
+ key_part_info->offset= field->offset(record[0]);
+ key_part_info->length= (uint16) field->pack_length();
+ keyinfo->key_length+= key_part_info->length;
+ key_part_info->key_part_flag= 0;
+ /* TODO:
+ The below method of computing the key format length of the
+ key part is a copy/paste from opt_range.cc, and table.cc.
+ This should be factored out, e.g. as a method of Field.
+ In addition it is not clear if any of the Field::*_length
+ methods is supposed to compute the same length. If so, it
+ might be reused.
+ */
+ key_part_info->store_length= key_part_info->length;
+
+ if (field->real_maybe_null())
+ {
+ key_part_info->store_length+= HA_KEY_NULL_LENGTH;
+ keyinfo->key_length+= HA_KEY_NULL_LENGTH;
+ }
+ if (field->type() == MYSQL_TYPE_BLOB ||
+ field->real_type() == MYSQL_TYPE_VARCHAR)
+ {
+ key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
+ keyinfo->key_length+= HA_KEY_BLOB_LENGTH; // ???
+ key_part_info->key_part_flag|=
+ field->type() == MYSQL_TYPE_BLOB ? HA_BLOB_PART: HA_VAR_LENGTH_PART;
+ }
+
+ key_part_info->type= (uint8) field->key_type();
+ key_part_info->key_type =
+ ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
+ (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
+ (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
+ 0 : FIELDFLAG_BINARY;
+}
+
+
/**
- Add a key to a temporary table
+ @brief
+ Add one key to a temporary table
@param key the number of the key
@param key_parts number of components of the key
@param next_field_no the call-back function that returns the number of
the field used as the next component of the key
@param arg the argument for the above function
- @param unique Is it unique index
+ @param unique TRUE <=> it is a unique index
@details
- The function adds a new key to the table that is assumed to be
- temprary table. The call-back function must at each call must return
- the number of the field that used as next component of this key
+ The function adds a new key to the table that is assumed to be a temporary
+ table. At each its invocation the call-back function must return
+ the number of the field that is used as the next component of this key.
@return FALSE is a success
@return TRUE if a failure
+
*/
bool TABLE::add_tmp_key(uint key, uint key_parts,
@@ -5592,59 +5687,63 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
if (!keyinfo->rec_per_key)
return TRUE;
bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts);
+
for (i= 0; i < key_parts; i++)
{
- reg_field= field + next_field_no(arg);
+ uint fld_idx= next_field_no(arg);
+ reg_field= field + fld_idx;
if (key_start)
(*reg_field)->key_start.set_bit(key);
+ (*reg_field)->part_of_key.set_bit(key);
+ create_key_part_by_field(keyinfo, key_part_info, *reg_field, fld_idx+1);
key_start= FALSE;
- (*reg_field)->part_of_key.set_bit(key);
- (*reg_field)->flags|= PART_KEY_FLAG;
- key_part_info->null_bit= (*reg_field)->null_bit;
- key_part_info->null_offset= (uint) ((*reg_field)->null_ptr -
- (uchar*) record[0]);
- key_part_info->field= *reg_field;
- key_part_info->offset= (*reg_field)->offset(record[0]);
- key_part_info->length= (uint16) (*reg_field)->pack_length();
- keyinfo->key_length+= key_part_info->length;
- key_part_info->key_part_flag= 0;
- /* TODO:
- The below method of computing the key format length of the
- key part is a copy/paste from opt_range.cc, and table.cc.
- This should be factored out, e.g. as a method of Field.
- In addition it is not clear if any of the Field::*_length
- methods is supposed to compute the same length. If so, it
- might be reused.
- */
- key_part_info->store_length= key_part_info->length;
-
- if ((*reg_field)->real_maybe_null())
- {
- key_part_info->store_length+= HA_KEY_NULL_LENGTH;
- keyinfo->key_length+= HA_KEY_NULL_LENGTH;
- if (unique)
- keyinfo->flags|= HA_NULL_ARE_EQUAL; // def. that NULL == NULL
- }
- if ((*reg_field)->type() == MYSQL_TYPE_BLOB ||
- (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR)
- {
- key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
- keyinfo->key_length+= HA_KEY_BLOB_LENGTH; // ???
- }
-
- key_part_info->type= (uint8) (*reg_field)->key_type();
- key_part_info->key_type =
- ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
- 0 : FIELDFLAG_BINARY;
key_part_info++;
}
+
set_if_bigger(s->max_key_length, keyinfo->key_length);
s->keys++;
return FALSE;
}
+/*
+ @brief
+ Drop all indexes except specified one.
+
+ @param key_to_save the key to save
+
+ @details
+ Drop all indexes on this table except 'key_to_save'. The saved key becomes
+ key #0. Memory occupied by key parts of dropped keys are freed.
+ If the 'key_to_save' is negative then all keys are freed.
+*/
+
+void TABLE::use_index(int key_to_save)
+{
+ uint i= 1;
+ DBUG_ASSERT(!created && key_to_save < (int)s->keys);
+ if (key_to_save >= 0)
+ /* Save the given key. */
+ memmove(key_info, key_info + key_to_save, sizeof(KEY));
+ else
+ /* Drop all keys; */
+ i= 0;
+
+ s->keys= (key_to_save < 0) ? 0 : 1;
+}
+
+/*
+ Return TRUE if the table is filled at execution phase
+
+ (and so, the optimizer must not do anything that depends on the contents of
+ the table, like range analysis or constant table detection)
+*/
+
+bool TABLE::is_filled_at_execution()
+{
+ return test(pos_in_table_list->jtbm_subselect ||
+ pos_in_table_list->is_active_sjm());
+}
+
/*
Cleanup this table for re-execution.
@@ -5678,6 +5777,7 @@ void TABLE_LIST::reinit_before_use(THD *thd)
mdl_request.ticket= NULL;
}
+
/*
Return subselect that contains the FROM list this table is taken from
@@ -6020,6 +6120,297 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write)
DBUG_RETURN(0);
}
+/*
+ @brief Reset const_table flag
+
+ @detail
+ Reset const_table flag for this table. If this table is a merged derived
+ table/view the flag is recursively reseted for all tables of the underlying
+ select.
+*/
+
+void TABLE_LIST::reset_const_table()
+{
+ table->const_table= 0;
+ if (is_merged_derived())
+ {
+ SELECT_LEX *select_lex= get_unit()->first_select();
+ TABLE_LIST *tl;
+ List_iterator<TABLE_LIST> ti(select_lex->leaf_tables);
+ while ((tl= ti++))
+ tl->reset_const_table();
+ }
+}
+
+
+/*
+ @brief Run derived tables/view handling phases on underlying select_lex.
+
+ @param lex LEX for this thread
+ @param phases derived tables/views handling phases to run
+ (set of DT_XXX constants)
+ @details
+ This function runs this derived table through specified 'phases'.
+ Underlying tables of this select are handled prior to this derived.
+ 'lex' is passed as an argument to called functions.
+
+ @return TRUE on error
+ @return FALSE ok
+*/
+
+bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
+{
+ SELECT_LEX_UNIT *unit= get_unit();
+ if (unit)
+ {
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ if (sl->handle_derived(lex, phases))
+ return TRUE;
+ return mysql_handle_single_derived(lex, this, phases);
+ }
+ return FALSE;
+}
+
+
+/**
+ @brief
+ Return unit of this derived table/view
+
+ @return reference to a unit if it's a derived table/view.
+ @return 0 when it's not a derived table/view.
+*/
+
+st_select_lex_unit *TABLE_LIST::get_unit()
+{
+ return (view ? &view->unit : derived);
+}
+
+
+/**
+ @brief
+ Return select_lex of this derived table/view
+
+ @return select_lex of this derived table/view.
+ @return 0 when it's not a derived table.
+*/
+
+st_select_lex *TABLE_LIST::get_single_select()
+{
+ SELECT_LEX_UNIT *unit= get_unit();
+ return (unit ? unit->first_select() : 0);
+}
+
+
+/**
+ @brief
+ Attach a join table list as a nested join to this TABLE_LIST.
+
+ @param join_list join table list to attach
+
+ @details
+ This function wraps 'join_list' into a nested_join of this table, thus
+ turning it to a nested join leaf.
+*/
+
+void TABLE_LIST::wrap_into_nested_join(List<TABLE_LIST> &join_list)
+{
+ TABLE_LIST *tl;
+ /*
+ Walk through derived table top list and set 'embedding' to point to
+ the nesting table.
+ */
+ nested_join->join_list.empty();
+ List_iterator_fast<TABLE_LIST> li(join_list);
+ nested_join->join_list= join_list;
+ while ((tl= li++))
+ {
+ tl->embedding= this;
+ tl->join_list= &nested_join->join_list;
+ }
+}
+
+
+/**
+ @brief
+ Initialize this derived table/view
+
+ @param thd Thread handle
+
+ @details
+ This function makes initial preparations of this derived table/view for
+ further processing:
+ if it's a derived table this function marks it either as mergeable or
+ materializable
+ creates temporary table for name resolution purposes
+ creates field translation for mergeable derived table/view
+
+ @return TRUE an error occur
+ @return FALSE ok
+*/
+
+bool TABLE_LIST::init_derived(THD *thd, bool init_view)
+{
+ SELECT_LEX *first_select= get_single_select();
+ SELECT_LEX_UNIT *unit= get_unit();
+
+ if (!unit)
+ return FALSE;
+ /*
+ Check whether we can merge this derived table into main select.
+ Depending on the result field translation will or will not
+ be created.
+ */
+ TABLE_LIST *first_table= (TABLE_LIST *) first_select->table_list.first;
+ if (first_select->table_list.elements > 1 ||
+ (first_table && first_table->is_multitable()))
+ set_multitable();
+
+ unit->derived= this;
+ if (init_view && !view)
+ {
+ /* This is all what we can do for a derived table for now. */
+ set_derived();
+ }
+
+ if (!is_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->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI))
+ set_merged_derived();
+ else
+ set_materialized_derived();
+ }
+ /*
+ Derived tables/view are materialized prior to UPDATE, thus we can skip
+ them from table uniqueness check
+ */
+ if (is_materialized_derived())
+ {
+ unit->master_unit()->set_unique_exclude();
+ }
+ /*
+ Create field translation for mergeable derived tables/views.
+ For derived tables field translation can be created only after
+ unit is prepared so all '*' are get unrolled.
+ */
+ if (is_merged_derived())
+ {
+ if (is_view() || unit->prepared)
+ create_field_translation(thd);
+ }
+
+ return FALSE;
+}
+
+
+/**
+ @brief
+ Retrieve number of rows in the table
+
+ @details
+ Retrieve number of rows in the table referred by this TABLE_LIST and
+ store it in the table's stats.records variable. If this TABLE_LIST refers
+ to a materialized derived table/view then the estimated number of rows of
+ the derived table/view is used instead.
+
+ @return 0 ok
+ @return non zero error
+*/
+
+int TABLE_LIST::fetch_number_of_rows()
+{
+ int error= 0;
+ if (is_materialized_derived() && !fill_me)
+
+ {
+ table->file->stats.records= ((select_union*)derived->result)->records;
+ set_if_bigger(table->file->stats.records, 2);
+ }
+ else
+ error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ return error;
+}
+
+/*
+ Procedure of keys generation for result tables of materialized derived
+ tables/views.
+
+ A key is generated for each equi-join pair derived table-another table.
+ Each generated key consists of fields of derived table used in equi-join.
+ Example:
+
+ SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN
+ t1 ON tt.f1=t1.f3 and tt.f2.=t1.f4;
+ In this case for the derived table tt one key will be generated. It will
+ consist of two parts f1 and f2.
+ Example:
+
+ SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN
+ t1 ON tt.f1=t1.f3 JOIN
+ t2 ON tt.f2=t2.f4;
+ In this case for the derived table tt two keys will be generated.
+ One key over f1 field, and another key over f2 field.
+ Currently optimizer may choose to use only one such key, thus the second
+ one will be dropped after range optimizer is finished.
+ See also JOIN::drop_unused_derived_keys function.
+ Example:
+
+ SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN
+ t1 ON tt.f1=a_function(t1.f3);
+ In this case for the derived table tt one key will be generated. It will
+ consist of one field - f1.
+*/
+
+
+
+/*
+ @brief
+ Change references to underlying items of a merged derived table/view
+ for fields in derived table's result table.
+
+ @return FALSE ok
+ @return TRUE Out of memory
+*/
+bool TABLE_LIST::change_refs_to_fields()
+{
+ List_iterator<Item> li(used_items);
+ Item_direct_ref *ref;
+ Field_iterator_view field_it;
+ THD *thd= table->in_use;
+ DBUG_ASSERT(is_merged_derived());
+
+ if (!used_items.elements)
+ return FALSE;
+
+ materialized_items= (Item**)thd->calloc(sizeof(void*) * table->s->fields);
+
+ while ((ref= (Item_direct_ref*)li++))
+ {
+ uint idx;
+ Item *orig_item= *ref->ref;
+ field_it.set(this);
+ for (idx= 0; !field_it.end_of_fields(); field_it.next(), idx++)
+ {
+ if (field_it.item() == orig_item)
+ break;
+ }
+ DBUG_ASSERT(!field_it.end_of_fields());
+ if (!materialized_items[idx])
+ {
+ materialized_items[idx]= new Item_field(table->field[idx]);
+ if (!materialized_items[idx])
+ return TRUE;
+ }
+ ref->ref= materialized_items + idx;
+ }
+
+ return FALSE;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/