summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Alff <marc.alff@sun.com>2008-07-07 10:00:08 -0600
committerMarc Alff <marc.alff@sun.com>2008-07-07 10:00:08 -0600
commitc7724872d85cebf07df3380757cf7259f9116682 (patch)
tree373c5fa1c21bed1d12376b834674b7bb3f0463aa
parent5647bce3668b46c5c74349520cefb2a791125e95 (diff)
downloadmariadb-git-c7724872d85cebf07df3380757cf7259f9116682.tar.gz
Bug#26030 (Parsing fails for stored routine w/multi-statement execution
enabled) Before this fix, the lexer and parser would treat the ';' character as a different token (either ';' or END_OF_INPUT), based on convoluted logic, which failed in simple cases where a stored procedure is implemented as a single statement, and used in a multi query. With this fix: - the character ';' is always parsed as a ';' token in the lexer, - parsing multi queries is implemented in the parser, in the 'query:' rules, - the value of thd->client_capabilities, which is the capabilities negotiated between the client and the server during bootstrap, is immutable and not arbitrarily modified during parsing (which was the root cause of the bug)
-rw-r--r--mysql-test/r/comments.result2
-rw-r--r--mysql-test/r/parser.result68
-rw-r--r--mysql-test/r/ps.result4
-rw-r--r--mysql-test/t/parser.test59
-rw-r--r--sql/sp_head.h1
-rw-r--r--sql/sql_lex.cc19
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_yacc.yy93
8 files changed, 186 insertions, 65 deletions
diff --git a/mysql-test/r/comments.result b/mysql-test/r/comments.result
index 1040c3fc8e9..b9d0028dc7c 100644
--- a/mysql-test/r/comments.result
+++ b/mysql-test/r/comments.result
@@ -35,7 +35,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp
prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*! AND 2=2;";
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*! AND 2=2;*";
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ';*' at line 1
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*' at line 1
prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*!98765' AND b = 'bar';";
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '/*!98765' AND b = 'bar'' at line 1
prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*!98765' AND b = 'bar';*";
diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result
new file mode 100644
index 00000000000..18391bd2a45
--- /dev/null
+++ b/mysql-test/r/parser.result
@@ -0,0 +1,68 @@
+DROP PROCEDURE IF EXISTS p26030;
+select "non terminated"$$
+non terminated
+non terminated
+select "terminated";$$
+terminated
+terminated
+select "non terminated, space" $$
+non terminated, space
+non terminated, space
+select "terminated, space"; $$
+terminated, space
+terminated, space
+select "non terminated, comment" /* comment */$$
+non terminated, comment
+non terminated, comment
+select "terminated, comment"; /* comment */$$
+terminated, comment
+terminated, comment
+select "stmt 1";select "stmt 2 non terminated"$$
+stmt 1
+stmt 1
+stmt 2 non terminated
+stmt 2 non terminated
+select "stmt 1";select "stmt 2 terminated";$$
+stmt 1
+stmt 1
+stmt 2 terminated
+stmt 2 terminated
+select "stmt 1";select "stmt 2 non terminated, space" $$
+stmt 1
+stmt 1
+stmt 2 non terminated, space
+stmt 2 non terminated, space
+select "stmt 1";select "stmt 2 terminated, space"; $$
+stmt 1
+stmt 1
+stmt 2 terminated, space
+stmt 2 terminated, space
+select "stmt 1";select "stmt 2 non terminated, comment" /* comment */$$
+stmt 1
+stmt 1
+stmt 2 non terminated, comment
+stmt 2 non terminated, comment
+select "stmt 1";select "stmt 2 terminated, comment"; /* comment */$$
+stmt 1
+stmt 1
+stmt 2 terminated, comment
+stmt 2 terminated, comment
+select "stmt 1"; select "space, stmt 2"$$
+stmt 1
+stmt 1
+space, stmt 2
+space, stmt 2
+select "stmt 1";/* comment */select "comment, stmt 2"$$
+stmt 1
+stmt 1
+comment, stmt 2
+comment, stmt 2
+DROP PROCEDURE IF EXISTS p26030; CREATE PROCEDURE p26030() BEGIN SELECT 1; END; CALL p26030()
+$$
+1
+1
+DROP PROCEDURE IF EXISTS p26030; CREATE PROCEDURE p26030() SELECT 1; CALL p26030()
+$$
+1
+1
+DROP PROCEDURE p26030;
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 9aef58d5702..09deaf2f322 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -85,9 +85,9 @@ NULL
NULL
NULL
prepare stmt6 from 'select 1; select2';
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '; select2' at line 1
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select2' at line 1
prepare stmt6 from 'insert into t1 values (5,"five"); select2';
-ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '; select2' at line 1
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select2' at line 1
explain prepare stmt6 from 'insert into t1 values (5,"five"); select2';
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'from 'insert into t1 values (5,"five"); select2'' at line 1
create table t2
diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test
new file mode 100644
index 00000000000..02fe98dc7b2
--- /dev/null
+++ b/mysql-test/t/parser.test
@@ -0,0 +1,59 @@
+#
+# This file contains tests covering the parser
+#
+
+#=============================================================================
+# LEXICAL PARSER (lex)
+#=============================================================================
+
+#
+# Maintainer: these tests are for the lexical parser, so every character,
+# even whitespace or comments, is significant here.
+#
+
+#
+# Bug#26030 (Parsing fails for stored routine w/multi-statement execution
+# enabled)
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p26030;
+--enable_warnings
+
+delimiter $$;
+
+select "non terminated"$$
+select "terminated";$$
+select "non terminated, space" $$
+select "terminated, space"; $$
+select "non terminated, comment" /* comment */$$
+select "terminated, comment"; /* comment */$$
+
+# Multi queries can not be used in --ps-protocol test mode
+--disable_ps_protocol
+
+select "stmt 1";select "stmt 2 non terminated"$$
+select "stmt 1";select "stmt 2 terminated";$$
+select "stmt 1";select "stmt 2 non terminated, space" $$
+select "stmt 1";select "stmt 2 terminated, space"; $$
+select "stmt 1";select "stmt 2 non terminated, comment" /* comment */$$
+select "stmt 1";select "stmt 2 terminated, comment"; /* comment */$$
+
+select "stmt 1"; select "space, stmt 2"$$
+select "stmt 1";/* comment */select "comment, stmt 2"$$
+
+DROP PROCEDURE IF EXISTS p26030; CREATE PROCEDURE p26030() BEGIN SELECT 1; END; CALL p26030()
+$$
+
+DROP PROCEDURE IF EXISTS p26030; CREATE PROCEDURE p26030() SELECT 1; CALL p26030()
+$$
+
+--enable_ps_protocol
+
+delimiter ;$$
+DROP PROCEDURE p26030;
+
+#============================================================================r
+# SYNTACTIC PARSER (bison)
+#=============================================================================
+
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 11ff7160c03..91f465a4e2a 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -117,7 +117,6 @@ public:
create_field m_return_field_def; /* This is used for FUNCTIONs only. */
const char *m_tmp_query; // Temporary pointer to sub query string
- uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
st_sp_chistics *m_chistics;
ulong m_sql_mode; // For SHOW CREATE and execution
LEX_STRING m_qname; // db.name
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 6bfcd982b04..a1bfc3edc6c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1010,21 +1010,8 @@ int MYSQLlex(void *arg, void *yythd)
yySkip();
return (SET_VAR);
case MY_LEX_SEMICOLON: // optional line terminator
- if (yyPeek())
- {
- if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
- !lip->stmt_prepare_mode)
- {
- lex->safe_to_cache_query= 0;
- lip->found_semicolon= lip->ptr;
- thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
- lip->next_state= MY_LEX_END;
- return (END_OF_INPUT);
- }
- state= MY_LEX_CHAR; // Return ';'
- break;
- }
- /* fall true */
+ state= MY_LEX_CHAR; // Return ';'
+ break;
case MY_LEX_EOL:
if (lip->ptr >= lip->end_of_query)
{
@@ -1039,7 +1026,7 @@ int MYSQLlex(void *arg, void *yythd)
case MY_LEX_END:
lip->next_state=MY_LEX_END;
return(0); // We found end of input last time
-
+
/* Actually real shouldn't start with . but allow them anyhow */
case MY_LEX_REAL_OR_POINT:
if (my_isdigit(cs,yyPeek()))
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 98e04e45bdd..62a5a79a833 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6169,6 +6169,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
(thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
thd->query_length--;
/* Actually execute the query */
+ if (*found_semicolon)
+ {
+ lex->safe_to_cache_query= 0;
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
+ }
lex->set_trg_event_type_for_tables();
mysql_execute_command(thd);
query_cache_end_of_result(thd);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ffc84bde9c1..090585392a0 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1203,21 +1203,54 @@ END_OF_INPUT
query:
- END_OF_INPUT
- {
- THD *thd= YYTHD;
- if (!thd->bootstrap &&
- (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
- {
- my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0));
- MYSQL_YYABORT;
- }
- else
- {
- thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
- }
- }
- | verb_clause END_OF_INPUT {};
+ END_OF_INPUT
+ {
+ THD *thd= YYTHD;
+ if (!thd->bootstrap &&
+ (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
+ {
+ my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0));
+ MYSQL_YYABORT;
+ }
+ thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
+ thd->m_lip->found_semicolon= NULL;
+ }
+ | verb_clause
+ {
+ Lex_input_stream *lip = YYTHD->m_lip;
+
+ if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) &&
+ ! lip->stmt_prepare_mode &&
+ ! (lip->ptr >= lip->end_of_query))
+ {
+ /*
+ We found a well formed query, and multi queries are allowed:
+ - force the parser to stop after the ';'
+ - mark the start of the next query for the next invocation
+ of the parser.
+ */
+ lip->next_state= MY_LEX_END;
+ lip->found_semicolon= lip->ptr;
+ }
+ else
+ {
+ /* Single query, terminated. */
+ lip->found_semicolon= NULL;
+ }
+ }
+ ';'
+ opt_end_of_input
+ | verb_clause END_OF_INPUT
+ {
+ /* Single query, not terminated. */
+ YYTHD->m_lip->found_semicolon= NULL;
+ }
+ ;
+
+opt_end_of_input:
+ /* empty */
+ | END_OF_INPUT
+ ;
verb_clause:
statement
@@ -9867,13 +9900,6 @@ trigger_tail:
lex->sphead= sp;
lex->spname= $3;
- /*
- We have to turn of CLIENT_MULTI_QUERIES while parsing a
- stored procedure, otherwise yylex will chop it into pieces
- at each ';'.
- */
- sp->m_old_cmq= thd->client_capabilities & CLIENT_MULTI_QUERIES;
- thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
lex->sphead->m_chistics= &lex->sp_chistics;
@@ -9888,9 +9914,6 @@ trigger_tail:
lex->sql_command= SQLCOM_CREATE_TRIGGER;
sp->init_strings(YYTHD, lex);
- /* Restore flag if it was cleared above */
- if (sp->m_old_cmq)
- YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
if (sp->is_not_allowed_in_function("trigger"))
@@ -9968,13 +9991,6 @@ sf_tail:
sp->m_type= TYPE_ENUM_FUNCTION;
lex->sphead= sp;
- /*
- * We have to turn of CLIENT_MULTI_QUERIES while parsing a
- * stored procedure, otherwise yylex will chop it into pieces
- * at each ';'.
- */
- sp->m_old_cmq= thd->client_capabilities & CLIENT_MULTI_QUERIES;
- thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
lex->sphead->m_param_begin= lip->tok_start+1;
}
sp_fdparam_list /* $6 */
@@ -10030,9 +10046,6 @@ sf_tail:
my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str);
MYSQL_YYABORT;
}
- /* Restore flag if it was cleared above */
- if (sp->m_old_cmq)
- YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
}
;
@@ -10062,13 +10075,6 @@ sp_tail:
sp->init_sp_name(YYTHD, $3);
lex->sphead= sp;
- /*
- * We have to turn of CLIENT_MULTI_QUERIES while parsing a
- * stored procedure, otherwise yylex will chop it into pieces
- * at each ';'.
- */
- sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
- YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
}
'('
{
@@ -10104,9 +10110,6 @@ sp_tail:
sp->init_strings(YYTHD, lex);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- /* Restore flag if it was cleared above */
- if (sp->m_old_cmq)
- YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
}
;