用于EagleEye3.0 规则集漏报和误报测试的示例项目,项目收集于github和gitee
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

729 lines
24 KiB

/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql/dd/dd_view.h"
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <memory>
#include <string>
#include "lex_string.h"
#include "my_alloc.h"
#include "my_dbug.h"
#include "my_inttypes.h"
#include "my_loglevel.h"
#include "my_sys.h"
#include "my_time.h"
#include "mysql/components/services/log_builtins.h"
#include "mysql/components/services/log_shared.h"
#include "mysql/mysql_lex_string.h"
#include "mysql/udf_registration_types.h"
#include "mysql_com.h"
#include "mysqld_error.h"
#include "sql/auth/sql_security_ctx.h"
#include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client
#include "sql/dd/dd.h" // dd::get_dictionary
#include "sql/dd/dd_table.h" // fill_dd_columns_from_create_*
#include "sql/dd/dictionary.h" // dd::Dictionary
#include "sql/dd/impl/dictionary_impl.h" // default_catalog_name
#include "sql/dd/impl/utils.h" // dd::my_time_t_to_ull_datetime()
#include "sql/dd/properties.h" // dd::Properties
#include "sql/dd/string_type.h"
#include "sql/dd/types/abstract_table.h" // dd::enum_table_type
#include "sql/dd/types/schema.h" // dd::Schema
#include "sql/dd/types/view.h" // dd::View
#include "sql/dd/types/view_routine.h" // dd::View_routine
#include "sql/dd/types/view_table.h" // dd::View_table
#include "sql/dd_table_share.h" // dd_get_mysql_charset
#include "sql/field.h"
#include "sql/handler.h"
#include "sql/histograms/value_map.h"
#include "sql/item.h"
#include "sql/item_create.h"
#include "sql/item_func.h" // Item_func
#include "sql/key.h"
#include "sql/log.h"
#include "sql/mem_root_array.h"
#include "sql/parse_file.h" // PARSE_FILE_TIMESTAMPLENGTH
#include "sql/sp.h" // Sroutine_hash_entry
#include "sql/sql_class.h" // THD
#include "sql/sql_lex.h"
#include "sql/sql_list.h"
#include "sql/sql_servers.h"
#include "sql/sql_tmp_table.h" // create_tmp_field
#include "sql/system_variables.h"
#include "sql/table.h"
#include "sql/thd_raii.h"
#include "sql/transaction.h" // trans_commit
#include "sql/tztime.h" // Time_zone
namespace dd {
static ulonglong dd_get_old_view_check_type(dd::View::enum_check_option type) {
switch (type) {
case dd::View::CO_NONE:
return VIEW_CHECK_NONE;
case dd::View::CO_LOCAL:
return VIEW_CHECK_LOCAL;
case dd::View::CO_CASCADED:
return VIEW_CHECK_CASCADED;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view check option.");
DBUG_ASSERT(false);
return VIEW_CHECK_NONE;
/* purecov: end */
}
/** For enum in dd::View */
static dd::View::enum_check_option dd_get_new_view_check_type(ulonglong type) {
switch (type) {
case VIEW_CHECK_NONE:
return dd::View::CO_NONE;
case VIEW_CHECK_LOCAL:
return dd::View::CO_LOCAL;
case VIEW_CHECK_CASCADED:
return dd::View::CO_CASCADED;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view check option.");
DBUG_ASSERT(false);
return dd::View::CO_NONE;
/* purecov: end */
}
static enum enum_view_algorithm dd_get_old_view_algorithm_type(
dd::View::enum_algorithm type) {
switch (type) {
case dd::View::VA_UNDEFINED:
return VIEW_ALGORITHM_UNDEFINED;
case dd::View::VA_TEMPORARY_TABLE:
return VIEW_ALGORITHM_TEMPTABLE;
case dd::View::VA_MERGE:
return VIEW_ALGORITHM_MERGE;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view algorithm.");
DBUG_ASSERT(false);
return VIEW_ALGORITHM_UNDEFINED;
/* purecov: end */
}
static dd::View::enum_algorithm dd_get_new_view_algorithm_type(
enum enum_view_algorithm type) {
switch (type) {
case VIEW_ALGORITHM_UNDEFINED:
return dd::View::VA_UNDEFINED;
case VIEW_ALGORITHM_TEMPTABLE:
return dd::View::VA_TEMPORARY_TABLE;
case VIEW_ALGORITHM_MERGE:
return dd::View::VA_MERGE;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view algorithm.");
DBUG_ASSERT(false);
return dd::View::VA_UNDEFINED;
/* purecov: end */
}
static ulonglong dd_get_old_view_security_type(
dd::View::enum_security_type type) {
switch (type) {
case dd::View::ST_DEFAULT:
return VIEW_SUID_DEFAULT;
case dd::View::ST_INVOKER:
return VIEW_SUID_INVOKER;
case dd::View::ST_DEFINER:
return VIEW_SUID_DEFINER;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view security type.");
DBUG_ASSERT(false);
return VIEW_SUID_DEFAULT;
/* purecov: end */
}
static dd::View::enum_security_type dd_get_new_view_security_type(
ulonglong type) {
switch (type) {
case VIEW_SUID_DEFAULT:
return dd::View::ST_DEFAULT;
case VIEW_SUID_INVOKER:
return dd::View::ST_INVOKER;
case VIEW_SUID_DEFINER:
return dd::View::ST_DEFINER;
}
/* purecov: begin deadcode */
LogErr(ERROR_LEVEL, ER_DD_FAILSAFE, "view security type.");
DBUG_ASSERT(false);
return dd::View::ST_DEFAULT;
/* purecov: end */
}
/**
Method to fill view columns from the first SELECT_LEX of view query.
@param thd Thread Handle.
@param view_obj DD view object.
@param view TABLE_LIST object of view.
@retval false On Success.
@retval true On failure.
*/
static bool fill_dd_view_columns(THD *thd, View *view_obj,
const TABLE_LIST *view) {
DBUG_TRACE;
// Helper class which takes care restoration of THD::variables.sql_mode and
// delete handler created for dummy table.
class Context_handler {
public:
Context_handler(THD *thd, handler *file) : m_thd(thd), m_file(file) {
m_sql_mode = m_thd->variables.sql_mode;
m_thd->variables.sql_mode = 0;
}
~Context_handler() {
m_thd->variables.sql_mode = m_sql_mode;
destroy(m_file);
}
private:
// Thread Handle.
THD *m_thd;
// Handler object of dummy table.
handler *m_file;
// sql_mode.
sql_mode_t m_sql_mode;
};
// Creating dummy TABLE and TABLE_SHARE objects to prepare Field objects from
// the items of first SELECT_LEX of the view query. We prepare these once and
// reuse them for all the fields.
TABLE table;
TABLE_SHARE share;
init_tmp_table_share(thd, &share, "", 0, "", "", nullptr);
table.s = &share;
handler *file = get_new_handler(&share, false, thd->mem_root,
ha_default_temp_handlerton(thd));
if (file == nullptr) {
my_error(ER_STORAGE_ENGINE_NOT_LOADED, MYF(0), view->db, view->table_name);
return true;
}
Context_handler ctx_handler(thd, file);
const dd::Properties &names_dict = view_obj->column_names();
/*
Iterate through all the items of first SELECT_LEX if view query is of
single query block. Otherwise iterate through all the type holders items
created for unioned column types of all the query blocks.
*/
List_iterator_fast<Item> it(*(thd->lex->unit->get_unit_column_types()));
List<Create_field> create_fields;
Item *item;
uint i = 0;
while ((item = it++) != nullptr) {
i++;
bool is_sp_func_item = false;
// Create temporary Field object from the item.
Field *tmp_field;
if (item->type() == Item::FUNC_ITEM) {
if (item->result_type() != STRING_RESULT) {
is_sp_func_item = ((static_cast<Item_func *>(item))->functype() ==
Item_func::FUNC_SP);
/*
INT Result values with MY_INT32_NUM_DECIMAL_DIGITS digits may or may
not fit into Field_long so make them Field_longlong.
*/
if (is_sp_func_item == false && item->result_type() == INT_RESULT &&
item->max_char_length() >= (MY_INT32_NUM_DECIMAL_DIGITS - 1)) {
tmp_field = new (thd->mem_root)
Field_longlong(item->max_char_length(), item->maybe_null,
item->item_name.ptr(), item->unsigned_flag);
if (tmp_field) tmp_field->init(&table);
} else
tmp_field = item->tmp_table_field(&table);
} else {
switch (item->data_type()) {
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
// Packlength is not set for blobs as per the length in
// tmp_table_field_from_field_type, so creating the blob field by
// passing set_packlenth value as "true" here.
tmp_field = new (thd->mem_root) Field_blob(
item->max_length, item->maybe_null, item->item_name.ptr(),
item->collation.collation, true);
if (tmp_field) tmp_field->init(&table);
break;
default:
tmp_field = item->tmp_table_field_from_field_type(&table, false);
}
}
} else {
Field *from_field, *default_field;
tmp_field = create_tmp_field(thd, &table, item, item->type(), nullptr,
&from_field, &default_field, false, false,
false, false);
}
if (!tmp_field) {
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return true;
}
// We have to take into account both the real table's fields and
// pseudo-fields used in trigger's body. These fields are used to copy
// defaults values later inside constructor of the class Create_field.
Field *orig_field = nullptr;
if (item->type() == Item::FIELD_ITEM ||
item->type() == Item::TRIGGER_FIELD_ITEM)
orig_field = ((Item_field *)item)->field;
// Create object of type Create_field from the tmp_field.
Create_field *cr_field =
new (thd->mem_root) Create_field(tmp_field, orig_field);
if (cr_field == nullptr) {
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return true;
}
if (!names_dict.empty()) // Explicit names were provided
{
std::string i_s = std::to_string(i);
String_type value;
char *name = nullptr;
if (!names_dict.get(String_type(i_s.begin(), i_s.end()), &value)) {
name = static_cast<char *>(
strmake_root(thd->mem_root, value.c_str(), value.length()));
}
if (!name) return true; /* purecov: inspected */
cr_field->field_name = name;
} else if (thd->lex->unit->is_union()) {
/*
If view query has any duplicate column names then generated unique name
is stored only with the first SELECT_LEX. So when Create_field instance
is created with type holder item, store name from first SELECT_LEX.
*/
cr_field->field_name =
thd->lex->select_lex->item_list[i - 1]->item_name.ptr();
}
cr_field->after = nullptr;
cr_field->offset = 0;
cr_field->pack_length_override = 0;
cr_field->maybe_null = !(tmp_field->flags & NOT_NULL_FLAG);
cr_field->is_zerofill = (tmp_field->flags & ZEROFILL_FLAG);
cr_field->is_unsigned = (tmp_field->flags & UNSIGNED_FLAG);
create_fields.push_back(cr_field);
}
// Fill view columns information from the Create_field objects.
return fill_dd_columns_from_create_fields(thd, view_obj, create_fields, file);
}
/**
Method to fill base table and view names used by view query in DD
View object.
@param view_obj DD view object.
@param view TABLE_LIST object of view.
@param query_tables View query tables list.
*/
static void fill_dd_view_tables(View *view_obj, const TABLE_LIST *view,
const TABLE_LIST *query_tables) {
DBUG_TRACE;
for (const TABLE_LIST *table = query_tables; table != nullptr;
table = table->next_global) {
/*
Skip if table is not directly referred by a view or if table is a
data-dictionary or temporary table.
*/
if ((table->referencing_view && table->referencing_view != view) ||
get_dictionary()->is_dd_table_name(table->get_db_name(),
table->get_table_name()) ||
is_temporary_table(const_cast<TABLE_LIST *>(table)))
continue;
LEX_CSTRING db_name;
LEX_CSTRING table_name;
if (table->schema_table_name) {
db_name = {table->db, table->db_length};
table_name = {table->schema_table_name, strlen(table->schema_table_name)};
} else if (table->is_view()) {
db_name = table->view_db;
table_name = table->view_name;
} else {
db_name = {table->db, table->db_length};
table_name = {table->table_name, table->table_name_length};
}
// Avoid duplicate entries.
{
bool duplicate_vw = false;
for (const View_table *vw : view_obj->tables()) {
if (!strcmp(vw->table_schema().c_str(), db_name.str) &&
!strcmp(vw->table_name().c_str(), table_name.str)) {
duplicate_vw = true;
break;
}
}
if (duplicate_vw) continue;
}
View_table *view_table_obj = view_obj->add_table();
// view table catalog
view_table_obj->set_table_catalog(Dictionary_impl::default_catalog_name());
// View table schema
view_table_obj->set_table_schema(String_type(db_name.str, db_name.length));
// View table name
view_table_obj->set_table_name(
String_type(table_name.str, table_name.length));
}
}
/**
Method to fill view routines from the set of routines used by view query.
@param view_obj DD view object.
@param routines_ctx Query_table_list object for the view which contains
set of routines used by view query.
*/
static void fill_dd_view_routines(View *view_obj,
Query_tables_list *routines_ctx) {
DBUG_TRACE;
// View stored functions. We need only directly used routines.
for (Sroutine_hash_entry *rt = routines_ctx->sroutines_list.first;
rt != nullptr && rt != *routines_ctx->sroutines_list_own_last;
rt = rt->next) {
View_routine *view_sf_obj = view_obj->add_routine();
/*
We should get only stored functions here, as procedures are not directly
used by views, and thus not stored as dependencies.
*/
DBUG_ASSERT(rt->type() == Sroutine_hash_entry::FUNCTION);
// view routine catalog
view_sf_obj->set_routine_catalog(Dictionary_impl::default_catalog_name());
// View routine schema
view_sf_obj->set_routine_schema(String_type(rt->db(), rt->m_db_length));
// View routine name
view_sf_obj->set_routine_name(String_type(rt->name(), rt->name_length()));
}
}
/**
Method to fill view information in the View object.
@param thd Thread handle.
@param view_obj DD view object.
@param view View description.
@retval false On success.
@retval true On failure.
*/
static bool fill_dd_view_definition(THD *thd, View *view_obj,
TABLE_LIST *view) {
// View name.
view_obj->set_name(view->table_name);
// Set definer.
view_obj->set_definer(view->definer.user.str, view->definer.host.str);
// View definition.
view_obj->set_definition(
String_type(view->select_stmt.str, view->select_stmt.length));
view_obj->set_definition_utf8(
String_type(view->view_body_utf8.str, view->view_body_utf8.length));
// Set updatable.
view_obj->set_updatable(view->updatable_view);
// Set check option.
view_obj->set_check_option(dd_get_new_view_check_type(view->with_check));
// Set algorithm.
view_obj->set_algorithm(dd_get_new_view_algorithm_type(
(enum enum_view_algorithm)view->algorithm));
// Set security type.
view_obj->set_security_type(dd_get_new_view_security_type(view->view_suid));
// Assign client collation ID. The create option specifies character
// set name, and we store the default collation id for this character set
// name, which implicitly identifies the character set.
const CHARSET_INFO *collation = nullptr;
if (resolve_charset(view->view_client_cs_name.str, system_charset_info,
&collation)) {
// resolve_charset will not cause an error to be reported if the
// character set was not found, so we must report error here.
my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), view->view_client_cs_name.str);
return true;
}
view_obj->set_client_collation_id(collation->number);
// Assign connection collation ID.
if (resolve_collation(view->view_connection_cl_name.str, system_charset_info,
&collation)) {
// resolve_charset will not cause an error to be reported if the
// collation was not found, so we must report error here.
my_error(ER_UNKNOWN_COLLATION, MYF(0), view->view_connection_cl_name.str);
return true;
}
view_obj->set_connection_collation_id(collation->number);
if (view->derived_column_names()) {
dd::Properties &names_dict = view_obj->column_names();
uint i = 0;
for (auto name : *view->derived_column_names()) {
std::string i_s = std::to_string(++i);
names_dict.set(String_type(i_s.begin(), i_s.end()),
String_type(name.str, name.length));
}
}
time_t tm = my_time(0);
get_date(view->timestamp.str,
GETDATE_DATE_TIME | GETDATE_GMT | GETDATE_FIXEDLENGTH, tm);
view->timestamp.length = PARSE_FILE_TIMESTAMPLENGTH;
dd::Properties *view_options = &view_obj->options();
view_options->set("timestamp",
String_type(view->timestamp.str, view->timestamp.length));
view_options->set("view_valid", true);
/*
Fill view columns information in View object.
During DD upgrade, view metadata is stored in 2 phases. In first phase,
view metadata is stored without column information. In second phase view
metadata stored with column information. Fill view columns only when view
metadata is stored with column information.
*/
if ((thd->lex->select_lex->item_list.elements > 0) &&
fill_dd_view_columns(thd, view_obj, view))
return true;
// Fill view tables information in View object.
fill_dd_view_tables(view_obj, view, thd->lex->query_tables);
/*
Fill view routines information in View object. It is important that
THD::lex points to the view's LEX at this point, so information about
directly used routines in it is correct.
*/
fill_dd_view_routines(view_obj, thd->lex);
return false;
}
bool update_view(THD *thd, dd::View *new_view, TABLE_LIST *view) {
// Clear the columns, tables and routines since it will be added later.
new_view->remove_children();
// Get statement start time.
// Set last altered time.
new_view->set_last_altered(
dd::my_time_t_to_ull_datetime(thd->query_start_in_secs()));
if (fill_dd_view_definition(thd, new_view, view)) return true;
// Update DD tables.
return thd->dd_client()->update(new_view);
}
bool create_view(THD *thd, const dd::Schema &schema, TABLE_LIST *view) {
// Create dd::View object.
bool hidden_system_view = false;
std::unique_ptr<dd::View> view_obj;
if (dd::get_dictionary()->is_system_view_name(view->db, view->table_name,
&hidden_system_view)) {
view_obj.reset(schema.create_system_view(thd));
// Mark the internal system views as hidden from users.
if (hidden_system_view)
view_obj->set_hidden(dd::Abstract_table::HT_HIDDEN_SYSTEM);
} else
view_obj.reset(schema.create_view(thd));
if (fill_dd_view_definition(thd, view_obj.get(), view)) return true;
// Store info in DD views table.
return thd->dd_client()->store(view_obj.get());
}
bool read_view(TABLE_LIST *view, const dd::View &view_obj, MEM_ROOT *mem_root) {
// Fill TABLE_LIST 'view' with view details.
String_type definer_user = view_obj.definer_user();
view->definer.user.length = definer_user.length();
view->definer.user.str = (char *)strmake_root(mem_root, definer_user.c_str(),
definer_user.length());
String_type definer_host = view_obj.definer_host();
view->definer.host.length = definer_host.length();
view->definer.host.str = (char *)strmake_root(mem_root, definer_host.c_str(),
definer_host.length());
// View definition body.
String_type vd_utf8 = view_obj.definition_utf8();
view->view_body_utf8.length = vd_utf8.length();
view->view_body_utf8.str =
(char *)strmake_root(mem_root, vd_utf8.c_str(), vd_utf8.length());
// Get updatable.
view->updatable_view = view_obj.is_updatable();
// Get check option.
view->with_check = dd_get_old_view_check_type(view_obj.check_option());
// Get algorithm.
view->algorithm = dd_get_old_view_algorithm_type(view_obj.algorithm());
// Get security type.
view->view_suid = dd_get_old_view_security_type(view_obj.security_type());
// Mark true, if we are reading a system view.
view->is_system_view = (view_obj.type() == dd::enum_table_type::SYSTEM_VIEW);
// Get definition.
String_type view_definition = view_obj.definition();
view->select_stmt.length = view_definition.length();
view->select_stmt.str = (char *)strmake_root(
mem_root, view_definition.c_str(), view_definition.length());
// Get view_client_cs_name. Note that this is the character set name.
CHARSET_INFO *collation =
dd_get_mysql_charset(view_obj.client_collation_id());
DBUG_ASSERT(collation);
view->view_client_cs_name.length = strlen(collation->csname);
view->view_client_cs_name.str = strdup_root(mem_root, collation->csname);
// Get view_connection_cl_name. Note that this is the collation name.
collation = dd_get_mysql_charset(view_obj.connection_collation_id());
DBUG_ASSERT(collation);
view->view_connection_cl_name.length = strlen(collation->name);
view->view_connection_cl_name.str = strdup_root(mem_root, collation->name);
if (!(view->definer.user.str && view->definer.host.str && // OOM
view->view_body_utf8.str && view->select_stmt.str &&
view->view_client_cs_name.str && view->view_connection_cl_name.str))
return true; /* purecov: inspected */
const dd::Properties &names_dict = view_obj.column_names();
if (!names_dict.empty()) // Explicit names were provided
{
auto *names_array = static_cast<Create_col_name_list *>(
mem_root->Alloc(sizeof(Create_col_name_list)));
if (!names_array) return true; /* purecov: inspected */
names_array->init(mem_root);
uint i = 0;
while (true) {
std::string i_s = std::to_string(++i);
String_type key(i_s.begin(), i_s.end());
if (!names_dict.exists(key)) break;
String_type value;
names_dict.get(key, &value);
char *name = static_cast<char *>(
strmake_root(mem_root, value.c_str(), value.length()));
if (!name || (names_array->push_back({name, value.length()})))
return true; /* purecov: inspected */
}
view->set_derived_column_names(names_array);
}
return false;
}
bool update_view_status(THD *thd, const char *schema_name,
const char *view_name, bool status,
bool commit_dd_changes) {
dd::cache::Dictionary_client *client = thd->dd_client();
dd::cache::Dictionary_client::Auto_releaser releaser(client);
dd::View *new_view = nullptr;
if (client->acquire_for_modification(schema_name, view_name, &new_view))
return true;
if (new_view == nullptr) return false;
// Update view error status.
dd::Properties *view_options = &new_view->options();
view_options->set("view_valid", status);
Disable_gtid_state_update_guard disabler(thd);
// Update DD tables.
if (client->update(new_view)) {
if (commit_dd_changes) {
trans_rollback_stmt(thd);
trans_rollback(thd);
}
return true;
}
return commit_dd_changes && (trans_commit_stmt(thd) || trans_commit(thd));
}
} // namespace dd