summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc475
1 files changed, 473 insertions, 2 deletions
diff --git a/sql/table.cc b/sql/table.cc
index 7c1701cc4d3..810ade44b6c 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1755,8 +1755,6 @@ void st_table_list::set_ancestor()
*/
tbl->ancestor->set_ancestor();
}
- if (tbl->multitable_view)
- multitable_view= TRUE;
} while ((tbl= tbl->next_local));
if (!multitable_view)
@@ -2182,8 +2180,290 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
}
+/*
+ Test if this is a leaf with respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::is_leaf_for_name_resolution()
+
+ DESCRIPTION
+ A table reference is a leaf with respect to name resolution if
+ it is either a leaf node in a nested join tree (table, view,
+ schema table, subquery), or an inner node that represents a
+ NATURAL/USING join, or a nested join with materialized join
+ columns.
+
+ RETURN
+ TRUE if a leaf, FALSE otherwise.
+*/
+bool st_table_list::is_leaf_for_name_resolution()
+{
+ return (view || is_natural_join || is_join_columns_complete ||
+ !nested_join);
+}
+
+
+/*
+ Retrieve the first (left-most) leaf in a nested join tree with
+ respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::first_leaf_for_name_resolution()
+
+ DESCRIPTION
+ Given that 'this' is a nested table reference, recursively walk
+ down the left-most children of 'this' until we reach a leaf
+ table reference with respect to name resolution.
+
+ IMPLEMENTATION
+ The left-most child of a nested table reference is the last element
+ in the list of children because the children are inserted in
+ reverse order.
+
+ RETURN
+ If 'this' is a nested table reference - the left-most child of
+ the tree rooted in 'this',
+ else return 'this'
+*/
+
+TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
+{
+ TABLE_LIST *cur_table_ref;
+ NESTED_JOIN *cur_nested_join;
+ LINT_INIT(cur_table_ref);
+
+ if (is_leaf_for_name_resolution())
+ return this;
+ DBUG_ASSERT(nested_join);
+
+ for (cur_nested_join= nested_join;
+ cur_nested_join;
+ cur_nested_join= cur_table_ref->nested_join)
+ {
+ List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
+ cur_table_ref= it++;
+ /*
+ If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse
+ order, thus the first operand is already at the front of the list.
+ */
+ if (!(cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
+ {
+ TABLE_LIST *next;
+ while ((next= it++))
+ cur_table_ref= next;
+ }
+ if (cur_table_ref->is_leaf_for_name_resolution())
+ break;
+ }
+ return cur_table_ref;
+}
+
+
+/*
+ Retrieve the last (right-most) leaf in a nested join tree with
+ respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::last_leaf_for_name_resolution()
+
+ DESCRIPTION
+ Given that 'this' is a nested table reference, recursively walk
+ down the right-most children of 'this' until we reach a leaf
+ table reference with respect to name resolution.
+
+ IMPLEMENTATION
+ The right-most child of a nested table reference is the first
+ element in the list of children because the children are inserted
+ in reverse order.
+
+ RETURN
+ - If 'this' is a nested table reference - the right-most child of
+ the tree rooted in 'this',
+ - else - 'this'
+*/
+
+TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
+{
+ TABLE_LIST *cur_table_ref= this;
+ NESTED_JOIN *cur_nested_join;
+
+ if (is_leaf_for_name_resolution())
+ return this;
+ DBUG_ASSERT(nested_join);
+
+ for (cur_nested_join= nested_join;
+ cur_nested_join;
+ cur_nested_join= cur_table_ref->nested_join)
+ {
+ /*
+ If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse
+ order, thus the last operand is in the end of the list.
+ */
+ if ((cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
+ {
+ List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
+ TABLE_LIST *next;
+ cur_table_ref= it++;
+ while ((next= it++))
+ cur_table_ref= next;
+ }
+ else
+ cur_table_ref= cur_nested_join->join_list.head();
+ if (cur_table_ref->is_leaf_for_name_resolution())
+ break;
+ }
+ return cur_table_ref;
+}
+
+
+Natural_join_column::Natural_join_column(Field_translator *field_param,
+ TABLE_LIST *tab)
+{
+ DBUG_ASSERT(tab->field_translation);
+ view_field= field_param;
+ table_field= NULL;
+ table_ref= tab;
+ is_common= FALSE;
+}
+
+
+Natural_join_column::Natural_join_column(Field *field_param,
+ TABLE_LIST *tab)
+{
+ DBUG_ASSERT(tab->table == field_param->table);
+ table_field= field_param;
+ view_field= NULL;
+ table_ref= tab;
+ is_common= FALSE;
+}
+
+
+const char *Natural_join_column::name()
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return view_field->name;
+ }
+
+ return table_field->field_name;
+}
+
+
+Item *Natural_join_column::create_item(THD *thd)
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return create_view_field(thd, table_ref, &view_field->item,
+ view_field->name);
+ }
+ return new Item_field(thd, &thd->lex->current_select->context, table_field);
+}
+
+
+Field *Natural_join_column::field()
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return NULL;
+ }
+ return table_field;
+}
+
+
+const char *Natural_join_column::table_name()
+{
+ return table_ref->alias;
+ /*
+ TODO:
+ I think that it is sufficient to return just
+ table->alias, which is correctly set to either
+ the view name, the table name, or the alias to
+ the table reference (view or stored table).
+ */
+#ifdef NOT_YET
+ if (view_field)
+ return table_ref->view_name.str;
+
+ DBUG_ASSERT(!strcmp(table_ref->table_name,
+ table_ref->table->s->table_name));
+ return table_ref->table_name;
+}
+#endif
+}
+
+
+const char *Natural_join_column::db_name()
+{
+ if (view_field)
+ return table_ref->view_db.str;
+
+ DBUG_ASSERT(!strcmp(table_ref->db,
+ table_ref->table->s->db));
+ return table_ref->db;
+}
+
+
+GRANT_INFO *Natural_join_column::grant()
+{
+ if (view_field)
+ return &(table_ref->grant);
+ return &(table_ref->table->grant);
+}
+
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
+/*
+ Check the access rights for the current join column.
+ columns.
+
+ SYNOPSIS
+ Natural_join_column::check_grants()
+
+ DESCRIPTION
+ Check the access rights to a column from a natural join in a generic
+ way that hides the heterogeneity of the column representation - whether
+ it is a view or a stored table colum.
+
+ RETURN
+ FALSE The column can be accessed
+ TRUE There are no access rights to all equivalent columns
+*/
+
+bool
+Natural_join_column::check_grants(THD *thd, const char *name, uint length)
+{
+ GRANT_INFO *grant;
+ const char *db_name;
+ const char *table_name;
+
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ grant= &(table_ref->grant);
+ db_name= table_ref->view_db.str;
+ table_name= table_ref->view_name.str;
+ }
+ else
+ {
+ DBUG_ASSERT(table_field && view_field == NULL);
+ grant= &(table_ref->table->grant);
+ db_name= table_ref->table->s->db;
+ table_name= table_ref->table->s->table_name;
+ }
+
+ return check_grant_column(thd, grant, db_name, table_name, name, length);
+}
+#endif
+
+
void Field_iterator_view::set(TABLE_LIST *table)
{
+ DBUG_ASSERT(table->field_translation);
view= table;
ptr= table->field_translation;
array_end= table->field_translation_end;
@@ -2253,6 +2533,197 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
}
+void Field_iterator_natural_join::set(TABLE_LIST *table_ref)
+{
+ DBUG_ASSERT(table_ref->join_columns);
+ delete column_ref_it;
+
+ /*
+ TODO: try not to allocate new iterator every time. If we have to,
+ then check for out of memory condition.
+ */
+ column_ref_it= new List_iterator_fast<Natural_join_column>
+ (*(table_ref->join_columns));
+ cur_column_ref= (*column_ref_it)++;
+}
+
+
+void Field_iterator_natural_join::next()
+{
+ cur_column_ref= (*column_ref_it)++;
+ DBUG_ASSERT(!cur_column_ref || ! cur_column_ref->table_field ||
+ cur_column_ref->table_ref->table ==
+ cur_column_ref->table_field->table);
+}
+
+
+void Field_iterator_table_ref::set_field_iterator()
+{
+ DBUG_ENTER("Field_iterator_table_ref::set_field_iterator");
+ /*
+ If the table reference we are iterating over is a natural join, or it is
+ an operand of a natural join, and TABLE_LIST::join_columns contains all
+ the columns of the join operand, then we pick the columns from
+ TABLE_LIST::join_columns, instead of the orginial container of the
+ columns of the join operator.
+ */
+ if (table_ref->is_join_columns_complete)
+ {
+ /* Necesary, but insufficient conditions. */
+ DBUG_ASSERT(table_ref->is_natural_join ||
+ table_ref->nested_join ||
+ table_ref->join_columns &&
+ /* This is a merge view. */
+ ((table_ref->field_translation &&
+ table_ref->join_columns->elements ==
+ (ulong)(table_ref->field_translation_end -
+ table_ref->field_translation)) ||
+ /* This is stored table or a tmptable view. */
+ (!table_ref->field_translation &&
+ table_ref->join_columns->elements ==
+ table_ref->table->s->fields)));
+ field_it= &natural_join_it;
+ DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
+ table_ref->alias));
+ }
+ /* 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);
+ field_it= &view_field_it;
+ DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
+ table_ref->alias));
+ }
+ /* This is a base table or stored view. */
+ else
+ {
+ DBUG_ASSERT(table_ref->table || table_ref->view);
+ field_it= &table_field_it;
+ DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
+ table_ref->alias));
+ }
+ field_it->set(table_ref);
+ DBUG_VOID_RETURN;
+}
+
+
+void Field_iterator_table_ref::set(TABLE_LIST *table)
+{
+ DBUG_ASSERT(table);
+ first_leaf= table->first_leaf_for_name_resolution();
+ last_leaf= table->last_leaf_for_name_resolution();
+ DBUG_ASSERT(first_leaf && last_leaf);
+ table_ref= first_leaf;
+ set_field_iterator();
+}
+
+
+void Field_iterator_table_ref::next()
+{
+ /* Move to the next field in the current table reference. */
+ field_it->next();
+ /*
+ If all fields of the current table reference are exhausted, move to
+ the next leaf table reference.
+ */
+ if (field_it->end_of_fields() && table_ref != last_leaf)
+ {
+ table_ref= table_ref->next_name_resolution_table;
+ DBUG_ASSERT(table_ref);
+ set_field_iterator();
+ }
+}
+
+
+const char *Field_iterator_table_ref::table_name()
+{
+ if (table_ref->view)
+ return table_ref->view_name.str;
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->table_name();
+
+ DBUG_ASSERT(!strcmp(table_ref->table_name,
+ table_ref->table->s->table_name));
+ return table_ref->table_name;
+}
+
+
+const char *Field_iterator_table_ref::db_name()
+{
+ if (table_ref->view)
+ return table_ref->view_db.str;
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->db_name();
+
+ DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db));
+ return table_ref->db;
+}
+
+
+GRANT_INFO *Field_iterator_table_ref::grant()
+{
+ if (table_ref->view)
+ return &(table_ref->grant);
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->grant();
+ return &(table_ref->table->grant);
+}
+
+
+/*
+ Create new or return existing column reference to a column of a
+ natural/using join.
+
+ SYNOPSIS
+ Field_iterator_table_ref::get_or_create_column_ref()
+ thd [in] pointer to current thread
+ is_created [out] set to TRUE if the column was created,
+ FALSE if we return an already created colum
+
+ DESCRIPTION
+ TODO
+
+ RETURN
+ # Pointer to a column of a natural join (or its operand)
+ NULL No memory to allocate the column
+*/
+
+Natural_join_column *
+Field_iterator_table_ref::get_or_create_column_ref(THD *thd, bool *is_created)
+{
+ Natural_join_column *nj_col;
+
+ *is_created= TRUE;
+ if (field_it == &table_field_it)
+ {
+ /* The field belongs to a stored table. */
+ Field *field= table_field_it.field();
+ nj_col= new Natural_join_column(field, table_ref);
+ }
+ else if (field_it == &view_field_it)
+ {
+ /* 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);
+ }
+ else
+ {
+ /*
+ The field belongs to a NATURAL join, therefore the column reference was
+ already created via one of the two constructor calls above. In this case
+ we just return the already created column reference.
+ */
+ *is_created= FALSE;
+ nj_col= natural_join_it.column_ref();
+ DBUG_ASSERT(nj_col);
+ }
+ DBUG_ASSERT(!nj_col->table_field ||
+ nj_col->table_ref->table == nj_col->table_field->table);
+ return nj_col;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/