summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorbell@sanja.is.com.ua <>2005-10-28 00:24:13 +0300
committerbell@sanja.is.com.ua <>2005-10-28 00:24:13 +0300
commit6143c6543efa3dd527e5fd867c5fa3b36fbf6586 (patch)
treefe93843434a12c6357de38534683f49f8f411744 /sql
parenta9b1ff40951a74666f3de90c7d79f40fc507ae16 (diff)
parent1b164c7b83f7081acc99f877d2b7fbfbdedbec2f (diff)
downloadmariadb-git-6143c6543efa3dd527e5fd867c5fa3b36fbf6586.tar.gz
Merge sanja.is.com.ua:/home/bell/mysql/bk/mysql-5.0
into sanja.is.com.ua:/home/bell/mysql/bk/work-owner7-5.0
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc16
-rw-r--r--sql/item.h8
-rw-r--r--sql/item_func.cc36
-rw-r--r--sql/mysql_priv.h14
-rw-r--r--sql/share/errmsg.txt3
-rw-r--r--sql/sql_acl.cc55
-rw-r--r--sql/sql_base.cc94
-rw-r--r--sql/sql_cache.cc20
-rw-r--r--sql/sql_delete.cc5
-rw-r--r--sql/sql_derived.cc39
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_parse.cc14
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_update.cc2
-rw-r--r--sql/sql_view.cc115
-rw-r--r--sql/table.cc233
-rw-r--r--sql/table.h48
17 files changed, 554 insertions, 155 deletions
diff --git a/sql/item.cc b/sql/item.cc
index fed3ffcc080..dc4369f406d 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3234,8 +3234,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
context->last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
- !any_privileges &&
- context->check_privileges,
+ !any_privileges,
TRUE)) ==
not_found_field)
{
@@ -3297,9 +3296,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
- outer_context->
- check_privileges,
- TRUE)) !=
+ TRUE, TRUE)) !=
not_found_field)
{
if (from_field)
@@ -3374,7 +3371,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
context->last_name_resolution_table,
reference, REPORT_ALL_ERRORS,
!any_privileges &&
- context->check_privileges, TRUE);
+ TRUE, TRUE);
}
goto error;
}
@@ -3449,7 +3446,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
We can leave expression substituted from view for next PS/SP rexecution
(i.e. do not register this substitution for reverting on cleupup()
(register_item_tree_changing())), because this subtree will be
- fix_field'ed during setup_tables()->setup_ancestor() (i.e. before
+ fix_field'ed during setup_tables()->setup_underlying() (i.e. before
all other expressions of query, and references on tables which do
not present in query will not make problems.
@@ -4544,8 +4541,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
- outer_context->check_privileges,
- TRUE);
+ TRUE, TRUE);
if (! from_field)
goto error;
if (from_field == view_ref_found)
@@ -5157,7 +5153,7 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table)
set field_idx properly.
*/
(void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name),
- 0, 0, &field_idx);
+ 0, 0, &field_idx, 0);
thd->set_query_id= save_set_query_id;
triggers= table->triggers;
}
diff --git a/sql/item.h b/sql/item.h
index 3e52cbe5fd7..5bff285b9f3 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -294,15 +294,15 @@ struct Name_resolution_context: Sql_alloc
bool resolve_in_select_list;
/*
- When FALSE we do not check columns right of resolving items, used to
- prevent rights check on underlying tables of view
+ Security context of this name resolution context. It's used for views
+ and is non-zero only if the view is defined with SQL SECURITY DEFINER.
*/
- bool check_privileges;
+ Security_context *security_ctx;
Name_resolution_context()
:outer_context(0), table_list(0), select_lex(0),
error_processor_data(0),
- check_privileges(TRUE)
+ security_ctx(0)
{}
void init()
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a5e1c37cd18..fbd13d84c2d 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4732,6 +4732,7 @@ Item_func_sp::execute(Field **flp)
if (execute(&it))
{
null_value= 1;
+ context->process_error(current_thd);
return 1;
}
if (!(f= *flp))
@@ -4754,9 +4755,17 @@ Item_func_sp::execute(Item **itp)
THD *thd= current_thd;
int res= -1;
Sub_statement_state statement_state;
- Security_context *save_ctx;
+ Security_context *save_security_ctx= 0, *save_ctx_func;
- if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx))
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (context->security_ctx)
+ {
+ /* Set view definer security context */
+ save_security_ctx= thd->security_ctx;
+ thd->security_ctx= context->security_ctx;
+ }
+#endif
+ if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func))
goto error;
/*
@@ -4774,9 +4783,14 @@ Item_func_sp::execute(Item **itp)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_FAILED_ROUTINE_BREAK_BINLOG,
ER(ER_FAILED_ROUTINE_BREAK_BINLOG));
-
- sp_restore_security_context(thd, save_ctx);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, save_ctx_func);
error:
+ if (save_security_ctx)
+ thd->security_ctx= save_security_ctx;
+#else
+error:
+#endif
DBUG_RETURN(res);
}
@@ -4957,8 +4971,20 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ASSERT(fixed == 0);
res= Item_func::fix_fields(thd, ref);
- if (!res)
+ if (!res && thd->lex->view_prepare_mode)
{
+ /*
+ Here we check privileges of the stored routine only during view
+ creation, in order to validate the view. A runtime check is perfomed
+ in Item_func_sp::execute(), and this method is not called during
+ context analysis. We do not need to restore the security context
+ changed in find_and_check_access because all view structures created
+ in CREATE VIEW are not used for execution. Notice, that during view
+ creation we do not infer into stored routine bodies and do not check
+ privileges of its statements, which would probably be a good idea
+ especially if the view has SQL SECURITY DEFINER and the used stored
+ procedure has SQL
+ */
Security_context *save_ctx;
if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx)))
sp_restore_security_context(thd, save_ctx);
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index e33ac05e293..5f1d0fe18db 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -679,11 +679,11 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
select_result *result);
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
-int mysql_handle_derived(LEX *lex, int (*processor)(THD *thd,
- LEX *lex,
- TABLE_LIST *table));
-int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
-int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
+bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd,
+ LEX *lex,
+ TABLE_LIST *table));
+bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
+bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item ***copy_func, Field **from_field,
bool group, bool modify_item,
@@ -793,7 +793,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
Field *
find_field_in_table(THD *thd, TABLE *table, const char *name,
uint length, bool check_grants, bool allow_rowid,
- uint *cached_field_index_ptr);
+ uint *cached_field_index_ptr,
+ Security_context *sctx);
+
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
struct st_des_keyblock
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index ccf11248a1f..f85bda90e81 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5210,8 +5210,7 @@ ER_WARN_VIEW_WITHOUT_KEY
rus "ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÙÈ(ÏÊ) × ÎÅÍ ÔÁÂÌÉÃ(Ù)"
ukr "View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ"
ER_VIEW_INVALID
- eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s)"
- rus "View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ ÉÌÉ ÆÕÎËÃÉÉ"
+ eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
ER_SP_NO_DROP_SP
eng "Can't drop or alter a %s from within another stored routine"
ER_SP_GOTO_IN_HNDLR
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 0691708f19b..332606771aa 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -934,6 +934,9 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
ACL_USER *acl_user= 0;
DBUG_ENTER("acl_getroot_no_password");
+ DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
+ (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
+ (user ? user : "(NULL)"), (db ? db : "(NULL)")));
sctx->user= user;
sctx->host= host;
sctx->ip= ip;
@@ -3512,17 +3515,32 @@ end:
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_table, uint number, bool no_errors)
{
- TABLE_LIST *table;
+ TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx;
+ uint i;
DBUG_ENTER("check_grant");
DBUG_ASSERT(number > 0);
+ /*
+ Iterate tables until first prelocking placeholder (if this query do not
+ have placeholders first_not_own_table is 0)
+ */
+ for (i= 0, table= tables;
+ table && table != first_not_own_table && i < number;
+ table= table->next_global, i++)
+ {
+ /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
+ table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+ }
+
want_access&= ~sctx->master_access;
if (!want_access)
DBUG_RETURN(0); // ok
rw_rdlock(&LOCK_grant);
- for (table= tables; table && number--; table= table->next_global)
+ for (table= tables;
+ table && number-- && table != first_not_own_table;
+ table= table->next_global)
{
GRANT_TABLE *grant_table;
if (!(~table->grant.privilege & want_access) ||
@@ -3532,8 +3550,16 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
It is subquery in the FROM clause. VIEW set table->derived after
table opening, but this function always called before table opening.
*/
- table->grant.want_privilege= 0;
- continue; // Already checked
+ if (!table->referencing_view)
+ {
+ /*
+ If it's a temporary table created for a subquery in the FROM
+ clause, or an INFORMATION_SCHEMA table, drop the request for
+ a privilege.
+ */
+ table->grant.want_privilege= 0;
+ }
+ continue;
}
if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
table->db, sctx->priv_user,
@@ -5842,24 +5868,37 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table)
{
Security_context *sctx= thd->security_ctx;
+ DBUG_ENTER("fill_effective_table_privileges");
+ DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
+ sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
+ (sctx->priv_user ? sctx->priv_user : "(NULL)"),
+ db, table));
/* --skip-grants */
if (!initialized)
{
+ DBUG_PRINT("info", ("skip grants"));
grant->privilege= ~NO_ACCESS; // everything is allowed
- return;
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
}
/* global privileges */
grant->privilege= sctx->master_access;
if (!sctx->priv_user)
- return; // it is slave
+ {
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN; // it is slave
+ }
/* db privileges */
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
if (!grant_option)
- return;
+ {
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
+ }
/* table privileges */
if (grant->version != grant_version)
@@ -5876,6 +5915,8 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
{
grant->privilege|= grant->grant_table->privs;
}
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
}
#else /* NO_EMBEDDED_ACCESS_CHECKS */
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 973fbca12f5..2da0f7e2ce0 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1971,11 +1971,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
derived/information schema tables and views possible. Thus "counter"
may be still zero for prelocked statement...
- NOTE: The above notes may be out of date. Please wait for psergey to
+ NOTE: The above notes may be out of date. Please wait for psergey to
document new prelocked behavior.
*/
-
- if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
+
+ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
thd->lex->sroutines_list.elements)
{
bool first_no_prelocking, need_prelocking;
@@ -2025,7 +2025,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
/* VIEW placeholder */
(*counter)--;
- /*
+ /*
tables->next_global list consists of two parts:
1) Query tables and underlying tables of views.
2) Tables used by all stored routines that this statement invokes on
@@ -2677,6 +2677,49 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ Check column rights in given security context
+
+ SYNOPSIS
+ check_grant_column_in_sctx()
+ thd thread handler
+ grant grant information structure
+ db db name
+ table table name
+ name column name
+ length column name length
+ check_grants need to check grants
+ sctx 0 or security context
+
+ RETURN
+ FALSE OK
+ TRUE access denied
+*/
+
+static bool check_grant_column_in_sctx(THD *thd, GRANT_INFO *grant,
+ const char *db, const char *table,
+ const char *name, uint length,
+ bool check_grants,
+ Security_context *sctx)
+{
+ if (!check_grants)
+ return FALSE;
+ Security_context *save_security_ctx= 0;
+ bool res;
+ if (sctx)
+ {
+ save_security_ctx= thd->security_ctx;
+ thd->security_ctx= sctx;
+ }
+ res= check_grant_column(thd, grant, db, table, name, length);
+ if (save_security_ctx)
+ thd->security_ctx= save_security_ctx;
+ return res;
+}
+#endif
+
+
/*
Find a field by name in a view that uses merge algorithm.
@@ -2727,11 +2770,11 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
*/
DBUG_RETURN(((Item_field*) (field_it.item()))->field);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_grants &&
- check_grant_column(thd, &table_list->grant,
- table_list->view_db.str,
- table_list->view_name.str,
- name, length))
+ if (check_grant_column_in_sctx(thd, &table_list->grant,
+ table_list->view_db.str,
+ table_list->view_name.str, name, length,
+ check_grants,
+ table_list->security_ctx))
DBUG_RETURN(WRONG_GRANT);
#endif
// in PS use own arena or data will be freed after prepare
@@ -2900,7 +2943,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
Field *
find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
bool check_grants, bool allow_rowid,
- uint *cached_field_index_ptr)
+ uint *cached_field_index_ptr,
+ Security_context *sctx)
{
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
@@ -2909,7 +2953,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->s->fields &&
- !my_strcasecmp(system_charset_info,
+ !my_strcasecmp(system_charset_info,
table->field[cached_field_index]->field_name, name))
field_ptr= table->field + cached_field_index;
else if (table->s->name_hash.records)
@@ -2940,9 +2984,10 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
update_field_dependencies(thd, field, table);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_grants && check_grant_column(thd, &table->grant,
- table->s->db,
- table->s->table_name, name, length))
+ if (check_grant_column_in_sctx(thd, &table->grant,
+ table->s->db, table->s->table_name,
+ name, length,
+ check_grants, sctx))
field= WRONG_GRANT;
#endif
DBUG_RETURN(field);
@@ -3054,7 +3099,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
DBUG_ASSERT(table_list->table);
if ((fld= find_field_in_table(thd, table_list->table, name, length,
check_grants_table, allow_rowid,
- cached_field_index_ptr)))
+ cached_field_index_ptr,
+ table_list->security_ctx)))
*actual_table= table_list;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* check for views with temporary table algorithm */
@@ -3188,7 +3234,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
test(table_ref->table->
grant.want_privilege) &&
check_privileges,
- 1, &(item->cached_field_index));
+ 1, &(item->cached_field_index),
+ table_ref->security_ctx);
else
found= find_field_in_table_ref(thd, table_ref, name, item->name,
NULL, NULL, length, ref,
@@ -4324,8 +4371,12 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
{
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- if (table->view && table->effective_algorithm == VIEW_ALGORITHM_MERGE)
- list= make_leaves_list(list, table->ancestor);
+ if (table->merge_underlying_list)
+ {
+ DBUG_ASSERT(table->view &&
+ table->effective_algorithm == VIEW_ALGORITHM_MERGE);
+ list= make_leaves_list(list, table->merge_underlying_list);
+ }
else
{
*list= table;
@@ -4425,16 +4476,17 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
table_list;
table_list= table_list->next_local)
{
- if (table_list->ancestor)
+ if (table_list->merge_underlying_list)
{
- DBUG_ASSERT(table_list->view);
+ DBUG_ASSERT(table_list->view &&
+ table_list->effective_algorithm == VIEW_ALGORITHM_MERGE);
Query_arena *arena= thd->stmt_arena, backup;
bool res;
if (arena->is_conventional())
arena= 0; // For easier test
else
thd->set_n_backup_active_arena(arena, &backup);
- res= table_list->setup_ancestor(thd);
+ res= table_list->setup_underlying(thd);
if (arena)
thd->restore_active_arena(arena, &backup);
if (res)
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index ed781e9bba3..3965079988b 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -2208,15 +2208,10 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
tables_used->view_db.length + 1,
HA_CACHE_TBL_NONTRANSACT, 0, 0))
DBUG_RETURN(0);
- {
- TABLE_COUNTER_TYPE inc= register_tables_from_list(tables_used->ancestor,
- n + 1,
- block_table + 1);
- if (!inc)
- DBUG_RETURN(0);
- n+= inc;
- block_table+= inc;
- }
+ /*
+ We do not need to register view tables here because they are already
+ present in the global list.
+ */
}
else
{
@@ -2832,13 +2827,6 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used,
tables_used->view_name.str,
tables_used->view_db.str));
*tables_type|= HA_CACHE_TBL_NONTRANSACT;
- {
- TABLE_COUNTER_TYPE subcount;
- if (!(subcount= process_and_count_tables(tables_used->ancestor,
- tables_type)))
- DBUG_RETURN(0);
- table_count+= subcount;
- }
}
else
{
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index c8e0b6c66f9..24b11916bf1 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -416,8 +416,9 @@ bool mysql_multi_delete_prepare(THD *thd)
if (!(target_tbl->table= target_tbl->correspondent_table->table))
{
DBUG_ASSERT(target_tbl->correspondent_table->view &&
- target_tbl->correspondent_table->ancestor &&
- target_tbl->correspondent_table->ancestor->next_local);
+ target_tbl->correspondent_table->merge_underlying_list &&
+ target_tbl->correspondent_table->merge_underlying_list->
+ next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
target_tbl->correspondent_table->view_db.str,
target_tbl->correspondent_table->view_name.str);
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 74b239e1637..e1817985cbd 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -35,14 +35,14 @@
processor procedure of derived table processing
RETURN
- 0 ok
- 1 Error and error message given
+ FALSE OK
+ TRUE Error
*/
-int
-mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*))
+bool
+mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*))
{
- int res= 0;
+ bool res= FALSE;
if (lex->derived_tables)
{
lex->thd->derived_tables_processing= TRUE;
@@ -95,16 +95,16 @@ out:
close_thread_tables()
RETURN
- 0 ok
- 1 Error and an error message was given
+ FALSE OK
+ TRUE Error
*/
-int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
+bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
SELECT_LEX_UNIT *unit= orig_table_list->derived;
- int res= 0;
ulonglong create_options;
DBUG_ENTER("mysql_derived_prepare");
+ bool res= FALSE;
if (unit)
{
SELECT_LEX *first_select= unit->first_select();
@@ -118,7 +118,7 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
sl->context.outer_context= 0;
if (!(derived_result= new select_union))
- DBUG_RETURN(1); // out of memory
+ DBUG_RETURN(TRUE); // out of memory
// st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived_result, 0)))
@@ -184,7 +184,10 @@ exit:
table->derived_select_number= first_select->select_number;
table->s->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- table->grant.privilege= SELECT_ACL;
+ if (orig_table_list->referencing_view)
+ table->grant= orig_table_list->grant;
+ else
+ table->grant.privilege= SELECT_ACL;
#endif
orig_table_list->db= (char *)"";
orig_table_list->db_length= 0;
@@ -195,8 +198,8 @@ exit:
thd->derived_tables= table;
}
}
- else if (orig_table_list->ancestor)
- orig_table_list->set_ancestor();
+ else if (orig_table_list->merge_underlying_list)
+ orig_table_list->set_underlying_merge();
DBUG_RETURN(res);
}
@@ -220,15 +223,15 @@ exit:
Due to evaluation of LIMIT clause it can not be used at prepared stage.
RETURN
- 0 ok
- 1 Error and an error message was given
+ FALSE OK
+ TRUE Error
*/
-int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
+bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
TABLE *table= orig_table_list->table;
SELECT_LEX_UNIT *unit= orig_table_list->derived;
- int res= 0;
+ bool res= FALSE;
/*check that table creation pass without problem and it is derived table */
if (table && unit)
@@ -271,7 +274,7 @@ int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
there were no derived tables
*/
if (derived_result->flush())
- res= 1;
+ res= TRUE;
if (!lex->describe)
unit->cleanup();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 1bf346eafb1..88d34a81ea8 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -801,6 +801,11 @@ typedef struct st_lex
*/
uint table_count;
uint8 describe;
+ /*
+ A flag that indicates what kinds of derived tables are present in the
+ query (0 if no derived tables, otherwise a combination of flags
+ DERIVED_SUBQUERY and DERIVED_VIEW.
+ */
uint8 derived_tables;
uint8 create_view_algorithm;
uint8 create_view_check;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 33020fccd8e..5b27eafcaff 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5026,8 +5026,13 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
{
uint found=0;
ulong found_access=0;
- TABLE_LIST *org_tables=tables;
- for (; tables; tables= tables->next_global)
+ TABLE_LIST *org_tables= tables;
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+ /*
+ Iterate tables until first prelocking placeholder (if this query do not
+ have placeholders first_not_own_table is 0)
+ */
+ for (; tables && tables != first_not_own_table; tables= tables->next_global)
{
if (tables->schema_table &&
(want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
@@ -5038,6 +5043,11 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
information_schema_name.str);
return TRUE;
}
+ /*
+ Register access for view underlying table.
+ Remove SHOW_VIEW_ACL, because it will be checked during making view
+ */
+ tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
if (tables->derived || tables->schema_table || tables->belong_to_view ||
(tables->table && (int)tables->table->s->tmp_table) ||
my_tz_check_n_skip_implicit_tables(&tables,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 865c597e00d..67fb8245c58 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1158,6 +1158,7 @@ static int mysql_test_update(Prepared_statement *stmt,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= want_privilege;
table_list->table->grant.want_privilege= want_privilege;
+ table_list->register_want_access(want_privilege);
#endif
thd->lex->select_lex.no_wrap_view_item= TRUE;
res= setup_fields(thd, 0, select->item_list, 1, 0, 0);
@@ -1169,6 +1170,7 @@ static int mysql_test_update(Prepared_statement *stmt,
table_list->grant.want_privilege=
table_list->table->grant.want_privilege=
(SELECT_ACL & ~table_list->table->grant.privilege);
+ table_list->register_want_access(SELECT_ACL);
#endif
if (setup_fields(thd, 0, stmt->lex->value_list, 0, 0, 0))
goto error;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index a8e21177338..9900625f208 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -195,6 +195,7 @@ int mysql_update(THD *thd,
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
+ table_list->register_want_access(want_privilege);
#endif
if (setup_fields_with_no_wrap(thd, 0, fields, 1, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
@@ -584,6 +585,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
+ table_list->register_want_access(SELECT_ACL);
#endif
bzero((char*) &tables,sizeof(tables)); // For ORDER BY
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 858f0c2520e..f123fee9c60 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -722,6 +722,7 @@ loop_out:
}
+
/*
read VIEW .frm and create structures
@@ -738,11 +739,26 @@ loop_out:
my_bool
mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
+ THD *thd= current_thd;
DBUG_ENTER("mysql_make_view");
DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name));
if (table->view)
{
+ /*
+ It's an execution of a PS/SP and the view has already been unfolded
+ into a list of used tables. Now we only need to update the information
+ about granted privileges in the view tables with the actual data
+ stored in MySQL privilege system. We don't need to restore the
+ required privileges (by calling register_want_access) because they has
+ not changed since PREPARE or the previous execution: the only case
+ when this information is changed is execution of UPDATE on a view, but
+ the original want_access is restored in its end.
+ */
+ if (!table->prelocking_placeholder && table->prepare_security(thd))
+ {
+ DBUG_RETURN(1);
+ }
DBUG_PRINT("info",
("VIEW %s.%s is already processed on previous PS/SP execution",
table->view_db.str, table->view_name.str));
@@ -750,7 +766,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
SELECT_LEX *end;
- THD *thd= current_thd;
LEX *old_lex= thd->lex, *lex;
SELECT_LEX *view_select;
int res= 0;
@@ -769,7 +784,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (!table->timestamp.str)
table->timestamp.str= table->timestamp_buffer;
/* prepare default values for old format */
- table->view_suid= 1;
+ table->view_suid= TRUE;
table->definer.user.str= table->definer.host.str= 0;
table->definer.user.length= table->definer.host.length= 0;
@@ -880,6 +895,10 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
goto err;
}
+
+ if (!(table->view_tables=
+ (List<TABLE_LIST>*) new(thd->mem_root) List<TABLE_LIST>))
+ goto err;
/*
mark to avoid temporary table using and put view reference and find
last view table
@@ -890,6 +909,22 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
tbl->skip_temporary= 1;
tbl->belong_to_view= top_view;
+ tbl->referencing_view= table;
+ /*
+ First we fill want_privilege with SELECT_ACL (this is needed for the
+ tables which belongs to view subqueries and temporary table views,
+ then for the merged view underlying tables we will set wanted
+ privileges of top_view
+ */
+ tbl->grant.want_privilege= SELECT_ACL;
+ /*
+ After unfolding the view we lose the list of tables referenced in it
+ (we will have only a list of underlying tables in case of MERGE
+ algorithm, which does not include the tables referenced from
+ subqueries used in view definition).
+ Let's build a list of all tables referenced in the view.
+ */
+ table->view_tables->push_back(tbl);
}
/*
@@ -915,16 +950,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
/*
- Let us set proper lock type for tables of the view's main select
- since we may want to perform update or insert on view. This won't
- work for view containing union. But this is ok since we don't
- allow insert and update on such views anyway.
- */
- if (!lex->select_lex.next_select())
- for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local)
- tbl->lock_type= table->lock_type;
-
- /*
If we are opening this view as part of implicit LOCK TABLES, then
this view serves as simple placeholder and we should not continue
further processing.
@@ -932,7 +957,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (table->prelocking_placeholder)
goto ok2;
- old_lex->derived_tables|= DERIVED_VIEW;
+ old_lex->derived_tables|= (DERIVED_VIEW | lex->derived_tables);
/* move SQL_NO_CACHE & Co to whole query */
old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
@@ -941,6 +966,37 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (view_select->options & OPTION_TO_QUERY_CACHE)
old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+ if (table->view_suid)
+ {
+ /*
+ Prepare a security context to check underlying objects of the view
+ */
+ Security_context *save_security_ctx= thd->security_ctx;
+ if (!(table->view_sctx= (Security_context *)
+ thd->stmt_arena->alloc(sizeof(Security_context))))
+ goto err;
+ /* Assign the context to the tables referenced in the view */
+ for (tbl= view_tables; tbl; tbl= tbl->next_global)
+ tbl->security_ctx= table->view_sctx;
+ /* assign security context to SELECT name resolution contexts of view */
+ for(SELECT_LEX *sl= lex->all_selects_list;
+ sl;
+ sl= sl->next_select_in_list())
+ sl->context.security_ctx= table->view_sctx;
+ }
+
+ /*
+ Setup an error processor to hide error messages issued by stored
+ routines referenced in the view
+ */
+ for (SELECT_LEX *sl= lex->all_selects_list;
+ sl;
+ sl= sl->next_select_in_list())
+ {
+ sl->context.error_processor= &view_error_processor;
+ sl->context.error_processor_data= (void *)table;
+ }
+
/*
check MERGE algorithm ability
- algorithm is not explicit TEMPORARY TABLE
@@ -962,24 +1018,28 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->updatable= (table->updatable_view != 0);
table->effective_with_check=
old_lex->get_effective_with_check(table);
+ table->merge_underlying_list= view_tables;
+ /*
+ Let us set proper lock type for tables of the view's main select
+ since we may want to perform update or insert on view. This won't
+ work for view containing union. But this is ok since we don't
+ allow insert and update on such views anyway.
+
+ Also we fill correct wanted privileges.
+ */
+ for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local)
+ {
+ tbl->lock_type= table->lock_type;
+ tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
+ }
/* prepare view context */
- lex->select_lex.context.resolve_in_table_list_only(table->ancestor=
- view_tables);
+ lex->select_lex.context.resolve_in_table_list_only(view_tables);
lex->select_lex.context.outer_context= 0;
lex->select_lex.context.select_lex= table->select_lex;
lex->select_lex.select_n_having_items+=
table->select_lex->select_n_having_items;
- /* do not check privileges & hide errors for view underlyings */
- for (SELECT_LEX *sl= lex->all_selects_list;
- sl;
- sl= sl->next_select_in_list())
- {
- sl->context.check_privileges= FALSE;
- sl->context.error_processor= &view_error_processor;
- sl->context.error_processor_data= (void *)table;
- }
/*
Tables of the main select of the view should be marked as belonging
to the same select as original view (again we can use LEX::select_lex
@@ -1012,7 +1072,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
}
- /* Store WHERE clause for post-processing in setup_ancestor */
+ /* Store WHERE clause for post-processing in setup_underlying */
table->where= view_select->where;
/*
Add subqueries units to SELECT into which we merging current view.
@@ -1078,6 +1138,11 @@ ok2:
if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used)
old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used;
thd->lex= old_lex;
+ if (!table->prelocking_placeholder && table->prepare_security(thd))
+ {
+ DBUG_RETURN(1);
+ }
+
DBUG_RETURN(0);
err:
diff --git a/sql/table.cc b/sql/table.cc
index 722e4e4df25..e1d6f5c7346 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1798,41 +1798,43 @@ void st_table_list::calc_md5(char *buffer)
/*
- set ancestor TABLE for table place holder of VIEW
+ set underlying TABLE for table place holder of VIEW
DESCRIPTION
Replace all views that only uses one table with the table itself.
This allows us to treat the view as a simple table and even update
- it
+ it (it is a kind of optimisation)
SYNOPSIS
- st_table_list::set_ancestor()
+ st_table_list::set_underlying_merge()
*/
-void st_table_list::set_ancestor()
+void st_table_list::set_underlying_merge()
{
TABLE_LIST *tbl;
- if ((tbl= ancestor))
+ if ((tbl= merge_underlying_list))
{
/* This is a view. Process all tables of view */
- DBUG_ASSERT(view);
+ DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE);
do
{
- if (tbl->ancestor) // This is a view
+ if (tbl->merge_underlying_list) // This is a view
{
+ 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->ancestor->set_ancestor();
+ tbl->merge_underlying_list->set_underlying_merge();
}
} while ((tbl= tbl->next_local));
if (!multitable_view)
{
- table= ancestor->table;
- schema_table= ancestor->schema_table;
+ table= merge_underlying_list->table;
+ schema_table= merge_underlying_list->schema_table;
}
}
}
@@ -1842,12 +1844,9 @@ void st_table_list::set_ancestor()
setup fields of placeholder of merged VIEW
SYNOPSIS
- st_table_list::setup_ancestor()
+ st_table_list::setup_underlying()
thd - thread handler
- NOTES
- ancestor is list of tables and views used by view (underlying tables/views)
-
DESCRIPTION
It is:
- preparing translation table for view columns
@@ -1858,10 +1857,11 @@ void st_table_list::set_ancestor()
TRUE - error
*/
-bool st_table_list::setup_ancestor(THD *thd)
+bool st_table_list::setup_underlying(THD *thd)
{
- DBUG_ENTER("st_table_list::setup_ancestor");
- if (!field_translation)
+ DBUG_ENTER("st_table_list::setup_underlying");
+
+ if (!field_translation && merge_underlying_list)
{
Field_translator *transl;
SELECT_LEX *select= &view->select_lex;
@@ -1875,10 +1875,10 @@ bool st_table_list::setup_ancestor(THD *thd)
DBUG_RETURN(TRUE);
}
- for (tbl= ancestor; tbl; tbl= tbl->next_local)
+ for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
- if (tbl->ancestor &&
- tbl->setup_ancestor(thd))
+ if (tbl->merge_underlying_list &&
+ tbl->setup_underlying(thd))
{
DBUG_RETURN(TRUE);
}
@@ -1941,7 +1941,7 @@ bool st_table_list::prep_where(THD *thd, Item **conds,
{
DBUG_ENTER("st_table_list::prep_where");
- for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
{
@@ -2023,7 +2023,7 @@ 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)
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
if (tbl->view &&
@@ -2046,7 +2046,7 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
}
if (check_opt_type == VIEW_CHECK_CASCADED)
{
- for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->check_option)
item= and_conds(item, tbl->check_option);
@@ -2085,16 +2085,21 @@ 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)
+ thd->net.last_errno == ER_SP_DOES_NOT_EXIST ||
+ thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR ||
+ thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR)
{
+ TABLE_LIST *top= top_table();
thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str);
+ my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
{
+ TABLE_LIST *top= top_table();
thd->clear_error();
// TODO: make correct error message
- my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), view_db.str, view_name.str);
+ my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
+ top->view_db.str, top->view_name.str);
}
}
@@ -2115,10 +2120,10 @@ void st_table_list::hide_view_error(THD *thd)
st_table_list *st_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 && ancestor == 0)
+ if (table == table_to_find && merge_underlying_list == 0)
return this;
- for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
TABLE_LIST *result;
if ((result= tbl->find_underlying_table(table_to_find)))
@@ -2201,7 +2206,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure)
bool st_table_list::check_single_table(st_table_list **table, table_map map,
st_table_list *view)
{
- for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->table)
{
@@ -2243,8 +2248,8 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
}
else
{
- DBUG_ASSERT(view && ancestor);
- for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ DBUG_ASSERT(view && merge_underlying_list);
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
return TRUE;
}
@@ -2390,6 +2395,159 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
}
+/*
+ Register access mode which we need for underlying tables
+
+ SYNOPSIS
+ register_want_access()
+ want_access Acess which we require
+*/
+
+void st_table_list::register_want_access(ulong want_access)
+{
+ /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
+ want_access&= ~SHOW_VIEW_ACL;
+ if (belong_to_view)
+ {
+ grant.want_privilege= want_access;
+ if (table)
+ table->grant.want_privilege= want_access;
+ }
+ for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
+ tbl->register_want_access(want_access);
+}
+
+
+/*
+ Load security context infoemation for this view
+
+ SYNOPSIS
+ st_table_list::prepare_view_securety_context()
+ thd [in] thread handler
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+bool st_table_list::prepare_view_securety_context(THD *thd)
+{
+ DBUG_ENTER("st_table_list::prepare_view_securety_context");
+ DBUG_PRINT("enter", ("table: %s", alias));
+
+ DBUG_ASSERT(!prelocking_placeholder && view);
+ if (view_suid)
+ {
+ DBUG_PRINT("info", ("This table is suid view => load contest"));
+ DBUG_ASSERT(view && view_sctx);
+ if (acl_getroot_no_password(view_sctx,
+ definer.user.str,
+ definer.host.str,
+ definer.host.str,
+ thd->db))
+ {
+ my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+#endif
+
+
+/*
+ Find security context of current view
+
+ SYNOPSIS
+ st_table_list::find_view_security_context()
+ thd [in] thread handler
+
+*/
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+Security_context *st_table_list::find_view_security_context(THD *thd)
+{
+ Security_context *sctx;
+ TABLE_LIST *upper_view= this;
+ DBUG_ENTER("st_table_list::find_view_security_context");
+
+ DBUG_ASSERT(view);
+ while (upper_view && !upper_view->view_suid)
+ {
+ DBUG_ASSERT(!upper_view->prelocking_placeholder);
+ upper_view= upper_view->referencing_view;
+ }
+ if (upper_view)
+ {
+ DBUG_PRINT("info", ("Securety context of view %s will be used",
+ upper_view->alias));
+ sctx= upper_view->view_sctx;
+ DBUG_ASSERT(sctx);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Current global context will be used"));
+ sctx= thd->security_ctx;
+ }
+ DBUG_RETURN(sctx);
+}
+#endif
+
+
+/*
+ Prepare security context and load underlying tables priveleges for view
+
+ SYNOPSIS
+ st_table_list::prepare_security()
+ thd [in] thread handler
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
+bool st_table_list::prepare_security(THD *thd)
+{
+ List_iterator_fast<TABLE_LIST> tb(*view_tables);
+ TABLE_LIST *tbl;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx= thd->security_ctx;
+ DBUG_ENTER("st_table_list::prepare_security");
+
+ DBUG_ASSERT(!prelocking_placeholder);
+ if (prepare_view_securety_context(thd))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ thd->security_ctx= find_view_security_context(thd);
+ while ((tbl= tb++))
+ {
+ DBUG_ASSERT(tbl->referencing_view);
+ char *db, *table_name;
+ if (tbl->view)
+ {
+ db= tbl->view_db.str;
+ table_name= tbl->view_name.str;
+ }
+ else
+ {
+ db= tbl->db;
+ table_name= tbl->table_name;
+ }
+ fill_effective_table_privileges(thd, &tbl->grant, db, table_name);
+ if (tbl->table)
+ tbl->table->grant= grant;
+ }
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(FALSE);
+#else
+ while ((tbl= tb++))
+ tbl->grant.privilege= ~NO_ACCESS;
+#endif
+}
+
+
Natural_join_column::Natural_join_column(Field_translator *field_param,
TABLE_LIST *tab)
{
@@ -2498,6 +2656,9 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length)
GRANT_INFO *grant;
const char *db_name;
const char *table_name;
+ Security_context *save_security_ctx= 0;
+ Security_context *new_sctx= table_ref->security_ctx;
+ bool res;
if (view_field)
{
@@ -2514,7 +2675,15 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length)
table_name= table_ref->table->s->table_name;
}
- return check_grant_column(thd, grant, db_name, table_name, name, length);
+ if (new_sctx)
+ {
+ save_security_ctx= thd->security_ctx;
+ thd->security_ctx= new_sctx;
+ }
+ res= check_grant_column(thd, grant, db_name, table_name, name, length);
+ if (save_security_ctx)
+ thd->security_ctx= save_security_ctx;
+ return res;
}
#endif
diff --git a/sql/table.h b/sql/table.h
index e76d005f494..aff687077b8 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -22,6 +22,7 @@ class GRANT_TABLE;
class st_select_lex_unit;
class st_select_lex;
class COND_EQUAL;
+class Security_context;
/* Order clause list element */
@@ -47,6 +48,11 @@ typedef struct st_grant_info
uint version;
ulong privilege;
ulong want_privilege;
+ /*
+ Stores the requested access acl of top level tables list. Is used to
+ check access rights to the underlying tables of a view.
+ */
+ ulong orig_want_privilege;
} GRANT_INFO;
enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2,
@@ -359,7 +365,6 @@ typedef struct st_schema_table
#define VIEW_CHECK_SKIP 2
struct st_lex;
-struct st_table_list;
class select_union;
class TMP_TABLE_PARAM;
@@ -525,11 +530,36 @@ typedef struct st_table_list
Field_translator *field_translation; /* array of VIEW fields */
/* pointer to element after last one in translation table above */
Field_translator *field_translation_end;
- /* list of ancestor(s) of this table (underlying table(s)/view(s) */
- st_table_list *ancestor;
+ /*
+ List (based on next_local) of underlying tables of this view. I.e. it
+ does not include the tables of subqueries used in the view. Is set only
+ for merged views.
+ */
+ st_table_list *merge_underlying_list;
+ /*
+ - 0 for base tables
+ - in case of the view it is the list of all (not only underlying
+ tables but also used in subquery ones) tables of the view.
+ */
+ List<st_table_list> *view_tables;
/* most upper view this table belongs to */
st_table_list *belong_to_view;
/*
+ The view directly referencing this table
+ (non-zero only for merged underlying tables of a view).
+ */
+ st_table_list *referencing_view;
+ /*
+ security context (non-zero only for tables which belong
+ to view with SQL SEURITY DEFINER)
+ */
+ Security_context *security_ctx;
+ /*
+ this view security context (non-zero only for views with
+ SQL SEURITY DEFINER)
+ */
+ Security_context *view_sctx;
+ /*
List of all base tables local to a subquery including all view
tables. Unlike 'next_local', this in this list views are *not*
leaves. Created in setup_tables() -> make_leaves_list().
@@ -595,9 +625,9 @@ typedef struct st_table_list
bool prelocking_placeholder;
void calc_md5(char *buffer);
- void set_ancestor();
+ void set_underlying_merge();
int view_check_option(THD *thd, bool ignore_failure);
- bool setup_ancestor(THD *thd);
+ bool setup_underlying(THD *thd);
void cleanup_items();
bool placeholder() {return derived || view; }
void print(THD *thd, String *str);
@@ -625,6 +655,14 @@ typedef struct st_table_list
return prep_where(thd, conds, no_where_clause);
return FALSE;
}
+
+ void register_want_access(ulong want_access);
+ bool prepare_security(THD *thd);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *find_view_security_context(THD *thd);
+ bool prepare_view_securety_context(THD *thd);
+#endif
+
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);