mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
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.
This commit is contained in:
parent
9e599235a5
commit
a0b4e0f816
3 changed files with 66 additions and 17 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -283,7 +283,7 @@ public:
|
|||
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 @@ protected:
|
|||
return LEX_CSTRING{m_query.str, m_query.length};
|
||||
}
|
||||
|
||||
protected:
|
||||
bool on_after_expr_parsing(THD *) override
|
||||
{
|
||||
m_valid= true;
|
||||
|
@ -1185,6 +1184,8 @@ public:
|
|||
|
||||
int execute(THD *thd, uint *nextp) override;
|
||||
|
||||
int exec_core(THD *thd, uint *nextp) override;
|
||||
|
||||
void print(String *str) override;
|
||||
|
||||
/**
|
||||
|
@ -1217,9 +1218,20 @@ public:
|
|||
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 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5980,6 +5980,8 @@ public:
|
|||
};
|
||||
|
||||
|
||||
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 @@ public:
|
|||
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;
|
||||
|
|
Loading…
Reference in a new issue