diff options
author | Dmitry Shulga <dmitry.shulga@mariadb.com> | 2023-02-21 20:09:14 +0700 |
---|---|---|
committer | Dmitry Shulga <dmitry.shulga@mariadb.com> | 2023-02-21 20:22:59 +0700 |
commit | 99973403edc57f113114aa9724043b476a6836ae (patch) | |
tree | 81d10a0f3863bcb8587ae2437dbbc450d3dd4d0b | |
parent | b66d3c67647eea787ebcb4dfb46a8ab51c4b11c6 (diff) | |
download | mariadb-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.cc | 143 | ||||
-rw-r--r-- | sql/sp_instr.h | 13 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 |
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; + } }; |