diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-04-05 10:52:31 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:03:02 +0400 |
commit | e1cff0ac5dd583732bb693b1d6eca3376e41f69a (patch) | |
tree | 9be1b50103f815bfacdd8c7cabf1265a13e25385 /sql/sp_pcontext.cc | |
parent | d433277f530689aa3869bad93ed6105e5e0cf954 (diff) | |
download | mariadb-git-e1cff0ac5dd583732bb693b1d6eca3376e41f69a.tar.gz |
MDEV-12441 Variables declared after cursors with parameters lose values
Parse context frames (sp_pcontext) can have holes in variable run-time offsets,
the missing offsets reside on the children contexts in such cases.
Example:
CREATE PROCEDURE p1() AS
x0 INT:=100; -- context 0, position 0, run-time 0
CURSOR cur(
p0 INT, -- context 1, position 0, run-time 1
p1 INT -- context 1, position 1, run-time 2
) IS SELECT p0, p1;
x1 INT:=101; -- context 0, position 1, run-time 3
BEGIN
...
END;
Fixing a few methods to take this into account:
- sp_pcontext::find_variable()
- sp_pcontext::retrieve_field_definitions()
- LEX::sp_variable_declarations_init()
- LEX::sp_variable_declarations_finalize()
- LEX::sp_variable_declarations_rowtype_finalize()
- LEX::sp_variable_declarations_with_ref_finalize()
Adding a convenience method:
sp_pcontext::get_last_context_variable(uint offset_from_the_end);
to access variables from the end, rather than from the beginning.
This helps to loop through the context variable array (m_vars)
on the fragment that does not have any holes.
Additionally, renaming sp_pcontext::find_context_variable() to
sp_pcontext::get_context_variable(). This method simply returns
the variable by its index. So let's rename to avoid assumptions
that some heavy lookup is going on inside.
Diffstat (limited to 'sql/sp_pcontext.cc')
-rw-r--r-- | sql/sp_pcontext.cc | 79 |
1 files changed, 74 insertions, 5 deletions
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index dd70c444d4e..93d7bc11281 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -222,10 +222,46 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name, } +/* + Find a variable by its run-time offset. + If the variable with a desired run-time offset is not found in this + context frame, it's recursively searched on parent context frames. + + Note, context frames can have holes: + CREATE PROCEDURE p1() AS + x0 INT:=100; + CURSOR cur(p0 INT, p1 INT) IS SELECT p0, p1; + x1 INT:=101; + BEGIN + ... + END; + The variables (x0 and x1) and the cursor parameters (p0 and p1) + reside in separate parse context frames. + + The variables reside on the top level parse context frame: + - x0 has frame offset 0 and run-time offset 0 + - x1 has frame offset 1 and run-time offset 3 + + The cursor parameters reside on the second level parse context frame: + - p0 has frame offset 0 and run-time offset 1 + - p1 has frame offset 1 and run-time offset 2 + + Run-time offsets on a frame can have holes, but offsets monotonocally grow, + so run-time offsets of all variables are not greater than the run-time offset + of the very last variable in this frame. +*/ sp_variable *sp_pcontext::find_variable(uint offset) const { - if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements()) - return m_vars.at(offset - m_var_offset); // This frame + if (m_var_offset <= offset && + m_vars.elements() && + offset <= get_last_context_variable()->offset) + { + for (uint i= 0; i < m_vars.elements(); i++) + { + if (m_vars.at(i)->offset == offset) + return m_vars.at(i); // This frame + } + } return m_parent ? m_parent->find_variable(offset) : // Some previous frame @@ -236,7 +272,7 @@ sp_variable *sp_pcontext::find_variable(uint offset) const sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name) { sp_variable *p= - new (thd->mem_root) sp_variable(name, current_var_count()); + new (thd->mem_root) sp_variable(name, m_var_offset + m_max_var_index); if (!p) return NULL; @@ -593,16 +629,49 @@ void sp_pcontext::retrieve_field_definitions( { /* Put local/context fields in the result list. */ + size_t next_child= 0; for (size_t i= 0; i < m_vars.elements(); ++i) { sp_variable *var_def= m_vars.at(i); + /* + The context can have holes in run-time offsets, + the missing offsets reside on the children contexts in such cases. + Example: + CREATE PROCEDURE p1() AS + x0 INT:=100; -- context 0, position 0, run-time 0 + CURSOR cur( + p0 INT, -- context 1, position 0, run-time 1 + p1 INT -- context 1, position 1, run-time 2 + ) IS SELECT p0, p1; + x1 INT:=101; -- context 0, position 1, run-time 3 + BEGIN + ... + END; + See more comments in sp_pcontext::find_variable(). + We must retrieve the definitions in the order of their run-time offsets. + Check that there are children that should go before the current variable. + */ + for ( ; next_child < m_children.elements(); next_child++) + { + sp_pcontext *child= m_children.at(next_child); + if (!child->context_var_count() || + child->get_context_variable(0)->offset > var_def->offset) + break; + /* + All variables on the embedded context (that fills holes of the parent) + should have the run-time offset strictly less than var_def. + */ + DBUG_ASSERT(child->get_context_variable(0)->offset < var_def->offset); + DBUG_ASSERT(child->get_last_context_variable()->offset < var_def->offset); + child->retrieve_field_definitions(field_def_lst); + } field_def_lst->push_back(&var_def->field_def); } - /* Put the fields of the enclosed contexts in the result list. */ + /* Put the fields of the remaining enclosed contexts in the result list. */ - for (size_t i= 0; i < m_children.elements(); ++i) + for (size_t i= next_child; i < m_children.elements(); ++i) m_children.at(i)->retrieve_field_definitions(field_def_lst); } |