diff options
Diffstat (limited to 'sql/sql_view.cc')
-rw-r--r-- | sql/sql_view.cc | 194 |
1 files changed, 87 insertions, 107 deletions
diff --git a/sql/sql_view.cc b/sql/sql_view.cc index b081e3c8dbb..50e3c615b86 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2004, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2014, SkySQL Ab. + Copyright (c) 2011, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ */ #define MYSQL_LEX 1 -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_view.h" @@ -33,7 +33,8 @@ #include "sp_head.h" #include "sp.h" #include "sp_cache.h" -#include "datadict.h" // dd_frm_type() +#include "datadict.h" // dd_frm_is_view() +#include "sql_derived.h" #define MD5_BUFF_LENGTH 33 @@ -210,16 +211,12 @@ static void make_valid_column_names(List<Item> &item_list) static bool fill_defined_view_parts (THD *thd, TABLE_LIST *view) { - char key[MAX_DBKEY_LENGTH]; - uint key_length; LEX *lex= thd->lex; TABLE_LIST decoy; memcpy (&decoy, view, sizeof (TABLE_LIST)); - key_length= create_table_def_key(thd, key, view, 0); - - if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length, - thd->mem_root, OPEN_VIEW_NO_PARSE)) + if (tdc_open_view(thd, &decoy, decoy.alias, thd->mem_root, + OPEN_VIEW_NO_PARSE)) return TRUE; if (!lex->definer) @@ -248,7 +245,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) @param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE @retval FALSE Operation was a success. - @retval TRUE An error occured. + @retval TRUE An error occurred. */ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, @@ -353,7 +350,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, while ((item= it++)) { Item_field *field; - if ((field= item->filed_for_view_update())) + if ((field= item->field_for_view_update())) { /* any_privileges may be reset later by the Item_field::set_field @@ -391,7 +388,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, @note This function handles both create and alter view commands. @retval FALSE Operation was a success. - @retval TRUE An error occured. + @retval TRUE An error occurred. */ bool mysql_create_view(THD *thd, TABLE_LIST *views, @@ -433,7 +430,17 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, lex->link_first_table_back(view, link_to_local); view->open_type= OT_BASE_ONLY; - if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0)) + /* + ignore lock specs for CREATE statement + */ + if (lex->current_select->lock_type != TL_READ_DEFAULT) + { + lex->current_select->set_lock_for_tables(TL_READ_DEFAULT, false); + view->mdl_request.set_type(MDL_EXCLUSIVE); + } + + if (open_temporary_tables(thd, lex->query_tables) || + open_and_lock_tables(thd, lex->query_tables, TRUE, 0)) { view= lex->unlink_first_table(&link_to_local); res= TRUE; @@ -467,60 +474,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } sp_cache_invalidate(); + if (sp_process_definer(thd)) + goto err; - if (!lex->definer) - { - /* - DEFINER-clause is missing; we have to create default definer in - persistent arena to be PS/SP friendly. - If this is an ALTER VIEW then the current user should be set as - the definer. - */ - Query_arena original_arena; - Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); - - if (!(lex->definer= create_default_definer(thd))) - res= TRUE; - - if (ps_arena) - thd->restore_active_arena(ps_arena, &original_arena); - - if (res) - goto err; - } - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* - check definer of view: - - same as current user - - current user has SUPER_ACL - */ - if (lex->definer && - (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host) != 0)) - { - if (!(thd->security_ctx->master_access & SUPER_ACL)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - res= TRUE; - goto err; - } - else - { - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_SUCH_USER, - ER(ER_NO_SUCH_USER), - lex->definer->user.str, - lex->definer->host.str); - } - } - } -#endif /* check that tables are not temporary and this VIEW do not used in query (it is possible with ALTERing VIEW). @@ -638,7 +594,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, Item *item; while ((item= it++)) { - Item_field *fld= item->filed_for_view_update(); + Item_field *fld= item->field_for_view_update(); uint priv= (get_column_grant(thd, &view->grant, view->db, view->table_name, item->name) & VIEW_ANY_ACL); @@ -668,6 +624,15 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, res= mysql_register_view(thd, view, mode); + /* + View TABLE_SHARE must be removed from the table definition cache in order to + make ALTER VIEW work properly. Otherwise, we would not be able to detect + meta-data changes after ALTER VIEW. + */ + + if (!res) + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false); + if (!res && mysql_bin_log.is_open()) { String buff; @@ -722,7 +687,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, DBUG_RETURN(0); err: - thd_proc_info(thd, "end"); + THD_STAGE_INFO(thd, stage_end); lex->link_first_table_back(view, link_to_local); unit->cleanup(); DBUG_RETURN(res || thd->is_error()); @@ -953,14 +918,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->source= thd->lex->create_view_select; if (!thd->make_lex_string(&view->select_stmt, view_query.ptr(), - view_query.length(), false)) + view_query.length())) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); error= -1; goto err; } - view->file_version= 1; + /* + version 1 - before 10.0.5 + version 2 - empty definer_host means a role + */ + view->file_version= 2; view->mariadb_version= MYSQL_VERSION_ID; view->calc_md5(md5); if (!(view->md5.str= (char*) thd->memdup(md5, 32))) @@ -974,7 +943,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && !lex->can_be_merged()) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, ER(ER_WARN_VIEW_MERGE)); lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } @@ -1025,7 +994,7 @@ loop_out: fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); - if (!access(path.str, F_OK)) + if (ha_table_exists(thd, view->db, view->table_name, NULL)) { if (mode == VIEW_CREATE_NEW) { @@ -1079,7 +1048,7 @@ loop_out: view->view_creation_ctx->get_connection_cl()->name); if (!thd->make_lex_string(&view->view_body_utf8, is_query.ptr(), - is_query.length(), false)) + is_query.length())) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); error= -1; @@ -1148,19 +1117,16 @@ err: bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, uint flags) { - SELECT_LEX *end, *view_select; + SELECT_LEX *end, *UNINIT_VAR(view_select); LEX *old_lex, *lex; Query_arena *arena, backup; TABLE_LIST *top_view= table->top_table(); - bool parse_status; + bool UNINIT_VAR(parse_status); bool result, view_is_mergeable; TABLE_LIST *UNINIT_VAR(view_main_select_tables); DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); - LINT_INIT(parse_status); - LINT_INIT(view_select); - if (table->view) { /* @@ -1180,6 +1146,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, DBUG_PRINT("info", ("VIEW %s.%s is already processed on previous PS/SP execution", table->view_db.str, table->view_name.str)); + + /* + Clear old variables in the TABLE_LIST that could be left from an old view + This is only needed if there was an error at last usage of view, + in which case the reinit call wasn't done. + See MDEV-6668 for details. + */ + mysql_derived_reinit(thd, NULL, table); + DBUG_RETURN(0); } @@ -1240,11 +1215,19 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, DBUG_ASSERT(!table->definer.host.str && !table->definer.user.length && !table->definer.host.length); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - get_default_definer(thd, &table->definer); + get_default_definer(thd, &table->definer, false); } + + /* + since 10.0.5 definer.host can never be "" for a User, but it's + always "" for a Role. Before 10.0.5 it could be "" for a User, + but roles didn't exist. file_version helps. + */ + if (!table->definer.host.str[0] && table->file_version < 2) + table->definer.host= host_not_specified; // User, not Role /* Initialize view definition context by character set names loaded from @@ -1368,15 +1351,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, TABLE_LIST *view_tables= lex->query_tables; TABLE_LIST *view_tables_tail= 0; TABLE_LIST *tbl; - Security_context *security_ctx; + Security_context *security_ctx= 0; /* Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show underlying tables. Skip this step if we are opening view for prelocking only. */ - if (!table->prelocking_placeholder && - (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)) + if (!table->prelocking_placeholder && (old_lex->describe)) { /* The user we run EXPLAIN as (either the connected user who issued @@ -1549,6 +1531,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, if (view_select->options & OPTION_TO_QUERY_CACHE) old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (table->view_suid) { /* @@ -1569,6 +1552,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, */ security_ctx= table->security_ctx; } +#endif /* Assign the context to the tables referenced in the view */ if (view_tables) @@ -1646,7 +1630,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, lex->select_lex.order_list.elements && !table->select_lex->master_unit()->is_union()) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_ORDERBY_IGNORED, ER(ER_VIEW_ORDERBY_IGNORED), table->db, table->table_name); @@ -1728,7 +1712,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) String non_existant_views; char *wrong_object_db= NULL, *wrong_object_name= NULL; bool error= FALSE; - enum legacy_db_type not_used; bool some_views_deleted= FALSE; bool something_wrong= FALSE; DBUG_ENTER("mysql_drop_view"); @@ -1745,29 +1728,33 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) DBUG_RETURN(TRUE); } - if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); for (view= views; view; view= view->next_local) { - frm_type_enum type= FRMTYPE_ERROR; + bool not_exist; build_table_filename(path, sizeof(path) - 1, view->db, view->table_name, reg_ext, 0); - if (access(path, F_OK) || - FRMTYPE_VIEW != (type= dd_frm_type(thd, path, ¬_used))) + if ((not_exist= my_access(path, F_OK)) || !dd_frm_is_view(thd, path)) { char name[FN_REFLEN]; my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name); - if (thd->lex->drop_if_exists) + if (thd->lex->check_exists) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), name); continue; } - if (type == FRMTYPE_TABLE) + if (not_exist) + { + if (non_existant_views.length()) + non_existant_views.append(','); + non_existant_views.append(name); + } + else { if (!wrong_object_name) { @@ -1775,12 +1762,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) wrong_object_name= view->table_name; } } - else - { - if (non_existant_views.length()) - non_existant_views.append(','); - non_existant_views.append(String(view->table_name,system_charset_info)); - } continue; } if (mysql_file_delete(key_file_frm, path, MYF(MY_WME))) @@ -1789,9 +1770,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) some_views_deleted= TRUE; /* - For a view, there is a TABLE_SHARE object, but its - ref_count never goes above 1. Remove it from the table - definition cache, in case the view was cached. + For a view, there is a TABLE_SHARE object. + Remove it from the table definition cache, in case the view was cached. */ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, FALSE); @@ -1898,7 +1878,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) { KEY_PART_INFO *key_part= key_info->key_part; - KEY_PART_INFO *key_part_end= key_part + key_info->key_parts; + KEY_PART_INFO *key_part_end= key_part + key_info->user_defined_key_parts; /* check that all key parts are used */ for (;;) @@ -1907,7 +1887,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) for (k= trans; k < end_of_trans; k++) { Item_field *field; - if ((field= k->item->filed_for_view_update()) && + if ((field= k->item->field_for_view_update()) && field->field == key_part->field) break; } @@ -1929,7 +1909,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) for (fld= trans; fld < end_of_trans; fld++) { Item_field *field; - if ((field= fld->item->filed_for_view_update()) && + if ((field= fld->item->field_for_view_update()) && field->field == *field_ptr) break; } @@ -1943,7 +1923,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) if (thd->variables.updatable_views_with_limit) { /* update allowed, but issue warning */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); DBUG_RETURN(FALSE); } @@ -1983,7 +1963,7 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view) for (Field_translator *entry= trans; entry < trans_end; entry++) { Item_field *fld; - if ((fld= entry->item->filed_for_view_update())) + if ((fld= entry->item->field_for_view_update())) list->push_back(fld); else { |