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,
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 @@ 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;
}
......
......@@ -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;
......
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