用于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.

458 lines
16 KiB

5 months ago
/* Copyright (c) 2013, 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/parse_tree_items.h"
#include "my_dbug.h"
#include "my_sqlcommand.h"
#include "mysql/udf_registration_types.h"
#include "mysql_com.h"
#include "sql/auth/auth_acls.h"
#include "sql/item_cmpfunc.h" // Item_func_eq
#include "sql/mysqld.h" // using_udf_functions
#include "sql/parse_tree_nodes.h"
#include "sql/protocol.h"
#include "sql/sp.h"
#include "sql/sp_pcontext.h" // sp_pcontext
#include "sql/sql_udf.h"
#include "sql/table.h"
#include "sql/trigger_def.h"
/**
Apply a truth test to given expression. Either the expression can implement
it itself, or we create an Item node to implement it by wrapping the
expression. Expression is possibly an incomplete predicate.
@param pc current parse context
@param expr expression
@param truth_test test to apply
@returns the resulting expression, or NULL if error
*/
static Item *change_truth_value_of_condition(Parse_context *pc, Item *expr,
Item::Bool_test truth_test) {
switch (truth_test) {
case Item::BOOL_NEGATED:
case Item::BOOL_IS_TRUE:
case Item::BOOL_IS_FALSE:
case Item::BOOL_NOT_TRUE:
case Item::BOOL_NOT_FALSE:
break;
default:
DBUG_ASSERT(false);
}
// Ensure that all incomplete predicates are made complete:
if (!expr->is_bool_func()) {
expr = make_condition(pc, expr);
if (expr == nullptr) return nullptr;
}
Item *changed = expr->truth_transformer(pc->thd, truth_test);
if (changed != nullptr) return changed;
if (truth_test == Item::BOOL_NEGATED)
return new (pc->mem_root) Item_func_not(expr);
return new (pc->mem_root) Item_func_truth(expr, truth_test);
}
/**
Helper to resolve the SQL:2003 Syntax exception 1) in @<in predicate@>.
See SQL:2003, Part 2, section 8.4 @<in predicate@>, Note 184, page 383.
This function returns the proper item for the SQL expression
<code>left [NOT] IN ( expr )</code>
@param pc the current parse context
@param left the in predicand
@param equal true for IN predicates, false for NOT IN predicates
@param expr first and only expression of the in value list
@return an expression representing the IN predicate.
*/
static Item *handle_sql2003_note184_exception(Parse_context *pc, Item *left,
bool equal, Item *expr) {
/*
Relevant references for this issue:
- SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
- SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- SQL:2003 Feature F561, "Full value expressions".
The exception in SQL:2003 Note 184 means:
Item_singlerow_subselect, which corresponds to a <scalar subquery>,
should be re-interpreted as an Item_in_subselect, which corresponds
to a <table subquery> when used inside an <in predicate>.
Our reading of Note 184 is reccursive, so that all:
- IN (( <subquery> ))
- IN ((( <subquery> )))
- IN '('^N <subquery> ')'^N
- etc
should be interpreted as a <table subquery>, no matter how deep in the
expression the <subquery> is.
*/
Item *result;
DBUG_TRACE;
if (expr->type() == Item::SUBSELECT_ITEM) {
Item_subselect *expr2 = (Item_subselect *)expr;
if (expr2->substype() == Item_subselect::SINGLEROW_SUBS) {
Item_singlerow_subselect *expr3 = (Item_singlerow_subselect *)expr2;
SELECT_LEX *subselect;
/*
Implement the mandated change, by altering the semantic tree:
left IN Item_singlerow_subselect(subselect)
is modified to
left IN (subselect)
which is represented as
Item_in_subselect(left, subselect)
*/
subselect = expr3->invalidate_and_restore_select_lex();
result = new (pc->mem_root) Item_in_subselect(left, subselect);
if (!equal)
result =
change_truth_value_of_condition(pc, result, Item::BOOL_NEGATED);
return result;
}
}
if (equal)
result = new (pc->mem_root) Item_func_eq(left, expr);
else
result = new (pc->mem_root) Item_func_ne(left, expr);
return result;
}
bool PTI_table_wild::itemize(Parse_context *pc, Item **item) {
if (super::itemize(pc, item)) return true;
schema = pc->thd->get_protocol()->has_client_capability(CLIENT_NO_SCHEMA)
? NullS
: schema;
*item = new (pc->mem_root) Item_field(POS(), schema, table, "*");
if (*item == NULL || (*item)->itemize(pc, item)) return true;
pc->select->with_wild++;
return false;
}
bool PTI_comp_op::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || left->itemize(pc, &left) ||
right->itemize(pc, &right))
return true;
*res = (*boolfunc2creator)(0)->create(left, right);
return *res == NULL;
}
bool PTI_comp_op_all::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || left->itemize(pc, &left) ||
subselect->contextualize(pc))
return true;
*res = all_any_subquery_creator(left, comp_op, is_all, subselect->value());
return *res == nullptr;
}
bool PTI_function_call_nonkeyword_sysdate::itemize(Parse_context *pc,
Item **res) {
if (super::itemize(pc, res)) return true;
/*
Unlike other time-related functions, SYSDATE() is
replication-unsafe because it is not affected by the
TIMESTAMP variable. It is unsafe even if
sysdate_is_now=1, because the slave may have
sysdate_is_now=0.
*/
THD *thd = pc->thd;
LEX *lex = thd->lex;
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
if (global_system_variables.sysdate_is_now == 0)
*res = new (pc->mem_root) Item_func_sysdate_local(dec);
else
*res = new (pc->mem_root) Item_func_now_local(dec);
if (*res == NULL) return true;
lex->safe_to_cache_query = 0;
return false;
}
bool PTI_udf_expr::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || expr->itemize(pc, &expr)) return true;
/*
Use Item::name as a storage for the attribute value of user
defined function argument. It is safe to use Item::name
because the syntax will not allow having an explicit name here.
See WL#1017 re. udf attributes.
*/
if (select_alias.str) {
expr->item_name.copy(select_alias.str, select_alias.length,
system_charset_info, false);
}
/*
A field has to have its proper name in order for name
resolution to work, something we are only guaranteed if we
parse it out. If we hijack the input stream with
[@1.cpp.start ... @1.cpp.end) we may get quoted or escaped names.
*/
else if (expr->type() != Item::FIELD_ITEM &&
expr->type() != Item::REF_ITEM /* For HAVING */)
expr->item_name.copy(expr_loc.start, expr_loc.length(), pc->thd->charset());
*res = expr;
return false;
}
bool PTI_function_call_generic_ident_sys::itemize(Parse_context *pc,
Item **res) {
if (super::itemize(pc, res)) return true;
THD *thd = pc->thd;
udf = 0;
if (using_udf_functions && (udf = find_udf(ident.str, ident.length)) &&
udf->type == UDFTYPE_AGGREGATE) {
pc->select->in_sum_expr++;
}
if (sp_check_name(&ident)) return true;
/*
Implementation note:
names are resolved with the following order:
- MySQL native functions,
- User Defined Functions,
- Stored Functions (assuming the current <use> database)
This will be revised with WL#2128 (SQL PATH)
*/
Create_func *builder = find_native_function_builder(ident);
if (builder)
*res = builder->create_func(thd, ident, opt_udf_expr_list);
else {
if (udf) {
if (udf->type == UDFTYPE_AGGREGATE) {
pc->select->in_sum_expr--;
}
*res = Create_udf_func::s_singleton.create(thd, udf, opt_udf_expr_list);
} else {
builder = find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
*res = builder->create_func(thd, ident, opt_udf_expr_list);
}
}
return *res == NULL || (*res)->itemize(pc, res);
}
bool PTI_function_call_generic_2d::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res)) return true;
/*
The following in practice calls:
<code>Create_sp_func::create()</code>
and builds a stored function.
However, it's important to maintain the interface between the
parser and the implementation in item_create.cc clean,
since this will change with WL#2128 (SQL PATH):
- INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native
function version(),
- MySQL.version() is the SQL 2003 syntax for the native function
version() (a vendor can specify any schema).
*/
if (!db.str ||
(check_and_convert_db_name(&db, false) != Ident_name_check::OK))
return true;
if (sp_check_name(&func)) return true;
Create_qfunc *builder = find_qualified_function_builder(pc->thd);
DBUG_ASSERT(builder);
*res = builder->create(pc->thd, db, func, true, opt_expr_list);
return *res == NULL || (*res)->itemize(pc, res);
}
bool PTI_text_literal_nchar_string::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res)) return true;
uint repertoire = is_7bit ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
init(literal.str, literal.length, national_charset_info, DERIVATION_COERCIBLE,
repertoire);
return false;
}
bool PTI_singlerow_subselect::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || subselect->contextualize(pc)) return true;
*res = new (pc->mem_root) Item_singlerow_subselect(subselect->value());
return *res == NULL;
}
bool PTI_exists_subselect::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || subselect->contextualize(pc)) return true;
*res = new (pc->mem_root) Item_exists_subselect(subselect->value());
return *res == NULL;
}
bool PTI_handle_sql2003_note184_exception::itemize(Parse_context *pc,
Item **res) {
if (super::itemize(pc, res) || left->itemize(pc, &left) ||
right->itemize(pc, &right))
return true;
*res = handle_sql2003_note184_exception(pc, left, is_negation, right);
return *res == NULL;
}
bool PTI_expr_with_alias::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || expr->itemize(pc, &expr)) return true;
if (alias.str) {
if (pc->thd->lex->sql_command == SQLCOM_CREATE_VIEW &&
check_column_name(alias.str)) {
my_error(ER_WRONG_COLUMN_NAME, MYF(0), alias.str);
return true;
}
expr->item_name.copy(alias.str, alias.length, system_charset_info, false);
} else if (!expr->item_name.is_set()) {
expr->item_name.copy(expr_loc.start, (uint)(expr_loc.end - expr_loc.start),
pc->thd->charset());
}
*res = expr;
return false;
}
bool PTI_simple_ident_ident::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res)) return true;
THD *thd = pc->thd;
LEX *lex = thd->lex;
sp_pcontext *pctx = lex->get_sp_current_parsing_ctx();
sp_variable *spv;
if (pctx && (spv = pctx->find_variable(ident.str, ident.length, false))) {
sp_head *sp = lex->sphead;
DBUG_ASSERT(sp);
/* We're compiling a stored procedure and found a variable */
if (!lex->parsing_options.allows_variable) {
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
return true;
}
*res = create_item_for_sp_var(
thd, ident, spv, sp->m_parser_data.get_current_stmt_start_ptr(),
raw.start, raw.end);
lex->safe_to_cache_query = false;
} else {
if ((pc->select->parsing_place != CTX_HAVING) ||
(pc->select->get_in_sum_expr() > 0)) {
*res = new (pc->mem_root) Item_field(POS(), NullS, NullS, ident.str);
} else {
*res = new (pc->mem_root) Item_ref(POS(), NullS, NullS, ident.str);
}
if (*res == NULL || (*res)->itemize(pc, res)) return true;
}
return *res == NULL;
}
bool PTI_simple_ident_q_3d::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res)) return true;
THD *thd = pc->thd;
const char *schema =
thd->get_protocol()->has_client_capability(CLIENT_NO_SCHEMA) ? nullptr
: db;
if (pc->select->no_table_names_allowed) {
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), table, thd->where);
}
if ((pc->select->parsing_place != CTX_HAVING) ||
(pc->select->get_in_sum_expr() > 0)) {
*res = new (pc->mem_root) Item_field(POS(), schema, table, field);
} else {
*res = new (pc->mem_root) Item_ref(POS(), schema, table, field);
}
return *res == nullptr || (*res)->itemize(pc, res);
}
bool PTI_simple_ident_q_2d::itemize(Parse_context *pc, Item **res) {
THD *thd = pc->thd;
LEX *lex = thd->lex;
sp_head *sp = lex->sphead;
/*
FIXME This will work ok in simple_ident_nospvar case because
we can't meet simple_ident_nospvar in trigger now. But it
should be changed in future.
*/
if (sp && sp->m_type == enum_sp_type::TRIGGER &&
(!my_strcasecmp(system_charset_info, table, "NEW") ||
!my_strcasecmp(system_charset_info, table, "OLD"))) {
if (Parse_tree_item::itemize(pc, res)) return true;
bool new_row = (table[0] == 'N' || table[0] == 'n');
if (sp->m_trg_chistics.event == TRG_EVENT_INSERT && !new_row) {
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT");
return true;
}
if (sp->m_trg_chistics.event == TRG_EVENT_DELETE && new_row) {
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
return true;
}
DBUG_ASSERT(!new_row || (sp->m_trg_chistics.event == TRG_EVENT_INSERT ||
sp->m_trg_chistics.event == TRG_EVENT_UPDATE));
const bool read_only =
!(new_row && sp->m_trg_chistics.action_time == TRG_ACTION_BEFORE);
Item_trigger_field *trg_fld = new (pc->mem_root)
Item_trigger_field(POS(), new_row ? TRG_NEW_ROW : TRG_OLD_ROW, field,
SELECT_ACL, read_only);
if (trg_fld == NULL || trg_fld->itemize(pc, (Item **)&trg_fld)) return true;
DBUG_ASSERT(trg_fld->type() == TRIGGER_FIELD_ITEM);
/*
Let us add this item to list of all Item_trigger_field objects
in trigger.
*/
lex->sphead->m_cur_instr_trig_field_items.link_in_list(
trg_fld, &trg_fld->next_trg_field);
*res = trg_fld;
} else {
if (super::itemize(pc, res)) return true;
}
return false;
}
bool PTI_truth_transform::itemize(Parse_context *pc, Item **res) {
if (super::itemize(pc, res) || expr->itemize(pc, &expr)) return true;
*res = change_truth_value_of_condition(pc, expr, truth_test);
return *res == NULL;
}