diff options
author | bell@sanja.is.com.ua <> | 2005-10-28 00:24:13 +0300 |
---|---|---|
committer | bell@sanja.is.com.ua <> | 2005-10-28 00:24:13 +0300 |
commit | 6143c6543efa3dd527e5fd867c5fa3b36fbf6586 (patch) | |
tree | fe93843434a12c6357de38534683f49f8f411744 /sql | |
parent | a9b1ff40951a74666f3de90c7d79f40fc507ae16 (diff) | |
parent | 1b164c7b83f7081acc99f877d2b7fbfbdedbec2f (diff) | |
download | mariadb-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.cc | 16 | ||||
-rw-r--r-- | sql/item.h | 8 | ||||
-rw-r--r-- | sql/item_func.cc | 36 | ||||
-rw-r--r-- | sql/mysql_priv.h | 14 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 3 | ||||
-rw-r--r-- | sql/sql_acl.cc | 55 | ||||
-rw-r--r-- | sql/sql_base.cc | 94 | ||||
-rw-r--r-- | sql/sql_cache.cc | 20 | ||||
-rw-r--r-- | sql/sql_delete.cc | 5 | ||||
-rw-r--r-- | sql/sql_derived.cc | 39 | ||||
-rw-r--r-- | sql/sql_lex.h | 5 | ||||
-rw-r--r-- | sql/sql_parse.cc | 14 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/sql_view.cc | 115 | ||||
-rw-r--r-- | sql/table.cc | 233 | ||||
-rw-r--r-- | sql/table.h | 48 |
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); |