You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

565 lines
15 KiB

17 years ago
17 years ago
17 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
  1. /*****************************************************************************
  2. Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
  3. This program is free software; you can redistribute it and/or modify it under
  4. the terms of the GNU General Public License as published by the Free Software
  5. Foundation; version 2 of the License.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along with
  10. this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
  12. *****************************************************************************/
  13. /**************************************************//**
  14. @file include/trx0sys.ic
  15. Transaction system
  16. Created 3/26/1996 Heikki Tuuri
  17. *******************************************************/
  18. #include "trx0trx.h"
  19. #include "data0type.h"
  20. #ifndef UNIV_HOTBACKUP
  21. # include "srv0srv.h"
  22. # include "mtr0log.h"
  23. /* The typedef for rseg slot in the file copy */
  24. typedef byte trx_sysf_rseg_t;
  25. /* Rollback segment specification slot offsets */
  26. /*-------------------------------------------------------------*/
  27. #define TRX_SYS_RSEG_SPACE 0 /* space where the segment
  28. header is placed; starting with
  29. MySQL/InnoDB 5.1.7, this is
  30. UNIV_UNDEFINED if the slot is unused */
  31. #define TRX_SYS_RSEG_PAGE_NO 4 /* page number where the segment
  32. header is placed; this is FIL_NULL
  33. if the slot is unused */
  34. /*-------------------------------------------------------------*/
  35. /* Size of a rollback segment specification slot */
  36. #define TRX_SYS_RSEG_SLOT_SIZE 8
  37. /*****************************************************************//**
  38. Writes the value of max_trx_id to the file based trx system header. */
  39. UNIV_INTERN
  40. void
  41. trx_sys_flush_max_trx_id(void);
  42. /*==========================*/
  43. /***************************************************************//**
  44. Checks if a page address is the trx sys header page.
  45. @return TRUE if trx sys header page */
  46. UNIV_INLINE
  47. ibool
  48. trx_sys_hdr_page(
  49. /*=============*/
  50. ulint space, /*!< in: space */
  51. ulint page_no)/*!< in: page number */
  52. {
  53. if ((space == TRX_SYS_SPACE) && (page_no == TRX_SYS_PAGE_NO)) {
  54. return(TRUE);
  55. }
  56. return(FALSE);
  57. }
  58. /***************************************************************//**
  59. Gets the pointer in the nth slot of the rseg array.
  60. @return pointer to rseg object, NULL if slot not in use */
  61. UNIV_INLINE
  62. trx_rseg_t*
  63. trx_sys_get_nth_rseg(
  64. /*=================*/
  65. trx_sys_t* sys, /*!< in: trx system */
  66. ulint n) /*!< in: index of slot */
  67. {
  68. ut_ad(n < TRX_SYS_N_RSEGS);
  69. return(sys->rseg_array[n]);
  70. }
  71. /**********************************************************************//**
  72. Gets a pointer to the transaction system header and x-latches its page.
  73. @return pointer to system header, page x-latched. */
  74. UNIV_INLINE
  75. trx_sysf_t*
  76. trx_sysf_get(
  77. /*=========*/
  78. mtr_t* mtr) /*!< in: mtr */
  79. {
  80. buf_block_t* block;
  81. trx_sysf_t* header;
  82. ut_ad(mtr);
  83. block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO,
  84. RW_X_LATCH, mtr);
  85. buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
  86. header = TRX_SYS + buf_block_get_frame(block);
  87. return(header);
  88. }
  89. /*****************************************************************//**
  90. Gets the space of the nth rollback segment slot in the trx system
  91. file copy.
  92. @return space id */
  93. UNIV_INLINE
  94. ulint
  95. trx_sysf_rseg_get_space(
  96. /*====================*/
  97. trx_sysf_t* sys_header, /*!< in: trx sys header */
  98. ulint i, /*!< in: slot index == rseg id */
  99. mtr_t* mtr) /*!< in: mtr */
  100. {
  101. ut_ad(sys_header);
  102. ut_ad(i < TRX_SYS_N_RSEGS);
  103. return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS
  104. + i * TRX_SYS_RSEG_SLOT_SIZE
  105. + TRX_SYS_RSEG_SPACE, MLOG_4BYTES, mtr));
  106. }
  107. /*****************************************************************//**
  108. Gets the page number of the nth rollback segment slot in the trx system
  109. header.
  110. @return page number, FIL_NULL if slot unused */
  111. UNIV_INLINE
  112. ulint
  113. trx_sysf_rseg_get_page_no(
  114. /*======================*/
  115. trx_sysf_t* sys_header, /*!< in: trx system header */
  116. ulint i, /*!< in: slot index == rseg id */
  117. mtr_t* mtr) /*!< in: mtr */
  118. {
  119. ut_ad(sys_header);
  120. ut_ad(i < TRX_SYS_N_RSEGS);
  121. return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS
  122. + i * TRX_SYS_RSEG_SLOT_SIZE
  123. + TRX_SYS_RSEG_PAGE_NO, MLOG_4BYTES, mtr));
  124. }
  125. /*****************************************************************//**
  126. Sets the space id of the nth rollback segment slot in the trx system
  127. file copy. */
  128. UNIV_INLINE
  129. void
  130. trx_sysf_rseg_set_space(
  131. /*====================*/
  132. trx_sysf_t* sys_header, /*!< in: trx sys file copy */
  133. ulint i, /*!< in: slot index == rseg id */
  134. ulint space, /*!< in: space id */
  135. mtr_t* mtr) /*!< in: mtr */
  136. {
  137. ut_ad(sys_header);
  138. ut_ad(i < TRX_SYS_N_RSEGS);
  139. mlog_write_ulint(sys_header + TRX_SYS_RSEGS
  140. + i * TRX_SYS_RSEG_SLOT_SIZE
  141. + TRX_SYS_RSEG_SPACE,
  142. space,
  143. MLOG_4BYTES, mtr);
  144. }
  145. /*****************************************************************//**
  146. Sets the page number of the nth rollback segment slot in the trx system
  147. header. */
  148. UNIV_INLINE
  149. void
  150. trx_sysf_rseg_set_page_no(
  151. /*======================*/
  152. trx_sysf_t* sys_header, /*!< in: trx sys header */
  153. ulint i, /*!< in: slot index == rseg id */
  154. ulint page_no, /*!< in: page number, FIL_NULL if the
  155. slot is reset to unused */
  156. mtr_t* mtr) /*!< in: mtr */
  157. {
  158. ut_ad(sys_header);
  159. ut_ad(i < TRX_SYS_N_RSEGS);
  160. mlog_write_ulint(sys_header + TRX_SYS_RSEGS
  161. + i * TRX_SYS_RSEG_SLOT_SIZE
  162. + TRX_SYS_RSEG_PAGE_NO,
  163. page_no,
  164. MLOG_4BYTES, mtr);
  165. }
  166. #endif /* !UNIV_HOTBACKUP */
  167. /*****************************************************************//**
  168. Writes a trx id to an index page. In case that the id size changes in
  169. some future version, this function should be used instead of
  170. mach_write_... */
  171. UNIV_INLINE
  172. void
  173. trx_write_trx_id(
  174. /*=============*/
  175. byte* ptr, /*!< in: pointer to memory where written */
  176. trx_id_t id) /*!< in: id */
  177. {
  178. #if DATA_TRX_ID_LEN != 6
  179. # error "DATA_TRX_ID_LEN != 6"
  180. #endif
  181. mach_write_to_6(ptr, id);
  182. }
  183. #ifndef UNIV_HOTBACKUP
  184. /*****************************************************************//**
  185. Reads a trx id from an index page. In case that the id size changes in
  186. some future version, this function should be used instead of
  187. mach_read_...
  188. @return id */
  189. UNIV_INLINE
  190. trx_id_t
  191. trx_read_trx_id(
  192. /*============*/
  193. const byte* ptr) /*!< in: pointer to memory from where to read */
  194. {
  195. #if DATA_TRX_ID_LEN != 6
  196. # error "DATA_TRX_ID_LEN != 6"
  197. #endif
  198. return(mach_read_from_6(ptr));
  199. }
  200. /****************************************************************//**
  201. Looks for the trx handle with the given id in rw_trx_list.
  202. The caller must be holding trx_sys->mutex.
  203. @return the trx handle or NULL if not found;
  204. the pointer must not be dereferenced unless lock_sys->mutex was
  205. acquired before calling this function and is still being held */
  206. UNIV_INLINE
  207. trx_t*
  208. trx_get_rw_trx_by_id(
  209. /*=================*/
  210. trx_id_t trx_id) /*!< in: trx id to search for */
  211. {
  212. trx_t* trx;
  213. ulint len;
  214. trx_t* first;
  215. ut_ad(mutex_own(&trx_sys->mutex));
  216. len = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
  217. if (len == 0) {
  218. return(NULL);
  219. }
  220. /* Because the list is ordered on trx id in descending order,
  221. we try to speed things up a bit. */
  222. trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
  223. assert_trx_in_rw_list(trx);
  224. if (trx_id == trx->id) {
  225. return(trx);
  226. } else if (len == 1 || trx_id > trx->id) {
  227. return(NULL);
  228. }
  229. first = trx;
  230. trx = UT_LIST_GET_LAST(trx_sys->rw_trx_list);
  231. assert_trx_in_rw_list(trx);
  232. if (trx_id == trx->id) {
  233. return(trx);
  234. } else if (len == 2 || trx_id < trx->id) {
  235. return(NULL);
  236. }
  237. /* Search the list from the lower end (tail). */
  238. if (trx_id < (first->id + trx->id) >> 1) {
  239. for (trx = UT_LIST_GET_PREV(trx_list, trx);
  240. trx != NULL && trx_id > trx->id;
  241. trx = UT_LIST_GET_PREV(trx_list, trx)) {
  242. assert_trx_in_rw_list(trx);
  243. }
  244. } else {
  245. for (trx = UT_LIST_GET_NEXT(trx_list, first);
  246. trx != NULL && trx_id < trx->id;
  247. trx = UT_LIST_GET_NEXT(trx_list, trx)) {
  248. assert_trx_in_rw_list(trx);
  249. }
  250. }
  251. return((trx != NULL && trx->id == trx_id) ? trx : NULL);
  252. }
  253. /****************************************************************//**
  254. Returns the minimum trx id in trx list. This is the smallest id for which
  255. the trx can possibly be active. (But, you must look at the trx->state
  256. to find out if the minimum trx id transaction itself is active, or already
  257. committed.). The caller must be holding the trx_sys_t::mutex in shared mode.
  258. @return the minimum trx id, or trx_sys->max_trx_id if the trx list is empty */
  259. UNIV_INLINE
  260. trx_id_t
  261. trx_rw_min_trx_id_low(void)
  262. /*=======================*/
  263. {
  264. trx_id_t id;
  265. const trx_t* trx;
  266. ut_ad(mutex_own(&trx_sys->mutex));
  267. trx = UT_LIST_GET_LAST(trx_sys->rw_trx_list);
  268. if (trx == NULL) {
  269. id = trx_sys->max_trx_id;
  270. } else {
  271. assert_trx_in_rw_list(trx);
  272. id = trx->id;
  273. }
  274. return(id);
  275. }
  276. #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
  277. /***********************************************************//**
  278. Assert that a transaction has been recovered.
  279. @return TRUE */
  280. UNIV_INLINE
  281. ibool
  282. trx_assert_recovered(
  283. /*=================*/
  284. trx_id_t trx_id) /*!< in: transaction identifier */
  285. {
  286. const trx_t* trx;
  287. mutex_enter(&trx_sys->mutex);
  288. trx = trx_get_rw_trx_by_id(trx_id);
  289. ut_a(trx->is_recovered);
  290. mutex_exit(&trx_sys->mutex);
  291. return(TRUE);
  292. }
  293. #endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
  294. /****************************************************************//**
  295. Returns the minimum trx id in rw trx list. This is the smallest id for which
  296. the rw trx can possibly be active. (But, you must look at the trx->state
  297. to find out if the minimum trx id transaction itself is active, or already
  298. committed.)
  299. @return the minimum trx id, or trx_sys->max_trx_id if rw trx list is empty */
  300. UNIV_INLINE
  301. trx_id_t
  302. trx_rw_min_trx_id(void)
  303. /*===================*/
  304. {
  305. trx_id_t id;
  306. mutex_enter(&trx_sys->mutex);
  307. id = trx_rw_min_trx_id_low();
  308. mutex_exit(&trx_sys->mutex);
  309. return(id);
  310. }
  311. /****************************************************************//**
  312. Returns pointer to a transaction instance if a rw transaction with the given id
  313. is active. Caller must hold trx_sys->mutex. If the caller is not holding
  314. lock_sys->mutex, the transaction may already have been committed.
  315. @return transaction instance if active, or NULL;
  316. the pointer must not be dereferenced unless lock_sys->mutex was
  317. acquired before calling this function and is still being held */
  318. UNIV_INLINE
  319. trx_t*
  320. trx_rw_get_active_trx_by_id(
  321. /*========================*/
  322. trx_id_t trx_id, /*!< in: trx id of the transaction */
  323. ibool* corrupt) /*!< in: NULL or pointer to a flag
  324. that will be set if corrupt */
  325. {
  326. trx_t* trx;
  327. ut_ad(mutex_own(&trx_sys->mutex));
  328. if (trx_id < trx_rw_min_trx_id_low()) {
  329. trx = NULL;
  330. } else if (trx_id >= trx_sys->max_trx_id) {
  331. /* There must be corruption: we let the caller handle the
  332. diagnostic prints in this case. */
  333. trx = NULL;
  334. if (corrupt != NULL) {
  335. *corrupt = TRUE;
  336. }
  337. } else {
  338. trx = trx_get_rw_trx_by_id(trx_id);
  339. if (trx != NULL
  340. && trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) {
  341. trx = NULL;
  342. }
  343. }
  344. return(trx);
  345. }
  346. /****************************************************************//**
  347. Checks if a rw transaction with the given id is active. Caller must hold
  348. trx_sys->mutex. If the caller is not holding lock_sys->mutex, the
  349. transaction may already have been committed.
  350. @return true if rw transaction it with a given id is active. */
  351. UNIV_INLINE
  352. bool
  353. trx_rw_is_active_low(
  354. /*=================*/
  355. trx_id_t trx_id, /*!< in: trx id of the transaction */
  356. ibool* corrupt) /*!< in: NULL or pointer to a flag
  357. that will be set if corrupt */
  358. {
  359. ut_ad(mutex_own(&trx_sys->mutex));
  360. if (UNIV_UNLIKELY(trx_id >= trx_sys->max_trx_id)) {
  361. /* There must be corruption: we let the caller handle the
  362. diagnostic prints in this case. */
  363. if (corrupt != NULL) {
  364. *corrupt = TRUE;
  365. }
  366. return(false);
  367. }
  368. return(trx_find_descriptor(trx_sys->descriptors, trx_sys->descr_n_used,
  369. trx_id) != NULL);
  370. }
  371. /****************************************************************//**
  372. Checks if a rw transaction with the given id is active. If the caller is
  373. not holding lock_sys->mutex, the transaction may already have been
  374. committed.
  375. @return true if rw transaction it with a given id is active. */
  376. UNIV_INLINE
  377. bool
  378. trx_rw_is_active(
  379. /*=============*/
  380. trx_id_t trx_id, /*!< in: trx id of the transaction */
  381. ibool* corrupt) /*!< in: NULL or pointer to a flag
  382. that will be set if corrupt */
  383. {
  384. bool res;
  385. mutex_enter(&trx_sys->mutex);
  386. res = trx_rw_is_active_low(trx_id, corrupt);
  387. mutex_exit(&trx_sys->mutex);
  388. return(res);
  389. }
  390. /*****************************************************************//**
  391. Allocates a new transaction id.
  392. @return new, allocated trx id */
  393. UNIV_INLINE
  394. trx_id_t
  395. trx_sys_get_new_trx_id(void)
  396. /*========================*/
  397. {
  398. ut_ad(mutex_own(&trx_sys->mutex));
  399. /* VERY important: after the database is started, max_trx_id value is
  400. divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if
  401. will evaluate to TRUE when this function is first time called,
  402. and the value for trx id will be written to disk-based header!
  403. Thus trx id values will not overlap when the database is
  404. repeatedly started! */
  405. if (!(trx_sys->max_trx_id % (trx_id_t) TRX_SYS_TRX_ID_WRITE_MARGIN)) {
  406. trx_sys_flush_max_trx_id();
  407. }
  408. return(trx_sys->max_trx_id++);
  409. }
  410. /*****************************************************************//**
  411. Determines the maximum transaction id.
  412. @return maximum currently allocated trx id; will be stale after the
  413. next call to trx_sys_get_new_trx_id() */
  414. UNIV_INLINE
  415. trx_id_t
  416. trx_sys_get_max_trx_id(void)
  417. /*========================*/
  418. {
  419. #if UNIV_WORD_SIZE < DATA_TRX_ID_LEN
  420. trx_id_t max_trx_id;
  421. #endif
  422. ut_ad(!mutex_own(&trx_sys->mutex));
  423. #if UNIV_WORD_SIZE < DATA_TRX_ID_LEN
  424. /* Avoid torn reads. */
  425. mutex_enter(&trx_sys->mutex);
  426. max_trx_id = trx_sys->max_trx_id;
  427. mutex_exit(&trx_sys->mutex);
  428. return(max_trx_id);
  429. #else
  430. /* Perform a dirty read. Callers should be prepared for stale
  431. values, and we know that the value fits in a machine word, so
  432. that it will be read and written atomically. */
  433. return(trx_sys->max_trx_id);
  434. #endif
  435. }
  436. /*****************************************************************//**
  437. Get the number of transaction in the system, independent of their state.
  438. @return count of transactions in trx_sys_t::rw_trx_list */
  439. UNIV_INLINE
  440. ulint
  441. trx_sys_get_n_rw_trx(void)
  442. /*======================*/
  443. {
  444. ulint n_trx;
  445. mutex_enter(&trx_sys->mutex);
  446. n_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
  447. mutex_exit(&trx_sys->mutex);
  448. return(n_trx);
  449. }
  450. /*************************************************************//**
  451. Find a slot for a given trx ID in a descriptors array.
  452. @return: slot pointer */
  453. UNIV_INLINE
  454. trx_id_t*
  455. trx_find_descriptor(
  456. /*================*/
  457. const trx_id_t* descriptors, /*!< in: descriptors array */
  458. ulint n_descr, /*!< in: array size */
  459. trx_id_t trx_id) /*!< in: trx id */
  460. {
  461. ut_ad(descriptors != trx_sys->descriptors ||
  462. mutex_own(&trx_sys->mutex));
  463. if (UNIV_UNLIKELY(n_descr == 0)) {
  464. return(NULL);
  465. }
  466. return((trx_id_t *) bsearch(&trx_id, descriptors, n_descr,
  467. sizeof(trx_id_t), trx_descr_cmp));
  468. }
  469. #endif /* !UNIV_HOTBACKUP */