1
/******************************************************
2
Transaction undo log record
6
Created 3/26/1996 Heikki Tuuri
7
*******************************************************/
16
#include "mach0data.h"
20
#include "dict0dict.h"
24
#include "trx0purge.h"
27
/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
29
/**************************************************************************
30
Writes the mtr log entry of the inserted undo log record on the undo log
34
trx_undof_page_add_undo_rec_log(
35
/*============================*/
36
page_t* undo_page, /* in: undo log page */
37
ulint old_free, /* in: start offset of the inserted entry */
38
ulint new_free, /* in: end offset of the entry */
39
mtr_t* mtr) /* in: mtr */
50
ut_ad(new_free >= old_free + 4);
53
ptr = undo_page + old_free + 2;
55
if (old_free > mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
56
+ TRX_UNDO_PAGE_START)) {
57
prev_rec_ptr = undo_page + mach_read_from_2(ptr - 4) + 2;
59
min_len = ut_min(new_free - old_free - 4,
60
(undo_page + old_free - 2) - prev_rec_ptr);
65
} else if ((*ptr == *prev_rec_ptr)
66
|| ((*ptr == *prev_rec_ptr + 1)
67
&& (ptr + 1 == suffix))) {
77
mlog_write_initial_log_record(undo_page, MLOG_UNDO_INSERT, mtr);
79
mlog_catenate_ulint(mtr, old_free, MLOG_2BYTES);
81
mlog_catenate_ulint_compressed(mtr, i);
83
mlog_catenate_string(mtr, ptr, new_free - old_free - 2 - i);
85
log_ptr = mlog_open(mtr, 30 + MLOG_BUF_MARGIN);
87
if (log_ptr == NULL) {
92
log_ptr = mlog_write_initial_log_record_fast(undo_page,
93
MLOG_UNDO_INSERT, log_ptr, mtr);
94
len = new_free - old_free - 4;
96
mach_write_to_2(log_ptr, len);
100
ut_memcpy(log_ptr, undo_page + old_free + 2, len);
104
mlog_close(mtr, log_ptr);
106
if (len >= MLOG_BUF_MARGIN) {
107
mlog_catenate_string(mtr, undo_page + old_free + 2, len);
111
/***************************************************************
112
Parses a redo log record of adding an undo log record. */
115
trx_undo_parse_add_undo_rec(
116
/*========================*/
117
/* out: end of log record or NULL */
118
byte* ptr, /* in: buffer */
119
byte* end_ptr,/* in: buffer end */
120
page_t* page) /* in: page or NULL */
126
if (end_ptr < ptr + 2) {
131
len = mach_read_from_2(ptr);
134
if (end_ptr < ptr + len) {
144
first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
145
+ TRX_UNDO_PAGE_FREE);
146
rec = page + first_free;
148
mach_write_to_2(rec, first_free + 4 + len);
149
mach_write_to_2(rec + 2 + len, first_free);
151
mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
152
first_free + 4 + len);
153
ut_memcpy(rec + 2, ptr, len);
158
/**************************************************************************
159
Calculates the free space left for extending an undo log record. */
164
/* out: bytes left */
165
page_t* page, /* in: undo log page */
166
byte* ptr) /* in: pointer to page */
168
/* The '- 10' is a safety margin, in case we have some small
169
calculation error below */
171
return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
174
/**************************************************************************
175
Reports in the undo log of an insert of a clustered index record. */
178
trx_undo_page_report_insert(
179
/*========================*/
180
/* out: offset of the inserted entry
181
on the page if succeed, 0 if fail */
182
page_t* undo_page, /* in: undo log page */
183
trx_t* trx, /* in: transaction */
184
dict_index_t* index, /* in: clustered index */
185
dtuple_t* clust_entry, /* in: index entry which will be
186
inserted to the clustered index */
187
mtr_t* mtr) /* in: mtr */
196
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
197
+ TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
199
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
200
+ TRX_UNDO_PAGE_FREE);
201
ptr = undo_page + first_free;
203
ut_ad(first_free <= UNIV_PAGE_SIZE);
205
if (trx_undo_left(undo_page, ptr) < 30) {
207
/* NOTE: the value 30 must be big enough such that the general
208
fields written below fit on the undo log page */
213
/* Reserve 2 bytes for the pointer to the next undo log record */
216
/* Store first some general parameters to the undo log */
217
mach_write_to_1(ptr, TRX_UNDO_INSERT_REC);
220
len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
223
len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
225
/*----------------------------------------*/
226
/* Store then the fields required to uniquely determine the record
227
to be inserted in the clustered index */
229
for (i = 0; i < dict_index_get_n_unique(index); i++) {
231
field = dtuple_get_nth_field(clust_entry, i);
233
flen = dfield_get_len(field);
235
if (trx_undo_left(undo_page, ptr) < 5) {
240
len = mach_write_compressed(ptr, flen);
243
if (flen != UNIV_SQL_NULL) {
244
if (trx_undo_left(undo_page, ptr) < flen) {
249
ut_memcpy(ptr, dfield_get_data(field), flen);
254
if (trx_undo_left(undo_page, ptr) < 2) {
259
/*----------------------------------------*/
260
/* Write pointers to the previous and the next undo log records */
262
if (trx_undo_left(undo_page, ptr) < 2) {
267
mach_write_to_2(ptr, first_free);
270
mach_write_to_2(undo_page + first_free, ptr - undo_page);
272
mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
275
/* Write the log entry to the REDO log of this change in the UNDO log */
277
trx_undof_page_add_undo_rec_log(undo_page, first_free,
278
ptr - undo_page, mtr);
282
/**************************************************************************
283
Reads from an undo log record the general parameters. */
286
trx_undo_rec_get_pars(
287
/*==================*/
288
/* out: remaining part of undo log
289
record after reading these values */
290
trx_undo_rec_t* undo_rec, /* in: undo log record */
291
ulint* type, /* out: undo record type:
292
TRX_UNDO_INSERT_REC, ... */
293
ulint* cmpl_info, /* out: compiler info, relevant only
294
for update type records */
295
dulint* undo_no, /* out: undo log record number */
296
dulint* table_id) /* out: table id */
304
type_cmpl = mach_read_from_1(ptr);
307
*type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
308
*cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
310
*undo_no = mach_dulint_read_much_compressed(ptr);
311
len = mach_dulint_get_much_compressed_size(*undo_no);
314
*table_id = mach_dulint_read_much_compressed(ptr);
315
len = mach_dulint_get_much_compressed_size(*table_id);
321
/**************************************************************************
322
Reads from an undo log record a stored column value. */
325
trx_undo_rec_get_col_val(
326
/*=====================*/
327
/* out: remaining part of undo log record after
328
reading these values */
329
byte* ptr, /* in: pointer to remaining part of undo log record */
330
byte** field, /* out: pointer to stored field */
331
ulint* len) /* out: length of the field, or UNIV_SQL_NULL */
333
*len = mach_read_compressed(ptr);
334
ptr += mach_get_compressed_size(*len);
338
if (*len != UNIV_SQL_NULL) {
345
/***********************************************************************
346
Builds a row reference from an undo log record. */
349
trx_undo_rec_get_row_ref(
350
/*=====================*/
351
/* out: pointer to remaining part of undo
353
byte* ptr, /* in: remaining part of a copy of an undo log
354
record, at the start of the row reference;
355
NOTE that this copy of the undo log record must
356
be preserved as long as the row reference is
357
used, as we do NOT copy the data in the
359
dict_index_t* index, /* in: clustered index */
360
dtuple_t** ref, /* out, own: row reference */
361
mem_heap_t* heap) /* in: memory heap from which the memory
362
needed is allocated */
370
ut_ad(index && ptr && ref && heap);
372
ref_len = dict_index_get_n_unique(index);
374
*ref = dtuple_create(heap, ref_len);
376
dict_index_copy_types(*ref, index, ref_len);
378
for (i = 0; i < ref_len; i++) {
379
dfield = dtuple_get_nth_field(*ref, i);
381
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
383
dfield_set_data(dfield, field, len);
389
/***********************************************************************
390
Skips a row reference from an undo log record. */
393
trx_undo_rec_skip_row_ref(
394
/*======================*/
395
/* out: pointer to remaining part of undo
397
byte* ptr, /* in: remaining part in update undo log
398
record, at the start of the row reference */
399
dict_index_t* index) /* in: clustered index */
408
ref_len = dict_index_get_n_unique(index);
410
for (i = 0; i < ref_len; i++) {
411
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
417
/**************************************************************************
418
Reports in the undo log of an update or delete marking of a clustered index
422
trx_undo_page_report_modify(
423
/*========================*/
424
/* out: byte offset of the inserted
425
undo log entry on the page if succeed,
427
page_t* undo_page, /* in: undo log page */
428
trx_t* trx, /* in: transaction */
429
dict_index_t* index, /* in: clustered index where update or
430
delete marking is done */
431
rec_t* rec, /* in: clustered index record which
432
has NOT yet been modified */
433
upd_t* update, /* in: update vector which tells the
434
columns to be updated; in the case of
435
a delete, this should be set to NULL */
436
ulint cmpl_info, /* in: compiler info on secondary
438
mtr_t* mtr) /* in: mtr */
441
upd_field_t* upd_field;
457
ut_ad(index->type & DICT_CLUSTERED);
458
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
459
+ TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
460
table = index->table;
462
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
463
+ TRX_UNDO_PAGE_FREE);
464
ptr = undo_page + first_free;
466
ut_ad(first_free <= UNIV_PAGE_SIZE);
468
if (trx_undo_left(undo_page, ptr) < 50) {
470
/* NOTE: the value 50 must be big enough so that the general
471
fields written below fit on the undo log page */
476
/* Reserve 2 bytes for the pointer to the next undo log record */
479
/* Store first some general parameters to the undo log */
481
if (rec_get_deleted_flag(rec)) {
482
type_cmpl = TRX_UNDO_UPD_DEL_REC;
484
type_cmpl = TRX_UNDO_UPD_EXIST_REC;
487
type_cmpl = TRX_UNDO_DEL_MARK_REC;
490
type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT);
492
mach_write_to_1(ptr, type_cmpl);
495
len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
498
len = mach_dulint_write_much_compressed(ptr, table->id);
501
/*----------------------------------------*/
502
/* Store the state of the info bits */
504
bits = rec_get_info_bits(rec);
505
mach_write_to_1(ptr, bits);
508
/* Store the values of the system columns */
509
trx_id = dict_index_rec_get_sys_col(index, DATA_TRX_ID, rec);
511
roll_ptr = dict_index_rec_get_sys_col(index, DATA_ROLL_PTR, rec);
513
len = mach_dulint_write_compressed(ptr, trx_id);
516
len = mach_dulint_write_compressed(ptr, roll_ptr);
519
/*----------------------------------------*/
520
/* Store then the fields required to uniquely determine the
521
record which will be modified in the clustered index */
523
for (i = 0; i < dict_index_get_n_unique(index); i++) {
525
field = rec_get_nth_field(rec, i, &flen);
527
if (trx_undo_left(undo_page, ptr) < 4) {
532
len = mach_write_compressed(ptr, flen);
535
if (flen != UNIV_SQL_NULL) {
536
if (trx_undo_left(undo_page, ptr) < flen) {
541
ut_memcpy(ptr, field, flen);
546
/*----------------------------------------*/
547
/* Save to the undo log the old values of the columns to be updated. */
550
if (trx_undo_left(undo_page, ptr) < 5) {
555
len = mach_write_compressed(ptr, upd_get_n_fields(update));
558
for (i = 0; i < upd_get_n_fields(update); i++) {
560
upd_field = upd_get_nth_field(update, i);
561
pos = upd_field->field_no;
563
/* Write field number to undo log */
564
if (trx_undo_left(undo_page, ptr) < 5) {
569
len = mach_write_compressed(ptr, pos);
572
/* Save the old value of field */
573
field = rec_get_nth_field(rec, pos, &flen);
575
if (trx_undo_left(undo_page, ptr) < 5) {
580
len = mach_write_compressed(ptr, flen);
583
if (flen != UNIV_SQL_NULL) {
584
if (trx_undo_left(undo_page, ptr) < flen) {
589
ut_memcpy(ptr, field, flen);
595
/*----------------------------------------*/
596
/* In the case of a delete marking, and also in the case of an update
597
where any ordering field of any index changes, store the values of all
598
columns which occur as ordering fields in any index. This info is used
599
in the purge of old versions where we use it to build and search the
600
delete marked index records, to look if we can remove them from the
603
if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
605
(trx->update_undo)->del_marks = TRUE;
607
if (trx_undo_left(undo_page, ptr) < 5) {
614
/* Reserve 2 bytes to write the number of bytes the stored fields
615
take in this undo record */
619
for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) {
621
col = dict_table_get_nth_col(table, col_no);
623
if (col->ord_part > 0) {
625
pos = dict_index_get_nth_col_pos(index, col_no);
627
/* Write field number to undo log */
628
if (trx_undo_left(undo_page, ptr) < 5) {
633
len = mach_write_compressed(ptr, pos);
636
/* Save the old value of field */
637
field = rec_get_nth_field(rec, pos, &flen);
639
if (trx_undo_left(undo_page, ptr) < 5) {
644
len = mach_write_compressed(ptr, flen);
647
if (flen != UNIV_SQL_NULL) {
648
if (trx_undo_left(undo_page, ptr) < flen) {
653
ut_memcpy(ptr, field, flen);
659
mach_write_to_2(old_ptr, ptr - old_ptr);
662
/*----------------------------------------*/
663
/* Write pointers to the previous and the next undo log records */
664
if (trx_undo_left(undo_page, ptr) < 2) {
669
mach_write_to_2(ptr, first_free);
671
mach_write_to_2(undo_page + first_free, ptr - undo_page);
673
mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
676
/* Write to the REDO log about this change in the UNDO log */
678
trx_undof_page_add_undo_rec_log(undo_page, first_free,
679
ptr - undo_page, mtr);
683
/**************************************************************************
684
Reads from an undo log update record the system field values of the old
688
trx_undo_update_rec_get_sys_cols(
689
/*=============================*/
690
/* out: remaining part of undo log
691
record after reading these values */
692
byte* ptr, /* in: remaining part of undo log
693
record after reading general
695
dulint* trx_id, /* out: trx id */
696
dulint* roll_ptr, /* out: roll ptr */
697
ulint* info_bits) /* out: info bits state */
701
/* Read the state of the info bits */
702
*info_bits = mach_read_from_1(ptr);
705
/* Read the values of the system columns */
707
*trx_id = mach_dulint_read_compressed(ptr);
708
len = mach_dulint_get_compressed_size(*trx_id);
711
*roll_ptr = mach_dulint_read_compressed(ptr);
712
len = mach_dulint_get_compressed_size(*roll_ptr);
718
/**************************************************************************
719
Reads from an update undo log record the number of updated fields. */
722
trx_undo_update_rec_get_n_upd_fields(
723
/*=================================*/
724
/* out: remaining part of undo log record after
725
reading this value */
726
byte* ptr, /* in: pointer to remaining part of undo log record */
727
ulint* n) /* out: number of fields */
729
*n = mach_read_compressed(ptr);
730
ptr += mach_get_compressed_size(*n);
735
/**************************************************************************
736
Reads from an update undo log record a stored field number. */
739
trx_undo_update_rec_get_field_no(
740
/*=============================*/
741
/* out: remaining part of undo log record after
742
reading this value */
743
byte* ptr, /* in: pointer to remaining part of undo log record */
744
ulint* field_no)/* out: field number */
746
*field_no = mach_read_compressed(ptr);
747
ptr += mach_get_compressed_size(*field_no);
752
/***********************************************************************
753
Builds an update vector based on a remaining part of an undo log record. */
756
trx_undo_update_rec_get_update(
757
/*===========================*/
758
/* out: remaining part of the record */
759
byte* ptr, /* in: remaining part in update undo log
760
record, after reading the row reference
761
NOTE that this copy of the undo log record must
762
be preserved as long as the update vector is
763
used, as we do NOT copy the data in the
765
dict_index_t* index, /* in: clustered index */
766
ulint type, /* in: TRX_UNDO_UPD_EXIST_REC,
767
TRX_UNDO_UPD_DEL_REC, or
768
TRX_UNDO_DEL_MARK_REC; in the last case,
769
only trx id and roll ptr fields are added to
771
dulint trx_id, /* in: transaction id from this undorecord */
772
dulint roll_ptr,/* in: roll pointer from this undo record */
773
ulint info_bits,/* in: info bits from this undo record */
774
mem_heap_t* heap, /* in: memory heap from which the memory
775
needed is allocated */
776
upd_t** upd) /* out, own: update vector */
778
upd_field_t* upd_field;
787
if (type != TRX_UNDO_DEL_MARK_REC) {
788
ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
793
update = upd_create(n_fields + 2, heap);
795
update->info_bits = info_bits;
797
/* Store first trx id and roll ptr to update vector */
799
upd_field = upd_get_nth_field(update, n_fields);
800
buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
801
trx_write_trx_id(buf, trx_id);
803
upd_field_set_field_no(upd_field,
804
dict_index_get_sys_col_pos(index, DATA_TRX_ID),
806
dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
808
upd_field = upd_get_nth_field(update, n_fields + 1);
809
buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
810
trx_write_roll_ptr(buf, roll_ptr);
812
upd_field_set_field_no(upd_field,
813
dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
815
dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
817
/* Store then the updated ordinary columns to update vector */
819
for (i = 0; i < n_fields; i++) {
821
ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
822
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
824
upd_field = upd_get_nth_field(update, i);
826
upd_field_set_field_no(upd_field, field_no, index);
828
dfield_set_data(&(upd_field->new_val), field, len);
836
/***********************************************************************
837
Builds a partial row from an update undo log record. It contains the
838
columns which occur as ordering in any index of the table. */
841
trx_undo_rec_get_partial_row(
842
/*=========================*/
843
/* out: pointer to remaining part of undo
845
byte* ptr, /* in: remaining part in update undo log
846
record of a suitable type, at the start of
847
the stored index columns;
848
NOTE that this copy of the undo log record must
849
be preserved as long as the partial row is
850
used, as we do NOT copy the data in the
852
dict_index_t* index, /* in: clustered index */
853
dtuple_t** row, /* out, own: partial row */
854
mem_heap_t* heap) /* in: memory heap from which the memory
855
needed is allocated */
867
ut_ad(index && ptr && row && heap);
869
row_len = dict_table_get_n_cols(index->table);
871
*row = dtuple_create(heap, row_len);
873
dict_table_copy_types(*row, index->table);
877
total_len = mach_read_from_2(ptr);
882
if (ptr == start_ptr + total_len) {
887
ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
889
col_no = dict_index_get_nth_col_no(index, field_no);
891
ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
893
dfield = dtuple_get_nth_field(*row, col_no);
895
dfield_set_data(dfield, field, len);
901
/***************************************************************************
902
Erases the unused undo log page end. */
905
trx_undo_erase_page_end(
906
/*====================*/
907
page_t* undo_page, /* in: undo page whose end to erase */
908
mtr_t* mtr) /* in: mtr */
913
first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
914
+ TRX_UNDO_PAGE_FREE);
915
for (i = first_free; i < UNIV_PAGE_SIZE - FIL_PAGE_DATA_END; i++) {
919
mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
922
/***************************************************************
923
Parses a redo log record of erasing of an undo page end. */
926
trx_undo_parse_erase_page_end(
927
/*==========================*/
928
/* out: end of log record or NULL */
929
byte* ptr, /* in: buffer */
930
byte* end_ptr,/* in: buffer end */
931
page_t* page, /* in: page or NULL */
932
mtr_t* mtr) /* in: mtr or NULL */
934
ut_ad(ptr && end_ptr);
941
trx_undo_erase_page_end(page, mtr);
946
/***************************************************************************
947
Writes information to an undo log about an insert, update, or a delete marking
948
of a clustered index record. This information is used in a rollback of the
949
transaction and in consistent reads that must look to the history of this
953
trx_undo_report_row_operation(
954
/*==========================*/
955
/* out: DB_SUCCESS or error code */
956
ulint flags, /* in: if BTR_NO_UNDO_LOG_FLAG bit is
958
ulint op_type, /* in: TRX_UNDO_INSERT_OP or
959
TRX_UNDO_MODIFY_OP */
960
que_thr_t* thr, /* in: query thread */
961
dict_index_t* index, /* in: clustered index */
962
dtuple_t* clust_entry, /* in: in the case of an insert,
963
index entry to insert into the
964
clustered index, otherwise NULL */
965
upd_t* update, /* in: in the case of an update,
966
the update vector, otherwise NULL */
967
ulint cmpl_info, /* in: compiler info on secondary
969
rec_t* rec, /* in: case of an update or delete
970
marking, the record in the clustered
971
index, otherwise NULL */
972
dulint* roll_ptr) /* out: rollback pointer to the
973
inserted undo log record,
974
ut_dulint_zero if BTR_NO_UNDO_LOG
975
flag was specified */
986
if (flags & BTR_NO_UNDO_LOG_FLAG) {
988
*roll_ptr = ut_dulint_zero;
994
ut_ad(index->type & DICT_CLUSTERED);
995
ut_ad((op_type != TRX_UNDO_INSERT_OP)
996
|| (clust_entry && !update && !rec));
998
trx = thr_get_trx(thr);
1001
mutex_enter(&(trx->undo_mutex));
1003
/* If the undo log is not assigned yet, assign one */
1005
if (op_type == TRX_UNDO_INSERT_OP) {
1007
if (trx->insert_undo == NULL) {
1009
trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
1012
undo = trx->insert_undo;
1015
ut_ad(op_type == TRX_UNDO_MODIFY_OP);
1017
if (trx->update_undo == NULL) {
1019
trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
1023
undo = trx->update_undo;
1028
/* Did not succeed: out of space */
1029
mutex_exit(&(trx->undo_mutex));
1031
return(DB_OUT_OF_FILE_SPACE);
1034
page_no = undo->last_page_no;
1039
undo_page = buf_page_get_gen(undo->space, page_no,
1040
RW_X_LATCH, undo->guess_page,
1042
#ifdef UNIV_SYNC_DEBUG
1047
buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
1049
if (op_type == TRX_UNDO_INSERT_OP) {
1050
offset = trx_undo_page_report_insert(undo_page, trx,
1054
offset = trx_undo_page_report_modify(undo_page, trx,
1060
/* The record did not fit on the page. We erase the
1061
end segment of the undo log page and write a log
1062
record of it: this is to ensure that in the debug
1063
version the replicate page constructed using the log
1064
records stays identical to the original page */
1066
trx_undo_erase_page_end(undo_page, &mtr);
1077
ut_ad(page_no == undo->last_page_no);
1079
/* We have to extend the undo log by one page */
1083
/* When we add a page to an undo log, this is analogous to
1084
a pessimistic insert in a B-tree, and we must reserve the
1085
counterpart of the tree latch, which is the rseg mutex. */
1087
mutex_enter(&(rseg->mutex));
1089
page_no = trx_undo_add_page(trx, undo, &mtr);
1091
mutex_exit(&(rseg->mutex));
1093
if (page_no == FIL_NULL) {
1094
/* Did not succeed: out of space */
1096
mutex_exit(&(trx->undo_mutex));
1099
return(DB_OUT_OF_FILE_SPACE);
1103
undo->empty = FALSE;
1104
undo->top_page_no = page_no;
1105
undo->top_offset = offset;
1106
undo->top_undo_no = trx->undo_no;
1107
undo->guess_page = undo_page;
1109
UT_DULINT_INC(trx->undo_no);
1111
mutex_exit(&(trx->undo_mutex));
1113
*roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
1118
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
1120
/**********************************************************************
1121
Copies an undo record to heap. This function can be called if we know that
1122
the undo log record exists. */
1125
trx_undo_get_undo_rec_low(
1126
/*======================*/
1127
/* out, own: copy of the record */
1128
dulint roll_ptr, /* in: roll pointer to record */
1129
mem_heap_t* heap) /* in: memory heap where copied */
1138
trx_undo_rec_t* undo_rec;
1140
trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
1142
rseg = trx_rseg_get_on_id(rseg_id);
1146
undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
1148
undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
1155
/**********************************************************************
1156
Copies an undo record to heap. */
1159
trx_undo_get_undo_rec(
1160
/*==================*/
1161
/* out: DB_SUCCESS, or
1162
DB_MISSING_HISTORY if the undo log
1163
has been truncated and we cannot
1164
fetch the old version; NOTE: the
1165
caller must have latches on the
1166
clustered index page and purge_view */
1167
dulint roll_ptr, /* in: roll pointer to record */
1168
dulint trx_id, /* in: id of the trx that generated
1169
the roll pointer: it points to an
1170
undo log of this transaction */
1171
trx_undo_rec_t** undo_rec, /* out, own: copy of the record */
1172
mem_heap_t* heap) /* in: memory heap where copied */
1174
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1176
if (!trx_purge_update_undo_must_exist(trx_id)) {
1178
/* It may be that the necessary undo log has already been
1181
return(DB_MISSING_HISTORY);
1184
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
1189
/***********************************************************************
1190
Build a previous version of a clustered index record. This function checks
1191
that the caller has a latch on the index page of the clustered index record
1192
and an s-latch on the purge_view. This guarantees that the stack of versions
1196
trx_undo_prev_version_build(
1197
/*========================*/
1198
/* out: DB_SUCCESS, or DB_MISSING_HISTORY if
1199
the previous version is not >= purge_view,
1200
which means that it may have been removed */
1201
rec_t* index_rec,/* in: clustered index record in the
1203
mtr_t* index_mtr,/* in: mtr which contains the latch to
1204
index_rec page and purge_view */
1205
rec_t* rec, /* in: version of a clustered index record */
1206
dict_index_t* index, /* in: clustered index */
1207
mem_heap_t* heap, /* in: memory heap from which the memory
1208
needed is allocated */
1209
rec_t** old_vers)/* out, own: previous version, or NULL if
1210
rec is the first inserted version, or if
1211
history data has been deleted */
1213
trx_undo_rec_t* undo_rec;
1228
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1229
ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1230
MTR_MEMO_PAGE_S_FIX) ||
1231
mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1232
MTR_MEMO_PAGE_X_FIX));
1234
roll_ptr = row_get_rec_roll_ptr(rec, index);
1236
if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
1238
/* The record rec is the first inserted version */
1244
rec_trx_id = row_get_rec_trx_id(rec, index);
1246
err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
1248
if (err != DB_SUCCESS) {
1255
ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info, &undo_no,
1257
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
1259
ptr = trx_undo_rec_skip_row_ref(ptr, index);
1261
trx_undo_update_rec_get_update(ptr, index, type, trx_id, roll_ptr,
1262
info_bits, heap, &update);
1264
if (row_upd_changes_field_size(rec, index, update)) {
1266
entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
1268
row_upd_clust_index_replace_new_col_vals(entry, update);
1270
buf = mem_heap_alloc(heap, rec_get_converted_size(entry));
1272
*old_vers = rec_convert_dtuple_to_rec(buf, entry);
1274
buf = mem_heap_alloc(heap, rec_get_size(rec));
1276
*old_vers = rec_copy(buf, rec);
1278
row_upd_rec_in_place(*old_vers, update);