summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/t/disabled.def1
-rw-r--r--sql/item.cc477
-rw-r--r--sql/item.h1
-rw-r--r--sql/item_subselect.h1
4 files changed, 285 insertions, 195 deletions
diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
index 8a990f47e30..f71e24ff3ed 100644
--- a/mysql-test/t/disabled.def
+++ b/mysql-test/t/disabled.def
@@ -11,5 +11,4 @@
##############################################################################
sp-goto : GOTO is currently is disabled - will be fixed in the future
-subselect : Bug#15706
ndb_load : Bug#17233
diff --git a/sql/item.cc b/sql/item.cc
index 70fea63ba7e..fbdb217f5df 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3211,6 +3211,252 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
/*
+ Resolve the name of an outer select column reference.
+
+ SYNOPSIS
+ Item_field::fix_outer_field()
+ thd [in] current thread
+ from_field [in/out] found field reference or (Field*)not_found_field
+ reference [in/out] view column if this item was resolved to a view column
+
+ DESCRIPTION
+ The method resolves the column reference represented by 'this' as a column
+ present in outer selects that contain current select.
+
+ NOTES
+ This is the inner loop of Item_field::fix_fields:
+
+ for each outer query Q_k beginning from the inner-most one
+ {
+ search for a column or derived column named col_ref_i
+ [in table T_j] in the FROM clause of Q_k;
+
+ if such a column is not found
+ Search for a column or derived column named col_ref_i
+ [in table T_j] in the SELECT and GROUP clauses of Q_k.
+ }
+
+ IMPLEMENTATION
+ In prepared statements, because of cache, find_field_in_tables()
+ can resolve fields even if they don't belong to current context.
+ In this case this method only finds appropriate context and marks
+ current select as dependent. The found reference of field should be
+ provided in 'from_field'.
+
+ RETURN
+ 1 - column succefully resolved and fix_fields() should continue.
+ 0 - column fully fixed and fix_fields() should return FALSE
+ -1 - error occured
+*/
+int
+Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
+{
+ enum_parsing_place place= NO_MATTER;
+ bool field_found= (*from_field != not_found_field);
+ bool upward_lookup= FALSE;
+
+ /*
+ If there are outer contexts (outer selects, but current select is
+ not derived table or view) try to resolve this reference in the
+ outer contexts.
+
+ We treat each subselect as a separate namespace, so that different
+ subselects may contain columns with the same names. The subselects
+ are searched starting from the innermost.
+ */
+ Name_resolution_context *last_checked_context= context;
+ Item **ref= (Item **) not_found_item;
+ Name_resolution_context *outer_context= context->outer_context;
+ for (;
+ outer_context;
+ outer_context= outer_context->outer_context)
+ {
+ SELECT_LEX *select= outer_context->select_lex;
+ Item_subselect *prev_subselect_item=
+ last_checked_context->select_lex->master_unit()->item;
+ last_checked_context= outer_context;
+ upward_lookup= TRUE;
+
+ place= prev_subselect_item->parsing_place;
+ /*
+ If outer_field is set, field was already found by first call
+ to find_field_in_tables(). Only need to find appropriate context.
+ */
+ if (field_found && outer_context->select_lex !=
+ cached_table->select_lex)
+ continue;
+ /*
+ In case of a view, find_field_in_tables() writes the pointer to
+ the found view field into '*reference', in other words, it
+ substitutes this Item_field with the found expression.
+ */
+ if (field_found || (*from_field= find_field_in_tables(thd, this,
+ outer_context->
+ first_name_resolution_table,
+ outer_context->
+ last_name_resolution_table,
+ reference,
+ IGNORE_EXCEPT_NON_UNIQUE,
+ TRUE, TRUE)) !=
+ not_found_field)
+ {
+ if (*from_field)
+ {
+ if (*from_field != view_ref_found)
+ {
+ prev_subselect_item->used_tables_cache|= (*from_field)->table->map;
+ prev_subselect_item->const_item_cache= 0;
+ if (thd->lex->in_sum_func &&
+ thd->lex->in_sum_func->nest_level ==
+ thd->lex->current_select->nest_level)
+ {
+ Item::Type type= (*reference)->type();
+ set_if_bigger(thd->lex->in_sum_func->max_arg_level,
+ select->nest_level);
+ set_field(*from_field);
+ fixed= 1;
+ mark_as_dependent(thd, last_checked_context->select_lex,
+ context->select_lex, this,
+ ((type == REF_ITEM || type == FIELD_ITEM) ?
+ (Item_ident*) (*reference) : 0));
+ return 0;
+ }
+ }
+ else
+ {
+ Item::Type type= (*reference)->type();
+ prev_subselect_item->used_tables_cache|=
+ (*reference)->used_tables();
+ prev_subselect_item->const_item_cache&=
+ (*reference)->const_item();
+ mark_as_dependent(thd, last_checked_context->select_lex,
+ context->select_lex, this,
+ ((type == REF_ITEM || type == FIELD_ITEM) ?
+ (Item_ident*) (*reference) :
+ 0));
+ /*
+ A reference to a view field had been found and we
+ substituted it instead of this Item (find_field_in_tables
+ does it by assigning the new value to *reference), so now
+ we can return from this function.
+ */
+ return 0;
+ }
+ }
+ break;
+ }
+
+ /* Search in SELECT and GROUP lists of the outer select. */
+ if (outer_context->resolve_in_select_list)
+ {
+ if (!(ref= resolve_ref_in_select_and_group(thd, this, select)))
+ return -1; /* Some error occurred (e.g. ambiguous names). */
+ if (ref != not_found_item)
+ {
+ DBUG_ASSERT(*ref && (*ref)->fixed);
+ prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
+ prev_subselect_item->const_item_cache&= (*ref)->const_item();
+ break;
+ }
+ }
+
+ /*
+ Reference is not found in this select => this subquery depend on
+ outer select (or we just trying to find wrong identifier, in this
+ case it does not matter which used tables bits we set)
+ */
+ prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
+ prev_subselect_item->const_item_cache= 0;
+ }
+
+ DBUG_ASSERT(ref != 0);
+ if (!*from_field)
+ return -1;
+ if (ref == not_found_item && *from_field == not_found_field)
+ {
+ if (upward_lookup)
+ {
+ // We can't say exactly what absent table or field
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where);
+ }
+ else
+ {
+ /* Call find_field_in_tables only to report the error */
+ find_field_in_tables(thd, this,
+ context->first_name_resolution_table,
+ context->last_name_resolution_table,
+ reference, REPORT_ALL_ERRORS,
+ !any_privileges &&
+ TRUE, TRUE);
+ }
+ return -1;
+ }
+ else if (ref != not_found_item)
+ {
+ Item *save;
+ Item_ref *rf;
+
+ /* Should have been checked in resolve_ref_in_select_and_group(). */
+ DBUG_ASSERT(*ref && (*ref)->fixed);
+ /*
+ Here, a subset of actions performed by Item_ref::set_properties
+ is not enough. So we pass ptr to NULL into Item_[direct]_ref
+ constructor, so no initialization is performed, and call
+ fix_fields() below.
+ */
+ save= *ref;
+ *ref= NULL; // Don't call set_properties()
+ rf= (place == IN_HAVING ?
+ new Item_ref(context, ref, (char*) table_name,
+ (char*) field_name) :
+ new Item_direct_ref(context, ref, (char*) table_name,
+ (char*) field_name));
+ *ref= save;
+ if (!rf)
+ return -1;
+ thd->change_item_tree(reference, rf);
+ /*
+ rf is Item_ref => never substitute other items (in this case)
+ during fix_fields() => we can use rf after fix_fields()
+ */
+ DBUG_ASSERT(!rf->fixed); // Assured by Item_ref()
+ if (rf->fix_fields(thd, reference) || rf->check_cols(1))
+ return -1;
+
+ mark_as_dependent(thd, last_checked_context->select_lex,
+ context->select_lex, this,
+ rf);
+ return 0;
+ }
+ else
+ {
+ mark_as_dependent(thd, last_checked_context->select_lex,
+ context->select_lex,
+ this, this);
+ if (last_checked_context->select_lex->having_fix_field)
+ {
+ Item_ref *rf;
+ rf= new Item_ref(context,
+ (cached_table->db[0] ? cached_table->db : 0),
+ (char*) cached_table->alias, (char*) field_name);
+ if (!rf)
+ return -1;
+ thd->change_item_tree(reference, rf);
+ /*
+ rf is Item_ref => never substitute other items (in this case)
+ during fix_fields() => we can use rf after fix_fields()
+ */
+ DBUG_ASSERT(!rf->fixed); // Assured by Item_ref()
+ if (rf->fix_fields(thd, reference) || rf->check_cols(1))
+ return -1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
Resolve the name of a column reference.
SYNOPSIS
@@ -3257,12 +3503,11 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
bool Item_field::fix_fields(THD *thd, Item **reference)
{
- enum_parsing_place place= NO_MATTER;
DBUG_ASSERT(fixed == 0);
if (!field) // If field is not checked
{
- bool upward_lookup= FALSE;
Field *from_field= (Field *)not_found_field;
+ bool outer_fixed= false;
/*
In case of view, find_field_in_tables() write pointer to view field
expression to 'reference', i.e. it substitute that expression instead
@@ -3277,7 +3522,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
TRUE)) ==
not_found_field)
{
-
+ int ret;
/* Look up in current select's item_list to find aliased fields */
if (thd->lex->current_select->is_item_list_lookup)
{
@@ -3292,197 +3537,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
return 0;
}
}
-
- /*
- If there are outer contexts (outer selects, but current select is
- not derived table or view) try to resolve this reference in the
- outer contexts.
-
- We treat each subselect as a separate namespace, so that different
- subselects may contain columns with the same names. The subselects
- are searched starting from the innermost.
- */
- Name_resolution_context *last_checked_context= context;
- Item **ref= (Item **) not_found_item;
- Name_resolution_context *outer_context= context->outer_context;
- for (;
- outer_context;
- outer_context= outer_context->outer_context)
- {
- SELECT_LEX *select= outer_context->select_lex;
- Item_subselect *prev_subselect_item=
- last_checked_context->select_lex->master_unit()->item;
- last_checked_context= outer_context;
- upward_lookup= TRUE;
-
- place= prev_subselect_item->parsing_place;
- /*
- In case of a view, find_field_in_tables() writes the pointer to
- the found view field into '*reference', in other words, it
- substitutes this Item_field with the found expression.
- */
- if ((from_field= find_field_in_tables(thd, this,
- outer_context->
- first_name_resolution_table,
- outer_context->
- last_name_resolution_table,
- reference,
- IGNORE_EXCEPT_NON_UNIQUE,
- TRUE, TRUE)) !=
- not_found_field)
- {
- if (from_field)
- {
- if (from_field != view_ref_found)
- {
- prev_subselect_item->used_tables_cache|= from_field->table->map;
- prev_subselect_item->const_item_cache= 0;
- if (thd->lex->in_sum_func &&
- thd->lex->in_sum_func->nest_level ==
- thd->lex->current_select->nest_level)
- {
- Item::Type type= (*reference)->type();
- set_if_bigger(thd->lex->in_sum_func->max_arg_level,
- select->nest_level);
- set_field(from_field);
- fixed= 1;
- mark_as_dependent(thd, last_checked_context->select_lex,
- context->select_lex, this,
- ((type == REF_ITEM || type == FIELD_ITEM) ?
- (Item_ident*) (*reference) : 0));
- return FALSE;
- }
- }
- else
- {
- Item::Type type= (*reference)->type();
- prev_subselect_item->used_tables_cache|=
- (*reference)->used_tables();
- prev_subselect_item->const_item_cache&=
- (*reference)->const_item();
- mark_as_dependent(thd, last_checked_context->select_lex,
- context->select_lex, this,
- ((type == REF_ITEM || type == FIELD_ITEM) ?
- (Item_ident*) (*reference) :
- 0));
- /*
- A reference to a view field had been found and we
- substituted it instead of this Item (find_field_in_tables
- does it by assigning the new value to *reference), so now
- we can return from this function.
- */
- return FALSE;
- }
- }
- break;
- }
-
- /* Search in SELECT and GROUP lists of the outer select. */
- if (outer_context->resolve_in_select_list)
- {
- if (!(ref= resolve_ref_in_select_and_group(thd, this, select)))
- goto error; /* Some error occurred (e.g. ambiguous names). */
- if (ref != not_found_item)
- {
- DBUG_ASSERT(*ref && (*ref)->fixed);
- prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
- prev_subselect_item->const_item_cache&= (*ref)->const_item();
- break;
- }
- }
-
- /*
- Reference is not found in this select => this subquery depend on
- outer select (or we just trying to find wrong identifier, in this
- case it does not matter which used tables bits we set)
- */
- prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
- prev_subselect_item->const_item_cache= 0;
- }
-
- DBUG_ASSERT(ref != 0);
- if (!from_field)
- goto error;
- if (ref == not_found_item && from_field == not_found_field)
- {
- if (upward_lookup)
- {
- // We can't say exactly what absent table or field
- my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where);
- }
- else
- {
- /* Call find_field_in_tables only to report the error */
- find_field_in_tables(thd, this,
- context->first_name_resolution_table,
- context->last_name_resolution_table,
- reference, REPORT_ALL_ERRORS,
- !any_privileges &&
- TRUE, TRUE);
- }
- goto error;
- }
- else if (ref != not_found_item)
- {
- Item *save;
- Item_ref *rf;
-
- /* Should have been checked in resolve_ref_in_select_and_group(). */
- DBUG_ASSERT(*ref && (*ref)->fixed);
- /*
- Here, a subset of actions performed by Item_ref::set_properties
- is not enough. So we pass ptr to NULL into Item_[direct]_ref
- constructor, so no initialization is performed, and call
- fix_fields() below.
- */
- save= *ref;
- *ref= NULL; // Don't call set_properties()
- rf= (place == IN_HAVING ?
- new Item_ref(context, ref, (char*) table_name,
- (char*) field_name) :
- new Item_direct_ref(context, ref, (char*) table_name,
- (char*) field_name));
- *ref= save;
- if (!rf)
- goto error;
- thd->change_item_tree(reference, rf);
- /*
- rf is Item_ref => never substitute other items (in this case)
- during fix_fields() => we can use rf after fix_fields()
- */
- DBUG_ASSERT(!rf->fixed); // Assured by Item_ref()
- if (rf->fix_fields(thd, reference) || rf->check_cols(1))
- goto error;
-
- mark_as_dependent(thd, last_checked_context->select_lex,
- context->select_lex, this,
- rf);
- return FALSE;
- }
- else
- {
- mark_as_dependent(thd, last_checked_context->select_lex,
- context->select_lex,
- this, this);
- if (last_checked_context->select_lex->having_fix_field)
- {
- Item_ref *rf;
- rf= new Item_ref(context,
- (cached_table->db[0] ? cached_table->db : 0),
- (char*) cached_table->alias, (char*) field_name);
- if (!rf)
- goto error;
- thd->change_item_tree(reference, rf);
- /*
- rf is Item_ref => never substitute other items (in this case)
- during fix_fields() => we can use rf after fix_fields()
- */
- DBUG_ASSERT(!rf->fixed); // Assured by Item_ref()
- if (rf->fix_fields(thd, reference) || rf->check_cols(1))
- goto error;
- return FALSE;
- }
- }
+ if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
+ goto error;
+ else if (!ret)
+ return FALSE;
+ outer_fixed= TRUE;
}
else if (!from_field)
goto error;
@@ -3502,6 +3561,17 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (from_field == view_ref_found)
return FALSE;
+ if (!outer_fixed && cached_table && cached_table->select_lex &&
+ context->select_lex &&
+ cached_table->select_lex != context->select_lex)
+ {
+ int ret;
+ if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
+ goto error;
+ else if (!ret)
+ return FALSE;
+ }
+
set_field(from_field);
if (thd->lex->in_sum_func &&
thd->lex->in_sum_func->nest_level ==
@@ -4620,6 +4690,25 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
}
if (from_field != not_found_field)
{
+ if (cached_table && cached_table->select_lex &&
+ outer_context->select_lex &&
+ cached_table->select_lex != outer_context->select_lex)
+ {
+ /*
+ Due to cache, find_field_in_tables() can return field which
+ doesn't belong to provided outer_context. In this case we have
+ to find proper field context in order to fix field correcly.
+ */
+ do
+ {
+ outer_context= outer_context->outer_context;
+ select= outer_context->select_lex;
+ prev_subselect_item=
+ last_checked_context->select_lex->master_unit()->item;
+ last_checked_context= outer_context;
+ } while (outer_context && outer_context->select_lex &&
+ cached_table->select_lex != outer_context->select_lex);
+ }
prev_subselect_item->used_tables_cache|= from_field->table->map;
prev_subselect_item->const_item_cache= 0;
break;
diff --git a/sql/item.h b/sql/item.h
index af42ce38b9b..8feba903bb2 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1161,6 +1161,7 @@ public:
inline uint32 max_disp_length() { return field->max_length(); }
Item_field *filed_for_view_update() { return this; }
Item *safe_charset_converter(CHARSET_INFO *tocs);
+ int fix_outer_field(THD *thd, Field **field, Item **reference);
friend class Item_default_value;
friend class Item_insert_value;
friend class st_select_lex_unit;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index f1c99f74498..a4dac5bda87 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -125,6 +125,7 @@ public:
friend class select_subselect;
friend class Item_in_optimizer;
friend bool Item_field::fix_fields(THD *, Item **);
+ friend int Item_field::fix_outer_field(THD *, Field **, Item **);
friend bool Item_ref::fix_fields(THD *, Item **);
friend void mark_select_range_as_dependent(THD*,
st_select_lex*, st_select_lex*,