1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Name resolution context added (BUG#6443)

This commit is contained in:
bell@sanja.is.com.ua
2005-07-01 07:05:42 +03:00
parent d8cb0cbc3f
commit d3905f3d0e
55 changed files with 1688 additions and 1069 deletions

View File

@ -594,6 +594,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
goto err; /* purecov: inspected */
}
reg_field->field_index= i;
reg_field->comment=comment;
if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag))
{
@ -1713,8 +1714,6 @@ void st_table_list::set_ancestor()
}
if (tbl->multitable_view)
multitable_view= TRUE;
if (tbl->table)
tbl->table->grant= grant;
} while ((tbl= tbl->next_local));
if (!multitable_view)
@ -1726,69 +1725,19 @@ void st_table_list::set_ancestor()
}
/*
Save old want_privilege and clear want_privilege
SYNOPSIS
save_and_clear_want_privilege()
*/
void st_table_list::save_and_clear_want_privilege()
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->table)
{
privilege_backup= tbl->table->grant.want_privilege;
tbl->table->grant.want_privilege= 0;
}
else
{
tbl->save_and_clear_want_privilege();
}
}
}
/*
restore want_privilege saved by save_and_clear_want_privilege
SYNOPSIS
restore_want_privilege()
*/
void st_table_list::restore_want_privilege()
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->table)
tbl->table->grant.want_privilege= privilege_backup;
else
{
tbl->restore_want_privilege();
}
}
}
/*
setup fields of placeholder of merged VIEW
SYNOPSIS
st_table_list::setup_ancestor()
thd - thread handler
conds - condition of this JOIN
check_opt_type - WHITH CHECK OPTION type (VIEW_CHECK_NONE,
VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED)
NOTES
ancestor is list of tables and views used by view (underlying tables/views)
DESCRIPTION
It is:
- preparing translation table for view columns (fix_fields() for every
call and creation for first call)
- preparing WHERE, ON and CHECK OPTION condition (fix_fields() for every
call and merging for first call).
- preparing translation table for view columns
If there are underlying view(s) procedure first will be called for them.
RETURN
@ -1796,163 +1745,114 @@ void st_table_list::restore_want_privilege()
TRUE - error
*/
bool st_table_list::setup_ancestor(THD *thd, Item **conds,
uint8 check_opt_type)
bool st_table_list::setup_ancestor(THD *thd)
{
Field_translator *transl;
SELECT_LEX *select= &view->select_lex;
SELECT_LEX *current_select_save= thd->lex->current_select;
byte *main_table_list_save= select_lex->table_list.first;
Item *item;
TABLE_LIST *tbl;
List_iterator_fast<Item> it(select->item_list);
uint i= 0;
enum sub_select_type linkage_save=
select_lex->master_unit()->first_select()->linkage;
bool save_set_query_id= thd->set_query_id;
bool save_wrapper= select_lex->no_wrap_view_item;
bool save_allow_sum_func= thd->allow_sum_func;
bool res= FALSE;
DBUG_ENTER("st_table_list::setup_ancestor");
if (check_stack_overrun(thd, STACK_MIN_SIZE, (char *)&res))
return TRUE;
for (tbl= ancestor; tbl; tbl= tbl->next_local)
if (!field_translation)
{
if (tbl->ancestor &&
tbl->setup_ancestor(thd, conds,
(check_opt_type == VIEW_CHECK_CASCADED ?
VIEW_CHECK_CASCADED :
VIEW_CHECK_NONE)))
DBUG_RETURN(TRUE);
}
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;
/*
We have to ensure that inside the view we are not referring to any
table outside of the view. We do it by changing the pointers used
by fix_fields to look up tables so that only tables and views in
view are seen. We also set linkage to DERIVED_TABLE_TYPE as a barrier
so that we stop resolving fields as this level.
*/
thd->lex->current_select= select_lex;
select_lex->table_list.first= (byte *)ancestor;
select_lex->master_unit()->first_select()->linkage= DERIVED_TABLE_TYPE;
if (field_translation)
{
DBUG_PRINT("info", ("there are already translation table"));
select_lex->no_wrap_view_item= 1;
thd->set_query_id= 1;
/* this view was prepared already on previous PS/SP execution */
Field_translator *end= field_translation + select->item_list.elements;
/* real rights will be checked in VIEW field */
save_and_clear_want_privilege();
/* aggregate function are allowed */
thd->allow_sum_func= 1;
for (transl= field_translation; transl < end; transl++)
if (check_stack_overrun(thd, STACK_MIN_SIZE, (char *)&field_count))
{
if (!transl->item->fixed &&
transl->item->fix_fields(thd, ancestor, &transl->item))
goto err;
DBUG_RETURN(TRUE);
}
for (tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->on_expr && !tbl->on_expr->fixed &&
tbl->on_expr->fix_fields(thd, ancestor, &tbl->on_expr))
goto err;
}
if (where && !where->fixed && where->fix_fields(thd, ancestor, &where))
goto err;
if (check_option && !check_option->fixed &&
check_option->fix_fields(thd, ancestor, &check_option))
goto err;
restore_want_privilege();
/* WHERE/ON resolved => we can rename fields */
for (transl= field_translation; transl < end; transl++)
{
transl->item->rename((char *)transl->name);
}
goto ok;
}
/* Create view fields translation table */
if (!(transl=
(Field_translator*)(thd->current_arena->
alloc(select->item_list.elements *
sizeof(Field_translator)))))
{
res= TRUE;
goto ok; // Restore thd
}
select_lex->no_wrap_view_item= 1;
/*
Resolve all view items against ancestor table.
TODO: do it only for real used fields "on demand" to mark really
used fields correctly.
*/
thd->set_query_id= 1;
/* real rights will be checked in VIEW field */
save_and_clear_want_privilege();
/* aggregate function are allowed */
thd->allow_sum_func= 1;
while ((item= it++))
{
/* save original name of view column */
char *name= item->name;
transl[i].item= item;
if (!item->fixed && item->fix_fields(thd, ancestor, &transl[i].item))
goto err;
/* set new item get in fix fields and original column name */
transl[i++].name= name;
}
field_translation= transl;
/* TODO: sort this list? Use hash for big number of fields */
for (tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->on_expr && !tbl->on_expr->fixed &&
tbl->on_expr->fix_fields(thd, ancestor, &tbl->on_expr))
goto err;
}
if (where ||
(check_opt_type == VIEW_CHECK_CASCADED &&
ancestor->check_option))
{
Query_arena *arena= thd->current_arena, backup;
TABLE_LIST *tbl= this;
if (arena->is_conventional())
arena= 0; // For easier test
if (where && !where->fixed && where->fix_fields(thd, ancestor, &where))
goto err;
if (arena)
thd->set_n_backup_item_arena(arena, &backup);
if (check_opt_type)
{
if (where)
check_option= where->copy_andor_structure(thd);
if (check_opt_type == VIEW_CHECK_CASCADED)
if (tbl->ancestor &&
tbl->setup_ancestor(thd))
{
check_option= and_conds(check_option, ancestor->check_option);
DBUG_RETURN(TRUE);
}
}
/* Create view fields translation table */
if (!(transl=
(Field_translator*)(thd->current_arena->
alloc(select->item_list.elements *
sizeof(Field_translator)))))
{
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)
{
Item_func_match *ifm;
SELECT_LEX *current_select= thd->lex->current_select;
List_iterator_fast<Item_func_match>
li(*(view->select_lex.ftfunc_list));
while ((ifm= li++))
current_select->ftfunc_list->push_front(ifm);
}
}
DBUG_RETURN(FALSE);
}
/*
Prepare where expression of view
SYNOPSIS
st_table_list::prep_where()
thd - thread handler
conds - condition of this JOIN
no_where_clause - do not build WHERE or ON outer qwery do not need it
(it is INSERT), we do not need conds if this flag is set
NOTE: have to be called befor CHECK OPTION preparation, because it makes
fix_fields for view WHERE clause
RETURN
FALSE - OK
TRUE - error
*/
bool st_table_list::prep_where(THD *thd, Item **conds,
bool no_where_clause)
{
DBUG_ENTER("st_table_list::prep_where");
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
{
DBUG_RETURN(TRUE);
}
}
if (where)
{
if (!where->fixed && where->fix_fields(thd, &where))
{
DBUG_RETURN(TRUE);
}
/*
check that it is not VIEW in which we insert with INSERT SELECT
(in this case we can't add view WHERE condition to main SELECT_LEX)
*/
if (where && !no_where_clause)
if (!no_where_clause && !where_processed)
{
TABLE_LIST *tbl= this;
Query_arena *arena= thd->current_arena, backup;
arena= thd->change_arena_if_needed(&backup); // For easier test
/* Go up to join tree and try to find left join */
for (; tbl; tbl= tbl->embedding)
{
@ -1969,72 +1869,107 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds,
}
}
if (tbl == 0)
*conds= and_conds(*conds, where);
if (arena)
thd->restore_backup_item_arena(arena, &backup);
where_processed= TRUE;
}
}
DBUG_RETURN(FALSE);
}
/*
Prepare check option expression of table
SYNOPSIS
st_table_list::prep_check_option()
thd - thread handler
check_opt_type - WITH CHECK OPTION type (VIEW_CHECK_NONE,
VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED)
we use this parameter instead of direct check of
effective_with_check to change type of underlying
views to VIEW_CHECK_CASCADED if outer view have
such option and prevent processing of underlying
view check options if outer view have just
VIEW_CHECK_LOCAL option.
NOTE
This method build check options for every call
(usual execution or every SP/PS call)
This method have to be called after WHERE preparation
(st_table_list::prep_where)
RETURN
FALSE - OK
TRUE - error
*/
bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
{
DBUG_ENTER("st_table_list::prep_check_option");
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
if (tbl->view &&
tbl->prep_check_option(thd,
((check_opt_type == VIEW_CHECK_CASCADED) ?
VIEW_CHECK_CASCADED :
VIEW_CHECK_NONE)))
{
DBUG_RETURN(TRUE);
}
}
if (check_opt_type)
{
Item *item= 0;
if (where)
{
DBUG_ASSERT(where->fixed);
item= where->copy_andor_structure(thd);
}
if (check_opt_type == VIEW_CHECK_CASCADED)
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (outer_join)
{
/*
Store WHERE condition to ON expression for outer join, because
we can't use WHERE to correctly execute left joins on VIEWs and
this expression will not be moved to WHERE condition (i.e. will
be clean correctly for PS/SP)
*/
on_expr= and_conds(on_expr, where);
}
else
{
/*
It is conds of JOIN, but it will be stored in
st_select_lex::prep_where for next reexecution
*/
*conds= and_conds(*conds, where);
}
if (tbl->check_option)
item= and_conds(item, tbl->check_option);
}
}
if (arena)
thd->restore_backup_item_arena(arena, &backup);
if (item)
thd->change_item_tree(&check_option, item);
}
restore_want_privilege();
/*
fix_fields do not need tables, because new are only AND operation and we
just need recollect statistics
*/
if (check_option && !check_option->fixed &&
check_option->fix_fields(thd, 0, &check_option))
goto err;
/* WHERE/ON resolved => we can rename fields */
if (check_option)
{
Field_translator *end= field_translation + select->item_list.elements;
for (transl= field_translation; transl < end; transl++)
const char *save_where= thd->where;
thd->where= "check option";
if (!check_option->fixed &&
check_option->fix_fields(thd, &check_option) ||
check_option->check_cols(1))
{
transl->item->rename((char *)transl->name);
DBUG_RETURN(TRUE);
}
thd->where= save_where;
}
DBUG_RETURN(FALSE);
}
/* full text function moving to current select */
if (view->select_lex.ftfunc_list->elements)
{
Query_arena *arena= thd->current_arena, backup;
if (arena->is_conventional())
arena= 0; // For easier test
else
thd->set_n_backup_item_arena(arena, &backup);
Item_func_match *ifm;
List_iterator_fast<Item_func_match>
li(*(view->select_lex.ftfunc_list));
while ((ifm= li++))
current_select_save->ftfunc_list->push_front(ifm);
if (arena)
thd->restore_backup_item_arena(arena, &backup);
}
/*
Hide errors which show view underlying table information
goto ok;
SYNOPSIS
st_table_list::hide_view_error()
thd thread handler
err:
res= TRUE;
*/
void st_table_list::hide_view_error(THD *thd)
{
/* Hide "Unknown column" or "Unknown function" error */
if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
thd->net.last_errno == ER_SP_DOES_NOT_EXIST)
@ -2042,15 +1977,12 @@ err:
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str);
}
ok:
select_lex->no_wrap_view_item= save_wrapper;
thd->lex->current_select= current_select_save;
select_lex->table_list.first= main_table_list_save;
select_lex->master_unit()->first_select()->linkage= linkage_save;
thd->set_query_id= save_set_query_id;
thd->allow_sum_func= save_allow_sum_func;
DBUG_RETURN(res);
else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
{
thd->clear_error();
// TODO: make correct error message
my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), view_db.str, view_name.str);
}
}
@ -2094,9 +2026,9 @@ void st_table_list::cleanup_items()
if (!field_translation)
return;
Field_translator *end= (field_translation +
view->select_lex.item_list.elements);
for (Field_translator *transl= field_translation; transl < end; transl++)
for (Field_translator *transl= field_translation;
transl < field_translation_end;
transl++)
transl->item->walk(&Item::cleanup_processor, 0);
}
@ -2209,8 +2141,9 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
void Field_iterator_view::set(TABLE_LIST *table)
{
view= table;
ptr= table->field_translation;
array_end= ptr + table->view->select_lex.item_list.elements;
array_end= table->field_translation_end;
}
@ -2220,9 +2153,9 @@ const char *Field_iterator_table::name()
}
Item *Field_iterator_table::item(THD *thd)
Item *Field_iterator_table::create_item(THD *thd)
{
return new Item_field(thd, *ptr);
return new Item_field(thd, &thd->lex->current_select->context, *ptr);
}
@ -2232,6 +2165,51 @@ const char *Field_iterator_view::name()
}
Item *Field_iterator_view::create_item(THD *thd)
{
return create_view_field(thd, view, &ptr->item, ptr->name);
}
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
const char *name)
{
bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
Item *field= *field_ref;
DBUG_ENTER("create_view_field");
if (view->schema_table_reformed)
{
/*
In case of SHOW command (schema_table_reformed set) all items are
fixed
*/
DBUG_ASSERT(field && field->fixed);
DBUG_RETURN(field);
}
DBUG_ASSERT(field);
thd->lex->current_select->no_wrap_view_item= TRUE;
if (!field->fixed)
{
if (field->fix_fields(thd, field_ref))
{
thd->lex->current_select->no_wrap_view_item= save_wrapper;
DBUG_RETURN(0);
}
field= *field_ref;
}
thd->lex->current_select->no_wrap_view_item= save_wrapper;
if (thd->lex->current_select->no_wrap_view_item)
{
DBUG_RETURN(field);
}
Item *item= new Item_direct_view_ref(&view->view->select_lex.context,
field_ref, view->view_name.str,
name);
DBUG_RETURN(item);
}
/*****************************************************************************
** Instansiate templates
*****************************************************************************/