summaryrefslogtreecommitdiff
path: root/sql/sp_head.cc
Commit message (Collapse)AuthorAgeFilesLines
* Merge branch '10.3' into 10.4Sergei Golubchik2023-01-101-2/+2
|\
| * fix typoslilinjie2023-01-051-2/+2
| | | | | | | | Signed-off-by: lilinjie <lilinjie@uniontech.com>
* | Merge branch '10.3' into 10.4Sergei Golubchik2022-10-011-1/+1
|\ \ | |/
| * MDEV-28548: ER_TABLEACCESS_DENIED_ERROR is missing information about DBAnel Husakovic2022-09-301-1/+1
| | | | | | | | | | | | | | - Added missing information about database of corresponding table for various types of commands - Update some typos - Reviewed by: <vicentiu@mariadb.org>
* | Reduce compilation dependencies on wsrep_mysqld.hDaniele Sciascia2022-08-311-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Making changes to wsrep_mysqld.h causes large parts of server code to be recompiled. The reason is that wsrep_mysqld.h is included by sql_class.h, even tough very little of wsrep_mysqld.h is needed in sql_class.h. This commit introduces a new header file, wsrep_on.h, which is meant to be included from sql_class.h, and contains only macros and variable declarations used to determine whether wsrep is enabled. Also, header wsrep.h should only contain definitions that are also used outside of sql/. Therefore, move WSREP_TO_ISOLATION* and WSREP_SYNC_WAIT macros to wsrep_mysqld.h. Reviewed-by: Jan Lindström <jan.lindstrom@mariadb.com>
* | Merge branch '10.3' into 10.4Sergei Golubchik2022-05-081-1/+1
|\ \ | |/
| * Merge branch '10.2' into 10.3Oleksandr Byelkin2022-05-031-1/+1
| |\
| | * MDEV-6899 extra semicolon in show create event syntaxSergei Golubchik2022-04-251-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | to detect the end of SP definition correctly we need to know where the parser stopped parsing the SP. lip->get_cpp_ptr() shows the current parsing position, lip->get_cpp_tok_start() shows the start of the last parsed token. The actual value depends on whether the parser has performed a look-ahead. For example, in CREATE PROCEDURE ... BEGIN ... END ; the parser reads 'END' and knows that this ends the procedure definition, it does not need to read the next token for this. But in CREATE PROCEDURE ... SELECT 1 ; the parser cannot know that the procedure ends at '1'. It has to read the semicolon first (it could be '1 + 2' for example). In the first case, the "current parsing position" is after END, before the semicolon, in the second case it's *after* the semicolon. Note that SP definition in both cases ends before the semicolon. To be able to detect the end of SP deterministically, we need the parser to do the look-ahead always or never. The bug fix introduces a new parser token FORCE_LOOKAHEAD. Lexer never returns it, so this token can never match. But the parser cannot know it so it will have to perform a look-ahead to determine that the next token is not FORCE_LOOKAHEAD. This way we deterministically end SP parsing with a look-ahead.
| | * MDEV-26833 Missed statement rollback in case transaction drops or create ↵bb-10.2-andreiAndrei Elkin2021-10-281-1/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | temporary table When transaction creates or drops temporary tables and afterward its statement faces an error even the transactional table statement's cached ROW format events get involved into binlog and are visible after the transaction's commit. Fixed with proper analysis of whether the errored-out statement needs to be rolled back in binlog. For instance a fact of already cached CREATE or DROP for temporary tables by previous statements alone does not cause to retain the being errored-out statement events in the cache. Conversely, if the statement creates or drops a temporary table itself it can't be rolled back - this rule remains.
* | | Merge 10.3 into 10.4Marko Mäkelä2022-03-291-0/+1
|\ \ \ | |/ /
| * | MDEV-26009 Server crash when calling twice procedure using FOR-loopOleksandr Byelkin2022-03-211-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The problem was that instructions sp_instr_cursor_copy_struct and sp_instr_copen uses the same lex, adding and removing "tail" of prelocked tables and forgetting that tail of all tables is kept in LEX::query_tables_last. If the LEX used only by one instruction or the query do not have prelocked tables it is not important. But to work correctly in all cases LEX::query_tables_last should be reset to make new tables added in the correct list (after last table in the LEX instead after last table of the prelocking "tail" which was cut).
* | | Merge branch '10.3' into 10.4mariadb-10.4.22Oleksandr Byelkin2021-11-051-1/+7
|\ \ \ | |/ /
| * | Merge branch '10.2' into 10.3mariadb-10.3.32Oleksandr Byelkin2021-11-051-1/+7
| |\ \
| | * | MDEV-26833 Missed statement rollback in case transaction drops or create ↵mariadb-10.2.41Andrei Elkin2021-11-051-1/+7
| | |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | temporary table When transaction creates or drops temporary tables and afterward its statement faces an error even the transactional table statement's cached ROW format events get involved into binlog and are visible after the transaction's commit. Fixed with proper analysis of whether the errored-out statement needs to be rolled back in binlog. For instance a fact of already cached CREATE or DROP for temporary tables by previous statements alone does not cause to retain the being errored-out statement events in the cache. Conversely, if the statement creates or drops a temporary table itself it can't be rolled back - this rule remains.
| * | Merge 10.2 into 10.3Marko Mäkelä2021-05-241-2/+1
| |\ \ | | |/
| | * MDEV-23886 Reusing CTE inside a function fails with table doesn't existIgor Babaev2021-05-211-2/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In the code existed just before this patch binding of a table reference to the specification of the corresponding CTE happens in the function open_and_process_table(). If the table reference is not the first in the query the specification is cloned in the same way as the specification of a view is cloned for any reference of the view. This works fine for standalone queries, but does not work for stored procedures / functions for the following reason. When the first call of a stored procedure/ function SP is processed the body of SP is parsed. When a query of SP is parsed the info on each encountered table reference is put into a TABLE_LIST object linked into a global chain associated with the query. When parsing of the query is finished the basic info on the table references from this chain except table references to derived tables and information schema tables is put in one hash table associated with SP. When parsing of the body of SP is finished this hash table is used to construct TABLE_LIST objects for all table references mentioned in SP and link them into the list of such objects passed to a pre-locking process that calls open_and_process_table() for each table from the list. When a TABLE_LIST for a view is encountered the view is opened and its specification is parsed. For any table reference occurred in the specification a new TABLE_LIST object is created to be included into the list for pre-locking. After all objects in the pre-locking have been looked through the tables mentioned in the list are locked. Note that the objects referenced CTEs are just skipped here as it is impossible to resolve these references without any info on the context where they occur. Now the statements from the body of SP are executed one by one that. At the very beginning of the execution of a query the tables used in the query are opened and open_and_process_table() now is called for each table reference mentioned in the list of TABLE_LIST objects associated with the query that was built when the query was parsed. For each table reference first the reference is checked against CTEs definitions in whose scope it occurred. If such definition is found the reference is considered resolved and if this is not the first reference to the found CTE the the specification of the CTE is re-parsed and the result of the parsing is added to the parsing tree of the query as a sub-tree. If this sub-tree contains table references to other tables they are added to the list of TABLE_LIST objects associated with the query in order the referenced tables to be opened. When the procedure that opens the tables comes to the TABLE_LIST object created for a non-first reference to a CTE it discovers that the referenced table instance is not locked and reports an error. Thus processing non-first table references to a CTE similar to how references to view are processed does not work for queries used in stored procedures / functions. And the main problem is that the current pre-locking mechanism employed for stored procedures / functions does not allow to save the context in which a CTE reference occur. It's not trivial to save the info about the context where a CTE reference occurs while the resolution of the table reference cannot be done without this context and consequentially the specification for the table reference cannot be determined. This patch solves the above problem by moving resolution of all CTE references at the parsing stage. More exactly references to CTEs occurred in a query are resolved right after parsing of the query has finished. After resolution any CTE reference it is marked as a reference to to derived table. So it is excluded from the hash table created for pre-locking used base tables and view when the first call of a stored procedure / function is processed. This solution required recursive calls of the parser. The function THD::sql_parser() has been added specifically for recursive invocations of the parser.
* | | MDEV-23886 Reusing CTE inside a function fails with table doesn't existIgor Babaev2021-05-251-2/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In the code existed just before this patch binding of a table reference to the specification of the corresponding CTE happens in the function open_and_process_table(). If the table reference is not the first in the query the specification is cloned in the same way as the specification of a view is cloned for any reference of the view. This works fine for standalone queries, but does not work for stored procedures / functions for the following reason. When the first call of a stored procedure/ function SP is processed the body of SP is parsed. When a query of SP is parsed the info on each encountered table reference is put into a TABLE_LIST object linked into a global chain associated with the query. When parsing of the query is finished the basic info on the table references from this chain except table references to derived tables and information schema tables is put in one hash table associated with SP. When parsing of the body of SP is finished this hash table is used to construct TABLE_LIST objects for all table references mentioned in SP and link them into the list of such objects passed to a pre-locking process that calls open_and_process_table() for each table from the list. When a TABLE_LIST for a view is encountered the view is opened and its specification is parsed. For any table reference occurred in the specification a new TABLE_LIST object is created to be included into the list for pre-locking. After all objects in the pre-locking have been looked through the tables mentioned in the list are locked. Note that the objects referenced CTEs are just skipped here as it is impossible to resolve these references without any info on the context where they occur. Now the statements from the body of SP are executed one by one that. At the very beginning of the execution of a query the tables used in the query are opened and open_and_process_table() now is called for each table reference mentioned in the list of TABLE_LIST objects associated with the query that was built when the query was parsed. For each table reference first the reference is checked against CTEs definitions in whose scope it occurred. If such definition is found the reference is considered resolved and if this is not the first reference to the found CTE the the specification of the CTE is re-parsed and the result of the parsing is added to the parsing tree of the query as a sub-tree. If this sub-tree contains table references to other tables they are added to the list of TABLE_LIST objects associated with the query in order the referenced tables to be opened. When the procedure that opens the tables comes to the TABLE_LIST object created for a non-first reference to a CTE it discovers that the referenced table instance is not locked and reports an error. Thus processing non-first table references to a CTE similar to how references to view are processed does not work for queries used in stored procedures / functions. And the main problem is that the current pre-locking mechanism employed for stored procedures / functions does not allow to save the context in which a CTE reference occur. It's not trivial to save the info about the context where a CTE reference occurs while the resolution of the table reference cannot be done without this context and consequentially the specification for the table reference cannot be determined. This patch solves the above problem by moving resolution of all CTE references at the parsing stage. More exactly references to CTEs occurred in a query are resolved right after parsing of the query has finished. After resolution any CTE reference it is marked as a reference to to derived table. So it is excluded from the hash table created for pre-locking used base tables and view when the first call of a stored procedure / function is processed. This solution required recursive calls of the parser. The function THD::sql_parser() has been added specifically for recursive invocations of the parser.
* | | Merge 10.3 into 10.4Marko Mäkelä2020-12-011-4/+4
|\ \ \ | |/ /
| * | Merge 10.2 into 10.3Marko Mäkelä2020-12-011-4/+4
| |\ \ | | |/
| | * MDEV 15532 Assertion `!log->same_pk' failed in row_log_table_apply_deleteMonty2020-11-301-4/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The reason for the failure is that thd->mdl_context.release_transactional_locks() was called after commit & rollback even in cases where the current transaction is still active. For 10.2, 10.3 and 10.4 the fix is simple: - Replace all calls to thd->mdl_context.release_transactional_locks() with thd->release_transactional_locks(). The thd function will only call the mdl_context function if there are no active transactional locks. In 10.6 we will better fix where we will change the return value for some trans_xxx() functions to indicate if transaction did close the transaction or not. This will avoid the need of the indirect call. Other things: - trans_xa_commit() and trans_xa_rollback() will automatically call release_transactional_locks() if the transaction is closed. - We can't do that for the other functions as the caller of many of these are doing additional work (like close_thread_tables) before calling release_transactional_locks(). - Added missing abort_result_set() and missing DBUG_RETURN in select_create::send_eof() - Fixed wrong indentation in injector::transaction::commit()
* | | Merge 10.3 into 10.4Marko Mäkelä2020-06-061-0/+4
|\ \ \ | |/ /
| * | Merge 10.2 into 10.3Marko Mäkelä2020-06-061-0/+93
| |\ \ | | |/
| | * MDEV-22042 Server crash in Item_field::print on ANALYZE FORMAT=JSONIgor Babaev2020-06-051-0/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When processing a query with a recursive CTE a temporary table is used for each recursive reference of the CTE. As any temporary table it uses its own mem-root for table definition structures. Due to specifics of the current implementation of ANALYZE stmt command this mem-root can be freed only at the very of query processing. Such deallocation of mem-root memory happens in close_thread_tables(). The function looks through the list of the tmp tables rec_tables attached to the THD of the query and frees corresponding mem-roots. If the query uses a stored function then such list is created for each query of the function. When a new rec_list has to be created the old one has to be saved and then restored at the proper moment. The bug occurred because only one rec_list for the query containing CTE was created. As a result close_thread_tables() freed tmp mem-roots used for rec_tables prematurely destroying some data needed for the output produced by the ANALYZE command.
| | * Merge 10.1 into 10.2Julius Goryavsky2020-06-051-0/+89
| | |\
| | | * MDEV-22763 backporting MDEV-20225 fix into 10.1sjaakola2020-06-031-0/+89
| | | | | | | | | | | | | | | | | | | | | | | | Backported the support for aborting and replaying stored procedure and fix for trigger key assigments from 10.4 version. Backported also two mtr tests: wsrep_sp_bf_abort and MDEV-20225
* | | | Merge 10.3 into 10.4Marko Mäkelä2020-05-161-1/+1
|\ \ \ \ | |/ / /
| * | | MDEV-18100: User defined aggregate functions not working correctly when the ↵Varun Gupta2020-05-151-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | schema is changed The issue here was that when the schema was changed the value for the THD::server_status is ored with SERVER_SESSION_STATE_CHANGED. For custom aggregate functions, currently we check if the server_status is equal to SERVER_STATUS_LAST_ROW_SENT then we should terminate the execution of the custom aggregate function as there are no more rows to fetch. So the check should be that if the server status has the bit set for SERVER_STATUS_LAST_ROW_SENT then we should terminate the execution of the custom aggregate function.
* | | | Merge 10.3 into 10.4Marko Mäkelä2020-01-201-37/+52
|\ \ \ \ | |/ / / | | | | | | | | | | | | The MDEV-17062 fix in commit c4195305b2a8431f39a4c75cc1c66ba43685f7a0 was omitted.
| * | | Fix another trivial merge errorSergei Petrunia2020-01-191-2/+2
| | | |
| * | | Merge branch '10.2' into 10.3Sergei Petrunia2020-01-171-3/+9
| |\ \ \ | | |/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | # Conflicts: # mysql-test/suite/galera/r/MW-388.result # mysql-test/suite/galera/t/MW-388.test # mysql-test/suite/innodb/r/truncate_inject.result # mysql-test/suite/innodb/t/truncate_inject.test # mysql-test/suite/rpl/r/rpl_stop_slave.result # mysql-test/suite/rpl/t/rpl_stop_slave.test # sql/sp_head.cc # sql/sp_head.h # sql/sql_lex.cc # sql/sql_yacc.yy # storage/xtradb/buf/buf0dblwr.cc
| | * | Merge branch '10.1' into 10.2Sergei Petrunia2020-01-171-36/+27
| | |\ \ | | | |/ | | | | | | | | | | | | | | | | | | | | # Conflicts: # sql/sp_head.cc # sql/sql_select.cc # sql/sql_trigger.cc
| | | * MDEV-21341: Fix UBSAN failures: Issue SixSergei Petrunia2020-01-141-35/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (Variant #2 of the patch, which keeps the sp_head object inside the MEM_ROOT that sp_head object owns) (10.3 requires extra work due to sp_package, will commit a separate patch for it) sp_head::operator new() and operator delete() were dereferencing sp_head* pointers to memory that didn't hold a valid sp_head object (it was not created/already destroyed). This caused UBSan to crash when looking up type information. Fixed by providing static sp_head::create() and sp_head::destroy() methods.
| * | | MDEV-21341: Fix UBSAN failures: Issue Sixbb-10.3-mdev21341-issueSixSergei Petrunia2020-01-121-34/+43
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (Variant #2 of the patch, which keeps the sp_head object inside the MEM_ROOT that sp_head object owns) (10.3 version of the fix, with handling for class sp_package) sp_head::operator new() and operator delete() were dereferencing sp_head* pointers to memory that didn't hold a valid sp_head object (it was not created/already destroyed). This caused UBSan to crash when looking up type information. Fixed by providing static sp_head::create() and sp_head::destroy() methods.
* | | | Merge 10.3 into 10.4Marko Mäkelä2019-12-131-2/+4
|\ \ \ \ | |/ / / | | | | | | | | | | | | We disable the MDEV-21189 test galera.galera_partition because it times out.
| * | | MDEV-20667 Server crash on pop_cursorAlexander Barkov2019-12-121-2/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When backpatching a forward GOTO label, the old code erroneously used CURSOR/HANDLER difference between context frames "c" and "a" to tune a cpop/hpop command. So the cpop/hpop command later tried to pop all cursors/handlers declared between "a" and "c", but those between "b" and "c" were not cpushed/hpoped yet during the execution of "GOTO x". Fixing the code to use the difference between frames "b" and "a" only. BEGIN -- a ... GOTO x; -- b ... <<x>> -- c ... END -- d
* | | | Merge branch '10.3' into 10.4Oleksandr Byelkin2019-12-091-5/+1
|\ \ \ \ | |/ / /
| * | | MDEV-18929 2nd execution of SP does not detect ER_VERS_NOT_VERSIONEDAleksey Midenkov2019-12-041-5/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Related to 87731177 and Bug#47649. Don't set Query_arena::STMT_EXECUTED for any error code. skip_setup_conds() depends on STMT_INITIALIZED_FOR_SP, but sp_lex_keeper::reset_lex_and_exec_core() sets it to STMT_EXECUTED on ER_TABLE_NOT_LOCKED_FOR_WRITE. There are other error codes that can break skip_setup_conds() (ER_IT_IS_A_VIEW, ER_NON_UPDATABLE_TABLE, etc).
* | | | Merge 10.3 into 10.4Marko Mäkelä2019-11-011-1/+5
|\ \ \ \ | |/ / /
| * | | MDEV-20074: Lost connection on update triggerOleksandr Byelkin2019-10-171-1/+5
| | | | | | | | | | | | | | | | | | | | Instead of checking lex->sql_command which does not corect in case of triggers mark tables for insert.
* | | | MDEV-20225 BF aborting SP execution (#1394)seppo2019-10-011-43/+63
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * MDEV-20225 BF aborting SP execution When stored procedure execution was chosen as victim for a BF abort, the old implemnetationn called for rollback immediately when execution was inside SP isntruction. Technically this happened in wsrep_after_statement() call, which identified the need for a rollback. The problem was that MariaDB does not accept rollback (nor commit) inside sub statement, there are several asserts about it, checking for THD::in_sub_stmt. This patch contains a fix, which skips calling wsrep_after_statement() for SP execution, which is marked as BF must abort. Instead, we return error code to upper level, where rollback will eventually happen, ouside of SP execution. Also, appending the affected trigger table (dropped or created) in the populated key set for the write set, which prevents parallel applying of other transactions working on the same table. * MDEV-20225 BF aborting SP execution, second patch First PR missed 4 commits, which are now squashed in this patch: - Added galera_sp_bf_abort test. A MTR test case which will reproduce BF-BF conflict if all keys corresponding to affected tables are not assigned for DROP TRIGGER. - Fixed incorrect use of sync pointsin MDEV-20225 - Added condition for SQLCOM_DROP_TRIGGER in wsrep_can_run_in_toi() to make it replicate. * MDEV-20225 BF aborting SP execution, third patch The galera_trigger.test caused a situation, where SP invocation caused a trigger to fire, and the trigger executed as sub statement SP, and was BF aborted by applier. because of wsrep_after_statement() was called for the sub-statement level, it ended up in exeuting rollback and asserted there. Thus fix will catch sub-statement level SP execution, and avoids calling wsrep_after_statement()
* | | | Merge 10.3 into 10.4Marko Mäkelä2019-07-251-1/+1
|\ \ \ \ | |/ / /
| * | | Merge 10.2 into 10.3Eugene Kosov2019-07-161-1/+1
| |\ \ \ | | |/ /
| | * | Merge 10.1 into 10.2Eugene Kosov2019-07-091-1/+1
| | |\ \ | | | |/
| | | * MDEV-17963: Assertion `field_pos < field_count' failed in ↵Varun Gupta2019-07-091-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Protocol_text::store, Assertion `field_handlers == 0 || field_pos < field_count' The problem was that sp_head::MULTI_RESULTS was not set correctly for ANALYZE statement with SELECT ... INTO variable. This is a follow up fix for MDEV-7023
* | | | Merge branch '10.3' into 10.4Oleksandr Byelkin2019-05-191-2/+2
|\ \ \ \ | |/ / /
| * | | Merge 10.2 into 10.3Marko Mäkelä2019-05-141-1/+1
| |\ \ \ | | |/ /
| | * | Merge 10.1 into 10.2Marko Mäkelä2019-05-131-1/+1
| | |\ \ | | | |/
| | | * Merge branch '5.5' into 10.1Vicențiu Ciorbaru2019-05-111-1/+1
| | | |\
| | | | * Update FSF AddressVicențiu Ciorbaru2019-05-111-1/+1
| | | | | | | | | | | | | | | | | | | | * Update wrong zip-code
| | | | * MDEV-18507 can't update temporary table when joined with table with triggers ↵Sergei Golubchik2019-04-241-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | on read-only triggers are opened and tables used in triggers are prelocked in open_tables(). But multi-update can detect what tables will actually be updated only later, after all main tables are opened. Meaning, if a table is used in multi-update, but is not actually updated, its on-update treggers will be opened and tables will be prelocked, even if it's unnecessary. This can cause more tables to be write-locked than needed, causing read_only errors, privilege errors and lock waits. Fix: don't open/prelock triggers unless table->updating is true. In multi-update after setting table->updating=true, do a second open_tables() for newly added tables, if any.