summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2023-02-21 20:09:14 +0700
committerDmitry Shulga <dmitry.shulga@mariadb.com>2023-02-21 20:22:59 +0700
commit99973403edc57f113114aa9724043b476a6836ae (patch)
tree81d10a0f3863bcb8587ae2437dbbc450d3dd4d0b
parentb66d3c67647eea787ebcb4dfb46a8ab51c4b11c6 (diff)
downloadmariadb-git-99973403edc57f113114aa9724043b476a6836ae.tar.gz
MDEV-5816: Stored programs: validation of stored program statements
This patch is the second part of implementation for cursor's statement re-parsing. The patch does the following changes: - implements placing of items in SP instruction's free_list when a statement of failing SP instruction is re-parsed. New items are allocated on mem_root of SP instruction - on re-parsing a failed SP instruction that does need to get access to LEX a new lex is instantiated for every SP instruction except cursor relating SP instructions. - items created on re-parsing a cursor relating statement are moved to the free_list of sp_lex_cursor.
-rw-r--r--sql/sp_instr.cc143
-rw-r--r--sql/sp_instr.h13
-rw-r--r--sql/sql_lex.h7
3 files changed, 115 insertions, 48 deletions
diff --git a/sql/sp_instr.cc b/sql/sp_instr.cc
index 430510eee14..aa6ce20cada 100644
--- a/sql/sp_instr.cc
+++ b/sql/sp_instr.cc
@@ -365,34 +365,55 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
void sp_lex_keeper::free_lex(THD *thd)
{
+ /*
+ Currently, m_lex_resp == false for sp_instr_cursor_copy_struct instructions
+ and in some cases for sp_instr_set instructions. For these classes
+ free_lex() returns control flow immediately and doesn't change m_lex.
+ */
if (!m_lex_resp || !m_lex) return;
/* Prevent endless recursion. */
m_lex->sphead= nullptr;
lex_end(m_lex);
- delete (st_lex_local *)m_lex;
+ sp_lex_cursor* cursor_lex= m_lex->get_lex_for_cursor();
+ if (cursor_lex == nullptr)
+ {
+ delete (st_lex_local *)m_lex;
+ /*
+ In case it is not sp_lex_cursor set thd->lex to the null value
+ if it points to a LEX object we just deleted in order to avoid
+ dangling pointers problem.
+ */
+ if (thd->lex == m_lex)
+ thd->lex= nullptr;
- /*
- Set thd->lex to the null value in case it points to a LEX object
- we just deleted in order to avoid dangling pointer problem
- */
- if (thd->lex == m_lex)
- thd->lex= nullptr;
+ m_lex= nullptr;
+ m_lex_resp= false;
+ }
+ else
+ {
+ /*
+ sp_lex_cursor has references to items allocated on parsing a cursor
+ declaration statement. These items are deleted on re-parsing a failing
+ cursor declaration statement at the method
+ sp_lex_instr::cleanup_before_parsing.
+ Remove the reference to items that will be deleted from sp_lex_cursor
+ in order to avoid dangling pointers problem.
+ */
+ cleanup_items(cursor_lex->free_list);
+ cursor_lex->free_list= nullptr;
+ }
- m_lex= nullptr;
- m_lex_resp= false;
lex_query_tables_own_last= nullptr;
}
-void sp_lex_keeper::set_lex(LEX *lex, bool is_lex_owner)
+void sp_lex_keeper::set_lex(LEX *lex)
{
m_lex= lex;
- m_lex_resp= is_lex_owner;
-
- if (m_lex)
- m_lex->sp_lex_in_use= true;
+ m_lex_resp= true;
+ m_lex->sp_lex_in_use= true;
}
@@ -408,11 +429,15 @@ int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp,
{
thd->clear_error();
free_lex(thd);
- LEX *lex= instr->parse_expr(thd, thd->spcont->m_sp);
+ LEX *lex= instr->parse_expr(thd, thd->spcont->m_sp, m_lex);
if (!lex) return true;
- set_lex(lex, true);
+ /*
+ m_lex != nullptr in case it points to sp_lex_cursor.
+ */
+ if (m_lex == nullptr)
+ set_lex(lex);
m_first_execution= true;
}
@@ -603,7 +628,7 @@ bool sp_lex_instr::setup_table_fields_for_trigger(
}
-LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
+LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp, LEX *sp_instr_lex)
{
String sql_query;
@@ -632,8 +657,26 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
saved_ptr_to_next_trg_items_list=
m_cur_trigger_stmt_items.first->next_trig_field_list;
+ /*
+ Clean up items owned by this SP instruction.
+ */
cleanup_before_parsing(sp->m_handler->type());
+ DBUG_ASSERT(mem_root != thd->mem_root);
+ /*
+ Back up the current free_list pointer and reset it to nullptr.
+ Set thd->mem_root pointing to a mem_root of SP instruction being re-parsed.
+ In that way any items created on parsing a statement of the current
+ instruction is allocated on SP instruction's mem_root and placed on its own
+ free_list that later assigned to the current sp_instr. We use the separate
+ free list for every instruction since at least at one place in the source
+ code (the function subst_spvars() to be accurate) we iterate along the
+ list sp_instr->free_list on executing of every SP instruction.
+ */
+ Query_arena backup;
+ thd->set_n_backup_active_arena(this, &backup);
+ thd->free_list= nullptr;
+
Parser_state parser_state;
if (parser_state.init(thd, sql_query.c_ptr(), sql_query.length()))
@@ -642,24 +685,32 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
// Create a new LEX and initialize it.
LEX *lex_saved= thd->lex;
-
- Query_arena *arena, backup;
- arena= thd->activate_stmt_arena_if_needed(&backup);
+ Item **cursor_free_list= nullptr;
/*
- Back up the current free_list pointer and reset it to nullptr.
- In that way any items created on parsing a statement of the current
- instruction is placed on its own free_list that later assigned to
- the current sp_instr. We use the separate free list for every instruction
- since at least at one place in the source code (the function subst_spvars()
- to be accurate) we iterate along the list sp_instr->free_list
- on executing of every instruction.
+ sp_instr_lex == nullptr for cursor relating SP instructions (sp_instr_cpush,
+ sp_instr_cursor_copy_struct) and in some cases for sp_instr_set.
*/
- Item *execution_free_list= thd->free_list;
- thd->free_list= nullptr;
-
- thd->lex= new (thd->mem_root) st_lex_local;
-
+ if (sp_instr_lex == nullptr)
+ thd->lex= new (thd->mem_root) st_lex_local;
+ else
+ {
+ sp_lex_cursor* cursor_lex= sp_instr_lex->get_lex_for_cursor();
+ /*
+ In case sp_instr_cursor_copy_struct instruction being re-parsed
+ the items stored in free_list of sp_lex_cursor are not cleaned up
+ since the class sp_instr_cursor_copy_struct don't pass ownership of
+ lex object to sp_lex_keeper. So, clean up items stored in free_list of
+ sp_lex_cursor explicitly. For sp_instr_cpush instruction items stored
+ in free_list of sp_lex_cursor are cleaned up in the method free_lex()
+ since sp_instr_cpush owns a lex object stored in its sp_lex_keeper
+ data member. So, for the sp_instr_cpush instruction by the time we reach
+ this block cursor_lex->free_list is already empty.
+ */
+ cleanup_items(cursor_lex->free_list);
+ cursor_free_list= &cursor_lex->free_list;
+ DBUG_ASSERT(thd->lex == sp_instr_lex);
+ }
lex_start(thd);
thd->lex->sphead= sp;
@@ -699,20 +750,26 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
setup_table_fields_for_trigger(thd, sp,
saved_ptr_to_next_trg_items_list);
- /*
- Assign the list of items created on parsing to the current
- stored routine instruction.
- */
- free_list= thd->free_list;
- }
- if (arena)
- thd->restore_active_arena(arena, &backup);
+ if (cursor_free_list)
+ /*
+ Update sp_lex_cursor::free_list to point to a list of items
+ just created on re-parsing the cursor's statement.
+ */
+ *cursor_free_list= thd->free_list;
+ else
+ /*
+ Assign the list of items created on re-parsing the statement to
+ the current stored routine's instruction.
+ */
+ free_list= thd->free_list;
+
+ thd->free_list= nullptr;
+ }
- thd->free_list= execution_free_list;
+ Query_arena old;
+ thd->restore_active_arena(&old, &backup);
- thd->lex->sphead= nullptr;
- thd->lex->spcont= nullptr;
LEX *expr_lex= thd->lex;
thd->lex= lex_saved;
diff --git a/sql/sp_instr.h b/sql/sp_instr.h
index 4f79bf625c5..794e9b2ff6c 100644
--- a/sql/sp_instr.h
+++ b/sql/sp_instr.h
@@ -64,6 +64,11 @@ public:
return m_expr_str;
}
+ sp_lex_cursor* get_lex_for_cursor() override
+ {
+ return this;
+ }
+
private:
LEX_CSTRING m_expr_str;
};
@@ -314,11 +319,8 @@ private:
Set LEX object.
@param lex LEX-object
- @param is_lex_owner this flag specifies whether this LEX object is owned
- by the sp_lex_keeper and so should deleted when
- needed.
*/
- void set_lex(LEX *lex, bool is_lex_owner);
+ void set_lex(LEX *lex);
private:
@@ -383,10 +385,11 @@ public:
@param thd Thread context.
@param sp The stored program.
+ @param lex SP instruction's lex
@return new LEX-object or NULL in case of failure.
*/
- LEX *parse_expr(THD *thd, sp_head *sp);
+ LEX *parse_expr(THD *thd, sp_head *sp, LEX *lex);
SQL_I_List<Item_trigger_field>* get_instr_trig_field_list() override
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 104d8e8f531..11207b6ade3 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3155,6 +3155,8 @@ public:
};
+class sp_lex_cursor;
+
struct LEX: public Query_tables_list
{
SELECT_LEX_UNIT unit; /* most upper unit */
@@ -4863,6 +4865,11 @@ public:
{
return query_tables != nullptr || sroutines.records > 0;
}
+
+ virtual sp_lex_cursor* get_lex_for_cursor()
+ {
+ return nullptr;
+ }
};