Commit a0b4e0f8 authored by Dmitry Shulga's avatar Dmitry Shulga

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.
parent 9e599235
...@@ -472,7 +472,7 @@ int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 @@ class sp_lex_keeper final ...@@ -283,7 +283,7 @@ class sp_lex_keeper final
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 @@ class sp_instr_stmt : public sp_lex_instr ...@@ -514,7 +514,6 @@ class sp_instr_stmt : public sp_lex_instr
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 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor ...@@ -1185,6 +1184,8 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor
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 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor ...@@ -1217,9 +1218,20 @@ class sp_instr_cpush : public sp_lex_instr, public sp_cursor
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 @@ class sp_instr_copen : public sp_instr ...@@ -1285,8 +1297,6 @@ class sp_instr_copen : public sp_instr
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 @@ class sp_instr_cursor_copy_struct: public sp_lex_instr ...@@ -1348,6 +1358,15 @@ class sp_instr_cursor_copy_struct: public sp_lex_instr
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 @@ class sp_cursor_statistics ...@@ -5980,6 +5980,8 @@ class sp_cursor_statistics
}; };
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 @@ class sp_cursor: public sp_cursor_statistics ...@@ -6046,6 +6048,7 @@ class sp_cursor: public sp_cursor_statistics
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment