log0recv.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:62k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. Recovery
  3. (c) 1997 Innobase Oy
  4. Created 9/20/1997 Heikki Tuuri
  5. *******************************************************/
  6. #include "log0recv.h"
  7. #ifdef UNIV_NONINL
  8. #include "log0recv.ic"
  9. #endif
  10. #include "mem0mem.h"
  11. #include "buf0buf.h"
  12. #include "buf0flu.h"
  13. #include "buf0rea.h"
  14. #include "srv0srv.h"
  15. #include "mtr0mtr.h"
  16. #include "mtr0log.h"
  17. #include "page0page.h"
  18. #include "page0cur.h"
  19. #include "btr0btr.h"
  20. #include "btr0cur.h"
  21. #include "ibuf0ibuf.h"
  22. #include "trx0undo.h"
  23. #include "trx0rec.h"
  24. #include "trx0roll.h"
  25. #include "btr0cur.h"
  26. #include "btr0cur.h"
  27. #include "btr0cur.h"
  28. #include "dict0boot.h"
  29. #include "fil0fil.h"
  30. /* Size of block reads when the log groups are scanned forward to do a
  31. roll-forward */
  32. #define RECV_SCAN_SIZE (4 * UNIV_PAGE_SIZE)
  33. /* Size of the parsing buffer */
  34. #define RECV_PARSING_BUF_SIZE LOG_BUFFER_SIZE
  35. /* Log records are stored in the hash table in chunks at most of this size;
  36. this must be less than UNIV_PAGE_SIZE as it is stored in the buffer pool */
  37. #define RECV_DATA_BLOCK_SIZE (MEM_MAX_ALLOC_IN_BUF - sizeof(recv_data_t))
  38. /* Read-ahead area in applying log records to file pages */
  39. #define RECV_READ_AHEAD_AREA 32
  40. recv_sys_t* recv_sys = NULL;
  41. ibool recv_recovery_on = FALSE;
  42. ibool recv_recovery_from_backup_on = FALSE;
  43. /* If the following is TRUE, the buffer pool file pages must be invalidated
  44. after recovery and no ibuf operations are allowed; this becomes TRUE if
  45. the log record hash table becomes too full, and log records must be merged
  46. to file pages already before the recovery is finished: in this case no
  47. ibuf operations are allowed, as they could modify the pages read in the
  48. buffer pool before the pages have been recovered to the up-to-date state */
  49. /* Recovery is running and no operations on the log files are allowed
  50. yet: the variable name is misleading */
  51. ibool recv_no_ibuf_operations = FALSE;
  52. /************************************************************
  53. Creates the recovery system. */
  54. void
  55. recv_sys_create(void)
  56. /*=================*/
  57. {
  58. if (recv_sys != NULL) {
  59. return;
  60. }
  61. recv_sys = mem_alloc(sizeof(recv_sys_t));
  62. mutex_create(&(recv_sys->mutex));
  63. mutex_set_level(&(recv_sys->mutex), SYNC_RECV);
  64. recv_sys->heap = NULL;
  65. recv_sys->addr_hash = NULL;
  66. }
  67. /************************************************************
  68. Inits the recovery system for a recovery operation. */
  69. void
  70. recv_sys_init(void)
  71. /*===============*/
  72. {
  73. if (recv_sys->heap != NULL) {
  74. return;
  75. }
  76. mutex_enter(&(recv_sys->mutex));
  77. recv_sys->heap = mem_heap_create_in_buffer(256);
  78. recv_sys->buf = ut_malloc(RECV_PARSING_BUF_SIZE);
  79. recv_sys->len = 0;
  80. recv_sys->recovered_offset = 0;
  81. recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 64);
  82. recv_sys->n_addrs = 0;
  83. recv_sys->apply_log_recs = FALSE;
  84. recv_sys->apply_batch_on = FALSE;
  85. recv_sys->last_block_buf_start = mem_alloc(2 * OS_FILE_LOG_BLOCK_SIZE);
  86. recv_sys->last_block = ut_align(recv_sys->last_block_buf_start,
  87. OS_FILE_LOG_BLOCK_SIZE);
  88. mutex_exit(&(recv_sys->mutex));
  89. }
  90. /************************************************************
  91. Empties the hash table when it has been fully processed. */
  92. static
  93. void
  94. recv_sys_empty_hash(void)
  95. /*=====================*/
  96. {
  97. ut_ad(mutex_own(&(recv_sys->mutex)));
  98. ut_a(recv_sys->n_addrs == 0);
  99. hash_table_free(recv_sys->addr_hash);
  100. mem_heap_empty(recv_sys->heap);
  101. recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 256);
  102. }
  103. /************************************************************
  104. Frees the recovery system. */
  105. void
  106. recv_sys_free(void)
  107. /*===============*/
  108. {
  109. mutex_enter(&(recv_sys->mutex));
  110. hash_table_free(recv_sys->addr_hash);
  111. mem_heap_free(recv_sys->heap);
  112. ut_free(recv_sys->buf);
  113. mem_free(recv_sys->last_block_buf_start);
  114. recv_sys->addr_hash = NULL;
  115. recv_sys->heap = NULL;
  116. mutex_exit(&(recv_sys->mutex));
  117. }
  118. /************************************************************
  119. Truncates possible corrupted or extra records from a log group. */
  120. static
  121. void
  122. recv_truncate_group(
  123. /*================*/
  124. log_group_t* group, /* in: log group */
  125. dulint recovered_lsn, /* in: recovery succeeded up to this
  126. lsn */
  127. dulint limit_lsn, /* in: this was the limit for
  128. recovery */
  129. dulint checkpoint_lsn, /* in: recovery was started from this
  130. checkpoint */
  131. dulint archived_lsn) /* in: the log has been archived up to
  132. this lsn */
  133. {
  134. dulint start_lsn;
  135. dulint end_lsn;
  136. dulint finish_lsn1;
  137. dulint finish_lsn2;
  138. dulint finish_lsn;
  139. ulint len;
  140. ulint i;
  141. if (ut_dulint_cmp(archived_lsn, ut_dulint_max) == 0) {
  142. /* Checkpoint was taken in the NOARCHIVELOG mode */
  143. archived_lsn = checkpoint_lsn;
  144. }
  145. finish_lsn1 = ut_dulint_add(ut_dulint_align_down(archived_lsn,
  146. OS_FILE_LOG_BLOCK_SIZE),
  147. log_group_get_capacity(group));
  148. finish_lsn2 = ut_dulint_add(ut_dulint_align_up(recovered_lsn,
  149. OS_FILE_LOG_BLOCK_SIZE),
  150. recv_sys->last_log_buf_size);
  151. if (ut_dulint_cmp(limit_lsn, ut_dulint_max) != 0) {
  152. /* We do not know how far we should erase log records: erase
  153. as much as possible */
  154. finish_lsn = finish_lsn1;
  155. } else {
  156. /* It is enough to erase the length of the log buffer */
  157. finish_lsn = ut_dulint_get_min(finish_lsn1, finish_lsn2);
  158. }
  159. ut_a(RECV_SCAN_SIZE <= log_sys->buf_size);
  160. /* Write the log buffer full of zeros */
  161. for (i = 0; i < RECV_SCAN_SIZE; i++) {
  162. *(log_sys->buf + i) = '';
  163. }
  164. start_lsn = ut_dulint_align_down(recovered_lsn,
  165. OS_FILE_LOG_BLOCK_SIZE);
  166. if (ut_dulint_cmp(start_lsn, recovered_lsn) != 0) {
  167. /* Copy the last incomplete log block to the log buffer and
  168. edit its data length: */
  169. ut_memcpy(log_sys->buf, recv_sys->last_block,
  170. OS_FILE_LOG_BLOCK_SIZE);
  171. log_block_set_data_len(log_sys->buf,
  172. ut_dulint_minus(recovered_lsn, start_lsn));
  173. }
  174. if (ut_dulint_cmp(start_lsn, finish_lsn) >= 0) {
  175. return;
  176. }
  177.      for (;;) {
  178. end_lsn = ut_dulint_add(start_lsn, RECV_SCAN_SIZE);
  179.     
  180. if (ut_dulint_cmp(end_lsn, finish_lsn) > 0) {
  181. end_lsn = finish_lsn;
  182. }
  183. len = ut_dulint_minus(end_lsn, start_lsn);
  184. log_group_write_buf(LOG_RECOVER, group, log_sys->buf, len,
  185. start_lsn, 0);
  186. if (ut_dulint_cmp(end_lsn, finish_lsn) >= 0) {
  187. return;
  188. }
  189. /* Write the log buffer full of zeros */
  190. for (i = 0; i < RECV_SCAN_SIZE; i++) {
  191. *(log_sys->buf + i) = '';
  192. }
  193. start_lsn = end_lsn;
  194. }
  195. }
  196. /************************************************************
  197. Copies the log segment between group->recovered_lsn and recovered_lsn from the
  198. most up-to-date log group to group, so that it contains the latest log data. */
  199. static
  200. void
  201. recv_copy_group(
  202. /*============*/
  203. log_group_t* up_to_date_group, /* in: the most up-to-date log
  204. group */
  205. log_group_t* group, /* in: copy to this log group */
  206. dulint recovered_lsn) /* in: recovery succeeded up
  207. to this lsn */
  208. {
  209. dulint start_lsn;
  210. dulint end_lsn;
  211. ulint len;
  212. if (ut_dulint_cmp(group->scanned_lsn, recovered_lsn) >= 0) {
  213. return;
  214. }
  215. ut_a(RECV_SCAN_SIZE <= log_sys->buf_size);
  216. start_lsn = ut_dulint_align_down(group->scanned_lsn,
  217. OS_FILE_LOG_BLOCK_SIZE);
  218.      for (;;) {
  219. end_lsn = ut_dulint_add(start_lsn, RECV_SCAN_SIZE);
  220.     
  221. if (ut_dulint_cmp(end_lsn, recovered_lsn) > 0) {
  222. end_lsn = ut_dulint_align_up(recovered_lsn,
  223. OS_FILE_LOG_BLOCK_SIZE);
  224. }
  225. log_group_read_log_seg(LOG_RECOVER, log_sys->buf,
  226. up_to_date_group, start_lsn, end_lsn);
  227. len = ut_dulint_minus(end_lsn, start_lsn);
  228. log_group_write_buf(LOG_RECOVER, group, log_sys->buf, len,
  229. start_lsn, 0);
  230. if (ut_dulint_cmp(end_lsn, recovered_lsn) >= 0) {
  231. return;
  232. }
  233. start_lsn = end_lsn;
  234. }
  235. }
  236. /************************************************************
  237. Copies a log segment from the most up-to-date log group to the other log
  238. groups, so that they all contain the latest log data. Also writes the info
  239. about the latest checkpoint to the groups, and inits the fields in the group
  240. memory structs to up-to-date values. */
  241. void
  242. recv_synchronize_groups(
  243. /*====================*/
  244. log_group_t* up_to_date_group) /* in: the most up-to-date
  245. log group */
  246. {
  247. log_group_t* group;
  248. dulint start_lsn;
  249. dulint end_lsn;
  250. dulint recovered_lsn;
  251. dulint limit_lsn;
  252. recovered_lsn = recv_sys->recovered_lsn;
  253. limit_lsn = recv_sys->limit_lsn;
  254. /* Read the last recovered log block to the recovery system buffer:
  255. the block is always incomplete */
  256. start_lsn = ut_dulint_align_down(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE);
  257. end_lsn = ut_dulint_align_up(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE);
  258. ut_ad(ut_dulint_cmp(start_lsn, end_lsn) != 0);
  259. log_group_read_log_seg(LOG_RECOVER, recv_sys->last_block,
  260. up_to_date_group, start_lsn, end_lsn);
  261. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  262. while (group) {
  263. if (group != up_to_date_group) {
  264. /* Copy log data if needed */
  265. recv_copy_group(group, up_to_date_group,
  266. recovered_lsn);
  267. }
  268. /* Update the fields in the group struct to correspond to
  269. recovered_lsn */
  270. log_group_set_fields(group, recovered_lsn);
  271. group = UT_LIST_GET_NEXT(log_groups, group);
  272. }
  273. /* Copy the checkpoint info to the groups; remember that we have
  274. incremented checkpoint_no by one, and the info will not be written
  275. over the max checkpoint info, thus making the preservation of max
  276. checkpoint info on disk certain */
  277. log_groups_write_checkpoint_info();
  278. mutex_exit(&(log_sys->mutex));
  279. /* Wait for the checkpoint write to complete */
  280. rw_lock_s_lock(&(log_sys->checkpoint_lock));
  281. rw_lock_s_unlock(&(log_sys->checkpoint_lock));
  282. mutex_enter(&(log_sys->mutex));
  283. }
  284. /************************************************************
  285. Looks for the maximum consistent checkpoint from the log groups. */
  286. static
  287. ulint
  288. recv_find_max_checkpoint(
  289. /*=====================*/
  290. /* out: error code or DB_SUCCESS */
  291. log_group_t** max_group, /* out: max group */
  292. ulint* max_field) /* out: LOG_CHECKPOINT_1 or
  293. LOG_CHECKPOINT_2 */
  294. {
  295. log_group_t* group;
  296. dulint max_no;
  297. dulint checkpoint_no;
  298. ulint field;
  299. ulint fold;
  300. byte* buf;
  301. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  302. max_no = ut_dulint_zero;
  303. *max_group = NULL;
  304. buf = log_sys->checkpoint_buf;
  305. while (group) {
  306. group->state = LOG_GROUP_CORRUPTED;
  307. for (field = LOG_CHECKPOINT_1; field <= LOG_CHECKPOINT_2;
  308. field += LOG_CHECKPOINT_2 - LOG_CHECKPOINT_1) {
  309. log_group_read_checkpoint_info(group, field);
  310. /* Check the consistency of the checkpoint info */
  311. fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1);
  312. if ((fold & 0xFFFFFFFF)
  313.                                   != mach_read_from_4(buf
  314. + LOG_CHECKPOINT_CHECKSUM_1)) {
  315. if (log_debug_writes) {
  316. fprintf(stderr, 
  317.     "Innobase: Checkpoint in group %lu at %lu invalid, %lu, %lun",
  318. group->id, field,
  319.                                                 fold & 0xFFFFFFFF,
  320.                                  mach_read_from_4(buf
  321.       + LOG_CHECKPOINT_CHECKSUM_1));
  322. }
  323. goto not_consistent;
  324. }
  325. fold = ut_fold_binary(buf + LOG_CHECKPOINT_LSN,
  326. LOG_CHECKPOINT_CHECKSUM_2
  327. - LOG_CHECKPOINT_LSN);
  328. if ((fold & 0xFFFFFFFF)
  329.                                   != mach_read_from_4(buf
  330. + LOG_CHECKPOINT_CHECKSUM_2)) {
  331. if (log_debug_writes) {
  332. fprintf(stderr, 
  333. "Innobase: Checkpoint in group %lu at %lu invalid, %lu, %lun",
  334. group->id, field,
  335.                                                 fold & 0xFFFFFFFF,
  336.                                  mach_read_from_4(buf
  337.   + LOG_CHECKPOINT_CHECKSUM_2));
  338. }
  339. goto not_consistent;
  340. }
  341. group->state = LOG_GROUP_OK;
  342. group->lsn = mach_read_from_8(buf
  343. + LOG_CHECKPOINT_LSN);
  344. group->lsn_offset = mach_read_from_4(buf
  345. + LOG_CHECKPOINT_OFFSET);
  346. checkpoint_no =
  347. mach_read_from_8(buf + LOG_CHECKPOINT_NO);
  348. if (log_debug_writes) {
  349. fprintf(stderr, 
  350. "Innobase: Checkpoint number %lu found in group %lun",
  351. ut_dulint_get_low(checkpoint_no), group->id);
  352. }
  353. if (ut_dulint_cmp(checkpoint_no, max_no) >= 0) {
  354. *max_group = group;
  355. *max_field = field;
  356. max_no = checkpoint_no;
  357. }
  358. not_consistent:
  359. ;
  360. }
  361. group = UT_LIST_GET_NEXT(log_groups, group);
  362. }
  363. if (*max_group == NULL) {
  364. fprintf(stderr, "Innobase: No valid checkpoint foundn");
  365. return(DB_ERROR);
  366. }
  367. return(DB_SUCCESS);
  368. }
  369. /***********************************************************************
  370. Tries to parse a single log record body and also applies it to a page if
  371. specified. */
  372. static
  373. byte*
  374. recv_parse_or_apply_log_rec_body(
  375. /*=============================*/
  376. /* out: log record end, NULL if not a complete
  377. record */
  378. byte type, /* in: type */
  379. byte* ptr, /* in: pointer to a buffer */
  380. byte* end_ptr,/* in: pointer to the buffer end */
  381. page_t* page, /* in: buffer page or NULL; if not NULL, then the log
  382. record is applied to the page, and the log record
  383. should be complete then */
  384. mtr_t* mtr) /* in: mtr or NULL; should be non-NULL if and only if
  385. page is non-NULL */
  386. {
  387. byte* new_ptr;
  388. if (type <= MLOG_8BYTES) {
  389. new_ptr = mlog_parse_nbytes(type, ptr, end_ptr, page);
  390. } else if (type == MLOG_REC_INSERT) {
  391. new_ptr = page_cur_parse_insert_rec(FALSE, ptr, end_ptr, page,
  392. mtr);
  393. } else if (type == MLOG_REC_CLUST_DELETE_MARK) {
  394. new_ptr = btr_cur_parse_del_mark_set_clust_rec(ptr, end_ptr,
  395. page);
  396. } else if (type == MLOG_REC_SEC_DELETE_MARK) {
  397. new_ptr = btr_cur_parse_del_mark_set_sec_rec(ptr, end_ptr,
  398. page);
  399. } else if (type == MLOG_REC_UPDATE_IN_PLACE) {
  400. new_ptr = btr_cur_parse_update_in_place(ptr, end_ptr, page);
  401. } else if ((type == MLOG_LIST_END_DELETE)
  402.    || (type == MLOG_LIST_START_DELETE)) {
  403. new_ptr = page_parse_delete_rec_list(type, ptr, end_ptr, page,
  404. mtr);
  405. } else if (type == MLOG_LIST_END_COPY_CREATED) {
  406. new_ptr = page_parse_copy_rec_list_to_created_page(ptr,
  407. end_ptr, page, mtr);
  408. } else if (type == MLOG_PAGE_REORGANIZE) {
  409. new_ptr = btr_parse_page_reorganize(ptr, end_ptr, page, mtr);
  410. } else if (type == MLOG_PAGE_CREATE) {
  411. new_ptr = page_parse_create(ptr, end_ptr, page, mtr);
  412. } else if (type == MLOG_UNDO_INSERT) {
  413. new_ptr = trx_undo_parse_add_undo_rec(ptr, end_ptr, page);
  414. } else if (type == MLOG_UNDO_ERASE_END) {
  415. new_ptr = trx_undo_parse_erase_page_end(ptr, end_ptr, page,
  416. mtr);
  417. } else if (type == MLOG_UNDO_INIT) {
  418. new_ptr = trx_undo_parse_page_init(ptr, end_ptr, page, mtr);
  419. } else if (type == MLOG_UNDO_HDR_DISCARD) {
  420. new_ptr = trx_undo_parse_discard_latest(ptr, end_ptr, page,
  421. mtr);
  422. } else if ((type == MLOG_UNDO_HDR_CREATE)
  423.    || (type == MLOG_UNDO_HDR_REUSE)) {
  424. new_ptr = trx_undo_parse_page_header(type, ptr, end_ptr, page,
  425. mtr);
  426. } else if (type == MLOG_REC_MIN_MARK) {
  427. new_ptr = btr_parse_set_min_rec_mark(ptr, end_ptr, page, mtr);
  428. } else if (type == MLOG_REC_DELETE) {
  429. new_ptr = page_cur_parse_delete_rec(ptr, end_ptr, page, mtr);
  430. } else if (type == MLOG_IBUF_BITMAP_INIT) {
  431. new_ptr = ibuf_parse_bitmap_init(ptr, end_ptr, page, mtr);
  432. } else if (type == MLOG_FULL_PAGE) {
  433. new_ptr = mtr_log_parse_full_page(ptr, end_ptr, page);
  434. } else if (type == MLOG_INIT_FILE_PAGE) {
  435. new_ptr = fsp_parse_init_file_page(ptr, end_ptr, page);
  436. } else if (type <= MLOG_WRITE_STRING) {
  437. new_ptr = mlog_parse_string(ptr, end_ptr, page);
  438. } else {
  439. ut_error;
  440. }
  441. ut_ad(!page || new_ptr);
  442. return(new_ptr);
  443. }
  444. /*************************************************************************
  445. Calculates the fold value of a page file address: used in inserting or
  446. searching for a log record in the hash table. */
  447. UNIV_INLINE
  448. ulint
  449. recv_fold(
  450. /*======*/
  451. /* out: folded value */
  452. ulint space, /* in: space */
  453. ulint page_no)/* in: page number */
  454. {
  455. return(ut_fold_ulint_pair(space, page_no));
  456. }
  457. /*************************************************************************
  458. Calculates the hash value of a page file address: used in inserting or
  459. searching for a log record in the hash table. */
  460. UNIV_INLINE
  461. ulint
  462. recv_hash(
  463. /*======*/
  464. /* out: folded value */
  465. ulint space, /* in: space */
  466. ulint page_no)/* in: page number */
  467. {
  468. return(hash_calc_hash(recv_fold(space, page_no), recv_sys->addr_hash));
  469. }
  470. /*************************************************************************
  471. Gets the hashed file address struct for a page. */
  472. static
  473. recv_addr_t*
  474. recv_get_fil_addr_struct(
  475. /*=====================*/
  476. /* out: file address struct, NULL if not found from
  477. the hash table */
  478. ulint space, /* in: space id */
  479. ulint page_no)/* in: page number */
  480. {
  481. recv_addr_t* recv_addr;
  482. recv_addr = HASH_GET_FIRST(recv_sys->addr_hash,
  483. recv_hash(space, page_no));
  484. while (recv_addr) {
  485. if ((recv_addr->space == space)
  486. && (recv_addr->page_no == page_no)) {
  487. break;
  488. }
  489. recv_addr = HASH_GET_NEXT(addr_hash, recv_addr);
  490. }
  491. return(recv_addr);
  492. }
  493. /***********************************************************************
  494. Adds a new log record to the hash table of log records. */
  495. static
  496. void
  497. recv_add_to_hash_table(
  498. /*===================*/
  499. byte type, /* in: log record type */
  500. ulint space, /* in: space id */
  501. ulint page_no, /* in: page number */
  502. byte* body, /* in: log record body */
  503. byte* rec_end, /* in: log record end */
  504. dulint start_lsn, /* in: start lsn of the mtr */
  505. dulint end_lsn) /* in: end lsn of the mtr */
  506. {
  507. recv_t* recv;
  508. ulint len;
  509. recv_data_t* recv_data;
  510. recv_data_t** prev_field;
  511. recv_addr_t* recv_addr;
  512. ut_a(space == 0); /* For debugging; TODO: remove this */
  513. len = rec_end - body;
  514. recv = mem_heap_alloc(recv_sys->heap, sizeof(recv_t));
  515. recv->type = type;
  516. recv->len = rec_end - body;
  517. recv->start_lsn = start_lsn;
  518. recv->end_lsn = end_lsn;
  519. recv_addr = recv_get_fil_addr_struct(space, page_no);
  520. if (recv_addr == NULL) {
  521. recv_addr = mem_heap_alloc(recv_sys->heap,
  522. sizeof(recv_addr_t));
  523. recv_addr->space = space;
  524. recv_addr->page_no = page_no;
  525. recv_addr->state = RECV_NOT_PROCESSED;
  526. UT_LIST_INIT(recv_addr->rec_list);
  527. HASH_INSERT(recv_addr_t, addr_hash, recv_sys->addr_hash,
  528. recv_fold(space, page_no), recv_addr);
  529. recv_sys->n_addrs++;
  530. }
  531. UT_LIST_ADD_LAST(rec_list, recv_addr->rec_list, recv);
  532. prev_field = &(recv->data);
  533. /* Store the log record body in chunks of less than UNIV_PAGE_SIZE:
  534. recv_sys->heap grows into the buffer pool, and bigger chunks could not
  535. be allocated */
  536. while (rec_end > body) {
  537. len = rec_end - body;
  538. if (len > RECV_DATA_BLOCK_SIZE) {
  539. len = RECV_DATA_BLOCK_SIZE;
  540. }
  541. recv_data = mem_heap_alloc(recv_sys->heap,
  542. sizeof(recv_data_t) + len);
  543. *prev_field = recv_data;
  544. ut_memcpy(((byte*)recv_data) + sizeof(recv_data_t), body, len);
  545. prev_field = &(recv_data->next);
  546. body += len;
  547. }
  548. *prev_field = NULL;
  549. }
  550. /*************************************************************************
  551. Copies the log record body from recv to buf. */
  552. static
  553. void
  554. recv_data_copy_to_buf(
  555. /*==================*/
  556. byte* buf, /* in: buffer of length at least recv->len */
  557. recv_t* recv) /* in: log record */
  558. {
  559. recv_data_t* recv_data;
  560. ulint part_len;
  561. ulint len;
  562. len = recv->len;
  563. recv_data = recv->data;
  564. while (len > 0) {
  565. if (len > RECV_DATA_BLOCK_SIZE) {
  566. part_len = RECV_DATA_BLOCK_SIZE;
  567. } else {
  568. part_len = len;
  569. }
  570. ut_memcpy(buf, ((byte*)recv_data) + sizeof(recv_data_t),
  571. part_len);
  572. buf += part_len;
  573. len -= part_len;
  574. recv_data = recv_data->next;
  575. }
  576. }
  577. /****************************************************************************
  578. Applies the hashed log records to the page, if the page lsn is less than the
  579. lsn of a log record. This can be called when a buffer page has just been
  580. read in, or also for a page already in the buffer pool. */
  581. void
  582. recv_recover_page(
  583. /*==============*/
  584. ibool just_read_in, /* in: TRUE if the i/o-handler calls this for
  585. a freshly read page */
  586. page_t* page, /* in: buffer page */
  587. ulint space, /* in: space id */
  588. ulint page_no) /* in: page number */
  589. {
  590. buf_block_t* block;
  591. recv_addr_t* recv_addr;
  592. recv_t* recv;
  593. byte* buf;
  594. dulint start_lsn;
  595. dulint end_lsn;
  596. dulint page_lsn;
  597. dulint page_newest_lsn;
  598. ibool modification_to_page;
  599. ibool success;
  600. mtr_t mtr;
  601. mutex_enter(&(recv_sys->mutex));
  602. if (recv_sys->apply_log_recs == FALSE) {
  603. /* Log records should not be applied now */
  604. mutex_exit(&(recv_sys->mutex));
  605. return;
  606. }
  607. recv_addr = recv_get_fil_addr_struct(space, page_no);
  608. if ((recv_addr == NULL)
  609.     || (recv_addr->state == RECV_BEING_PROCESSED)
  610.     || (recv_addr->state == RECV_PROCESSED)) {
  611. mutex_exit(&(recv_sys->mutex));
  612. return;
  613. }
  614. recv_addr->state = RECV_BEING_PROCESSED;
  615. mutex_exit(&(recv_sys->mutex));
  616. block = buf_block_align(page);
  617. if (just_read_in) {
  618. /* Move the ownership of the x-latch on the page to this OS
  619. thread, so that we can acquire a second x-latch on it. This
  620. is needed for the operations to the page to pass the debug
  621. checks. */
  622. rw_lock_x_lock_move_ownership(&(block->lock));
  623. }
  624. mtr_start(&mtr);
  625. mtr_set_log_mode(&mtr, MTR_LOG_NONE);
  626. success = buf_page_get_known_nowait(RW_X_LATCH, page, BUF_KEEP_OLD,
  627. #ifdef UNIV_SYNC_DEBUG
  628. IB__FILE__, __LINE__,
  629. #endif
  630. &mtr);
  631. ut_a(success);
  632. buf_page_dbg_add_level(page, SYNC_NO_ORDER_CHECK);
  633. /* Read the newest modification lsn from the page */
  634. page_lsn = mach_read_from_8(page + FIL_PAGE_LSN);
  635. /* It may be that the page has been modified in the buffer pool: read
  636. the newest modification lsn there */
  637. page_newest_lsn = buf_frame_get_newest_modification(page);
  638. if (!ut_dulint_is_zero(page_newest_lsn)) {
  639. page_lsn = page_newest_lsn;
  640. }
  641. modification_to_page = FALSE;
  642. recv = UT_LIST_GET_FIRST(recv_addr->rec_list);
  643. while (recv) {
  644. end_lsn = recv->end_lsn;
  645. if (recv->len > RECV_DATA_BLOCK_SIZE) {
  646. /* We have to copy the record body to a separate
  647. buffer */
  648. buf = mem_alloc(recv->len);
  649. recv_data_copy_to_buf(buf, recv);
  650. } else {
  651. buf = ((byte*)(recv->data)) + sizeof(recv_data_t);
  652. }
  653. if ((recv->type == MLOG_INIT_FILE_PAGE)
  654.     || (recv->type == MLOG_FULL_PAGE)) {
  655. /* A new file page may has been taken into use,
  656. or we have stored the full contents of the page:
  657. in this case it may be that the original log record
  658. type was MLOG_INIT_FILE_PAGE, and we replaced it
  659. with MLOG_FULL_PAGE, thus to we have to apply
  660. any record of type MLOG_FULL_PAGE */
  661. page_lsn = page_newest_lsn;
  662. mach_write_to_8(page + UNIV_PAGE_SIZE
  663. - FIL_PAGE_END_LSN, ut_dulint_zero);
  664. mach_write_to_8(page + FIL_PAGE_LSN, ut_dulint_zero);
  665. }
  666. if (ut_dulint_cmp(recv->start_lsn, page_lsn) >= 0) {
  667. if (!modification_to_page) {
  668. modification_to_page = TRUE;
  669. start_lsn = recv->start_lsn;
  670. }
  671. if (log_debug_writes) {
  672. fprintf(stderr, 
  673.      "Innobase: Applying log rec type %lu len %lu to space %lu page no %lun",
  674. (ulint)recv->type, recv->len, recv_addr->space,
  675. recv_addr->page_no);
  676. }
  677. recv_parse_or_apply_log_rec_body(recv->type, buf,
  678. buf + recv->len, page, &mtr);
  679. }
  680. if (recv->len > RECV_DATA_BLOCK_SIZE) {
  681. mem_free(buf);
  682. }
  683. recv = UT_LIST_GET_NEXT(rec_list, recv);
  684. }
  685. /* If the following assert fails, the file page is incompletely
  686. written, and a recovery from a backup is required */
  687. ut_a(0 == ut_dulint_cmp(mach_read_from_8(page + FIL_PAGE_LSN),
  688. mach_read_from_8(page + UNIV_PAGE_SIZE
  689. - FIL_PAGE_END_LSN)));
  690. mutex_enter(&(recv_sys->mutex));
  691. recv_addr->state = RECV_PROCESSED;
  692. ut_a(recv_sys->n_addrs);
  693. recv_sys->n_addrs--;
  694. mutex_exit(&(recv_sys->mutex));
  695. if (modification_to_page) {
  696. buf_flush_recv_note_modification(block, start_lsn, end_lsn);
  697. }
  698. /* Make sure that committing mtr does not change the modification
  699. lsn values of page */
  700. mtr.modifications = FALSE;
  701. mtr_commit(&mtr);
  702. }
  703. /***********************************************************************
  704. Reads in pages which have hashed log records, from an area around a given
  705. page number. */
  706. static
  707. ulint
  708. recv_read_in_area(
  709. /*==============*/
  710. /* out: number of pages found */
  711. ulint space, /* in: space */
  712. ulint page_no)/* in: page number */
  713. {
  714. recv_addr_t* recv_addr;
  715. ulint page_nos[RECV_READ_AHEAD_AREA];
  716. ulint low_limit;
  717. ulint n;
  718. low_limit = page_no - (page_no % RECV_READ_AHEAD_AREA);
  719. n = 0;
  720. for (page_no = low_limit; page_no < low_limit + RECV_READ_AHEAD_AREA;
  721. page_no++) {
  722. recv_addr = recv_get_fil_addr_struct(space, page_no);
  723. if (recv_addr && !buf_page_peek(space, page_no)) {
  724. mutex_enter(&(recv_sys->mutex));
  725. if (recv_addr->state == RECV_NOT_PROCESSED) {
  726. recv_addr->state = RECV_BEING_READ;
  727. page_nos[n] = page_no;
  728. n++;
  729. }
  730. mutex_exit(&(recv_sys->mutex));
  731. }
  732. }
  733. buf_read_recv_pages(FALSE, space, page_nos, n);
  734. /* printf("Recv pages at %lu n %lun", page_nos[0], n); */
  735. return(n);
  736. }
  737. /***********************************************************************
  738. Empties the hash table of stored log records, applying them to appropriate
  739. pages. */
  740. void
  741. recv_apply_hashed_log_recs(
  742. /*=======================*/
  743. ibool allow_ibuf) /* in: if TRUE, also ibuf operations are
  744. allowed during the application; if FALSE,
  745. no ibuf operations are allowed, and after
  746. the application all file pages are flushed to
  747. disk and invalidated in buffer pool: this
  748. alternative means that no new log records
  749. can be generated during the application;
  750. the caller must in this case own the log
  751. mutex */
  752. {
  753. recv_addr_t* recv_addr;
  754. page_t* page;
  755. ulint i;
  756. ulint space;
  757. ulint page_no;
  758. ulint n_pages;
  759. ibool has_printed = FALSE;
  760. mtr_t mtr;
  761. loop:
  762. mutex_enter(&(recv_sys->mutex));
  763. if (recv_sys->apply_batch_on) {
  764. mutex_exit(&(recv_sys->mutex));
  765. os_thread_sleep(500000);
  766. goto loop;
  767. }
  768. if (!allow_ibuf) {
  769. ut_ad(mutex_own(&(log_sys->mutex)));
  770. recv_no_ibuf_operations = TRUE;
  771. } else {
  772. ut_ad(!mutex_own(&(log_sys->mutex)));
  773. }
  774. recv_sys->apply_log_recs = TRUE;
  775. recv_sys->apply_batch_on = TRUE;
  776. for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {
  777. recv_addr = HASH_GET_FIRST(recv_sys->addr_hash, i);
  778. while (recv_addr) {
  779. space = recv_addr->space;
  780. page_no = recv_addr->page_no;
  781. if (recv_addr->state == RECV_NOT_PROCESSED) {
  782. if (!has_printed) {
  783. fprintf(stderr, 
  784. "Innobase: Starting an apply batch of log records to the database...n");
  785. has_printed = TRUE;
  786. }
  787. mutex_exit(&(recv_sys->mutex));
  788. if (buf_page_peek(space, page_no)) {
  789. mtr_start(&mtr);
  790. page = buf_page_get(space, page_no,
  791. RW_X_LATCH, &mtr);
  792. buf_page_dbg_add_level(page,
  793. SYNC_NO_ORDER_CHECK);
  794. recv_recover_page(FALSE, page, space,
  795. page_no);
  796. mtr_commit(&mtr);
  797. } else {
  798. recv_read_in_area(space, page_no);
  799. }
  800. mutex_enter(&(recv_sys->mutex));
  801. }
  802. recv_addr = HASH_GET_NEXT(addr_hash, recv_addr);
  803. }
  804. }
  805. /* Wait until all the pages have been processed */
  806. while (recv_sys->n_addrs != 0) {
  807. mutex_exit(&(recv_sys->mutex));
  808. os_thread_sleep(500000);
  809. mutex_enter(&(recv_sys->mutex));
  810. }
  811. if (!allow_ibuf) {
  812. /* Flush all the file pages to disk and invalidate them in
  813. the buffer pool */
  814. mutex_exit(&(recv_sys->mutex));
  815. mutex_exit(&(log_sys->mutex));
  816. n_pages = buf_flush_batch(BUF_FLUSH_LIST, ULINT_MAX,
  817. ut_dulint_max);
  818. ut_a(n_pages != ULINT_UNDEFINED);
  819. buf_flush_wait_batch_end(BUF_FLUSH_LIST);
  820. buf_pool_invalidate();
  821. mutex_enter(&(log_sys->mutex));
  822. mutex_enter(&(recv_sys->mutex));
  823. recv_no_ibuf_operations = FALSE;
  824. }
  825. recv_sys->apply_log_recs = FALSE;
  826. recv_sys->apply_batch_on = FALSE;
  827. recv_sys_empty_hash();
  828. if (has_printed) {
  829. fprintf(stderr, "Innobase: Apply batch completedn");
  830. }
  831. mutex_exit(&(recv_sys->mutex));
  832. }
  833. /***********************************************************************
  834. In the debug version, updates the replica of a file page, based on a log
  835. record. */
  836. static
  837. void
  838. recv_update_replicate(
  839. /*==================*/
  840. byte type, /* in: log record type */
  841. ulint space, /* in: space id */
  842. ulint page_no,/* in: page number */
  843. byte* body, /* in: log record body */
  844. byte* end_ptr)/* in: log record end */
  845. {
  846. page_t* replica;
  847. mtr_t mtr;
  848. byte* ptr;
  849. mtr_start(&mtr);
  850. mtr_set_log_mode(&mtr, MTR_LOG_NONE);
  851. replica = buf_page_get(space + RECV_REPLICA_SPACE_ADD, page_no,
  852. RW_X_LATCH, &mtr);
  853. buf_page_dbg_add_level(replica, SYNC_NO_ORDER_CHECK);
  854. ptr = recv_parse_or_apply_log_rec_body(type, body, end_ptr, replica,
  855. &mtr);
  856. ut_a(ptr == end_ptr);
  857. /* Notify the buffer manager that the page has been updated */
  858. buf_flush_recv_note_modification(buf_block_align(replica),
  859. log_sys->old_lsn, log_sys->old_lsn);
  860. /* Make sure that committing mtr does not call log routines, as
  861. we currently own the log mutex */
  862. mtr.modifications = FALSE;
  863. mtr_commit(&mtr);
  864. }
  865. /***********************************************************************
  866. Checks that two strings are identical. */
  867. static
  868. void
  869. recv_check_identical(
  870. /*=================*/
  871. byte* str1, /* in: first string */
  872. byte* str2, /* in: second string */
  873. ulint len) /* in: length of strings */
  874. {
  875. ulint i;
  876. for (i = 0; i < len; i++) {
  877. if (str1[i] != str2[i]) {
  878. fprintf(stderr, "Strings do not match at offset %lun", i);
  879. ut_print_buf(str1 + i, 16);
  880. fprintf(stderr, "n");
  881. ut_print_buf(str2 + i, 16);
  882. ut_error;
  883. }
  884. }
  885. }
  886. /***********************************************************************
  887. In the debug version, checks that the replica of a file page is identical
  888. to the original page. */
  889. static
  890. void
  891. recv_compare_replicate(
  892. /*===================*/
  893. ulint space, /* in: space id */
  894. ulint page_no)/* in: page number */
  895. {
  896. page_t* replica;
  897. page_t* page;
  898. mtr_t mtr;
  899. mtr_start(&mtr);
  900. mutex_enter(&(buf_pool->mutex));
  901. page = buf_page_hash_get(space, page_no)->frame;
  902. mutex_exit(&(buf_pool->mutex));
  903. replica = buf_page_get(space + RECV_REPLICA_SPACE_ADD, page_no,
  904. RW_X_LATCH, &mtr);
  905. buf_page_dbg_add_level(replica, SYNC_NO_ORDER_CHECK);
  906. recv_check_identical(page + FIL_PAGE_DATA,
  907. replica + FIL_PAGE_DATA,
  908. PAGE_HEADER + PAGE_MAX_TRX_ID - FIL_PAGE_DATA);
  909. recv_check_identical(page + PAGE_HEADER + PAGE_MAX_TRX_ID + 8,
  910. replica + PAGE_HEADER + PAGE_MAX_TRX_ID + 8,
  911. UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
  912. - PAGE_HEADER - PAGE_MAX_TRX_ID - 8);
  913. mtr_commit(&mtr);
  914. }
  915. /***********************************************************************
  916. Checks that a replica of a space is identical to the original space. */
  917. void
  918. recv_compare_spaces(
  919. /*================*/
  920. ulint space1, /* in: space id */
  921. ulint space2, /* in: space id */
  922. ulint n_pages)/* in: number of pages */
  923. {
  924. page_t* replica;
  925. page_t* page;
  926. mtr_t mtr;
  927. page_t* frame;
  928. ulint page_no;
  929. replica = buf_frame_alloc();
  930. page = buf_frame_alloc();
  931. for (page_no = 0; page_no < n_pages; page_no++) {
  932. mtr_start(&mtr);
  933. frame = buf_page_get_gen(space1, page_no, RW_S_LATCH, NULL,
  934. BUF_GET_IF_IN_POOL,
  935. #ifdef UNIV_SYNC_DEBUG
  936. IB__FILE__, __LINE__,
  937. #endif
  938. &mtr);
  939. if (frame) {
  940. buf_page_dbg_add_level(frame, SYNC_NO_ORDER_CHECK);
  941. ut_memcpy(page, frame, UNIV_PAGE_SIZE);
  942. } else {
  943. /* Read it from file */
  944. fil_io(OS_FILE_READ, TRUE, space1, page_no, 0,
  945. UNIV_PAGE_SIZE, page, NULL);
  946. }
  947. frame = buf_page_get_gen(space2, page_no, RW_S_LATCH, NULL,
  948. BUF_GET_IF_IN_POOL,
  949. #ifdef UNIV_SYNC_DEBUG
  950. IB__FILE__, __LINE__,
  951. #endif
  952. &mtr);
  953. if (frame) {
  954. buf_page_dbg_add_level(frame, SYNC_NO_ORDER_CHECK);
  955. ut_memcpy(replica, frame, UNIV_PAGE_SIZE);
  956. } else {
  957. /* Read it from file */
  958. fil_io(OS_FILE_READ, TRUE, space2, page_no, 0,
  959. UNIV_PAGE_SIZE, replica, NULL);
  960. }
  961. recv_check_identical(page + FIL_PAGE_DATA,
  962. replica + FIL_PAGE_DATA,
  963. PAGE_HEADER + PAGE_MAX_TRX_ID - FIL_PAGE_DATA);
  964. recv_check_identical(page + PAGE_HEADER + PAGE_MAX_TRX_ID + 8,
  965. replica + PAGE_HEADER + PAGE_MAX_TRX_ID + 8,
  966. UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
  967. - PAGE_HEADER - PAGE_MAX_TRX_ID - 8);
  968. mtr_commit(&mtr);
  969. }
  970. buf_frame_free(replica);
  971. buf_frame_free(page);
  972. }
  973. /***********************************************************************
  974. Checks that a replica of a space is identical to the original space. Disables
  975. ibuf operations and flushes and invalidates the buffer pool pages after the
  976. test. This function can be used to check the recovery before dict or trx
  977. systems are initialized. */
  978. void
  979. recv_compare_spaces_low(
  980. /*====================*/
  981. ulint space1, /* in: space id */
  982. ulint space2, /* in: space id */
  983. ulint n_pages)/* in: number of pages */
  984. {
  985. mutex_enter(&(log_sys->mutex));
  986. recv_apply_hashed_log_recs(FALSE);
  987. mutex_exit(&(log_sys->mutex));
  988. recv_compare_spaces(space1, space2, n_pages);
  989. }
  990. /***********************************************************************
  991. Tries to parse a single log record and returns its length. */
  992. static
  993. ulint
  994. recv_parse_log_rec(
  995. /*===============*/
  996. /* out: length of the record, or 0 if the record was
  997. not complete */
  998. byte* ptr, /* in: pointer to a buffer */
  999. byte* end_ptr,/* in: pointer to the buffer end */
  1000. byte* type, /* out: type */
  1001. ulint* space, /* out: space id */
  1002. ulint* page_no,/* out: page number */
  1003. byte** body) /* out: log record body start */
  1004. {
  1005. byte* new_ptr;
  1006. if (ptr == end_ptr) {
  1007. return(0);
  1008. }
  1009. if (*ptr == MLOG_MULTI_REC_END) {
  1010. *type = *ptr;
  1011. return(1);
  1012. }
  1013. if (*ptr == MLOG_DUMMY_RECORD) {
  1014. *type = *ptr;
  1015. *space = 1000; /* For debugging */
  1016. return(1);
  1017. }
  1018. new_ptr = mlog_parse_initial_log_record(ptr, end_ptr, type, space,
  1019. page_no);
  1020. if (!new_ptr) {
  1021. return(0);
  1022. }
  1023. *body = new_ptr;
  1024. new_ptr = recv_parse_or_apply_log_rec_body(*type, new_ptr, end_ptr,
  1025. NULL, NULL);
  1026. if (new_ptr == NULL) {
  1027. return(0);
  1028. }
  1029. return(new_ptr - ptr);
  1030. }
  1031. /***********************************************************
  1032. Calculates the new value for lsn when more data is added to the log. */
  1033. static
  1034. dulint
  1035. recv_calc_lsn_on_data_add(
  1036. /*======================*/
  1037. dulint lsn, /* in: old lsn */
  1038. ulint len) /* in: this many bytes of data is added, log block
  1039. headers not included */
  1040. {
  1041. ulint frag_len;
  1042. ulint lsn_len;
  1043. frag_len = (ut_dulint_get_low(lsn) % OS_FILE_LOG_BLOCK_SIZE)
  1044.     - LOG_BLOCK_HDR_SIZE;
  1045. ut_ad(frag_len < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE
  1046.        - LOG_BLOCK_TRL_SIZE);
  1047. lsn_len = len + ((len + frag_len)
  1048.       / (OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE
  1049.        - LOG_BLOCK_TRL_SIZE))
  1050.        * (LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE);
  1051. return(ut_dulint_add(lsn, lsn_len));
  1052. }
  1053. /***********************************************************
  1054. Checks that the parser recognizes incomplete initial segments of a log
  1055. record as incomplete. */
  1056. void
  1057. recv_check_incomplete_log_recs(
  1058. /*===========================*/
  1059. byte* ptr, /* in: pointer to a complete log record */
  1060. ulint len) /* in: length of the log record */
  1061. {
  1062. ulint i;
  1063. byte type;
  1064. ulint space;
  1065. ulint page_no;
  1066. byte* body;
  1067. for (i = 0; i < len; i++) {
  1068. ut_a(0 == recv_parse_log_rec(ptr, ptr + i, &type, &space,
  1069. &page_no, &body));
  1070. }
  1071. }
  1072. /***********************************************************
  1073. Parses log records from a buffer and stores them to a hash table to wait
  1074. merging to file pages. If the hash table becomes too full, applies it
  1075. automatically to file pages. */
  1076. void
  1077. recv_parse_log_recs(
  1078. /*================*/
  1079. ibool store_to_hash) /* in: TRUE if the records should be stored
  1080. to the hash table; this is set to FALSE if just
  1081. debug checking is needed */
  1082. {
  1083. byte* ptr;
  1084. byte* end_ptr;
  1085. ulint single_rec;
  1086. ulint len;
  1087. ulint total_len;
  1088. dulint new_recovered_lsn;
  1089. dulint old_lsn;
  1090. byte type;
  1091. ulint space;
  1092. ulint page_no;
  1093. byte* body;
  1094. ulint n_recs;
  1095. ut_ad(mutex_own(&(log_sys->mutex)));
  1096. ut_ad(!ut_dulint_is_zero(recv_sys->parse_start_lsn));
  1097. loop:
  1098. ptr = recv_sys->buf + recv_sys->recovered_offset;
  1099. end_ptr = recv_sys->buf + recv_sys->len;
  1100. if (ptr == end_ptr) {
  1101. return;
  1102. }
  1103. single_rec = (ulint)*ptr & MLOG_SINGLE_REC_FLAG;
  1104. if (single_rec || *ptr == MLOG_DUMMY_RECORD) {
  1105. /* The mtr only modified a single page */
  1106. old_lsn = recv_sys->recovered_lsn;
  1107. len = recv_parse_log_rec(ptr, end_ptr, &type, &space,
  1108. &page_no, &body);
  1109. if (len == 0) {
  1110. return;
  1111. }
  1112. new_recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len);
  1113. if (ut_dulint_cmp(new_recovered_lsn, recv_sys->scanned_lsn)
  1114. > 0) {
  1115. /* The log record filled a log block, and we require
  1116. that also the next log block should have been scanned
  1117. in */
  1118. return;
  1119. }
  1120. recv_sys->recovered_offset += len;
  1121. recv_sys->recovered_lsn = new_recovered_lsn;
  1122. if (log_debug_writes) {
  1123. fprintf(stderr, 
  1124. "Innobase: Parsed a single log rec type %lu len %lu space %lu page no %lun",
  1125. (ulint)type, len, space, page_no);
  1126. }
  1127. if (type == MLOG_DUMMY_RECORD) {
  1128. /* Do nothing */
  1129. } else if (store_to_hash) {
  1130. recv_add_to_hash_table(type, space, page_no, body,
  1131. ptr + len, old_lsn,
  1132. recv_sys->recovered_lsn);
  1133. } else {
  1134. /* In debug checking, update a replicate page
  1135. according to the log record, and check that it
  1136. becomes identical with the original page */
  1137. #ifdef UNIV_LOG_DEBUG
  1138. recv_check_incomplete_log_recs(ptr, len);
  1139. #endif
  1140. recv_update_replicate(type, space, page_no, body,
  1141. ptr + len);
  1142. recv_compare_replicate(space, page_no);
  1143. }
  1144. } else {
  1145. /* Check that all the records associated with the single mtr
  1146. are included within the buffer */
  1147. total_len = 0;
  1148. n_recs = 0;
  1149. for (;;) {
  1150. len = recv_parse_log_rec(ptr, end_ptr, &type, &space,
  1151. &page_no, &body);
  1152. if (len == 0) {
  1153. return;
  1154. }
  1155. if ((!store_to_hash) && (type != MLOG_MULTI_REC_END)) {
  1156. /* In debug checking, update a replicate page
  1157. according to the log record */
  1158. #ifdef UNIV_LOG_DEBUG
  1159. recv_check_incomplete_log_recs(ptr, len);
  1160. #endif
  1161. recv_update_replicate(type, space, page_no,
  1162. body, ptr + len);
  1163. }
  1164. if (log_debug_writes) {
  1165. fprintf(stderr, 
  1166. "Innobase: Parsed a multi log rec type %lu len %lu space %lu page no %lun",
  1167. (ulint)type, len, space, page_no);
  1168. }
  1169. total_len += len;
  1170. n_recs++;
  1171. ptr += len;
  1172. if (type == MLOG_MULTI_REC_END) {
  1173. /* Found the end mark for the records */
  1174. break;
  1175. }
  1176. }
  1177. new_recovered_lsn = recv_calc_lsn_on_data_add(
  1178. recv_sys->recovered_lsn, total_len);
  1179. if (ut_dulint_cmp(new_recovered_lsn, recv_sys->scanned_lsn)
  1180. > 0) {
  1181. /* The log record filled a log block, and we require
  1182. that also the next log block should have been scanned
  1183. in */
  1184. return;
  1185. }
  1186. if (2 * n_recs * (sizeof(recv_t) + sizeof(recv_addr_t))
  1187. + total_len
  1188. + mem_heap_get_size(recv_sys->heap)
  1189.      + RECV_POOL_N_FREE_BLOCKS * UNIV_PAGE_SIZE
  1190. > buf_pool_get_curr_size()) {
  1191. /* Hash table of log records will grow too big:
  1192. empty it */
  1193. recv_apply_hashed_log_recs(FALSE);
  1194. }
  1195. ut_ad(2 * n_recs * (sizeof(recv_t) + sizeof(recv_addr_t))
  1196. + total_len
  1197. + mem_heap_get_size(recv_sys->heap)
  1198.      + RECV_POOL_N_FREE_BLOCKS * UNIV_PAGE_SIZE
  1199. < buf_pool_get_curr_size());
  1200. /* Add all the records to the hash table */
  1201. ptr = recv_sys->buf + recv_sys->recovered_offset;
  1202. for (;;) {
  1203. old_lsn = recv_sys->recovered_lsn;
  1204. len = recv_parse_log_rec(ptr, end_ptr, &type, &space,
  1205. &page_no, &body);
  1206. ut_a(len != 0);
  1207. ut_a(0 == ((ulint)*ptr & MLOG_SINGLE_REC_FLAG));
  1208. recv_sys->recovered_offset += len;
  1209. recv_sys->recovered_lsn = recv_calc_lsn_on_data_add(
  1210. old_lsn, len);
  1211. if (type == MLOG_MULTI_REC_END) {
  1212. /* Found the end mark for the records */
  1213. break;
  1214. }
  1215. if (store_to_hash) {
  1216. recv_add_to_hash_table(type, space, page_no,
  1217. body, ptr + len, old_lsn,
  1218. new_recovered_lsn);
  1219. } else {
  1220. /* In debug checking, check that the replicate
  1221. page has become identical with the original
  1222. page */
  1223. recv_compare_replicate(space, page_no);
  1224. }
  1225. ptr += len;
  1226. }
  1227. }
  1228. if (store_to_hash && buf_get_free_list_len()
  1229. < RECV_POOL_N_FREE_BLOCKS) {
  1230. /* Hash table of log records has grown too big: empty it;
  1231. FALSE means no ibuf operations allowed, as we cannot add
  1232. new records to the log yet: they would be produced by ibuf
  1233. operations */
  1234. recv_apply_hashed_log_recs(FALSE);
  1235. }     
  1236. goto loop;
  1237. }
  1238. /***********************************************************
  1239. Adds data from a new log block to the parsing buffer of recv_sys if
  1240. recv_sys->parse_start_lsn is non-zero. */
  1241. static
  1242. ibool
  1243. recv_sys_add_to_parsing_buf(
  1244. /*========================*/
  1245. /* out: TRUE if more data added */
  1246. byte* log_block, /* in: log block */
  1247. dulint scanned_lsn) /* in: lsn of how far we were able to find
  1248. data in this log block */
  1249. {
  1250. ulint more_len;
  1251. ulint data_len;
  1252. ulint start_offset;
  1253. ulint end_offset;
  1254. ut_ad(ut_dulint_cmp(scanned_lsn, recv_sys->scanned_lsn) >= 0);
  1255. if (ut_dulint_is_zero(recv_sys->parse_start_lsn)) {
  1256. /* Cannot start parsing yet because no start point for
  1257. it found */
  1258. return(FALSE);
  1259. }
  1260. data_len = log_block_get_data_len(log_block);
  1261. if (ut_dulint_cmp(recv_sys->parse_start_lsn, scanned_lsn) >= 0) {
  1262. return(FALSE);
  1263. } else if (ut_dulint_cmp(recv_sys->scanned_lsn, scanned_lsn) >= 0) {
  1264. return(FALSE);
  1265. } else if (ut_dulint_cmp(recv_sys->parse_start_lsn,
  1266. recv_sys->scanned_lsn) > 0) {
  1267. more_len = ut_dulint_minus(scanned_lsn,
  1268. recv_sys->parse_start_lsn);
  1269. } else {
  1270. more_len = ut_dulint_minus(scanned_lsn, recv_sys->scanned_lsn);
  1271. }
  1272. if (more_len == 0) {
  1273. return(FALSE);
  1274. }
  1275. ut_ad(data_len >= more_len);
  1276. start_offset = data_len - more_len;
  1277. if (start_offset < LOG_BLOCK_HDR_SIZE) {
  1278. start_offset = LOG_BLOCK_HDR_SIZE;
  1279. }
  1280. end_offset = data_len;
  1281. if (end_offset > OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) {
  1282. end_offset = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE;
  1283. }
  1284. ut_ad(start_offset <= end_offset);
  1285. if (start_offset < end_offset) {
  1286. ut_memcpy(recv_sys->buf + recv_sys->len,
  1287. log_block + start_offset, end_offset - start_offset);
  1288. recv_sys->len += end_offset - start_offset;
  1289. ut_ad(recv_sys->len <= RECV_PARSING_BUF_SIZE);
  1290. }
  1291. return(TRUE);
  1292. }
  1293. /***********************************************************
  1294. Moves the parsing buffer data left to the buffer start. */
  1295. static
  1296. void
  1297. recv_sys_justify_left_parsing_buf(void)
  1298. /*===================================*/
  1299. {
  1300. ut_memmove(recv_sys->buf, recv_sys->buf + recv_sys->recovered_offset,
  1301. recv_sys->len - recv_sys->recovered_offset);
  1302. recv_sys->len -= recv_sys->recovered_offset;
  1303. recv_sys->recovered_offset = 0;
  1304. }
  1305. /***********************************************************
  1306. Scans log from a buffer and stores new log data to the parsing buffer. Parses
  1307. and hashes the log records if new data found. */
  1308. ibool
  1309. recv_scan_log_recs(
  1310. /*===============*/
  1311. /* out: TRUE if limit_lsn has been reached, or
  1312. not able to scan any more in this log group */
  1313. ibool store_to_hash, /* in: TRUE if the records should be stored
  1314. to the hash table; this is set to FALSE if just
  1315. debug checking is needed */
  1316. byte* buf, /* in: buffer containing a log segment or
  1317. garbage */
  1318. ulint len, /* in: buffer length */
  1319. dulint start_lsn, /* in: buffer start lsn */
  1320. dulint* contiguous_lsn, /* in/out: it is known that all log groups
  1321. contain contiguous log data up to this lsn */
  1322. dulint* group_scanned_lsn)/* out: scanning succeeded up to this lsn */
  1323. {
  1324. byte* log_block;
  1325. ulint no;
  1326. dulint scanned_lsn;
  1327. ibool finished;
  1328. ulint data_len;
  1329. ibool more_data;
  1330. ut_ad(ut_dulint_get_low(start_lsn) % OS_FILE_LOG_BLOCK_SIZE == 0);
  1331. ut_ad(len % OS_FILE_LOG_BLOCK_SIZE == 0);
  1332. ut_ad(len > 0);
  1333. finished = FALSE;
  1334. log_block = buf;
  1335. scanned_lsn = start_lsn;
  1336. more_data = FALSE;
  1337. while (log_block < buf + len && !finished) {
  1338. no = log_block_get_hdr_no(log_block);
  1339. /* fprintf(stderr, "Log block header no %lun", no); */
  1340. if (no != log_block_get_trl_no(log_block)
  1341.     || no != log_block_convert_lsn_to_no(scanned_lsn)) {
  1342. /* Garbage or an incompletely written log block */
  1343. finished = TRUE;
  1344. break;
  1345. }
  1346. if (log_block_get_flush_bit(log_block)) {
  1347. /* This block was a start of a log flush operation:
  1348. we know that the previous flush operation must have
  1349. been completed for all log groups before this block
  1350. can have been flushed to any of the groups. Therefore,
  1351. we know that log data is contiguous up to scanned_lsn
  1352. in all non-corrupt log groups. */
  1353. if (ut_dulint_cmp(scanned_lsn, *contiguous_lsn) > 0) {
  1354. *contiguous_lsn = scanned_lsn;
  1355. }
  1356. }
  1357. data_len = log_block_get_data_len(log_block);
  1358. if ((store_to_hash || (data_len == OS_FILE_LOG_BLOCK_SIZE))
  1359.     && (ut_dulint_cmp(ut_dulint_add(scanned_lsn, data_len),
  1360. recv_sys->scanned_lsn) > 0)
  1361.     && (recv_sys->scanned_checkpoint_no > 0)
  1362.     && (log_block_get_checkpoint_no(log_block)
  1363.        < recv_sys->scanned_checkpoint_no)
  1364.     && (recv_sys->scanned_checkpoint_no
  1365. - log_block_get_checkpoint_no(log_block)
  1366. > 0x80000000)) {
  1367. /* Garbage from a log buffer flush which was made
  1368. before the most recent database recovery */
  1369. finished = TRUE;
  1370. #ifdef UNIV_LOG_DEBUG
  1371. /* This is not really an error, but currently
  1372. we stop here in the debug version: */
  1373. ut_error;
  1374. #endif
  1375. break;
  1376. }     
  1377. if (ut_dulint_is_zero(recv_sys->parse_start_lsn)
  1378. && (log_block_get_first_rec_group(log_block) > 0)) {
  1379. /* We found a point from which to start the parsing
  1380. of log records */
  1381. recv_sys->parse_start_lsn =
  1382. ut_dulint_add(scanned_lsn,
  1383.    log_block_get_first_rec_group(log_block));
  1384. recv_sys->scanned_lsn = recv_sys->parse_start_lsn;
  1385. recv_sys->recovered_lsn = recv_sys->parse_start_lsn;
  1386. }
  1387. scanned_lsn = ut_dulint_add(scanned_lsn, data_len);
  1388. if (ut_dulint_cmp(scanned_lsn, recv_sys->scanned_lsn) > 0) {
  1389. /* We were able to find more log data: add it to the
  1390. parsing buffer if parse_start_lsn is already non-zero */
  1391. more_data = recv_sys_add_to_parsing_buf(log_block,
  1392. scanned_lsn);
  1393. recv_sys->scanned_lsn = scanned_lsn;
  1394. recv_sys->scanned_checkpoint_no =
  1395. log_block_get_checkpoint_no(log_block);
  1396. }
  1397. if (data_len < OS_FILE_LOG_BLOCK_SIZE) {
  1398. /* Log data for this group ends here */
  1399. finished = TRUE;
  1400. } else {
  1401. log_block += OS_FILE_LOG_BLOCK_SIZE;
  1402. }
  1403. }
  1404. *group_scanned_lsn = scanned_lsn;
  1405. if (more_data) {
  1406. fprintf(stderr, 
  1407. "Innobase: Doing recovery: scanned up to log sequence number %lu %lun",
  1408. ut_dulint_get_high(*group_scanned_lsn),
  1409. ut_dulint_get_low(*group_scanned_lsn));
  1410. /* Try to parse more log records */
  1411. recv_parse_log_recs(store_to_hash);
  1412. if (recv_sys->recovered_offset > RECV_PARSING_BUF_SIZE / 4) {
  1413. /* Move parsing buffer data to the buffer start */
  1414. recv_sys_justify_left_parsing_buf();
  1415. }
  1416. }
  1417. return(finished);
  1418. }
  1419. /***********************************************************
  1420. Scans log from a buffer and stores new log data to the parsing buffer. Parses
  1421. and hashes the log records if new data found. */
  1422. static
  1423. void
  1424. recv_group_scan_log_recs(
  1425. /*=====================*/
  1426. log_group_t* group, /* in: log group */
  1427. dulint* contiguous_lsn, /* in/out: it is known that all log groups
  1428. contain contiguous log data up to this lsn */
  1429. dulint* group_scanned_lsn)/* out: scanning succeeded up to this lsn */
  1430. {
  1431. ibool finished;
  1432. dulint start_lsn;
  1433. dulint end_lsn;
  1434. finished = FALSE;
  1435. start_lsn = *contiguous_lsn;
  1436. while (!finished) {
  1437. end_lsn = ut_dulint_add(start_lsn, RECV_SCAN_SIZE);
  1438. log_group_read_log_seg(LOG_RECOVER, log_sys->buf,
  1439. group, start_lsn, end_lsn);
  1440. finished = recv_scan_log_recs(TRUE, log_sys->buf,
  1441. RECV_SCAN_SIZE, start_lsn,
  1442. contiguous_lsn,
  1443. group_scanned_lsn);
  1444. start_lsn = end_lsn;
  1445. }
  1446. if (log_debug_writes) {
  1447. fprintf(stderr,
  1448. "Innobase: Scanned group %lu up to log sequence number %lu %lun",
  1449. group->id,
  1450. ut_dulint_get_high(*group_scanned_lsn),
  1451. ut_dulint_get_low(*group_scanned_lsn));
  1452. }
  1453. }
  1454. /************************************************************
  1455. Recovers from a checkpoint. When this function returns, the database is able
  1456. to start processing of new user transactions, but the function
  1457. recv_recovery_from_checkpoint_finish should be called later to complete
  1458. the recovery and free the resources used in it. */
  1459. ulint
  1460. recv_recovery_from_checkpoint_start(
  1461. /*================================*/
  1462. /* out: error code or DB_SUCCESS */
  1463. ulint type, /* in: LOG_CHECKPOINT or LOG_ARCHIVE */
  1464. dulint limit_lsn, /* in: recover up to this lsn if possible */
  1465. dulint min_flushed_lsn,/* in: min flushed lsn from data files */
  1466. dulint max_flushed_lsn)/* in: max flushed lsn from data files */
  1467. {
  1468. log_group_t* group;
  1469. log_group_t* max_cp_group;
  1470. log_group_t* up_to_date_group;
  1471. ulint max_cp_field;
  1472. dulint checkpoint_lsn;
  1473. dulint checkpoint_no;
  1474. dulint old_scanned_lsn;
  1475. dulint group_scanned_lsn;
  1476. dulint contiguous_lsn;
  1477. dulint archived_lsn;
  1478. ulint capacity;
  1479. byte* buf;
  1480. ulint err;
  1481. ut_ad((type != LOG_CHECKPOINT)
  1482. || (ut_dulint_cmp(limit_lsn, ut_dulint_max) == 0));
  1483. if (type == LOG_CHECKPOINT) {
  1484. recv_sys_create();
  1485. recv_sys_init();
  1486. }
  1487. sync_order_checks_on = TRUE;
  1488. recv_recovery_on = TRUE;
  1489. recv_sys->limit_lsn = limit_lsn;
  1490. mutex_enter(&(log_sys->mutex));
  1491. /* Look for the latest checkpoint from any of the log groups */
  1492. err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
  1493. if (err != DB_SUCCESS) {
  1494. mutex_exit(&(log_sys->mutex));
  1495. return(err);
  1496. }
  1497. log_group_read_checkpoint_info(max_cp_group, max_cp_field);
  1498. buf = log_sys->checkpoint_buf;
  1499. checkpoint_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_LSN);
  1500. checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
  1501. archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN);
  1502. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  1503. while (group) {
  1504. log_checkpoint_get_nth_group_info(buf, group->id,
  1505. &(group->archived_file_no),
  1506. &(group->archived_offset));
  1507. group = UT_LIST_GET_NEXT(log_groups, group);
  1508. }
  1509. if (type == LOG_CHECKPOINT) {
  1510. /* Start reading the log groups from the checkpoint lsn up. The
  1511. variable contiguous_lsn contains an lsn up to which the log is
  1512. known to be contiguously written to all log groups. */
  1513. recv_sys->parse_start_lsn = checkpoint_lsn;
  1514. recv_sys->scanned_lsn = checkpoint_lsn;
  1515. recv_sys->scanned_checkpoint_no = 0;
  1516. recv_sys->recovered_lsn = checkpoint_lsn;
  1517. /* NOTE: we always do recovery at startup, but only if
  1518. there is something wrong we will print a message to the
  1519. user about recovery: */
  1520. if (ut_dulint_cmp(checkpoint_lsn, max_flushed_lsn) != 0
  1521.         || ut_dulint_cmp(checkpoint_lsn, min_flushed_lsn) != 0) {
  1522.      fprintf(stderr,
  1523. "Innobase: Database was not shut down normally.n"
  1524.      "Innobase: Starting recovery from log files...n");
  1525. fprintf(stderr, 
  1526. "Innobase: Starting log scan based on checkpoint atn"
  1527. "Innobase: log sequence number %lu %lun",
  1528.   ut_dulint_get_high(checkpoint_lsn),
  1529. ut_dulint_get_low(checkpoint_lsn));
  1530. }
  1531. }
  1532. contiguous_lsn = ut_dulint_align_down(recv_sys->scanned_lsn,
  1533. OS_FILE_LOG_BLOCK_SIZE);
  1534. if (type == LOG_ARCHIVE) {
  1535.   /* Try to recover the remaining part from logs: first from
  1536. the logs of the archived group */
  1537. group = recv_sys->archive_group;
  1538. capacity = log_group_get_capacity(group);
  1539. if ((ut_dulint_cmp(recv_sys->scanned_lsn,
  1540. ut_dulint_add(checkpoint_lsn, capacity)) > 0)
  1541.    || (ut_dulint_cmp(checkpoint_lsn,
  1542. ut_dulint_add(recv_sys->scanned_lsn, capacity)) > 0)) {
  1543. mutex_exit(&(log_sys->mutex));
  1544. /* The group does not contain enough log: probably
  1545. an archived log file was missing or corrupt */
  1546. return(DB_ERROR);
  1547. }
  1548. recv_group_scan_log_recs(group, &contiguous_lsn,
  1549. &group_scanned_lsn);
  1550. if (ut_dulint_cmp(recv_sys->scanned_lsn, checkpoint_lsn) < 0) {
  1551. mutex_exit(&(log_sys->mutex));
  1552. /* The group did not contain enough log: an archived
  1553. log file was missing or invalid, or the log group
  1554. was corrupt */
  1555. return(DB_ERROR);
  1556. }
  1557. group->scanned_lsn = group_scanned_lsn;
  1558. up_to_date_group = group;
  1559. } else {
  1560. up_to_date_group = max_cp_group;
  1561. }
  1562. ut_ad(RECV_SCAN_SIZE <= log_sys->buf_size);
  1563. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  1564. if ((type == LOG_ARCHIVE) && (group == recv_sys->archive_group)) {
  1565. group = UT_LIST_GET_NEXT(log_groups, group);
  1566. }
  1567. while (group) {
  1568. old_scanned_lsn = recv_sys->scanned_lsn;
  1569. recv_group_scan_log_recs(group, &contiguous_lsn,
  1570. &group_scanned_lsn);
  1571. group->scanned_lsn = group_scanned_lsn;
  1572. if (ut_dulint_cmp(old_scanned_lsn, group_scanned_lsn) < 0) {
  1573. /* We found a more up-to-date group */
  1574. up_to_date_group = group;
  1575. }
  1576. if ((type == LOG_ARCHIVE)
  1577. && (group == recv_sys->archive_group)) {
  1578. group = UT_LIST_GET_NEXT(log_groups, group);
  1579. }
  1580. group = UT_LIST_GET_NEXT(log_groups, group);
  1581. }
  1582. if (ut_dulint_cmp(recv_sys->recovered_lsn, checkpoint_lsn) < 0) {
  1583. mutex_exit(&(log_sys->mutex));
  1584. if (ut_dulint_cmp(recv_sys->recovered_lsn, limit_lsn) >= 0) {
  1585. return(DB_SUCCESS);
  1586. }
  1587. ut_error;
  1588. return(DB_ERROR);
  1589. }
  1590. /* Synchronize the uncorrupted log groups to the most up-to-date log
  1591. group; we also copy checkpoint info to groups */
  1592. log_sys->next_checkpoint_lsn = checkpoint_lsn;
  1593. log_sys->next_checkpoint_no = ut_dulint_add(checkpoint_no, 1);
  1594. log_sys->archived_lsn = archived_lsn;
  1595. recv_synchronize_groups(up_to_date_group);
  1596. log_sys->lsn = recv_sys->recovered_lsn;
  1597. ut_memcpy(log_sys->buf, recv_sys->last_block, OS_FILE_LOG_BLOCK_SIZE);
  1598. log_sys->buf_free = ut_dulint_get_low(log_sys->lsn)
  1599. % OS_FILE_LOG_BLOCK_SIZE;
  1600. log_sys->buf_next_to_write = log_sys->buf_free;
  1601. log_sys->written_to_some_lsn = log_sys->lsn;
  1602. log_sys->written_to_all_lsn = log_sys->lsn;
  1603. log_sys->last_checkpoint_lsn = checkpoint_lsn;
  1604. log_sys->next_checkpoint_no = ut_dulint_add(checkpoint_no, 1);
  1605. if (ut_dulint_cmp(archived_lsn, ut_dulint_max) == 0) {
  1606. log_sys->archiving_state = LOG_ARCH_OFF;
  1607. }
  1608. mutex_enter(&(recv_sys->mutex));
  1609. recv_sys->apply_log_recs = TRUE;
  1610.   mutex_exit(&(recv_sys->mutex));
  1611. mutex_exit(&(log_sys->mutex));
  1612. sync_order_checks_on = FALSE;
  1613. /* The database is now ready to start almost normal processing of user
  1614. transactions: transaction rollbacks and the application of the log
  1615. records in the hash table can be run in background. */
  1616. return(DB_SUCCESS);
  1617. }
  1618. /************************************************************
  1619. Completes recovery from a checkpoint. */
  1620. void
  1621. recv_recovery_from_checkpoint_finish(void)
  1622. /*======================================*/
  1623. {
  1624. /* Rollback the uncommitted transactions which have no user session */
  1625. trx_rollback_all_without_sess();
  1626. /* Apply the hashed log records to the respective file pages */
  1627. recv_apply_hashed_log_recs(TRUE);
  1628. if (log_debug_writes) {
  1629. fprintf(stderr,
  1630. "Innobase: Log records applied to the databasen");
  1631. }
  1632. /* Free the resources of the recovery system */
  1633. recv_recovery_on = FALSE;
  1634. #ifndef UNIV_LOG_DEBUG
  1635. recv_sys_free();
  1636. #endif
  1637. }
  1638. /**********************************************************
  1639. Resets the logs. The contents of log files will be lost! */
  1640. void
  1641. recv_reset_logs(
  1642. /*============*/
  1643. dulint lsn, /* in: reset to this lsn rounded up to
  1644. be divisible by OS_FILE_LOG_BLOCK_SIZE,
  1645. after which we add LOG_BLOCK_HDR_SIZE */
  1646. ulint arch_log_no, /* in: next archived log file number */
  1647. ibool new_logs_created)/* in: TRUE if resetting logs is done
  1648. at the log creation; FALSE if it is done
  1649. after archive recovery */
  1650. {
  1651. log_group_t* group;
  1652. ut_ad(mutex_own(&(log_sys->mutex)));
  1653. log_sys->lsn = ut_dulint_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE);
  1654. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  1655. while (group) {
  1656. group->lsn = log_sys->lsn;
  1657. group->lsn_offset = LOG_FILE_HDR_SIZE;
  1658. group->archived_file_no = arch_log_no;
  1659. group->archived_offset = 0;
  1660. if (!new_logs_created) {
  1661. recv_truncate_group(group, group->lsn, group->lsn,
  1662. group->lsn, group->lsn);
  1663. }
  1664. group = UT_LIST_GET_NEXT(log_groups, group);
  1665. }
  1666. log_sys->buf_next_to_write = 0;
  1667. log_sys->written_to_some_lsn = log_sys->lsn;
  1668. log_sys->written_to_all_lsn = log_sys->lsn;
  1669. log_sys->next_checkpoint_no = ut_dulint_zero;
  1670. log_sys->last_checkpoint_lsn = ut_dulint_zero;
  1671. log_sys->archived_lsn = log_sys->lsn;
  1672. log_block_init(log_sys->buf, log_sys->lsn);
  1673. log_block_set_first_rec_group(log_sys->buf, LOG_BLOCK_HDR_SIZE);
  1674. log_sys->buf_free = LOG_BLOCK_HDR_SIZE;
  1675. log_sys->lsn = ut_dulint_add(log_sys->lsn, LOG_BLOCK_HDR_SIZE);
  1676. mutex_exit(&(log_sys->mutex));
  1677. /* Reset the checkpoint fields in logs */
  1678. log_make_checkpoint_at(ut_dulint_max, TRUE);
  1679. log_make_checkpoint_at(ut_dulint_max, TRUE);
  1680. mutex_enter(&(log_sys->mutex));
  1681. }
  1682. /**********************************************************
  1683. Reads from the archive of a log group and performs recovery. */
  1684. static
  1685. ibool
  1686. log_group_recover_from_archive_file(
  1687. /*================================*/
  1688. /* out: TRUE if no more complete
  1689. consistent archive files */
  1690. log_group_t* group) /* in: log group */
  1691. {
  1692. os_file_t file_handle;
  1693. dulint start_lsn;
  1694. dulint file_end_lsn;
  1695. dulint dummy_lsn;
  1696. dulint scanned_lsn;
  1697. ulint len;
  1698. char name[10000];
  1699. ibool ret;
  1700. byte* buf;
  1701. ulint read_offset;
  1702. ulint file_size;
  1703. ulint file_size_high;
  1704. int input_char;
  1705. try_open_again:
  1706. buf = log_sys->buf;
  1707. /* Add the file to the archive file space; open the file */
  1708. log_archived_file_name_gen(name, group->id, group->archived_file_no);
  1709. fil_reserve_right_to_open();
  1710. file_handle = os_file_create(name, OS_FILE_OPEN, OS_FILE_AIO, &ret);
  1711. if (ret == FALSE) {
  1712. fil_release_right_to_open();
  1713. ask_again:
  1714. fprintf(stderr, 
  1715. "Innobase: Do you want to copy additional archived log filesn"
  1716. "Innobase: to the directoryn");
  1717. fprintf(stderr, 
  1718. "Innobase: or were these all the files needed in recovery?n");
  1719. fprintf(stderr, 
  1720. "Innobase: (Y == copy more files; N == this is all)?");
  1721. input_char = getchar();
  1722. if (input_char == (int) 'N') {
  1723. return(TRUE);
  1724. } else if (input_char == (int) 'Y') {
  1725. goto try_open_again;
  1726. } else {
  1727. goto ask_again;
  1728. }
  1729. }
  1730. ret = os_file_get_size(file_handle, &file_size, &file_size_high);
  1731. ut_a(ret);
  1732. ut_a(file_size_high == 0);
  1733. fprintf(stderr, "Innobase: Opened archived log file %sn", name);
  1734. ret = os_file_close(file_handle);
  1735. if (file_size < LOG_FILE_HDR_SIZE) {
  1736. fprintf(stderr,
  1737. "Innobase: Archive file header incomplete %sn", name);
  1738.     
  1739. return(TRUE);
  1740. }
  1741. ut_a(ret);
  1742. fil_release_right_to_open();
  1743. /* Add the archive file as a node to the space */
  1744. fil_node_create(name, 1 + file_size / UNIV_PAGE_SIZE,
  1745. group->archive_space_id);
  1746. ut_a(RECV_SCAN_SIZE >= LOG_FILE_HDR_SIZE);
  1747. /* Read the archive file header */
  1748. fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, group->archive_space_id, 0, 0,
  1749. LOG_FILE_HDR_SIZE, buf, NULL);
  1750. /* Check if the archive file header is consistent */
  1751. if (mach_read_from_4(buf + LOG_GROUP_ID) != group->id
  1752.     || mach_read_from_4(buf + LOG_FILE_NO)
  1753. != group->archived_file_no) {
  1754. fprintf(stderr,
  1755. "Innobase: Archive file header inconsistent %sn", name);
  1756.     
  1757. return(TRUE);
  1758. }
  1759. if (!mach_read_from_4(buf + LOG_FILE_ARCH_COMPLETED)) {
  1760. fprintf(stderr,
  1761. "Innobase: Archive file not completely written %sn", name);
  1762. return(TRUE);
  1763. }
  1764. start_lsn = mach_read_from_8(buf + LOG_FILE_START_LSN);
  1765. file_end_lsn = mach_read_from_8(buf + LOG_FILE_END_LSN);
  1766. if (ut_dulint_is_zero(recv_sys->scanned_lsn)) {
  1767. if (ut_dulint_cmp(recv_sys->parse_start_lsn, start_lsn) < 0) {
  1768. fprintf(stderr, 
  1769. "Innobase: Archive log file %s starts from too big a lsnn",
  1770. name);     
  1771. return(TRUE);
  1772. }
  1773. recv_sys->scanned_lsn = start_lsn;
  1774. }
  1775. if (ut_dulint_cmp(recv_sys->scanned_lsn, start_lsn) != 0) {
  1776. fprintf(stderr,
  1777. "Innobase: Archive log file %s starts from a wrong lsnn",
  1778. name);
  1779. return(TRUE);
  1780. }
  1781. read_offset = LOG_FILE_HDR_SIZE;
  1782. for (;;) {
  1783. len = RECV_SCAN_SIZE;
  1784. if (read_offset + len > file_size) {
  1785. len = ut_calc_align_down(file_size - read_offset,
  1786. OS_FILE_LOG_BLOCK_SIZE);
  1787. }
  1788. if (len == 0) {
  1789. break;
  1790. }
  1791. if (log_debug_writes) {
  1792. fprintf(stderr, 
  1793. "Innobase: Archive read starting at lsn %lu %lu, len %lu from file %sn",
  1794. ut_dulint_get_high(start_lsn),
  1795. ut_dulint_get_low(start_lsn),
  1796. len, name);
  1797. }
  1798. fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE,
  1799. group->archive_space_id, read_offset / UNIV_PAGE_SIZE,
  1800. read_offset % UNIV_PAGE_SIZE, len, buf, NULL);
  1801. ret = recv_scan_log_recs(TRUE, buf, len, start_lsn,
  1802. &dummy_lsn, &scanned_lsn);
  1803. if (ut_dulint_cmp(scanned_lsn, file_end_lsn) == 0) {
  1804. return(FALSE);
  1805. }
  1806. if (ret) {
  1807. fprintf(stderr,
  1808. "Innobase: Archive log file %s does not scan rightn",
  1809. name);     
  1810. return(TRUE);
  1811. }
  1812. read_offset += len;
  1813. start_lsn = ut_dulint_add(start_lsn, len);
  1814. ut_ad(ut_dulint_cmp(start_lsn, scanned_lsn) == 0);
  1815. }
  1816. return(FALSE);
  1817. }
  1818. /************************************************************
  1819. Recovers from archived log files, and also from log files, if they exist. */
  1820. ulint
  1821. recv_recovery_from_archive_start(
  1822. /*=============================*/
  1823. /* out: error code or DB_SUCCESS */
  1824. dulint min_flushed_lsn,/* in: min flushed lsn field from the
  1825. data files */
  1826. dulint limit_lsn, /* in: recover up to this lsn if possible */
  1827. ulint first_log_no) /* in: number of the first archived log file
  1828. to use in the recovery; the file will be
  1829. searched from INNOBASE_LOG_ARCH_DIR specified
  1830. in server config file */
  1831. {
  1832. log_group_t* group;
  1833. ulint group_id;
  1834. ulint trunc_len;
  1835. ibool ret;
  1836. ulint err;
  1837. recv_sys_create();
  1838. recv_sys_init();
  1839. sync_order_checks_on = TRUE;
  1840. recv_recovery_on = TRUE;
  1841. recv_recovery_from_backup_on = TRUE;
  1842. recv_sys->limit_lsn = limit_lsn;
  1843. group_id = 0;
  1844. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  1845. while (group) {
  1846. if (group->id == group_id) {
  1847.   break;
  1848. }
  1849. group = UT_LIST_GET_NEXT(log_groups, group);
  1850. }
  1851. if (!group) {
  1852. fprintf(stderr,
  1853. "Innobase: There is no log group defined with id %lu!n",
  1854. group_id);
  1855. return(DB_ERROR);
  1856. }
  1857. group->archived_file_no = first_log_no;
  1858. recv_sys->parse_start_lsn = min_flushed_lsn;
  1859. recv_sys->scanned_lsn = ut_dulint_zero;
  1860. recv_sys->scanned_checkpoint_no = 0;
  1861. recv_sys->recovered_lsn = recv_sys->parse_start_lsn;
  1862. recv_sys->archive_group = group;
  1863. ret = FALSE;
  1864. mutex_enter(&(log_sys->mutex));
  1865. while (!ret) {
  1866. ret = log_group_recover_from_archive_file(group);
  1867. /* Close and truncate a possible processed archive file
  1868. from the file space */
  1869. trunc_len = UNIV_PAGE_SIZE
  1870.     * fil_space_get_size(group->archive_space_id);
  1871. if (trunc_len > 0) {
  1872. fil_space_truncate_start(group->archive_space_id,
  1873. trunc_len);
  1874. }
  1875. group->archived_file_no++;
  1876. }
  1877. if (ut_dulint_cmp(recv_sys->recovered_lsn, limit_lsn) < 0) {
  1878. if (ut_dulint_is_zero(recv_sys->scanned_lsn)) {
  1879. recv_sys->scanned_lsn = recv_sys->parse_start_lsn;
  1880. }
  1881. mutex_exit(&(log_sys->mutex));
  1882. err = recv_recovery_from_checkpoint_start(LOG_ARCHIVE,
  1883. limit_lsn,
  1884. ut_dulint_max,
  1885. ut_dulint_max);
  1886. if (err != DB_SUCCESS) {
  1887. return(err);
  1888. }
  1889. mutex_enter(&(log_sys->mutex));
  1890. }
  1891. if (ut_dulint_cmp(limit_lsn, ut_dulint_max) != 0) {
  1892. recv_apply_hashed_log_recs(FALSE);
  1893. recv_reset_logs(recv_sys->recovered_lsn, 0, FALSE);
  1894. }
  1895. mutex_exit(&(log_sys->mutex));
  1896. sync_order_checks_on = FALSE;
  1897. return(DB_SUCCESS);
  1898. }
  1899. /************************************************************
  1900. Completes recovery from archive. */
  1901. void
  1902. recv_recovery_from_archive_finish(void)
  1903. /*===================================*/
  1904. {
  1905. recv_recovery_from_checkpoint_finish();
  1906. recv_recovery_from_backup_on = FALSE;
  1907. }