diff options
-rw-r--r-- | mysql-test/main/sp-condition-handler.result | 43 | ||||
-rw-r--r-- | mysql-test/main/sp-condition-handler.test | 50 | ||||
-rw-r--r-- | sql/sp_head.cc | 2 | ||||
-rw-r--r-- | sql/sp_head.h | 2 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 24 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 34 |
6 files changed, 107 insertions, 48 deletions
diff --git a/mysql-test/main/sp-condition-handler.result b/mysql-test/main/sp-condition-handler.result new file mode 100644 index 00000000000..350c5d2bd3d --- /dev/null +++ b/mysql-test/main/sp-condition-handler.result @@ -0,0 +1,43 @@ +# +# Start of 10.3 tests +# +# +# MDEV-16595 SP with a CONTINUE HANDLER inside a loop wastes THD memory aggressively +# +CREATE PROCEDURE p1() +BEGIN +DECLARE mem_used_old BIGINT UNSIGNED DEFAULT +(SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME='MEMORY_USED'); +DECLARE i INT DEFAULT 1; +WHILE i <= 1000 +DO +BEGIN +DECLARE msg TEXT; +DECLARE mem_used_cur BIGINT UNSIGNED DEFAULT +(SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME='MEMORY_USED'); +DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23001' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23002' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23003' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23004' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23005' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23006' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23007' SET @x=1; +DECLARE CONTINUE HANDLER FOR SQLSTATE '23008' SET @x=1; +IF (mem_used_cur >= mem_used_old * 1.1) THEN +SHOW STATUS LIKE 'Memory_used'; +SET msg=CONCAT('Memory leak detected: i=', i, ' mem_used_old=',mem_used_old,' mem_used_cur=', mem_used_cur); +SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT=msg; +END IF; +END; +SET i=i+1; +END WHILE; +END; +$$ +CALL p1; +DROP PROCEDURE p1; +# +# End of 10.3 tests +# diff --git a/mysql-test/main/sp-condition-handler.test b/mysql-test/main/sp-condition-handler.test new file mode 100644 index 00000000000..29f841607e2 --- /dev/null +++ b/mysql-test/main/sp-condition-handler.test @@ -0,0 +1,50 @@ + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-16595 SP with a CONTINUE HANDLER inside a loop wastes THD memory aggressively +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE mem_used_old BIGINT UNSIGNED DEFAULT + (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS + WHERE VARIABLE_NAME='MEMORY_USED'); + DECLARE i INT DEFAULT 1; + WHILE i <= 1000 + DO + BEGIN + DECLARE msg TEXT; + DECLARE mem_used_cur BIGINT UNSIGNED DEFAULT + (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS + WHERE VARIABLE_NAME='MEMORY_USED'); + DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23001' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23002' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23003' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23004' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23005' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23006' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23007' SET @x=1; + DECLARE CONTINUE HANDLER FOR SQLSTATE '23008' SET @x=1; + IF (mem_used_cur >= mem_used_old * 1.1) THEN + SHOW STATUS LIKE 'Memory_used'; + SET msg=CONCAT('Memory leak detected: i=', i, ' mem_used_old=',mem_used_old,' mem_used_cur=', mem_used_cur); + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT=msg; + END IF; + END; + SET i=i+1; + END WHILE; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # End of 10.3 tests +--echo # diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 6b116cb4550..c2d42cd830f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -4027,7 +4027,7 @@ sp_instr_hpush_jump::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_hpush_jump::execute"); - int ret= thd->spcont->push_handler(m_handler, m_ip + 1); + int ret= thd->spcont->push_handler(this); *nextp= m_dest; diff --git a/sql/sp_head.h b/sql/sp_head.h index 580859b8831..cf934603cf0 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1677,8 +1677,6 @@ public: { return m_handler; } private: - -private: /// Handler. sp_handler *m_handler; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index d77309b9bc0..24777abe1c3 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -448,19 +448,9 @@ void sp_rcontext::pop_cursors(THD *thd, size_t count) } -bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip) +bool sp_rcontext::push_handler(sp_instr_hpush_jump *entry) { - /* - We should create handler entries in the callers arena, as - they could be (and usually are) used in several instructions. - */ - sp_handler_entry *he= - new (callers_arena->mem_root) sp_handler_entry(handler, first_ip); - - if (he == NULL) - return true; - - return m_handlers.append(he); + return m_handlers.append(entry); } @@ -548,12 +538,12 @@ bool sp_rcontext::handle_sql_condition(THD *thd, DBUG_ASSERT(found_condition); - sp_handler_entry *handler_entry= NULL; + sp_instr_hpush_jump *handler_entry= NULL; for (size_t i= 0; i < m_handlers.elements(); ++i) { - sp_handler_entry *h= m_handlers.at(i); + sp_instr_hpush_jump *h= m_handlers.at(i); - if (h->handler == found_handler) + if (h->get_handler() == found_handler) { handler_entry= h; break; @@ -582,7 +572,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, // Mark active conditions so that they can be deleted when the handler exits. da->mark_sql_conditions_for_removal(); - uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ? + uint continue_ip= handler_entry->get_handler()->type == sp_handler::CONTINUE ? cur_spi->get_cont_dest() : 0; /* End aborted result set. */ @@ -601,7 +591,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, new (callers_arena->mem_root) Handler_call_frame(cond_info, continue_ip); m_handler_call_stack.append(frame); - *ip= handler_entry->first_ip; + *ip= handler_entry->m_ip + 1; DBUG_RETURN(true); } diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 40d636b3529..b02ba14b99b 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -31,6 +31,7 @@ class sp_cursor; class sp_lex_keeper; class sp_instr_cpush; +class sp_instr_hpush_jump; class Query_arena; class sp_head; class Item_cache; @@ -87,27 +88,6 @@ private: sp_rcontext(const sp_rcontext &); void operator=(sp_rcontext &); -private: - /// This is an auxillary class to store entering instruction pointer for an - /// SQL-handler. - class sp_handler_entry : public Sql_alloc - { - public: - /// Handler definition (from parsing context). - const sp_handler *handler; - - /// Instruction pointer to the first instruction. - uint first_ip; - - /// The constructor. - /// - /// @param _handler sp_handler object. - /// @param _first_ip first instruction pointer. - sp_handler_entry(const sp_handler *_handler, uint _first_ip) - :handler(_handler), first_ip(_first_ip) - { } - }; - public: /// This class stores basic information about SQL-condition, such as: /// - SQL error code; @@ -235,18 +215,16 @@ public: // SQL-handlers. ///////////////////////////////////////////////////////////////////////// - /// Create a new sp_handler_entry instance and push it to the handler call - /// stack. + /// Push an sp_instr_hpush_jump instance to the handler call stack. /// - /// @param handler SQL-handler object. - /// @param first_ip First instruction pointer of the handler. + /// @param entry The condition handler entry /// /// @return error flag. /// @retval false on success. /// @retval true on error. - bool push_handler(sp_handler *handler, uint first_ip); + bool push_handler(sp_instr_hpush_jump *entry); - /// Pop and delete given number of sp_handler_entry instances from the handler + /// Pop and delete given number of instances from the handler /// call stack. /// /// @param count Number of handler entries to pop & delete. @@ -411,7 +389,7 @@ private: bool m_in_sub_stmt; /// Stack of visible handlers. - Dynamic_array<sp_handler_entry *> m_handlers; + Dynamic_array<sp_instr_hpush_jump *> m_handlers; /// Stack of caught SQL conditions. Dynamic_array<Handler_call_frame *> m_handler_call_stack; |