diff options
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r-- | sql/sql_lex.cc | 388 |
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) |