summaryrefslogtreecommitdiffstats
path: root/sql/sql_lex.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r--sql/sql_lex.cc388
1 files changed, 359 insertions, 29 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d9706020..e6db1456 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1459,7 +1459,7 @@ bool is_lex_native_function(const LEX_CSTRING *name)
bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
- if (native_functions_hash.find(thd, *name))
+ if (mariadb_schema.find_native_function_builder(thd, *name))
return true;
if (is_lex_native_function(name))
@@ -2138,7 +2138,18 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
if (lex->parsing_options.lookup_keywords_after_qualifier)
next_state= MY_LEX_IDENT_OR_KEYWORD;
else
- next_state= MY_LEX_IDENT_START; // Next is ident (not keyword)
+ {
+ /*
+ Next is:
+ - A qualified func with a special syntax:
+ mariadb_schema.REPLACE('a','b','c')
+ mariadb_schema.SUSTRING('a',1,2)
+ mariadb_schema.TRIM('a')
+ - Or an identifier otherwise. No keyword lookup is done,
+ all keywords are treated as identifiers.
+ */
+ next_state= MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC;
+ }
if (!ident_map[(uchar) yyPeek()]) // Probably ` or "
next_state= MY_LEX_START;
return((int) c);
@@ -2582,7 +2593,12 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
We should now be able to handle:
[(global | local | session) .]variable_name
*/
- return scan_ident_sysvar(thd, &yylval->ident_cli);
+ return scan_ident_common(thd, &yylval->ident_cli,
+ GENERAL_KEYWORD_OR_FUNC_LPAREN);
+
+ case MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC:
+ return scan_ident_common(thd, &yylval->ident_cli,
+ QUALIFIED_SPECIAL_FUNC_LPAREN);
}
}
}
@@ -2604,7 +2620,64 @@ bool Lex_input_stream::get_7bit_or_8bit_ident(THD *thd, uchar *last_char)
}
-int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
+/*
+ Resolve special SQL functions that have a qualified syntax in sql_yacc.yy.
+ These functions are not listed in the native function registry
+ because of a special syntax, or a reserved keyword:
+
+ mariadb_schema.SUBSTRING('a' FROM 1 FOR 2) -- Special syntax
+ mariadb_schema.TRIM(BOTH ' ' FROM 'a') -- Special syntax
+ mariadb_schema.REPLACE('a','b','c') -- Verb keyword
+*/
+
+int Lex_input_stream::find_keyword_qualified_special_func(Lex_ident_cli_st *str,
+ uint length) const
+{
+ /*
+ There are many other special functions, see the following grammar rules:
+ function_call_keyword
+ function_call_nonkeyword
+ Here we resolve only those that have a qualified syntax to handle
+ different behavior in different @@sql_mode settings.
+
+ Other special functions do not work in qualified context:
+ SELECT mariadb_schema.year(now()); -- Function year is not defined
+ SELECT mariadb_schema.now(); -- Function now is not defined
+
+ We don't resolve TRIM_ORACLE here, because it does not have
+ a qualified syntax yet. Search for "trim_operands" in sql_yacc.yy
+ to find more comments.
+ */
+ static LEX_CSTRING funcs[]=
+ {
+ {STRING_WITH_LEN("SUBSTRING")},
+ {STRING_WITH_LEN("SUBSTR")},
+ {STRING_WITH_LEN("TRIM")},
+ {STRING_WITH_LEN("REPLACE")}
+ };
+
+ int tokval= find_keyword(str, length, true);
+ if (!tokval)
+ return 0;
+ for (size_t i= 0; i < array_elements(funcs); i++)
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ /*
+ Check length equality to avoid non-ASCII variants
+ compared as equal to ASCII variants.
+ */
+ if (length == funcs[i].length &&
+ !cs->coll->strnncollsp(cs,
+ (const uchar *) m_tok_start, length,
+ (const uchar *) funcs[i].str, funcs[i].length))
+ return tokval;
+ }
+ return 0;
+}
+
+
+int Lex_input_stream::scan_ident_common(THD *thd, Lex_ident_cli_st *str,
+ Ident_mode mode)
{
uchar last_char;
uint length;
@@ -2618,10 +2691,41 @@ int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
next_state= MY_LEX_IDENT_SEP;
if (!(length= yyLength()))
return ABORT_SYM; // Names must be nonempty.
- if ((tokval= find_keyword(str, length, 0)))
- {
- yyUnget(); // Put back 'c'
- return tokval; // Was keyword
+
+ switch (mode) {
+ case GENERAL_KEYWORD_OR_FUNC_LPAREN:
+ /*
+ We can come here inside a system variable after "@@",
+ e.g. @@global.character_set_client.
+ We resolve all general purpose keywords here.
+
+ We can come here when LEX::parsing_options.lookup_keywords_after_qualifier
+ is true, i.e. within the "field_spec" Bison rule.
+ We need to resolve functions that have special rules inside sql_yacc.yy,
+ such as SUBSTR, REPLACE, TRIM, to make this work:
+ c2 varchar(4) GENERATED ALWAYS AS (mariadb_schema.substr(c1,1,4))
+ */
+ if ((tokval= find_keyword(str, length, last_char == '(')))
+ {
+ yyUnget(); // Put back 'c'
+ return tokval; // Was keyword
+ }
+ break;
+ case QUALIFIED_SPECIAL_FUNC_LPAREN:
+ /*
+ We come here after '.' in various contexts:
+ SELECT @@global.character_set_client;
+ SELECT t1.a FROM t1;
+ SELECT test.f1() FROM t1;
+ SELECT mariadb_schema.trim('a');
+ */
+ if (last_char == '(' &&
+ (tokval= find_keyword_qualified_special_func(str, length)))
+ {
+ yyUnget(); // Put back 'c'
+ return tokval; // Was keyword
+ }
+ break;
}
yyUnget(); // ptr points now after last token char
@@ -2960,6 +3064,7 @@ void st_select_lex::init_query()
tvc= 0;
versioned_tables= 0;
pushdown_select= 0;
+ orig_names_of_item_list_elems= 0;
}
void st_select_lex::init_select()
@@ -3011,6 +3116,7 @@ void st_select_lex::init_select()
versioned_tables= 0;
is_tvc_wrapper= false;
nest_flags= 0;
+ orig_names_of_item_list_elems= 0;
}
/*
@@ -3526,8 +3632,9 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
uint n_elems= get_cardinality_of_ref_ptrs_slice(order_group_num) * 5;
if (!ref_pointer_array.is_null())
return false;
- Item **array= static_cast<Item**>(thd->stmt_arena->alloc(sizeof(Item*) *
- n_elems));
+
+ Item **array= static_cast<Item**>(
+ thd->active_stmt_arena_to_use()->alloc(sizeof(Item*) * n_elems));
if (likely(array != NULL))
ref_pointer_array= Ref_ptr_array(array, n_elems);
return array == NULL;
@@ -4671,18 +4778,27 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
Item **having_conds)
{
+ Query_arena *active_arena= thd->active_stmt_arena_to_use();
+
DBUG_ENTER("st_select_lex::fix_prepare_information");
- if (!thd->stmt_arena->is_conventional() &&
+
+ if (!active_arena->is_conventional() &&
!(changed_elements & TOUCHED_SEL_COND))
{
Query_arena_stmt on_stmt_arena(thd);
changed_elements|= TOUCHED_SEL_COND;
+ /*
+ TODO: return after MDEV-33218 fix
+ DBUG_ASSERT(
+ active_arena->is_stmt_prepare_or_first_stmt_execute() ||
+ active_arena->state == Query_arena::STMT_SP_QUERY_ARGUMENTS);
+ */
if (group_list.first)
{
if (!group_list_ptrs)
{
- void *mem= thd->stmt_arena->alloc(sizeof(Group_list_ptrs));
- group_list_ptrs= new (mem) Group_list_ptrs(thd->stmt_arena->mem_root);
+ void *mem= active_arena->alloc(sizeof(Group_list_ptrs));
+ group_list_ptrs= new (mem) Group_list_ptrs(active_arena->mem_root);
}
group_list_ptrs->reserve(group_list.elements);
for (ORDER *order= group_list.first; order; order= order->next)
@@ -7342,7 +7458,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
name->m_name);
else
sp->init_sp_name(name);
- sp->make_qname(sp->get_main_mem_root(), &sp->m_qname);
+ if (!(sp->m_qname= sp->make_qname(sp->get_main_mem_root(), true)).str)
+ return NULL;
}
sphead= sp;
}
@@ -9229,7 +9346,7 @@ bool LEX::call_statement_start(THD *thd,
const Lex_ident_sys_st *proc)
{
Database_qualified_name q_db_pkg(db, pkg);
- Database_qualified_name q_pkg_proc(pkg, proc);
+ Identifier_chain2 q_pkg_proc(*pkg, *proc);
sp_name *spname;
sql_command= SQLCOM_CALL;
@@ -9247,7 +9364,7 @@ bool LEX::call_statement_start(THD *thd,
// Concat `pkg` and `name` to `pkg.name`
LEX_CSTRING pkg_dot_proc;
- if (q_pkg_proc.make_qname(thd->mem_root, &pkg_dot_proc) ||
+ if (!(pkg_dot_proc= q_pkg_proc.make_qname(thd->mem_root, false)).str ||
check_ident_length(&pkg_dot_proc) ||
!(spname= new (thd->mem_root) sp_name(db, &pkg_dot_proc, true)))
return true;
@@ -9313,7 +9430,8 @@ sp_package *LEX::create_package_start(THD *thd,
return NULL;
pkg->reset_thd_mem_root(thd);
pkg->init(this);
- pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname);
+ if (!(pkg->m_qname= pkg->make_qname(pkg->get_main_mem_root(), true)).str)
+ return NULL;
sphead= pkg;
return pkg;
}
@@ -9376,6 +9494,136 @@ Item *LEX::make_item_func_sysdate(THD *thd, uint fsp)
}
+const Schema *
+LEX::find_func_schema_by_name_or_error(const Lex_ident_sys &schema,
+ const Lex_ident_sys &func)
+{
+ Schema *res= Schema::find_by_name(schema);
+ if (res)
+ return res;
+ Database_qualified_name qname(schema, func);
+ my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), ErrConvDQName(&qname).ptr());
+ return NULL;
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ const Lex_substring_spec_st &spec)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_substr(thd, spec) : NULL;
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ Schema *schema;
+ if (item_list &&
+ (item_list->elements == 2 || item_list->elements == 3) &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ Lex_substring_spec_st spec=
+ Lex_substring_spec_st::init(args.arguments()[0],
+ args.arguments()[1],
+ item_list->elements == 3 ?
+ args.arguments()[2] : NULL);
+ return schema->make_item_func_substr(thd, spec);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
+Item *LEX::make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ Item *org,
+ Item *find,
+ Item *replace)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_replace(thd, org, find, replace) :
+ NULL;
+}
+
+
+Item *LEX::make_item_func_replace(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema;
+ if (item_list && item_list->elements == 3 &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ return schema->make_item_func_replace(thd, args.arguments()[0],
+ args.arguments()[1],
+ args.arguments()[2]);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
+Item *LEX::make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ const Lex_trim_st &spec)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema= find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_trim(thd, spec) : NULL;
+}
+
+
+Item *LEX::make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli,
+ List<Item> *item_list)
+{
+ Lex_ident_sys schema_name(thd, &schema_name_cli);
+ Lex_ident_sys func_name(thd, &func_name_cli);
+ if (schema_name.is_null() || func_name.is_null())
+ return NULL; // EOM
+ const Schema *schema;
+ if (item_list && item_list->elements == 1 &&
+ (schema= Schema::find_by_name(schema_name)))
+ {
+ Item_args args(thd, *item_list);
+ Lex_trim spec(TRIM_BOTH, args.arguments()[0]);
+ return schema->make_item_func_trim(thd, spec);
+ }
+ return make_item_func_call_generic(thd, schema_name, func_name, item_list);
+}
+
+
bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
const LEX_CSTRING field_name)
{
@@ -9454,16 +9702,10 @@ Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const
}
-Item *Lex_trim_st::make_item_func_trim(THD *thd) const
-{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- make_item_func_trim_oracle(thd) :
- make_item_func_trim_std(thd);
-}
-
-
-Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
- Lex_ident_cli_st *cname, List<Item> *args)
+Item *LEX::make_item_func_call_generic(THD *thd,
+ const Lex_ident_cli_st *cdb,
+ const Lex_ident_cli_st *cname,
+ List<Item> *args)
{
Lex_ident_sys db(thd, cdb), name(thd, cname);
if (db.is_null() || name.is_null())
@@ -9490,6 +9732,19 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
if (check_routine_name(&name))
return NULL;
+ return make_item_func_call_generic(thd, db, name, args);
+}
+
+
+Item *LEX::make_item_func_call_generic(THD *thd,
+ const Lex_ident_sys &db,
+ const Lex_ident_sys &name,
+ List<Item> *args)
+{
+ const Schema *schema= Schema::find_by_name(db);
+ if (schema)
+ return schema->make_item_func_call_native(thd, name, args);
+
Create_qfunc *builder= find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
return builder->create_with_db(thd, &db, &name, true, args);
@@ -9510,7 +9765,7 @@ Item *LEX::make_item_func_call_generic(THD *thd,
static Lex_cstring dot(".", 1);
Lex_ident_sys db(thd, cdb), pkg(thd, cpkg), func(thd, cfunc);
Database_qualified_name q_db_pkg(db, pkg);
- Database_qualified_name q_pkg_func(pkg, func);
+ Identifier_chain2 q_pkg_func(pkg, func);
sp_name *qname;
if (db.is_null() || pkg.is_null() || func.is_null())
@@ -9527,7 +9782,7 @@ Item *LEX::make_item_func_call_generic(THD *thd,
// Concat `pkg` and `name` to `pkg.name`
LEX_CSTRING pkg_dot_func;
- if (q_pkg_func.make_qname(thd->mem_root, &pkg_dot_func) ||
+ if (!(pkg_dot_func= q_pkg_func.make_qname(thd->mem_root, false)).str ||
check_ident_length(&pkg_dot_func) ||
!(qname= new (thd->mem_root) sp_name(&db, &pkg_dot_func, true)))
return NULL;
@@ -9936,8 +10191,17 @@ bool Lex_order_limit_lock::set_to(SELECT_LEX *sel)
"CUBE/ROLLUP", "ORDER BY");
return TRUE;
}
+ for (ORDER *order= order_list->first; order; order= order->next)
+ (*order->item)->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
sel->order_list= *(order_list);
}
+ if (limit.select_limit)
+ limit.select_limit->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
+ if (limit.offset_limit)
+ limit.offset_limit->walk(&Item::change_context_processor, FALSE,
+ &sel->context);
sel->is_set_query_expr_tail= true;
return FALSE;
}
@@ -11191,6 +11455,72 @@ exit:
}
+/**
+ @brief
+ Save the original names of items from the item list.
+
+ @retval
+ true - if an error occurs
+ false - otherwise
+*/
+
+bool st_select_lex::save_item_list_names(THD *thd)
+{
+ if (orig_names_of_item_list_elems)
+ return false;
+
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ if (unlikely(!(orig_names_of_item_list_elems= new(thd->mem_root)
+ List<Lex_ident_sys>)))
+ return true;
+
+ List_iterator_fast<Item> li(item_list);
+ Item *item;
+
+ while ((item= li++))
+ {
+ Lex_ident_sys *name= new (thd->mem_root) Lex_ident_sys(thd, &item->name);
+ if (unlikely(!name ||
+ orig_names_of_item_list_elems->push_back(name, thd->mem_root)))
+ {
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ orig_names_of_item_list_elems= 0;
+ return true;
+ }
+ }
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ return false;
+}
+
+
+/**
+ @brief
+ Restore the name of each item in the item_list of this st_select_lex
+ from orig_names_of_item_list_elems.
+*/
+
+void st_select_lex::restore_item_list_names()
+{
+ if (!orig_names_of_item_list_elems)
+ return;
+
+ DBUG_ASSERT(item_list.elements == orig_names_of_item_list_elems->elements);
+
+ List_iterator_fast<Lex_ident_sys> it(*orig_names_of_item_list_elems);
+ Lex_ident_sys *new_name;
+ List_iterator_fast<Item> li(item_list);
+ Item *item;
+
+ while ((item= li++) && (new_name= it++))
+ lex_string_set( &item->name, new_name->str);
+}
+
bool LEX::stmt_install_plugin(const DDL_options_st &opt,
const Lex_ident_sys_st &name,
const LEX_CSTRING &soname)