summaryrefslogtreecommitdiff
path: root/sql/sql_lex.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r--sql/sql_lex.cc189
1 files changed, 163 insertions, 26 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 7f1b362d91d..6b0a351f76e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -827,7 +827,7 @@ Yacc_state::~Yacc_state()
}
int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
- uint len, bool function)
+ uint len, bool function) const
{
const char *tok= m_tok_start;
@@ -847,7 +847,6 @@ int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
case CLOB_MARIADB_SYM: return CLOB_ORACLE_SYM;
case CONTINUE_MARIADB_SYM: return CONTINUE_ORACLE_SYM;
case DECLARE_MARIADB_SYM: return DECLARE_ORACLE_SYM;
- case DECODE_MARIADB_SYM: return DECODE_ORACLE_SYM;
case ELSEIF_MARIADB_SYM: return ELSEIF_ORACLE_SYM;
case ELSIF_MARIADB_SYM: return ELSIF_ORACLE_SYM;
case EXCEPTION_MARIADB_SYM: return EXCEPTION_ORACLE_SYM;
@@ -917,7 +916,7 @@ bool is_lex_native_function(const LEX_CSTRING *name)
bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
- if (find_native_function_builder(thd, name))
+ if (mariadb_schema.find_native_function_builder(thd, *name))
return true;
if (is_lex_native_function(name))
@@ -1542,7 +1541,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);
@@ -1986,7 +1996,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);
}
}
}
@@ -2008,7 +2023,59 @@ 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;
+ if (!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;
@@ -2022,10 +2089,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
@@ -2075,9 +2173,9 @@ int Lex_input_stream::scan_ident_start(THD *thd, Lex_ident_cli_st *str)
{
is_8bit= get_7bit_or_8bit_ident(thd, &c);
}
+
if (c == '.' && ident_map[(uchar) yyPeek()])
next_state= MY_LEX_IDENT_SEP;// Next is '.'
-
uint length= yyLength();
yyUnget(); // ptr points now after last token char
str->set_ident(m_tok_start, length, is_8bit);
@@ -8035,30 +8133,49 @@ bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
}
-Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b, Item *c)
+const Schema *
+LEX::find_func_schema_by_name_or_error(const Lex_ident_sys &schema,
+ const Lex_ident_sys &func)
{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_substr_oracle(thd, a, b, c) :
- new (thd->mem_root) Item_func_substr(thd, a, b, c);
+ 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, Item *a, Item *b)
+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)
{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_substr_oracle(thd, a, b) :
- new (thd->mem_root) Item_func_substr(thd, a, b);
+ 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_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)
{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_replace_oracle(thd, org, find, replace) :
- new (thd->mem_root) Item_func_replace(thd, org, find, 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;
}
@@ -8140,11 +8257,18 @@ Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const
}
-Item *Lex_trim_st::make_item_func_trim(THD *thd) const
+Item *Lex_trim_st::make_item_func_trim(THD *thd,
+ const Lex_ident_cli_st &schema_name_cli,
+ const Lex_ident_cli_st &func_name_cli)
+ const
{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- make_item_func_trim_oracle(thd) :
- make_item_func_trim_std(thd);
+ 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= LEX::find_func_schema_by_name_or_error(schema_name,
+ func_name);
+ return schema ? schema->make_item_func_trim(thd, *this) : NULL;
}
@@ -8176,6 +8300,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);