From a0b4e0f816f46ac4e2a1c2d6223cf775664a5826 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 19 Jul 2023 18:14:00 +0700 Subject: [PATCH] MDEV-5816: Stored programs: validation of stored program statements Added re-parsing of a failing cursor body. Re-parsing of a failing SP statement is implemented by the method validate_lex_and_exec_core(), therefore invocation of the method reset_lex_and_exec_core() inside sp_lex_keeper::cursor_reset_lex_and_exec_core was replaced by the method validate_lex_and_exec_core(). Re-parsing of a failed SP statement is relied upon interface provided by the class sp_lex_instr (the methods used for this goal are is_invalid(), parse_expr(), invalidate(), get_query(), get_expr_query()). To provide access to these methods on opening a cursor, the signature of the method sp_lex_keeper::cursor_reset_lex_and_exec_core was changed to accept a pointer to the class sp_lex_instr instead of the class sp_instr, and the new method get_push_instr() was added into the class sp_cursor. This method is to get access to an instance of the class sp_instr_cpush on opening a cursor (on handling the statement OPEN cursors_name). Default implementation of this method just returns NULL pointer of the type sp_instr_cpush. This method is overridden in the class sp_instr_cpush with trivial implementation { return this; } On handling the statement DECLARE CURSOR FOR the new instruction of the type sp_instr_cpush is added into sp_head. The class sp_instr_cpush holds a text of SELECT query referencing by a cursor declaration. When a cursor is being opened (on handling the statement 'OPEN cur_name') a pointer to sp_instr_cpush is returned by the method sp_cursor::get_push_instr() and this pointer is passed to the method sp_lex_keeper::cursor_reset_lex_and_exec_core in order to open a cursor and provide access to an interface required for SP statement re-parsing in case metadata changes took place. Since real access to a lex object is required on handling instruction sp_instr_cpush (an instance of this class is created during parsing of cursor declaration statement), calling of the method sp_cursor::open is moved from the method sp_instr_copen::exec_core into the method sp_instr_cpush::exec_core. Additionally, updated the methods get_query/get_expr_query in the classes sp_instr_cpush, sp_instr_cursor_copy_struct in order to return correct text of cursor's body taking into account that lexer treated the clause CURSOR FOR/ CURSOR IS as two different tokens following one after another. So, to return a correct text of SELECT statement specified in CURSOR declaration statement, the token FOR/IS should be skipped and text following it should be returned as a text of cursors's query. --- sql/sp_instr.cc | 53 +++++++++++++++++++++++++++++++++++++------------ sql/sp_instr.h | 27 +++++++++++++++++++++---- sql/sql_class.h | 3 +++ 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/sql/sp_instr.cc b/sql/sp_instr.cc index b11f4bf2b537b..c7fe786988353 100644 --- a/sql/sp_instr.cc +++ b/sql/sp_instr.cc @@ -472,7 +472,7 @@ int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp, int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, - sp_instr *instr) + sp_lex_instr *instr) { Query_arena *old_arena= thd->stmt_arena; /* @@ -482,7 +482,7 @@ int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables). */ thd->stmt_arena= m_lex->query_arena(); - int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr); + int res= validate_lex_and_exec_core(thd, nextp, open_tables, instr); cleanup_items(thd->stmt_arena->free_list); thd->stmt_arena= old_arena; return res; @@ -1534,6 +1534,13 @@ sp_instr_cpush::execute(THD *thd, uint *nextp) } +int +sp_instr_cpush::exec_core(THD *thd, uint *nextp) +{ + sp_cursor *c = thd->spcont->get_cursor(m_cursor); + return c ? c->open(thd) : true; +} + void sp_instr_cpush::print(String *str) { @@ -1612,22 +1619,42 @@ sp_instr_copen::execute(THD *thd, uint *nextp) else { sp_lex_keeper *lex_keeper= c->get_lex_keeper(); - res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, false, this); - /* TODO: Assert here that we either have an error or a cursor */ + /* + The expression + sp_cursor *c= thd->spcont->get_cursor(m_cursor); + that has run above returns an instance of the class sp_instr_cpush + that was added former on handling the statement DECLARE CURSOR. + The class sp_instr_cpush implements the pure virtual method + sp_cursor::get_lex_keeper() + so the following DBUG_ASSERT must be ok. This DBUG_ASSERT is added + in order to catch possible future changes in execution flow that could + break implicit relationship between sp_instr_copen and sp_instr_cpush. + */ + DBUG_ASSERT(lex_keeper); + /* + Get a pointer to a SP instruction sp_instr_cpush that was instantiated + on handling the statement DECLARE CURSOR. The pointer to sp_instr_cpush + is passed to the method cursor_reset_lex_and_exec_core() finishing + a process of cursor opening by calling the method + sp_instr_cpush::exec_core + that does a real work for cursor opening. + */ + sp_instr_cpush *cpush_instr= c->get_push_instr(); + /* + For the same goal as previous DBUG_ASSERT, this DBUG_ASSERT ensure that + sp_inst_cpush has been already added to SP, that is the statement + DECLARE CURSOR occurred before the statement OPEN cursor_name. + */ + DBUG_ASSERT(cpush_instr); + res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, false, + cpush_instr); + + *nextp= m_ip + 1; } DBUG_RETURN(res); } -int -sp_instr_copen::exec_core(THD *thd, uint *nextp) -{ - sp_cursor *c= thd->spcont->get_cursor(m_cursor); - int res= c->open(thd); - *nextp= m_ip+1; - return res; -} - void sp_instr_copen::print(String *str) { diff --git a/sql/sp_instr.h b/sql/sp_instr.h index b3c1ce84ce2b4..27d1f917ceed0 100644 --- a/sql/sp_instr.h +++ b/sql/sp_instr.h @@ -283,7 +283,7 @@ class sp_lex_keeper final sp_lex_instr* instr); int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, - sp_instr *instr); + sp_lex_instr *instr); /** (Re-)parse the query corresponding to this instruction and return a new @@ -514,7 +514,6 @@ class sp_instr_stmt : public sp_lex_instr return LEX_CSTRING{m_query.str, m_query.length}; } -protected: bool on_after_expr_parsing(THD *) override { m_valid= true; @@ -1185,6 +1184,8 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor int execute(THD *thd, uint *nextp) override; + int exec_core(THD *thd, uint *nextp) override; + void print(String *str) override; /** @@ -1217,9 +1218,20 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor sql_query->append(get_expr_query()); } + sp_instr_cpush *get_push_instr() override { return this; } + protected: LEX_CSTRING get_expr_query() const override { + /* + Lexer on processing the clause CURSOR FOR / CURSOR IS doesn't + move a pointer on cpp_buf after the token FOR/IS so skip it explicitly + in order to get correct value of cursor's query string. + */ + if (strncasecmp(m_cursor_stmt.str, "FOR ", 4) == 0) + return LEX_CSTRING{m_cursor_stmt.str + 4, m_cursor_stmt.length - 4}; + if (strncasecmp(m_cursor_stmt.str, "IS ", 3) == 0) + return LEX_CSTRING{m_cursor_stmt.str + 3, m_cursor_stmt.length - 3}; return m_cursor_stmt; } @@ -1285,8 +1297,6 @@ class sp_instr_copen : public sp_instr int execute(THD *thd, uint *nextp) override; - int exec_core(THD *thd, uint *nextp) override; - void print(String *str) override; private: @@ -1348,6 +1358,15 @@ class sp_instr_cursor_copy_struct: public sp_lex_instr protected: LEX_CSTRING get_expr_query() const override { + /* + Lexer on processing the clause CURSOR FOR / CURSOR IS doesn't + move a pointer on cpp_buf after the token FOR/IS so skip it explicitly + in order to get correct value of cursor's query string. + */ + if (strncasecmp(m_cursor_stmt.str, "FOR ", 4) == 0) + return LEX_CSTRING{m_cursor_stmt.str + 4, m_cursor_stmt.length - 4}; + if (strncasecmp(m_cursor_stmt.str, "IS ", 3) == 0) + return LEX_CSTRING{m_cursor_stmt.str + 3, m_cursor_stmt.length - 3}; return m_cursor_stmt; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 06cb6b7e3f1b4..79df052cf58b4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5980,6 +5980,8 @@ class sp_cursor_statistics }; +class sp_instr_cpush; + /* A mediator between stored procedures and server side cursors */ class sp_lex_keeper; class sp_cursor: public sp_cursor_statistics @@ -6046,6 +6048,7 @@ class sp_cursor: public sp_cursor_statistics server_side_cursor= NULL; } + virtual sp_instr_cpush *get_push_instr() { return nullptr; } private: Select_fetch_into_spvars result; Server_side_cursor *server_side_cursor;