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.

588 lines
17 KiB

20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
  1. /*****************************************************************************
  2. Copyright (c) 1996, 2009, Innobase Oy. 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., 59 Temple
  11. Place, Suite 330, Boston, MA 02111-1307 USA
  12. *****************************************************************************/
  13. /**************************************************//**
  14. @file btr/btr0pcur.c
  15. The index tree persistent cursor
  16. Created 2/23/1996 Heikki Tuuri
  17. *******************************************************/
  18. #include "btr0pcur.h"
  19. #ifdef UNIV_NONINL
  20. #include "btr0pcur.ic"
  21. #endif
  22. #include "ut0byte.h"
  23. #include "rem0cmp.h"
  24. #include "trx0trx.h"
  25. /**************************************************************//**
  26. Allocates memory for a persistent cursor object and initializes the cursor.
  27. @return own: persistent cursor */
  28. UNIV_INTERN
  29. btr_pcur_t*
  30. btr_pcur_create_for_mysql(void)
  31. /*============================*/
  32. {
  33. btr_pcur_t* pcur;
  34. pcur = mem_alloc(sizeof(btr_pcur_t));
  35. pcur->btr_cur.index = NULL;
  36. btr_pcur_init(pcur);
  37. return(pcur);
  38. }
  39. /**************************************************************//**
  40. Frees the memory for a persistent cursor object. */
  41. UNIV_INTERN
  42. void
  43. btr_pcur_free_for_mysql(
  44. /*====================*/
  45. btr_pcur_t* cursor) /*!< in, own: persistent cursor */
  46. {
  47. if (cursor->old_rec_buf != NULL) {
  48. mem_free(cursor->old_rec_buf);
  49. cursor->old_rec_buf = NULL;
  50. }
  51. cursor->btr_cur.page_cur.rec = NULL;
  52. cursor->old_rec = NULL;
  53. cursor->old_n_fields = 0;
  54. cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
  55. cursor->latch_mode = BTR_NO_LATCHES;
  56. cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
  57. mem_free(cursor);
  58. }
  59. /**************************************************************//**
  60. The position of the cursor is stored by taking an initial segment of the
  61. record the cursor is positioned on, before, or after, and copying it to the
  62. cursor data structure, or just setting a flag if the cursor id before the
  63. first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
  64. page where the cursor is positioned must not be empty if the index tree is
  65. not totally empty! */
  66. UNIV_INTERN
  67. void
  68. btr_pcur_store_position(
  69. /*====================*/
  70. btr_pcur_t* cursor, /*!< in: persistent cursor */
  71. mtr_t* mtr) /*!< in: mtr */
  72. {
  73. page_cur_t* page_cursor;
  74. buf_block_t* block;
  75. rec_t* rec;
  76. dict_index_t* index;
  77. page_t* page;
  78. ulint offs;
  79. ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
  80. ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
  81. block = btr_pcur_get_block(cursor);
  82. index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
  83. page_cursor = btr_pcur_get_page_cur(cursor);
  84. rec = page_cur_get_rec(page_cursor);
  85. page = page_align(rec);
  86. offs = page_offset(rec);
  87. ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
  88. || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
  89. ut_a(cursor->latch_mode != BTR_NO_LATCHES);
  90. if (UNIV_UNLIKELY(page_get_n_recs(page) == 0)) {
  91. /* It must be an empty index tree; NOTE that in this case
  92. we do not store the modify_clock, but always do a search
  93. if we restore the cursor position */
  94. ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
  95. ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
  96. cursor->old_stored = BTR_PCUR_OLD_STORED;
  97. if (page_rec_is_supremum_low(offs)) {
  98. cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
  99. } else {
  100. cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
  101. }
  102. return;
  103. }
  104. if (page_rec_is_supremum_low(offs)) {
  105. rec = page_rec_get_prev(rec);
  106. cursor->rel_pos = BTR_PCUR_AFTER;
  107. } else if (page_rec_is_infimum_low(offs)) {
  108. rec = page_rec_get_next(rec);
  109. cursor->rel_pos = BTR_PCUR_BEFORE;
  110. } else {
  111. cursor->rel_pos = BTR_PCUR_ON;
  112. }
  113. cursor->old_stored = BTR_PCUR_OLD_STORED;
  114. cursor->old_rec = dict_index_copy_rec_order_prefix(
  115. index, rec, &cursor->old_n_fields,
  116. &cursor->old_rec_buf, &cursor->buf_size);
  117. cursor->block_when_stored = block;
  118. cursor->modify_clock = buf_block_get_modify_clock(block);
  119. }
  120. /**************************************************************//**
  121. Copies the stored position of a pcur to another pcur. */
  122. UNIV_INTERN
  123. void
  124. btr_pcur_copy_stored_position(
  125. /*==========================*/
  126. btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the
  127. position info */
  128. btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is
  129. copied */
  130. {
  131. if (pcur_receive->old_rec_buf) {
  132. mem_free(pcur_receive->old_rec_buf);
  133. }
  134. ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
  135. if (pcur_donate->old_rec_buf) {
  136. pcur_receive->old_rec_buf = mem_alloc(pcur_donate->buf_size);
  137. ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
  138. pcur_donate->buf_size);
  139. pcur_receive->old_rec = pcur_receive->old_rec_buf
  140. + (pcur_donate->old_rec - pcur_donate->old_rec_buf);
  141. }
  142. pcur_receive->old_n_fields = pcur_donate->old_n_fields;
  143. }
  144. /**************************************************************//**
  145. Restores the stored position of a persistent cursor bufferfixing the page and
  146. obtaining the specified latches. If the cursor position was saved when the
  147. (1) cursor was positioned on a user record: this function restores the position
  148. to the last record LESS OR EQUAL to the stored record;
  149. (2) cursor was positioned on a page infimum record: restores the position to
  150. the last record LESS than the user record which was the successor of the page
  151. infimum;
  152. (3) cursor was positioned on the page supremum: restores to the first record
  153. GREATER than the user record which was the predecessor of the supremum.
  154. (4) cursor was positioned before the first or after the last in an empty tree:
  155. restores to before first or after the last in the tree.
  156. @return TRUE if the cursor position was stored when it was on a user
  157. record and it can be restored on a user record whose ordering fields
  158. are identical to the ones of the original user record */
  159. UNIV_INTERN
  160. ibool
  161. btr_pcur_restore_position_func(
  162. /*===========================*/
  163. ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */
  164. btr_pcur_t* cursor, /*!< in: detached persistent cursor */
  165. const char* file, /*!< in: file name */
  166. ulint line, /*!< in: line where called */
  167. mtr_t* mtr) /*!< in: mtr */
  168. {
  169. dict_index_t* index;
  170. dtuple_t* tuple;
  171. ulint mode;
  172. ulint old_mode;
  173. mem_heap_t* heap;
  174. index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
  175. if (UNIV_UNLIKELY(cursor->old_stored != BTR_PCUR_OLD_STORED)
  176. || UNIV_UNLIKELY(cursor->pos_state != BTR_PCUR_WAS_POSITIONED
  177. && cursor->pos_state != BTR_PCUR_IS_POSITIONED)) {
  178. ut_print_buf(stderr, cursor, sizeof(btr_pcur_t));
  179. putc('\n', stderr);
  180. if (cursor->trx_if_known) {
  181. trx_print(stderr, cursor->trx_if_known, 0);
  182. }
  183. ut_error;
  184. }
  185. if (UNIV_UNLIKELY
  186. (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
  187. || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
  188. /* In these cases we do not try an optimistic restoration,
  189. but always do a search */
  190. btr_cur_open_at_index_side(
  191. cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
  192. index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr);
  193. cursor->block_when_stored = btr_pcur_get_block(cursor);
  194. return(FALSE);
  195. }
  196. ut_a(cursor->old_rec);
  197. ut_a(cursor->old_n_fields);
  198. if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
  199. || UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
  200. /* Try optimistic restoration */
  201. if (UNIV_LIKELY(buf_page_optimistic_get(
  202. latch_mode,
  203. cursor->block_when_stored,
  204. cursor->modify_clock,
  205. file, line, mtr))) {
  206. cursor->pos_state = BTR_PCUR_IS_POSITIONED;
  207. buf_block_dbg_add_level(btr_pcur_get_block(cursor),
  208. SYNC_TREE_NODE);
  209. if (cursor->rel_pos == BTR_PCUR_ON) {
  210. #ifdef UNIV_DEBUG
  211. const rec_t* rec;
  212. const ulint* offsets1;
  213. const ulint* offsets2;
  214. #endif /* UNIV_DEBUG */
  215. cursor->latch_mode = latch_mode;
  216. #ifdef UNIV_DEBUG
  217. rec = btr_pcur_get_rec(cursor);
  218. heap = mem_heap_create(256);
  219. offsets1 = rec_get_offsets(
  220. cursor->old_rec, index, NULL,
  221. cursor->old_n_fields, &heap);
  222. offsets2 = rec_get_offsets(
  223. rec, index, NULL,
  224. cursor->old_n_fields, &heap);
  225. ut_ad(!cmp_rec_rec(cursor->old_rec,
  226. rec, offsets1, offsets2,
  227. index));
  228. mem_heap_free(heap);
  229. #endif /* UNIV_DEBUG */
  230. return(TRUE);
  231. }
  232. return(FALSE);
  233. }
  234. }
  235. /* If optimistic restoration did not succeed, open the cursor anew */
  236. heap = mem_heap_create(256);
  237. tuple = dict_index_build_data_tuple(index, cursor->old_rec,
  238. cursor->old_n_fields, heap);
  239. /* Save the old search mode of the cursor */
  240. old_mode = cursor->search_mode;
  241. if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) {
  242. mode = PAGE_CUR_LE;
  243. } else if (cursor->rel_pos == BTR_PCUR_AFTER) {
  244. mode = PAGE_CUR_G;
  245. } else {
  246. ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE);
  247. mode = PAGE_CUR_L;
  248. }
  249. btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
  250. cursor, 0, file, line, mtr);
  251. /* Restore the old search mode */
  252. cursor->search_mode = old_mode;
  253. if (cursor->rel_pos == BTR_PCUR_ON
  254. && btr_pcur_is_on_user_rec(cursor)
  255. && 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
  256. rec_get_offsets(
  257. btr_pcur_get_rec(cursor), index,
  258. NULL, ULINT_UNDEFINED, &heap))) {
  259. /* We have to store the NEW value for the modify clock, since
  260. the cursor can now be on a different page! But we can retain
  261. the value of old_rec */
  262. cursor->block_when_stored = btr_pcur_get_block(cursor);
  263. cursor->modify_clock = buf_block_get_modify_clock(
  264. cursor->block_when_stored);
  265. cursor->old_stored = BTR_PCUR_OLD_STORED;
  266. mem_heap_free(heap);
  267. return(TRUE);
  268. }
  269. mem_heap_free(heap);
  270. /* We have to store new position information, modify_clock etc.,
  271. to the cursor because it can now be on a different page, the record
  272. under it may have been removed, etc. */
  273. btr_pcur_store_position(cursor, mtr);
  274. return(FALSE);
  275. }
  276. /**************************************************************//**
  277. If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY,
  278. releases the page latch and bufferfix reserved by the cursor.
  279. NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes
  280. made by the current mini-transaction to the data protected by the
  281. cursor latch, as then the latch must not be released until mtr_commit. */
  282. UNIV_INTERN
  283. void
  284. btr_pcur_release_leaf(
  285. /*==================*/
  286. btr_pcur_t* cursor, /*!< in: persistent cursor */
  287. mtr_t* mtr) /*!< in: mtr */
  288. {
  289. buf_block_t* block;
  290. ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
  291. ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
  292. block = btr_pcur_get_block(cursor);
  293. btr_leaf_page_release(block, cursor->latch_mode, mtr);
  294. cursor->latch_mode = BTR_NO_LATCHES;
  295. cursor->pos_state = BTR_PCUR_WAS_POSITIONED;
  296. }
  297. /*********************************************************//**
  298. Moves the persistent cursor to the first record on the next page. Releases the
  299. latch on the current page, and bufferunfixes it. Note that there must not be
  300. modifications on the current page, as then the x-latch can be released only in
  301. mtr_commit. */
  302. UNIV_INTERN
  303. void
  304. btr_pcur_move_to_next_page(
  305. /*=======================*/
  306. btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the
  307. last record of the current page */
  308. mtr_t* mtr) /*!< in: mtr */
  309. {
  310. ulint next_page_no;
  311. ulint space;
  312. ulint zip_size;
  313. page_t* page;
  314. buf_block_t* next_block;
  315. page_t* next_page;
  316. ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
  317. ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
  318. ut_ad(btr_pcur_is_after_last_on_page(cursor));
  319. cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
  320. page = btr_pcur_get_page(cursor);
  321. next_page_no = btr_page_get_next(page, mtr);
  322. space = buf_block_get_space(btr_pcur_get_block(cursor));
  323. zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor));
  324. ut_ad(next_page_no != FIL_NULL);
  325. next_block = btr_block_get(space, zip_size, next_page_no,
  326. cursor->latch_mode, mtr);
  327. next_page = buf_block_get_frame(next_block);
  328. #ifdef UNIV_BTR_DEBUG
  329. ut_a(page_is_comp(next_page) == page_is_comp(page));
  330. ut_a(btr_page_get_prev(next_page, mtr)
  331. == buf_block_get_page_no(btr_pcur_get_block(cursor)));
  332. #endif /* UNIV_BTR_DEBUG */
  333. next_block->check_index_page_at_flush = TRUE;
  334. btr_leaf_page_release(btr_pcur_get_block(cursor),
  335. cursor->latch_mode, mtr);
  336. page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
  337. page_check_dir(next_page);
  338. }
  339. /*********************************************************//**
  340. Moves the persistent cursor backward if it is on the first record of the page.
  341. Commits mtr. Note that to prevent a possible deadlock, the operation
  342. first stores the position of the cursor, commits mtr, acquires the necessary
  343. latches and restores the cursor position again before returning. The
  344. alphabetical position of the cursor is guaranteed to be sensible on
  345. return, but it may happen that the cursor is not positioned on the last
  346. record of any page, because the structure of the tree may have changed
  347. during the time when the cursor had no latches. */
  348. UNIV_INTERN
  349. void
  350. btr_pcur_move_backward_from_page(
  351. /*=============================*/
  352. btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first
  353. record of the current page */
  354. mtr_t* mtr) /*!< in: mtr */
  355. {
  356. ulint prev_page_no;
  357. ulint space;
  358. page_t* page;
  359. buf_block_t* prev_block;
  360. ulint latch_mode;
  361. ulint latch_mode2;
  362. ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
  363. ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
  364. ut_ad(btr_pcur_is_before_first_on_page(cursor));
  365. ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
  366. latch_mode = cursor->latch_mode;
  367. if (latch_mode == BTR_SEARCH_LEAF) {
  368. latch_mode2 = BTR_SEARCH_PREV;
  369. } else if (latch_mode == BTR_MODIFY_LEAF) {
  370. latch_mode2 = BTR_MODIFY_PREV;
  371. } else {
  372. latch_mode2 = 0; /* To eliminate compiler warning */
  373. ut_error;
  374. }
  375. btr_pcur_store_position(cursor, mtr);
  376. mtr_commit(mtr);
  377. mtr_start(mtr);
  378. btr_pcur_restore_position(latch_mode2, cursor, mtr);
  379. page = btr_pcur_get_page(cursor);
  380. prev_page_no = btr_page_get_prev(page, mtr);
  381. space = buf_block_get_space(btr_pcur_get_block(cursor));
  382. if (prev_page_no == FIL_NULL) {
  383. } else if (btr_pcur_is_before_first_on_page(cursor)) {
  384. prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
  385. btr_leaf_page_release(btr_pcur_get_block(cursor),
  386. latch_mode, mtr);
  387. page_cur_set_after_last(prev_block,
  388. btr_pcur_get_page_cur(cursor));
  389. } else {
  390. /* The repositioned cursor did not end on an infimum record on
  391. a page. Cursor repositioning acquired a latch also on the
  392. previous page, but we do not need the latch: release it. */
  393. prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
  394. btr_leaf_page_release(prev_block, latch_mode, mtr);
  395. }
  396. cursor->latch_mode = latch_mode;
  397. cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
  398. }
  399. /*********************************************************//**
  400. Moves the persistent cursor to the previous record in the tree. If no records
  401. are left, the cursor stays 'before first in tree'.
  402. @return TRUE if the cursor was not before first in tree */
  403. UNIV_INTERN
  404. ibool
  405. btr_pcur_move_to_prev(
  406. /*==================*/
  407. btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the
  408. function may release the page latch */
  409. mtr_t* mtr) /*!< in: mtr */
  410. {
  411. ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
  412. ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
  413. cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
  414. if (btr_pcur_is_before_first_on_page(cursor)) {
  415. if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
  416. return(FALSE);
  417. }
  418. btr_pcur_move_backward_from_page(cursor, mtr);
  419. return(TRUE);
  420. }
  421. btr_pcur_move_to_prev_on_page(cursor);
  422. return(TRUE);
  423. }
  424. /**************************************************************//**
  425. If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
  426. user record satisfying the search condition, in the case PAGE_CUR_L or
  427. PAGE_CUR_LE, on the last user record. If no such user record exists, then
  428. in the first case sets the cursor after last in tree, and in the latter case
  429. before first in tree. The latching mode must be BTR_SEARCH_LEAF or
  430. BTR_MODIFY_LEAF. */
  431. UNIV_INTERN
  432. void
  433. btr_pcur_open_on_user_rec_func(
  434. /*===========================*/
  435. dict_index_t* index, /*!< in: index */
  436. const dtuple_t* tuple, /*!< in: tuple on which search done */
  437. ulint mode, /*!< in: PAGE_CUR_L, ... */
  438. ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or
  439. BTR_MODIFY_LEAF */
  440. btr_pcur_t* cursor, /*!< in: memory buffer for persistent
  441. cursor */
  442. const char* file, /*!< in: file name */
  443. ulint line, /*!< in: line where called */
  444. mtr_t* mtr) /*!< in: mtr */
  445. {
  446. btr_pcur_open_func(index, tuple, mode, latch_mode, cursor,
  447. file, line, mtr);
  448. if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
  449. if (btr_pcur_is_after_last_on_page(cursor)) {
  450. btr_pcur_move_to_next_user_rec(cursor, mtr);
  451. }
  452. } else {
  453. ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
  454. /* Not implemented yet */
  455. ut_error;
  456. }
  457. }