mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03: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:
@ -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,
|
int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
|
||||||
bool open_tables,
|
bool open_tables,
|
||||||
sp_instr *instr)
|
sp_lex_instr *instr)
|
||||||
{
|
{
|
||||||
Query_arena *old_arena= thd->stmt_arena;
|
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).
|
e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
|
||||||
*/
|
*/
|
||||||
thd->stmt_arena= m_lex->query_arena();
|
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);
|
cleanup_items(thd->stmt_arena->free_list);
|
||||||
thd->stmt_arena= old_arena;
|
thd->stmt_arena= old_arena;
|
||||||
return res;
|
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
|
void
|
||||||
sp_instr_cpush::print(String *str)
|
sp_instr_cpush::print(String *str)
|
||||||
{
|
{
|
||||||
@ -1612,22 +1619,42 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
|
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);
|
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
|
void
|
||||||
sp_instr_copen::print(String *str)
|
sp_instr_copen::print(String *str)
|
||||||
{
|
{
|
||||||
|
@ -283,7 +283,7 @@ public:
|
|||||||
sp_lex_instr* instr);
|
sp_lex_instr* instr);
|
||||||
|
|
||||||
int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
|
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
|
(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};
|
return LEX_CSTRING{m_query.str, m_query.length};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
bool on_after_expr_parsing(THD *) override
|
bool on_after_expr_parsing(THD *) override
|
||||||
{
|
{
|
||||||
m_valid= true;
|
m_valid= true;
|
||||||
@ -1185,6 +1184,8 @@ public:
|
|||||||
|
|
||||||
int execute(THD *thd, uint *nextp) override;
|
int execute(THD *thd, uint *nextp) override;
|
||||||
|
|
||||||
|
int exec_core(THD *thd, uint *nextp) override;
|
||||||
|
|
||||||
void print(String *str) override;
|
void print(String *str) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1217,9 +1218,20 @@ public:
|
|||||||
sql_query->append(get_expr_query());
|
sql_query->append(get_expr_query());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sp_instr_cpush *get_push_instr() override { return this; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LEX_CSTRING get_expr_query() const override
|
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;
|
return m_cursor_stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1285,8 +1297,6 @@ public:
|
|||||||
|
|
||||||
int execute(THD *thd, uint *nextp) override;
|
int execute(THD *thd, uint *nextp) override;
|
||||||
|
|
||||||
int exec_core(THD *thd, uint *nextp) override;
|
|
||||||
|
|
||||||
void print(String *str) override;
|
void print(String *str) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1348,6 +1358,15 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
LEX_CSTRING get_expr_query() const override
|
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;
|
return m_cursor_stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5980,6 +5980,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class sp_instr_cpush;
|
||||||
|
|
||||||
/* A mediator between stored procedures and server side cursors */
|
/* A mediator between stored procedures and server side cursors */
|
||||||
class sp_lex_keeper;
|
class sp_lex_keeper;
|
||||||
class sp_cursor: public sp_cursor_statistics
|
class sp_cursor: public sp_cursor_statistics
|
||||||
@ -6046,6 +6048,7 @@ public:
|
|||||||
server_side_cursor= NULL;
|
server_side_cursor= NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual sp_instr_cpush *get_push_instr() { return nullptr; }
|
||||||
private:
|
private:
|
||||||
Select_fetch_into_spvars result;
|
Select_fetch_into_spvars result;
|
||||||
Server_side_cursor *server_side_cursor;
|
Server_side_cursor *server_side_cursor;
|
||||||
|
Reference in New Issue
Block a user