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

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. Transaction undo log record
  3. (c) 1996 Innobase Oy
  4. Created 3/26/1996 Heikki Tuuri
  5. *******************************************************/
  6. #include "trx0rec.h"
  7. #ifdef UNIV_NONINL
  8. #include "trx0rec.ic"
  9. #endif
  10. #include "fsp0fsp.h"
  11. #include "mach0data.h"
  12. #include "trx0rseg.h"
  13. #include "trx0trx.h"
  14. #include "trx0undo.h"
  15. #include "dict0dict.h"
  16. #include "ut0mem.h"
  17. #include "row0upd.h"
  18. #include "que0que.h"
  19. #include "trx0purge.h"
  20. #include "row0row.h"
  21. /*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
  22. /**************************************************************************
  23. Writes the mtr log entry of the inserted undo log record on the undo log
  24. page. */
  25. UNIV_INLINE
  26. void
  27. trx_undof_page_add_undo_rec_log(
  28. /*============================*/
  29. page_t* undo_page, /* in: undo log page */
  30. ulint old_free, /* in: start offset of the inserted entry */
  31. ulint new_free, /* in: end offset of the entry */
  32. mtr_t* mtr) /* in: mtr */
  33. {
  34. byte* log_ptr;
  35. ulint len;
  36. #ifdef notdefined
  37. ulint i;
  38. byte* prev_rec_ptr;
  39. byte* ptr;
  40. ulint min_len;
  41. ut_ad(new_free >= old_free + 4);
  42. i = 0;
  43. ptr = undo_page + old_free + 2;
  44. if (old_free > mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  45. + TRX_UNDO_PAGE_START)) {
  46. prev_rec_ptr = undo_page + mach_read_from_2(ptr - 4) + 2;
  47. min_len = ut_min(new_free - old_free - 4,
  48.  (undo_page + old_free - 2) - prev_rec_ptr); 
  49. for (;;) {
  50. if (i >= min_len) {
  51. break;
  52. } else if ((*ptr == *prev_rec_ptr)
  53.    || ((*ptr == *prev_rec_ptr + 1)
  54.        && (ptr + 1 == suffix))) {
  55. i++;
  56. ptr++;
  57. prev_rec_ptr++;
  58. } else {
  59. break;
  60. }
  61. }
  62. }
  63. mlog_write_initial_log_record(undo_page, MLOG_UNDO_INSERT, mtr);
  64. mlog_catenate_ulint(mtr, old_free, MLOG_2BYTES);
  65. mlog_catenate_ulint_compressed(mtr, i);
  66. mlog_catenate_string(mtr, ptr, new_free - old_free - 2 - i);
  67. #endif
  68. log_ptr = mlog_open(mtr, 30 + MLOG_BUF_MARGIN);
  69. if (log_ptr == NULL) {
  70. return;
  71. }
  72. log_ptr = mlog_write_initial_log_record_fast(undo_page,
  73. MLOG_UNDO_INSERT, log_ptr, mtr);
  74. len = new_free - old_free - 4;
  75. mach_write_to_2(log_ptr, len);
  76. log_ptr += 2;
  77. if (len < 256) {
  78. ut_memcpy(log_ptr, undo_page + old_free + 2, len);
  79. log_ptr += len;
  80. }
  81. mlog_close(mtr, log_ptr);
  82. if (len >= MLOG_BUF_MARGIN) {
  83. mlog_catenate_string(mtr, undo_page + old_free + 2, len);
  84. }
  85. }
  86. /***************************************************************
  87. Parses a redo log record of adding an undo log record. */
  88. byte*
  89. trx_undo_parse_add_undo_rec(
  90. /*========================*/
  91. /* out: end of log record or NULL */
  92. byte* ptr, /* in: buffer */
  93. byte* end_ptr,/* in: buffer end */
  94. page_t* page) /* in: page or NULL */
  95. {
  96. ulint len;
  97. byte* rec;
  98. ulint first_free;
  99. if (end_ptr < ptr + 2) {
  100. return(NULL);
  101. }
  102. len = mach_read_from_2(ptr);
  103. ptr += 2;
  104. if (end_ptr < ptr + len) {
  105. return(NULL);
  106. }
  107. if (page == NULL) {
  108. return(ptr + len);
  109. }
  110. first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
  111. + TRX_UNDO_PAGE_FREE);
  112. rec = page + first_free;
  113. mach_write_to_2(rec, first_free + 4 + len);
  114. mach_write_to_2(rec + 2 + len, first_free);
  115. mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
  116. first_free + 4 + len);
  117. ut_memcpy(rec + 2, ptr, len);
  118. return(ptr + len);
  119. }
  120. /**************************************************************************
  121. Calculates the free space left for extending an undo log record. */
  122. UNIV_INLINE
  123. ulint
  124. trx_undo_left(
  125. /*==========*/
  126. /* out: bytes left */
  127. page_t* page, /* in: undo log page */
  128. byte* ptr) /* in: pointer to page */
  129. {
  130. /* The '- 10' is a safety margin, in case we have some small
  131. calculation error below */
  132. return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
  133. }
  134. /**************************************************************************
  135. Reports in the undo log of an insert of a clustered index record. */
  136. static
  137. ulint
  138. trx_undo_page_report_insert(
  139. /*========================*/
  140. /* out: offset of the inserted entry
  141. on the page if succeed, 0 if fail */
  142. page_t*  undo_page, /* in: undo log page */
  143. trx_t* trx, /* in: transaction */
  144. dict_index_t* index, /* in: clustered index */
  145. dtuple_t* clust_entry, /* in: index entry which will be
  146. inserted to the clustered index */
  147. mtr_t* mtr) /* in: mtr */
  148. {
  149. ulint first_free;
  150. byte* ptr;
  151. ulint len;
  152. dfield_t* field;
  153. ulint flen;
  154. ulint i;
  155. ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  156. + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
  157. first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  158. + TRX_UNDO_PAGE_FREE);
  159. ptr = undo_page + first_free;
  160. ut_ad(first_free <= UNIV_PAGE_SIZE);
  161. if (trx_undo_left(undo_page, ptr) < 30) {
  162. /* NOTE: the value 30 must be big enough such that the general
  163. fields written below fit on the undo log page */
  164. return(0);
  165. }
  166. /* Reserve 2 bytes for the pointer to the next undo log record */
  167. ptr += 2;
  168. /* Store first some general parameters to the undo log */ 
  169. mach_write_to_1(ptr, TRX_UNDO_INSERT_REC);
  170. ptr++;
  171. len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
  172. ptr += len;
  173. len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
  174. ptr += len;
  175. /*----------------------------------------*/
  176. /* Store then the fields required to uniquely determine the record
  177. to be inserted in the clustered index */
  178. for (i = 0; i < dict_index_get_n_unique(index); i++) {
  179. field = dtuple_get_nth_field(clust_entry, i);
  180. flen = dfield_get_len(field);
  181. if (trx_undo_left(undo_page, ptr) < 5) {
  182. return(0);
  183. }
  184. len = mach_write_compressed(ptr, flen); 
  185. ptr += len;
  186. if (flen != UNIV_SQL_NULL) {
  187. if (trx_undo_left(undo_page, ptr) < flen) {
  188. return(0);
  189. }
  190. ut_memcpy(ptr, dfield_get_data(field), flen);
  191. ptr += flen;
  192. }
  193. }
  194. if (trx_undo_left(undo_page, ptr) < 2) {
  195. return(0);
  196. }
  197. /*----------------------------------------*/
  198. /* Write pointers to the previous and the next undo log records */
  199. if (trx_undo_left(undo_page, ptr) < 2) {
  200. return(0);
  201. }
  202. mach_write_to_2(ptr, first_free);
  203. ptr += 2;
  204. mach_write_to_2(undo_page + first_free, ptr - undo_page);
  205. mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
  206. ptr - undo_page);
  207. /* Write the log entry to the REDO log of this change in the UNDO log */
  208. trx_undof_page_add_undo_rec_log(undo_page, first_free,
  209. ptr - undo_page, mtr);
  210. return(first_free);
  211. }
  212. /**************************************************************************
  213. Reads from an undo log record the general parameters. */
  214. byte*
  215. trx_undo_rec_get_pars(
  216. /*==================*/
  217. /* out: remaining part of undo log
  218. record after reading these values */
  219. trx_undo_rec_t* undo_rec, /* in: undo log record */
  220. ulint* type, /* out: undo record type:
  221. TRX_UNDO_INSERT_REC, ... */
  222. ulint* cmpl_info, /* out: compiler info, relevant only
  223. for update type records */
  224. dulint* undo_no, /* out: undo log record number */
  225. dulint* table_id) /* out: table id */
  226. {
  227. byte* ptr;
  228. ulint len;
  229. ulint type_cmpl;
  230. ptr = undo_rec + 2;
  231. type_cmpl = mach_read_from_1(ptr);
  232. ptr++;
  233. *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
  234. *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
  235. *undo_no = mach_dulint_read_much_compressed(ptr); 
  236. len = mach_dulint_get_much_compressed_size(*undo_no);
  237. ptr += len;
  238. *table_id = mach_dulint_read_much_compressed(ptr); 
  239. len = mach_dulint_get_much_compressed_size(*table_id);
  240. ptr += len;
  241. return(ptr);
  242. }
  243. /**************************************************************************
  244. Reads from an undo log record a stored column value. */
  245. UNIV_INLINE
  246. byte*
  247. trx_undo_rec_get_col_val(
  248. /*=====================*/
  249. /* out: remaining part of undo log record after
  250. reading these values */
  251. byte* ptr, /* in: pointer to remaining part of undo log record */
  252. byte** field, /* out: pointer to stored field */
  253. ulint* len) /* out: length of the field, or UNIV_SQL_NULL */
  254. {
  255. *len = mach_read_compressed(ptr); 
  256. ptr += mach_get_compressed_size(*len);
  257. *field = ptr;
  258. if (*len != UNIV_SQL_NULL) {
  259. ptr += *len;
  260. }
  261. return(ptr);
  262. }
  263. /***********************************************************************
  264. Builds a row reference from an undo log record. */
  265. byte*
  266. trx_undo_rec_get_row_ref(
  267. /*=====================*/
  268. /* out: pointer to remaining part of undo
  269. record */
  270. byte* ptr, /* in: remaining part of a copy of an undo log
  271. record, at the start of the row reference;
  272. NOTE that this copy of the undo log record must
  273. be preserved as long as the row reference is
  274. used, as we do NOT copy the data in the
  275. record! */
  276. dict_index_t* index, /* in: clustered index */
  277. dtuple_t** ref, /* out, own: row reference */
  278. mem_heap_t* heap) /* in: memory heap from which the memory
  279. needed is allocated */
  280. {
  281. ulint i;
  282. dfield_t* dfield;
  283. byte* field;
  284. ulint len;
  285. ulint ref_len;
  286. ut_ad(index && ptr && ref && heap);
  287. ref_len = dict_index_get_n_unique(index);
  288. *ref = dtuple_create(heap, ref_len);
  289. dict_index_copy_types(*ref, index, ref_len);
  290. for (i = 0; i < ref_len; i++) {
  291. dfield = dtuple_get_nth_field(*ref, i);
  292. ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
  293. dfield_set_data(dfield, field, len);
  294. }
  295. return(ptr);
  296. }
  297. /***********************************************************************
  298. Skips a row reference from an undo log record. */
  299. byte*
  300. trx_undo_rec_skip_row_ref(
  301. /*======================*/
  302. /* out: pointer to remaining part of undo
  303. record */
  304. byte* ptr, /* in: remaining part in update undo log
  305. record, at the start of the row reference */
  306. dict_index_t* index) /* in: clustered index */
  307. {
  308. ulint i;
  309. byte* field;
  310. ulint len;
  311. ulint ref_len;
  312. ut_ad(index && ptr);
  313. ref_len = dict_index_get_n_unique(index);
  314. for (i = 0; i < ref_len; i++) {
  315. ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
  316. }
  317. return(ptr);
  318. }
  319. /**************************************************************************
  320. Reports in the undo log of an update or delete marking of a clustered index
  321. record. */
  322. static
  323. ulint
  324. trx_undo_page_report_modify(
  325. /*========================*/
  326. /* out: byte offset of the inserted
  327. undo log entry on the page if succeed,
  328. 0 if fail */
  329. page_t*  undo_page, /* in: undo log page */
  330. trx_t* trx, /* in: transaction */
  331. dict_index_t* index, /* in: clustered index where update or
  332. delete marking is done */
  333. rec_t* rec, /* in: clustered index record which
  334. has NOT yet been modified */
  335. upd_t* update, /* in: update vector which tells the
  336. columns to be updated; in the case of
  337. a delete, this should be set to NULL */
  338. ulint cmpl_info, /* in: compiler info on secondary
  339. index updates */
  340. mtr_t* mtr) /* in: mtr */
  341. {
  342. dict_table_t* table;
  343. upd_field_t* upd_field;
  344. dict_col_t* col;
  345. ulint first_free;
  346. byte* ptr;
  347. ulint len;
  348. byte*  field;
  349. ulint flen;
  350. ulint pos;
  351. dulint roll_ptr;
  352. dulint trx_id;
  353. ulint bits;
  354. ulint col_no;
  355. byte* old_ptr;
  356. ulint type_cmpl;
  357. ulint i;
  358. ut_ad(index->type & DICT_CLUSTERED);
  359. ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  360. + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
  361. table = index->table;
  362. first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  363. + TRX_UNDO_PAGE_FREE);
  364. ptr = undo_page + first_free;
  365. ut_ad(first_free <= UNIV_PAGE_SIZE);
  366. if (trx_undo_left(undo_page, ptr) < 50) {
  367. /* NOTE: the value 50 must be big enough so that the general
  368. fields written below fit on the undo log page */
  369. return(0);
  370. }
  371. /* Reserve 2 bytes for the pointer to the next undo log record */
  372. ptr += 2;
  373. /* Store first some general parameters to the undo log */ 
  374. if (update) {
  375. if (rec_get_deleted_flag(rec)) {
  376. type_cmpl = TRX_UNDO_UPD_DEL_REC;
  377. } else {
  378. type_cmpl = TRX_UNDO_UPD_EXIST_REC;
  379. }
  380. } else {
  381. type_cmpl = TRX_UNDO_DEL_MARK_REC;
  382. }
  383. type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT);
  384. mach_write_to_1(ptr, type_cmpl);
  385. ptr++;
  386. len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
  387. ptr += len;
  388. len = mach_dulint_write_much_compressed(ptr, table->id);
  389. ptr += len;
  390. /*----------------------------------------*/
  391. /* Store the state of the info bits */
  392. bits = rec_get_info_bits(rec);
  393. mach_write_to_1(ptr, bits);
  394. ptr += 1;
  395. /* Store the values of the system columns */
  396. trx_id = dict_index_rec_get_sys_col(index, DATA_TRX_ID, rec);
  397. roll_ptr = dict_index_rec_get_sys_col(index, DATA_ROLL_PTR, rec);
  398. len = mach_dulint_write_compressed(ptr, trx_id);
  399. ptr += len;
  400. len = mach_dulint_write_compressed(ptr, roll_ptr);
  401. ptr += len;
  402. /*----------------------------------------*/
  403. /* Store then the fields required to uniquely determine the
  404. record which will be modified in the clustered index */
  405. for (i = 0; i < dict_index_get_n_unique(index); i++) {
  406. field = rec_get_nth_field(rec, i, &flen);
  407. if (trx_undo_left(undo_page, ptr) < 4) {
  408. return(0);
  409. }
  410. len = mach_write_compressed(ptr, flen); 
  411. ptr += len;
  412. if (flen != UNIV_SQL_NULL) {
  413. if (trx_undo_left(undo_page, ptr) < flen) {
  414. return(0);
  415. }
  416. ut_memcpy(ptr, field, flen);
  417. ptr += flen;
  418. }
  419. }
  420. /*----------------------------------------*/
  421. /* Save to the undo log the old values of the columns to be updated. */
  422. if (update) {
  423.     if (trx_undo_left(undo_page, ptr) < 5) {
  424. return(0);
  425.     }
  426.     len = mach_write_compressed(ptr, upd_get_n_fields(update));
  427.     ptr += len;
  428.     for (i = 0; i < upd_get_n_fields(update); i++) {
  429. upd_field = upd_get_nth_field(update, i);
  430. pos = upd_field->field_no;
  431. /* Write field number to undo log */
  432. if (trx_undo_left(undo_page, ptr) < 5) {
  433. return(0);
  434. }
  435. len = mach_write_compressed(ptr, pos);
  436. ptr += len;
  437. /* Save the old value of field */
  438. field = rec_get_nth_field(rec, pos, &flen);
  439. if (trx_undo_left(undo_page, ptr) < 5) {
  440. return(0);
  441. }
  442. len = mach_write_compressed(ptr, flen);
  443. ptr += len;
  444. if (flen != UNIV_SQL_NULL) {
  445. if (trx_undo_left(undo_page, ptr) < flen) {
  446. return(0);
  447. }
  448. ut_memcpy(ptr, field, flen);
  449. ptr += flen;
  450. }
  451.     }
  452. }
  453. /*----------------------------------------*/
  454. /* In the case of a delete marking, and also in the case of an update
  455. where any ordering field of any index changes, store the values of all
  456. columns which occur as ordering fields in any index. This info is used
  457. in the purge of old versions where we use it to build and search the
  458. delete marked index records, to look if we can remove them from the
  459. index tree. */
  460. if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {     
  461.     (trx->update_undo)->del_marks = TRUE;
  462.     if (trx_undo_left(undo_page, ptr) < 5) {
  463. return(0);
  464.     }
  465.     
  466.     old_ptr = ptr;
  467.     /* Reserve 2 bytes to write the number of bytes the stored fields
  468.     take in this undo record */
  469.     ptr += 2;
  470.     for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) {
  471.      col = dict_table_get_nth_col(table, col_no);
  472.      if (col->ord_part > 0) {
  473.     
  474. pos = dict_index_get_nth_col_pos(index, col_no);
  475. /* Write field number to undo log */
  476. if (trx_undo_left(undo_page, ptr) < 5) {
  477. return(0);
  478. }
  479. len = mach_write_compressed(ptr, pos);
  480. ptr += len;
  481. /* Save the old value of field */
  482. field = rec_get_nth_field(rec, pos, &flen);
  483. if (trx_undo_left(undo_page, ptr) < 5) {
  484. return(0);
  485. }
  486. len = mach_write_compressed(ptr, flen);
  487. ptr += len;
  488. if (flen != UNIV_SQL_NULL) {
  489. if (trx_undo_left(undo_page, ptr) < flen) {
  490. return(0);
  491. }
  492. ut_memcpy(ptr, field, flen);
  493. ptr += flen;
  494. }
  495. }
  496.     }
  497.     mach_write_to_2(old_ptr, ptr - old_ptr);     
  498. }
  499. /*----------------------------------------*/
  500. /* Write pointers to the previous and the next undo log records */
  501. if (trx_undo_left(undo_page, ptr) < 2) {
  502. return(0);
  503. }
  504. mach_write_to_2(ptr, first_free);
  505. ptr += 2;
  506. mach_write_to_2(undo_page + first_free, ptr - undo_page);
  507. mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
  508. ptr - undo_page);
  509. /* Write to the REDO log about this change in the UNDO log */
  510. trx_undof_page_add_undo_rec_log(undo_page, first_free,
  511. ptr - undo_page, mtr);
  512. return(first_free);
  513. }
  514. /**************************************************************************
  515. Reads from an undo log update record the system field values of the old
  516. version. */
  517. byte*
  518. trx_undo_update_rec_get_sys_cols(
  519. /*=============================*/
  520. /* out: remaining part of undo log
  521. record after reading these values */
  522. byte* ptr, /* in: remaining part of undo log
  523. record after reading general
  524. parameters */
  525. dulint* trx_id, /* out: trx id */
  526. dulint* roll_ptr, /* out: roll ptr */
  527. ulint* info_bits) /* out: info bits state */
  528. {
  529. ulint len;
  530. /* Read the state of the info bits */
  531. *info_bits = mach_read_from_1(ptr);
  532. ptr += 1;
  533. /* Read the values of the system columns */
  534. *trx_id = mach_dulint_read_compressed(ptr); 
  535. len = mach_dulint_get_compressed_size(*trx_id);
  536. ptr += len;
  537. *roll_ptr = mach_dulint_read_compressed(ptr); 
  538. len = mach_dulint_get_compressed_size(*roll_ptr);
  539. ptr += len;
  540. return(ptr);
  541. }
  542. /**************************************************************************
  543. Reads from an update undo log record the number of updated fields. */
  544. UNIV_INLINE
  545. byte*
  546. trx_undo_update_rec_get_n_upd_fields(
  547. /*=================================*/
  548. /* out: remaining part of undo log record after
  549. reading this value */
  550. byte* ptr, /* in: pointer to remaining part of undo log record */
  551. ulint* n) /* out: number of fields */
  552. {
  553. *n = mach_read_compressed(ptr); 
  554. ptr += mach_get_compressed_size(*n);
  555. return(ptr);
  556. }
  557. /**************************************************************************
  558. Reads from an update undo log record a stored field number. */
  559. UNIV_INLINE
  560. byte*
  561. trx_undo_update_rec_get_field_no(
  562. /*=============================*/
  563. /* out: remaining part of undo log record after
  564. reading this value */
  565. byte* ptr, /* in: pointer to remaining part of undo log record */
  566. ulint* field_no)/* out: field number */
  567. {
  568. *field_no = mach_read_compressed(ptr); 
  569. ptr += mach_get_compressed_size(*field_no);
  570. return(ptr);
  571. }
  572. /***********************************************************************
  573. Builds an update vector based on a remaining part of an undo log record. */
  574. byte*
  575. trx_undo_update_rec_get_update(
  576. /*===========================*/
  577. /* out: remaining part of the record */
  578. byte* ptr, /* in: remaining part in update undo log
  579. record, after reading the row reference
  580. NOTE that this copy of the undo log record must
  581. be preserved as long as the update vector is
  582. used, as we do NOT copy the data in the
  583. record! */
  584. dict_index_t* index, /* in: clustered index */
  585. ulint type, /* in: TRX_UNDO_UPD_EXIST_REC,
  586. TRX_UNDO_UPD_DEL_REC, or
  587. TRX_UNDO_DEL_MARK_REC; in the last case,
  588. only trx id and roll ptr fields are added to
  589. the update vector */
  590. dulint trx_id, /* in: transaction id from this undorecord */
  591. dulint roll_ptr,/* in: roll pointer from this undo record */
  592. ulint info_bits,/* in: info bits from this undo record */
  593. mem_heap_t* heap, /* in: memory heap from which the memory
  594. needed is allocated */
  595. upd_t** upd) /* out, own: update vector */
  596. {
  597. upd_field_t* upd_field;
  598. upd_t* update;
  599. ulint n_fields;
  600. byte* buf;
  601. byte* field;
  602. ulint len;
  603. ulint field_no;
  604. ulint i;
  605. if (type != TRX_UNDO_DEL_MARK_REC) {
  606. ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
  607. } else {
  608. n_fields = 0;
  609. }
  610. update = upd_create(n_fields + 2, heap);
  611. update->info_bits = info_bits;
  612. /* Store first trx id and roll ptr to update vector */
  613. upd_field = upd_get_nth_field(update, n_fields);
  614. buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
  615. trx_write_trx_id(buf, trx_id);
  616. upd_field_set_field_no(upd_field,
  617. dict_index_get_sys_col_pos(index, DATA_TRX_ID),
  618. index);
  619. dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
  620. upd_field = upd_get_nth_field(update, n_fields + 1);
  621. buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
  622. trx_write_roll_ptr(buf, roll_ptr);
  623. upd_field_set_field_no(upd_field,
  624. dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
  625. index);
  626. dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
  627. /* Store then the updated ordinary columns to update vector */
  628. for (i = 0; i < n_fields; i++) {
  629. ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
  630. ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
  631. upd_field = upd_get_nth_field(update, i);
  632. upd_field_set_field_no(upd_field, field_no, index);
  633. dfield_set_data(&(upd_field->new_val), field, len);
  634. }
  635. *upd = update;
  636. return(ptr);
  637. }
  638. /***********************************************************************
  639. Builds a partial row from an update undo log record. It contains the
  640. columns which occur as ordering in any index of the table. */
  641. byte*
  642. trx_undo_rec_get_partial_row(
  643. /*=========================*/
  644. /* out: pointer to remaining part of undo
  645. record */
  646. byte* ptr, /* in: remaining part in update undo log
  647. record of a suitable type, at the start of
  648. the stored index columns;
  649. NOTE that this copy of the undo log record must
  650. be preserved as long as the partial row is
  651. used, as we do NOT copy the data in the
  652. record! */
  653. dict_index_t* index, /* in: clustered index */
  654. dtuple_t** row, /* out, own: partial row */
  655. mem_heap_t* heap) /* in: memory heap from which the memory
  656. needed is allocated */
  657. {
  658. dfield_t* dfield;
  659. byte* field;
  660. ulint len;
  661. ulint field_no;
  662. ulint col_no;
  663. ulint row_len;
  664. ulint total_len;
  665. byte* start_ptr;
  666. ulint i;
  667. ut_ad(index && ptr && row && heap);
  668. row_len = dict_table_get_n_cols(index->table);
  669. *row = dtuple_create(heap, row_len);
  670. dict_table_copy_types(*row, index->table);
  671. start_ptr = ptr;
  672. total_len = mach_read_from_2(ptr);
  673. ptr += 2;
  674. for (i = 0;; i++) {
  675. if (ptr == start_ptr + total_len) {
  676. break;
  677. }
  678. ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
  679. col_no = dict_index_get_nth_col_no(index, field_no);
  680. ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
  681. dfield = dtuple_get_nth_field(*row, col_no);
  682. dfield_set_data(dfield, field, len);
  683. }
  684. return(ptr);
  685. }
  686. /***************************************************************************
  687. Erases the unused undo log page end. */
  688. static
  689. void
  690. trx_undo_erase_page_end(
  691. /*====================*/
  692. page_t* undo_page, /* in: undo page whose end to erase */
  693. mtr_t* mtr) /* in: mtr */
  694. {
  695. ulint first_free;
  696. ulint i;
  697. first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
  698. + TRX_UNDO_PAGE_FREE);
  699. for (i = first_free; i < UNIV_PAGE_SIZE - FIL_PAGE_DATA_END; i++) {
  700. undo_page[i] = 0xFF;
  701. }
  702. mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
  703. }
  704. /***************************************************************
  705. Parses a redo log record of erasing of an undo page end. */
  706. byte*
  707. trx_undo_parse_erase_page_end(
  708. /*==========================*/
  709. /* out: end of log record or NULL */
  710. byte* ptr, /* in: buffer */
  711. byte* end_ptr,/* in: buffer end */
  712. page_t* page, /* in: page or NULL */
  713. mtr_t* mtr) /* in: mtr or NULL */
  714. {
  715. ut_ad(ptr && end_ptr);
  716. if (page == NULL) {
  717. return(ptr);
  718. }
  719. trx_undo_erase_page_end(page, mtr);
  720. return(ptr);
  721. }
  722. /***************************************************************************
  723. Writes information to an undo log about an insert, update, or a delete marking
  724. of a clustered index record. This information is used in a rollback of the
  725. transaction and in consistent reads that must look to the history of this
  726. transaction. */
  727. ulint
  728. trx_undo_report_row_operation(
  729. /*==========================*/
  730. /* out: DB_SUCCESS or error code */
  731. ulint flags, /* in: if BTR_NO_UNDO_LOG_FLAG bit is
  732. set, does nothing */
  733. ulint op_type, /* in: TRX_UNDO_INSERT_OP or
  734. TRX_UNDO_MODIFY_OP */
  735. que_thr_t* thr, /* in: query thread */
  736. dict_index_t* index, /* in: clustered index */
  737. dtuple_t* clust_entry, /* in: in the case of an insert,
  738. index entry to insert into the
  739. clustered index, otherwise NULL */
  740. upd_t* update, /* in: in the case of an update,
  741. the update vector, otherwise NULL */
  742. ulint cmpl_info, /* in: compiler info on secondary
  743. index updates */
  744. rec_t* rec, /* in: case of an update or delete
  745. marking, the record in the clustered
  746. index, otherwise NULL */
  747. dulint* roll_ptr) /* out: rollback pointer to the
  748. inserted undo log record,
  749. ut_dulint_zero if BTR_NO_UNDO_LOG
  750. flag was specified */
  751. {
  752. trx_t* trx;
  753. trx_undo_t* undo;
  754. page_t* undo_page;
  755. ulint offset;
  756. mtr_t mtr;
  757. ulint page_no;
  758. ibool is_insert;
  759. trx_rseg_t* rseg;
  760. if (flags & BTR_NO_UNDO_LOG_FLAG) {
  761. *roll_ptr = ut_dulint_zero;
  762. return(DB_SUCCESS);
  763. }
  764. ut_ad(thr);
  765. ut_ad(index->type & DICT_CLUSTERED);
  766. ut_ad((op_type != TRX_UNDO_INSERT_OP)
  767.       || (clust_entry && !update && !rec));
  768. trx = thr_get_trx(thr);
  769. rseg = trx->rseg;
  770. mutex_enter(&(trx->undo_mutex));
  771. /* If the undo log is not assigned yet, assign one */
  772. if (op_type == TRX_UNDO_INSERT_OP) {
  773. if (trx->insert_undo == NULL) {
  774. trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
  775. }
  776. undo = trx->insert_undo;
  777. is_insert = TRUE;
  778. } else {
  779. ut_ad(op_type == TRX_UNDO_MODIFY_OP);
  780. if (trx->update_undo == NULL) {
  781. trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
  782. }
  783. undo = trx->update_undo;
  784. is_insert = FALSE;
  785. }
  786. if (undo == NULL) {
  787. /* Did not succeed: out of space */
  788. mutex_exit(&(trx->undo_mutex));
  789. return(DB_OUT_OF_FILE_SPACE);
  790. }
  791. page_no = undo->last_page_no;
  792. mtr_start(&mtr);
  793. for (;;) {
  794. undo_page = buf_page_get_gen(undo->space, page_no,
  795. RW_X_LATCH, undo->guess_page,
  796. BUF_GET,
  797. #ifdef UNIV_SYNC_DEBUG
  798. IB__FILE__, __LINE__,
  799. #endif
  800. &mtr);
  801. buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
  802. if (op_type == TRX_UNDO_INSERT_OP) {
  803. offset = trx_undo_page_report_insert(undo_page, trx,
  804. index, clust_entry,
  805. &mtr);
  806. } else {
  807. offset = trx_undo_page_report_modify(undo_page, trx,
  808. index, rec, update,
  809. cmpl_info, &mtr);
  810. }
  811. if (offset == 0) {
  812. /* The record did not fit on the page. We erase the
  813. end segment of the undo log page and write a log
  814. record of it: this is to ensure that in the debug
  815. version the replicate page constructed using the log
  816. records stays identical to the original page */
  817. trx_undo_erase_page_end(undo_page, &mtr);
  818. }
  819. mtr_commit(&mtr);
  820. if (offset != 0) {
  821. /* Success */
  822. break;
  823. }
  824. ut_ad(page_no == undo->last_page_no);
  825. /* We have to extend the undo log by one page */
  826. mtr_start(&mtr);
  827. /* When we add a page to an undo log, this is analogous to
  828. a pessimistic insert in a B-tree, and we must reserve the
  829. counterpart of the tree latch, which is the rseg mutex. */
  830. mutex_enter(&(rseg->mutex));
  831. page_no = trx_undo_add_page(trx, undo, &mtr);
  832. mutex_exit(&(rseg->mutex));
  833. if (page_no == FIL_NULL) {
  834. /* Did not succeed: out of space */
  835. mutex_exit(&(trx->undo_mutex));
  836. mtr_commit(&mtr);
  837. return(DB_OUT_OF_FILE_SPACE);
  838. }
  839. }
  840. undo->empty = FALSE;
  841. undo->top_page_no = page_no;
  842. undo->top_offset  = offset;
  843. undo->top_undo_no = trx->undo_no;
  844. undo->guess_page = undo_page;
  845. UT_DULINT_INC(trx->undo_no);
  846. mutex_exit(&(trx->undo_mutex));
  847. *roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
  848. offset);
  849. return(DB_SUCCESS);
  850. }
  851. /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
  852. /**********************************************************************
  853. Copies an undo record to heap. This function can be called if we know that
  854. the undo log record exists. */
  855. trx_undo_rec_t*
  856. trx_undo_get_undo_rec_low(
  857. /*======================*/
  858. /* out, own: copy of the record */
  859. dulint roll_ptr, /* in: roll pointer to record */
  860. mem_heap_t* heap) /* in: memory heap where copied */
  861. {
  862. ulint rseg_id;
  863. ulint page_no;
  864. ulint offset;
  865. page_t* undo_page;
  866. trx_rseg_t* rseg;
  867. ibool is_insert;
  868. mtr_t mtr;
  869. trx_undo_rec_t* undo_rec;
  870. trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
  871. &offset);
  872. rseg = trx_rseg_get_on_id(rseg_id);
  873. mtr_start(&mtr);
  874. undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
  875. undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
  876. mtr_commit(&mtr);
  877. return(undo_rec);
  878. }
  879. /**********************************************************************
  880. Copies an undo record to heap. */
  881. ulint
  882. trx_undo_get_undo_rec(
  883. /*==================*/
  884. /* out: DB_SUCCESS, or
  885. DB_MISSING_HISTORY if the undo log
  886. has been truncated and we cannot
  887. fetch the old version; NOTE: the
  888. caller must have latches on the
  889. clustered index page and purge_view */
  890. dulint roll_ptr, /* in: roll pointer to record */
  891. dulint trx_id, /* in: id of the trx that generated
  892. the roll pointer: it points to an
  893. undo log of this transaction */
  894. trx_undo_rec_t** undo_rec, /* out, own: copy of the record */
  895. mem_heap_t* heap) /* in: memory heap where copied */
  896. {
  897. ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
  898. if (!trx_purge_update_undo_must_exist(trx_id)) {
  899.      /* It may be that the necessary undo log has already been
  900. deleted */
  901. return(DB_MISSING_HISTORY);
  902. }
  903. *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
  904. return(DB_SUCCESS);
  905. }
  906. /***********************************************************************
  907. Build a previous version of a clustered index record. This function checks
  908. that the caller has a latch on the index page of the clustered index record
  909. and an s-latch on the purge_view. This guarantees that the stack of versions
  910. is locked. */
  911. ulint
  912. trx_undo_prev_version_build(
  913. /*========================*/
  914. /* out: DB_SUCCESS, or DB_MISSING_HISTORY if
  915. the previous version is not >= purge_view,
  916. which means that it may have been removed */
  917. rec_t* index_rec,/* in: clustered index record in the
  918. index tree */
  919. mtr_t* index_mtr,/* in: mtr which contains the latch to
  920. index_rec page and purge_view */
  921. rec_t* rec, /* in: version of a clustered index record */
  922. dict_index_t* index, /* in: clustered index */
  923. mem_heap_t* heap, /* in: memory heap from which the memory
  924. needed is allocated */
  925. rec_t** old_vers)/* out, own: previous version, or NULL if
  926. rec is the first inserted version, or if
  927. history data has been deleted */
  928. {
  929. trx_undo_rec_t* undo_rec;
  930. dtuple_t* entry;
  931. dulint rec_trx_id;
  932. ulint type;
  933. dulint undo_no;
  934. dulint table_id;
  935. dulint trx_id;
  936. dulint roll_ptr;
  937. upd_t* update;
  938. byte* ptr;
  939. ulint info_bits;
  940. ulint cmpl_info;
  941. byte* buf;
  942. ulint err;
  943. ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
  944. ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec), 
  945. MTR_MEMO_PAGE_S_FIX) ||
  946.       mtr_memo_contains(index_mtr, buf_block_align(index_rec), 
  947. MTR_MEMO_PAGE_X_FIX));
  948. roll_ptr = row_get_rec_roll_ptr(rec, index);
  949. if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
  950. /* The record rec is the first inserted version */
  951. *old_vers = NULL;
  952. return(DB_SUCCESS);
  953. }
  954. rec_trx_id = row_get_rec_trx_id(rec, index);
  955. err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
  956. if (err != DB_SUCCESS) {
  957. *old_vers = NULL;
  958. return(err);
  959. }
  960. ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info, &undo_no,
  961. &table_id);
  962. ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
  963. &info_bits);
  964. ptr = trx_undo_rec_skip_row_ref(ptr, index);
  965. trx_undo_update_rec_get_update(ptr, index, type, trx_id, roll_ptr,
  966. info_bits, heap, &update);
  967. if (row_upd_changes_field_size(rec, index, update)) {
  968. entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
  969. row_upd_clust_index_replace_new_col_vals(entry, update);
  970. buf = mem_heap_alloc(heap, rec_get_converted_size(entry));
  971. *old_vers = rec_convert_dtuple_to_rec(buf, entry);
  972. } else {
  973. buf = mem_heap_alloc(heap, rec_get_size(rec));
  974. *old_vers = rec_copy(buf, rec);
  975. row_upd_rec_in_place(*old_vers, update);
  976. }
  977. return(DB_SUCCESS);
  978. }