mirror of
https://github.com/MariaDB/server.git
synced 2025-01-21 06:22:28 +01:00
37312683cc
------------------------------------------------------------------------ r5971 | marko | 2009-09-23 23:03:51 +1000 (Wed, 23 Sep 2009) | 2 lines branches/zip: os_file_pwrite(): Make the code compile in InnoDB Hot Backup when the pwrite system call is not available. ------------------------------------------------------------------------ r5972 | marko | 2009-09-24 05:44:52 +1000 (Thu, 24 Sep 2009) | 5 lines branches/zip: fil_node_open_file(): In InnoDB Hot Backup, determine the page size of single-file tablespaces before computing the file node size. Otherwise, the space->size of compressed tablespaces would be computed with UNIV_PAGE_SIZE instead of key_block_size. This should fix Issue #313. ------------------------------------------------------------------------ r5973 | marko | 2009-09-24 05:53:21 +1000 (Thu, 24 Sep 2009) | 2 lines branches/zip: recv_add_to_hash_table(): Simplify obfuscated pointer arithmetics. ------------------------------------------------------------------------ r5978 | marko | 2009-09-24 17:47:56 +1000 (Thu, 24 Sep 2009) | 1 line branches/zip: Fix warnings and errors when UNIV_HOTBACKUP is defined. ------------------------------------------------------------------------ r5979 | marko | 2009-09-24 20:16:10 +1000 (Thu, 24 Sep 2009) | 4 lines branches/zip: ha_innodb.cc: Define MYSQL_PLUGIN_IMPORT when necessary. This preprocessor symbol has been recently introduced in MySQL 5.1. The InnoDB Plugin should remain source compatible with MySQL 5.1.24 and later. ------------------------------------------------------------------------ r5988 | calvin | 2009-09-26 05:14:43 +1000 (Sat, 26 Sep 2009) | 8 lines branches/zip: fix bug#47055 unconditional exit(1) on ERROR_WORKING_SET_QUOTA 1453 (0x5AD) for InnoDB backend When error ERROR_WORKING_SET_QUOTA or ERROR_NO_SYSTEM_RESOURCES occurs, yields for 100ms and retries the operation. Approved by: Heikki (on IM) ------------------------------------------------------------------------ r5992 | vasil | 2009-09-28 17:10:29 +1000 (Mon, 28 Sep 2009) | 4 lines branches/zip: Add ChangeLog entry for c5988. ------------------------------------------------------------------------ r5994 | marko | 2009-09-28 18:33:59 +1000 (Mon, 28 Sep 2009) | 17 lines branches/zip: Try to prevent the reuse of tablespace identifiers after InnoDB has crashed during table creation. Also, refuse to start if files with duplicate tablespace identifiers are encountered. fil_node_create(): Update fil_system->max_assigned_id. This should prevent the reuse of a space->id when InnoDB does a full crash recovery and invokes fil_load_single_table_tablespaces(). Normally, fil_system->max_assigned_id is initialized from SELECT MAX(ID) FROM SYS_TABLES. fil_open_single_table_tablespace(): Return FALSE when fil_space_create() fails. fil_load_single_table_tablespace(): Exit if fil_space_create() fails and innodb_force_recovery=0. rb://173 approved by Heikki Tuuri. This addresses Issue #335. ------------------------------------------------------------------------ r5995 | marko | 2009-09-28 18:52:25 +1000 (Mon, 28 Sep 2009) | 17 lines branches/zip: Do not write to PAGE_INDEX_ID after page creation, not even when restoring an uncompressed page after a compression failure. btr_page_reorganize_low(): On compression failure, do not restore those page header fields that should not be affected by the reorganization. Instead, compare the fields. page_zip_decompress(): Add the parameter ibool all, for copying all page header fields. Pass the parameter all=TRUE on block read completion, redo log application, and page_zip_validate(); pass all=FALSE in all other cases. page_zip_reorganize(): Do not restore the uncompressed page on failure. It will be restored (to pre-modification state) by the caller anyway. rb://167, Issue #346 ------------------------------------------------------------------------ r5996 | marko | 2009-09-28 22:46:02 +1000 (Mon, 28 Sep 2009) | 4 lines branches/zip: Address Issue #350 in comments. lock_rec_queue_validate(), lock_rec_queue_validate(): Note that this debug code may violate the latching order and cause deadlocks. ------------------------------------------------------------------------ r5997 | marko | 2009-09-28 23:03:58 +1000 (Mon, 28 Sep 2009) | 12 lines branches/zip: Remove an assertion failure when the InnoDB data dictionary is inconsistent with the MySQL .frm file. ha_innobase::index_read(): When the index cannot be found, return an error. ha_innobase::change_active_index(): When prebuilt->index == NULL, set also prebuilt->index_usable = FALSE. This is not needed for correctness, because prebuilt->index_usable is only checked by row_search_for_mysql(), which requires prebuilt->index != NULL. This addresses Issue #349. Approved by Heikki Tuuri over IM. ------------------------------------------------------------------------ r6005 | vasil | 2009-09-29 18:09:52 +1000 (Tue, 29 Sep 2009) | 4 lines branches/zip: ChangeLog: wrap around 78th column, not earlier. ------------------------------------------------------------------------ r6006 | vasil | 2009-09-29 20:15:25 +1000 (Tue, 29 Sep 2009) | 4 lines branches/zip: Add ChangeLog entry for the release of 1.0.4. ------------------------------------------------------------------------ r6007 | vasil | 2009-09-29 23:19:59 +1000 (Tue, 29 Sep 2009) | 6 lines branches/zip: Fix the year, should be 2009. Pointed by: Calvin ------------------------------------------------------------------------ r6026 | marko | 2009-09-30 17:18:24 +1000 (Wed, 30 Sep 2009) | 1 line branches/zip: Add some debug assertions for checking FSEG_MAGIC_N. ------------------------------------------------------------------------ r6028 | marko | 2009-09-30 23:55:23 +1000 (Wed, 30 Sep 2009) | 3 lines branches/zip: recv_no_log_write: New debug flag for tracking down Mantis Issue #347. No modifications should be made to the database while recv_apply_hashed_log_recs() is about to complete. ------------------------------------------------------------------------ r6029 | calvin | 2009-10-01 06:32:02 +1000 (Thu, 01 Oct 2009) | 4 lines branches/zip: non-functional changes Fix typo. ------------------------------------------------------------------------ r6031 | marko | 2009-10-01 21:24:33 +1000 (Thu, 01 Oct 2009) | 49 lines branches/zip: Clean up after a crash during DROP INDEX. When InnoDB crashes while dropping an index, ensure that the index will be completely dropped during crash recovery. row_merge_drop_index(): Before dropping an index, rename the index to start with TEMP_INDEX_PREFIX_STR and commit the change, so that row_merge_drop_temp_indexes() will drop the index after crash recovery if the server crashes while dropping the index. fseg_inode_try_get(): New function, forked from fseg_inode_get(). Return NULL if the file segment index node is free. fseg_inode_get(): Assert that the file segment index node is not free. fseg_free_step(): If the file segment index node is already free, print a diagnostic message and return TRUE. fsp_free_seg_inode(): Write a nonzero number to FSEG_MAGIC_N, so that allocated-and-freed file segment index nodes can be better distinguished from uninitialized ones. This is rb://174, addressing Issue #348. Tested by restarting mysqld upon the completion of the added log_write_up_to() invocation below, during DROP INDEX. The index was dropped after crash recovery, and re-issuing the DROP INDEX did not crash the server. Index: btr/btr0btr.c =================================================================== --- btr/btr0btr.c (revision 6026) +++ btr/btr0btr.c (working copy) @@ -42,6 +42,7 @@ Created 6/2/1994 Heikki Tuuri #include "ibuf0ibuf.h" #include "trx0trx.h" +#include "log0log.h" /* Latching strategy of the InnoDB B-tree -------------------------------------- @@ -873,6 +874,8 @@ leaf_loop: goto leaf_loop; } + + log_write_up_to(mtr.end_lsn, LOG_WAIT_ALL_GROUPS, TRUE); top_loop: mtr_start(&mtr); ------------------------------------------------------------------------ r6033 | calvin | 2009-10-02 06:19:46 +1000 (Fri, 02 Oct 2009) | 4 lines branches/zip: fix a typo in error message Reported as bug#47763. ------------------------------------------------------------------------ r6043 | inaam | 2009-10-06 01:45:35 +1100 (Tue, 06 Oct 2009) | 12 lines branches/zip rb://176 Do not invalidate buffer pool while an LRU batch is active. Added code to buf_pool_invalidate() to wait for the running batches to finish. This patch also resets the state of buf_pool struct at invalidation. This addresses the concern where buf_pool->freed_page_clock becomes non-zero because we read in a system tablespace page for file format info at startup. Approved by: Marko ------------------------------------------------------------------------ r6044 | pekka | 2009-10-07 01:44:54 +1100 (Wed, 07 Oct 2009) | 5 lines branches/zip: Add os_file_is_same() function for Hot Backup (inside ifdef UNIV_HOTBACKUP). This is part of the fix for Issue #186. Note! The Windows implementation is incomplete. ------------------------------------------------------------------------ r6046 | pekka | 2009-10-08 20:24:56 +1100 (Thu, 08 Oct 2009) | 3 lines branches/zip: Revert r6044 which added os_file_is_same() function (issue#186). This functionality is moved to Hot Backup source tree. ------------------------------------------------------------------------ r6048 | vasil | 2009-10-09 16:42:55 +1100 (Fri, 09 Oct 2009) | 16 lines branches/zip: When scanning a directory readdir() is called and stat() after it, if a file is deleted between the two calls stat will fail and the whole precedure will fail. Change this behavior to continue with the next entry if stat() fails because of nonexistent file. This is transparent change as it will make it look as if the file was deleted before the readdir() call. This change is needed in order to fix https://svn.innodb.com/mantis/view.php?id=174 in which we need to abort if os_file_readdir_next_file() encounters "real" errors. Approved by: Marko, Pekka (rb://177) ------------------------------------------------------------------------ r6049 | vasil | 2009-10-10 03:05:26 +1100 (Sat, 10 Oct 2009) | 7 lines branches/zip: Fix compilation warning in Hot Backup: innodb/fil/fil0fil.c: In function 'fil_load_single_table_tablespace': innodb/fil/fil0fil.c:3253: warning: format '%lld' expects type 'long long int', but argument 6 has type 'ib_int64_t' ------------------------------------------------------------------------ r6064 | calvin | 2009-10-14 02:23:35 +1100 (Wed, 14 Oct 2009) | 4 lines branches/zip: non-functional changes Changes from MySQL to fix build issue. ------------------------------------------------------------------------ r6065 | inaam | 2009-10-14 04:43:13 +1100 (Wed, 14 Oct 2009) | 7 lines branches/zip rb://182 Call fsync() on datafiles after a batch of pages is written to disk even when skip_innodb_doublewrite is set. Approved by: Heikki ------------------------------------------------------------------------ r6080 | sunny | 2009-10-15 09:29:01 +1100 (Thu, 15 Oct 2009) | 3 lines branches/zip: Change page_mem_alloc_free() to inline. Fix Bug #47058 - Failure to compile innodb_plugin on solaris 10u7 + spro cc/CC 5.10 ------------------------------------------------------------------------ r6084 | vasil | 2009-10-15 16:21:17 +1100 (Thu, 15 Oct 2009) | 4 lines branches/zip: Add ChangeLog entry for r6080. ------------------------------------------------------------------------ r6095 | vasil | 2009-10-20 00:04:59 +1100 (Tue, 20 Oct 2009) | 7 lines branches/zip: Fix Bug#47808 innodb_information_schema.test fails when run under valgrind by using the wait_until_rows_count macro that loops until the number of rows becomes 14 instead of sleep 0.1, which is obviously very fragile. ------------------------------------------------------------------------ r6096 | vasil | 2009-10-20 00:06:09 +1100 (Tue, 20 Oct 2009) | 4 lines branches/zip: Add ChangeLog entry for r6095. ------------------------------------------------------------------------ r6099 | jyang | 2009-10-22 13:58:39 +1100 (Thu, 22 Oct 2009) | 7 lines branches/zip: Port bug #46000 related changes from 5.1 to zip branch. Due to different code path for creating index in zip branch comparing to 5.1), the index reserved name check function is extended to be used in ha_innobase::add_index(). rb://190 Approved by: Marko ------------------------------------------------------------------------ r6100 | jyang | 2009-10-22 14:51:07 +1100 (Thu, 22 Oct 2009) | 6 lines branches/zip: As a request from mysql, WARN_LEVEL_ERROR cannot be used for push_warning_* call any more. Switch to WARN_LEVEL_WARN. Bug #47233. rb://172 approved by Sunny Bains and Marko. ------------------------------------------------------------------------ r6101 | jyang | 2009-10-23 19:45:50 +1100 (Fri, 23 Oct 2009) | 7 lines branches/zip: Update test result with the WARN_LEVEL_ERROR to WARN_LEVEL_WARN change. This is the same result as submitted in rb://172 review, which approved by Sunny Bains and Marko. ------------------------------------------------------------------------ r6102 | marko | 2009-10-26 18:32:23 +1100 (Mon, 26 Oct 2009) | 1 line branches/zip: row_prebuilt_struct::prebuilts: Unused field, remove. ------------------------------------------------------------------------ r6103 | marko | 2009-10-27 00:46:18 +1100 (Tue, 27 Oct 2009) | 4 lines branches/zip: row_ins_alloc_sys_fields(): Zero out the system columns DB_TRX_ID, DB_ROLL_PTR and DB_ROW_ID, in order to avoid harmless Valgrind warnings about uninitialized data. (The warnings were harmless, because the fields would be initialized at a later stage.) ------------------------------------------------------------------------ r6105 | calvin | 2009-10-28 09:05:52 +1100 (Wed, 28 Oct 2009) | 6 lines branches/zip: backport r3848 from 6.0 branch ---- branches/6.0: innobase_start_or_create_for_mysql(): Make the 10 MB minimum tablespace limit independent of UNIV_PAGE_SIZE. (Bug #41490) ------------------------------------------------------------------------ r6107 | marko | 2009-10-29 01:10:34 +1100 (Thu, 29 Oct 2009) | 5 lines branches/zip: buf_page_set_old(): Improve UNIV_LRU_DEBUG diagnostics in order to catch the buf_pool->LRU_old corruption reported in Issue #381. buf_LRU_old_init(): Set the property from the tail towards the front of the buf_pool->LRU list, in order not to trip the debug check. ------------------------------------------------------------------------ r6108 | calvin | 2009-10-29 16:58:04 +1100 (Thu, 29 Oct 2009) | 5 lines branches/zip: close file handle when building with UNIV_HOTBACKUP The change does not affect regular InnoDB engine. Confirmed by Marko. ------------------------------------------------------------------------ r6109 | jyang | 2009-10-29 19:37:32 +1100 (Thu, 29 Oct 2009) | 7 lines branches/zip: In os_mem_alloc_large(), if we fail to attach the shared memory, reset memory pointer ptr to NULL, and allocate memory from conventional pool. Bug #48237 Error handling in os_mem_alloc_large appears to be incorrect rb://198 Approved by: Marko ------------------------------------------------------------------------ r6110 | marko | 2009-10-29 21:44:57 +1100 (Thu, 29 Oct 2009) | 2 lines branches/zip: Makefile.am (INCLUDES): Merge a change from MySQL: Use $(srcdir)/include instead of $(top_srcdir)/storage/innobase/include. ------------------------------------------------------------------------ r6111 | marko | 2009-10-29 22:04:11 +1100 (Thu, 29 Oct 2009) | 33 lines branches/zip: Fix corruption of buf_pool->LRU_old and improve debug assertions. This was reported as Issue #381. buf_page_set_old(): Assert that blocks may only be set old if buf_pool->LRU_old is initialized and buf_pool->LRU_old_len is nonzero. Assert that buf_pool->LRU_old points to the block at the old/new boundary. buf_LRU_old_adjust_len(): Invoke buf_page_set_old() after adjusting buf_pool->LRU_old and buf_pool->LRU_old_len, in order not to violate the added assertions. buf_LRU_old_init(): Replace buf_page_set_old() with a direct assignment to bpage->old, because these loops that initialize all the blocks would temporarily violate the assertions about buf_pool->LRU_old. buf_LRU_remove_block(): When setting buf_pool->LRU_old = NULL, also clear all bpage->old flags and set buf_pool->LRU_old_len = 0. buf_LRU_add_block_to_end_low(), buf_LRU_add_block_low(): Move the buf_page_set_old() call later in order not to violate the debug assertions. If buf_pool->LRU_old is NULL, set old=FALSE. buf_LRU_free_block(): Replace the UNIV_LRU_DEBUG assertion with a dummy buf_page_set_old() call that performs more thorough checks. buf_LRU_validate(): Do not tolerate garbage in buf_pool->LRU_old_len even if buf_pool->LRU_old is NULL. Check that bpage->old is monotonic. buf_relocate(): Make the UNIV_LRU_DEBUG checks stricter. buf0buf.h: Revise the documentation of buf_page_t::old and buf_pool_t::LRU_old_len. ------------------------------------------------------------------------ r6112 | calvin | 2009-10-30 01:21:15 +1100 (Fri, 30 Oct 2009) | 4 lines branches/zip: consideration for icc compilers Proposed by MySQL, and approved by Marko. ------------------------------------------------------------------------ r6113 | vasil | 2009-10-30 03:15:50 +1100 (Fri, 30 Oct 2009) | 93 lines branches/zip: Merge r5912:6112 from branches/5.1: (after this merge the innodb-autoinc test starts to fail, but I commit anyway because it would be easier to investigate the failure this way) ------------------------------------------------------------------------ r5952 | calvin | 2009-09-22 19:45:07 +0300 (Tue, 22 Sep 2009) | 7 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc branches/5.1: fix bug#42383: Can't create table 'test.bug39438' For embedded server, MySQL may pass in full path, which is currently disallowed. It is needed to relax the condition by accepting full paths in the embedded case. Approved by: Heikki (on IM) ------------------------------------------------------------------------ r6032 | vasil | 2009-10-01 15:55:49 +0300 (Thu, 01 Oct 2009) | 8 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc branches/5.1: Fix Bug#38996 Race condition in ANALYZE TABLE by serializing ANALYZE TABLE inside InnoDB. Approved by: Heikki (rb://175) ------------------------------------------------------------------------ r6045 | jyang | 2009-10-08 02:27:08 +0300 (Thu, 08 Oct 2009) | 7 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc A /branches/5.1/mysql-test/innodb_bug47777.result A /branches/5.1/mysql-test/innodb_bug47777.test branches/5.1: Fix bug #47777. Treat the Geometry data same as Binary BLOB in ha_innobase::store_key_val_for_row(), since the Geometry data is stored as Binary BLOB in Innodb. Review: rb://180 approved by Marko Makela. ------------------------------------------------------------------------ r6051 | sunny | 2009-10-12 07:05:00 +0300 (Mon, 12 Oct 2009) | 6 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc M /branches/5.1/mysql-test/innodb-autoinc.result M /branches/5.1/mysql-test/innodb-autoinc.test branches/5.1: Ignore negative values supplied by the user when calculating the next value to store in dict_table_t. Setting autoincrement columns top negative values is undefined behavior and this change should bring the behavior of InnoDB closer to what users expect. Added several tests to check. rb://162 ------------------------------------------------------------------------ r6052 | sunny | 2009-10-12 07:09:56 +0300 (Mon, 12 Oct 2009) | 4 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc M /branches/5.1/mysql-test/innodb-autoinc.result M /branches/5.1/mysql-test/innodb-autoinc.test branches/5.1: Reset the statement level autoinc counter on ROLLBACK. Fix the test results too. rb://164 ------------------------------------------------------------------------ r6053 | sunny | 2009-10-12 07:37:49 +0300 (Mon, 12 Oct 2009) | 6 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc M /branches/5.1/mysql-test/innodb-autoinc.result M /branches/5.1/mysql-test/innodb-autoinc.test branches/5.1: Copy the maximum AUTOINC value from the old table to the new table when MySQL does a CREATE INDEX ON T. This is required because MySQL does a table copy, rename and drops the old table. Fix Bug#47125: auto_increment start value is ignored if an index is created and engine=innodb rb://168 ------------------------------------------------------------------------ r6076 | vasil | 2009-10-14 19:30:12 +0300 (Wed, 14 Oct 2009) | 4 lines Changed paths: M /branches/5.1/row/row0mysql.c branches/5.1: Fix typo. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r6114 | vasil | 2009-10-30 03:43:51 +1100 (Fri, 30 Oct 2009) | 6 lines branches/zip: * Add ChangeLog entries for latest changes * Obey alphabetical order in the list of the files * White-space fixup ------------------------------------------------------------------------ r6121 | sunny | 2009-10-30 10:42:11 +1100 (Fri, 30 Oct 2009) | 7 lines branches/zip: This test has been problematic for sometime now. The underlying bug is that the data dictionaries get out of sync. In the AUTOINC code we try and apply salve to the symptoms. In the past MySQL made some unrelated change and the dictionaries stopped getting out of sync and this test started to fail. Now, it seems they have reverted that changed and the test is passing again. I suspect this is not he last time that this test will change. ------------------------------------------------------------------------ r6124 | jyang | 2009-10-30 19:02:31 +1100 (Fri, 30 Oct 2009) | 5 lines branches/zip: Correct the bug number for -r6109 change from # 48273 to #48237 ------------------------------------------------------------------------ r6126 | vasil | 2009-10-30 19:36:07 +1100 (Fri, 30 Oct 2009) | 45 lines branches/zip: Merge r6112:6125 from branches/5.1: (skipping r6122 and r6123, Jimmy says these are already present and need not be merged): ------------------------------------------------------------------------ r6122 | jyang | 2009-10-30 05:18:38 +0200 (Fri, 30 Oct 2009) | 7 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc M /branches/5.1/mysql-test/innodb_bug44369.result M /branches/5.1/mysql-test/innodb_bug44369.test M /branches/5.1/mysql-test/innodb_bug46000.result M /branches/5.1/mysql-test/innodb_bug46000.test branches/5.1: Chnage WARN_LEVEL_ERROR to WARN_LEVEL_WARN for push_warning_printf() call in innodb. Fix Bug#47233: Innodb calls push_warning(MYSQL_ERROR::WARN_LEVEL_ERROR) rb://170 approved by Marko. ------------------------------------------------------------------------ r6123 | jyang | 2009-10-30 05:43:06 +0200 (Fri, 30 Oct 2009) | 8 lines Changed paths: M /branches/5.1/os/os0proc.c branches/5.1: In os_mem_alloc_large(), if we fail to attach the shared memory, reset memory pointer ptr to NULL, and allocate memory from conventional pool. This is a port from branches/zip. Bug #48237 Error handling in os_mem_alloc_large appears to be incorrect rb://198 Approved by: Marko ------------------------------------------------------------------------ r6125 | vasil | 2009-10-30 10:31:23 +0200 (Fri, 30 Oct 2009) | 4 lines Changed paths: M /branches/5.1/handler/ha_innodb.cc branches/5.1: White-space fixup. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r6130 | marko | 2009-11-02 20:42:56 +1100 (Mon, 02 Nov 2009) | 9 lines branches/zip: Free all resources at shutdown. Set pointers to NULL, so that Valgrind will not complain about freed data structures that are reachable via pointers. This addresses Bug #45992 and Bug #46656. This patch is mostly based on changes copied from branches/embedded-1.0, mainly c5432, c3439, c3134, c2994, c2978, but also some other code was copied. Some added cleanup code is specific to MySQL/InnoDB. rb://199 approved by Sunny Bains ------------------------------------------------------------------------
1436 lines
34 KiB
C
1436 lines
34 KiB
C
/*****************************************************************************
|
|
|
|
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/**************************************************//**
|
|
@file que/que0que.c
|
|
Query graph
|
|
|
|
Created 5/27/1996 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "que0que.h"
|
|
|
|
#ifdef UNIV_NONINL
|
|
#include "que0que.ic"
|
|
#endif
|
|
|
|
#include "srv0que.h"
|
|
#include "usr0sess.h"
|
|
#include "trx0trx.h"
|
|
#include "trx0roll.h"
|
|
#include "row0undo.h"
|
|
#include "row0ins.h"
|
|
#include "row0upd.h"
|
|
#include "row0sel.h"
|
|
#include "row0purge.h"
|
|
#include "dict0crea.h"
|
|
#include "log0log.h"
|
|
#include "eval0proc.h"
|
|
#include "eval0eval.h"
|
|
#include "pars0types.h"
|
|
|
|
#define QUE_PARALLELIZE_LIMIT (64 * 256 * 256 * 256)
|
|
#define QUE_ROUND_ROBIN_LIMIT (64 * 256 * 256 * 256)
|
|
#define QUE_MAX_LOOPS_WITHOUT_CHECK 16
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/* If the following flag is set TRUE, the module will print trace info
|
|
of SQL execution in the UNIV_SQL_DEBUG version */
|
|
UNIV_INTERN ibool que_trace_on = FALSE;
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/* Short introduction to query graphs
|
|
==================================
|
|
|
|
A query graph consists of nodes linked to each other in various ways. The
|
|
execution starts at que_run_threads() which takes a que_thr_t parameter.
|
|
que_thr_t contains two fields that control query graph execution: run_node
|
|
and prev_node. run_node is the next node to execute and prev_node is the
|
|
last node executed.
|
|
|
|
Each node has a pointer to a 'next' statement, i.e., its brother, and a
|
|
pointer to its parent node. The next pointer is NULL in the last statement
|
|
of a block.
|
|
|
|
Loop nodes contain a link to the first statement of the enclosed statement
|
|
list. While the loop runs, que_thr_step() checks if execution to the loop
|
|
node came from its parent or from one of the statement nodes in the loop. If
|
|
it came from the parent of the loop node it starts executing the first
|
|
statement node in the loop. If it came from one of the statement nodes in
|
|
the loop, then it checks if the statement node has another statement node
|
|
following it, and runs it if so.
|
|
|
|
To signify loop ending, the loop statements (see e.g. while_step()) set
|
|
que_thr_t->run_node to the loop node's parent node. This is noticed on the
|
|
next call of que_thr_step() and execution proceeds to the node pointed to by
|
|
the loop node's 'next' pointer.
|
|
|
|
For example, the code:
|
|
|
|
X := 1;
|
|
WHILE X < 5 LOOP
|
|
X := X + 1;
|
|
X := X + 1;
|
|
X := 5
|
|
|
|
will result in the following node hierarchy, with the X-axis indicating
|
|
'next' links and the Y-axis indicating parent/child links:
|
|
|
|
A - W - A
|
|
|
|
|
|
|
|
A - A
|
|
|
|
A = assign_node_t, W = while_node_t. */
|
|
|
|
/* How a stored procedure containing COMMIT or ROLLBACK commands
|
|
is executed?
|
|
|
|
The commit or rollback can be seen as a subprocedure call.
|
|
The problem is that if there are several query threads
|
|
currently running within the transaction, their action could
|
|
mess the commit or rollback operation. Or, at the least, the
|
|
operation would be difficult to visualize and keep in control.
|
|
|
|
Therefore the query thread requesting a commit or a rollback
|
|
sends to the transaction a signal, which moves the transaction
|
|
to TRX_QUE_SIGNALED state. All running query threads of the
|
|
transaction will eventually notice that the transaction is now in
|
|
this state and voluntarily suspend themselves. Only the last
|
|
query thread which suspends itself will trigger handling of
|
|
the signal.
|
|
|
|
When the transaction starts to handle a rollback or commit
|
|
signal, it builds a query graph which, when executed, will
|
|
roll back or commit the incomplete transaction. The transaction
|
|
is moved to the TRX_QUE_ROLLING_BACK or TRX_QUE_COMMITTING state.
|
|
If specified, the SQL cursors opened by the transaction are closed.
|
|
When the execution of the graph completes, it is like returning
|
|
from a subprocedure: the query thread which requested the operation
|
|
starts running again. */
|
|
|
|
/**********************************************************************//**
|
|
Moves a thread from another state to the QUE_THR_RUNNING state. Increments
|
|
the n_active_thrs counters of the query graph and transaction.
|
|
***NOTE***: This is the only function in which such a transition is allowed
|
|
to happen! */
|
|
static
|
|
void
|
|
que_thr_move_to_run_state(
|
|
/*======================*/
|
|
que_thr_t* thr); /*!< in: an query thread */
|
|
|
|
/***********************************************************************//**
|
|
Adds a query graph to the session's list of graphs. */
|
|
UNIV_INTERN
|
|
void
|
|
que_graph_publish(
|
|
/*==============*/
|
|
que_t* graph, /*!< in: graph */
|
|
sess_t* sess) /*!< in: session */
|
|
{
|
|
ut_ad(mutex_own(&kernel_mutex));
|
|
|
|
UT_LIST_ADD_LAST(graphs, sess->graphs, graph);
|
|
}
|
|
|
|
/***********************************************************************//**
|
|
Creates a query graph fork node.
|
|
@return own: fork node */
|
|
UNIV_INTERN
|
|
que_fork_t*
|
|
que_fork_create(
|
|
/*============*/
|
|
que_t* graph, /*!< in: graph, if NULL then this
|
|
fork node is assumed to be the
|
|
graph root */
|
|
que_node_t* parent, /*!< in: parent node */
|
|
ulint fork_type, /*!< in: fork type */
|
|
mem_heap_t* heap) /*!< in: memory heap where created */
|
|
{
|
|
que_fork_t* fork;
|
|
|
|
ut_ad(heap);
|
|
|
|
fork = mem_heap_alloc(heap, sizeof(que_fork_t));
|
|
|
|
fork->common.type = QUE_NODE_FORK;
|
|
fork->n_active_thrs = 0;
|
|
|
|
fork->state = QUE_FORK_COMMAND_WAIT;
|
|
|
|
if (graph != NULL) {
|
|
fork->graph = graph;
|
|
} else {
|
|
fork->graph = fork;
|
|
}
|
|
|
|
fork->common.parent = parent;
|
|
fork->fork_type = fork_type;
|
|
|
|
fork->caller = NULL;
|
|
|
|
UT_LIST_INIT(fork->thrs);
|
|
|
|
fork->sym_tab = NULL;
|
|
fork->info = NULL;
|
|
|
|
fork->heap = heap;
|
|
|
|
return(fork);
|
|
}
|
|
|
|
/***********************************************************************//**
|
|
Creates a query graph thread node.
|
|
@return own: query thread node */
|
|
UNIV_INTERN
|
|
que_thr_t*
|
|
que_thr_create(
|
|
/*===========*/
|
|
que_fork_t* parent, /*!< in: parent node, i.e., a fork node */
|
|
mem_heap_t* heap) /*!< in: memory heap where created */
|
|
{
|
|
que_thr_t* thr;
|
|
|
|
ut_ad(parent && heap);
|
|
|
|
thr = mem_heap_alloc(heap, sizeof(que_thr_t));
|
|
|
|
thr->common.type = QUE_NODE_THR;
|
|
thr->common.parent = parent;
|
|
|
|
thr->magic_n = QUE_THR_MAGIC_N;
|
|
|
|
thr->graph = parent->graph;
|
|
|
|
thr->state = QUE_THR_COMMAND_WAIT;
|
|
|
|
thr->is_active = FALSE;
|
|
|
|
thr->run_node = NULL;
|
|
thr->resource = 0;
|
|
thr->lock_state = QUE_THR_LOCK_NOLOCK;
|
|
|
|
UT_LIST_ADD_LAST(thrs, parent->thrs, thr);
|
|
|
|
return(thr);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Moves a suspended query thread to the QUE_THR_RUNNING state and may release
|
|
a single worker thread to execute it. This function should be used to end
|
|
the wait state of a query thread waiting for a lock or a stored procedure
|
|
completion. */
|
|
UNIV_INTERN
|
|
void
|
|
que_thr_end_wait(
|
|
/*=============*/
|
|
que_thr_t* thr, /*!< in: query thread in the
|
|
QUE_THR_LOCK_WAIT,
|
|
or QUE_THR_PROCEDURE_WAIT, or
|
|
QUE_THR_SIG_REPLY_WAIT state */
|
|
que_thr_t** next_thr) /*!< in/out: next query thread to run;
|
|
if the value which is passed in is
|
|
a pointer to a NULL pointer, then the
|
|
calling function can start running
|
|
a new query thread; if NULL is passed
|
|
as the parameter, it is ignored */
|
|
{
|
|
ibool was_active;
|
|
|
|
ut_ad(mutex_own(&kernel_mutex));
|
|
ut_ad(thr);
|
|
ut_ad((thr->state == QUE_THR_LOCK_WAIT)
|
|
|| (thr->state == QUE_THR_PROCEDURE_WAIT)
|
|
|| (thr->state == QUE_THR_SIG_REPLY_WAIT));
|
|
ut_ad(thr->run_node);
|
|
|
|
thr->prev_node = thr->run_node;
|
|
|
|
was_active = thr->is_active;
|
|
|
|
que_thr_move_to_run_state(thr);
|
|
|
|
if (was_active) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (next_thr && *next_thr == NULL) {
|
|
*next_thr = thr;
|
|
} else {
|
|
ut_a(0);
|
|
srv_que_task_enqueue_low(thr);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Same as que_thr_end_wait, but no parameter next_thr available. */
|
|
UNIV_INTERN
|
|
void
|
|
que_thr_end_wait_no_next_thr(
|
|
/*=========================*/
|
|
que_thr_t* thr) /*!< in: query thread in the QUE_THR_LOCK_WAIT,
|
|
or QUE_THR_PROCEDURE_WAIT, or
|
|
QUE_THR_SIG_REPLY_WAIT state */
|
|
{
|
|
ibool was_active;
|
|
|
|
ut_a(thr->state == QUE_THR_LOCK_WAIT); /* In MySQL this is the
|
|
only possible state here */
|
|
ut_ad(mutex_own(&kernel_mutex));
|
|
ut_ad(thr);
|
|
ut_ad((thr->state == QUE_THR_LOCK_WAIT)
|
|
|| (thr->state == QUE_THR_PROCEDURE_WAIT)
|
|
|| (thr->state == QUE_THR_SIG_REPLY_WAIT));
|
|
|
|
was_active = thr->is_active;
|
|
|
|
que_thr_move_to_run_state(thr);
|
|
|
|
if (was_active) {
|
|
|
|
return;
|
|
}
|
|
|
|
/* In MySQL we let the OS thread (not just the query thread) to wait
|
|
for the lock to be released: */
|
|
|
|
srv_release_mysql_thread_if_suspended(thr);
|
|
|
|
/* srv_que_task_enqueue_low(thr); */
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Inits a query thread for a command. */
|
|
UNIV_INLINE
|
|
void
|
|
que_thr_init_command(
|
|
/*=================*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
thr->run_node = thr;
|
|
thr->prev_node = thr->common.parent;
|
|
|
|
que_thr_move_to_run_state(thr);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Starts execution of a command in a query fork. Picks a query thread which
|
|
is not in the QUE_THR_RUNNING state and moves it to that state. If none
|
|
can be chosen, a situation which may arise in parallelized fetches, NULL
|
|
is returned.
|
|
@return a query thread of the graph moved to QUE_THR_RUNNING state, or
|
|
NULL; the query thread should be executed by que_run_threads by the
|
|
caller */
|
|
UNIV_INTERN
|
|
que_thr_t*
|
|
que_fork_start_command(
|
|
/*===================*/
|
|
que_fork_t* fork) /*!< in: a query fork */
|
|
{
|
|
que_thr_t* thr;
|
|
que_thr_t* suspended_thr = NULL;
|
|
que_thr_t* completed_thr = NULL;
|
|
|
|
fork->state = QUE_FORK_ACTIVE;
|
|
|
|
fork->last_sel_node = NULL;
|
|
|
|
suspended_thr = NULL;
|
|
completed_thr = NULL;
|
|
|
|
/* Choose the query thread to run: usually there is just one thread,
|
|
but in a parallelized select, which necessarily is non-scrollable,
|
|
there may be several to choose from */
|
|
|
|
/* First we try to find a query thread in the QUE_THR_COMMAND_WAIT
|
|
state. Then we try to find a query thread in the QUE_THR_SUSPENDED
|
|
state, finally we try to find a query thread in the QUE_THR_COMPLETED
|
|
state */
|
|
|
|
thr = UT_LIST_GET_FIRST(fork->thrs);
|
|
|
|
/* We make a single pass over the thr list within which we note which
|
|
threads are ready to run. */
|
|
while (thr) {
|
|
switch (thr->state) {
|
|
case QUE_THR_COMMAND_WAIT:
|
|
|
|
/* We have to send the initial message to query thread
|
|
to start it */
|
|
|
|
que_thr_init_command(thr);
|
|
|
|
return(thr);
|
|
|
|
case QUE_THR_SUSPENDED:
|
|
/* In this case the execution of the thread was
|
|
suspended: no initial message is needed because
|
|
execution can continue from where it was left */
|
|
if (!suspended_thr) {
|
|
suspended_thr = thr;
|
|
}
|
|
|
|
break;
|
|
|
|
case QUE_THR_COMPLETED:
|
|
if (!completed_thr) {
|
|
completed_thr = thr;
|
|
}
|
|
|
|
break;
|
|
|
|
case QUE_THR_LOCK_WAIT:
|
|
ut_error;
|
|
|
|
}
|
|
|
|
thr = UT_LIST_GET_NEXT(thrs, thr);
|
|
}
|
|
|
|
if (suspended_thr) {
|
|
|
|
thr = suspended_thr;
|
|
que_thr_move_to_run_state(thr);
|
|
|
|
} else if (completed_thr) {
|
|
|
|
thr = completed_thr;
|
|
que_thr_init_command(thr);
|
|
}
|
|
|
|
return(thr);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
After signal handling is finished, returns control to a query graph error
|
|
handling routine. (Currently, just returns the control to the root of the
|
|
graph so that the graph can communicate an error message to the client.) */
|
|
UNIV_INTERN
|
|
void
|
|
que_fork_error_handle(
|
|
/*==================*/
|
|
trx_t* trx __attribute__((unused)), /*!< in: trx */
|
|
que_t* fork) /*!< in: query graph which was run before signal
|
|
handling started, NULL not allowed */
|
|
{
|
|
que_thr_t* thr;
|
|
|
|
ut_ad(mutex_own(&kernel_mutex));
|
|
ut_ad(trx->sess->state == SESS_ERROR);
|
|
ut_ad(UT_LIST_GET_LEN(trx->reply_signals) == 0);
|
|
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
|
|
|
|
thr = UT_LIST_GET_FIRST(fork->thrs);
|
|
|
|
while (thr != NULL) {
|
|
ut_ad(!thr->is_active);
|
|
ut_ad(thr->state != QUE_THR_SIG_REPLY_WAIT);
|
|
ut_ad(thr->state != QUE_THR_LOCK_WAIT);
|
|
|
|
thr->run_node = thr;
|
|
thr->prev_node = thr->child;
|
|
thr->state = QUE_THR_COMPLETED;
|
|
|
|
thr = UT_LIST_GET_NEXT(thrs, thr);
|
|
}
|
|
|
|
thr = UT_LIST_GET_FIRST(fork->thrs);
|
|
|
|
que_thr_move_to_run_state(thr);
|
|
|
|
ut_a(0);
|
|
srv_que_task_enqueue_low(thr);
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Tests if all the query threads in the same fork have a given state.
|
|
@return TRUE if all the query threads in the same fork were in the
|
|
given state */
|
|
UNIV_INLINE
|
|
ibool
|
|
que_fork_all_thrs_in_state(
|
|
/*=======================*/
|
|
que_fork_t* fork, /*!< in: query fork */
|
|
ulint state) /*!< in: state */
|
|
{
|
|
que_thr_t* thr_node;
|
|
|
|
thr_node = UT_LIST_GET_FIRST(fork->thrs);
|
|
|
|
while (thr_node != NULL) {
|
|
if (thr_node->state != state) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
thr_node = UT_LIST_GET_NEXT(thrs, thr_node);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Calls que_graph_free_recursive for statements in a statement list. */
|
|
static
|
|
void
|
|
que_graph_free_stat_list(
|
|
/*=====================*/
|
|
que_node_t* node) /*!< in: first query graph node in the list */
|
|
{
|
|
while (node) {
|
|
que_graph_free_recursive(node);
|
|
|
|
node = que_node_get_next(node);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Frees a query graph, but not the heap where it was created. Does not free
|
|
explicit cursor declarations, they are freed in que_graph_free. */
|
|
UNIV_INTERN
|
|
void
|
|
que_graph_free_recursive(
|
|
/*=====================*/
|
|
que_node_t* node) /*!< in: query graph node */
|
|
{
|
|
que_fork_t* fork;
|
|
que_thr_t* thr;
|
|
undo_node_t* undo;
|
|
sel_node_t* sel;
|
|
ins_node_t* ins;
|
|
upd_node_t* upd;
|
|
tab_node_t* cre_tab;
|
|
ind_node_t* cre_ind;
|
|
purge_node_t* purge;
|
|
|
|
if (node == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
switch (que_node_get_type(node)) {
|
|
|
|
case QUE_NODE_FORK:
|
|
fork = node;
|
|
|
|
thr = UT_LIST_GET_FIRST(fork->thrs);
|
|
|
|
while (thr) {
|
|
que_graph_free_recursive(thr);
|
|
|
|
thr = UT_LIST_GET_NEXT(thrs, thr);
|
|
}
|
|
|
|
break;
|
|
case QUE_NODE_THR:
|
|
|
|
thr = node;
|
|
|
|
if (thr->magic_n != QUE_THR_MAGIC_N) {
|
|
fprintf(stderr,
|
|
"que_thr struct appears corrupt;"
|
|
" magic n %lu\n",
|
|
(unsigned long) thr->magic_n);
|
|
mem_analyze_corruption(thr);
|
|
ut_error;
|
|
}
|
|
|
|
thr->magic_n = QUE_THR_MAGIC_FREED;
|
|
|
|
que_graph_free_recursive(thr->child);
|
|
|
|
break;
|
|
case QUE_NODE_UNDO:
|
|
|
|
undo = node;
|
|
|
|
mem_heap_free(undo->heap);
|
|
|
|
break;
|
|
case QUE_NODE_SELECT:
|
|
|
|
sel = node;
|
|
|
|
sel_node_free_private(sel);
|
|
|
|
break;
|
|
case QUE_NODE_INSERT:
|
|
|
|
ins = node;
|
|
|
|
que_graph_free_recursive(ins->select);
|
|
|
|
mem_heap_free(ins->entry_sys_heap);
|
|
|
|
break;
|
|
case QUE_NODE_PURGE:
|
|
purge = node;
|
|
|
|
mem_heap_free(purge->heap);
|
|
|
|
break;
|
|
|
|
case QUE_NODE_UPDATE:
|
|
|
|
upd = node;
|
|
|
|
if (upd->in_mysql_interface) {
|
|
|
|
btr_pcur_free_for_mysql(upd->pcur);
|
|
}
|
|
|
|
que_graph_free_recursive(upd->cascade_node);
|
|
|
|
if (upd->cascade_heap) {
|
|
mem_heap_free(upd->cascade_heap);
|
|
}
|
|
|
|
que_graph_free_recursive(upd->select);
|
|
|
|
mem_heap_free(upd->heap);
|
|
|
|
break;
|
|
case QUE_NODE_CREATE_TABLE:
|
|
cre_tab = node;
|
|
|
|
que_graph_free_recursive(cre_tab->tab_def);
|
|
que_graph_free_recursive(cre_tab->col_def);
|
|
que_graph_free_recursive(cre_tab->commit_node);
|
|
|
|
mem_heap_free(cre_tab->heap);
|
|
|
|
break;
|
|
case QUE_NODE_CREATE_INDEX:
|
|
cre_ind = node;
|
|
|
|
que_graph_free_recursive(cre_ind->ind_def);
|
|
que_graph_free_recursive(cre_ind->field_def);
|
|
que_graph_free_recursive(cre_ind->commit_node);
|
|
|
|
mem_heap_free(cre_ind->heap);
|
|
|
|
break;
|
|
case QUE_NODE_PROC:
|
|
que_graph_free_stat_list(((proc_node_t*)node)->stat_list);
|
|
|
|
break;
|
|
case QUE_NODE_IF:
|
|
que_graph_free_stat_list(((if_node_t*)node)->stat_list);
|
|
que_graph_free_stat_list(((if_node_t*)node)->else_part);
|
|
que_graph_free_stat_list(((if_node_t*)node)->elsif_list);
|
|
|
|
break;
|
|
case QUE_NODE_ELSIF:
|
|
que_graph_free_stat_list(((elsif_node_t*)node)->stat_list);
|
|
|
|
break;
|
|
case QUE_NODE_WHILE:
|
|
que_graph_free_stat_list(((while_node_t*)node)->stat_list);
|
|
|
|
break;
|
|
case QUE_NODE_FOR:
|
|
que_graph_free_stat_list(((for_node_t*)node)->stat_list);
|
|
|
|
break;
|
|
|
|
case QUE_NODE_ASSIGNMENT:
|
|
case QUE_NODE_EXIT:
|
|
case QUE_NODE_RETURN:
|
|
case QUE_NODE_COMMIT:
|
|
case QUE_NODE_ROLLBACK:
|
|
case QUE_NODE_LOCK:
|
|
case QUE_NODE_FUNC:
|
|
case QUE_NODE_ORDER:
|
|
case QUE_NODE_ROW_PRINTF:
|
|
case QUE_NODE_OPEN:
|
|
case QUE_NODE_FETCH:
|
|
/* No need to do anything */
|
|
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"que_node struct appears corrupt; type %lu\n",
|
|
(unsigned long) que_node_get_type(node));
|
|
mem_analyze_corruption(node);
|
|
ut_error;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Frees a query graph. */
|
|
UNIV_INTERN
|
|
void
|
|
que_graph_free(
|
|
/*===========*/
|
|
que_t* graph) /*!< in: query graph; we assume that the memory
|
|
heap where this graph was created is private
|
|
to this graph: if not, then use
|
|
que_graph_free_recursive and free the heap
|
|
afterwards! */
|
|
{
|
|
ut_ad(graph);
|
|
|
|
if (graph->sym_tab) {
|
|
/* The following call frees dynamic memory allocated
|
|
for variables etc. during execution. Frees also explicit
|
|
cursor definitions. */
|
|
|
|
sym_tab_free_private(graph->sym_tab);
|
|
}
|
|
|
|
if (graph->info && graph->info->graph_owns_us) {
|
|
pars_info_free(graph->info);
|
|
}
|
|
|
|
que_graph_free_recursive(graph);
|
|
|
|
mem_heap_free(graph->heap);
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Performs an execution step on a thr node.
|
|
@return query thread to run next, or NULL if none */
|
|
static
|
|
que_thr_t*
|
|
que_thr_node_step(
|
|
/*==============*/
|
|
que_thr_t* thr) /*!< in: query thread where run_node must
|
|
be the thread node itself */
|
|
{
|
|
ut_ad(thr->run_node == thr);
|
|
|
|
if (thr->prev_node == thr->common.parent) {
|
|
/* If control to the node came from above, it is just passed
|
|
on */
|
|
|
|
thr->run_node = thr->child;
|
|
|
|
return(thr);
|
|
}
|
|
|
|
mutex_enter(&kernel_mutex);
|
|
|
|
if (que_thr_peek_stop(thr)) {
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
return(thr);
|
|
}
|
|
|
|
/* Thread execution completed */
|
|
|
|
thr->state = QUE_THR_COMPLETED;
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Moves a thread from another state to the QUE_THR_RUNNING state. Increments
|
|
the n_active_thrs counters of the query graph and transaction if thr was
|
|
not active.
|
|
***NOTE***: This and ..._mysql are the only functions in which such a
|
|
transition is allowed to happen! */
|
|
static
|
|
void
|
|
que_thr_move_to_run_state(
|
|
/*======================*/
|
|
que_thr_t* thr) /*!< in: an query thread */
|
|
{
|
|
trx_t* trx;
|
|
|
|
ut_ad(thr->state != QUE_THR_RUNNING);
|
|
|
|
trx = thr_get_trx(thr);
|
|
|
|
if (!thr->is_active) {
|
|
|
|
(thr->graph)->n_active_thrs++;
|
|
|
|
trx->n_active_thrs++;
|
|
|
|
thr->is_active = TRUE;
|
|
|
|
ut_ad((thr->graph)->n_active_thrs == 1);
|
|
ut_ad(trx->n_active_thrs == 1);
|
|
}
|
|
|
|
thr->state = QUE_THR_RUNNING;
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Decrements the query thread reference counts in the query graph and the
|
|
transaction. May start signal handling, e.g., a rollback.
|
|
*** NOTE ***:
|
|
This and que_thr_stop_for_mysql are the only functions where the reference
|
|
count can be decremented and this function may only be called from inside
|
|
que_run_threads or que_thr_check_if_switch! These restrictions exist to make
|
|
the rollback code easier to maintain. */
|
|
static
|
|
void
|
|
que_thr_dec_refer_count(
|
|
/*====================*/
|
|
que_thr_t* thr, /*!< in: query thread */
|
|
que_thr_t** next_thr) /*!< in/out: next query thread to run;
|
|
if the value which is passed in is
|
|
a pointer to a NULL pointer, then the
|
|
calling function can start running
|
|
a new query thread */
|
|
{
|
|
que_fork_t* fork;
|
|
trx_t* trx;
|
|
ulint fork_type;
|
|
ibool stopped;
|
|
|
|
fork = thr->common.parent;
|
|
trx = thr_get_trx(thr);
|
|
|
|
mutex_enter(&kernel_mutex);
|
|
|
|
ut_a(thr->is_active);
|
|
|
|
if (thr->state == QUE_THR_RUNNING) {
|
|
|
|
stopped = que_thr_stop(thr);
|
|
|
|
if (!stopped) {
|
|
/* The reason for the thr suspension or wait was
|
|
already canceled before we came here: continue
|
|
running the thread */
|
|
|
|
/* fputs("!!!!!!!! Wait already ended: continue thr\n",
|
|
stderr); */
|
|
|
|
if (next_thr && *next_thr == NULL) {
|
|
/* Normally srv_suspend_mysql_thread resets
|
|
the state to DB_SUCCESS before waiting, but
|
|
in this case we have to do it here,
|
|
otherwise nobody does it. */
|
|
trx->error_state = DB_SUCCESS;
|
|
|
|
*next_thr = thr;
|
|
} else {
|
|
ut_error;
|
|
srv_que_task_enqueue_low(thr);
|
|
}
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
ut_ad(fork->n_active_thrs == 1);
|
|
ut_ad(trx->n_active_thrs == 1);
|
|
|
|
fork->n_active_thrs--;
|
|
trx->n_active_thrs--;
|
|
|
|
thr->is_active = FALSE;
|
|
|
|
if (trx->n_active_thrs > 0) {
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
return;
|
|
}
|
|
|
|
fork_type = fork->fork_type;
|
|
|
|
/* Check if all query threads in the same fork are completed */
|
|
|
|
if (que_fork_all_thrs_in_state(fork, QUE_THR_COMPLETED)) {
|
|
|
|
switch (fork_type) {
|
|
case QUE_FORK_ROLLBACK:
|
|
/* This is really the undo graph used in rollback,
|
|
no roll_node in this graph */
|
|
|
|
ut_ad(UT_LIST_GET_LEN(trx->signals) > 0);
|
|
ut_ad(trx->handling_signals == TRUE);
|
|
|
|
trx_finish_rollback_off_kernel(fork, trx, next_thr);
|
|
break;
|
|
|
|
case QUE_FORK_PURGE:
|
|
case QUE_FORK_RECOVERY:
|
|
case QUE_FORK_MYSQL_INTERFACE:
|
|
|
|
/* Do nothing */
|
|
break;
|
|
|
|
default:
|
|
ut_error; /*!< not used in MySQL */
|
|
}
|
|
}
|
|
|
|
if (UT_LIST_GET_LEN(trx->signals) > 0 && trx->n_active_thrs == 0) {
|
|
|
|
/* If the trx is signaled and its query thread count drops to
|
|
zero, then we start processing a signal; from it we may get
|
|
a new query thread to run */
|
|
|
|
trx_sig_start_handle(trx, next_thr);
|
|
}
|
|
|
|
if (trx->handling_signals && UT_LIST_GET_LEN(trx->signals) == 0) {
|
|
|
|
trx_end_signal_handling(trx);
|
|
}
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Stops a query thread if graph or trx is in a state requiring it. The
|
|
conditions are tested in the order (1) graph, (2) trx. The kernel mutex has
|
|
to be reserved.
|
|
@return TRUE if stopped */
|
|
UNIV_INTERN
|
|
ibool
|
|
que_thr_stop(
|
|
/*=========*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
trx_t* trx;
|
|
que_t* graph;
|
|
ibool ret = TRUE;
|
|
|
|
ut_ad(mutex_own(&kernel_mutex));
|
|
|
|
graph = thr->graph;
|
|
trx = graph->trx;
|
|
|
|
if (graph->state == QUE_FORK_COMMAND_WAIT) {
|
|
thr->state = QUE_THR_SUSPENDED;
|
|
|
|
} else if (trx->que_state == TRX_QUE_LOCK_WAIT) {
|
|
|
|
UT_LIST_ADD_FIRST(trx_thrs, trx->wait_thrs, thr);
|
|
thr->state = QUE_THR_LOCK_WAIT;
|
|
|
|
} else if (trx->error_state != DB_SUCCESS
|
|
&& trx->error_state != DB_LOCK_WAIT) {
|
|
|
|
/* Error handling built for the MySQL interface */
|
|
thr->state = QUE_THR_COMPLETED;
|
|
|
|
} else if (UT_LIST_GET_LEN(trx->signals) > 0
|
|
&& graph->fork_type != QUE_FORK_ROLLBACK) {
|
|
|
|
thr->state = QUE_THR_SUSPENDED;
|
|
} else {
|
|
ut_ad(graph->state == QUE_FORK_ACTIVE);
|
|
|
|
ret = FALSE;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
A patch for MySQL used to 'stop' a dummy query thread used in MySQL. The
|
|
query thread is stopped and made inactive, except in the case where
|
|
it was put to the lock wait state in lock0lock.c, but the lock has already
|
|
been granted or the transaction chosen as a victim in deadlock resolution. */
|
|
UNIV_INTERN
|
|
void
|
|
que_thr_stop_for_mysql(
|
|
/*===================*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
trx_t* trx;
|
|
|
|
trx = thr_get_trx(thr);
|
|
|
|
mutex_enter(&kernel_mutex);
|
|
|
|
if (thr->state == QUE_THR_RUNNING) {
|
|
|
|
if (trx->error_state != DB_SUCCESS
|
|
&& trx->error_state != DB_LOCK_WAIT) {
|
|
|
|
/* Error handling built for the MySQL interface */
|
|
thr->state = QUE_THR_COMPLETED;
|
|
} else {
|
|
/* It must have been a lock wait but the lock was
|
|
already released, or this transaction was chosen
|
|
as a victim in selective deadlock resolution */
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
ut_ad(thr->is_active == TRUE);
|
|
ut_ad(trx->n_active_thrs == 1);
|
|
ut_ad(thr->graph->n_active_thrs == 1);
|
|
|
|
thr->is_active = FALSE;
|
|
(thr->graph)->n_active_thrs--;
|
|
|
|
trx->n_active_thrs--;
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Moves a thread from another state to the QUE_THR_RUNNING state. Increments
|
|
the n_active_thrs counters of the query graph and transaction if thr was
|
|
not active. */
|
|
UNIV_INTERN
|
|
void
|
|
que_thr_move_to_run_state_for_mysql(
|
|
/*================================*/
|
|
que_thr_t* thr, /*!< in: an query thread */
|
|
trx_t* trx) /*!< in: transaction */
|
|
{
|
|
if (thr->magic_n != QUE_THR_MAGIC_N) {
|
|
fprintf(stderr,
|
|
"que_thr struct appears corrupt; magic n %lu\n",
|
|
(unsigned long) thr->magic_n);
|
|
|
|
mem_analyze_corruption(thr);
|
|
|
|
ut_error;
|
|
}
|
|
|
|
if (!thr->is_active) {
|
|
|
|
thr->graph->n_active_thrs++;
|
|
|
|
trx->n_active_thrs++;
|
|
|
|
thr->is_active = TRUE;
|
|
}
|
|
|
|
thr->state = QUE_THR_RUNNING;
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
A patch for MySQL used to 'stop' a dummy query thread used in MySQL
|
|
select, when there is no error or lock wait. */
|
|
UNIV_INTERN
|
|
void
|
|
que_thr_stop_for_mysql_no_error(
|
|
/*============================*/
|
|
que_thr_t* thr, /*!< in: query thread */
|
|
trx_t* trx) /*!< in: transaction */
|
|
{
|
|
ut_ad(thr->state == QUE_THR_RUNNING);
|
|
ut_ad(thr->is_active == TRUE);
|
|
ut_ad(trx->n_active_thrs == 1);
|
|
ut_ad(thr->graph->n_active_thrs == 1);
|
|
|
|
if (thr->magic_n != QUE_THR_MAGIC_N) {
|
|
fprintf(stderr,
|
|
"que_thr struct appears corrupt; magic n %lu\n",
|
|
(unsigned long) thr->magic_n);
|
|
|
|
mem_analyze_corruption(thr);
|
|
|
|
ut_error;
|
|
}
|
|
|
|
thr->state = QUE_THR_COMPLETED;
|
|
|
|
thr->is_active = FALSE;
|
|
(thr->graph)->n_active_thrs--;
|
|
|
|
trx->n_active_thrs--;
|
|
}
|
|
|
|
/****************************************************************//**
|
|
Get the first containing loop node (e.g. while_node_t or for_node_t) for the
|
|
given node, or NULL if the node is not within a loop.
|
|
@return containing loop node, or NULL. */
|
|
UNIV_INTERN
|
|
que_node_t*
|
|
que_node_get_containing_loop_node(
|
|
/*==============================*/
|
|
que_node_t* node) /*!< in: node */
|
|
{
|
|
ut_ad(node);
|
|
|
|
for (;;) {
|
|
ulint type;
|
|
|
|
node = que_node_get_parent(node);
|
|
|
|
if (!node) {
|
|
break;
|
|
}
|
|
|
|
type = que_node_get_type(node);
|
|
|
|
if ((type == QUE_NODE_FOR) || (type == QUE_NODE_WHILE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(node);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Prints info of an SQL query graph node. */
|
|
UNIV_INTERN
|
|
void
|
|
que_node_print_info(
|
|
/*================*/
|
|
que_node_t* node) /*!< in: query graph node */
|
|
{
|
|
ulint type;
|
|
const char* str;
|
|
|
|
type = que_node_get_type(node);
|
|
|
|
if (type == QUE_NODE_SELECT) {
|
|
str = "SELECT";
|
|
} else if (type == QUE_NODE_INSERT) {
|
|
str = "INSERT";
|
|
} else if (type == QUE_NODE_UPDATE) {
|
|
str = "UPDATE";
|
|
} else if (type == QUE_NODE_WHILE) {
|
|
str = "WHILE";
|
|
} else if (type == QUE_NODE_ASSIGNMENT) {
|
|
str = "ASSIGNMENT";
|
|
} else if (type == QUE_NODE_IF) {
|
|
str = "IF";
|
|
} else if (type == QUE_NODE_FETCH) {
|
|
str = "FETCH";
|
|
} else if (type == QUE_NODE_OPEN) {
|
|
str = "OPEN";
|
|
} else if (type == QUE_NODE_PROC) {
|
|
str = "STORED PROCEDURE";
|
|
} else if (type == QUE_NODE_FUNC) {
|
|
str = "FUNCTION";
|
|
} else if (type == QUE_NODE_LOCK) {
|
|
str = "LOCK";
|
|
} else if (type == QUE_NODE_THR) {
|
|
str = "QUERY THREAD";
|
|
} else if (type == QUE_NODE_COMMIT) {
|
|
str = "COMMIT";
|
|
} else if (type == QUE_NODE_UNDO) {
|
|
str = "UNDO ROW";
|
|
} else if (type == QUE_NODE_PURGE) {
|
|
str = "PURGE ROW";
|
|
} else if (type == QUE_NODE_ROLLBACK) {
|
|
str = "ROLLBACK";
|
|
} else if (type == QUE_NODE_CREATE_TABLE) {
|
|
str = "CREATE TABLE";
|
|
} else if (type == QUE_NODE_CREATE_INDEX) {
|
|
str = "CREATE INDEX";
|
|
} else if (type == QUE_NODE_FOR) {
|
|
str = "FOR LOOP";
|
|
} else if (type == QUE_NODE_RETURN) {
|
|
str = "RETURN";
|
|
} else if (type == QUE_NODE_EXIT) {
|
|
str = "EXIT";
|
|
} else {
|
|
str = "UNKNOWN NODE TYPE";
|
|
}
|
|
|
|
fprintf(stderr, "Node type %lu: %s, address %p\n",
|
|
(ulong) type, str, (void*) node);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Performs an execution step on a query thread.
|
|
@return query thread to run next: it may differ from the input
|
|
parameter if, e.g., a subprocedure call is made */
|
|
UNIV_INLINE
|
|
que_thr_t*
|
|
que_thr_step(
|
|
/*=========*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
que_node_t* node;
|
|
que_thr_t* old_thr;
|
|
trx_t* trx;
|
|
ulint type;
|
|
|
|
trx = thr_get_trx(thr);
|
|
|
|
ut_ad(thr->state == QUE_THR_RUNNING);
|
|
ut_a(trx->error_state == DB_SUCCESS);
|
|
|
|
thr->resource++;
|
|
|
|
node = thr->run_node;
|
|
type = que_node_get_type(node);
|
|
|
|
old_thr = thr;
|
|
|
|
#ifdef UNIV_DEBUG
|
|
if (que_trace_on) {
|
|
fputs("To execute: ", stderr);
|
|
que_node_print_info(node);
|
|
}
|
|
#endif
|
|
if (type & QUE_NODE_CONTROL_STAT) {
|
|
if ((thr->prev_node != que_node_get_parent(node))
|
|
&& que_node_get_next(thr->prev_node)) {
|
|
|
|
/* The control statements, like WHILE, always pass the
|
|
control to the next child statement if there is any
|
|
child left */
|
|
|
|
thr->run_node = que_node_get_next(thr->prev_node);
|
|
|
|
} else if (type == QUE_NODE_IF) {
|
|
if_step(thr);
|
|
} else if (type == QUE_NODE_FOR) {
|
|
for_step(thr);
|
|
} else if (type == QUE_NODE_PROC) {
|
|
|
|
/* We can access trx->undo_no without reserving
|
|
trx->undo_mutex, because there cannot be active query
|
|
threads doing updating or inserting at the moment! */
|
|
|
|
if (thr->prev_node == que_node_get_parent(node)) {
|
|
trx->last_sql_stat_start.least_undo_no
|
|
= trx->undo_no;
|
|
}
|
|
|
|
proc_step(thr);
|
|
} else if (type == QUE_NODE_WHILE) {
|
|
while_step(thr);
|
|
} else {
|
|
ut_error;
|
|
}
|
|
} else if (type == QUE_NODE_ASSIGNMENT) {
|
|
assign_step(thr);
|
|
} else if (type == QUE_NODE_SELECT) {
|
|
thr = row_sel_step(thr);
|
|
} else if (type == QUE_NODE_INSERT) {
|
|
thr = row_ins_step(thr);
|
|
} else if (type == QUE_NODE_UPDATE) {
|
|
thr = row_upd_step(thr);
|
|
} else if (type == QUE_NODE_FETCH) {
|
|
thr = fetch_step(thr);
|
|
} else if (type == QUE_NODE_OPEN) {
|
|
thr = open_step(thr);
|
|
} else if (type == QUE_NODE_FUNC) {
|
|
proc_eval_step(thr);
|
|
|
|
} else if (type == QUE_NODE_LOCK) {
|
|
|
|
ut_error;
|
|
/*
|
|
thr = que_lock_step(thr);
|
|
*/
|
|
} else if (type == QUE_NODE_THR) {
|
|
thr = que_thr_node_step(thr);
|
|
} else if (type == QUE_NODE_COMMIT) {
|
|
thr = trx_commit_step(thr);
|
|
} else if (type == QUE_NODE_UNDO) {
|
|
thr = row_undo_step(thr);
|
|
} else if (type == QUE_NODE_PURGE) {
|
|
thr = row_purge_step(thr);
|
|
} else if (type == QUE_NODE_RETURN) {
|
|
thr = return_step(thr);
|
|
} else if (type == QUE_NODE_EXIT) {
|
|
thr = exit_step(thr);
|
|
} else if (type == QUE_NODE_ROLLBACK) {
|
|
thr = trx_rollback_step(thr);
|
|
} else if (type == QUE_NODE_CREATE_TABLE) {
|
|
thr = dict_create_table_step(thr);
|
|
} else if (type == QUE_NODE_CREATE_INDEX) {
|
|
thr = dict_create_index_step(thr);
|
|
} else if (type == QUE_NODE_ROW_PRINTF) {
|
|
thr = row_printf_step(thr);
|
|
} else {
|
|
ut_error;
|
|
}
|
|
|
|
if (type == QUE_NODE_EXIT) {
|
|
old_thr->prev_node = que_node_get_containing_loop_node(node);
|
|
} else {
|
|
old_thr->prev_node = node;
|
|
}
|
|
|
|
if (thr) {
|
|
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
|
|
}
|
|
|
|
return(thr);
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Run a query thread until it finishes or encounters e.g. a lock wait. */
|
|
static
|
|
void
|
|
que_run_threads_low(
|
|
/*================*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
que_thr_t* next_thr;
|
|
ulint cumul_resource;
|
|
ulint loop_count;
|
|
|
|
ut_ad(thr->state == QUE_THR_RUNNING);
|
|
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
|
|
ut_ad(!mutex_own(&kernel_mutex));
|
|
|
|
/* cumul_resource counts how much resources the OS thread (NOT the
|
|
query thread) has spent in this function */
|
|
|
|
loop_count = QUE_MAX_LOOPS_WITHOUT_CHECK;
|
|
cumul_resource = 0;
|
|
loop:
|
|
/* Check that there is enough space in the log to accommodate
|
|
possible log entries by this query step; if the operation can touch
|
|
more than about 4 pages, checks must be made also within the query
|
|
step! */
|
|
|
|
log_free_check();
|
|
|
|
/* Perform the actual query step: note that the query thread
|
|
may change if, e.g., a subprocedure call is made */
|
|
|
|
/*-------------------------*/
|
|
next_thr = que_thr_step(thr);
|
|
/*-------------------------*/
|
|
|
|
ut_a(!next_thr || (thr_get_trx(next_thr)->error_state == DB_SUCCESS));
|
|
|
|
loop_count++;
|
|
|
|
if (next_thr != thr) {
|
|
ut_a(next_thr == NULL);
|
|
|
|
/* This can change next_thr to a non-NULL value if there was
|
|
a lock wait that already completed. */
|
|
que_thr_dec_refer_count(thr, &next_thr);
|
|
|
|
if (next_thr == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
loop_count = QUE_MAX_LOOPS_WITHOUT_CHECK;
|
|
|
|
thr = next_thr;
|
|
}
|
|
|
|
goto loop;
|
|
}
|
|
|
|
/**********************************************************************//**
|
|
Run a query thread. Handles lock waits. */
|
|
UNIV_INTERN
|
|
void
|
|
que_run_threads(
|
|
/*============*/
|
|
que_thr_t* thr) /*!< in: query thread */
|
|
{
|
|
loop:
|
|
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
|
|
que_run_threads_low(thr);
|
|
|
|
mutex_enter(&kernel_mutex);
|
|
|
|
switch (thr->state) {
|
|
|
|
case QUE_THR_RUNNING:
|
|
/* There probably was a lock wait, but it already ended
|
|
before we came here: continue running thr */
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
goto loop;
|
|
|
|
case QUE_THR_LOCK_WAIT:
|
|
mutex_exit(&kernel_mutex);
|
|
|
|
/* The ..._mysql_... function works also for InnoDB's
|
|
internal threads. Let us wait that the lock wait ends. */
|
|
|
|
srv_suspend_mysql_thread(thr);
|
|
|
|
if (thr_get_trx(thr)->error_state != DB_SUCCESS) {
|
|
/* thr was chosen as a deadlock victim or there was
|
|
a lock wait timeout */
|
|
|
|
que_thr_dec_refer_count(thr, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
goto loop;
|
|
|
|
case QUE_THR_COMPLETED:
|
|
case QUE_THR_COMMAND_WAIT:
|
|
/* Do nothing */
|
|
break;
|
|
|
|
default:
|
|
ut_error;
|
|
}
|
|
|
|
mutex_exit(&kernel_mutex);
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
Evaluate the given SQL.
|
|
@return error code or DB_SUCCESS */
|
|
UNIV_INTERN
|
|
ulint
|
|
que_eval_sql(
|
|
/*=========*/
|
|
pars_info_t* info, /*!< in: info struct, or NULL */
|
|
const char* sql, /*!< in: SQL string */
|
|
ibool reserve_dict_mutex,
|
|
/*!< in: if TRUE, acquire/release
|
|
dict_sys->mutex around call to pars_sql. */
|
|
trx_t* trx) /*!< in: trx */
|
|
{
|
|
que_thr_t* thr;
|
|
que_t* graph;
|
|
|
|
ut_a(trx->error_state == DB_SUCCESS);
|
|
|
|
if (reserve_dict_mutex) {
|
|
mutex_enter(&dict_sys->mutex);
|
|
}
|
|
|
|
graph = pars_sql(info, sql);
|
|
|
|
if (reserve_dict_mutex) {
|
|
mutex_exit(&dict_sys->mutex);
|
|
}
|
|
|
|
ut_a(graph);
|
|
|
|
graph->trx = trx;
|
|
trx->graph = NULL;
|
|
|
|
graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
|
|
|
|
ut_a(thr = que_fork_start_command(graph));
|
|
|
|
que_run_threads(thr);
|
|
|
|
que_graph_free(graph);
|
|
|
|
return(trx->error_state);
|
|
}
|